Release History - banisterious/obsidian-charted-roots GitHub Wiki

Release History

This document contains detailed implementation documentation for completed Charted Roots features. For the current roadmap of planned features, see Roadmap.

For version-specific changes, see the CHANGELOG and GitHub Releases.


Table of Contents


v0.22.x

v0.22.16 Round-Up: Modal Polish, Marriage Popup Parity, and Filename Casing (v0.22.16)

Five fixes refining Universe handling and marriage popups across modal and map surfaces. Edit Person dropdown now lists every form a universe might reach the user via the dropdown (#505 β€” typed name + cascaded basename, post-#503 alias-aware matching means picking either resolves correctly). The Edit Event modal exposes the Universe field on every event type (#507 β€” previously gated alongside the narrative-only isCanonical toggle, leaving Vital and Life events with no path to set their universe via the modal) and reactively reveals the Worldbuilding section when the user picks a narrative type from the dropdown after opening the modal. Marriage popups pair the partner's age alongside the focal person's on the static-marker surface (#508 β€” parity with the journey-mode treatment from 0.22.15's #504), and event / source / proof-summary filenames now preserve accented characters and casing instead of being slugified into ASCII-only forms (#509).

Driven by @DigitalDreamn's verification thread that surfaced #505, #507, #508, and #509 across multiple cycles. The investigation track she seeded with empirical evidence in #506 (parens / brackets / braces in filenames) bore fruit indirectly here β€” #509's filename generators were a separate aggressive sanitization path that the same testing pass surfaced. All five changes are non-data-loss; stability window continues unchanged from 0.22.4. Twelfth patch in the run without a reset.

Fix: Edit Person dropdown includes the universe note's typed name (#505):

  • src/plugin/bulk-operations.ts β€” the right-click Edit Person flow merged universes from only two sources (placeGraph.getAllUniverses() and familyGraph.getAllUniverses()), omitting the universe-note source that the Control Center's getCachedUniverses already uses. After a Universe rename where sanitization stripped chars from the basename β€” e.g. typed Star Wars (AU) becoming basename Star Wars AU β€” the cascaded entity values made it into the dropdown but the typed name had no path in. Users saw only the basename and couldn't recognize the name they typed.
  • New src/universes/services/merged-universe-list.ts extracts the merge logic into a shared mergeUniverseList helper. Both call sites (Control Center + Edit Person via context menu) now go through it, so the three-source merge can't drift apart again. 10 new tests in tests/universe-cache-after-rename.test.ts. Reported by @DigitalDreamn during #488 verification.

Fix: Universe field always available in Edit Event modal, reactively (#507):

  • src/events/ui/create-event-modal.ts β€” the Universe Setting was bundled inside the conditional that gates the isCanonical toggle on narrative event types, so Vital (birth/marriage/death/burial) and Life (residence/immigration/etc.) events had no way to set their universe via the modal. Pulled Universe out into its own always-rendered Setting at the same level as Place / Timeline; isCanonical stays narrative-only since canon/non-canon is storytelling-specific, not applicable to vital records or life events.
  • Follow-up: the Worldbuilding section that wraps isCanonical was decided once at form-build time from the initial eventType and never re-evaluated, so picking a narrative type from the dropdown after opening the modal didn't reveal the section. The section now always renders into the DOM and toggles cr-hidden based on isNarrativeEventType(this.eventType). The event-type dropdown's onChange updates the visibility so users can switch into a narrative type and immediately see the Canonical event toggle without saving and reopening.
  • isNarrativeEventType lives in src/events/types/event-types.ts so the membership fence test can import without pulling in the modal's UI deps. 5 new tests in tests/event-narrative-type.test.ts. Reported and verified by @DigitalDreamn.

Added: Static map marker popup pairs the partner's age alongside the focal person's (#508):

  • src/maps/types/map-types.ts + src/maps/map-data-service.ts β€” the journey-mode rich popup gained partner age in 0.22.15 (#504), but the non-journey marker popup (clicked directly on a map marker) didn't. MapMarker gains spouseBirthDate?: string; buildMarkers builds the same peopleById lookup buildJourneyPaths already does, resolves the spouse via marriage.spouseId, and threads the spouse's born value onto marriage markers when resolvable.
  • src/maps/map-controller.ts β€” the static popup's existing with X participants line appends (age N) to the partner's entry when the marker carries spouseBirthDate. Uses the same DateService.calculateAge path the focal age already uses, so fictional eras round-trip correctly. Format ends up with Beru Whitesun (age 38) alongside the existing focal-side Marriage: 19 BBY (age 45). Compact in-line variant suggested by @DigitalDreamn during #504 verification β€” she'd originally meant the static popup when filing #501. 5 new tests in tests/marriage-marker-spouse-birth.test.ts.

Fix: Event / Source / Proof Summary filenames preserve accented characters and casing (#509):

  • src/events/services/event-service.ts + src/sources/services/source-service.ts + src/sources/services/proof-summary-service.ts + src/events/services/life-events-migration-service.ts β€” three services and the life-events migration helper each had their own private slugify running an aggressive [^a-z0-9]+ -> - regex that destroyed accented chars, lowercased everything, and turned spaces into hyphens. Birth of PadmΓ© Naberrie became birth-of-padm-naberrie.md. Person notes go through sanitizeName which preserves accented characters, casing, apostrophes, hyphens, and spaces β€” same character was treated inconsistently across entity types.
  • src/utils/name-sanitization.ts β€” new sanitizeFilename(title, maxLength = 100) helper wraps sanitizeName with a length cap and replaces all four call sites. Filenames now preserve user typing the same way person notes already do. Existing files keep their old slugified names; only new files going forward use the preserved format. Mixed state is unavoidable without a separate migration step. 18 new tests in tests/sanitize-filename.test.ts (replacing 6 obsolete slugifyTitle tests). Reported by @DigitalDreamn during #506 investigation.

Testing: 38 new tests, suite total 533 (was 495 at start of cycle).

  • tests/universe-cache-after-rename.test.ts (10 tests, #505) β€” alias preservation across rename, three-source merge with divergent basenames, exact-string dedup, sort order, empty inputs.
  • tests/event-narrative-type.test.ts (5 tests, #507 follow-up) β€” narrative-type membership for canonical types, vital types, life types, transfer / custom / empty rejection, case-sensitivity.
  • tests/marriage-marker-spouse-birth.test.ts (5 tests, #508) β€” happy path with both spouses in dataset, missing spouseId fallback, missing born value, numeric-year coercion, no bleed onto non-marriage markers.
  • tests/sanitize-filename.test.ts (18 tests, #509) β€” accented preservation, casing preservation, apostrophes / hyphens / spaces preservation, wikilink-unsafe stripping, length cap with custom max, empty / pathological input handling, parity with sanitizeName under cap.

Reporters: @DigitalDreamn for the verification thread that surfaced #505 / #507 / #508 / #509 across multiple cycles. The empirical evidence she contributed in #506 about filesystem- and wikilink-safe characters informed the conservative sanitizeFilename approach in #509.

Issues filed during this cycle (post-1.0 / deferred):

  • #506 β€” investigation track for relaxing WIKILINK_UNSAFE_CHARS (parens / brackets / braces) once cross-platform wikilink safety is verified. Marked post-1.0.

Stability-window impact: no reset β€” all five changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Twelfth patch (0.22.5 / 0.22.6 / 0.22.7 / 0.22.8 / 0.22.9 / 0.22.10 / 0.22.11 / 0.22.12 / 0.22.13 / 0.22.14 / 0.22.15 / 0.22.16) without a window reset. About two weeks of soak left.


v0.22.15 Round-Up: Universe Rename Cascade Coverage and Marriage Popup Partner Age (v0.22.15)

Three fixes that close the loose ends from 0.22.14's universe rename arc, plus a small enhancement to the marriage popup. The rename cascade now covers map notes (#503, Part 1 of three) β€” cr_type: map joins the cascade so map notes' universe: field gets rewritten alongside person/place/event/organization. Universe code blocks survive the name↔basename divergence that sanitization causes when typed names contain parens / brackets / quotes (Part 2) β€” new alias-aware lookup matches against any of basename, frontmatter name, or cr_id. The map view re-syncs its filter when a map note's universe field changes (Part 3) β€” both via cr_id resolution returning the basename and via auto-reloading mapConfigs on map-note metadata change. And the journey-mode marriage popup pairs the partner's age alongside the focal person's (#504, suggested by @DigitalDreamn during #501 verification).

The three #503 sub-fixes form a "Part 4" of the universe rename arc that started in 0.22.11 (#488 Part 1, dropdown sourcing), continued in 0.22.12 (#488 Part 2, cascade for plain-string references), and #488 Part 3 in 0.22.14 (Edit Universe modal triggers the cascade). #503 is the gap-closing pass: scope expansion (maps in cascade) + read-side resilience (alias-aware lookups + filter resync). Driven entirely by @DigitalDreamn's 0.22.14 verification thread, which exposed gaps the original tests didn't surface. All four changes are non-data-loss; stability window continues unchanged from 0.22.4. Eleventh patch in the run without a reset.

Fix: Universe rename cascade rewrites map notes alongside other entity types (#503):

  • src/universes/services/universe-service.ts β€” the 0.22.14 cascade scope was person | place | event | organization. Map notes (cr_type: map) carry a universe: field too but were silently skipped, so after a rename the map kept pointing at the old universe and its marker filter no longer matched the cascaded entities. cr_type: map added to REFERENCING_TYPES so map notes ride the cascade with the rest. Reported by @DigitalDreamn during 0.22.14 verification.

Fix: Universe dynamic code blocks survive name↔basename divergence after rename (#503):

  • src/universes/services/universe-service.ts β€” the rename cascade writes the file basename to entity universe: fields, but the charted-roots-universe-people / places / events / organizations / maps block processors compared against the universe note's frontmatter name. When sanitizeName strips characters during the file rename (parens / brackets / quotes β€” e.g. "The Dying Earth (Vance)" β†’ basename "The Dying Earth Vance"), the basename diverges from the typed name and the lookup silently returned zero entities ("No entities found for this universe").
  • New getEntitiesForUniverseFile matches against any of the universe note's aliases β€” basename, frontmatter name, or cr_id β€” so the lookup survives whichever form an entity's universe: field happens to hold (post-cascade basename, dropdown-written name, or cr_id reference). src/dynamic-content/processors/universe-entities-processor.ts and src/dynamic-content/processors/universe-maps-processor.ts both adopt the alias-aware shape, including the metadata-cache change handler comparison so cache invalidation respects the same alias set.

Fix: Map filter resolves universe cr_id to basename, and re-syncs when a map note changes (#503):

  • New src/maps/resolve-universe-filter.ts + src/maps/map-view.ts β€” two related issues with the map view's universe filter chain. (1) resolveUniverseFilterValue returned the universe note's frontmatter name when given a cr_id, but the rename cascade writes the basename to entities β€” so after a rename like "The Dying Earth" β†’ "The Dying Earth (Vance)" the resolved filter "The Dying Earth (Vance)" no longer matched cascaded places / people on basename "The Dying Earth Vance" and every marker silently disappeared. Resolver now returns the basename, which is what the cascade writes. (2) The map controller's in-memory mapConfigs cache stayed stale after an Edit Map save, so getActiveMapUniverse() kept returning the old value and refresh re-queried with the wrong filter.
  • src/maps/map-controller.ts β€” new reloadMapConfigs() method exposed publicly. Map view now calls it from a syncMapConfigOnChange helper on every metadata-cache change for cr_type: map files, then re-resolves this.filters.universe from getActiveMapUniverse() so refresh re-queries with the fresh filter. The resolver moves to its own module so the logic can be unit-tested without instantiating MapView. 7 new tests in tests/resolve-universe-filter.test.ts.

Added: Marriage popup pairs the partner's age alongside the focal person's (#504):

  • src/maps/types/map-types.ts + src/maps/map-data-service.ts β€” journey-mode marriage popups previously displayed the focal person's age at marriage but not the partner's, forcing the reader to navigate to the partner's note for the same calculation. JourneyWaypoint gains spouseBirthDate?: string; buildJourneyPaths builds a peopleById lookup from the same people array it iterates, resolves the spouse via marriage.spouseId, and threads the spouse's born value onto the marriage waypoint when resolvable.
  • src/maps/map-view.ts β€” popup renders a separate Partner's age row when both the marriage date and a resolvable spouse birth date are present, paired with the existing focal-person Age row via the same DateService.calculateAge path so fictional eras work correctly. Legacy flat marriages without spouseId, and spouses without a born value, quietly omit the row. Suggested by @DigitalDreamn during #501 verification. 5 new tests in tests/journey-marriage-spouse-birth.test.ts.

Testing: 20 new tests, suite total 500 (was 480 at start of cycle).

  • tests/universe-rename-cascade.test.ts (3 tests, #503 Part 1) β€” map cr_type inclusion, multi-type rewrite in single pass, unrelated cr_types untouched.
  • tests/universe-entities-by-file.test.ts (5 tests, #503 Part 2) β€” basename / name / cr_id alias matching post-rename and pre-rename, no-match isolation, case-insensitive across aliases.
  • tests/resolve-universe-filter.test.ts (7 tests, #503 Part 3) β€” cr_id-to-basename resolution with name divergence, pass-through for already-name values, null / empty handling, multi-universe selection, legacy type: universe shape support.
  • tests/journey-marriage-spouse-birth.test.ts (5 tests, #504) β€” happy path, missing spouseId fallback, missing born value, numeric-year coercion, no bleed onto non-marriage waypoints.

Reporters: @DigitalDreamn for the 0.22.14 verification thread that surfaced #503 (all three sub-fixes β€” cascade scope gap, dynamic-block lookup divergence, map filter freshness) and the #501 verification observation that prompted #504.

Stability-window impact: no reset β€” all four changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Eleventh patch (0.22.5 / 0.22.6 / 0.22.7 / 0.22.8 / 0.22.9 / 0.22.10 / 0.22.11 / 0.22.12 / 0.22.13 / 0.22.14 / 0.22.15) without a window reset.


v0.22.14 Round-Up: Universe Rename Closure, Marriage Marker Pairing, and a Cleanup Wizard Step (v0.22.14)

Three fixes plus one Cleanup Wizard step. Closes the Universe rename direction end-to-end (#488 Part 3, the third part of a three-part arc): the Edit Universe modal now actually renames the file when the name property changes, which triggers the existing 0.22.12 Part 2 cascade automatically. Collapses pair-symmetric marriage markers into a single combined marker with both partner names in the popup (#501 β€” sibling to #493's cr_type: event dedup, but for marriages, which live as frontmatter on each spouse's note rather than as event notes). And adds a Cleanup Wizard step (#502, Layer 2 of the original #471 plan) that surfaces place notes lacking cr_id and offers a "Generate cr_id" fix so they re-enter the place graph.

Driven by @DigitalDreamn's verification of the 0.22.13 cycle: her Edit Universe testing surfaced the missing rename trigger that became #488 Part 3, and her #493 / #498 verification observations seeded #501. All four changes are non-data-loss; stability window continues unchanged from 0.22.4. Tenth patch in the run without a reset.

Fix: Edit Universe modal renames the file when the name property changes (#488 Part 3):

  • src/universes/services/universe-service.ts β€” the 0.22.12 Part 2 cascade walks all entities and rewrites universe: plain-string references when a universe note's basename changes, but it's keyed on vault.on('rename'). UniverseService.updateUniverse was only writing the new name to the frontmatter; the file basename stayed the same. The cascade only fired when users renamed the universe FILE directly (drag, F2, wikilink rename). Editing via the Edit Universe modal β€” the natural path β€” left entities pointing at the old name. The dropdown showing both old and new names was a side effect of getCachedUniverses combining distinct universe: values from people / places (still the old name) with universe-note names (new name).
  • updateUniverse now sanitizes the new name (via the existing sanitizeName helper) and calls app.fileManager.renameFile when the sanitized name differs from the current basename. The cascade fires automatically off the rename event, and Obsidian's native wikilink-rewrite handles [[oldName]] β†’ [[newName]] updates for free. Reported by @DigitalDreamn after testing 0.22.13's Part 2 cascade β€” verified end-to-end on a dev-vault rename of "The Dying Earth" before commit.
  • The three-part arc closes here: Part 1 (0.22.11, d461b3f2) made the Edit Person Universe dropdown source from the universes folder; Part 2 (0.22.12, 452bcffd) added the rename cascade for plain-string universe: references; Part 3 (0.22.14) makes the Edit Universe modal trigger that cascade naturally instead of requiring users to know to rename the file directly.

Fix: Marriages between two spouses render as one combined marker with both partner names (#501):

  • src/maps/map-data-service.ts + src/maps/types/map-types.ts β€” a marriage produced one map marker per spouse (Owen's spouse1_marriage_* slot rendered a marker for Owen at Tatooine; Beru's spouse1_marriage_* slot rendered a separate marker for Beru at the same place). Neither popup named the partner. #493's eventCrId-based dedup didn't catch this because marriages live on each spouse's frontmatter rather than as cr_type: event notes.
  • loadMarriages now reads spouseN and spouseN_id alongside the existing marriage fields, attaches spouseId / spouseName to the resulting MapMarker. A new dedupeMarriageMarkers pass (running after the event-cr_id dedup) groups by sorted-pair + place + date so Owenβ†’Beru and Beruβ†’Owen markers collapse into one. Both spouses appear in the surviving marker's participants list, so the existing popup rendering surfaces "Owen Lars / with Beru Whitesun" naturally.
  • Journey-mode rich popup also gains a Partner row for marriage waypoints (covering @DigitalDreamn's "indicate to who" suggestion from #498 verification). 8 new tests in tests/marriage-marker-dedup.test.ts.

Added: Cleanup Wizard step β€” add cr_id to place notes (#502):

  • src/ui/cleanup-wizard-types.ts + src/ui/cleanup-wizard-modal.ts β€” new step 15 in the Post-Import Cleanup Wizard. Detects place-shaped notes via the canonical isPlaceNote detection, lists them in the preview, and applies a generated cr_id to each via processFrontMatter (defensive: skips notes that already have one in case state changed mid-run). Mirrors the step 14 child-to-children pattern.
  • Layer 2 of the original #471 three-layer plan. Layer 1 (the warn-level dev-console log) shipped in 0.22.9. Layer 3 (silent auto-heal during cache build) was discussed but rejected β€” keeping schema issues visible via the wizard preserves user awareness.

Test: Fence FamilyGraphService.extractPersonNode non-person rejection (#489 follow-up):

  • tests/family-graph-extract-person.test.ts β€” 6 tests fencing the inclusion check that #489 added in 0.22.11. Custom cr_type values ("hex" / "faction") return null instead of falling through and being coerced into people; existing place / person / legacy cr_id-no-cr_type / missing-cr_id paths preserve their behavior. The fix shipped without dedicated test coverage; this fences the regression class so future refactors can't quietly break it.

Testing: 6 new tests, suite total 480 (was 466 at start of cycle; +14 across this and the closing #489 fence).

  • tests/marriage-marker-dedup.test.ts (8 tests, #501) β€” pass-through non-marriage, single-marriage pass-through, sorted-pair collapse, legacy-no-spouseId pass-through, two unrelated couples kept separate, same couple different dates, same couple different places, multi-spouse with shared-partner-only-some-marriages.
  • tests/family-graph-extract-person.test.ts (6 tests, #489 follow-up) β€” custom non-person cr_type rejection, place sentinel preservation, person extraction, legacy-no-cr_type fallback, missing-cr_id null.
  • #488 Part 3 / #502 β€” manually verified by reporter / developer; integration-test mocking burden is high for marginal fence value.

Reporters: @DigitalDreamn for #488 Part 3 verification report + #501 surface from #493 / #498 testing.

Stability-window impact: no reset β€” all four changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Tenth patch (0.22.5 / 0.22.6 / 0.22.7 / 0.22.8 / 0.22.9 / 0.22.10 / 0.22.11 / 0.22.12 / 0.22.13 / 0.22.14) without a window reset. About two weeks of soak left.


v0.22.13 Round-Up: Map Coverage β€” Multi-Spouse, Multi-Participant, and Hierarchical Places (v0.22.13)

Six fixes, all map-adjacent. Most-impactful is the map-coverage cluster β€” three classes of map data that were silently invisible now render correctly: multi-spouse marriages on a multi-spouse person's journey (#498), multi-participant events that previously stacked one marker per participant (#493), and child-place events on vaults without a zoomed-in child map (#494). Plus a journey-popup label fix that completes the #466 custom-label work across the second render path (#499), and two follow-ups to 0.22.12: a sturdier compound-row layout for the Place modal coordinate inputs (#496 follow-up) and a flat-format spouse promotion that lets the marriage-detail mirror work even when the partner's note hasn't been migrated to indexed shape yet (#481 follow-up).

Driven heavily by @DigitalDreamn's continuing testing on the Star Wars / Lars-family vault: her #487 verification surfaced the missing-marriages observation that became #498 and the "custom" journey popup label that became #499; her Lars Homestead frontmatter clinched the diagnosis on #494; her Bail / Breha Organa repro drove the #481 follow-up. @doctorwodka also closed out #491 verification (both halves verified β€” tab labels and the merge step). All six changes are non-data-loss; stability window continues unchanged from 0.22.4.

Fix: Map journey mode reads every marriage on multi-spouse people (#498):

  • src/maps/map-data-service.ts β€” marriage waypoints and markers were sourced from a single set of legacy flat marriage_place / marriage_place_id / marriage_date frontmatter fields. People with multiple spouses (whose data is written to indexed spouseN_marriage_* slots after #481's bidirectional linker improvements) had no flat fields populated, so journeys silently dropped every marriage. PersonData.marriages becomes an array; new loadMarriages helper reads indexed slots spouse1_marriage_* through spouse10_marriage_* (matching the writer's iteration bound), falls back to a single legacy flat entry, and skips empty slots.
  • buildMarkers and buildJourneyPaths iterate the array, emitting one waypoint / marker per populated slot. Drops the && person.marriageDate requirement so dateless marriages still surface (sorting to the end of the life-event run, like death and burial already do). 10 new tests in tests/marriage-loader.test.ts. Surfaced by @DigitalDreamn during #487 verification β€” Cliegg Lars's death popup came back via the dedup fix, but neither of his two marriages appeared as waypoints.

Fix: Multi-participant events render one combined marker instead of one per participant (#493):

  • src/maps/map-data-service.ts + src/maps/types/map-types.ts β€” a cr_type: event note referenced by multiple people (e.g., a wedding with bride + groom, a battle with multiple combatants) produced one map marker per participant stacked at the same location. buildMarkers iterates per-person and EventService.getEventsForPerson surfaces the same external event for each participant, so each per-person pass contributed its own marker.
  • Threads the event note's cr_id through LifeEvent and MapMarker (set only for external cr_type: event notes β€” inline events stay per-person, never dedup), then a new dedupeEventMarkers pass after marker collection groups by eventCrId, keeps one marker per group with the event note's person field as primary, and lists all participants in the popup. 8 new tests in tests/event-marker-dedup.test.ts. Reported by @DigitalDreamn during #487 testing.

Fix: Events at child places render via inherited parent coordinates instead of disappearing (#494):

  • src/maps/map-data-service.ts β€” when a person's birth_place / death_place / event location pointed at a child place (e.g., Lars Homestead with parent_place: [[Tatooine]]) that had no own pixel or geographic coordinates, the marker dropped silently because hasValidCoordinates returned false on the child. The map's place resolution now walks up the parent_place / parent_place_id chain when the resolved place has no own coords, inheriting positioning fields (lat / lng / pixelX / pixelY / mapId / maps) from the nearest ancestor that does.
  • Popup and click-through keep the child's identity (Lars Homestead shows in the popup, opening the child's note); the marker visually appears at the parent's location β€” appropriate when the parent is the most-zoomed map level the user has set up. Same shape covers the real-world equivalent (a Stockholm event on a vault where Stockholm has no coords but Sweden does). Adds parentPlaceId to the place cache and an applyCoordinateFallback step inside resolvePlace. 7 new tests in tests/place-coordinate-fallback.test.ts. The forward-looking question of what happens once a child has its own coords on a child map (and you're viewing the parent map) is tracked separately as #500.

Fix: Journey mode popup and play-control label show the original event type for custom events (#499):

  • src/maps/types/map-types.ts + src/maps/map-view.ts β€” custom event types rendered as the literal string Custom in the journey-mode rich popup and play-control label, instead of preserving the original type (e.g., Backstory). Sibling to #466, which fixed the same UX gap on static map markers via customLabel on MapMarker; the journey-mode rendering path was missed at the time.
  • JourneyWaypoint now carries customLabel, buildJourneyPaths propagates it from the source LifeEvent, and a new getJourneyWaypointEventLabel helper resolves the display label (preferring customLabel for custom waypoints, falling back to canonical eventType otherwise) so both render sites stay in sync. 5 new tests in tests/journey-waypoint-display-label.test.ts. Reported by @DigitalDreamn during #487 verification.

Fix: Pixel coordinates X / Y (and Latitude / Longitude) render as a single compound row (#496 follow-up):

  • src/ui/create-place-modal.ts + styles/place-modals.css β€” the 0.22.12 fix tried to keep X / Y inline via align-items: center on a flex container of two adjacent setting-items, but @DigitalDreamn reported the rows still looked misaligned. DevTools inspection showed an Obsidian default :first-child / :last-child rule applying asymmetric padding (0 0 16px on X, 16px 0 0 on Y), pushing X content to the top of its box and Y content to the bottom. Specificity bumps couldn't reliably defeat the Obsidian default.
  • Reworked the layout so X and Y (and the Latitude / Longitude pair under the same UI surface) live inside a single Setting's control area as plain inputs with inline labels β€” one setting-item, no adjacent-sibling padding asymmetry, side-by-side layout preserved. The Look up button stacks below the lat/long inputs via flex-direction: column on the controlEl. Pixel inputs render at 100px each via a --pixel modifier; geographic inputs at 140px via --geo to accommodate signed decimals and DMS strings.

Fix: Marriage detail mirror also works when the partner's note uses legacy flat spouse format (#481 follow-up):

  • src/core/bidirectional-linker.ts β€” the 0.22.12 fix mirrored marriage details correctly when both partners' notes used the indexed spouseN: format, but missed the case where the partner was still on the legacy flat spouse: / spouse_id: shape. Couples that paired up before any marriage details existed kept the flat shape on both sides; setting marriage details on one side promoted that note to indexed via the writer's existing path, but the linker's mirror step bailed because findExistingSpouseIndex only scanned indexed slots.
  • New promoteFlatSpouseToIndexed helper β€” when the target uses single-spouse flat format and the source has marriage details to mirror, the promotion atomically rewrites the target's spouse: / spouse_id: to spouse1: / spouse1_id: so the existing mirror code has a spouseN_* namespace to write the companion fields into. The atomic single-processFrontMatter write avoids a phantom-deletion cascade that sequential writes would have triggered. Reported by @DigitalDreamn in her Bail / Breha Organa scenario; reproduced locally on a dev-vault couple matching the same shape.

Testing: 30 new tests, suite total 466 (was 436 at start of this cycle).

  • tests/marriage-loader.test.ts (10 tests) β€” indexed-slot reading, legacy fallback, partial fields, wikilink unwrap, indexed-wins precedence for #498.
  • tests/event-marker-dedup.test.ts (8 tests) β€” pass-through, single participant, dedup, primary selection, fallback, multi-event, mixed inline+external for #493.
  • tests/place-coordinate-fallback.test.ts (7 tests) β€” own-coords pass-through, parent-by-id / parent-by-name inheritance, grandparent walk, no-ancestor fallback, identity preservation, cycle protection for #494.
  • tests/journey-waypoint-display-label.test.ts (5 tests) β€” customLabel preservation, fallback paths, built-in type ignoring stray customLabel for #499.

Reporters: @DigitalDreamn for #498, #494, #499, #481 follow-up, and continued #487 diagnostic isolation; @doctorwodka closed out #491 verification (tab labels + merge step) and confirmed #496 visual misalignment.

Issues filed during this cycle (post-1.0 / deferred):

  • #497 β€” group_name vs collection discoverability gap on the Person picker; tracked separately from the post-1.0 Collections rework.
  • #500 β€” hierarchical-maps fallback when a child has its own coords on a child map but the user is viewing the parent map; tied to the post-1.0 hierarchical-maps UX work.

Stability-window impact: no reset β€” all six changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Ninth patch (0.22.5 / 0.22.6 / 0.22.7 / 0.22.8 / 0.22.9 / 0.22.10 / 0.22.11 / 0.22.12 / 0.22.13) without a window reset.


v0.22.12 Round-Up: Marriage Symmetry, Universe Rename Cascade, and Map UX Polish (v0.22.12)

Eleven fixes β€” the largest 0.22.x patch and arguably the most consequential. Closes one of the long-standing create/edit asymmetry cases (marriage details mirror between spouses now, sibling pattern with #411 / #415 / #426 / #429 / #478); closes the Universe rename direction end-to-end alongside Part 1 from 0.22.11; and resolves a journey-mode dedup bug that had been silently dropping death popups whenever a custom event sat at the same place. Plus three pieces of map-UX polish, a partial cut at the Person Picker rework, two affordance additions, and a catalog hide for an under-implemented report type. Driven by @DigitalDreamn's diagnostic isolation work on #487 (turned an ambiguous needs-discussion into a real code fix), her reproduction context for #481 and #496, and @doctorwodka's pushback on the original #491 triage that pivoted the picker fixes from "post-1.0 rework" to a 0.22.12 partial.

All eleven changes are non-data-loss. Stability window continues unchanged from 0.22.4 (2026-04-23 β†’ ~2026-05-14) β€” eight patches without a reset.

Fix: Marriage detail fields propagate bidirectionally between spouse notes (#481):

  • src/core/bidirectional-linker.ts β€” the linker mirrored spouse + spouse_id between two notes but skipped the spouseN_marriage_date / _marriage_location / _marriage_location_id / _marriage_status / _divorce_date companion fields. Filling marriage details on one partner's note left the other partner with just the spouse link β€” no date, no place, nothing in the dynamic timeline block for the marriage.
  • syncSpouse now accepts an optional MarriageDetails object extracted from the source's indexed slot via extractMarriageDetails. New findExistingSpouseIndex helper locates the target's slot for already-linked partners by spouseN_id match (with wikilink fallback), so updates propagate on top of an existing link, not just initial fill. writeMarriageDetailsToTarget only writes fields that are SET on the source β€” undefined source fields are left alone on the target so independently-set values survive.
  • Same field set the deletion handler at bidirectional-linker.ts:1473 already enumerated; coverage is now consistent in both directions. Reported by @DigitalDreamn after noticing Shmi's timeline didn't include her marriage to Cliegg even though Cliegg's note had it.

Fix: Universe note rename cascades to universe: references on referencing entity notes (#488 Part 2):

  • src/universes/services/universe-service.ts β€” new cascadeUniverseRename(oldBasename, newBasename) iterates all markdown files, filters to cr_type of person / place / event / organization, and rewrites universe: plain-string fields that exactly match oldBasename to newBasename. Wikilink-syntax values ([[Old Name]]) are left to Obsidian's native rewrite, and cr_id-based or slug-based references are stable identifiers that don't track the basename. Reloads the universe cache on success so downstream consumers (dropdown, validators) pick up the new state.
  • main.ts β€” registerUniverseRenameHandler subscribes to vault.on('rename') and dispatches the cascade for cr_type: universe files. Critical implementation note: the metadata cache is mid-update during the rename event so getFileCache returns null synchronously, and metadataCache.on('changed') doesn't fire for content-unchanged renames either. Took two failed approaches (synchronous cache read; one-shot changed listener) before landing on the working pattern: read the file content directly via cachedRead and parse cr_type from the frontmatter with a regex.
  • Pairs with Part 1 from 0.22.11 (Edit Person dropdown sources from the universes folder) to close the rename direction end-to-end. The full universe-calendar Phase 2 work (parser-side era awareness, calendar-level current_year) is still post-1.0.

Fix: Journey waypoint dedup key differentiates by event type (#487):

  • src/maps/types/map-types.ts β€” journeyWaypointDedupKey was place-only (id:${placeId} or coords composite). The chronological sort places undated custom events at the end of the life-event run, immediately before death and burial; when their place matched the death's place, the consecutive-dedup loop kept the custom event and silently dropped the death waypoint, leaving no death popup in journey mode.
  • Diagnosed by @DigitalDreamn through methodical isolation on the Lars-family vault: replicated by adding a Backstory event at Tatooine (Cliegg's death location), watching it swallow the death popup, removing the Backstory's location, and confirming the death returned only after a reload. Her bug report turned an initially needs-discussion-labeled issue into a real code fix.
  • Fix folds eventType into the dedup key so same-place-different-type waypoints both survive (death + custom at Tatooine = two waypoints), while same-place-same-type still collapses (multi-residence at one address = one waypoint, preserving #448's dedup intent). Four new tests in tests/journey-waypoint-dedup.test.ts covering the cross-type case and undefined-eventType backward compatibility.

Fix: Burial waypoints honor per-map visibility filter in journey mode (#487 sibling):

  • src/maps/map-data-service.ts β€” the burial inclusion branch in buildJourneyPaths checked the universe filter but skipped the isPlaceVisibleOnMap per-map visibility filter that every other waypoint type (birth / marriage / life events / death) honors. A burial place hidden by per-map filter rules would still produce a journey waypoint on that map, while every other event type for the same person would respect the filter.
  • One-line addition of the missing filter call brings burial behavior in line with the rest of the waypoint-coverage rules. Surfaced while reading the same code path during the dedup-key investigation.

Feat: Map path label outline setting for legibility on colorful or dark backgrounds (#483):

  • New plugin setting pathLabelStroke: 'none' | 'white' | 'black' (default 'none', preserving existing behavior). Lives in the Places section of plugin settings, alongside Heat map intensity. Plumbed through MapSettings β†’ MapView.getMapSettings() β†’ MapController.addPathLabel.
  • When non-none, each label's SVG <text> element gains paint-order: stroke fill plus a 2px stroke in the chosen color, producing a halo effect around the glyphs without changing the path color itself. Applies uniformly to migration paths and journey paths via the shared addPathLabel helper. Reported by @doctorwodka for legibility on busy fictional image-map backgrounds where the default colored text washes out.

Feat: Journey playback control reframed as popup dwell time (#486):

  • The slider previously labeled 1x / 1.5x / 2.5x etc. controlled total step duration, not popup dwell. After the ~1100ms camera fly the popup got whatever was left, so at default 1x (2000ms total) the popup was visible for ~900ms; at the slowest 0.25x setting the popup never opened before the next step fired.
  • Reframed semantically as an explicit dwell-time selector with second labels: 2s / 4s / 6s / 10s, default 4s. Total step interval is now JOURNEY_FLY_MS (1100) + dwellMs, so the popup always gets its full visible time regardless of fly duration. journeyMode.speed renamed to journeyMode.dwellMs; CSS class kept (cr-map-journey-speed) for minimal churn. Tooltip on the button reads "Popup dwell time per step (click to cycle)" so the semantics are discoverable. Reported by @DigitalDreamn during #474 verification.

Fix: Map path labels hide instead of overflowing when zoomed out (#482):

  • src/maps/map-controller.ts β€” at minimum zoom, polylines collapsed to small pixel regions but leaflet-textpath kept rendering the full label text along them, so the text overflowed past the path endpoints, visible as a label "teleporting" to empty space alongside the canvas. The 0.22.11 label-host fix (#472) addressed the multi-segment iteration problem but not this segment-too-short problem.
  • Adds zoom-aware suppression: each path-label registration estimates the rendered text pixel-width and compares to the chosen segment's screen-space length at the current zoom; skips creating the host polyline when the segment can't fit the text. New pathLabelEntries registry tracks every label so a debounced zoomend listener can re-evaluate visibility as the user zooms. Reported by @doctorwodka.

Feat: Person picker uses collection names and merges shared-name components (#491 β€” partial):

  • src/ui/person-picker.ts β€” the picker labeled every tab as Family ${index + 1} regardless of whether a connected component's members shared a group_name, and treated each disconnected graph component as its own tab even when multiple components shared a collection name. Player groups spanning unrelated characters appeared as N separate "Family" entries.
  • Picker now uses component.collectionName for the tab label when every member shares one (falls back to Family N otherwise), and merges components with the same collectionName into a single tab. Merge logic extracted to src/core/family-component-merge.ts with 10 regression tests covering empty input, pass-through of unnamed components, merge-by-name, mixed named/unnamed, sort-by-size-after-merge, representative selection rules, and input immutability.
  • Partial cut from a larger Collections rework that's still post-1.0. Reported by @doctorwodka who pushed back on an initial mistaken triage that asserted the picker already used named user collections β€” verifying her observation in code surfaced both this gap and the type-narrowing that was discarding the collectionName data the graph service already computed.

Feat: "Manage memberships..." affordance on the person side (#490):

  • src/plugin/context-menus.ts β€” ManageOrganizationMembersModal was reachable from three places, all org-side. Editing a person's membership required navigating to the organization first β€” non-obvious enough that @DigitalDreamn surfaced the discoverability gap in discussion #484 after spending ~2 hours hunting for the path.
  • Adds Manage memberships... to the person context menu (desktop submenu under Charted Roots plus the mobile flat menu for parity). Helper openManageMembershipsForPerson handles three cases: 0 memberships β†’ notice; exactly 1 β†’ opens the modal directly scoped to that org; 2+ β†’ opens a FuzzySuggestModal picker scoped to the person's orgs and routes the modal to the chosen one.

Fix: Pixel coordinates X and Y rows align vertically in the Create / Edit Place modal (#496):

  • styles/place-modals.css β€” the X and Y inputs sit in a flex row (.crc-coord-inputs) but the parent had no align-items rule, defaulting to stretch. Each .setting-item flex child layouts its inner label + input independently; depending on Obsidian's default .setting-item flex behavior, the rows could end up on slightly different vertical baselines.
  • One-line fix: add align-items: center to the flex container so both rows share a vertical center regardless of internal layout. Reported by @DigitalDreamn with a screenshot showing the misalignment.

Fix: Fan chart PDF report option hidden from the catalog until a real renderer lands (#492):

  • src/reports/types/report-types.ts β€” fan-chart-pdf was registered in the catalog UI (report wizard, trees tab, book builder) but UnifiedTreeWizardModal rewrites tree type 'fan' to 'ancestors' before tree generation, and there is no fan-specific layout downstream. The catalog entry was a registered surface without an implementation: selecting it produced an ancestor pedigree tree instead of the promised semicircular fan chart.
  • Adds an optional hidden?: boolean flag on ReportMetadata; getReportsByCategory and the trees-tab visual-trees iteration both honor it. The option will be re-exposed when the fan-layout renderer is implemented. Surfaced during the v0.22.12 Reports gallery media capture session when the Victoria fan-chart PDF rendered as a tree.

Changed: Journey playback preserves the user's zoom level between steps:

  • src/maps/map-view.ts β€” each step previously called flyTo(target, 12, { duration: 1 }) β€” a hardcoded zoom level that yanked the camera down to zoom 12 on every step regardless of how the user had framed the journey. On a wide journey (continental, multi-system fictional), this produced a jolting zoom-in between each waypoint. The step now passes map.getZoom() instead, so the user's framing β€” set by fitBounds on entry to journey mode and adjustable via manual zoom mid-playback β€” is respected throughout playback.

Testing: 14 new tests, suite total 436 (was 422 at start of this cycle).

  • tests/journey-waypoint-dedup.test.ts (+4 tests) β€” eventType-in-key behavior for #487 (cross-type at same placeId, same-type still collapses, undefined-eventType consistency, cross-type at coord-fallback path).
  • tests/family-component-merge.test.ts (10 tests) β€” merge-by-collectionName regression coverage for #491 (empty input, pass-through, merge cases, sort-by-size, representative selection, input immutability).
  • #481 / #483 / #486 / #488 Part 2 / #490 / #492 / #496 β€” manually verified by reporter and/or developer; full test coverage out of scope for the rename event hook and CSS visual regressions.

Reporters: @DigitalDreamn for #481, #487, #488 Part 2, #496 (and #487's diagnostic isolation that turned needs-discussion into a real code fix); @doctorwodka for #483, #486, and the #491 picker pushback; @doctorwodka also confirmed #482 verification feedback.

Stability-window impact: no reset β€” all eleven changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Eighth patch (0.22.5 / 0.22.6 / 0.22.7 / 0.22.8 / 0.22.9 / 0.22.10 / 0.22.11 / 0.22.12) without a window reset.


v0.22.11 Round-Up: Path Label Architecture, Person-Delete Hardening, and Universe Dropdown (v0.22.11)

Six fixes β€” three follow-ups to recent patches plus three new bugs. All non-data-loss. The #442 follow-up effectively closes #478 (the broken-wikilink-on-save empty-string injection) by removing the trigger condition: wikilinks now sweep at delete time alongside the existing _id cleanup, plus a latent scalar-form bug in the original 0.22.7 cleanup is fixed for only-child / only-spouse cases. #485 closes the lat/lng-only-on-pixel-CRS cluster that ran across #448 / #474 β€” all three journey-mode sites now use pixel coordinates correctly on CRS.Simple image maps. Reporter mix: @DigitalDreamn for #472 follow-up + #442 follow-up + #485 (via #474) + #488 Part 1; @doctorwodka for #476 follow-up; @Lemmeron for #489 (first contribution).

All six changes are non-data-loss. Stability window continues unchanged from 0.22.4 (2026-04-23 β†’ ~2026-05-14).

Fix: Map path labels render upright on multi-waypoint paths via single-segment label-host (#472 follow-up):

  • src/maps/map-controller.ts β€” the 0.22.10 longest-segment heuristic still left labels upside-down on some multi-waypoint paths, most visibly when two characters' journey paths shared the same visual segment in opposite traversal directions. Root cause: leaflet-textpath repeats the label along the entire polyline path and applies a single global 180Β° rotation when orientation: 'flip' is set. A path that bends in different screen-space directions can't be made upright everywhere by a single rotation β€” at least one segment will always render the label backward.
  • Structural fix: render labels on a separate invisible "label-host" polyline that covers only the longest screen-space segment of the source polyline, with opacity: 0 and weight: 0 so the line itself doesn't draw. The visible polyline keeps its full multi-waypoint shape; the label renders once per polyline on its longest segment with the correct flip decision. shouldFlipPathLabel is replaced by findLongestScreenSegment and createPathLabelHost. Supersedes the longest-segment-as-flip-heuristic from 0.22.10.

Fix: Person delete cleanup sweeps wikilinks and handles single-relationship scalars (#442 follow-up):

  • src/core/person-delete-cleanup.ts β€” two gaps in the original 0.22.7 cleanup, both surfaced by @DigitalDreamn's verification on the Lars / Star Wars fixture. (1) The cleanup planner swept *_id arrays but not the parallel wikilink-bearing relationship fields (father, children, step_child, etc.) β€” the original implementation delegated wikilink cleanup to Obsidian's native rewriting, but Obsidian only rewrites wikilinks on rename, not on delete. Deleted persons left broken [[placeholder]] links in referencing notes' frontmatter. (2) The "array" relationship fields are de-facto polymorphic: YAML serializers emit a scalar string when the field has a single element. The original cleanup's array-only branches silently skipped the scalar form, so deleting an only-child / only-step-child / only-spouse left the parent's children_id pointing at the dead cr_id.
  • Fix extends the planner with a parallel wikilink-sweep branch (gated on the deleted file's basename) and converts every "array" field handler to accept both array and scalar shapes. Wikilink matching handles path-prefixed, display-aliased, heading-anchored, block-anchored, and .md-extensioned forms; comparison is case-insensitive. 24 new tests across scalar/array shapes, wikilink format normalization, polymorphic spouse handling, and combined sweeps.
  • Effectively closes #478 by removing the trigger condition: the broken-wikilink + empty-string injection chain that #478 documented now can't fire because there are no broken wikilinks left after delete. The save-time injection path itself remains unmodified.

Fix: Negative birth years preserve sign when followed by a letter suffix (#476 follow-up):

  • src/dates/services/date-service.ts β€” @doctorwodka's 0.22.10 verification showed the fix worked for -90 but not for the original DE -5740ish repro. The 0.22.10 standalone-negative regex required a trailing \b (word boundary) after the captured digits, which fires correctly between a digit and whitespace / punctuation / end-of-string but NOT between a digit and a letter β€” both are word chars, so no boundary. Without the boundary, suffixed forms like DE -5740ish failed the negative-detection branch entirely and fell through to the bare-digits fallback, which strips the sign.
  • Replaced the trailing \b with (?=$|[^0-9]) lookahead so the negative-detection branch matches against any non-digit terminator. Also removed the duplicate extractYear implementation in the relationships renderer (a pre-existing bare \b(\d{4})\b regex that would have rendered any negative 4-digit year as positive in the relationships block β€” same class of bug as #476, on a different surface) and routed both call sites through the shared service helper. 3 new regression tests for the suffix case, the punctuation case, and the no-over-match case.

Fix: Journey mode no longer briefly frames the bottom-left corner of CRS.Simple maps before flying to the first waypoint (#485):

  • src/maps/map-view.ts β€” applyJourneyFilter's initial fitBounds call read marker.lat / marker.lng only when computing the journey's framing rectangle. Pixel-coord places default lat/lng to 0, so on a custom image map (CRS.Simple / pixel coordinates) the bounds collapsed around (0, 0) β€” the bottom-left corner of the image β€” and the camera framed there briefly before panToWaypoint flew to the actual first waypoint. Reported by @DigitalDreamn during #474 verification.
  • Third site in the #448 / #474 cluster (journey-path build dedup, camera fly-to and popup placement, now journey-mode framing β€” all the same lat/lng-only-on-pixel-CRS bug class). Fix mirrors #474's pattern: detect pixel CRS via mapController.getCurrentCRS() === 'pixel', build bounds from each marker's [pixelY, pixelX] when available on pixel maps and from [lat, lng] otherwise. Geographic maps unaffected. Cluster fully closed.

Feat: Edit Person modal's Universe dropdown reflects renamed and newly-created Universe notes (#488 Part 1):

  • src/ui/control-center.ts β€” getCachedUniverses previously sourced the dropdown from the distinct universe: field values found across person and place notes, not from the actual Universe notes in the universes folder. Renaming a Universe note (e.g. Star Wars β†’ Star Wars (AU)) left the dropdown showing the old name because no person/place file had been updated to reference the new name yet, and a freshly-created Universe note was absent from the dropdown until the first character was assigned to it. Reported by @DigitalDreamn.
  • Fix unions UniverseService.getAllUniverses() (the authoritative universe-notes folder) into getCachedUniverses alongside the existing person-graph and place-graph extractions, so renamed and newly-created Universe notes appear in the dropdown immediately. Doesn't touch the deeper rename-cascade question β€” referencing notes still hold the old name in their universe: field until a future bidirectional rename-handler updates them. Tracked as #488 Part 2 (closed in 0.22.12).

Fix: Custom non-person cr_type notes no longer appear as people (#489):

  • src/core/family-graph.ts β€” a note with cr_type: hex (or any user-defined type the plugin doesn't know about) was being treated as a person and listed in the control center's Person notes browser. FamilyGraphService.extractPersonNode ran an exclusion list β€” checking for the known non-person types (source, event, place, organization, proof_summary, universe, citation) and treating any cr_id-bearing note that didn't match as a person. User-defined custom types like hex or faction fell through every exclusion and got coerced into people.
  • Fix adds an explicit isPersonNote inclusion check after the exclusion list. isPersonNote already handles the unknown-cr_type case correctly (returns false when cr_type is set to anything other than person), while preserving the legacy "cr_id with no cr_type β†’ treat as person" behavior for older vaults that pre-date strict type-tagging. Reported by @Lemmeron β€” first contribution.

Testing: 32 new tests across this cycle, suite total 422 (was 390 at start).

  • tests/person-delete-cleanup.test.ts (+24 tests) β€” wikilink sweep across path-prefixed / display-aliased / heading-anchored / block-anchored / .md-extensioned forms, scalar-form polymorphic field handling, combined sweeps. #442 follow-up.
  • tests/date-service-extract-year-suffix.test.ts (+3 tests) β€” suffix case, punctuation case, no-over-match case for #476 follow-up.
  • tests/family-graph-person-detection.test.ts (+5 tests) β€” isPersonNote inclusion check for #489.
  • #472 / #485 / #488 β€” manually verified by reporter; Leaflet map mocking out of scope.

Reporters: @DigitalDreamn for #472 follow-up, #442 follow-up, #485 (via #474), and #488 Part 1; @doctorwodka for #476 follow-up; @Lemmeron for #489 (first contribution from this reporter).

Stability-window impact: no reset β€” all six changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Seventh patch (0.22.5 β†’ 0.22.11) without a reset.


v0.22.10 Round-Up: Negative Years, Modal Polish, Calendar Era Input, and Auto Regression (v0.22.10)

Five fixes β€” three new bugs and two follow-ups. Mix of new diagnostics from @DigitalDreamn (#459 follow-up modal overflow, #472 follow-up label flip refinement, #477 console-error regression caught and fixed within 24 hours of 0.22.9 ship), @doctorwodka's standalone-negative regex (#476), and a self-reported Calendar View era-input issue (#480). The 0.22.9 #472 fix introduced the #477 regression by passing leaflet-textpath an undocumented 'auto' orientation value that fell through and got injected literally into the SVG transform attribute β€” caught quickly via console-error spam reports and reverted to the documented behavior.

All five changes are non-data-loss. Stability window continues unchanged from 0.22.4 (2026-04-23 β†’ ~2026-05-14).

Fix: Calendar View accepts era-suffixed years and round-trips them (#480):

  • src/calendar/calendar-view.ts β€” the year-input field was a <input type="number"> with a min="0" constraint, so users on fictional-era universes (Star Wars BBY/ABY, Middle-earth TA/SA, etc.) couldn't enter their era-formatted year strings. Constraint relaxed and input changed to type="text". Change handler now routes through DateService.parseDate, accepting era-suffixed strings ("82 BBY", "1499 ABY", "30 AC"), ISO dates, and bare signed integers. When the parser resolves a fictional date, the system's universe is recorded alongside the canonical year (new currentYearUniverse field) so renders β€” including month-boundary rollovers β€” format via formatCanonicalYear. Persisted state gains a yearUniverse field so era context survives view reopens.
  • The year > 0 constraint was the visible blocker but the deeper issue was the Calendar View having its own year-rendering surface that wasn't era-aware β€” same DateService-bypass class as the cluster that ran 0.22.5 β†’ 0.22.9, just on a different surface. Self-reported during the Calendar View capture session for the website-features-page media program.

Fix: Create Place modal overflow and coord input width (#459 follow-up):

  • styles/place-modals.css β€” @DigitalDreamn re-verified after 0.22.8 and reported inputs still disappearing under the scroll bar. Diagnosis: 0.22.8's 220px input-width fix gave consistent widths but didn't address the underlying flex-shrink: 0 on .crc-create-place-modal .setting-item-info. On narrow viewports descriptions held natural width and pushed the control column past the modal's right edge. Also during dev verification, the 220px rule was cascading into the Latitude/Longitude inputs (which have their own width: 100% rule) and winning on specificity, leaving 220px lat/long inputs with empty space to the right.
  • Two fixes in this commit: drop flex-shrink: 0 (info column shrinks, descriptions wrap, controls stay in frame) and bump .crc-coord-inputs .setting-item-control input[type="text"] specificity to win the source-order tie. Built and verified in dev vault.

Fix: Use longest segment for map path label flip decision (#472 follow-up):

  • src/maps/map-controller.ts β€” @DigitalDreamn verified 0.22.9's chord-based shouldFlipPathLabel heuristic. Result: previously-flipped line now correct, but a different diagonal still upside-down. The chord-based heuristic was too coarse for multi-waypoint paths where chord direction disagrees with the segment where the label actually renders.
  • Refinement: helper now finds the longest segment in screen-space and uses its direction. Falls back to chord behavior naturally for 2-point paths. Also defensively flattens getLatLngs() for the LatLng[][] multi-polyline case. Closing more cases but not all β€” the structural fix follows in 0.22.11 (#472 final).

Fix: Negative birth years preserve sign in extractYear (#476):

  • src/dynamic-content/services/dynamic-content-service.ts β€” extractYear had four sign-bearing branches but no rule for custom-era formats with explicit standalone minus signs. The 4-digit-year regex \b(\d{4})\b matched the digits and dropped the leading - because \b (word boundary) eats it.
  • New (?:^|[^0-9])-(\d+)\b branch captures standalone negatives while excluding ISO date separators (where the hyphen is between digits). 5 new tests in tests/date-helpers.test.ts. Reported by @doctorwodka β€” first contribution from this reporter.

Fix: Stop passing 'auto' orientation to leaflet-textpath (#477):

  • src/maps/map-controller.ts β€” @DigitalDreamn reported a wall of console errors on 0.22.9: setText errors with transform="rotate(auto cx cy)". Sharper bug than the #472 follow-up could fix: [email protected] only recognizes 'flip' (180Β°), 'perpendicular' (90Β°), or numeric rotations β€” 'auto' is undocumented and falls through, getting injected literally into the SVG transform. Browser rejects each one as invalid. 0.22.9 regression caused by the #472 fix's misunderstanding of leaflet-textpath's API.
  • Fix omits the orientation key entirely when no flip is needed (per leaflet-textpath line 128 source if (options.orientation), undefined skips the rotation block). Side effect: paths the helper correctly identifies as not-needing-flip now render cleanly instead of relying on the malformed transform β€” some of #472's remaining upside-down cases may resolve here too. Caught and fixed within 24 hours of 0.22.9 ship.

Testing: 8 new tests across this cycle, suite total 390 (was 382 at start).

  • tests/date-helpers.test.ts (+5 tests) β€” standalone-negative regex for #476.
  • tests/calendar-view-era-input.test.ts (+3 tests) β€” era-suffixed parsing for #480.
  • #459 / #472 follow-up / #477 β€” manually verified by reporter.

Reporters: @DigitalDreamn for #459 follow-up, #472 follow-up, and #477 (caught the regression within 24 hours); @doctorwodka for #476 (first contribution); self-report for #480.

Stability-window impact: no reset β€” all five changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14. Sixth patch (0.22.5 β†’ 0.22.10) without a reset.


v0.22.9 Round-Up: Map Polish, Sibling Reality Windows, and Cluster Closure (v0.22.9)

Five fixes β€” all non-data-loss, mostly map and timeline polish on top of 0.22.8. Closes the DateService-bypass cluster that ran across 0.22.5 / 0.22.6 / 0.22.7 / 0.22.8 / 0.22.9 (eight subsystems total, all era-aware now); adds a third site to the reality-window cluster (sibling-births before focal birth); and wraps up the pixel-coord coverage gaps opened by #448 (build-path), with #472 (label flip) and #474 (camera fly-to) closing the follow path. Driven primarily by @DigitalDreamn's continued vault testing on the Lars / Star Wars fixture (four of five fixes); the fifth surfaced from her #464 investigation thread.

All five changes are non-data-loss. Stability window continues unchanged from 0.22.4 (2026-04-23 β†’ ~2026-05-14).

Fix: Map time slider derives its year range from data and renders era-formatted labels (#453):

  • src/maps/map-view.ts β€” slider min/max attrs now derive from MapData.yearRange (which already returns canonical signed years for fictional eras via #454). Previously hardcoded to 1800 / 2000, making the feature unusable on fictional-era universes β€” the slider's range never intersected the data.
  • src/dates/services/date-service.ts β€” new formatCanonicalYear(year, universe?) helper inverts canonical years back to era-formatted strings ("82 BBY", "5 ABY") for slider min/max labels and the current-year display. Real-world dates and unconfigured universes fall back to String(year).
  • Eighth and final site of the DateService-bypass cluster that ran across 0.22.5 β†’ 0.22.9 (data-quality validator, map popup ages, data-quality date inconsistencies, timeline ages, map marker popups, map year extraction, statistics dashboard, and now the time slider).
  • New test file tests/date-service-format-canonical-year.test.ts (10 tests) covers era-formatted inversion across BBY / ABY / TA / SA, real-world fallback, and the universe-unconfigured path.

Fix: Older siblings' births no longer appear before the focal person's birth on their timeline (#469):

  • src/dynamic-content/renderers/timeline-renderer.ts β€” the sibling-births block in gatherFamilyEvents had no reality-window guard for events predating the focal person's birth. An older sibling's birth would render as the first entry on the focal person's own timeline β€” PadmΓ© Naberrie's timeline showed her older sister Sola's birth (50 BBY) above PadmΓ©'s own birth (46 BBY), exposing the inconsistency.
  • New isEventBeforeFocalBirth(focalBirthDate, eventDate, universe) helper symmetric to #457's isEventAfterFocalDeath. Same-year siblings (twins, close births) still surface β€” the guard fires only on unambiguous before-focal-birth via DateService.getCanonicalYear, so fictional descending eras (BBY) compare correctly. No focal birth date allows everything (current behavior for unknown-birth focal persons).
  • Third site of the reality-window cluster alongside #456 (step-sibling filter) and #457 (after-focal-death guard for spouse + parent surfaces). The pattern across all three: same gatherFamilyEvents block, two symmetric helpers (isEventBeforeFocalBirth / isEventAfterFocalDeath).
  • Added 4 tests to tests/timeline-reality-window.test.ts mirroring the existing #457 suite (signed-year arithmetic, same-year admit, no-birth admit-all).

Fix: Map path labels read consistently upright across CRS.Simple image maps and diagonal lines (#472):

  • src/maps/map-controller.ts β€” leaflet-textpath's 'flip' orientation mode picks rotation directly from latlng coordinates, which produces inconsistent results on diagonal lines and on CRS.Simple image maps where coordinate orientation differs from screen orientation. Some labels rendered upright, others upside-down on the same map.
  • New shouldFlipPathLabel(polyline) helper computes the path's overall direction in screen-space (after CRS projection via latLngToLayerPoint) and chooses 'flip' or 'auto' accordingly. Both migration-path and journey-path label callsites now use it. The pre-existing leaflet-textpath flip remains a fallback for the unlikely case where the map ref isn't yet wired.
  • Pixel-coord coverage closure (alongside #448 journey-path build dedup and #474 camera fly-to below). Surfaced once #448 unblocked pixel-coord journey rendering and made the label feature visible for the first time on @DigitalDreamn's Star Wars galaxy fixture.

Fix: Journey-mode camera flies to pixel-coord waypoints on CRS.Simple image maps (#474):

  • src/maps/map-view.ts β€” panToWaypoint only consulted waypoint.lat / waypoint.lng for the camera fly-to and rich-popup placement. Pixel-coord places default lat/lng to 0 (per the journey waypoint construction), so the camera flew to (0, 0) β€” bottom-left corner of CRS.Simple β€” and the popup opened there too, even though the popup content correctly named the right place.
  • Now uses [pixelY, pixelX] when on pixel CRS and the waypoint has pixel coords; falls back to [lat, lng] for geographic maps. Same coord-system-aware pattern as #448, which fixed the journey-path build path; this fixes the camera-follow path. Pixel-coord coverage closure round-three.
  • Reported by @DigitalDreamn during #434 verification, after #448 unblocked pixel-coord journeys and made the camera-mismatch newly visible. Manually verified against her vault; no new tests since Leaflet map mocking is out of scope for the current harness.

Fix: Place notes silently excluded from the place graph now log a warning (#471):

  • src/core/place-graph.ts β€” PlaceGraphService.extractPlaceNode early-returns when a place-shaped note lacks a cr_id, so such notes never enter placeCache. By-name lookups (getPlaceByName), Create Place modal's parent dropdown, map markers, and downstream consumers can't see them. The exclusion was completely silent β€” no log, no UI surface, no data-quality flag.
  • Added a warn-level log on the skip with the file path so the exclusion is now discoverable from the dev console. A follow-up data-quality wizard check that surfaces and offers to fix missing-cr_id places is queued for a later cycle.
  • Surfaced during the #464 investigation as one of the candidate root causes for "by-name lookup returned undefined for a place that exists in the vault" β€” Coruscant in @DigitalDreamn's vault was created via the Organizations flow on April 8th and may not have generated a place-side cr_id at that time.

Testing: 14 new tests across two files, suite total 390 (was 376 at start of this cycle).

  • tests/date-service-format-canonical-year.test.ts (10 tests) β€” era-formatted label inversion for #453.
  • tests/timeline-reality-window.test.ts (+4 tests) β€” before-focal-birth guard for #469 (added to the existing file).
  • #471 / #472 / #474 β€” manually verified by reporter; Leaflet map mocking and console-spy harnesses out of scope for the current vitest setup.

Reporters: @DigitalDreamn for #453, #469, #472, #474 (four of five, all from continued Lars / Star Wars vault testing); the #471 diagnostic surfaced from her #464 investigation thread.

Stability-window impact: no reset β€” all five changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14.


v0.22.8 Round-Up: Map, Timeline, Statistics, and Modal Polish (v0.22.8)

Nine fixes addressing eleven issues β€” biggest 0.22.x patch, all non-data-loss. Driven mostly by @DigitalDreamn's continued vault testing on the Lars / Star Wars fixture (eight of eleven), with @doctorwodka contributing the marriage-statistics report (first contribution). Pattern of the cluster: every fix is a parallel surface or write-path gap that a previous round-up partially closed. The DateService-bypass cluster claimed two more sites (#454 map year extraction, #437 follow-up Statistics Dashboard counter) β€” count is now seven distinct subsystems where era-naive year extraction silently broke fictional dates. Create/edit asymmetry pattern surfaced again in the Place modal (#463). Marker color and popup label gaps fell out of #438's verification.

All eleven changes are non-data-loss. Stability window continues unchanged from 0.22.4 (2026-04-23 β†’ ~2026-05-14).

Fix: Statistics Dashboard date-inconsistency counter respects fictional eras (#437 follow-up):

  • src/statistics/services/statistics-service.ts β€” extractYear now defers to DateService.parseDate first when a fictional calendar resolves the input. Constructor takes an optional plugin ref to reach the date service; both production callers (statistics-tab.ts, statistics-view.ts) updated to pass it. Falls through to the existing regex for real-world dates.
  • The 0.22.6 fix routed data-quality.ts's parseYear through DateService for fictional-era awareness, but this Statistics Dashboard surface is a separate code path with its own extractYear regex. For BBY descending eras, the digit-run got read as a positive number (1045 BBY β†’ 1045) and the naive birthYear > deathYear check fired on coherent lifespans. Same fix shape as #437 / #454; seventh DateService-bypass site surfaced and fixed since the cluster started.

Fix: Map year extraction respects fictional eras (#454):

  • src/maps/map-data-service.ts β€” MapDataService.extractYear previously required a 4-digit numeric year, so fictional-era timestamps under 1000 like 82 BBY / 41 BBY parsed as undefined. Broke chronological sort on fictional-era timelines, dropped events from year-range filters, and downstream contributed to the "no journey path built" symptom on fictional-calendar maps before #448 was identified as the actual root cause. extractYear now defers to DateService.parseDate first, picking up the canonical signed year (negative for descending eras) so existing numeric comparisons stay coherent. Falls back to the legacy 4-digit regex when DateService isn't available.
  • Sixth DateService-bypass surface (alongside #433, #434, #437, #439, #444); paired with #437 follow-up above to bring the count to seven.

Fix: Marriage statistics respect the fictional-dates age cap (#458):

  • src/statistics/services/statistics-service.ts β€” two marriage-stat surfaces had a hardcoded <= 80 upper bound that bypassed the maxAge getter the rest of the engine uses. With enableFictionalDates on, maxAge returns Infinity to admit long-lived characters; the hardcoded 80 silently dropped marriages over that threshold. getMarriagePatternAnalysis (age at first marriage) and computeLongestMarriages (marriage duration) now both defer to this.maxAge. Real-world cap widens 80 β†’ 120 to match the lifespan cap used elsewhere.
  • New test file tests/statistics-marriage-age-cap.test.ts (6 tests) covers the cap behavior across both surfaces and fictional/real-world modes.

Fix: Map journey paths build correctly for pixel-coord places (#448):

  • src/maps/map-data-service.ts β€” buildJourneyPaths previously deduped consecutive waypoints by comparing lat and lng only. On custom image maps, every pixel-coord place uses pixel_x / pixel_y and defaults lat / lng to 0, so all pixel-coord waypoints shared (0, 0) and the dedup collapsed all of them into a single entry. The "β‰₯ 2 unique waypoints" check then failed silently, and journey playback was unavailable for fictional-map vaults regardless of how many places were authored.
  • src/maps/types/map-types.ts β€” new journeyWaypointDedupKey helper prefers placeId when available and falls back to a composite of both coordinate systems so pixel-coord and geographic places no longer collide on each other's defaults.
  • Was the actual root cause of "no play button on Cliegg Lars" through the entire #434 thread. The #445 placeholder correctly diagnosed "no journey built" but the underlying cause was dedup collapse, not insufficient input.

Fix: Custom-relationships overlay arcs paint on top of family-link layer in the typical case (#450):

  • src/family-tree/family-chart-view.ts β€” the original "always paint under family links" decision from #386 was meant to protect structural lines from being occluded by heavy overlay stacks (3+ arcs on a single endpoint pair). But it also hid the typical case where only one or two non-stacked arcs exist β€” overlay arcs disappeared behind family links instead of layering over them. Renderer now paints the overlay group ON TOP of links_view by default and falls back to the original "under" behavior only when at least one endpoint pair has 3+ overlay arcs stacked on it (preserves the #386 heavy-stack guarantee).
  • New shouldPaintOverlayUnderLinks(maxArcStackDepth) helper in src/family-tree/family-chart-overlay-z.ts returns true only when depth β‰₯ 3.
  • Surfaced during a Custom Relationships Overlay motion-capture demo setup; the visual problem made the demo not worth recording until the z-order was fixed.

Fix: Timeline filters relative events outside the focal person's reality window (#456, #457):

  • src/dynamic-content/renderers/timeline-renderer.ts β€” two related leaks where the timeline surfaced events that didn't fit the focal person's lived experience. Step-siblings' births appeared on each other's timelines because the sibling-iteration walked each parent's childrenCrIds without distinguishing biological from step-children β€” Anakin's timeline showed Owen's birth even though they share only a stepparent (#456). Spouse deaths surfaced on the survivor's timeline even when the survivor pre-deceased the spouse β€” Shmi's timeline showed Cliegg's death even though Shmi died first (#457).
  • Paired fix: step-sibling filter mirroring the #441 stepchild treatment (skip any id present in any parent's stepchildrenCrIds from the sibling-births iteration), plus a new isEventAfterFocalDeath helper using DateService.getCanonicalYear so fictional descending eras (BBY) compare correctly. Audit covered parent deaths in the same pass β€” those now also skip when the parent died after the focal person did. Same-year events allowed (intra-year ordering unknown). No death date on focal person allows everything (current behavior for living persons).
  • New test file tests/timeline-reality-window.test.ts (8 tests) covers the step-sibling filter and the focal-death guards across spouse and parent surfaces.

Fix: Person-delete cleanup now sweeps the step_child_id field on stepparents' notes (#442 follow-up):

  • src/core/person-delete-cleanup.ts β€” the cleanup planner shipped in 0.22.7 listed stepchild_id (no underscore) in its scan list, but the bidirectional-linker β€” the only code that writes the stepchildβ†’stepparent reverse-link onto a stepparent's frontmatter β€” uses step_child_id (underscore between step and child). Two parallel hardcoded lists never matched, so deleting a person who was a stepchild on someone else's note left their cr_id stranded in the stepparent's step_child_id array even though every other relationship array got cleaned. Renamed the entry to step_child_id so the cleanup matches the field the rest of the plugin actually writes; the dropped stepchild_id form was a phantom, never written by anything.

Fix: Create Place modal recognizes parents created earlier in the same session and writes their cr_id (#463, #464):

  • src/ui/create-place-modal.ts β€” two bundled bugs sharing a stale placeGraph cache root cause. PlaceGraphService.ensureCacheLoaded only loads when the cache is empty, so newly-created place notes weren't visible to subsequent Create Place modal invocations in the same session. Symptoms: typing an existing parent's name produced a spurious " doesn't exist" auto-create prompt (#464); saving anyway wrote only the parent_place wikilink without the companion parent_place_id, leaving a dual-storage half-write that only resolved after a subsequent Edit + Save round trip (#463).
  • Two-part fix: (1) refresh the cache when the modal opens so the dropdown sees the current vault state; (2) in checkForMissingParent, when a typed parent name matches an existing place via getPlaceByName, populate parentPlaceId and parentPlace from the resolved node and clear pendingParentPlace before the auto-create branch fires.
  • Same class as the create/edit asymmetry meta-pattern called out in #411: the Edit modal's load+save round trip eventually resolves parent_place_id correctly via the place graph, but the Create write-path skipped the companion field write.

Fix: Map popups for custom (cr_type: event) markers surface the original event type (#466):

  • src/maps/types/map-types.ts β€” LifeEvent and MapMarker both gain customLabel?: string. Carries the raw user-authored event type (or event-note title) when the resolved MarkerType collapses to custom.
  • src/maps/map-data-service.ts β€” parseEventsArray (inline events on the person's frontmatter) and loadExternalEventsForPerson (external cr_type: event notes) populate customLabel from the raw event_type or the EventNote.title when the resolved type is custom. Marker construction threads the field through.
  • src/maps/map-controller.ts β€” createPopupContent uses customLabel (capitalized) as the popup type label when data.type === 'custom' and a label is available; falls back to the literal Custom: only when neither raw event_type nor title exists. Built-in event-type popups (birth, death, marriage, etc.) are unchanged.
  • New test file tests/map-popup-custom-label.test.ts (3 tests) fences the customLabel propagation.

Fix: Custom event marker color is now visually distinct from death event markers (#465):

  • src/maps/types/map-types.ts and src/maps/map-view.ts β€” both hardcoded defaults for customMarkerColor changed from pink (#ec4899) to yellow (#eab308). Pink sat too close to death red (#ef4444) on dense maps, making custom event markers hard to distinguish from death markers when scanning.
  • Map color settings aren't user-persisted yet (per the TODO in getMapSettings), so this applies on next plugin load with no migration needed.

Fix: Create Place modal text inputs render at consistent widths (#459):

  • styles/place-modals.css β€” text inputs in the Create Place modal now render at a fixed 220px width regardless of description length. Without this, rows with long descriptions (Universe, Parent place) had narrower inputs than rows with short descriptions (Name, Aliases, Collection) because the existing flex-shrink: 0 rule on .setting-item-info meant the description column couldn't yield space back to the control column.

Fix: Wikipedia clipper template renders infobox photos correctly in Obsidian (#440):

  • docs/clipper-templates/wikipedia-biography-basic.json β€” protocol-relative image URLs (<img src="//upload.wikimedia.org/...">) preserved in the infobox HTML can't be resolved by Obsidian's app:// renderer, so infobox photos rendered as broken-image icons in reading mode. Added a replace filter to the selectorHtml:.infobox extraction that rewrites ="// to ="https:// so the preserved HTML carries valid absolute URLs.
  • Re-import the template in Web Clipper to pick up the fix; not part of a versioned plugin release since clipper templates ship via docs/clipper-templates/.

Testing: 33 new tests across five files for a suite total of 376 (was 343 at start of this cycle).

  • tests/map-data-service-extract-year.test.ts (10 tests) β€” fictional-era support and the legacy regex fallback for #454.
  • tests/statistics-marriage-age-cap.test.ts (6 tests) β€” marriage age and longest-marriages caps for #458.
  • tests/timeline-reality-window.test.ts (8 tests) β€” step-sibling filter and focal-death guards for #456 / #457.
  • tests/map-popup-custom-label.test.ts (3 tests) β€” customLabel propagation for #466.
  • tests/statistics-extract-year-fictional.test.ts (6 tests) β€” DateService routing for #437 follow-up.

Reporters: @DigitalDreamn for #437 follow-up, #442 follow-up, #448, #450 (overlay observation), #454, #456, #457, #459, #463 + #464, #465, #466 (eight of eleven, all from continued Lars / Star Wars vault testing). @doctorwodka for #458 (first contribution).

Stability-window impact: no reset β€” all eleven changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14.


v0.22.7 Round-Up: Map UX, Stepchild Handling, and Universe-Calendar Linking (v0.22.7)

Seven changes shipped in one bundle, six of them follow-ups from @DigitalDreamn's testing on her Star Wars / Lars-family vault. Three came from the post-#434 reply chain after she walked through the marker popup, fullscreen control, and journey-mode UX (#444, #445, #446). Three more fell out of the #439 timeline-fix verification when stepchildren and orphaned cr_ids surfaced in the same screenshots (#441, #442, #443). Plus the #432 Phase 1 universe-calendar wiring landed (the contract decision recorded earlier in the v0.22.5 cluster), and timelineShowSpouseDeaths flipped on by default (#447) so widow/widower context surfaces without setting discovery.

All seven are non-data-loss. Stability window continues unchanged from 0.22.4 (2026-04-23 β†’ ~2026-05-14).

Feature: Universe β†’ default calendar wiring (#432 Phase 1):

  • src/universes/ui/universe-wizard.ts β€” wizard step 2 now offers a three-way picker (None / Built-in / Custom) replacing the binary "create custom calendar?" toggle. Built-in mode shows a dropdown of DEFAULT_DATE_SYSTEMS. When the universe name slug-matches a built-in's universe field, that built-in is preselected (Star Wars β†’ Galactic Standard, Middle-earth β†’ Middle-earth Calendar, "Star Wars Legends" via slug-superset).
  • src/universes/ui/edit-universe-modal.ts β€” new Calendar field listing built-ins plus user-defined custom calendars from settings, with (unset) clearing the link. Calendars no longer in the available list show as "(missing)" so users don't silently lose values.
  • src/universes/ui/universes-tab.ts β€” entity-counts cell on each row now has a "Default: " sub-line when default_calendar is set.
  • src/universes/ui/calendar-suggest.ts β€” extracted universeNameToSlug and suggestBuiltinForUniverseName into their own module so unit tests can exercise the slug-match logic without pulling Modal in via the wizard's main file.
  • Layered contract per docs/planning/universe-calendar-linking.md: universe.default_calendar is the explicit pointer, the parser is unchanged and continues to use global era-abbreviation matching at parse time. Existing universes with no default_calendar continue to behave as before β€” no migration. Phase 2 (parser-side reading, bare-year inference) deferred to a separate issue.

Change: Spouse-deaths-on-timeline default flipped to true (#447):

  • src/settings.ts β€” DEFAULT_SETTINGS.timelineShowSpouseDeaths flipped from false to true. Existing users who haven't customized the setting will start seeing spouse deaths appear on surviving spouses' timelines after upgrading. Toggle remains in place for opt-out. Filed off DigitalDreamn's observation that the only place "widowed" status surfaced automatically was the Properties panel.

Fix: Person-delete cleanup for orphaned cr_ids (#442):

  • src/core/person-delete-cleanup.ts (new) β€” planFrontmatterCleanup rules engine over a single note's frontmatter, cleanupPersonReferencesAfterDelete walks the vault and applies the plan via fileManager.processFrontMatter, getDeletedPersonCrId recovers the cr_id from metadataCache's prevCache callback argument.
  • main.ts β€” registers a metadataCache.on('deleted') handler that gates on person-note-only and removes the deleted cr_id from canonical scalar fields (father_id, mother_id, adoptive_*_id), array fields (parents_id, step*_id, adoptive_parent_id, adopted_child_id, partners_id, children_id, stepchild_id), the polymorphic spouse_id (string or array), and indexed-spouse slots (spouse1_id, spouse2_id, ...). Honors property aliases.
  • Existing vault-wide orphans from prior deletes still go through the data-quality "Remove orphaned cr_id references" tool β€” that path is untouched.

Fix: Stepchildren on stepparent timelines and in profile pane (#441, #443):

  • src/core/family-graph.ts β€” PersonNode gains stepchildrenCrIds: string[], populated in the second pass by inverting each child's stepfatherCrIds / stepmotherCrIds. Mirrors the existing adoptedChildCrIds reverse-walk pattern.
  • src/core/cross-import-detection.ts and src/core/canvas-split.ts β€” initialize and filter the new field at the construction sites that build PersonNodes.
  • src/dynamic-content/renderers/timeline-renderer.ts β€” gatherFamilyEvents skips any childCrId in stepchildrenCrIds from the children-births block. Adopted-children handling unchanged.
  • src/profile-view/sections/relationships-section.ts β€” Children block de-duplicates by labeling adopted and step children with their specific category and falling back to "Child" only when neither marker applies. A child validly in both biological and step arrays now renders as "Stepchild" (specificity wins).

Fix: Map marker popup ages and date ranges (#444):

  • src/maps/types/map-types.ts β€” MapMarker gains birthDate?: string, plus a new formatPopupDateRange helper that renders from – to for true durations and collapses identical / single-sided cases.
  • src/maps/map-data-service.ts β€” both createMarkerFromPlace and createMarkerFromEvent populate birthDate from person.born, mirroring the JourneyPath shape from #434.
  • src/maps/map-controller.ts β€” createPopupContent formats the date row through formatPopupDateRange and appends (age N) for non-birth events when DateService.calculateAge resolves a non-negative age. Same age.error guard pattern as #439 to skip descending-era false positives. Birth events suppress the age annotation since age 0 is redundant alongside the birth date itself.
  • Sibling fix to #434 β€” same fix shape on a separate code path; fifth surface in the DateService-bypass cluster alongside #433, #437, #439.

Fix: Map journey mode explains why playback isn't available (#445):

  • src/maps/map-view.ts β€” applyJourneyFilter now renders an inline placeholder where the playback panel would have appeared when the selected person has fewer than 2 unique resolvable waypoints. The placeholder names the person and states what's needed; reuses journeyControlsEl so existing teardown clears it cleanly.
  • styles/map-view.css β€” .cr-map-journey-controls--empty and .cr-map-journey-empty-message modifiers for the placeholder variant.

Fix: Map fullscreen control tooltip (#446):

  • src/maps/map-controller.ts β€” initializeFullscreen now passes title to leaflet-fullscreen as { 'false': 'Enter fullscreen', 'true': 'Exit fullscreen' } (the option shape the plugin actually expects). The previous flat-string form caused options.title['false'] to evaluate to undefined, which got rendered verbatim as the tooltip.

Testing: 51 new tests across five files for a suite total of 330 (was 279 at the start of this cycle).

  • tests/map-marker-popup-data.test.ts (12 tests) β€” formatPopupDateRange and MapMarker.birthDate population for #444.
  • tests/universe-calendar-suggest.test.ts (12 tests) β€” universeNameToSlug and suggestBuiltinForUniverseName for #432 Phase 1.
  • tests/timeline-stepchildren.test.ts (4 tests) β€” stepchild filtering on the timeline gathering path for #441.
  • tests/person-delete-cleanup.test.ts (23 tests) β€” scalar / array / polymorphic / indexed-spouse / property-alias / non-person handling for #442.
  • No new tests for #443, #445, #446, #447 β€” DOM rendering / settings-default flips / config-shape changes that don't have a fence-able pure surface, but the data-model changes that power them are covered by #441 and #444 tests.

Reporters: @DigitalDreamn for #441, #442, #443, #444, #445, #446 (the entire vault-testing chain off #434 and #439 verification).

Stability-window impact: no reset β€” all seven changes are non-data-loss. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14.


v0.22.6 Fix: Fictional-Era Coverage Round-Up (v0.22.6)

Three non-data-loss fixes that all share the same DateService-bypass root cause first surfaced in v0.22.5's #433 / #434 cluster. Two of the three were filed during v0.22.5 testing and reflection (#437, #438); the third was filed and fixed off DigitalDreamn's verification follow-up on #434, where a screenshot revealed a fourth surface where the same naive year-subtraction pattern lived (#439). Pattern of the cluster: when a feature was scaffolded for real-world dates and a fictional-calendar surface was added later, the surface inherited the wrong arithmetic.

Fix: Date-inconsistency checks respect fictional eras (#437):

  • src/core/data-quality.ts β€” parseYear now accepts an optional universe and defers to DateService.parseDate first, returning the canonical signed year (negative for descending eras like BBY, positive for ABY). Cross-era arithmetic stays coherent across all six date-inconsistency check codes (DEATH_BEFORE_BIRTH, UNREASONABLE_AGE, BORN_BEFORE_PARENT, PARENT_TOO_YOUNG, PARENT_TOO_OLD, BORN_AFTER_PARENT_DEATH). New isFictionalDate helper gates FUTURE_BIRTH / FUTURE_DEATH (the only checks that compare against the real-world current year) β€” they're now skipped when the person's dates resolve as fictional. Real-world comparisons unchanged.

Fix: Map journey surfaces life events from cr_type: event notes (#438):

  • src/maps/map-data-service.ts β€” getPersonData now also calls EventService.getEventsForPerson for each person and normalizes the returned EventNotes into the same LifeEvent shape the journey already understands. The two sources are merged with a dedup key (event_type | place(unwrapped) | date_from); inline entries win on conflict. Birth / death / marriage / divorce stay filtered out so dedicated-field waypoints aren't doubled. New coerceDateValue helper normalizes YAML Date objects (unquoted ISO dates like date_from: 1905-04-05) into ISO strings so the chronological sort doesn't drop them. For dev-vault-style schemas (which use external event notes), journeys previously collapsed down to whatever the dedicated frontmatter milestones supported β€” often two or three waypoints across an entire life.

Fix: Timeline dynamic block respects fictional eras when annotating ages (#439):

  • src/dynamic-content/services/dynamic-content-service.ts β€” added getDateService() accessor on the service so renderers can reach the plugin's date service without a direct plugin reference.
  • src/dynamic-content/renderers/timeline-renderer.ts β€” new computeEventAge(birthDate, eventDate, universe) helper that defers to DateService.calculateAge and falls back to naive year subtraction only for real-world dates or when DateService isn't available. The helper checks AgeCalculation.error so it doesn't fall through to the naive path after the fictional parser has already flagged the dates as reversed. Replaced eight call sites: the person's own events, parseContextNote, six entry-builders inside gatherFamilyEvents (children's births, spouse / parent deaths, sibling births, adoptions, adopted children's births), and the marriage / divorce paths in buildTimelineEntries. Also dropped the birthYear: number parameter from gatherFamilyEvents and parseContextNote since they now read birthDate and universe from the context's person object directly.

Testing: 38 new tests across three files. tests/data-quality-fictional-dates.test.ts (10 tests) covers BBY-span coherence, parent-child ordering across descending eras, and FUTURE-check skipping for #437. tests/map-data-service-life-events.test.ts (13 tests) covers merge / dedup / wikilink-vs-raw normalization / date coercion for #438. tests/timeline-renderer-age.test.ts (15 tests) covers BBY span ages (the actual Cliegg Lars numbers), era crossings, real-world dates, fallback when DateService is unavailable, and missing/invalid inputs for #439. Suite total: 279 tests.

Reporter: @DigitalDreamn for the verification follow-up on #434 that surfaced #439's screenshot.

Stability-window impact: no reset β€” three non-data-loss fixes. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14.


v0.22.5 Fix: Fictional-Calendar Gaps and Dynamic-Content Noise (v0.22.5)

Three non-data-loss fixes surfaced during triage of @DigitalDreamn's Star Wars universe setup (#428) and #429 investigation. The trace revealed that DateService β€” the central entry point for standard + fictional date parsing β€” existed as an exported class but was never instantiated on the plugin, so every consumer that might have used it was either silently degrading to numeric-year logic or flagging fictional dates as invalid. This release wires the service up and brings three surfaces into the fold.

Groundwork: Wire DateService onto the plugin. Instantiated alongside the other long-lived services in onload() and refreshed inside saveSettings() so consumers always see the current fictional-date configuration. Groundwork for #432, and a prerequisite for both #434 and #433. Exposed via plugin.getDateService().

Fix: Map popup age and duration respect fictional calendars (#434):

  • src/maps/types/map-types.ts β€” added birthDate?: string to JourneyPath so the raw input is preserved alongside the extracted birthYear.
  • src/maps/map-data-service.ts β€” populated birthDate when assembling journey paths.
  • src/maps/map-view.ts β€” threaded the whole JourneyPath through journeyStep / toggleJourneyPlayback / panToWaypoint / buildRichWaypointPopup (replacing the bare birthYear?: number param), and swapped the numeric-subtraction age and duration calculations for DateService.calculateAge(birthDate, waypointDate, universe) with a numeric fallback when the service isn't available.

Fix: Data-quality validator accepts fictional dates (#433):

  • src/core/data-quality.ts β€” isStandardDateFormat and normalizeDateString now take an optional universe argument and defer to DateService.parseDate when the real-world regex gates fail; a string that parses as fictional counts as recognized. All six callers (two in checkDataFormat, two in the Fix all normalizer, two in the preview path) pass the person's universe through. NON_STANDARD_DATE no longer fires for 22 BBY-style inputs on persons scoped to a universe with an active fictional calendar, and the bulk-normalize path stops trying to rewrite them toward YYYY-MM-DD.

Fix: Dynamic-content processors skip cleanly when the source file is missing (#431):

  • src/dynamic-content/services/dynamic-content-service.ts β€” buildContext now returns null with a warn log instead of throwing Could not find file: … when app.vault.getAbstractFileByPath can't resolve ctx.sourcePath. Matches the skip-and-warn pattern BidirectionalLinker already uses.
  • All 18 callsites across media-processor, timeline-processor, relationships-processor, sources-processor, transfers-processor, and extractions-processor β€” initial renders and every re-render closure inside metadataCache.on('changed') and vault.on('create') handlers β€” now bail out cleanly when the context can't be built. Fixes a DevTools console flood triggered on every metadata tick whenever a container note was renamed or deleted before Obsidian cleaned up the MarkdownRenderChild.

Testing: No new unit tests this release; changes are behavior-threading and null-guard additions that existing suites cover. Suite remains at 241 tests. Verified manually against the #428 repro (Galactic Standard Calendar behavior on a universe-star-wars-<suffix> universe) and the #431 repro (rename a frontmatter-linked note with a dynamic block open).

Reporter: @DigitalDreamn for the underlying discussion and repros that surfaced all three gaps.

Stability-window impact: no reset β€” three non-data-loss fixes. Window continues from 0.22.4's start: 2026-04-23 β†’ ~2026-05-14.


v0.22.4 Hotfix: Step-Parent Save Path (v0.22.4)

Critical data-loss regression. Opening Edit Person on an existing person note, linking a step-father or step-mother, and hitting Save showed a success notice, but the step-parent fields were silently dropped β€” never written to the file. A classic create/edit asymmetry: the modal's save payload emitted the fields, createPersonNote handled them correctly, but updatePersonNote had no step-parent branch. The load path was also incomplete, so existing step-parent values in frontmatter never round-tripped into the modal on open.

Fix: Close the three-way step-parent gap (#429):

  • src/plugin/relationship-loader.ts β€” extended LoadedRelationships with stepfatherName/Id and stepmotherName/Id singletons; added extraction with the same wikilink-fallback pattern used for adoptive parents (supports explicit stepfather_id, falls back to name-resolution, falls back to basename match).
  • src/plugin/bulk-operations.ts β€” destructured the new step-parent fields from the loader and added them to the editPersonData passed to the Edit Person modal.
  • src/core/person-note-writer.ts β€” added step-father and step-mother write branches to updatePersonNote, mirroring the adjacent adoptive-parent pattern. Handles array inputs (as createPersonNote does), supports the single-slot case the modal actually uses (length 1), and clears frontmatter correctly when the user unlinks.

Track B (Universe / Collection wipe) β€” not reproduced: Reporter also observed existing Universe and Collection fields being cleared during the same broken save. We could not reproduce this in a clean post-fix environment (fixture with universe: Star Wars + collection: Test 429 Collection pre-set, step-mother linked, saved β€” both fields preserved). Code-tracing the Universe / Collection dropdown logic found no path from normal step-parent save to unrelated-field clearing. Most likely explanation: user-state-dependent interaction (an unintentional click on the Universe or Collection dropdown triggering the __custom__ onChange path, which sets the closure value to undefined and saves as an empty string, which the writer treats as a clear). If the wipe recurs post-0.22.4, we'll reopen with a new reproducer.

Testing: 6 new regression tests in relationship-loader.test.ts covering the step-parent load path (no data, explicit IDs, wikilink fallback, basename fallback, stepmother parity, coexistence with adoptive parents). Suite grows from 235 to 241 tests.

Reporter: @DigitalDreamn (fifth bug surfaced this week, three of which were critical data-loss).

Stability-window impact: critical data-loss regression triggers the window reset per VERSIONING.md. The 3-week BRAT stability window re-starts from this release; new end-date ~2026-05-14.


v0.22.3 Fix: Cross-Entity Collections Aggregation (v0.22.3)

Medium-priority UX bug β€” no data loss, stability window not reset. Creating a Collection through the Create Place modal wrote the value to the place note's frontmatter correctly, but the Collection was invisible to the Edit Person modal's Collection dropdown, the Control Center's Collections tab, and the dockable Collections sidebar. The reverse direction worked fine: Collections created via Edit Person appeared in Create Place's dropdown, because Create Place already aggregated from both person and place notes. The asymmetry lived in FamilyGraphService.getUserCollections() β€” it scanned person notes only, and three UI surfaces relied on it as their sole source of truth.

Fix: Cross-entity aggregator + three UI surface updates (#426):

  • New pure helper at src/core/collections-aggregator.ts merges person-side and place-side collection counts into a unified { name, personCount, placeCount, totalCount } list, sorted by totalCount desc then name asc.
  • Edit Person's loadExistingCollections now uses the aggregator so place-created Collections appear in the dropdown.
  • Control Center's Collections tab and dockable Collections sidebar both use the aggregator. Badge rendering is contextual: "X people" / "X places" / "X people, Y places" depending on membership. Empty-state text updated to mention both entity types.
  • FamilyGraphService.getUserCollections() is unchanged β€” it stays person-focused for the Canvas Collection Overview (person-first family+collection composition) and Collection Analytics (person-centric metrics). Those two surfaces are intentionally out of scope.

Testing: 13 new regression tests for the aggregator covering person-only, place-only, mixed membership, tie-breaking (totalCount desc, then alphabetical), duplicate-name collapse, empty-string name defense, and zero-count row dropping. Suite grows from 222 to 235 tests.

Reporter: @DigitalDreamn (second bug report this week).

Stability-window impact: no reset β€” #426 is a medium-priority UX bug, not critical data-loss. Window continues from 0.22.2's start: 2026-04-23 β†’ ~2026-05-14.

Related post-1.0 FR (to file separately): standalone Collection creation. Collections are currently purely membership-derived (no Collection-as-entity), so the only way to create one is to commit to a person or place that will live in it. Consistent with Obsidian's tag convention, but worth considering a dedicated Create-Collection entry point post-1.0.


v0.22.2 Hotfix: IDs-Only Relationship Arrays (v0.22.2)

Critical data-loss bug, distinct from 0.22.0's spouse-format issues. Opening Edit Person on a note whose frontmatter had children_id (or spouse_id / parents_id) without the paired children: / spouse: / parents: wikilink array showed an empty relationships section, and saving wiped the *_id block entirely. The inverse shape of #410 β€” that fix covered wikilinks-without-IDs, but when the wikilink key was entirely absent the loader exited before ever looking at the IDs.

Fix: Symmetric IDs-only fallback in the relationship loader (#415):

  • New resolveCrIdToName helper is the inverse of the existing resolveNameToCrId β€” takes a cr_id and returns the stored display name by looking up the person in the graph.
  • loadAlignedArray now falls back to walking the *_id array when the wikilink key is genuinely absent (null / undefined). An explicitly empty children: [] is still treated as an intentional empty list and skips the fallback β€” prevents phantom resurrection of stale IDs on a note that was cleared deliberately.
  • On save, the writer emits both arrays, healing the frontmatter to the full dual-storage shape.
  • Orphan IDs (present in *_id but pointing to a deleted / missing person note) are preserved round-trip with the ID string itself as a visible placeholder name. Produces [[id-str]] in the wikilink array β€” ugly-but-visible, so the user can find and fix it rather than silent drop or [[]] corruption.

Testing: 13 new regression tests β€” 4 for the new resolveCrIdToName resolver and 9 for the IDs-only fallback path (children / spouse / parents coverage, scalar vs. array coercion, orphan-ID handling, explicit-[] discrimination, falsy-entry skipping, dormancy when both arrays present). Suite grew from 209 to 222 tests.

Reporter: @DigitalDreamn (Benjymn Wilkin frontmatter β€” 5 children_id entries, no children wikilink array, save wiped the block).

Stability-window impact: critical data-loss bug triggers the window reset per VERSIONING.md. The 3-week BRAT stability window re-starts from this release; new end-date ~2026-05-14.


v0.22.1 Hotfix: Spouse Format Migration (v0.22.1)

Critical data-loss regression introduced by 0.22.0's #420 fix. Editing an existing spouse to add marriage date / location / status upgraded the spouse field from flat spouse: to indexed spouse1: format, which the bidirectional linker misread as "spouse removed" β€” firing a phantom-deletion cascade that wiped spouse data on both sides.

Fix: Format-migration detection in syncDeletions (#423):

  • New isSpouseInFrontmatter pure helper scans every possible spouse location (flat + all spouse{N} slots) before the deletion detector fires removeSpouseLink.
  • If the disappeared wikilink appears anywhere else in the current frontmatter, it's a format migration, not a deletion. Cascade is skipped.
  • Protects both the flat β†’ indexed upgrade (common when adding marriage metadata) and the indexed β†’ flat downgrade (less common but same guarantee).

Fix: Complete metadata cleanup in removeSpouseLink (#423):

  • Previous cleanup removed spouse{N}, spouse{N}_id, spouse{N}_marriage_date, spouse{N}_marriage_location, spouse{N}_divorce_date β€” but not spouse{N}_marriage_location_id or spouse{N}_marriage_status, leaving orphaned metadata when a legitimate unlink occurred.
  • Cleanup is now complete, in parity with the existing person-note-writer.ts clear at line ~1742.

Testing: 20 new regression tests for isSpouseInFrontmatter covering flat / indexed / mixed-state / format-migration / null-value / object-shape inputs. Suite grew from 189 to 209 tests.

Reporter: @doctorwodka (clean YAML-based repro on #420).

Stability-window impact: critical data-loss regression triggers the window reset per VERSIONING.md. The 3-week BRAT stability window re-starts from this release; new end-date ~2026-05-14.


v0.22.0 Stability Release (v0.22.0)

The first release after 0.21.0 on the path to 1.0. Addresses a critical data-loss bug surfaced during 1.0-gate testing, four lower-severity bugs, and one feature addition. The 3-week BRAT stability window resets to this release.

Fix: Cross-note spouse writes preserve indexed format (#420):

  • Adding a spouse from a different note could previously downgrade the target's indexed spouseN: format to flat spouse: keys, silently wiping the target's other spouses. Silent data loss on any cross-note spouse write to a multi-spouse person.
  • Both bidirectional write paths (the linker's syncSpouse and the addBidirectionalSpouseLink helper used by Create / Edit Person) now inspect the target's existing format and append to indexed slots when present.
  • Mixed states (residue flat keys alongside indexed slots from earlier bad writes) route to indexed so prior corruption doesn't compound.

Fix: Life Events Migration idempotent on re-run (#414):

  • Running the Cleanup Wizard's Life Events Migration twice on the same person note used to mint a duplicate event note each time because collision-avoidance only deduped by filename.
  • Migration now scans for existing cr_type: event notes before creating new ones and reuses any with a matching (persons, event_type, date) tuple.
  • Strict date matching: "1850" and "1850-01-01" are different identities, so user refinements aren't silently merged.
  • Cleanup Wizard notice reports (reused N existing events) alongside the created count.

Fix: Timeline no longer crashes on bare-year numeric dates (#416):

  • Obsidian's Properties panel writes died: 1893 (unquoted) as a Number. The timeline's date helpers called .trim() on the input and threw TypeError, surfacing as Obsidian's red code-block error.
  • Helpers now accept string | number | undefined | null and coerce at entry. Same-class fix applied to formatDisplayDate so the person picker also handles numeric-YAML date fields without crashing.

Fix: Dynamic relationships block surfaces adoptive siblings in extended / all modes (#417):

  • The block's sibling derivation only walked biological parent edges, so adoptive siblings (other children of adoptive parents) never appeared β€” even in extended / all modes.
  • Adoptive parent edges are now walked alongside biological ones. Results are labeled Adoptive sibling: to distinguish them, matching the existing Adoptive father / Adoptive mother label pattern.
  • Deduped against the biological set, so a person present on both edges lists once, unlabeled.

Fix: Symmetric custom relationships auto-propagate (#419):

  • Adding a symmetric custom relationship (e.g., "twin") from person A to person B used to leave B's note untouched. Built-in symmetric types (spouse, biological sibling) already reciprocated via the bidirectional linker; custom types didn't.
  • The Add Relationship modal now mirrors the entry onto the target when the type's symmetric flag is set.
  • Idempotent: re-adding the same symmetric link from either side is a silent no-op.

Added: Burial renders on the person timeline (#408):

  • burial_date and burial_place were already recognized by the GEDCOM importer/exporter, map markers, and cleanup tooling β€” but the charted-roots-timeline block was the one place they didn't surface.
  • Burial is now emitted alongside birth, death, adoption, and marriage with a "Buried" label, "in {place}" suffix when burial_place is set, and age computed from born.
  • Honors the timeline block's include: [...] filter.

Testing gate: Vitest suite grew from 109 to 189 tests. New pure-helper modules: sibling walker, relationship property writer, event identity, spouse format detector, date-display coercion. Every 0.22.0 fix in a volatile code path landed with regression tests.


v0.21.x

v0.21.0 Edit Person Round-Up (v0.21.0)

Stability release focused on four Edit Person modal round-trip bugs. No feature work.

Fix: Relationships not dropped when IDs are partial or wikilink basenames differ from name (#410):

  • The v0.20.62 fix for #403 closed the all-wikilinks-no-IDs case but left sibling gaps. The name-based fallback resolver now also matches on the note's basename; the array-field fallback runs per-entry instead of all-or-nothing. Unresolvable wikilinks are preserved through the round trip rather than dropped.

Fix: Clearing Universe and ten other optional fields now actually clears frontmatter (#406):

  • Fields affected: universe, collection, personType, sex, givenName, maidenName, pronouns, dnaTestingCompany, dnaKitId, dnaMatchType, dnaNotes. All use the established ?? '' (or ?? []) pattern so the writer's clear path actually fires.

Fix: Nickname field round-trips on edit (#412):

  • Three gaps in the path (load, type, save) all closed. Nickname now behaves like every other optional string field, including clearing via empty input.

Fix: Endogamy flag toggle-off persists (#413):

  • The onChange handler's value || undefined idiom converted a toggled-off false to undefined, which the writer read as "untouched." Fixed by passing the boolean through directly.

Testing infrastructure: Vitest test harness added with 31 regression tests for the relationship load path. New VERSIONING.md documents plugin-specific SemVer rules and 1.0 criteria.


v0.20.x

v0.20.57 Feature Round-Up (v0.20.57)

A collection of enhancements addressing TODO items and community feedback.

Multiple person picker in event modal (#366):

  • The create/edit event modal now supports adding multiple people to an event (e.g., marriages, group events)
  • Additional persons saved to the persons frontmatter array
  • Persisted across modal state restoration

Marriage data in Family Group Sheet (#370):

  • Family Group Sheet reports now include marriage date and place
  • Extracted from spouse relationship data (indexed spouse1_marriage_date etc.)
  • Both markdown and PDF output updated. Multiple marriages supported.

Targeted schema validation (#367):

  • "Validate matching notes" context menu action on schemas runs validation against only notes matching that schema
  • Progress modal and result summary

Organization membership statistics (#368):

  • Organizations statistics card now shows real membership counts
  • People with memberships, total memberships, empty organizations

Universe and collection pickers in Report Wizard (#369):

  • Report types targeting a universe or collection now have fuzzy-search picker modals
  • Replaces "not yet implemented" notices

Web Clipper discoverability (#364):

  • Info boxes in the Places, Sources, and People tabs link to Web Clipper wiki templates
  • Relevant examples per entity type (Wikidata for Places, FamilySearch for People)

Fix: Step-parent assignment (#365):

  • Step-parent relationships from the relationships array now check the target person's sex
  • Female step-parents correctly assigned to stepmotherCrIds instead of always stepfatherCrIds

Child Map Markers and Region Editing (v0.20.56)

Visual tools for managing child map regions on parent maps.

GitHub Issue: #362

Child map markers:

  • Gold map-icon markers appear on parent maps for every child map that has parent_map set
  • Positioned at the center of parent_region if defined, or at the parent map center as fallback
  • Popup shows child map name with "Open map" and "Edit/Draw region" buttons
  • Dedicated layer with "Child maps" toggle in the Layers menu

On-map region editing:

  • Click a child map marker β†’ "Edit region" (or "Draw region" for maps without a region)
  • Draggable dashed blue rectangle with four corner resize handles
  • Floating save/cancel toolbar at the top of the map
  • Works at any zoom level for precision positioning
  • Saves parent_region_x/y/w/h to the child map's frontmatter immediately

Region drawing modal:

  • "Draw region" button in Map Creation Wizard Step 2 when a parent map is selected
  • Canvas-based drawing on the parent map image with drag, resize, and coordinate readout
  • Also available via right-click context menu on child map notes ("Draw/Edit parent region")

Documentation: Custom Maps β€” Child Map Markers, Custom Maps β€” Editing Regions


Linked Map Drill-Down Navigation (v0.20.56)

Full drill-down navigation between custom maps for multi-scale worldbuilding.

GitHub Issue: #361

Phase 1 β€” Place-level linking:

  • linked_map property on place notes enables drill-down from one map to another
  • Clicking a place marker shows an "Open [map name]" button in the popup

Phase 2 β€” Map hierarchy and breadcrumbs:

  • parent_map property on child map notes establishes parent-child relationships
  • Breadcrumb navigation in the Map View toolbar (e.g., "The Dying Earth β†’ River Scaum")
  • "Parent map" dropdown in the Map Creation Wizard

Phase 3 β€” Visual overlay regions:

  • parent_region_x/y/w/h properties on child map notes define where the child sits on the parent
  • Clickable dashed blue rectangles rendered on the parent map
  • Hover shows child map name tooltip, click drills down to the child map

Documentation: Custom Maps β€” Linked Maps, Frontmatter Reference β€” Map Hierarchy


Universe Entity Dynamic Blocks (v0.20.56)

Four new dynamic content blocks for universe notes that automatically list all entities belonging to that universe.

GitHub Issue: #359

Blocks:

  • charted-roots-universe-people β€” table with name, born, died, occupation
  • charted-roots-universe-places β€” table with name and place type
  • charted-roots-universe-events β€” table with event name, date, type badge, and place
  • charted-roots-universe-organizations β€” table with name and type

All entries are clickable wikilinks. Supports sort (name/date/type) and limit parameters. Auto-refreshes when vault data changes.


Universe Map Thumbnails (v0.20.56)

New charted-roots-universe-maps dynamic block renders clickable map image thumbnails for custom maps belonging to a universe.

GitHub Issue: #360

Features:

  • Click a thumbnail to open it in Map View
  • Shows place count badge per map
  • Supports size parameter (small/medium/large)

Image Region Crop (v0.20.55)

Select a region of an image (e.g., a face in a group photo) to use as the thumbnail on person cards, media blocks, and profile views.

GitHub Issue: #354

Features:

  • media_crop frontmatter property β€” Stores crop coordinates (x, y, w, h) per image per note
  • Crop selection modal β€” Canvas-based UI with draggable/resizable rectangle, darkened overlay, and live preview
  • Right-click menu β€” "Set crop region" / "Edit crop region" / "Remove crop" on any image in the media block
  • Applied everywhere β€” Media block thumbnails, Family Chart avatars, Entity Profile View

Data model (Option B β€” separate property, backward compatible):

media:
  - "[[group-photo.jpg]]"
media_crop:
  - image: group-photo.jpg
    x: 100
    y: 50
    w: 200
    h: 250

Documentation: Frontmatter Reference β€” Image Crop Regions, Media Management


PDF Previews in Media (v0.20.54)

PDFs attached to person and source notes now show a first-page thumbnail preview instead of a generic file icon.

GitHub Issue: #350

Where PDF thumbnails display:

  • charted-roots-media dynamic block in reading view
  • Sources tab media gallery in Control Center
  • Entity Profile View media section

Implementation:

  • Uses Obsidian's built-in loadPdfJs() API (no bundled dependencies)
  • Thumbnails rendered at 200Γ—280px from the first page
  • In-memory cache for fast subsequent renders
  • Async generation with placeholder fallback
  • Click behavior unchanged (opens in Obsidian's PDF viewer, compatible with PDF++)

Documentation: Media Management, Dynamic Note Content β€” Media Block


Alt Name Display (v0.20.52–v0.20.53)

New alt_name frontmatter property for person notes, designed for multilingual genealogy where both native script and romanized names need to be visible at a glance.

GitHub Issues: #346, #347, #348, #349

Where alt name displays:

  • Family Chart View cards (below main name, with auto-adjusting card height)
  • Family Chart Person details panel
  • Entity Profile View header (muted styling)
  • Map view marker popups (birth, death, marriage, burial, event markers)
  • SVG visual tree chart nodes (all layout algorithms)
  • Excalidraw export nodes
  • Circle card style (Family Chart)

Documentation: Frontmatter Reference β€” alt_name, Data Entry β€” Alternate Name


Calendar View (v0.20.47)

A new workspace view showing a monthly calendar grid of significant dates across the vault β€” birthdays, death anniversaries, marriage dates, and other life events.

GitHub Issue: #299

Features:

  • Monthly calendar grid with color-coded event dots (blue = birth, red = death, yellow = marriage)
  • Text labels toggle showing person names inside day cells
  • Month dropdown and year input for instant navigation to any date
  • Day click detail panel showing all events with person name, type, year, years ago, and place
  • Imprecise dates section ("This month, day unknown") for entries with month but no day
  • Event type and living/deceased filters via filter menu
  • Right-click day cells to create events with date pre-filled
  • Keyboard navigation (arrow keys for month, T for today)
  • State persistence across reloads (month, year, filters, label toggle)

Entry points: Command palette, Control Center dashboard tile, Events tab button, person note context menu ("Show on calendar" β†’ birth month/year), event note context menu ("Show on calendar" β†’ event date).

Data sources: Person notes (birth/death dates via FamilyGraphService) and event notes (marriage, baptism, immigration, etc. via EventService).

Documentation: Calendar View


Source Note Hierarchies (v0.20.46)

New source_parent and source_parent_id properties for linking child source notes to a parent document, enabling modeling of multi-document record groups like probate packets, census pages, and multi-volume collections.

GitHub Issue: #337

Features:

  • source_parent (wikilink) and source_parent_id (cr_id) properties on source notes, following the existing dual-storage pattern
  • Parent source picker with autocomplete in the Create/Edit Source modal (under Additional details)
  • Parsing, reading, and writing in SourceService
  • Frontmatter Reference documentation updated

Use cases: Probate packets (case-level parent + child documents), multi-page census transcriptions, record groups or multi-volume collections.

Documentation: Evidence & Sources β€” Source Hierarchies, Frontmatter Reference β€” Source Hierarchy


Source Hierarchy Display (v0.20.46)

Hierarchy-aware display features built on top of the source_parent relationship, adding navigation and filtering across related source notes.

GitHub Issue: #338

Profile view sections:

  • Parent source β€” Link to the parent source at the top of child source profiles
  • Child documents β€” List of all child sources on parent profiles, with source type badge, title, and date
  • Related documents β€” Sibling sources (same parent, excluding self) on child profiles
  • Source tree β€” Collapsible tree visualization on parent profiles showing the full hierarchy with indented child nodes

Sources tab filtering:

  • "Has parent (child sources)" β€” show only sources with a parent
  • "No parent (top-level)" β€” show only sources without a parent
  • "Children of [source]" β€” show children of a specific parent source

Person-Focused Map Journey (v0.20.45)

Journey mode in the Map View isolates a single person's geographic path and provides animated step-through playback with rich popups and family overlay.

GitHub Issue: #295

Phase 1 β€” Person filter and isolation:

  • Route button in toolbar opens person picker to enter journey mode
  • Filters all markers and paths to the selected person
  • Fits map bounds to the person's waypoints
  • Person indicator in toolbar with clear button
  • "Show journey on map" context menu on person notes

Phase 2 β€” Animated step-through playback:

  • Floating playback controls (prev/play/next) with smooth fly-to animation
  • Progress bar, waypoint label, step counter, speed selector (0.25×–2.5Γ—)
  • Auto-loops back to start

Phase 3 β€” Rich waypoint popups:

  • Each stop shows event type, date, place, age, duration at location, and description

Phase 4 β€” Family journey overlay:

  • Toggle button shows dimmed journey paths for immediate family
  • Color-coded by relationship: blue (parents), pink (spouses), emerald (children)
  • Click a family path popup to switch focus to that person's journey

Documentation: Geographic Features β€” Journey Mode


Customizable Timeline Display Templates (v0.20.38)

Four new capabilities for controlling how timeline entries are displayed in dynamic content blocks.

GitHub Issue: #325

Features:

  • Layout modes: layout parameter with chronological (default), grouped (sections for personal/family/context), and personal-first options
  • Label customization: Six settings under Advanced > Timeline labels to override birth, death, and family event labels with {name} placeholder support
  • Format strings: Per-block format parameter with {year}, {title}, {place}, {age} placeholders
  • Template notes: Reference a markdown note via template: [[Note]] to define custom sections with independent sort, include, and format

Citation Integration (v0.20.38)

Bidirectional sync between citation notes and sourced_* fields, plus citation statistics in the dashboard.

GitHub Issue: #324

Features:

  • Three new commands: sync sourced fields from citations (per-person and vault-wide), generate citation notes from existing sourced fields
  • Source summary report includes a Page column when citation notes have page references
  • New "Citation statistics" section in the statistics dashboard: total citations, coverage percentage, quality distribution, most cited sources

Family Events on Timelines (v0.20.37)

Person timelines can now show family members' life events for broader genealogical context.

GitHub Issue: #323

Features:

  • Children's births, spouse deaths, parent deaths, and sibling births on person timelines
  • Controlled by four global toggles in Settings > Advanced (all off by default)
  • Each entry links to the family member's note with age annotations
  • Per-block suppression via familyEvents: none
  • Consistent icon rendering when family events are present

Calculate Multiple Relationships (v0.20.36)

The relationship calculator now finds multiple relationship paths between two people through different common ancestors.

GitHub Issue: #321

Features:

  • After the primary (shortest) result, "Find more relationships" searches for additional paths
  • Each result shows relationship type, common ancestor name, and blood/marriage indicator
  • Ancestor couples who are spouses are grouped together (e.g., "via John Smith & Jane Doe")
  • Configurable max search depth in Settings > Advanced (default 10 generations, 0 for unlimited)

Cross-Project Research Queries (v0.20.35)

Two new ways to see all research activity for a person across research projects, journals, and reports.

GitHub Issue: #303

Features:

  • "Research activity" section in person profiles aggregates IRNs, log entries, journals, reports, and projects referencing the person, grouped by project with date ranges and result indicators
  • "Find related research" command (also in the command menu) opens a modal with the same grouped view, with a person picker if no person note is active

Historical Context Overlay and Age Annotations (v0.20.34)

Timelines can now overlay historical events and display age annotations for richer genealogical context.

GitHub Issue: #296

Features:

  • context: [[Note]] parameter in timeline code blocks references a note containing historical events
  • Context events rendered with muted styling and landmark icon
  • defaultTimelineContext setting applies a context note to all timelines globally
  • All timeline events display age annotations when the person's birth date is known

Citation Metadata Support (v0.20.34)

New citation entity type for per-citation page references and quality assessments, with full GEDCOM roundtrip support.

GitHub Issue: #316

Features:

  • Citation notes with page references (citation_page) and quality assessments (citation_quality)
  • GEDCOM import generates citation notes from SOUR blocks with PAGE/QUAY sub-tags
  • GEDCOM and Gramps exports write citation metadata back as PAGE/QUAY
  • "Add citation" command and modal for manual creation
  • Citations section in Entity Profile View grouped by source with fact labels, page references, and color-coded quality badges
  • New citationsFolder setting (default: Charted Roots/Citations)

Comprehensive GEDCOM Field Coverage (v0.20.33)

Full import/export support for 16+ additional GEDCOM 5.5.1 fields, ensuring near-lossless roundtrip data fidelity.

GitHub Issue: #317 (umbrella)

New fields (import + export):

  • Name components: NPFX (prefix), NSFX (suffix), SPFX (surname prefix), NICK (export added)
  • Person attributes: TITL, RELI, NATI, DSCR, IDNO, PROP, CAST, NCHI, NMR, SSN
  • Burial: BURI.DATE and BURI.PLAC imported to person frontmatter
  • Death cause: DEAT.CAUS imported to death_cause
  • Age at event: AGE sub-tag stored on event notes and re-exported
  • Date ranges: FROM/TO format now parsed and exported (previously only BET/AND)

Export roundtrip fixes:

  • Family events (MARR, DIV, MARB, MARC, MARL, MARS, DIVF) export on FAM records instead of generic EVEN
  • NAME line constructed from explicit NPFX/GIVN/SPFX/SURN/NSFX components
  • Duplicate BIRT/DEAT/BURI/OCCU records eliminated
  • OCCU exports as inline value instead of NOTE sub-tag
  • Six event type export mappings added: MARB, MARC, MARL, MARS, DIVF, CHRA

Still open: #316 β€” Citation-level metadata (PAGE/QUAY) requires a data model decision and is tracked separately.

Planning document: gedcom-field-coverage.md


Book & Narrative Compilation (v0.20.26)

A book builder that combines multiple generated reports, visual trees, and user-written vault notes into a single sequenced document with cover page, table of contents, and optional index. Outputs as PDF or ODT.

GitHub Issue: #294

Chapter Types:

Type Description
Generated report Any of the 17 existing report types, configured inline
Visual tree Pedigree, descendant, hourglass, or fan chart embedded as image
Vault note User-written markdown rendered into the document
Section divider Title page for a new part of the book

Preset Templates:

Template Audience Typical contents
Family history book Family sharing Cover, pedigree chart, individual summaries, family group sheets, descendant register, timeline, bibliography, index
Research compilation Researcher Cover, gaps report, source summaries, individual summaries, ahnentafel, bibliography
Blank Any Empty canvas, user builds from scratch
Change Description
Book builder modal 4-step wizard for metadata, chapter selection with drag-and-drop ordering, output config, and progress-tracked generation
.book.json definitions Saveable book definitions that can be reopened and re-generated as vault data changes
Consolidated bibliography Deduplicates footnotes across chapters into a single bibliography section
Name index Auto-generated index sorted by last name with alphabetical grouping
Chapter numbering Numeric or Roman numeral chapter numbers
Template intelligence Templates derive chapters from the family graph β€” individual summaries for direct-line ancestors, family group sheets per nuclear family
Regenerate command Re-generate a book from its .book.json definition without opening the wizard, with change detection reporting which chapters were updated

Entry points: "Open book builder" command, Control Center tile (Trees & reports tab), context menu on .book.json files.

Documentation:


Entity Profile View (v0.20.18)

A dockable Profile View that auto-syncs to the active note and displays all related data for any entity type (Person, Place, Event, Source, Organization) in collapsible sections, enabling deep work without tab-hopping.

GitHub Issue: #251 | Discussion: #242

Phase 1 β€” Read-only Profile View:

Change Description
Auto-syncing view ItemView follows the active note with 150ms debounce
Identity header Entity type badge, avatar, key metadata, pin toggle
Collapsible sections Chevron toggle with compact summaries for all 5 entity types
Person sections Relationships (family + custom), Events, Sources, Media, Data Quality
Place sections Events at location, Sources, Media, Map preview (coordinates + "Open in Geo Map")
Event sections Participants, Sources, Media
Source sections Referenced facts (vault-wide scan), Media
Organization sections Members, Events, Sources, Media
Pin/unpin Freeze on a specific entity; multiple instances for side-by-side comparison
Breadcrumb navigation In-place entity traversal
State persistence Pinned entity, section states, breadcrumbs persist across sessions

Phase 2 β€” Inline editing:

Change Description
Click-to-edit All identity header fields across all five entity types
Input types Text, number, and select (dropdown) β€” Enter or blur saves, Escape cancels
Single active field Clicking another field saves the first automatically
Empty placeholders Clickable placeholders for adding new values
Frontmatter save Direct save via processFrontMatter() with property alias support
Wikilink requoting Automatic requoting for link-valued fields

Phase 3 β€” Polish and integration:

Change Description
Lazy rendering contentRenderer defers DOM until first expand
Keyboard navigation ArrowUp/Down, Enter/Space, Home/End on section headers (WAI-ARIA accordion)
Mobile-responsive 44px touch targets, narrow-pane media query for stacked metadata
Map preview Embedded Leaflet map for place profiles, lazy init/cleanup on collapse

Documentation:


Structured Role Lists for Organizations (v0.20.17)

Adds a roles property to organization notes defining valid roles and display order, with role picker autocomplete in membership modals.

GitHub Issue: #274

New frontmatter property:

roles:
  - Lord
  - Heir
  - Castellan
  - Maester

List order defines rank/display order (first = highest). Stored on the org note.

Change Description
roles property Ordered list of valid role names on organization notes
Role picker Autocomplete suggestions when adding/editing memberships
Type default roles Organization types can define default role templates
Members block fallback 3-level ordering: block role-order β†’ org roles β†’ alphabetical
Chip editor UI Add/remove roles with tag-style chips in create/edit modals

Role ordering fallback chain in charted-roots-members blocks:

  1. Block-level role-order config (explicit override)
  2. Organization's roles property (inherited from note)
  3. Alphabetical order (default)

Mills-Aligned Source Classification (v0.20.17)

Adds three optional source classification axes from Elizabeth Shown Mills' Evidence Explained, enabling GPS-oriented genealogists to apply the standard analytical framework used in professional genealogy.

GitHub Issue: #276

New frontmatter properties:

Property Values Question
source_classification original, derivative, authored_narrative What is the document?
information_classification primary, secondary, undetermined Who provided the info?
evidence_classification direct, indirect, negative How does it relate to the question?

Changes:

Change Description
Create Source modal Collapsible "Source classification (Mills)" section with three dropdowns
Evidence analysis information_classification takes precedence over source_quality when present
Reports Source Summary, Sources by Role, and PDF reports conditionally show classification columns
Templates Census (derivative/primary), vital record (original/primary/direct), full template (three suggesters)

All three properties are optional. Existing sources using only source_quality or confidence continue to work identically.

Community contributor: @ANYroots (Mills methodology, real-world examples)


Map View Marker Layering (v0.20.3)

Improved visual distinction between event markers and place markers on the Map View when the "All places" layer is enabled.

GitHub Issue: #164

Changes:

Change Description
Hollow circles Place markers now use hollow teal circles instead of solid circles
Z-ordering Event markers render on top of place markers (zIndexOffset: -1000)
CSS refactor Marker icons now use CSS classes instead of inline styles

Visual Distinction:

  • Event markers: Solid colored circles (12px) β€” birth (red), death (black), etc.
  • Place markers: Hollow teal circles (10px) β€” clearly distinguishable as background context

Community contributor: @ANYroots (use case, feedback)


Control Center Modularization

Break the Control Center modal into modular, independently dockable workspace views. The Control Center remains available for quick, transient access while dockable views serve extended work sessions.

GitHub Discussion: #239, #240

Phase 1 β€” Component Extraction:

Extracted all 16 tabs from the monolithic control-center.ts (~13,760 lines) into individual component files, reducing it to ~1,450 lines (89% reduction). Removed 3 legacy redirect tabs (Status, Guide, Statistics). No user-facing changes.

Phase 2 β€” Dockable ItemViews:

Created 9 dockable sidebar views, each accessible via a dock button on the corresponding Control Center card header or via the command palette.

View Command Icon Content
People Open people user Filter/sort/search table with expandable details, context menus
Places Open places map-pin Filter/sort/search table with category badges, coordinates
Events Open events calendar Type/person/date filters, sortable timeline table
Sources Open sources book-open Filter/sort table with type/confidence badges
Organizations Open organizations building Filter/sort table with type badges, member counts
Relationships Open relationships users Filter by type/category/person, sort, context menus
Universes Open universes globe Filter/sort/search with status badges, entity counts
Collections Open collections folder-tree Mode switcher (all people / families / collections)
Data quality Open data quality shield-check Research gaps, source conflicts, auto-running vault-wide analysis

Key design decisions:

  • The Control Center modal is not deprecated β€” modal and dockable views share the same extracted components
  • Each view is single-instance (clicking the dock button focuses an existing view rather than creating a duplicate)
  • Views auto-refresh on vault changes (debounced 2s) and persist filter/sort/search state across sessions
  • Data Quality view is a read-only dashboard (not an entity list) β€” batch operations, wizards, and data tools remain modal-only
  • Dock buttons appear on hover over card headers, using the panel-right icon

Relationships tab enhancements (prerequisite for dockable view):

  • Added filter dropdowns (by type, category, person)
  • Added sort options (by type, from-person, to-person, date)
  • Added pagination with load-more pattern
  • Added context menus on rows

See Control Center Modularization Planning Document for full specifications.


v0.19.x

Unified Place Lookup (v0.19.17)

Query multiple place databases (Wikidata, GeoNames, Nominatim) from a single interface and create properly-formatted place notes with coordinates, hierarchies, and standardized names.

GitHub Issue: #218 | Related: #128 (Web Clipper Integration)

Data Sources:

Source Best For Status
Wikidata Well-known places, multilingual research βœ… Complete
GeoNames Modern geography, worldwide coverage βœ… Complete (requires free username)
Nominatim/OSM Geocoding, address lookup βœ… Complete
FamilySearch Places U.S. genealogy, historical jurisdictions Deferred (requires OAuth)
GOV German/European historical boundaries Deferred (needs API research)
Change Description
PlaceLookupService Multi-source lookup with Wikidata, GeoNames, and Nominatim integration
Rate limiting 1 req/sec for Nominatim/GeoNames, 500ms for Wikidata
Place type mapping GeoNames fcode β†’ Charted Roots, Wikidata P31 β†’ Charted Roots
PlaceLookupModal Source selection chips and result cards with side-by-side comparison
Create Place integration "Look up place" button in Create Place modal header
Command palette Standalone "Look up place" command
Auto-populate Coordinates, place type, and parent place from results
GeoNames config Username configuration in Settings β†’ Places

Documentation:


Inheritance & Succession Tracking

Track ownership changes, property transfers, and succession relationships through the existing event system with a new transfer event type and dedicated UI.

GitHub Issue: #123

Features Implemented:

Feature Description
Transfer event type New event type for ownership transfers, property inheritance, and succession
Transfer history block charted-roots-transfers dynamic block shows transfer timeline for any entity
Context menu integration Right-click person/place/organization notes to insert transfer history
Property aliases previous_owner, new_owner, transferred_to, inherited_from map to canonical fields

Use Cases:

Scenario How to Model
Property inheritance Transfer event with previous_owner (decedent) and new_owner (heir)
Enslaved ancestor tracking Transfer events linking to probate/sale sources
Title succession Transfer event with position field for the title/role
Worldbuilding succession Chain of transfer events for thrones, lordships, etc.

YAML Example:

type: transfer
date: 1845-03-15
previous_owner: "[[John Smith Sr.]]"
new_owner: "[[John Smith Jr.]]"
subject: "[[Smith Family Farm]]"
source: "[[Probate Record 1845]]"
notes: "Inherited upon father's death"

See Inheritance & Succession Tracking Planning Document for design details.


Organization Member Management

Manage organization memberships directly from the Organizations tab or file explorer with a dedicated modal supporting bulk add and inline editing.

GitHub Issue: #226

Features Implemented:

Feature Description
Manage members modal Dedicated modal for viewing, adding, and editing organization members
Multi-select person picker Bulk add members with checkbox selection and search
Inline membership editing Edit role, date joined, and date left directly in the member list
Context menu integration "Manage members..." option in Organizations tab and file explorer right-click menus
Real-time updates Member list updates immediately after add/edit/remove operations

Access Points:

  • Organizations tab: Right-click an organization row β†’ "Manage members..."
  • File explorer: Right-click an organization note β†’ Charted Roots β†’ "Manage members..."

Membership Fields:

Field Description
Role Position or title within the organization (e.g., "Lord", "Squire", "Maester")
Date joined When the person joined the organization (supports fictional dates)
Date left When the person left; empty means currently active

See Organization Member Management Planning Document for design details.


Person Roles in Sources (v0.19.16)

Track the roles that people play in source documents (principal, witness, informant, official, etc.) to support FAN network research, information quality assessment, and enslaved ancestor research.

GitHub Issue: #219

Discussion: #189

Features Implemented:

Feature Description
Role properties Seven canonical role categories in source note frontmatter
Inline notation "[[Person|Person (Role details)]]" format for readability
Dynamic block charted-roots-source-roles renders role table with person links
Context menu Right-click on source notes to insert roles block
Modal UI Assign roles when linking people to sources via Create/Edit Source modal
Sources by Role report Control Center report showing all sources where a person appears by role

Role Categories:

Role Use Case
principals Subject(s) of the document (deceased, testator, groom/bride)
witnesses Named witnesses to events or document signing
informants Person providing information (affects quality assessment)
officials Clerks, judges, officiants, physicians, undertakers
enslaved_individuals Persons listed as property in wills, inventories
family Family members named in relation to principals
others Catch-all for roles not fitting above categories

YAML Example:

principals:
  - "[[John Smith Sr.|John Smith Sr. (Decedent)]]"
officials:
  - "[[Thomas Brown|Thomas Brown (Administrator)]]"
enslaved_individuals:
  - "[[Mary]]"
  - "[[Peter]]"

Dynamic Block:

```charted-roots-source-roles
source: "[[Estate Inventory of John Smith Sr.]]"
```

Sources by Role Report Options:

  • Filter by role type (witness, informant, official, etc.)
  • Grouping: by role, by source, or chronological
  • Show role details and source quality ratings

See Person Roles in Sources Planning Document for implementation details.

Community contributors: @ANYroots (original proposal, use cases, terminology), @wilbry (simplified role categories)


Event Type Icons (v0.19.15)

Display Lucide icons for event types in timelines and map popups, reducing visual clutter and improving cohesiveness.

GitHub Issue: #184

Features Implemented:

Feature Description
Global setting eventIconMode in Preferences > Canvas & Trees
Display modes text (default), icon (with tooltip), both (icon + text)
Person/family/place timelines Icons in Control Center timeline views
Dynamic timeline block Icons in canvas-roots-timeline code blocks
Map popup icons Event type icons with matching colors in map popups
Tooltip support Hover tooltip shows event type name in icon-only mode
Fallback icon Calendar icon for custom types without assigned icons

Display Modes:

Mode Description
text Current behavior: text labels only (default)
icon Icons only, with text in tooltip
both Icon + text label

Note: Canvas tree event nodes were not applicable since canvas exports use file embeds.

See Event Type Icons Planning Document for implementation details.


Multi-Spouse Visual Cues (v0.19.14)

Visual cues in the family chart clarify relationships when a person has multiple spouses, making it clear who the "hub" person is.

GitHub Issue: #195

The Problem: When displaying multi-spouse families in the family chart, the horizontal layout can be ambiguousβ€”making it unclear which person has multiple marriages.

The Solution: Spouse numbering on connecting edges (β‘ , β‘‘ etc.) indicates marriage order. Numbers work in static exports (PNG, SVG, PDF) unlike hover-based solutions.

Features Implemented:

Feature Description
Multi-spouse detection getSpouseNumberForLink() identifies spouse order
Circled numbers β‘ β‘‘β‘’... displayed on spouse connection edges
Label positioning Numbers positioned in visible gap between cards
Kinship toggle integration Works with "Show kinship labels" toggle
Export compatibility Labels included in PNG, SVG, and PDF exports

See Multi-Spouse Visual Cues Planning Document for implementation details.


GEDCOM Media Import (v0.19.13)

Import media object references (OBJE records) from GEDCOM files, bringing GEDCOM import to full parity with Gramps for media handling.

GitHub Issue: #202

Features Implemented:

Feature Description
OBJE record parsing Parse top-level 0 @Oxxxx@ OBJE records to build media handle β†’ file path map
Reference collection Collect 1 OBJE @Oxxxx@ references on INDI, FAM, SOUR, and event records
Path resolution Convert external file paths to vault-relative wikilinks (filename-only by default)
Path prefix stripping Optional setting to strip external path prefixes for complex folder structures
Import wizard preview Live preview showing path β†’ wikilink mappings before import
Vault validation Validates files exist in vault and reports missing media after import
Inline OBJE support Handle both pointer (@Oxxxx@) and inline OBJE formats

Import Wizard Options:

  • Media references β€” Enable/disable media import
  • Path prefix β€” Optional external path prefix to strip from GEDCOM paths

Frontmatter Output:

media:
  - "[[photo1.jpg]]"
  - "[[document.pdf]]"

Note: Media is added to person notes, event notes, and source notes based on where the OBJE reference appears in the GEDCOM file.


Research Workflow Phase 1 (v0.19.11)

GPS-aligned research workflow entity types for managing research projects, reports, individual research notes, and research journals within Obsidian.

GitHub Issue: #145 (consolidates #124, #125)

Features Implemented:

Feature Description
5 research entity types research_project, research_report, individual_research_note, research_journal, research_log_entry
Note type detection Recognize entities via cr_type frontmatter property
Tag detection Recognize via tags including #irn shorthand for Individual Research Notes
Statistics integration Research section in Statistics view with entity counts and status breakdowns
Status tracking Project statuses (open, in-progress, on-hold, completed) and report statuses (draft, review, final, published)

Entity Types:

Type Purpose
research_project Hub for complex, multi-phase research cases
research_report Living document analyzing specific research questions
individual_research_note Synthesis between reports and person notes (IRN)
research_journal Daily/session tracking across projects
research_log_entry Individual log entries as separate queryable notes

Key Properties:

  • subject β€” Links IRN to the person being researched
  • up β€” Links to parent in research hierarchy (project β†’ report, etc.)
  • status β€” Current state of project or report
  • private β€” Exclude from exports when true
  • related β€” Related research entities

Files Created:

  • src/research/types/research-types.ts β€” Type definitions for research entities
  • src/research/index.ts β€” Module exports

Files Modified:

  • src/utils/note-type-detection.ts β€” Added research entity types and detection functions
  • src/statistics/types/statistics-types.ts β€” Added research statistics types
  • src/statistics/services/statistics-service.ts β€” Added research entity counting and getResearchStatistics()
  • src/statistics/constants/statistics-constants.ts β€” Added RESEARCH section ID
  • src/statistics/ui/statistics-view.ts β€” Added research section with entity cards and status breakdowns
  • styles/statistics.css β€” Added research card and status badge styles

Documentation:

Community Contributors: @ANYroots (IRN structure, GPS methodology), @wilbry (lightweight approach, research journal concept)


DNA Match Tracking (v0.19.9)

Opt-in DNA match tracking for genetic genealogists, enabling recording of key DNA matches alongside family tree research. All features are invisible when the setting is disabled (default: OFF).

GitHub Issue: #126

Features Implemented:

Feature Description
enableDnaTracking setting Master toggle in Settings β†’ Advanced β†’ DNA tracking
DNA Match person type Select "DNA Match" when creating persons to mark them as genetic matches
DNA Information fields Track shared cM, testing company, kit ID, match type, endogamy flag, notes
dna_match relationship Bidirectional relationship type (A→B automatically creates B→A)
DNA badge in person picker Shows flask icon and shared cM value for DNA Match persons
dna relationship category New category for DNA/genetic relationships

Match Types:

  • BKM β€” Best Known Match (confirmed relationship, high confidence)
  • BMM β€” Best Mystery Match (strong match, relationship unknown)
  • confirmed β€” DNA confirms documented relationship
  • unconfirmed β€” Match recorded but not yet analyzed

Design Philosophy:

  • Charted Roots is not a DNA analysis toolβ€”specialized tools (DNAPainter, Genetic Affairs, etc.) handle that well
  • Focus on tracking "key matches" (BKM/BMM methodology) rather than comprehensive DNA management
  • All features are opt-in via settings; default experience is unchanged

Files Modified:

  • src/settings.ts β€” Added enableDnaTracking setting
  • src/ui/settings-tab.ts β€” Added DNA tracking toggle in Advanced section
  • src/models/person.ts β€” Added personType and DNA properties to interfaces
  • src/ui/create-person-modal.ts β€” Added Person Type dropdown and DNA fields
  • src/ui/control-center.ts β€” Added DNA fields to Edit Person modal
  • src/relationships/types/relationship-types.ts β€” Added dna category, requiresSetting property
  • src/relationships/constants/default-relationship-types.ts β€” Added dna_match relationship type
  • src/core/bidirectional-linker.ts β€” Added syncDnaMatch() and removeDnaMatchLink() methods
  • src/ui/person-picker-modal.ts β€” Added DNA badge rendering

Documentation:


Name Components (v0.19.7)

Explicit name component properties in frontmatter for multi-surname cultures (Hispanic, Portuguese) and maiden/married name tracking.

GitHub Issues: #174, #192

Features Implemented:

Feature Description
given_name property First/given name(s) - populated from GEDCOM GIVN tag
surnames property Array of surnames - supports multiple (Hispanic/Portuguese naming)
maiden_name property Birth surname (already existed with aliases)
married_names property Array of married surnames - supports multiple marriages
Statistics integration Top Surnames counts all surnames in array
Split Wizard integration Matches against all surname variants
GEDCOM import Writes given_name and surnames from GIVN/SURN tags
GEDCOM export Exports name components to GIVN/SURN tags
Create/Edit Person modal Fields for all name component properties

Usage Examples:

Hispanic dual surnames:

name: "JosΓ© GarcΓ­a LΓ³pez"
surnames:
  - GarcΓ­a
  - LΓ³pez

Maiden name tracking:

name: "Jane Smith"
maiden_name: "Jones"

Maiden-name-as-primary convention:

name: "Jane Jones"
married_names:
  - "Smith"
  - "Williams"

Property Priority for Statistics:

When computing surname statistics via extractSurnames():

  1. If surnames array exists β†’ count each surname
  2. Else if maiden_name exists β†’ count that (for maiden-name-primary users)
  3. Else β†’ fall back to parsing last word from name

Files Modified:

  • src/utils/name-utils.ts β€” New: extractSurnames(), extractAllSurnames(), matchesSurname()
  • src/core/property-alias-service.ts β€” Added name component property definitions
  • src/core/family-graph.ts β€” Added PersonNode properties, frontmatter parsing
  • src/statistics/services/statistics-service.ts β€” Uses extractSurnames()
  • src/ui/split-wizard-modal.ts β€” Uses matchesSurname() for matching
  • src/gedcom/gedcom-importer-v2.ts β€” Writes name components to frontmatter
  • src/core/person-note-writer.ts β€” Supports writing name components
  • src/ui/create-person-modal.ts β€” Added name component input fields
  • src/ui/control-center.ts β€” Passes name components to edit modal
  • main.ts β€” Context menu passes name components to edit modal
  • src/gedcom/gedcom-exporter.ts β€” Exports name components to GEDCOM tags

Documentation:


Per-Map Marker Assignment (v0.19.6)

Restrict places to specific custom maps within a universe. Enables regional maps, era-specific views, and detail-level separation without affecting universe-wide filtering.

GitHub Issue: #153

Features Implemented:

Feature Description
maps property Array of map IDs that a place should appear on
Backward compatibility Places without maps appear on all maps in their universe
Path filtering Paths/journeys only appear if both endpoints are visible on current map
Create Place modal "Restrict to maps" checkbox section with available maps
Auto-select current map When creating a place from a pixel map, current map is pre-selected

Usage:

Add a maps property to place notes to restrict which maps they appear on:

name: Fort Ticonderoga
universe: colonial-america
maps:
  - french-indian-war-map
  - revolutionary-war-map

Filtering Logic:

  • If place has no maps property: Shows on all maps with matching universe (existing behavior)
  • If place has maps property: Only shows on specified map(s)
  • Events inherit filtering from their associated places
  • Paths appear only if both endpoints are visible on the current map

UI Integration:

The Create/Edit Place modal includes a "Restrict to maps" section when custom maps exist in the vault:

  • Checkboxes for each available map in the same universe
  • Current map is highlighted and auto-selected when creating from a pixel map click
  • Empty selection means "show on all maps" (backward compatible)

GEDCOM Notes Support (v0.19.5)

Import GEDCOM NOTE tags attached to individuals into person notes, with support for inline notes, multi-line continuation, and shared NOTE record references.

GitHub Issue: #179

Features Implemented:

Feature Description
Inline notes 1 NOTE text tags parsed and imported
Multi-line notes CONT (newline) and CONC (concatenate) continuation support
Referenced notes 1 NOTE @N001@ resolves shared NOTE records
Embedded notes Notes appear in "## Notes" section with "### GEDCOM note" headers
Separate note files Optional toggle creates individual note entity files with wikilinks
Import wizard toggle Step 3 β†’ Entity types β†’ Notes checkbox

Import Options:

  • Import notes (default: on) β€” Import NOTE tags attached to individuals
  • Create separate note files (default: off) β€” Create individual note files instead of embedding content

Output Formats:

Embedded (default):

## Notes

### GEDCOM note

Information from Mary Jones in letter of September 25, 1990.

Separate file (when enabled):

  • Creates Note for {Person Name}.md in Notes folder
  • Person note contains wikilink: - [[Note for John Smith]]

Files Modified:

  • src/gedcom/gedcom-types.ts β€” Added notes, noteRefs to individual interface; added GedcomNoteRecord
  • src/gedcom/gedcom-parser-v2.ts β€” Parse 1 NOTE under INDI; parse 0 @N001@ NOTE records
  • src/gedcom/gedcom-importer-v2.ts β€” Resolve notes, format and write to person notes or create separate files
  • src/gedcom/gedcom-note-formatter.ts β€” New file: formatGedcomNotesSection()
  • src/gedcom/gedcom-import-wizard-modal.ts β€” Added "Import notes" and "Create separate note files" toggles

Documentation:

Future Work:

  • Family-level notes (deferred)
  • GEDCOM export with notes (deferred)

Timeline Event Description Display (v0.19.5)

Timeline now shows event descriptions for all event types when a description exists, instead of showing the generic event title.

GitHub Issue: #157

Features Implemented:

Feature Description
Description display Events show "Type: description" when description exists
Birth/death exception These events continue showing full title with person's name
List and markdown Both timeline list view and markdown export updated

Example:

Before: 1850 β€” Census of John Smith After: 1850 β€” Census: 1850 Federal Census

Implementation:

Changed from allowlist (DESCRIPTION_DISPLAY_TYPES) to blocklist (TITLE_ONLY_TYPES = ['birth', 'death']). All other event types (census, custom, occupation, residence, military, education, marriage, engagement, etc.) now show description when available.

Files Modified:

  • src/dynamic-content/renderers/timeline-renderer.ts β€” Updated display logic in renderTimelineList() and generateMarkdown()

Related:

  • #183 β€” Birth event role filtering (tracked separately)

Romantic Relationship Label Preference (v0.19.5)

UI preference setting to choose whether the plugin displays "Spouse" or "Partner" terminology throughout menus, forms, labels, and wizards. The underlying data model and property names remain unchanged.

GitHub Issue: #167

Features Implemented:

Feature Description
Setting toggle "Romantic relationship label" dropdown in Settings β†’ Sex & gender
Terminology options "Spouse" (default) or "Partner"
UI-only change Affects ~37 UI strings across 14 files; no data model changes

What Changes:

  • All UI labels showing "Spouse"/"Spouses" switch to "Partner"/"Partners" when enabled
  • Includes: person edit modals, family wizard, tree wizard, canvas settings, statistics display

What Does NOT Change:

  • Frontmatter property names (spouse, partners)
  • Property alias mappings
  • Data model and relationship types
  • Export formats (GEDCOM, CSV column headers)

Files Added:

  • src/utils/terminology.ts β€” Helper functions for dynamic terminology

Files Modified:

  • src/settings.ts β€” Setting interface, default value, UI dropdown
  • src/ui/modals/create-person-modal.ts β€” Person creation/editing UI
  • src/ui/wizards/family-creation-wizard.ts β€” Family wizard labels
  • src/control-center/tabs/preferences-tab.ts β€” Display preferences
  • src/ui/modals/canvas-style-modal.ts β€” Canvas style overrides
  • src/ui/wizards/unified-tree-wizard-modal.ts β€” Tree wizard options
  • src/ui/wizards/split-wizard-modal.ts β€” Canvas split options
  • src/ui/views/family-chart-view.ts β€” Family chart relationship labels
  • src/control-center/control-center.ts β€” Statistics display
  • src/ui/modals/merge-wizard-modal.ts β€” Merge preview fields

Documentation:


Partial Date Support (v0.19.2)

Enhanced date handling to preserve partial dates and GEDCOM qualifiers throughout the import/export round-trip, with user-friendly display formatting.

GitHub Issue: #172

Features Implemented:

Feature Description
Partial date import Month-only dates (MAR 1950) preserved as 1950-03 instead of normalizing to 1950-03-01
GEDCOM qualifier preservation ABT, BEF, AFT, CAL, EST qualifiers stored as-is (e.g., ABT 1878)
Date range preservation BET 1882 AND 1885 stored intact
User-friendly display Qualifiers formatted for readability: ABT 1878 β†’ "c. 1878", BEF 1950 β†’ "before 1950"
Export round-trip Partial dates and qualifiers exported back to GEDCOM, Gramps, and GedcomX formats correctly

Display Formatting:

Stored Format Display Format
ABT 1878 c. 1878
BEF 1950 before 1950
AFT 1880 after 1880
CAL 1945 c. 1945
EST 1880 c. 1880
BET 1882 AND 1885 1882–1885
1855-03 Mar 1855
1855-03-15 15 Mar 1855

Files Modified:

  • src/import/gedcom-date-parser.ts β€” Detect and preserve partial dates and qualifiers
  • src/import/gedcom-to-obsidian.ts β€” Updated to use partial date parsing
  • src/dates/utils/date-display.ts β€” New utility for user-friendly display formatting
  • src/dates/services/date-service.ts β€” Added formatDisplayDate() method
  • src/export/gedcom-exporter.ts β€” Export qualifiers in GEDCOM format
  • src/export/gramps-exporter.ts β€” Export qualifiers in Gramps XML format
  • src/export/gedcomx-exporter.ts β€” Export qualifiers in GedcomX JSON format

Plugin Rename: Canvas Roots β†’ Charted Roots (v0.19.0)

Renamed the plugin from "Canvas Roots" to "Charted Roots" to better reflect the plugin's broader genealogical visualization capabilities beyond Obsidian Canvas.

GitHub Issue: #141

Features Implemented:

Feature Description
Plugin rename All code, documentation, and UI updated from "Canvas Roots" to "Charted Roots"
Repository rename GitHub repository renamed from obsidian-canvas-roots to obsidian-charted-roots
Automatic vault migration One-time migration of canvas metadata and code blocks on first load
Backward compatibility Dual-registration for protocol handlers and commands; old identifiers continue working

Migration Service:

The plugin includes a one-time migration service that automatically updates:

  • Canvas metadata: plugin: 'canvas-roots' β†’ plugin: 'charted-roots'
  • Code blocks: canvas-roots-timeline, canvas-roots-relationships, canvas-roots-media β†’ charted-roots-*

What Stays the Same:

  • All vault data and notes
  • CSS class prefixes (cr-*)
  • Property prefixes (cr_*)
  • All plugin functionality and settings

For BRAT Users:

If you installed via BRAT using the old repository name, update your configuration:

  1. Settings β†’ BRAT β†’ Beta Plugin List
  2. Remove banisterious/obsidian-canvas-roots
  3. Add banisterious/obsidian-charted-roots

Files Added:

  • src/migration/plugin-rename-migration-service.ts β€” One-time vault migration service

Documentation:


v0.18.x

Automatic Wikilink Resolution (v0.18.32)

Automatically resolve [[Person Name]] wikilinks in relationship fields to cr_id values, creating family graph relationships without requiring manual _id field population.

GitHub Issue: #104

Features Implemented:

Feature Description
PersonIndexService Centralized service for cr_id ↔ file lookups with caching
FamilyGraph integration Wikilinks in relationship fields automatically resolve to cr_id values
Data Quality warnings Ambiguous wikilinks (multiple files with same basename) surface in Data Quality report
Performance optimization Index built on plugin load, updated incrementally via metadataCache events
Service consolidation RelationshipValidator and ProofSummaryService use centralized PersonIndexService

Key Design Decisions:

Decision Rationale
Precedence Explicit _id fields always take precedence over wikilink resolution
Read-only Resolution does not modify user files
Ambiguity handling When multiple files share the same basename, resolution returns null and a warning is shown

Example:

# Before: Required explicit _id field
father: "[[John Smith]]"
father_id: "abc-123-def-456"

# After: Just the wikilink works
father: "[[John Smith]]"
# cr_id resolved automatically from John Smith.md

Files Added:

  • src/core/person-index-service.ts β€” Centralized person index with wikilink resolution

Files Modified:

  • src/core/family-graph.ts β€” Integrated PersonIndexService for wikilink resolution
  • src/core/relationship-validator.ts β€” Uses PersonIndexService for cr_id lookups
  • src/sources/services/proof-summary-service.ts β€” Uses PersonIndexService for wikilink resolution
  • src/core/data-quality.ts β€” Added ambiguous wikilink detection
  • main.ts β€” Initializes and wires PersonIndexService to all consumers

Documentation:


MyHeritage GEDCOM Import Compatibility (v0.18.28)

Automatic detection and preprocessing of MyHeritage GEDCOM exports to fix vendor-specific formatting issues.

GitHub Issue: #144

Features Implemented:

Feature Description
Auto-detection Detects MyHeritage GEDCOM files by 1 SOUR MYHERITAGE tag and double-encoded entities
UTF-8 BOM removal Strips byte order mark that prevents parsing
Double-encoded entity fix Decodes &amp;lt; β†’ <, &amp;nbsp; β†’ space, etc.
Single-encoded entity fix Handles mixed encoding (single + double in same file)
<br> tag conversion Converts <br> and <br/> to newlines
Decorative HTML stripping Removes <a>text</a> tags without href attributes
Compatibility mode setting Settings β†’ Data & detection with Auto/MyHeritage/None options
Import results reporting Shows preprocessing fixes applied in import results modal

Compatibility Modes:

Mode Behavior
Auto (default) Detect MyHeritage files and apply fixes automatically
MyHeritage Always apply fixes (for manually edited files)
None Disable preprocessing (original behavior)

Files Added:

  • src/gedcom/gedcom-preprocessor.ts β€” Preprocessing logic with detection and fixes

Files Modified:

  • src/gedcom/gedcom-importer-v2.ts β€” Integrated preprocessor into import pipeline
  • src/gedcom/gedcom-types.ts β€” Added preprocessingApplied and preprocessingFixes to result types
  • src/settings.ts β€” Added gedcomCompatibilityMode setting and UI control
  • src/ui/gedcom-import-modal.ts β€” Display preprocessing info in import results

Documentation:


Optional Person Names (v0.18.27)

Create placeholder person notes without names, filling in details later as research progresses.

GitHub Issue: #140

Features Implemented:

Feature Description
Optional name fields Given Name and Surname are no longer required when creating person notes
Unnamed display Unnamed persons display as "Unnamed" in Family Wizard and other UI components
Data quality warning NO_NAME warning added to Data Quality Report for persons without names
Completeness metrics withName metric added to track persons with names vs. unnamed

Use Case:

Genealogists often know relationships before identities. For example, "John's father existed" is known before discovering the father's name. This feature allows creating placeholder persons to build family structures, then filling in names as research progresses.

Files Modified:

  • src/core/person-note-writer.ts β€” Made name property optional in PersonData interface
  • src/ui/create-person-modal.ts β€” Removed name validation, allowing empty names
  • src/ui/family-creation-wizard.ts β€” Display "Unnamed" fallback for persons without names
  • src/core/data-quality.ts β€” Added NO_NAME warning and withName completeness metric

Documentation:


DMS Coordinate Conversion (v0.18.27)

Opt-in DMS (degrees, minutes, seconds) parsing for coordinate input in place creation.

GitHub Issue: #121

Features Implemented:

Feature Description
DMS format parsing Enter coordinates like 33Β°51'08"N or 33 51 08 N
Auto-conversion DMS automatically converts to decimal degrees for storage
Opt-in setting Enable via Settings β†’ Data & detection β†’ "Accept DMS coordinate format"
Multiple formats Supports symbol notation, space-separated, hyphen-separated, and direction prefix

Supported Formats:

  • 33Β°51'08"N β€” Standard DMS with symbols
  • 33 51 08 N β€” Space-separated
  • 33-51-08-N β€” Hyphen-separated
  • N 33 51 08 β€” Direction prefix
  • 33.8522 β€” Decimal pass-through (always supported)

Files Modified:

  • src/utils/coordinate-converter.ts β€” New DMS parsing utility
  • src/settings.ts β€” Added enableDMSCoordinates setting
  • src/ui/create-place-modal.ts β€” Integrated DMS parser into coordinate inputs

Documentation:


DNA Match Tracking - Phase 1 (v0.18.27)

Lightweight DNA match tracking for genetic genealogy workflows.

GitHub Issue: #126

Features Implemented:

Feature Description
DNA match template Template snippet in template snippets modal with Templater prompts
Bases view "DNA Matches" view in People Bases template, filtered by dna_shared_cm
Property display names Friendly names for DNA properties in Bases views
Documented properties dna_shared_cm, dna_testing_company, dna_kit_id, dna_match_type, dna_endogamy_flag, dna_notes

DNA Properties:

Property Description Example
dna_shared_cm Shared centiMorgans 1847
dna_testing_company Testing company AncestryDNA, 23andMe, FamilyTreeDNA
dna_kit_id Kit identifier ABC123
dna_match_type Match classification BKM, BMM, confirmed, unconfirmed
dna_endogamy_flag Endogamy indicator true / false
dna_notes Free-form notes Matches on chromosome 7

Match Types:

  • BKM β€” Best Known Match (confirmed relationship, high confidence)
  • BMM β€” Best Mystery Match (strong match, relationship unknown)
  • confirmed β€” DNA confirms documented relationship
  • unconfirmed β€” Match recorded but not yet analyzed

Files Modified:

  • src/ui/template-snippets-modal.ts β€” Added DNA match template
  • src/constants/base-template.ts β€” Added DNA Matches view and property display names

Future Phases:

Phase 2-4 (planned) will add UI support for DNA Match person subtype, DNA relationship type, and visualization/reports. See DNA Match Tracking Planning for details.


Web Clipper Integration - Phase 1 (v0.18.25)

Auto-detect and manage web-clipped notes in staging workflow with dedicated filtering and tracking.

GitHub Issue: #128

Features Implemented:

Feature Description
Clipper metadata detection Auto-detect notes with clip_source_type, clipped_from, or clipped_date properties
File watcher integration Real-time detection when Web Clipper creates notes in staging folder
Dashboard indicator Unified "Staging" card shows breakdown: X clipped / Y other notes
Staging Manager filtering Toggle buttons: [All] [Clipped] [Other] with multi-level filtering
Unread clip tracking Unread clip count resets when Staging Manager opens
Template flexibility Works with any user-created Web Clipper templates

Clipper Metadata Properties:

Property Description Example
clip_source_type Type of clipped content obituary, census, article
clipped_from Original source URL https://example.com/article
clipped_date Date content was clipped 2026-01-05

Staging Workflow Integration:

Web Clipper creates note β†’ Auto-detected in staging β†’
Review in Staging Manager (filter: Clipped) β†’
Promote to main tree β†’ Cleanup

Multi-Level Filtering:

The toggle buttons filter at three levels:

  1. Stats summary β€” Only counts matching entities
  2. Batch cards β€” Only shows batches containing matches
  3. File lists β€” Only shows matching files within batches

UI Components:

Component Description
Dashboard card Shows "X clipped / Y other" breakdown when clips present
Filter toggles Three buttons: All (default), Clipped, Other
Active state Selected filter highlighted with accent color
Empty states "No clipped notes" / "No other notes" when filter yields no results

Files Modified:

  • src/core/staging-service.ts β€” Clipper metadata detection, file watcher
  • src/ui/staging-management-modal.ts β€” Filter UI and multi-level filtering logic
  • src/ui/views/control-center-view.ts β€” Dashboard card hybrid visibility
  • styles/staging-manager.css β€” Filter button styles

Documentation:

Future Phases:

Phase 2 and beyond (planned):

  • LLM extraction guidance in wiki
  • Multi-person clipping from census pages
  • Auto-create source notes linked to clipped people

Staging Management (v0.18.24)

Dedicated UI for managing staged imports with batch organization, duplicate detection, and promotion workflow.

GitHub Issue: #137

Features Implemented:

Feature Description
Staging Manager modal Accessible via Control Center β†’ Staging Manager button
Batch organization Imports grouped by timestamped subfolder (YYYY-MM-DD_HH-mm-ss)
Entity breakdown Count of people, places, sources, events, organizations per batch
Duplicate detection Cross-import detection identifies potential duplicates across batches
Expandable file lists Click batch headers to preview individual entities before promoting
Promote to main tree Move staged entities from staging folder to main tree folder
Batch cleanup Delete batches after promotion or when no longer needed
File preview Click any file row to open in new tab for review

Staging Workflow:

Import β†’ Review in Staging Manager β†’ Resolve duplicates β†’ Promote β†’ Cleanup

Duplicate Detection:

Factor Weight Description
Name similarity 60% Levenshtein distance comparison
Date proximity 30% Birth/death year within threshold
Gender match 5% bonus Additional confidence when genders match

Default thresholds: minConfidence=60, minNameSimilarity=70, maxYearDifference=5

UI Components:

Component Description
Stats summary Total files, batches, and potential duplicates
Batch cards Collapsible cards showing batch info and entity counts
Entity type badges Color-coded badges (person, place, source, event, org)
Chevron toggle Visual indicator for expand/collapse state
Action buttons Promote All, Delete Batch per batch card

Files Modified:

  • src/ui/staging-management-modal.ts β€” Main modal with batch listing and file preview
  • src/core/staging-service.ts β€” Staging folder operations and file retrieval
  • src/core/cross-import-detection.ts β€” Duplicate detection algorithms
  • styles/staging-manager.css β€” Modal and file list styling

Documentation:


Export Privacy & Sensitive Data (v0.18.22)

Comprehensive privacy protection for sensitive genealogical data during exports and canvas generation.

GitHub Issue: #95

Features Implemented:

Phase Feature Description
1 Sensitive field redaction SSN, identity numbers automatically excluded via PersonNode whitelist
2 cr_living override Manual frontmatter property to override automatic living detection
3 Private fields list private_fields frontmatter property for user-defined sensitive fields
4-5 Deadname + Export warnings Confirmation dialog before exporting private fields
6 Discoverability Post-import notice, export preview warning when privacy disabled
7 Pronouns field pronouns property displayed in pickers and reports
8 Canvas privacy Privacy-aware canvas/Excalidraw generation in Tree Wizard

Canvas Privacy Protection:

Feature Description
Text node obfuscation Living persons shown as text nodes with obfuscated names
Hidden option Exclude living persons entirely from generated canvas
Wikilinks preserved Text nodes include [[filename]] for navigation
Preview integration Wizard shows count of privacy-protected persons
Format choice "Text node" (obfuscated) or "File node" (clickable)

Known Limitations:

  • Canvas JSON is plain text (not encrypted)
  • File nodes reveal identity in canvas JSON
  • Wikilinks in text nodes contain original filename
  • Privacy applied at generation time only (no runtime toggle)

Files Modified:

  • src/core/canvas-generator.ts β€” Privacy node creation helpers
  • src/core/privacy-service.ts β€” Sensitive field utilities
  • src/trees/ui/unified-tree-wizard-modal.ts β€” Privacy UI and preview count

Documentation:


Card Style Options (v0.18.15)

Choose from 4 card styles in Family Chart view to match your visualization needs.

Card Styles:

Style Description
Rectangle Default style with avatar thumbnails and full details (name, dates)
Circle Circular avatar cards with name labels below
Compact Text-only cards without avatars for denser layouts
Mini Smaller name-only cards for high-level overviews

Features:

Feature Description
Style menu Access via toolbar Style menu β†’ Card Style submenu
State persistence Card style persists across Obsidian restarts
Export support PNG/PDF export works with all card styles including Circle
Open note button Appears on all card styles (smaller on Mini)

Technical Details:

  • Rectangle, Compact, Mini use SVG card renderer
  • Circle uses HTML card renderer with custom styling
  • Circle cards are converted to native SVG elements during export to avoid tainted canvas issues
  • State is saved immediately when changing style via requestSaveLayout()

Files Modified:

File Changes
src/ui/views/family-chart-view.ts Card style state, menu, renderer switching, export embedding
styles/family-chart-view.css Circle card styles, gender-based colors

Gramps Notes Integration (v0.18.13)

Import notes attached to Gramps entities during Gramps XML/.gpkg import.

Phased Implementation:

Phase Feature Version Status
1 Embedded person notes v0.18.13 βœ… Complete
2 Other entity notes (events, places) v0.18.13 βœ… Complete
3 Family entity type β€” Deferred
4 Separate note files v0.18.15 βœ… Complete
5 Export & sync back to Gramps β€” Deferred

Phase 1-2: Embedded Notes (v0.18.13)

Feature Description
Person notes Import notes attached to persons as "## Notes" section at bottom of person note
Multiple notes Organized by type (e.g., "### Research", "### Person Note")
Style conversion Bold, italic, strikethrough, underline, superscript, subscript, links
Formatted notes Preformatted notes wrapped in code fences to preserve whitespace
Privacy handling private: true added to frontmatter if any note has privacy flag
Event notes Notes attached to events appended to event note content
Place notes Notes attached to places appended to place note content
Family notes Family-level notes attached to marriage/partnership events
Import wizard Toggle to enable/disable notes import (enabled by default)

Phase 4: Separate Note Files (v0.18.15)

Feature Description
Import option "Create separate note files" checkbox in Gramps import wizard (opt-in)
Note entities Notes created as cr_type: note entities in configured Notes folder
Note naming Generated from type + first referencing entity (e.g., "Research on John Smith")
Entity linking Entity notes sections use wikilinks instead of embedded content
Create Note modal Manual note creation with note type, title, privacy toggle, linked entities
Context menu "New Charted Roots note" in Notes folder right-click menu
Command palette "Charted Roots: Create note" command
Bases template Notes base template with 11 views

Deferred Phases:

Phases 3 (Family Entity) and 5 (Export & Sync) are deferred indefinitely pending user demand. See planning doc for rationale.

Documentation:


Edit Person Events & Sources (v0.18.14)

Add events and sources sections to the Edit Person modal, allowing users to manage all person-related data from a single interface instead of editing multiple notes separately.

Features:

Feature Description
Sources section Multi-value picker to link source notes with Link and Create buttons
Source storage Stores as sources (wikilinks) and sources_id (cr_ids) arrays for reliable linking
Events section Display events referencing this person with type badges and dates
Event linking Link/unlink existing events or create new events with person pre-filled
Type badges Color-coded type badges for both events and sources matching picker modal styles

Data Model:

  • Events use inverse relationships: event notes contain persons: ["[[Person]]"]
  • Linking an event from the person modal modifies the event note, not the person note
  • Sources follow the dual-storage pattern: sources (wikilinks) + sources_id (cr_ids)

Implementation:

Phase Feature Status
1 Sources section with multi-value picker βœ… Complete
2 Events section with link/create/unlink βœ… Complete
3 Polish (type badges, display formatting) βœ… Complete

Bug Fixes:

Fix Description
Context menu Edit Person Fixed missing plugin reference causing "Plugin not available" error
Children display Fixed children displaying as cr_ids instead of names (#86)

Documentation:


Cleanup Wizard Phase 4 (v0.18.11)

User experience refinements for the Post-Import Cleanup Wizard, improving accessibility and feedback during batch operations.

Features:

Feature Description
Batch Progress Indicators Real-time progress bars during batch operations (Steps 2-6, 10-14) showing current/total count and current file
Keyboard Navigation Full keyboard accessibility: arrow keys for tile selection, Enter/Space to activate, Escape to go back

Batch Progress Implementation:

  • Progress callbacks added to all batch methods in DataQualityService and migration services
  • UI re-renders every 5 items to show progress without excessive updates
  • Displays "Processing X of Y notes..." with animated progress bar
  • Shows current filename being processed

Keyboard Navigation Implementation:

  • Arrow keys navigate between tiles on overview screen
  • Enter/Space activates focused tile
  • Escape returns to overview or closes modal from step view
  • ARIA attributes (role, aria-label) for screen reader accessibility
  • Visual focus indicators matching hover styles

Remaining Phase 4 Tasks (Deferred):

Task Status Notes
Step Reordering Deferred Drag-drop tiles with dependency validation
Cleanup Profiles Deferred Save/load named configurations
Step Animations Deferred Smooth transitions between views
Schema Integration Deferred Depends on schema validation feature

Documentation:


Property Naming Normalization (v0.18.11)

Standardized property naming for consistency and Obsidian compatibility, completing the child β†’ children migration.

Problem Solved:

The codebase had inconsistent naming for the children wikilink property:

  • child (singular) - legacy, used by older code paths
  • children (plural) - preferred, matches children_id

This caused duplicate properties to appear in YAML when both systems wrote to the same note.

Solution:

Component Change
Cleanup Wizard Step 14 Batch migrate child β†’ children across vault with preview
Documentation children marked as canonical in Frontmatter-Reference.md
Deprecation Notice Clear deprecation note with migration instructions
Wizard Extensibility Fixed hardcoded step count to use WIZARD_STEPS.length

Migration Logic:

  • Detects person notes with legacy child property
  • Merges with existing children if both exist (deduplicates)
  • Removes legacy child property after migration

Backward Compatibility:

  • Plugin reads both child and children during transition
  • Future breaking change to remove child read support planned

Documentation:


Custom Map Authoring (v0.18.10)

Streamlined custom map creation and place positioning, eliminating manual coordinate entry.

Features:

Feature Description
Map Creation Wizard 4-step guided wizard: select image β†’ configure map β†’ add initial places β†’ review & create
Right-Click to Create Place Right-click on custom map β†’ "Create place here" β†’ coordinates auto-filled
Draggable Place Markers Drag markers to reposition (edit mode required), auto-update frontmatter, undo support
Place Marker Context Menu Right-click markers to edit, open note, or copy coordinates
Icon-Only Toolbar Map View toolbar buttons converted to icons with tooltips for space efficiency

Map Creation Wizard Steps:

  1. Select Image β€” Browse vault for map image with preview and auto-detected dimensions
  2. Configure Map β€” Set name, universe (optional), coordinate system (pixel default for fantasy maps)
  3. Add Places β€” Click on map preview to add initial locations (optional, can skip)
  4. Review & Create β€” Summary view, then create map note and all place notes at once

Entry Points:

  • Control Center β†’ Maps β†’ "Create map wizard"
  • Command palette: "Charted Roots: Create custom map"
  • Context menu on image files: "Use as custom map"

Technical Notes:

  • Wizard supports inline universe creation
  • Modal state persistence allows resuming interrupted sessions
  • Coordinates properly convert between DOM (y=0 at top) and Leaflet Simple CRS (y=0 at bottom)

Documentation:


Nested Properties Redesign (v0.18.9)

Redesigned two features to use Obsidian-compatible flat property formats, eliminating "Type mismatch" warnings in the Properties panel and preventing data corruption.

Problem Solved:

Two plugin features used nested YAML structures incompatible with Obsidian's property panel:

  • sourced_facts (Evidence Tracking) - nested objects with source arrays
  • events (Life Events) - inline array of event objects

This caused "Type mismatch" warnings and risked data corruption if users clicked "update" in the property panel. (GitHub Issue #52)

Solution:

1. Evidence Tracking β†’ Flat Properties

Replaced nested sourced_facts object with individual flat properties:

# Old format (nested - incompatible)
sourced_facts:
  birth_date:
    sources:
      - "[[Census 1870]]"
  death_date:
    sources:
      - "[[Death Certificate]]"

# New format (flat - compatible)
sourced_birth_date:
  - "[[Census 1870]]"
sourced_death_date:
  - "[[Death Certificate]]"

10 flat properties for each fact type:

  • sourced_birth_date, sourced_birth_place
  • sourced_death_date, sourced_death_place
  • sourced_parents, sourced_spouse
  • sourced_marriage_date, sourced_marriage_place
  • sourced_occupation, sourced_residence

2. Life Events β†’ Event Note Files

Replaced inline events array with separate event note files:

# Old format (inline array - incompatible)
events:
  - event_type: residence
    place: "[[New York]]"
    date_from: "1920"

# New format (event note links - compatible)
life_events:
  - "[[Events/John Smith - Residence 1920]]"

Each event becomes a first-class note with full frontmatter, enabling:

  • Searchability and linking
  • Tags and attachments
  • Source citations per event
  • Organized in Events folder

3. Cleanup Wizard Integration

Added two new migration steps (now 13-step wizard):

Step Name Description
12 Migrate Evidence Tracking Convert sourced_facts β†’ sourced_* flat properties
13 Migrate Life Events Convert inline events β†’ event note files with life_events links

4. Migration Notice (v0.18.9)

  • Shows on upgrade to v0.18.9+
  • Explains both migrations with before/after examples
  • Checkmarks indicate completed migrations
  • "Open Cleanup Wizard" button for migration
  • "Skip for now" button as escape hatch

5. Backward Compatibility

  • Plugin reads both old and new formats during transition
  • Old data continues to work until migrated
  • Migration can be done at user's convenience

Benefits:

  • No more "Type mismatch" warnings in Properties panel
  • Safe to edit properties without data corruption
  • Better Dataview and Bases compatibility
  • Each event as a note enables linking, tags, and attachments

Files Changed:

  • src/sources/types/source-types.ts - New property types and mappings
  • src/types/frontmatter.ts - New sourced_* and life_events properties
  • src/sources/services/evidence-service.ts - Dual-format reading
  • src/ui/control-center.ts - Write to flat properties
  • src/sources/services/sourced-facts-migration-service.ts - Step 12 migration
  • src/events/services/life-events-migration-service.ts - Step 13 migration
  • src/ui/cleanup-wizard-modal.ts - Steps 12 and 13
  • src/ui/views/migration-notice-view.ts - v0.18.9 notice
  • src/settings.ts - Migration completion tracking

Inclusive Parent Relationships (v0.18.7)

Opt-in gender-neutral parent relationship support allowing users to represent diverse family structures while preserving traditional father/mother fields.

Problem Solved:

Users with nonbinary parents or those who prefer gender-neutral terminology had no way to represent these relationships. The plugin only supported gendered parent fields (father/mother), which doesn't accommodate all family structures.

User Request: "What if one or both parents are nonbinary? Could you add a 'Parent' option to father/mother?" (GitHub Issue #63)

Solution:

A complete opt-in gender-neutral parent system that coexists with traditional relationships:

1. Settings (Control Center > Preferences)

  • Enable Inclusive Parents toggle (default: OFF)
  • Parent Field Label text setting for customization (default: "Parents")
    • Examples: "Parents", "Guardians", "Progenitors", "Lolos"
    • Label shown in UI only; frontmatter always uses parents property
  • Conditional visibility: label setting only shown when toggle enabled

2. Schema Changes

  • New parents property (wikilinks, can be array for multiple parents)
  • New parents_id property (Charted Roots IDs, dual storage pattern)
  • Independent of father/mother β€” users can use either or both
  • Supports mixed usage for blended families or migration scenarios

3. Create/Edit Person Modal

  • Parents field appears when setting enabled (above father/mother)
  • Multi-select person picker (same pattern as children field)
  • Inline parent creation via person picker
  • No gender pre-fill (unlike father/mother)
  • Uses custom label from settings

4. Family Graph Integration

  • FamilyGraphService reads parents/parents_id relationships
  • Included in ancestor/descendant calculations
  • Same treatment as father/mother for graph traversal
  • Spouse edges between 2 parents (same pattern as father/mother)
  • Priority order for fallback: biological β†’ gender-neutral β†’ adoptive

5. Bidirectional Linking

  • When person added to parents array, automatically adds to each parent's children array
  • Uses dual storage: both wikilinks (parents) and IDs (parents_id)
  • Deduplication prevents duplicate entries
  • Handles removal: when parent removed, child removed from their children
  • Supports aliased wikilinks ([[basename|name]]) when filename differs from name

6. Relationship Displays

  • Relationships Block (canvas-roots-relationships): Shows parents with "Parent" label
  • Family Chart View: Displays gender-neutral parents in interactive tree
  • Sibling Detection: Checks gender-neutral parents' children for siblings

Design Principles:

  1. Opt-in, not replacement β€” Father/mother fields remain; this adds alongside
  2. Configurable β€” Users customize terminology to their preference
  3. Non-disruptive β€” Users with traditional setups see no UI changes
  4. Coexistent β€” Can use father, mother, AND parents simultaneously

Schema Example:

# Child's note
name: Jamie Smith
parents:
  - "[[Alex Smith]]"
  - "[[Jordan Smith]]"
parents_id:
  - "I0045"
  - "I0046"
# Parent's note (automatically updated via bidirectional linking)
name: Alex Smith
children:
  - "[[Jamie Smith]]"
children_id:
  - "I0050"

Implementation:

Files Modified:

User Benefits:

  • Represents nonbinary parents respectfully
  • Supports diverse family structures (queer families, cultural variations)
  • Fully customizable terminology
  • Backward compatible β€” no disruption to existing workflows
  • Full integration across all family graph features

Technical Details:

Dual Storage Pattern:

# Both wikilinks and IDs stored for flexibility
parents: ["[[Alex Smith]]"]  # For display and linking
parents_id: ["I0045"]         # For reliable graph traversal

Priority Order (Fallback Logic):

  1. Check biological parents (father/mother)
  2. If none, check gender-neutral parents
  3. If none, check adoptive parents

Bidirectional Sync:

  • Uses same children array as father/mother relationships
  • Each parent in parents array gets child added to their children
  • Deduplication by both cr_id and wikilink
  • Deletion detection for relationship cleanup

Planning Documentation:


Media Upload and Management Enhancement (v0.18.6)

Comprehensive media upload and management system allowing users to upload files directly from Charted Roots and link them to entities without manual file management.

Problem Solved:

Users could link existing vault files to entities (people, places, events, etc.), but had no way to upload new files directly from the plugin. This required breaking the workflow to manually add files to the vault before linking them, creating friction when attaching scanned documents, photos, or certificates to research.

User Request: "Can't link the Birth Certificate or picture" (GitHub Issue #60)

Solution:

A complete media upload and linking system with multiple workflows:

1. Settings Enhancement

  • Drag-and-drop reordering of media folders in Preferences
  • First folder in list becomes upload destination
  • Visual feedback during drag operations

2. Expanded Media Manager Dashboard

  • 6-tile layout (3Γ—2 grid) vs. previous 4-tile layout
  • Row 1 (Browse & Discover):
    • Linked Media Gallery β€” view all linked media with filters
    • Find Unlinked β€” discover orphaned media files
    • Source Media Linker β€” smart filename-based matching
  • Row 2 (Add & Link):
    • Upload Media β€” standalone file upload with optional linking
    • Link Media β€” media-first workflow (select files β†’ choose entities)
    • Bulk Link to Entities β€” entity-first workflow (select entities β†’ choose files)

3. Standalone Upload Modal

  • Drag-and-drop file upload with browse fallback
  • Upload to first configured media folder
  • Read-only destination display with helpful hint
  • Multiple file selection
  • Auto-rename collision handling (incremental numbering)
  • File type validation
  • Optional entity linking after upload

4. Inline Upload in Media Picker

  • "Upload files..." button in MediaPickerModal
  • Follows PlacePickerModal "Create new place" pattern
  • Auto-selects newly uploaded files
  • Available in both context menu and Dashboard workflows

5. Entity Picker Modal

  • Select entities after choosing media files (media-first workflow)
  • Supports all entity types: Person, Event, Place, Organization, Source
  • Person-specific filters:
    • Living status: All / Living only / Deceased only
    • Birth date: All / Has date / Missing date
    • Sex: All / Male / Female
  • Person-specific sorting:
    • Name (A-Z / Z-A)
    • Birth year (oldest first / youngest first)
    • Recently modified
  • Shows which entities already have selected media linked
  • Bulk linking with progress modal for β‰₯5 entities

6. Consistent Upload Availability

  • Context menu flow: Right-click entity β†’ Media β†’ Link media β†’ Upload files
  • Media Manager tile: Link Media β†’ Upload files
  • Both workflows use same enhanced MediaPickerModal

Architecture:

"Read Many, Write One" model:

  • Files upload to mediaFolders[0] (first configured folder)
  • MediaPickerModal browses ALL media folders
  • Users can reorganize files later via Obsidian's file explorer
  • Drag-and-drop reordering in settings allows changing upload destination

Key Design Decisions:

  • Media folders separate from maps folder (maps via place map picker)
  • No destination dropdown (simplified UX, predictable behavior)
  • Auto-rename collision handling vs. prompting user
  • Inline upload in existing modals vs. separate upload-only modal

Implementation:

Files Created:

  • src/core/ui/media-upload-modal.ts (302 lines) β€” Standalone upload modal
  • src/core/ui/entity-picker-modal.ts (608 lines) β€” Entity selection with filtering

Files Modified:

  • src/ui/preferences-tab.ts β€” Drag-and-drop reordering
  • src/core/ui/media-manager-modal.ts β€” 6-tile layout
  • src/core/ui/media-picker-modal.ts β€” Inline upload button
  • main.ts β€” Context menu upload support
  • styles/preferences.css β€” Folder reordering styles
  • styles/media-modals.css β€” Upload and entity picker styles

User Benefits:

  • No context switching to add files to vault
  • Streamlined workflow for attaching documents to research
  • Consistent upload experience across all entry points
  • Visual media folder management in settings
  • Powerful entity selection with filters for media-first workflows

Technical Details:

Uses Obsidian Vault API:

await this.app.vault.createBinary(path, arrayBuffer)

Auto-rename collision handling:

  • photo.jpg β†’ photo 1.jpg β†’ photo 2.jpg (incremental)
  • Prevents overwrite accidents
  • Allows quick bulk uploads without manual renaming

Planning Documentation:


Timeline Export Consolidation (v0.18.2)

Consolidated all timeline export functionality from the Events tab Export card into the unified Reports wizard, creating a single comprehensive experience with all 8 export formats.

Problem Solved:

Timeline exports existed in two separate locations with different capabilities:

Location Formats Strengths Weaknesses
Events tab β†’ Export card Canvas, Excalidraw, 4 markdown formats Visual exports, styling options No PDF/ODT, no date range filter
Reports β†’ Timeline PDF, ODT, markdown table Document exports, advanced filters No Canvas/Excalidraw, limited markdown

Users had to navigate between two different UIs to access the full range of export options.

Solution:

All timeline export capabilities are now unified in Statistics & Reports β†’ Reports β†’ Timeline:

Category Formats
Visual exports Canvas, Excalidraw (requires Excalidraw plugin)
Documents PDF, ODT
Markdown Vertical timeline (callouts), Table, Simple list, Dataview query

Consolidated Features:

Feature Source
All filters Person, event type, group, place, universe, date range
Canvas/Excalidraw styling Layout (horizontal/vertical/Gantt), color scheme, ordering edges
Excalidraw drawing options Style, font, stroke width
PDF/ODT options Page size, date format, cover page
Grouping options None, by year, by decade, by person, by place
Data quality insights Timeline gaps, unsourced events, orphan events

Implementation Phases:

Phase Description Status
1 Add Canvas, Excalidraw, and additional markdown formats to Reports Timeline βœ“ Complete
2 Redesign wizard steps for format selection and format-specific options βœ“ Complete
3 Add deprecation notice to Events tab Export card βœ“ Complete

Deprecation Notice:

The Events tab Export card now displays a notice directing users to the Reports wizard. The Export card will be removed in a future release.

Documentation:


Create Person Enhancements (v0.18.1)

A comprehensive set of enhancements to streamline family tree creation, addressing the tedious workflow of jumping in and out of modals when building a family tree from scratch.

Problem Solved:

Building a family tree required constant context-switching:

  1. Create Person A β†’ Save β†’ Close
  2. Create Person B β†’ Save β†’ Close
  3. Edit Person A to link B as spouse β†’ Save β†’ Close
  4. Create Child C β†’ Save β†’ Close
  5. Edit Child C to set parents β†’ Save β†’ Close
  6. ...repeat endlessly

Solution:

Four phases of enhancements enable continuous family creation without leaving the modal flow.

Phase 1: Inline Person Creation

Feature Description
"Create new" in pickers When selecting father/mother/spouse, offer "Create new person" option
QuickCreatePersonModal Simplified sub-modal for creating new family members inline
Smart defaults Pre-fill sex for parents (father→male, mother→female)
Folder context menu "Create person" option in People folder context menu

Phase 2: Children Section in Edit Modal

Feature Description
Children picker Multi-select person picker to view/manage children in Edit mode
Inline creation Create new children directly using Phase 1 infrastructure
Auto-detection Infer father/mother field from parent's sex property
Bidirectional sync Adding/removing children updates both parent and child notes

Phase 3: "Add Another" Flow

After creating a person, the modal shows quick actions to continue building the family:

  • Add spouse β†’ Opens spouse picker with inline creation
  • Add child β†’ Opens child picker with inline creation
  • Add parent β†’ Shows father/mother choice, then opens parent picker
  • Done β†’ Closes modal

Phase 4: Family Creation Wizard

A dedicated 5-step wizard for creating an entire nuclear family at once:

Step Description
Start Choose mode: start from scratch or build around existing person
Step 1 Create central person (name, nickname, sex, birth date)
Step 2 Add spouse(s) β€” supports multiple, create new or pick existing
Step 3 Add children β€” create new or pick existing
Step 4 Add parents (father and mother)
Step 5 Review β€” visual family tree preview, stats summary, confirm

Wizard Features:

Feature Description
Visual tree preview Mini family tree showing all members with initials
Batch creation All notes created with relationships automatically linked
Merge logic Links existing persons without overwriting their existing relationships
State persistence Resume interrupted wizard sessions via ModalStatePersistence
Multiple entry points Command palette, Dashboard tile, People tab, folder context menu

Bundled Enhancement: Nickname Property

Added nickname as a first-class frontmatter property:

  • Added to PersonData interface
  • Supported in Create/Edit Person and QuickCreate modals
  • Import support for GEDCOM (NICK), Gramps (nick), and GEDCOM X

Entry Points:

Entry Point Action
Command: Charted Roots: Create family wizard Opens Family Creation Wizard
Dashboard β†’ Create Family tile Opens Family Creation Wizard
People tab β†’ Actions β†’ Create family Opens Family Creation Wizard
People folder context menu β†’ Create family Opens wizard with folder pre-selected
People folder context menu β†’ Create person Opens CreatePersonModal with folder pre-selected

Documentation:


Event Person Property Consolidation (v0.18.0)

Consolidates the dual person/persons event properties into a single unified persons array format for all event types.

Problem Solved:

Event notes previously used two different properties to track participants:

  • person (string): Single participant for individual events (birth, death, occupation)
  • persons (array): Multiple participants for family events (marriage, divorce, residence)

This duality created complexity in base templates (required formula workarounds), importers (must decide which property to use), and user understanding.

Solution:

All events now use the persons array property. Single-participant events simply have an array with one element:

# Single-participant event
persons:
  - "[[John Smith]]"

# Multi-participant event
persons:
  - "[[John Smith]]"
  - "[[Jane Doe]]"

Features:

Feature Description
Unified Property All importers (GEDCOM, Gramps, GEDCOM X) now write persons array
Migration Wizard Step Cleanup Wizard Step 11 detects and migrates legacy person properties
Backward Compatibility Base templates and services continue reading both properties
Migration Notice Users upgrading from v0.17.x see a one-time notice with migration guidance

Migration:

  1. Open the Cleanup Wizard (Control Center β†’ Data Quality, or command palette)
  2. Navigate to Step 11: "Migrate Event Person Properties"
  3. Review detected notes and click "Apply All"

The legacy person property continues to be read indefinitely for backward compatibility.

Documentation:


v0.17.x

Research Level Property (v0.17.5)

A research_level property for Person notes to track research progress toward GPS-compliant documentation. Based on Yvette Hoitink's "Six Levels of Ancestral Profiles" system.

Problem Solved:

Genealogists need a way to track how thoroughly each ancestor has been researched, supporting the GPS principle of "reasonably exhaustive research." Previously, there was no standardized way to indicate which ancestors need more work.

Research Levels:

Level Name Description
0 Unidentified Ancestor exists but no name established (placeholder)
1 Name Only Name known, appears in others' records, no vital dates
2 Vital Statistics Birth, marriage, death dates researched
3 Life Events Occupations, residences, children, spouses documented
4 Extended Records Property, military, religion, legal records researched
5 GPS Complete Exhaustive research complete, written proof summary exists
6 Biography Full narrative biography with historical context

Features:

Feature Description
Edit Modal Selector Dropdown in Create/Edit Person modal to set research level
Research Gaps Report Filter/sort by level, show statistics by level range
Bases Views "By research level" grouped view, "Needs research" filtered view
GEDCOM Export _RESEARCH_LEVEL custom tag
Gramps Export <attribute type="Research Level"> element
Round-trip Import Both formats import back into research_level property

UI Integration:

The research level selector appears in the Edit Person modal when trackFactSourcing is enabled in settings. The "(Not assessed)" option allows distinguishing between "not yet evaluated" and "Level 0 (Unidentified)".

Files Modified:

File Changes
src/types/frontmatter.ts ResearchLevel type, RESEARCH_LEVELS metadata
src/core/person-note-writer.ts researchLevel in PersonData
src/ui/create-person-modal.ts Dropdown selector
src/core/family-graph.ts researchLevel in PersonNode
src/reports/services/gaps-report-generator.ts Filtering, sorting, statistics
src/gedcom/gedcom-exporter.ts _RESEARCH_LEVEL export
src/gramps/gramps-exporter.ts "Research Level" attribute export
src/gedcom/gedcom-parser-v2.ts Import parsing
src/gramps/gramps-parser.ts Import parsing
src/constants/base-template.ts Bases views

See Research Level Property Planning for implementation details.


Excalidraw Export Enhancements (v0.17.1)

Enhanced Excalidraw export with ExcalidrawAutomate API integration, style customization, and improved output quality.

Problem Solved:

The previous Excalidraw export was functional but limited:

  • Manual text sizing: Text dimensions estimated with character width multiplier, often inaccurate
  • Static arrows: Connections didn't adapt when elements were moved in Excalidraw
  • No wiki links: Couldn't click nodes to navigate to person notes
  • No style control: Fixed visual style with no customization options
  • Temp file pollution: Intermediate canvas files left behind after export

Features:

Feature Description
ExcalidrawAutomate API Uses API when available for smart connectors and accurate text measurement
Smart connectors Arrows adapt when elements are moved (API mode)
Wiki links Nodes link to person notes via Excalidraw's link property
Spouse styling Spouse relationships rendered with dashed lines
Drawing style options Architect (clean), Artist (sketchy), Cartoonist (rough)
Font family options Virgil (handwritten), Cascadia (code), system fonts
Fill/stroke styles Solid, hachure, cross-hatch fills; solid, dashed, dotted strokes
Node content levels Name only, name + dates, or name + dates + places
Dedicated wizard step Excalidraw style options in separate step for better UX
JSON fallback Works without Excalidraw plugin using direct JSON generation

Wizard Flow (Excalidraw):

Step Content
1 Select root person
2 Choose tree type
3 Select output format (Excalidraw)
4 Canvas options (scope, colors)
5 Preview tree
6 Excalidraw style options
7 Output settings and generate

Files Modified:

File Changes
src/excalidraw/excalidraw-exporter.ts API integration, style options, smart connectors
src/trees/ui/unified-tree-wizard-modal.ts Excalidraw style step, form data fields
src/excalidraw/excalidraw-automate.d.ts Type definitions for EA API

Bug Fixes:

  • Text centering in Excalidraw boxes
  • Duplicate visible boxes (removed box parameter from addText)
  • Wiki link brackets appearing in labels (stripped, set via element link property)
  • Temporary canvas file cleanup after export
  • Generate button reactivity on canvas name input
  • Duplicate navigation footer in wizard

Post-Import Cleanup Wizard (v0.17.0)

A 10-step guided wizard that consolidates post-import data quality operations into a single, sequential workflow. After importing a GEDCOM file (especially one with data quality issues), users previously had to navigate multiple Control Center tabs and run operations in the correct order. The wizard provides a unified experience with progress tracking.

Problem Solved:

  • Scattered tools: Cleanup operations were spread across Data Quality, Places, and other tabs
  • Unknown order: No guidance on which operations to run first
  • Manual coordination: Users had to remember to run each step and track what's done

Wizard Steps:

Step Operation Type
1 Quality Report Review-only
2 Fix Bidirectional Relationships Batch-fix
3 Normalize Date Formats Batch-fix
4 Normalize Gender Values Batch-fix
5 Clear Orphan References Batch-fix
6 Migrate Source Properties Batch-fix
7 Standardize Place Variants Interactive
8 Bulk Geocode Interactive
9 Enrich Place Hierarchy Interactive
10 Flatten Nested Properties Batch-fix

Features:

Feature Description
Overview Grid 5Γ—2 tile grid showing all 10 steps with status badges
Progress Tracking Horizontal progress bar with step completion state
Preview Mode Each batch step shows proposed changes before applying
Session Persistence Wizard state saved to resume interrupted cleanup
Smart Defaults Auto-skip steps with zero detected issues
Summary Report Export completion stats to markdown

Entry Points:

  • Import Wizard results: "Run Cleanup Wizard" button
  • Control Center > Data Quality > Quick Start card
  • Command palette: "Charted Roots: Post-Import Cleanup Wizard"

Technical Notes:

  • CleanupWizardModal orchestrates the 10-step flow
  • Reuses existing services: DataQualityService, GeocodingService, PlaceGraphService, SourceMigrationService
  • State persisted in plugin.settings.cleanupWizardState

Files Added:

File Purpose
src/ui/modals/cleanup-wizard-modal.ts Main wizard modal with step navigation
styles/cleanup-wizard.css Wizard-specific styling

See Post-Import Cleanup Wizard Planning for implementation details.


Source Array Migration (v0.17.0)

Migration from indexed source properties (source, source_2, source_3) to a YAML array format (sources: []). This change improves scalability, simplifies Dataview queries, and aligns with modern frontmatter practices.

Problem Solved:

The indexed format had limitations:

  • Fixed slots: Only 3 source slots available per entity
  • Query complexity: Dataview queries had to check multiple properties
  • Schema rigidity: Adding more sources required schema changes

Format Change:

# Old format (no longer supported)
source: "[[Birth Certificate]]"
source_2: "[[Census 1920]]"
source_3: "[[Family Bible]]"

# New format (unlimited sources)
sources:
  - "[[Birth Certificate]]"
  - "[[Census 1920]]"
  - "[[Family Bible]]"
  - "[[Interview Notes]]"

Migration Phases:

Phase Description Status
Phase 1 Support both formats (read array, fall back to indexed) βœ… Complete
Phase 2 Add migration tooling via Cleanup Wizard Step 6 βœ… Complete
Phase 3 Deprecate indexed format (console warnings) βœ… Complete
Phase 4 Remove indexed format support βœ… Complete

Features:

Feature Description
Wizard Integration Step 6 of Cleanup Wizard handles migration
Preview Mode Shows proposed changes before applying
Batch Processing Migrates all notes in one operation
Merge Support Combines indexed sources with existing array
Legacy Warning Console warning for notes still using old format

Technical Notes:

  • SourceMigrationService handles detection and migration
  • GEDCOM and Gramps importers now write array format by default
  • Statistics service only reads sources array (indexed parsing removed)

Files Added:

File Purpose
src/sources/services/source-migration-service.ts Detection and migration logic

Breaking Change: The indexed format (source, source_2, etc.) is no longer parsed. Users with legacy notes should run the Cleanup Wizard Step 6 to migrate.

See Source Array Migration Planning for implementation details.


Migration Notice (v0.17.0)

A one-time workspace tab displayed when users upgrade to v0.17.0, informing them about the source array format change and providing a direct path to the Cleanup Wizard.

Features:

  • Opens as main workspace tab on first load after upgrade
  • Shows before/after code examples of format change
  • "Open Cleanup Wizard" button for immediate migration
  • "Dismiss" button marks notice as seen
  • Version tracking via lastSeenVersion setting

Files Added:

File Purpose
src/ui/views/migration-notice-view.ts Workspace view for upgrade notice
styles/migration-notice.css Notice styling

v0.16.x

Import/Export Hub (v0.16.0)

Modal-based hub with step-by-step wizards for importing and exporting genealogical data, replacing the previous Import/Export tab in Control Center.

Problem Solved:

The previous import/export experience was fragmented:

  • Scattered UI: Import/export lived in a Control Center tab, but progress displayed in separate modals
  • Disconnected numbering: Post-import reference numbering was a separate modal, not integrated into the import workflow
  • Limited guidance: No step-by-step flow for format selection, options, and preview

Features:

Feature Description
Hub Modal Two-card layout (Import, Export) matching Reports Hub and Media Manager patterns
Import Wizard 7-step guided import with format selection, file picker, options, preview, progress, numbering, and completion
Export Wizard 6-step guided export with format selection, folder picker, privacy controls, preview, progress, and completion
Integrated Numbering Reference numbering (Ahnentafel, d'Aboville, Henry, Generation) built into import flow
Privacy Controls Living person exclusion with redact vs. exclude options in export wizard

Import Wizard Steps:

Step Purpose
1. Format Select GEDCOM 5.5.1, GEDCOM X (JSON), Gramps XML/.gpkg, or CSV
2. File Drag-and-drop file picker
3. Options Entity types, target folder, conflict handling, dynamic blocks toggle
4. Preview Entity counts, duplicate warnings
5. Import Progress with real-time log
6. Numbering Optional reference numbering with root person picker
7. Complete Summary with actions

Export Wizard Steps:

Step Purpose
1. Format Select GEDCOM 5.5.1, GEDCOM X (JSON), Gramps XML, or CSV
2. Folders Preference folders or custom folder pickers
3. Options Privacy controls, inclusions (sources, places, notes, media)
4. Preview Entity counts, privacy summary
5. Export Progress with real-time log
6. Complete Download/save options

Technical Notes:

  • Reuses existing import/export logic (GEDCOM parsing, export generation)
  • Integrates ReferenceNumberingService for numbering step
  • .gpkg format includes embedded media, extracted and linked during import

Files Added:

File Purpose
src/ui/import-export-hub-modal.ts Hub modal with import/export cards
src/ui/import-wizard-modal.ts 7-step import wizard
src/ui/export-wizard-modal.ts 6-step export wizard

See Import/Export Hub Planning Document for implementation details.


v0.15.x

Visual Tree PDF Quality Improvements (v0.15.3)

Improved rendering quality for Visual Tree PDFs generated from the unified tree wizard, achieving parity with Family Chart PDF exports.

Problem Solved:

  • Visual Tree PDFs generated via pdfmake appeared slightly blurry compared to Family Chart PDFs using jsPDF
  • The pdfmake image embedding was resampling the tree image, causing quality loss
  • No dynamic page sizing option for optimal digital viewing

Changes:

Change Description
4Γ— scale rendering Increased canvas scale from 2Γ— to 4Γ— in visual-tree-svg-renderer.ts
Aspect ratio preservation Removed explicit height constraint from pdfmake image content
Quality parity Visual Tree PDFs now match Family Chart PDF sharpness

Technical Details:

The quality difference stemmed from how images are embedded in PDFs:

  • jsPDF (Family Chart): Sizes the PDF page to match the content, avoiding any resampling
  • pdfmake (Visual Tree): Used fixed page sizes with explicit width/height, causing resampling

The fix increases the source canvas resolution (4Γ— instead of 2Γ—) to compensate for any resampling, and removes the explicit height constraint to preserve aspect ratio.

Files Changed:

File Change
src/trees/services/visual-tree-svg-renderer.ts Changed scale from 2 to 4
src/reports/services/pdf-report-renderer.ts Removed height from image content

See Visual Tree PDF Enhancements Planning Document for technical analysis.


Report Wizard Enhancements (v0.15.3)

Multi-step wizard interface for the Report Generator with improved UX, step-by-step navigation, and streamlined report creation.

Problem Solved:

  • The Report Generator modal had grown complex with 13 report types, 5 categories, and extensive PDF options
  • All options were displayed at once, creating cognitive overload
  • No way to save common report configurations for reuse

Features:

Feature Description
5-step wizard Report Type β†’ Subject β†’ Content Options β†’ Output & Styling β†’ Generate
Step navigation Previous/Next buttons with step indicator
Category filtering Filter reports by category (Genealogical, Research, Timeline, Geographic, Summary)
Dynamic options Content options step adapts to selected report type
Format selection Choose output format (Vault, Markdown, PDF, ODT) in Output step

Wizard Steps:

Step Purpose
1. Report Type Category filter + report selection from 13 types
2. Subject Person/place/universe/collection picker based on report type
3. Content Options Report-specific toggles (generations, spouses, sources, etc.)
4. Output & Styling Format selection + PDF/ODT customization options
5. Generate Review settings and generate report

Files Changed:

File Change
src/reports/ui/report-wizard-modal.ts New multi-step wizard modal
src/reports/services/pdf-report-renderer.ts ODT generation support
styles/report-wizard.css Wizard styling with compact cards

See Report Wizard Enhancements Planning Document for implementation details.


Report Generator ODT Export (v0.15.3)

ODT (Open Document Text) export capability for all report types, enabling document merging workflows with LibreOffice Writer and Microsoft Word.

Problem Solved:

  • Reports could only be saved as Markdown or PDF
  • No editable document format for further customization
  • Users couldn't easily merge text reports with visual tree charts

Features:

Feature Description
All 13 report types ODT export available for all report types
Cover page support Optional title page with logo, title, subtitle, and notes
Rich content Tables, lists, bold/italic text preserved
Image embedding Visual tree charts embedded as images (for tree reports)
Title in document Optional title at top of document (when not using cover page)
No external dependencies Uses JSZip (bundled with Obsidian) + manual XML generation

ODT Generation:

ODT files are ZIP archives containing XML. The generator creates:

File Purpose
content.xml Document content with text, tables, and images
styles.xml Paragraph, table, and character styles
meta.xml Document metadata (title, author, date)
manifest.xml File manifest for the archive
Pictures/ Embedded images (tree charts, logos)

Unified Tree Wizard Integration:

The unified tree wizard also supports ODT output:

  • ODT option in Step 3 (Output Format)
  • Title field in Step 5 for document title
  • Filename based on title field value
  • Tree image embedded in ODT document

Files Changed:

File Change
src/reports/services/odt-generator.ts New ODT generation service
src/reports/services/pdf-report-renderer.ts ODT generation for reports
src/trees/ui/unified-tree-wizard-modal.ts ODT support in tree wizard

See Report Generator ODT Export Planning Document for implementation details.


Calendarium Integration Phase 2 (v0.15.2)

Display events with Calendarium fc-* date fields on Charted Roots timelines, with calendar filtering support.

Problem Solved:

  • Events using Calendarium's fc-date format weren't visible on Charted Roots timelines
  • No way to filter timeline views by calendar system when mixing real and fictional dates
  • Timeline badges in People tab didn't show event counts

Features:

Feature Description
fc-date Parsing Read fc-date or fc-start as event start date
fc-end Support Read fc-end as event end date for date ranges (lifespans, reigns)
Month Conversion Handle 0-indexed months from Calendarium (converts to 1-indexed)
Calendar Filter Dropdown to filter timelines by fc-calendar value
Timeline Badges Calendar icon with event count in People tab table rows
Timeline Modal Click badge to open full timeline in modal dialog

Settings:

Setting Description
syncCalendariumEvents Enable/disable fc-* field parsing (default: false)
Integration mode Must be set to "Read-only" in Preferences β†’ Integrations

How It Works:

  1. Enable Calendarium integration in Preferences β†’ Integrations
  2. Enable "Show Calendarium dates on timelines" toggle
  3. Event notes with fc-date fields will now appear on timelines
  4. The fc-calendar value is used as the date system for filtering

Example Event Note:

---
cr_type: event
cr_id: "20251223120000"
title: "Birth of Aragorn"
event_type: birth
person: "[[Aragorn]]"
fc-date:
  year: 2931
  month: 2
  day: 1
fc-calendar: Middle-earth
---

Related Documentation:


Family Chart Export Wizard (v0.15.1)

Multi-step export wizard for Family Chart view with format presets, customization options, and progress tracking.

Problem Solved:

  • The export dropdown menu was cluttered and easy to trigger accidentally
  • No preview of export settings before generating
  • Large exports could freeze the UI without progress indication
  • No way to remember last-used settings

Features:

Feature Description
5 Quick Presets Quick Share (PNG 1x), High Quality (PNG 2x), Print Ready (PDF), Editable (SVG), Document (ODT)
Format Options PNG, SVG, PDF, ODT with format-specific settings
Scope Selection Full tree or limited depth (1-5 generations)
PDF Options Page size (fit/A4/letter/legal/tabloid), layout (single/tiled), orientation (auto/portrait/landscape)
Cover Page Optional title page for PDF/ODT with custom title and subtitle
Avatar Toggle Include or exclude person thumbnails
Progress Modal Real-time progress with phase indicators and cancel button
Settings Memory Last-used format, scale, and options remembered

Export Presets:

Preset Format Settings Use Case
Quick Share PNG 1x scale, no avatars Social media, messaging
High Quality PNG 2x scale, with avatars Printing, archiving
Print Ready PDF Cover page, with avatars Physical prints, sharing
Editable SVG Vector format, no avatars Editing in Inkscape/Illustrator
Document ODT Cover page, with avatars Merging with reports in Word/LibreOffice

ODT Export:

The ODT format creates an OpenDocument Text file that can be opened in LibreOffice Writer or Microsoft Word. This enables:

  • Merging family charts with narrative text
  • Adding custom formatting and styling
  • Creating comprehensive family history documents

Technical implementation uses JSZip for creating the ODT ZIP archive with manual XML generation (no external library dependencies).

Files Changed:

File Change
src/ui/views/family-chart-export-wizard.ts New export wizard modal
src/ui/views/family-chart-export-progress-modal.ts Progress tracking modal
src/ui/views/odt-generator.ts ODT generation using JSZip
src/ui/views/family-chart-view.ts Export button wiring, exportWithOptions method
src/settings.ts LastFamilyChartExportSettings interface
styles/family-chart-export.css Wizard and progress modal styling

See Family Chart View for usage documentation.


Family Chart Styling Panel (v0.15.1)

In-view color theming for Family Chart with preset themes and custom color picker.

Problem Solved:

  • Chart color options were only accessible via the Style Settings plugin
  • Users without Style Settings couldn't customize colors
  • No quick way to switch between color themes

Features:

Feature Description
Palette Button Toolbar button opens theme menu
5 Theme Presets Classic, Pastel, Earth Tones, High Contrast, Monochrome
Customize Modal Color pickers for all 7 chart colors
Live Preview Colors update in real-time while adjusting
Settings Persistence Custom colors saved across sessions
Reset Option Reverts to default colors

Theme Presets:

Theme Female Male Unknown Description
Classic Pink #c48a92 Blue #789fac Gray #d3d3d3 Default colors
Pastel Soft pink #f4c2c2 Soft blue #a7c7e7 Lavender #e6e6fa Lighter, softer tones
Earth Tones Terracotta #cc7a6f Sage #8fbc8f Sand #d2b48c Natural, warm palette
High Contrast Magenta #ff00ff Cyan #00ffff Yellow #ffff00 Accessibility-focused
Monochrome Dark gray #666666 Medium gray #888888 Light gray #aaaaaa No color coding

Customizable Colors:

Color Description
Female card Background color for female person cards
Male card Background color for male person cards
Unknown card Background color for unknown gender cards
Background (light) Chart background in light theme
Background (dark) Chart background in dark theme
Text (light) Card text color in light theme
Text (dark) Card text color in dark theme

Interaction with Style Settings:

If you have the Style Settings plugin installed:

  • In-view settings take precedence (applied via inline styles)
  • "Reset to defaults" clears in-view settings, revealing Style Settings values
  • Both can coexistβ€”use in-view for quick switching, Style Settings for vault-wide defaults

Files Changed:

File Change
src/ui/views/family-chart-view.ts Palette button, theme presets, FamilyChartStyleModal
src/settings.ts FamilyChartColors interface
styles/family-chart-view.css Style modal CSS

See Family Chart View for usage documentation.


Universal Media Linking (v0.15.0)

Extend the media property to all entity types (Person, Event, Place, Organization) with Gramps Package (.gpkg) import support and dynamic inline galleries.

Problem Solved:

  • The media property was only supported on Source notes
  • Gramps Package (.gpkg) import ignored bundled media files
  • No way to display media galleries inline within person notes
  • Writers and worldbuilders couldn't attach character portraits, location art, or scene illustrations

Features:

Feature Description
Universal media property media supported on Person, Event, Place, Source, Organization notes
Gramps Package import .gpkg files import with media extraction to vault
Media linking during import Media files linked to all entity types based on Gramps objref references
Dynamic media gallery canvas-roots-media code block renders inline gallery
Editable mode Drag-to-reorder with editable: true option
Freeze to callout Convert gallery to styled `[!info
Style Settings Gallery appearance customizable via Style Settings plugin
Find Unlinked Media Tool to discover orphaned media files in vault
Media folder filtering Settings to exclude folders from media searches

Dynamic Media Gallery:

```canvas-roots-media
columns: 3
size: medium
editable: false
title: Media
```

Configuration options:

Option Values Description
columns 2-6, auto Number of columns in grid (default: 3)
size small, medium, large Thumbnail size (default: medium)
editable true, false Enable drag-to-reorder (default: false)
title string Custom header text (default: "Media")

Editable Mode:

When editable: true is set:

  • Items show a drag handle on hover
  • Drag items to reorder their position
  • First item becomes the thumbnail (shown on Family Chart nodes)
  • Frontmatter is updated automatically when you drop
  • Gallery has a dashed border to indicate edit mode

Frozen Gallery:

Click the freeze button (❄️) to convert to a styled callout:

> [!info|cr-frozen-gallery]
> ![[portrait.jpg]]
> ![[wedding-photo.jpg]]
> ![[birth-certificate.pdf]]

The frozen gallery renders images in a flex layout with click-and-hold zoom.

Entity Support:

Entity Type Use Cases
Person Photos, portraits, scanned documents, character concept art
Event Ceremony photos, certificates, scene illustrations
Place Location photos, historical maps, fantasy maps, floor plans
Organization Logos, group photos, faction banners, heraldry
Source Original records, digitized documents

Gramps Package Import:

When importing a .gpkg file:

  1. Media files are extracted to your configured media folder
  2. objref elements in the Gramps XML are resolved to vault paths
  3. media wikilinks are added to Person, Event, Place, and Source frontmatter
  4. First media item serves as thumbnail (matching Gramps convention)

Implementation Phases:

Phase Scope
Phase 1 Add media property to Person, Event, Place, Organization schemas
Phase 2 Find Unlinked Media tool, Media Manager integration
Phase 3 Media folder filtering and settings
Phase 4 Gramps Package (.gpkg) import with media extraction
Phase 5 Dynamic canvas-roots-media block with freeze support

Files Changed:

File Change
src/gramps/types.ts Added mediaRefs to GrampsPerson, GrampsEvent, GrampsPlace interfaces
src/gramps/gramps-parser.ts Parse objref elements, populate mediaRefs arrays
src/gramps/gramps-importer.ts Resolve media refs to wikilinks during note creation
src/dynamic-content/media-processor.ts New processor for canvas-roots-media blocks
src/dynamic-content/dynamic-content-service.ts Media gallery rendering and freeze logic
styles/dynamic-content.css Gallery grid, editable mode, frozen callout styles

See Dynamic Note Content: Media Block for usage documentation and Universal Media Linking Planning Document for implementation details.


v0.14.x

Visual Tree Charts (v0.14.0)

Unified tree generation wizard supporting both Canvas and PDF output, with visual tree reports in the Statistics Dashboard.

Problem Solved:

  • Two separate wizards for Canvas and PDF tree generation created confusion
  • No visual tree diagrams in the Statistics and Reports system
  • Canvas Trees tab lacked modern dashboard design
  • No custom icons for tree chart types

Features:

Feature Description
Unified Tree Wizard Single wizard for both Canvas and PDF output with dynamic step flow
Visual Tree Reports 4 chart types in Statistics Dashboard: Pedigree, Descendant, Hourglass, Fan Chart
Custom SVG Icons Themeable icons for each chart type (cr-pedigree-tree, cr-descendant-tree, cr-hourglass-tree, cr-fan-chart)
Canvas Trees Tab Redesigned dashboard with recent trees, statistics, and quick actions
PDF Options Page size, orientation, node content, color schemes, large tree handling

Wizard Step Flow:

Step 1: Person Selection
    ↓
Step 2: Tree Type Selection
    ↓
Step 3: Output Format (Canvas vs PDF)
    β”œβ”€β”€ Canvas β†’ Step 4a: Canvas Options β†’ Step 5a: Preview β†’ Step 6a: Output
    └── PDF β†’ Step 4b: PDF Options β†’ Step 5b: Output

Chart Types:

Chart Type Description
Pedigree Tree Ancestors branching upward from root person
Descendant Tree Descendants branching downward from root person
Hourglass Tree Both ancestors and descendants from root person
Fan Chart Semicircular pedigree (PDF only, placeholder for future)

PDF Generation Paths:

Path Library Quality Use Case
Unified Wizard pdfmake Good Quick PDF generation from wizard
Family Chart View jsPDF Excellent High-quality printable output

The Family Chart view produces superior visual output (orthogonal connectors, profile icons, better spouse positioning). Both paths are maintained for different use cases.

Files Changed:

File Change
src/trees/ui/unified-tree-wizard-modal.ts New unified wizard modal
src/trees/services/visual-tree-service.ts Tree building and layout service
src/reports/services/pdf-report-renderer.ts Extended with visual tree PDF generation
src/reports/types/report-types.ts Added 4 visual tree report types
src/ui/lucide-icons.ts Added 4 custom SVG icons
src/ui/control-center.ts Canvas Trees tab dashboard redesign
styles/tree-output.css New card and wizard styles

Removed Files:

  • src/trees/ui/tree-generation-wizard.ts (1500+ lines, replaced by unified wizard)
  • src/trees/ui/visual-tree-wizard-modal.ts (570+ lines, replaced by unified wizard)

See Canvas Trees for user documentation and Tree Visualization Overhaul Planning Document for implementation details.


v0.13.x

Control Center Dashboard (v0.13.6)

Transform the Control Center's Status tab into a Dashboard with quick-action tiles, providing mobile-friendly access to common operations.

Problem Solved:

  • Status tab displayed entity counts and vault health but no actions
  • Common operations required navigating to other tabs or Command Palette
  • Mobile users faced extra friction due to limited screen space
  • No quick access to frequently-used operations

Features:

Feature Description
Dashboard tab Replaces Status tab as the Control Center's home screen
9 quick-action tiles One-tap access to Person, Event, Source, Place, Report, Statistics, Import, Tree Output, and Map
Vault Health section Collapsible section with entity counts and completeness metrics
Recent Files Last 5 accessed files with type badges and click-to-open
Context menu Right-click Recent items for type-specific actions
First-run notice Dismissible welcome message for new users
Responsive grid 3-column on desktop, 2-column on mobile

Quick Action Tiles:

Tile Icon Action
Person user Opens Create Person modal
Event calendar Opens Create Event modal
Source file-text Opens Create Source modal
Place map-pin Opens Create Place modal
Report file-chart-pie Opens Report Generator modal
Statistics bar-chart-3 Opens Statistics Dashboard view
Import upload Opens Import/Export tab
Tree Output git-branch Opens Tree Output tab
Map map Opens Map View

Recent Files Context Menu:

Entity Type Actions
All types Open note
Place Open in Map View (zooms to coordinates if available)
Person Open in Family Chart

Vault Health Section:

Metric Description
Entity counts People, Events, Sources, Places, Organizations, Canvases
Completeness Percentage of people with key data (birth, death, parents)
Issues Count of data quality warnings with "View details" link

Technical Details:

  • New DashboardTab component in src/ui/dashboard-tab.ts
  • New RecentFilesService in src/core/recent-files-service.ts
  • Recent files stored in plugin.settings.dashboardRecentFiles
  • Dashboard styles in styles/dashboard.css
  • Vault Health section collapse state persisted in settings

See Control Center Dashboard Planning Document for implementation details.


Extended Report Types (v0.13.5)

Six new report types expanding the report generator beyond traditional genealogical reports, plus enhanced PDF customization options.

Problem Solved:

  • Reports were limited to 7 genealogical report types
  • No way to document sources for a person in aggregate
  • No timeline report showing all events chronologically
  • No place-focused summaries
  • No reports for worldbuilders (universe/collection overviews)
  • Limited PDF customization options

New Report Types:

Report Category Description
Source Summary Research All sources cited for a person, grouped by fact type, with quality ratings and gap analysis
Timeline Report Timeline Chronological list of events with filtering by date range, event type, and participants
Place Summary Geographic All events and people associated with a location (born, died, resided, married)
Media Inventory Research Media files with linked entities, orphaned file detection, coverage gap analysis
Universe Overview Summary Entity statistics for a fictional world with date ranges and entity type breakdown
Collection Overview Summary Summary of a user collection or family component with member list and statistics

Report Category Selector:

The Report Generator modal now includes a category selector that groups all 13 report types:

Category Reports
Genealogical Ahnentafel, Pedigree Chart, Descendant Chart, Register Report, Family Group Sheet, Individual Summary
Research Source Summary, Gaps Report, Media Inventory
Timeline Timeline Report
Geographic Place Summary
Summary Universe Overview, Collection Overview

Source Summary Report:

Feature Description
Root person picker Select subject for the report
Grouping options By fact type, source type, or quality
Quality indicators Primary, secondary, derivative classification
Citation details Full citations with repository info
Gap analysis Highlights unsourced facts needing documentation

Timeline Report:

Feature Description
Date range filter Optional start and end dates
Event type filter Filter to specific event types
Participant filter Events involving specific people
Grouping None, by year, by decade, by person, by place
Source inclusion Toggle source references

Place Summary Report:

Feature Description
Root place picker Select subject location
Child places Option to include events at child locations
Date range filter Filter events by date
Place hierarchy Shows containment chain
Coordinate display Includes lat/long when available

Media Inventory Report:

Feature Description
Scope selection All media, sources only, or by folder
Orphan detection Lists media files not linked to any entity
Coverage gaps Shows entities that could have media but don't
File type breakdown Images, PDFs, audio counts
Grouping By entity type, folder, or file type

Universe Overview Report:

Feature Description
Universe picker Select subject universe
Entity breakdown Counts per type (people, places, events, organizations, sources)
Date range Earliest to latest dates using fictional dates if applicable
Geographic summary Places with coordinates and coverage percentage
Date systems Lists calendar systems used in the universe
Recent activity Optionally lists recently modified entities

Collection Overview Report:

Feature Description
Collection picker User collections or auto-detected family components
Member list People with key dates (birth, death)
Generation analysis Ancestor/descendant counts by generation
Geographic distribution Places and counts
Surname distribution For family components
Sort options By birth date, name, or death date

Enhanced PDF Options:

Option Description
Custom title Override default report title
Custom title scope Apply to cover only, headers only, or both
Custom subtitle Additional text below title on cover page
Cover notes Extended notes section on cover page
Date format MDY (12/20/2025), DMY (20/12/2025), or YMD (2025-12-20)

Access Points:

  • Statistics Dashboard β†’ Reports section β†’ Generate
  • Command palette: "Charted Roots: Generate Report"

Technical Details:

  • Each report type has a dedicated generator class in src/reports/services/
  • All reports use shared PDF infrastructure via PdfReportRenderer
  • Report options stored in modal state and passed to generators
  • Same output options: Save to vault, Download as MD, Download as PDF

See Extended Report Types Planning Document for implementation details.


PDF Report Export (v0.13.4)

Export genealogical reports as professionally styled PDF documents, generated entirely locally with no internet connection required.

Problem Solved:

  • Reports could only be saved as markdown files or copied to clipboard
  • No way to share polished, print-ready reports with family members
  • Professional archiving required manual formatting in external tools

Features:

Feature Description
All 7 report types Ahnentafel, Pedigree Chart, Descendant Chart, Register Report, Family Group Sheet, Individual Summary, Gaps Report
Page size options A4 or Letter
Optional cover page Title page with report name, subject, generation date, and Charted Roots branding
Logo/crest support Add custom image to cover page (automatically resized for optimal file size)
100% local generation PDFs created entirely on device using bundled pdfmake library
Privacy-first design No data sent to any server; no internet connection required

PDF Options:

Option Description
Page size A4 or Letter
Include cover page Add a title page with report metadata
Logo or crest Optional image for cover page (PNG, JPEG, GIF, WebP)

Cover Page Contents:

  • Report title (e.g., "Ahnentafel Report")
  • Subject name (e.g., "Ancestors of John Smith")
  • Decorative separator line
  • Generation date
  • "Charted Roots for Obsidian" branding
  • Optional logo/crest centered at top

Privacy & Security:

Genealogical data is highly personal. PDF generation is designed with privacy as a core principle:

  • 100% local generation β€” PDFs are created entirely on your device using the pdfmake library bundled with the plugin
  • No internet connection required β€” No data is sent to any server or cloud service
  • No external dependencies β€” Fonts are embedded; no network requests are made during generation
  • Downloads to your system β€” Files save to your operating system's Downloads folder, outside your vault

Access Points:

  • Statistics Dashboard β†’ Reports section β†’ Generate β†’ Select "Download as PDF"
  • Command palette: "Charted Roots: Open Statistics Dashboard"

Technical Details:

  • Uses pdfmake library (~400KB) for document generation, lazy-loaded on first use
  • Renders directly from structured report data (not markdown parsing)
  • Logo images automatically resized to max 200Γ—200px to reduce file size
  • Separate from jsPDF dependency used for Family Chart canvas export

See Statistics & Reports for usage documentation.


Universe Management (v0.13.0)

First-class universe entity type for managing fictional worlds, with a dedicated Control Center tab, guided setup wizard, and comprehensive statistics integration.

Problem Solved:

  • Worldbuilders had no central place to manage fictional universes (Middle-earth, Westeros, etc.)
  • The universe field was a plain string with no validation, leading to typos creating duplicate "universes"
  • No way to see which entities belonged to which universe
  • No guided setup for creating a new world with calendar, map, and validation rules

Universe Entity:

Universe notes (cr_type: universe) serve as a canonical registry for fictional worlds:

cr_type: universe
cr_id: middle-earth
name: Middle-earth
description: A fantasy world created by J.R.R. Tolkien
author: J.R.R. Tolkien
genre: fantasy
status: active
default_calendar: shire-reckoning
default_map: middle-earth-map

Features:

Feature Description
Universe entity type First-class note type with full CRUD support
UniverseService Entity aggregation, orphan detection, statistics
Universes tab Dedicated Control Center tab (conditional visibility)
Create Universe wizard Multi-step guided setup with optional calendar, map, and schema
Statistics integration Universes section with entity counts and drill-down
Guide tab documentation Universe notes section in Essential Properties card
Context menu action "Add essential universe properties" for universe notes
Universes base template 12 pre-configured views for browsing universes

Universes Tab:

The Universes tab appears in Control Center when:

  • Any universe notes exist in the vault, OR
  • Any orphan universe strings exist (entities with universe field but no matching note)

This keeps the UI clean for genealogists who never use fictional worlds.

Card Description
Actions Create universe (wizard), Create universes base
Your universes List of universe notes with entity counts
Orphan universe strings Entities referencing non-existent universes

Create Universe Wizard:

Step Description Skippable
1 Universe details (name, description, author, genre, status) No
2 Custom calendar? Creates linked date system Yes
3 Custom map? Creates linked map configuration Yes
4 Validation schema? Creates scoped schema Yes
5 Summary with links to all created entities No

Statistics Integration:

The Statistics dashboard includes a Universes section showing:

  • Universe count and list
  • Per-universe entity breakdown (people, events, places, sources, organizations)
  • Drill-down to view entities filtered by universe
  • "View full statistics β†’" link to dashboard with universe filter

Universes Base Template:

12 pre-configured views for browsing universes:

  • All Universes
  • By Status (active, draft, archived)
  • By Genre, By Author
  • With/Without Calendars
  • With/Without Maps
  • Recently Created

Context Menu:

Right-click on a universe note to access:

  • Add essential universe properties
  • Open in Universes tab
  • Create related entities (person, event, place, etc.) pre-populated with universe

Backward Compatibility:

  • String-only universe values continue to function
  • Orphan detection shows entities referencing non-existent universe notes
  • New entities can link to universe notes via wikilink or use string values

See Universe Management Planning Document for implementation details.


v0.12.x

Configurable Normalization (v0.12.12)

User-configurable sex value normalization modes that allow worldbuilders to protect custom sex values from GEDCOM-standard normalization.

Problem Solved:

  • Worldbuilders using custom sex values (e.g., "hermaphrodite", "neuter" for alien species) had those values normalized to GEDCOM M/F when running "Normalize sex values"
  • No way to skip normalization for notes covered by schemas with custom sex enum definitions
  • All-or-nothing approach: either normalize everything or nothing

Features:

Feature Description
Three normalization modes Standard (GEDCOM M/F), Schema-aware (skip protected notes), Disabled (never normalize)
Schema-aware detection Checks if person has applicable schema with custom sex enum values
Preview enhancement Shows which notes will be skipped due to schema override
Preferences setting New dropdown in Preferences β†’ Data Quality

Normalization Modes:

Mode Behavior
Standard Normalize all sex values to GEDCOM M/F (default, existing behavior)
Schema-aware Skip notes covered by schemas that define custom sex enum values
Disabled Never normalize sex values (preview shows what would change)

Schema-Aware Example:

A worldbuilder creates a schema for their sci-fi universe:

---
cr_type: schema
cr_id: schema-alien-species
applies_to_type: universe
applies_to_value: "Sci-Fi Universe"
---

```json schema
{
  "properties": {
    "sex": {
      "type": "enum",
      "values": ["male", "female", "neuter", "hermaphrodite"]
    }
  }
}

With Schema-aware mode enabled, person notes in the "Sci-Fi Universe" with sex values like "hermaphrodite" will be skipped during normalization, while notes in other universes (or without a universe) will still be normalized to GEDCOM M/F.

Integration:

  • Builds on Value Aliases for synonym mapping
  • Builds on Schema Validation for custom enum detection
  • Uses existing Data Quality tab batch operation infrastructure

See Sex/Gender Identity Expansion Planning Document for Phase 4 implementation details.


Step & Adoptive Parent Support (v0.12.10)

Comprehensive support for non-biological parent relationships, improving GEDCOM import fidelity and enabling accurate representation of blended families.

Problem Solved:

  • GEDCOM files with step-parent or adoptive relationships (via PEDI tags) were imported as primary parent claims, triggering false conflicts
  • No way to distinguish biological parents from step/adoptive parents in the data model
  • Canvas trees could not visualize non-biological parent relationships

Features:

Feature Description
GEDCOM PEDI parsing Parse PEDI tags (birth, step, adop, foster) from GEDCOM 5.5.1 files
Gramps mrel/frel parsing Parse mrel/frel attributes (Birth, Stepchild, Adopted) from Gramps XML
GEDCOM X lineage types Parse lineage type facts (StepParent, AdoptiveParent, etc.) from GEDCOM X JSON
Round-trip export Export step/adoptive parents with PEDI tags (GEDCOM), mrel/frel (Gramps), lineage facts (GEDCOM X)
Dedicated frontmatter fields stepfather_id, stepmother_id, adoptive_father_id, adoptive_mother_id
Conflict detection Step/adoptive parents excluded from biological parent conflicts
Canvas visualization Step-parents shown with dashed lines, adoptive parents with dotted lines
Tree generation toggles "Include step-parents" and "Include adoptive parents" options
Create/Edit modal New section for manual entry of step/adoptive parents
Statistics breakdown Parent type breakdown in Data Completeness, blended family metrics

New Frontmatter Fields:

# Biological parents (existing)
father_id: abc-123-def-456
mother_id: ghi-789-jkl-012

# Step-parents (can have multiple)
stepfather_id:
  - mno-345-pqr-678
  - abc-111-def-222
stepmother_id: stu-901-vwx-234

# Adoptive parents
adoptive_father_id: yza-567-bcd-890
adoptive_mother_id: efg-123-hij-456

GEDCOM 5.5.1 Pedigree Types:

PEDI Value Meaning Charted Roots Field
birth Biological father_id, mother_id
adop Adopted adoptive_father_id, adoptive_mother_id
step Step-child stepfather_id, stepmother_id
foster Foster child (stored but not specially handled)
(absent) Assumed biological father_id, mother_id

Gramps XML Pedigree Types:

mrel/frel Value Meaning Charted Roots Field
Birth Biological father_id, mother_id
Adopted Adopted adoptive_father_id, adoptive_mother_id
Stepchild Step-child stepfather_id, stepmother_id
Foster Foster child (stored but not specially handled)
(absent) Assumed biological father_id, mother_id

GEDCOM X Lineage Types:

Lineage Type Meaning Charted Roots Field
BiologicalParent Biological father_id, mother_id
AdoptiveParent Adopted adoptive_father_id, adoptive_mother_id
StepParent Step-parent stepfather_id, stepmother_id
FosterParent Foster parent (stored but not specially handled)
GuardianParent Guardian (stored but not specially handled)
SociologicalParent Sociological (stored but not specially handled)
(absent) Assumed biological father_id, mother_id

New Relationship Types:

Type Line Style Color
step_parent / step_child Dashed Teal (#14b8a6)
adoptive_parent / adopted_child Dotted Cyan (#06b6d4)

Statistics Enhancements:

Metric Description
Parent type breakdown Counts of biological, step, and adoptive parents in Completeness section
Biologically orphaned People with no biological parents but have step/adoptive
Blended family count People with multiple parent types

Tree Behavior:

  • Ancestor trees: Step/adoptive parents included as leaf nodes (no ancestry recursion)
  • Full trees: Step/adoptive parents included by default, follow their connections
  • Both respect the include toggles in tree generation UI

See Step & Adoptive Parent Support Planning Document for implementation details.


Statistics & Reports (v0.12.9)

Comprehensive statistics dashboard and report generation system for analyzing vault data and generating formatted genealogical reports.

Problem Solved:

  • No centralized view of vault statistics (entity counts, completeness, quality)
  • No way to generate standard genealogical reports (Family Group Sheets, Pedigree Charts)
  • Data quality issues were scattered and hard to track

Statistics Dashboard:

Section Description
Entity overview Counts for people, events, sources, places, organizations, canvases
Data completeness Progress bars for birth dates, death dates, sources, parents, spouses
Data quality Clickable issues with drill-down to affected records
Gender distribution Visual breakdown with bar chart
Top lists Surnames, locations, occupations, sources with drill-down
Extended statistics Longevity, family size, marriage patterns, migration, source coverage, timeline density

Data Quality Drill-Down:

Issue Type Severity Description
Date inconsistencies Error Birth after death, age over 120
Missing birth dates Warning People without birth date
Missing death dates Warning With birth but no death (excluding living)
Orphaned people Warning No relationships at all
Incomplete parents Warning Only one parent linked
Unsourced events Info Events without sources
Places without coordinates Info Missing lat/long

Click any issue to expand and see affected records. Click a person chip to open their note. Right-click for context menu. Ctrl+hover for preview.

Report Types:

Report Description
Family Group Sheet Single family unit with parents, marriage, children
Individual Summary Complete record of one person
Ahnentafel Report Numbered ancestor list
Gaps Report Analysis of missing data
Register Report NGSQ-style descendant numbering
Pedigree Chart ASCII ancestor tree
Descendant Chart ASCII descendant tree

Report Options:

  • Select root person
  • Configure generation depth (2-10)
  • Toggle details, spouses, sources
  • Output as new note or clipboard

Extended Statistics:

Analysis Description
Longevity Average lifespan by decade and location
Family size Children per family with distribution
Marriage patterns Age at marriage by sex, remarriage rates
Migration Birth-to-death location changes, top routes
Source coverage Coverage by generation depth
Timeline density Events per decade with gap detection

Access Points:

  • Control Center β†’ Statistics tab β†’ "Open Statistics Dashboard"
  • Command palette: "Charted Roots: Open Statistics Dashboard"
  • Reports section in Statistics Dashboard

See Statistics & Reports for detailed usage documentation.


Dynamic Note Content (v0.12.8)

Live computed content blocks within person notes using custom code block processors. Content updates dynamically from vault data with option to freeze to static markdown.

Problem Solved:

  • Person notes contained only frontmatter and user-written content
  • Computed data (timelines, relationships) required navigating to Control Center
  • No way to see a person's full story in one place

Features:

Feature Description
Timeline block canvas-roots-timeline renders chronological events for a person
Relationships block canvas-roots-relationships shows family members with wikilinks
Freeze to markdown Convert live blocks to static markdown via toolbar button
Create Person toggle Option to include blocks when creating new person notes
Import wizard toggle Option to include blocks during GEDCOM/Gramps/CSV import
Insert commands Context menu and command palette actions for existing notes
Bulk insert Add blocks to all person notes in a folder

Code Block Types:

```canvas-roots-timeline
sort: chronological
```

```canvas-roots-relationships
type: immediate
```

Timeline Block:

  • Shows birth, death, and all linked events chronologically
  • Displays year, event title, and place with wikilinks
  • Configuration options: sort (chronological/reverse), include/exclude event types, limit

Relationships Block:

  • Shows parents, spouse(s), children, and optionally siblings
  • Each person rendered as clickable wikilink with birth-death dates
  • Configuration options: type (immediate/extended/all), include/exclude relationship types

Freeze to Markdown:

  • Toolbar button converts live block to static markdown
  • Preserves wikilinks and formatting
  • Useful for export compatibility or manual editing

Inserting Blocks:

Method Description
Create Person modal "Include dynamic blocks" toggle
Import wizards "Include dynamic blocks" toggle in GEDCOM/Gramps/CSV import
Context menu Right-click person note β†’ "Insert dynamic blocks"
Command palette "Charted Roots: Insert dynamic blocks"
Bulk insert Right-click folder β†’ "Insert dynamic blocks in folder"

Technical Details:

  • Uses Obsidian's registerMarkdownCodeBlockProcessor API
  • DynamicContentService provides shared utilities for config parsing and data resolution
  • TimelineProcessor and RelationshipsProcessor handle block rendering
  • Content computed on note open; manual refresh via code block edit

Bug Fixes (v0.12.8):

  • Fixed Family Chart zoom buttons showing "NaN%" and causing chart to vanish (incorrect scale multiplier)
  • Fixed "Open family chart" showing wrong person instead of current note
  • Fixed Family Chart opening in sidebar instead of main workspace

Gramps Source Import (v0.12.6)

Import source and citation records from Gramps XML files, creating Charted Roots source notes with full metadata and linking citations to person/event notes.

Problem Solved:

  • Gramps XML import supported people, places, and events, but source/citation records were not imported
  • Users migrating from Gramps had to manually recreate source documentation
  • Repository metadata and media references were lost during migration

Features:

Feature Description
Source note creation One note per Gramps source record with full metadata
Repository support Parse <repositories> and <reporef> elements for archive/library data
Media references Store Gramps media handles in gramps_media_refs for manual linking
Source property aliases Full property alias support for all 15 source properties
Gramps ID preservation Store gramps_handle and gramps_id for re-import scenarios
Progress indicator Real-time progress modal during import
UI toggles Obsidian-style toggles for import options

Field Mapping:

Gramps Field Charted Roots Property
<stitle> title
<sauthor> author
<spubinfo> repository (fallback)
Repository <rname> repository
Repository <type> repository_type
<reporef medium> source_medium
<noteref> text Note body content
<objref> handles gramps_media_refs

Confidence Scale Mapping:

Gramps (0-4) Meaning Charted Roots
0 Very Low low
1 Low low
2 Normal medium
3 High high
4 Very High high

Source Property Aliases:

All 15 source properties support aliasing via Preferences β†’ Property aliases:

Property Description
cr_id Unique identifier
cr_type Note type (source)
title Source title
author Author/creator
source_type Type (census, vital_record, etc.)
repository Archive or website
repository_type Library, Archive, etc.
source_medium Book, Electronic, etc.
confidence High/medium/low
url Online source URL
access_date Date accessed
citation_detail Page, volume, etc.
gramps_handle Original Gramps handle
gramps_id Original Gramps ID
gramps_media_refs Media handles for manual linking

Import Options UI:

The Gramps import modal includes Obsidian-style toggles for:

  • Create source notes (enabled by default)
  • Create place notes
  • Create event notes

Each toggle shows the count of records found in the file.

Technical Details:

  • Parses <sources>, <citations>, <notes>, and <repositories> sections
  • Resolves note references to include text in source note body
  • Resolves repository references to get name, type, and medium
  • Builds citation-to-source mapping for linking to person/event notes
  • Source type inferred from title keywords (census, vital_record, church_record, etc.)

See Gramps Source Import Planning Document for full implementation details.


Bulk Source-Image Linking (v0.12.5)

Two wizard tools for managing source images: importing new images as source notes, and linking existing images to existing source notes.

Problem Solved:

  • Genealogists often have hundreds of source images (census records, vital records, photos) with inconsistent naming conventions
  • Manual creation of source notes from images is tedious
  • Existing source notes without media require manual attachment of related images
  • No way to bulk-process images with intelligent metadata extraction

Features:

Source Image Import Wizard (Sources tab β†’ Import):

Feature Description
Folder selection Browse vault folders containing source images
Filename parsing Extract surnames, years, record types, locations from filenames
Confidence indicators Visual dots (green/yellow/orange/gray) showing parse quality
Editable metadata Review and correct parsed data before import
Source note creation Creates source notes with media wikilinks in frontmatter

Source Media Linker Wizard (Sources tab β†’ Link):

Feature Description
Target sources Only shows source notes without existing media
Smart suggestions Scores potential matches based on filename analysis
Auto-selection Top suggestion pre-selected with confidence indicator
"+N more" badge Shows when alternative suggestions exist
Row highlighting Yellow background for rows needing manual selection
Summary breakdown Shows auto-matched vs. manual selection counts

Filename Parser:

Extracts metadata from common genealogy image naming patterns:

Pattern Extracted Data
smith_census_1900.jpg Surname: Smith, Type: census, Year: 1900
Marriage Cert Boston 1875.jpeg Type: marriage, Location: Boston, Year: 1875
henderson_obituary_1945.jpg Surname: Henderson, Type: obituary, Year: 1945

Confidence Scoring:

Score Range Confidence Visual
β‰₯50 High 🟒 Green dot
30-49 Medium 🟑 Yellow dot
1-29 Low 🟠 Orange dot
0 None βšͺ Gray dot

Technical Details:

  • ImageFilenameParser service handles filename analysis
  • Source matching uses scoring algorithm based on surname, year, type, and location overlap
  • Media stored as wikilinks in source frontmatter (media, media_2, etc.)
  • Builds on existing SourceService for note creation and updates

Calendarium Integration Phase 1 (v0.12.0)

Integration with the Calendarium plugin to import calendar definitions for fictional dates.

Problem Solved:

  • Worldbuilders using Calendarium for fantasy calendar management had to manually recreate calendar definitions in Charted Roots
  • No way to leverage existing Calendarium calendar structure (eras, year directions)

Features:

Feature Description
Calendar import Automatically import Calendarium calendars as Charted Roots date systems
Era preservation Era names, abbreviations, and year directions are preserved
Zero configuration Calendars appear automatically when integration is enabled
Invisible when not needed Integrations card only appears if Calendarium is installed

How It Works:

  1. Install Calendarium plugin
  2. Open Control Center β†’ Preferences β†’ Integrations
  3. Set Integration mode to "Read-only (import calendars)"
  4. Calendarium calendars appear in Date Systems card and Create Event modal

Technical Details:

  • Uses window.Calendarium global API
  • Waits for Calendarium settings to load before importing
  • Converts Calendarium eras to Charted Roots FictionalEra format
  • Handles starting eras (epoch 0) and regular eras with dates
  • Extracts era abbreviations from Calendarium format strings

Settings:

Setting Options Default
calendariumIntegration off, read off

Future Phases:

  • Phase 2: Display Calendarium events on timelines
  • Phase 3: Bidirectional sync between plugins
  • Phase 4: Cross-calendar date translation

See Fictional Date Systems - Calendarium Integration and Roadmap - Calendarium Integration for details.


v0.11.x

Export v2 (v0.11.0)

Complete overhaul of export functionality with full entity support and round-trip fidelity with GEDCOM Import v2.

See export-v2.md for implementation plan.

Problem Solved:

  • Previous exports only included people (person notes)
  • Events, sources, places, and custom relationships were lost on export
  • No round-trip fidelity with GEDCOM Import v2 (import created entity notes, but export discarded them)
  • Limited export options and no real-time statistics

Full Entity Export:

All four export formats (GEDCOM 5.5.1, GEDCOM X, Gramps XML, CSV) now support:

Entity Type Export Support
People Full person records with all properties
Events All 22 event types with dates, places, participants, sources, confidence levels
Sources Source notes with citations, repositories, quality classification
Places Place hierarchy, coordinates, categories, types preserved
Custom Relationships Godparent, guardian, mentor, etc. (GEDCOM: ASSO records; other formats: dedicated fields)

Enhanced Export UI:

  • Export statistics preview: Real-time count of entities before export
    • Shows people count, event count, source count, place count
    • Respects collection filters and branch filters
    • Updates dynamically as options change
  • Entity inclusion toggles: Granular control over what to include
    • Toggle people, events, sources, places individually
    • Statistics update to reflect selections
  • Format-specific options:
    • GEDCOM: Version selector (5.5.1 vs 7.0), collection codes toggle, custom relationships toggle
    • All formats: Entity toggles, output location, privacy settings
  • Output location options:
    • Download file (traditional behavior)
    • Save to vault (specify folder path)
  • Export progress modal: Full-screen modal showing:
    • Current phase (loading, filtering, privacy, events, sources, places, generating, writing)
    • Progress bar with percentage
    • Running statistics (entities processed so far)
    • Phase-specific icons and labels
  • Last export info: Display previous export timestamp, entity counts, destination

Property & Value Alias Integration:

All exporters now respect user-configured property and value aliases:

  • Property aliases: Export using canonical property names (e.g., born β†’ BIRT in GEDCOM)
  • Value aliases: Map custom event types, sex values, place categories to canonical values before export
  • Gender identity field: New gender_identity property exported appropriately for each format
    • GEDCOM: Custom _GEND tag
    • GEDCOM X: gender field
    • Gramps XML: Custom attribute
    • CSV: Dedicated column

Custom Relationships Export:

GEDCOM 5.5.1 now exports custom relationships as ASSO records:

1 ASSO @I2@
2 RELA godparent
2 NOTE Relationship from 1920-05-15 to 1935-08-20
2 NOTE Became godparent at baptism
  • Includes relationship type name as RELA descriptor
  • Date ranges in NOTE subrecords
  • Custom notes preserved
  • Only defined relationships exported (not inferred bidirectional ones)
  • Toggle option in export settings (enabled by default)

Round-Trip Fidelity:

Exports now preserve all data from GEDCOM Import v2:

Import Creates Export Preserves
Event notes All event types, dates, places, participants, sources
Source notes Citations, repositories, confidence levels, media links
Place notes Hierarchy, coordinates, categories, historical names
Custom relationships ASSO records with full metadata

Before Export v2:

Import GEDCOM β†’ 500 people, 350 events, 200 sources, 150 places
Export GEDCOM β†’ 500 people only (850 entities lost)

After Export v2:

Import GEDCOM β†’ 500 people, 350 events, 200 sources, 150 places
Export GEDCOM β†’ 500 people, 350 events, 200 sources, 150 places (full fidelity)

Architecture:

  • Shared ExportOptionsBuilder: Consolidated UI component used across all export formats
  • Service injection pattern: Exporters conditionally load EventService, SourceService, PlaceGraphService, RelationshipService
  • Progress callback system: Unified progress reporting for all export phases
  • ExportStatisticsService: Calculates real-time entity counts based on current filter settings

v0.10.x

Sex/Gender Identity Fields (v0.10.20)

Separate gender_identity field for inclusive handling of sex and gender, with full export support across all formats.

See sex-gender-expansion.md for implementation plan (Phases 1-3).

Problem Solved:

  • The sex field follows GEDCOM standards (M/F) for historical record compatibility, but doesn't capture gender identity
  • Writers and worldbuilders need separate fields for biological sex vs. gender identity
  • LGBTQ+ genealogists researching trans individuals need respectful data handling

Features:

Gender Identity Field (Phase 1):

  • New optional gender_identity property on person notes
  • Separate from biological sex field for historical record accuracy
  • Displayed in People tab person details
  • Included in all export formats:
    • GEDCOM: Custom _GEND tag
    • GEDCOM X: gender field
    • Gramps XML: Custom attribute
    • CSV: Dedicated column

Schema-Based Definitions (Phase 2):

  • Schema system supports custom sex/gender values via enum types
  • Scoped by collection or universe for worldbuilding
  • Example: sex values of ["male", "female", "neuter", "hermaphrodite", "asexual"] for alien species

Value Aliases (Phase 3):

  • Sex field supports 4 canonical values: male, female, nonbinary, unknown
  • Built-in synonyms (M β†’ male, F β†’ female)
  • Custom aliases configurable via Unified Property Configuration UI
  • All exporters respect value aliases

User Personas:

  • Genealogist: Uses sex field with GEDCOM M/F values; optionally gender_identity for living relatives or LGBTQ+ research
  • Fiction writer / Worldbuilder: Custom sex values via Schema, separate gender_identity field for character development

Respectful Trans Documentation: When documenting trans individuals:

  • name field holds chosen/current name (displayed by default)
  • Optional birth_name field for birth records if needed for research
  • gender_identity captures current identity
  • sex captures what appears on historical records
  • Privacy options can exclude birth_name and sex from exports

Unified Property Configuration (v0.10.19)

Consolidated property and value alias management in a single interface with comprehensive coverage of all canonical properties.

See unified-property-config.md for implementation plan.

Problem Solved:

  • Property aliases were managed through modal-based workflows with limited discoverability
  • Users couldn't see which properties supported aliasing without trial and error
  • Value aliases were separate from property aliases with inconsistent UI
  • No search/filter to find specific properties across ~55 canonical properties

Features:

Property Aliases:

  • Comprehensive coverage: All 56 canonical properties across Person (28), Event (20), and Place (8) entity types
  • Collapsible sections: Properties grouped by entity type (all collapsed by default)
  • Lazy rendering: Section content only renders when first expanded for performance
  • Search/filter: Find properties by label, description, canonical name, or common aliases
  • Inline editing: Configure aliases directly with auto-save on blur
  • Validation: Checks for empty values, self-aliasing, conflicts with other properties/aliases

Value Aliases:

  • Unified interface: Value aliases styled consistently with property aliases
  • Four value types: Event type (13 values), Sex (4 values), Place category (6 values), Note type (8 values)
  • Alias count badges: Section headers show how many aliases are configured per field
  • Inline editing: Configure value mappings with validation on blur
  • Canonical value labels: Human-readable display of canonical values

UI Improvements:

  • Native Obsidian Setting components for consistent look and feel
  • Validation on blur (not on keystroke) to avoid blocking partial input
  • All sections use HTML <details> elements for native collapsibility
  • Search box with real-time filtering across all properties

Property Coverage:

Entity Type Properties Examples
Person 28 name, born, died, cr_id, sex, gender_identity, father, mother, spouse, children, birth_place, death_place, occupation, nickname, maiden_name
Event 20 title, event_type, date, date_precision, place, person, participants, groups, sources, confidence
Place 8 name, full_name, parent_place, latitude, longitude, place_type, place_category, historical_name

Value Coverage:

Field Canonical Values Examples
Event type 13 birth, death, marriage, burial, residence, occupation, education, military, immigration, baptism, confirmation, ordination, custom
Sex 4 male, female, nonbinary, unknown
Place category 6 real, historical, disputed, legendary, mythological, fictional
Note type 8 person, place, event, source, organization, map, schema, timeline

Data Enhancement Pass (v0.10.17)

Commands and UI tools to upgrade existing vaults by creating missing linked entities from existing person note data. Designed for users who imported GEDCOM before Charted Roots supported event, place, or source note types.

See data-enhancement-pass.md for implementation plan.

Use Cases:

  • Imported GEDCOM before v0.10.0: No event notes were created; birth/death dates are flat properties
  • Imported GEDCOM before v0.9.0: No source notes; source citations were ignored
  • Have place strings instead of wikilinks: birthPlace: "Dublin, Ireland" instead of birthPlace: "[[Dublin, Ireland]]"
  • Want event notes for existing data: Retroactively create event notes to use timeline features

Generate Place Notes (v0.10.17):

  • Scans person notes for birth_place, death_place properties
  • Scans event notes for place properties
  • Detects string values (not wikilinks) that need conversion
  • Creates place notes with proper hierarchy (parents created first)
  • Updates references to use wikilinks
  • Preview mode shows what will be created/modified
  • Matches existing place notes to avoid duplicates
  • Progress indicator during bulk generation with cancel support
  • Paginated results table with search/sort after completion
  • Edit button on each result to open Edit Place modal

Planned Features:

  • Generate Events from Dates: Create event notes from person birthDate/deathDate properties
  • Re-parse GEDCOM for Sources: Re-import GEDCOM to extract sources, matching to existing person notes

Type Customization (v0.10.3)

Full type manager for each note category: events, sources, organizations, relationships, and places. Create, edit, hide, and customize types and categories with user-defined names.

Type Managers:

Each Control Center tab now includes a "Manage types" card:

Tab Types Features
Events 22 built-in event types Custom types, categories (Core, Extended, Narrative), icon/color
Sources 6 built-in source types Custom types, categories, description
Organizations 11 built-in org types Custom types, categories
Relationships 17 built-in relationship types Custom types, color, line style (solid, dashed, dotted)
Places 16 built-in place types Custom types, categories, hierarchy level (0-99)

Type Management Features:

  • Create custom types: Add new types with full customization
  • Override built-in types: Change name, description, icon, color
  • Hide types: Remove from dropdowns while preserving existing notes
  • Reset to defaults: Restore customized built-in types
  • Delete custom types: Remove user-created types entirely

Category Management:

  • Create custom categories to group related types
  • Rename built-in categories to match your terminology
  • Reorder categories with sort order field
  • Hide unused categories (built-in or custom)
  • "Show all" button to restore hidden categories

Place Type Specifics:

  • Hierarchy levels (0-99) determine valid parent-child relationships
  • Categories (geographic, political, settlement, subdivision, structure) organize the UI
  • Users can assign place types to any category regardless of hierarchy
  • Quick level presets for common hierarchy positions

Settings Storage:

// Per-category settings (events shown as example)
customEventTypes: EventTypeDefinition[];
eventTypeCustomizations: Record<string, Partial<EventTypeDefinition>>;
hiddenEventTypes: string[];
customEventCategories: EventCategoryDefinition[];
categoryCustomizations: Record<string, Partial<EventCategoryDefinition>>;
hiddenCategories: string[];

Use Cases:

  • Rename "birth" to "nameday" for fantasy world-building
  • Add "coronation" and "succession" event types for dynasty tracking
  • Create "Land Records" source type for property research
  • Hide unused relationship types like "apprentice" or "mentor"
  • Add "Bodies of Water" category for place types

Flexible Note Type Detection (v0.10.2)

Support multiple methods for identifying Charted Roots note types, avoiding conflicts with other plugins that use the type property.

Problem Solved:

  • The generic type property conflicts with other plugins (Templater, Dataview, etc.)
  • Some users prefer tags (#person) over frontmatter properties
  • Need a namespaced property to avoid conflicts

New Standard: cr_type

New installations now use cr_type as the primary type property:

cr_type: person

This aligns with the existing cr_id convention and avoids conflicts with other plugins.

Detection Methods (checked in order):

  1. cr_type property - New default, namespaced to avoid conflicts (e.g., cr_type: person)
  2. type property - Legacy fallback for existing vaults (e.g., type: person)
  3. Tags - Additional fallback via tags (#person, #place, #event, #source, #map, #organization)
    • Supports nested tags (e.g., #genealogy/person)

Settings:

  • Primary type property: Choose between cr_type (default) or type (legacy)
  • Enable tag-based detection: Toggle tags as fallback detection method

Supported Note Types:

  • person - Person notes with family relationships
  • place - Place notes with geographic data
  • event - Event notes for chronological mapping
  • source - Source notes for evidence management
  • map - Custom map configuration notes
  • organization - Organization notes for hierarchies
  • schema - Schema validation notes
  • proof_summary - GPS proof summary notes

Backwards Compatibility:

  • Existing users automatically keep type as their primary (migrated on first load)
  • Both properties are always checked (primary first, then fallback)
  • Person notes with cr_id but no explicit type are still detected as persons
  • No migration of existing notes required

GEDCOM Import v2 (v0.10.1)

Enhanced GEDCOM import that creates source notes, event notes, and place notes in addition to person notes.

Import Options UI:

  • Toggle for each note type: people, events, sources, places
  • Filename format selection: Original (John Smith.md), Kebab-case (john-smith.md), Snake_case (john_smith.md)
  • Per-type filename formats via "Customize per note type" toggle
  • Progress modal showing import phases with running statistics
  • File analysis with counts before confirming import

Source Import:

  • Parse SOUR records and @S1@-style source references
  • Create source notes (cr_type: source) with available metadata
  • Support for TITL, AUTH, PUBL, REPO fields

Event Import:

  • Create event notes (cr_type: event) for all supported GEDCOM tags:
    • Core (4): BIRT, DEAT, MARR, DIV
    • Life Events (6): BURI, CREM, ADOP, GRAD, RETI, CENS
    • Career/Residence (3): RESI, OCCU, EDUC
    • Legal/Estate (4): PROB, WILL, NATU, MILI
    • Migration (2): IMMI, EMIG
    • Religious (8): BAPM, CHR, CHRA, CONF, FCOM, ORDN, BARM, BASM, BLES
    • Family (7): ENGA, MARB, MARC, MARL, MARS, ANUL, DIVF
  • Preserve date precision from GEDCOM (ABT, BEF, AFT, BET)

Person Attributes (stored as properties):

  • DSCR β†’ physicalDescription
  • IDNO β†’ identityNumber (sensitive - redacted from exports)
  • NATI β†’ nationality
  • RELI β†’ religion
  • TITL β†’ title
  • PROP β†’ property
  • CAST β†’ caste
  • NCHI β†’ childrenCount
  • NMR β†’ marriageCount
  • SSN β†’ ssn (sensitive - redacted from exports)

Place Import:

  • Hierarchical place structure parsing (City, County, State, Country)
  • Create place notes (type: place) with parent/child relationships
  • Duplicate detection: case-insensitive matching on full_name property
  • Fallback matching: title + parent combination for same-named places
  • Update existing places (add missing parent links) instead of creating duplicates

Performance:

  • Optimized connected components analysis (O(n+m) instead of O(nΓ—m))
  • Paginated People tab (100 at a time) for large imports
  • Progress callback throughout all import phases

Integration Points:

  • Staging folder support (import to staging, review, then merge)
  • Property aliases (use configured property names)
  • Value aliases (map GEDCOM event types to Charted Roots types)

Chronological Story Mapping (v0.10.0)

Event-based timeline visualization supporting genealogists (source-derived events), worldbuilders (canonical events), and writers/plotters (narrative timelines).

See Events And Timelines wiki page for full documentation.

Features:

  • Event notes (cr_type: event) as first-class entities with 22 built-in event types
  • Create Event Modal for manual event creation
  • Source event extraction ("Extract events" action with smart suggestions)
  • Person Timeline view (calendar badge on person list items)
  • Family Timeline view (aggregate events for person + spouses + children)
  • Place Timeline view (events at a location over time)
  • Global Timeline in Events tab with filtering and gap analysis
  • Relative ordering with before/after constraints
  • Compute sort order (topological sort from DAG relationships)
  • Groups/factions property for filtering by nation, faction, organization
  • Timeline Canvas/Excalidraw export with multiple layouts (horizontal, vertical, Gantt)
  • Color-coding by event type, category, confidence, or monochrome
  • Events Base template with 20 pre-configured views
  • Fictional date system integration (date_system field, era-based dates)
  • Per-canvas style overrides preserved during regeneration

Event Schema:

cr_type: event
cr_id: "20251205123456"
title: "Birth of John Smith"
event_type: birth
date: 1850-03-15
date_precision: exact
person: "[[John Smith]]"
place: "[[Dublin, Ireland]]"
sources:
  - "[[1850 Birth Certificate]]"
confidence: high
groups:
  - "Smith Family"

Event Types:

  • Core (4): birth, death, marriage, divorce
  • Extended (9): burial, residence, occupation, education, military, immigration, baptism, confirmation, ordination
  • Narrative (8): anecdote, lore_event, plot_point, flashback, foreshadowing, backstory, climax, resolution

v0.9.x

Value Aliases (v0.9.4)

Extend Property Aliases to support custom property values. Allows users with existing vaults to use custom terminology (e.g., nameday instead of birth for event types) without editing existing notes.

See value-aliases.md for implementation plan.

Features:

  • Map custom values to Charted Roots canonical values
  • Support for three field types:
    • Event type: birth, death, marriage, burial, residence, occupation, education, military, immigration, baptism, confirmation, ordination, custom
    • Sex: male, female, nonbinary, unknown
    • Place category: real, historical, disputed, legendary, mythological, fictional
  • Graceful fallback: unknown event types treated as custom
  • Unified "Aliases" card in Preferences with property names and property values sections

Property Aliases (v0.9.3)

Map custom frontmatter property names to Charted Roots fields, enabling compatibility with existing vaults and other plugins without requiring property renaming.

See Settings & Configuration wiki page for configuration documentation.

Features:

  • Configure aliases in Control Center β†’ Preferences β†’ Property Aliases
  • Read resolution: canonical property first, then falls back to aliases
  • Write integration: imports create notes with aliased property names
  • Essential Properties UI displays aliased property names when configured
  • Bases templates generated with aliased property names
  • Full support for all person note properties (identity, dates, places, relationships)

Supported Properties:

  • Identity fields: name, cr_id, type, sex, gender, nickname, maiden_name
  • Date fields: born, died
  • Location fields: birth_place, death_place
  • Relationship fields: father, father_id, mother, mother_id, spouse, spouse_id, child, children_id
  • Other fields: occupation, universe, image, sourced_facts, relationships

Events Tab (v0.9.2)

Dedicated Events tab in the Control Center improves discoverability of Fictional Date Systems and provides foundation for Chronological Story Mapping features.

See events-tab.md for implementation details.

Features:

  • Date systems card: Moved from Canvas Settings with all existing functionality
  • Statistics card: Date coverage metrics (birth/death dates), fictional date usage breakdown
  • Event notes card: Foundation for Chronological Story Mapping

Style Settings Integration (v0.9.1)

Charted Roots styling options exposed via the Style Settings plugin.

See Styling & Theming wiki page for full documentation.

Family Chart View Colors:

  • Female card color (default: rgb(196, 138, 146))
  • Male card color (default: rgb(120, 159, 172))
  • Unknown gender card color (default: rgb(211, 211, 211))
  • Chart background (light/dark themes)
  • Card text color (light/dark themes)

Evidence Visualization Colors:

  • Primary source color (default: #22c55e green)
  • Secondary source color (default: #f59e0b amber)
  • Derivative source color (default: #ef4444 red)
  • Research coverage color bands (well-researched, moderate, needs research)

Evidence Visualization (v0.9.0)

Visual research methodology tools aligned with the Genealogical Proof Standard (GPS).

See evidence-visualization-plan.md for implementation details.

Genealogical Standards Support:

Standard Feature
GPS completeness Fact coverage map showing sourced vs. unsourced claims
Source classification Primary/secondary/derivative visual indicators
Evidence correlation Proof clusters grouping sources supporting conclusions
Conflict documentation Visual markers for contradictory evidence
Written conclusions Proof summary nodes documenting reasoning

Fact-Level Source Coverage:

sourced_facts:
  birth_date:
    sources: ["[[1850 Census]]", "[[Family Bible]]"]
  birth_place:
    sources: ["[[1850 Census]]"]
  death_date:
    sources: []  # Explicitly unsourced

Source Quality Classification:

Classification Meaning Examples
Primary Created at/near event by participant/witness Original vital records, census, contemporary letters
Secondary Created later from memory or hearsay Family bibles (later entries), obituaries, oral histories
Derivative Copies, transcriptions, or abstracts Database transcriptions, published abstracts

Features:

  • Research Gaps Report in Data Quality tab
  • Person fact coverage display (which facts have sources)
  • Enhanced source indicator tooltips on canvas
  • Schema validation for sourced_facts
  • Source quality visualization with color coding
  • Proof summary notes and conflict documentation
  • Canvas conflict markers

v0.8.x

Source Media Gallery & Document Viewer (v0.8.0)

Centralized evidence management linking source documents to person notes.

See Evidence & Sources wiki page for full documentation.

Features:

  • Source note type (cr_type: source) with frontmatter schema
  • 13 built-in source types (census, vital_record, photo, correspondence, newspaper, military, immigration, etc.)
  • Source counting using Obsidian's resolvedLinks metadata cache
  • Source indicators on generated trees: Small badges (e.g., "πŸ“Ž 3") on person nodes showing linked source count
    • Color-coded: green for 3+ sources (well-documented), yellow for 1-2 sources
    • Toggle in Settings β†’ Charted Roots β†’ Canvas styling β†’ "Show source indicators"
  • Media Gallery in Sources Tab: Thumbnail grid with search and filtering
    • Filter by media type (images, documents)
    • Filter by source type
    • Search by filename or source title
    • Lightbox viewer with keyboard navigation (arrow keys, Escape)
  • Sources Bases template with 17 pre-configured views
  • Citation Generator: Generate formatted citations in multiple styles
    • Chicago Manual of Style
    • Evidence Explained (Elizabeth Shown Mills) - genealogical standard
    • MLA (Modern Language Association)
    • Turabian

Source Note Schema:

cr_type: source
cr_id: source-1900-census-smith
title: "1900 US Federal Census - Smith Family"
source_type: census
source_date: "1900-06-01"
source_repository: "Ancestry.com"
media: "[[Census 1900.pdf]]"
confidence: high

v0.7.x

Organization Notes (v0.7.0)

Define and visualize non-genealogical hierarchies (houses, guilds, corporations).

Organization Note Schema:

cr_type: organization
name: "House Stark"
parent_org: "[[The North]]"
org_type: noble_house
founded: "Age of Heroes"
motto: "Winter is Coming"
seat: "[[Winterfell]]"

Person Membership:

house: "[[House Stark]]"
role: "Lord of Winterfell"
house_from: "TA 280"
memberships:
  - org: "[[Night's Watch]]"
    role: "Lord Commander"
    from: "TA 300"
    to: "TA 305"

Visualization:

  • D3-based org chart (tree, radial, dendrogram layouts)
  • View by organization or by person
  • Color coding by role, tenure, or organization type
  • Temporal filtering
  • Export as PNG, SVG, PDF

Fictional Date Systems (v0.7.0)

Custom calendars and eras for world-building and historical research.

See Fictional Date Systems wiki page for full documentation.

Features:

  • Era definitions with name, abbreviation, epoch offset, and direction (forward/backward)
  • Date parsing for {abbrev} {year} format (e.g., "TA 2941", "AC 283")
  • Built-in presets: Middle-earth, Westeros, Star Wars, Generic Fantasy calendars
  • Universe-scoped calendar systems
  • Date Systems card in Events tab
  • Test date parsing input for validation
  • Custom date system creation with era table editor
  • Canonical year conversion for sorting/comparison
  • Age calculation within calendar systems

Usage in Person Notes:

born: "TA 2890"
died: "FoA 61"

Custom Relationship Types (v0.7.0)

Define non-familial relationships beyond parent/child/spouse.

See Custom Relationships wiki page for full documentation.

Features:

  • 12 built-in relationship types across 4 categories (Legal, Religious, Professional, Social)
  • Relationships Tab in Control Center for management
  • Add Relationship Modal with category-grouped dropdown
  • Frontmatter storage in relationships array
  • Canvas edge support with colored edges
  • Statistics card with relationship counts

Schema:

relationships:
  - type: godparent
    target: "[[Jane Doe]]"
    target_id: person-jane-doe
    notes: "Became godparent at baptism in 1920"

v0.6.x

Schema Validation (v0.6.3)

User-defined validation schemas to catch data inconsistencies and enforce data quality rules.

See Schema Validation wiki page for full documentation.

Features:

  • Schema Notes: New note type (type: schema) with JSON code block for schema definition
  • Schemas Tab: Dedicated Control Center tab for schema management
    • Create Schema modal with full UI (no manual JSON editing required)
    • Edit existing schemas
    • Schema gallery with scope badges
    • Vault-wide validation with results display
    • Recent violations list
  • Schema Scopes: Apply schemas by collection, folder, universe, or all people
  • Property Validation:
    • Required properties
    • Type validation (string, number, date, boolean, enum, wikilink, array)
    • Enum validation with allowed values
    • Number range validation (min/max)
    • Wikilink target type validation (verify linked note type)
  • Conditional Requirements: requiredIf conditions based on other properties
  • Custom Constraints: JavaScript expressions for cross-property validation
  • Data Quality Integration: Schema violations section in Data Quality tab
  • Commands: "Open schemas tab", "Validate vault against schemas"

Maps Tab (v0.6.2)

Dedicated Maps tab in Control Center for geographic features management.

See maps-tab.md for implementation details.

Features:

  • Dedicated Maps tab in Control Center with 4 cards
  • Open Map View card: Quick access with coordinate coverage stats
  • Custom Maps gallery: Thumbnail grid with image previews (~150Γ—100px)
    • Map name overlay and universe badge
    • Hover actions: Edit button and context menu button
    • Click thumbnail to open map in Map View
  • Visualizations card: Migration diagrams and place network tools
  • Map Statistics card: Coordinate coverage, custom map count, universe list

Custom Map Management:

  • Create Map Modal for new map notes with image picker, bounds, and universe
  • Edit Map Modal to update existing map properties
  • Duplicate map with auto-generated unique ID
  • Export map configuration to JSON
  • Import map from JSON with duplicate ID detection
  • Delete map with confirmation dialog

Geographic Features (v0.6.0)

Interactive Map View with Leaflet.js for visualizing family history geographically.

See leaflet-maps-plan.md for implementation details.

Features:

  • Interactive Map View with Leaflet.js and OpenStreetMap tiles
  • Color-coded markers (birth, death, marriage, burial) with clustering
  • Additional marker types (residence, occupation, education, military, immigration, religious, custom)
  • Events array support for multiple life events per person
  • Migration paths with directional arrows and person name labels (TextPath)
  • Custom image maps for fictional worlds with universe-based switching
  • Time slider animation ("who was alive in year X?")
  • Heat map layer for geographic concentration
  • Fullscreen mode, mini-map, place search
  • Side-by-side map comparison (split view)
  • GeoJSON and SVG overlay export
  • Interactive image alignment (Leaflet.DistortableImage) - drag corners to align maps
  • Pixel-based coordinates (L.CRS.Simple) for worldbuilders
  • Route/journey visualization (connect all life events chronologically)

Import/Export Enhancements (v0.6.0)

Multiple format support for data interchange with other genealogy software.

Features:

  • GEDCOM import/export
  • GEDCOM X import/export (JSON format)
  • Gramps XML import/export
  • CSV import/export
  • Privacy-aware exports with redaction options
  • Separate Import and Export cards in Control Center UI
⚠️ **GitHub.com Fallback** ⚠️