Release History - banisterious/obsidian-charted-roots GitHub Wiki
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.
-
v0.22.x
- v0.22.16 Round-Up: Modal Polish, Marriage Popup Parity, and Filename Casing
- v0.22.15 Round-Up: Universe Rename Cascade Coverage and Marriage Popup Partner Age
- v0.22.14 Round-Up: Universe Rename Closure, Marriage Marker Pairing, and a Cleanup Wizard Step
- v0.22.13 Round-Up: Map Coverage β Multi-Spouse, Multi-Participant, and Hierarchical Places
- v0.22.12 Round-Up: Marriage Symmetry, Universe Rename Cascade, and Map UX Polish
- v0.22.11 Round-Up: Path Label Architecture, Person-Delete Hardening, and Universe Dropdown
- v0.22.10 Round-Up: Negative Years, Modal Polish, Calendar Era Input, and Auto Regression
- v0.22.9 Round-Up: Map Polish, Sibling Reality Windows, and Cluster Closure
- v0.22.8 Round-Up: Map, Timeline, Statistics, and Modal Polish
- v0.22.7 Round-Up: Map UX, Stepchild Handling, and Universe-Calendar Linking
- v0.22.6 Fix: Fictional-Era Coverage Round-Up
- v0.22.5 Fix: Fictional-Calendar Gaps and Dynamic-Content Noise
- v0.22.4 Hotfix: Step-Parent Save Path
- v0.22.3 Fix: Cross-Entity Collections Aggregation
- v0.22.2 Hotfix: IDs-Only Relationship Arrays
- v0.22.1 Hotfix: Spouse Format Migration
- v0.22.0 Stability Release
- v0.21.x
-
v0.20.x
- v0.20.57 Feature Round-Up
- Child Map Markers and Region Editing
- Linked Map Drill-Down Navigation
- Universe Entity Dynamic Blocks
- Universe Map Thumbnails
- Image Region Crop
- PDF Previews in Media
- Alt Name Display
- Calendar View
- Source Note Hierarchies
- Source Hierarchy Display
- Person-Focused Map Journey
- Customizable Timeline Display Templates
- Citation Integration
- Family Events on Timelines
- Calculate Multiple Relationships
- Cross-Project Research Queries
- Historical Context Overlay and Age Annotations
- Citation Metadata Support
- Comprehensive GEDCOM Field Coverage
- Book & Narrative Compilation
- Entity Profile View
- Structured Role Lists for Organizations
- Mills-Aligned Source Classification
- Map View Marker Layering
- Control Center Modularization
-
v0.19.x
- Unified Place Lookup
- Inheritance & Succession Tracking
- Organization Member Management
- Person Roles in Sources
- Event Type Icons
- Multi-Spouse Visual Cues
- GEDCOM Media Import
- Research Workflow Phase 1
- DNA Match Tracking
- Name Components
- Per-Map Marker Assignment
- GEDCOM Notes Support
- Timeline Event Description Display
- Romantic Relationship Label Preference
- Partial Date Support
- Plugin Rename: Canvas Roots β Charted Roots
-
v0.18.x
- Automatic Wikilink Resolution
- MyHeritage GEDCOM Import Compatibility
- Optional Person Names
- DMS Coordinate Conversion
- DNA Match Tracking - Phase 1
- Web Clipper Integration - Phase 1
- Staging Management
- Export Privacy & Sensitive Data
- Card Style Options
- Gramps Notes Integration
- Edit Person Events & Sources
- Cleanup Wizard Phase 4
- Property Naming Normalization
- Custom Map Authoring
- Nested Properties Redesign
- Inclusive Parent Relationships
- Media Upload and Management Enhancement
- Timeline Export Consolidation
- Create Person Enhancements
- Event Person Property Consolidation
- v0.17.x
- v0.16.x
- v0.15.x
- v0.14.x
- v0.13.x
- v0.12.x
- v0.11.x
- v0.10.x
- v0.9.x
- v0.8.x
- v0.7.x
- v0.6.x
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()andfamilyGraph.getAllUniverses()), omitting the universe-note source that the Control Center'sgetCachedUniversesalready uses. After a Universe rename where sanitization stripped chars from the basename β e.g. typedStar Wars (AU)becoming basenameStar 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.tsextracts the merge logic into a sharedmergeUniverseListhelper. 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 intests/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 theisCanonicaltoggle 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;isCanonicalstays narrative-only since canon/non-canon is storytelling-specific, not applicable to vital records or life events. - Follow-up: the Worldbuilding section that wraps
isCanonicalwas decided once at form-build time from the initialeventTypeand 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 togglescr-hiddenbased onisNarrativeEventType(this.eventType). The event-type dropdown's onChange updates the visibility so users can switch into a narrative type and immediately see theCanonical eventtoggle without saving and reopening. -
isNarrativeEventTypelives insrc/events/types/event-types.tsso the membership fence test can import without pulling in the modal's UI deps. 5 new tests intests/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.MapMarkergainsspouseBirthDate?: string;buildMarkersbuilds the samepeopleByIdlookupbuildJourneyPathsalready does, resolves the spouse viamarriage.spouseId, and threads the spouse'sbornvalue onto marriage markers when resolvable. -
src/maps/map-controller.tsβ the static popup's existingwith Xparticipants line appends(age N)to the partner's entry when the marker carriesspouseBirthDate. Uses the sameDateService.calculateAgepath the focal age already uses, so fictional eras round-trip correctly. Format ends upwith Beru Whitesun (age 38)alongside the existing focal-sideMarriage: 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 intests/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 privateslugifyrunning an aggressive[^a-z0-9]+ -> -regex that destroyed accented chars, lowercased everything, and turned spaces into hyphens.Birth of PadmΓ© Naberriebecamebirth-of-padm-naberrie.md. Person notes go throughsanitizeNamewhich preserves accented characters, casing, apostrophes, hyphens, and spaces β same character was treated inconsistently across entity types. -
src/utils/name-sanitization.tsβ newsanitizeFilename(title, maxLength = 100)helper wrapssanitizeNamewith 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 intests/sanitize-filename.test.ts(replacing 6 obsoleteslugifyTitletests). 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 withsanitizeNameunder 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. Markedpost-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.
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 wasperson | place | event | organization. Map notes (cr_type: map) carry auniverse: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: mapadded toREFERENCING_TYPESso 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 entityuniverse:fields, but thecharted-roots-universe-people/places/events/organizations/mapsblock processors compared against the universe note's frontmattername. WhensanitizeNamestrips 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
getEntitiesForUniverseFilematches against any of the universe note's aliases β basename, frontmattername, orcr_idβ so the lookup survives whichever form an entity'suniverse:field happens to hold (post-cascade basename, dropdown-written name, or cr_id reference).src/dynamic-content/processors/universe-entities-processor.tsandsrc/dynamic-content/processors/universe-maps-processor.tsboth 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)resolveUniverseFilterValuereturned the universe note's frontmatternamewhen 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-memorymapConfigscache stayed stale after an Edit Map save, sogetActiveMapUniverse()kept returning the old value and refresh re-queried with the wrong filter. -
src/maps/map-controller.tsβ newreloadMapConfigs()method exposed publicly. Map view now calls it from asyncMapConfigOnChangehelper on every metadata-cache change forcr_type: mapfiles, then re-resolvesthis.filters.universefromgetActiveMapUniverse()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 intests/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.JourneyWaypointgainsspouseBirthDate?: string;buildJourneyPathsbuilds apeopleByIdlookup from the samepeoplearray it iterates, resolves the spouse viamarriage.spouseId, and threads the spouse'sbornvalue onto the marriage waypoint when resolvable. -
src/maps/map-view.tsβ popup renders a separatePartner's agerow when both the marriage date and a resolvable spouse birth date are present, paired with the existing focal-personAgerow via the sameDateService.calculateAgepath so fictional eras work correctly. Legacy flat marriages withoutspouseId, and spouses without abornvalue, quietly omit the row. Suggested by @DigitalDreamn during #501 verification. 5 new tests intests/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, legacytype: universeshape 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 rewritesuniverse:plain-string references when a universe note's basename changes, but it's keyed onvault.on('rename').UniverseService.updateUniversewas only writing the newnameto 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 ofgetCachedUniversescombining distinctuniverse:values from people / places (still the old name) with universe-note names (new name). -
updateUniversenow sanitizes the new name (via the existingsanitizeNamehelper) and callsapp.fileManager.renameFilewhen 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-stringuniverse: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'sspouse1_marriage_*slot rendered a marker for Owen at Tatooine; Beru'sspouse1_marriage_*slot rendered a separate marker for Beru at the same place). Neither popup named the partner. #493'seventCrId-based dedup didn't catch this because marriages live on each spouse's frontmatter rather than ascr_type: eventnotes. -
loadMarriagesnow readsspouseNandspouseN_idalongside the existing marriage fields, attachesspouseId/spouseNameto the resultingMapMarker. A newdedupeMarriageMarkerspass (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'sparticipantslist, so the existing popup rendering surfaces "Owen Lars / with Beru Whitesun" naturally. - Journey-mode rich popup also gains a
Partnerrow for marriage waypoints (covering @DigitalDreamn's "indicate to who" suggestion from #498 verification). 8 new tests intests/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 canonicalisPlaceNotedetection, lists them in the preview, and applies a generatedcr_idto each viaprocessFrontMatter(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. Customcr_typevalues ("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 flatmarriage_place/marriage_place_id/marriage_datefrontmatter fields. People with multiple spouses (whose data is written to indexedspouseN_marriage_*slots after #481's bidirectional linker improvements) had no flat fields populated, so journeys silently dropped every marriage.PersonData.marriagesbecomes an array; newloadMarriageshelper reads indexed slotsspouse1_marriage_*throughspouse10_marriage_*(matching the writer's iteration bound), falls back to a single legacy flat entry, and skips empty slots. -
buildMarkersandbuildJourneyPathsiterate the array, emitting one waypoint / marker per populated slot. Drops the&& person.marriageDaterequirement so dateless marriages still surface (sorting to the end of the life-event run, like death and burial already do). 10 new tests intests/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β acr_type: eventnote 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.buildMarkersiterates per-person andEventService.getEventsForPersonsurfaces the same external event for each participant, so each per-person pass contributed its own marker. - Threads the event note's
cr_idthroughLifeEventandMapMarker(set only for externalcr_type: eventnotes β inline events stay per-person, never dedup), then a newdedupeEventMarkerspass after marker collection groups byeventCrId, keeps one marker per group with the event note'spersonfield as primary, and lists all participants in the popup. 8 new tests intests/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'sbirth_place/death_place/ event location pointed at a child place (e.g.,Lars Homesteadwithparent_place: [[Tatooine]]) that had no own pixel or geographic coordinates, the marker dropped silently becausehasValidCoordinatesreturned false on the child. The map's place resolution now walks up theparent_place/parent_place_idchain 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 Homesteadshows 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). AddsparentPlaceIdto the place cache and anapplyCoordinateFallbackstep insideresolvePlace. 7 new tests intests/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 stringCustomin 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 viacustomLabelonMapMarker; the journey-mode rendering path was missed at the time. -
JourneyWaypointnow carriescustomLabel,buildJourneyPathspropagates it from the sourceLifeEvent, and a newgetJourneyWaypointEventLabelhelper resolves the display label (preferringcustomLabelforcustomwaypoints, falling back to canonicaleventTypeotherwise) so both render sites stay in sync. 5 new tests intests/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 viaalign-items: centeron 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-childrule applying asymmetric padding (0 0 16pxon X,16px 0 0on 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: columnon the controlEl. Pixel inputs render at 100px each via a--pixelmodifier; geographic inputs at 140px via--geoto 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 indexedspouseN:format, but missed the case where the partner was still on the legacy flatspouse:/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 becausefindExistingSpouseIndexonly scanned indexed slots. - New
promoteFlatSpouseToIndexedhelper β when the target uses single-spouse flat format and the source has marriage details to mirror, the promotion atomically rewrites the target'sspouse:/spouse_id:tospouse1:/spouse1_id:so the existing mirror code has aspouseN_*namespace to write the companion fields into. The atomic single-processFrontMatterwrite 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_namevscollectiondiscoverability 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.
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 mirroredspouse+spouse_idbetween two notes but skipped thespouseN_marriage_date/_marriage_location/_marriage_location_id/_marriage_status/_divorce_datecompanion 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. -
syncSpousenow accepts an optionalMarriageDetailsobject extracted from the source's indexed slot viaextractMarriageDetails. NewfindExistingSpouseIndexhelper locates the target's slot for already-linked partners byspouseN_idmatch (with wikilink fallback), so updates propagate on top of an existing link, not just initial fill.writeMarriageDetailsToTargetonly 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β newcascadeUniverseRename(oldBasename, newBasename)iterates all markdown files, filters tocr_typeof person / place / event / organization, and rewritesuniverse:plain-string fields that exactly matcholdBasenametonewBasename. Wikilink-syntax values ([[Old Name]]) are left to Obsidian's native rewrite, andcr_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βregisterUniverseRenameHandlersubscribes tovault.on('rename')and dispatches the cascade forcr_type: universefiles. Critical implementation note: the metadata cache is mid-update during the rename event sogetFileCachereturns null synchronously, andmetadataCache.on('changed')doesn't fire for content-unchanged renames either. Took two failed approaches (synchronous cache read; one-shotchangedlistener) before landing on the working pattern: read the file content directly viacachedReadand parsecr_typefrom 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βjourneyWaypointDedupKeywas 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
eventTypeinto 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 intests/journey-waypoint-dedup.test.tscovering 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 inbuildJourneyPathschecked the universe filter but skipped theisPlaceVisibleOnMapper-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 throughMapSettingsβMapView.getMapSettings()βMapController.addPathLabel. - When non-
none, each label's SVG<text>element gainspaint-order: stroke fillplus 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 sharedaddPathLabelhelper. 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.5xetc. controlled total step duration, not popup dwell. After the ~1100ms camera fly the popup got whatever was left, so at default1x(2000ms total) the popup was visible for ~900ms; at the slowest0.25xsetting the popup never opened before the next step fired. - Reframed semantically as an explicit dwell-time selector with second labels:
2s/4s/6s/10s, default4s. Total step interval is nowJOURNEY_FLY_MS (1100) + dwellMs, so the popup always gets its full visible time regardless of fly duration.journeyMode.speedrenamed tojourneyMode.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
pathLabelEntriesregistry tracks every label so a debouncedzoomendlistener 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 asFamily ${index + 1}regardless of whether a connected component's members shared agroup_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.collectionNamefor the tab label when every member shares one (falls back toFamily Notherwise), and merges components with the same collectionName into a single tab. Merge logic extracted tosrc/core/family-component-merge.tswith 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
collectionNamedata the graph service already computed.
Feat: "Manage memberships..." affordance on the person side (#490):
-
src/plugin/context-menus.tsβManageOrganizationMembersModalwas 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 underCharted Rootsplus the mobile flat menu for parity). HelperopenManageMembershipsForPersonhandles three cases: 0 memberships β notice; exactly 1 β opens the modal directly scoped to that org; 2+ β opens aFuzzySuggestModalpicker 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 noalign-itemsrule, defaulting tostretch. Each.setting-itemflex child layouts its inner label + input independently; depending on Obsidian's default.setting-itemflex behavior, the rows could end up on slightly different vertical baselines. - One-line fix: add
align-items: centerto 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-pdfwas registered in the catalog UI (report wizard, trees tab, book builder) butUnifiedTreeWizardModalrewrites 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?: booleanflag onReportMetadata;getReportsByCategoryand 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 calledflyTo(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 passesmap.getZoom()instead, so the user's framing β set byfitBoundson 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 whenorientation: '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: 0andweight: 0so 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.shouldFlipPathLabelis replaced byfindLongestScreenSegmentandcreatePathLabelHost. 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*_idarrays 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'schildren_idpointing 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-90but not for the originalDE -5740ishrepro. 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 likeDE -5740ishfailed the negative-detection branch entirely and fell through to the bare-digits fallback, which strips the sign. - Replaced the trailing
\bwith(?=$|[^0-9])lookahead so the negative-detection branch matches against any non-digit terminator. Also removed the duplicateextractYearimplementation in the relationships renderer (a pre-existing bare\b(\d{4})\bregex 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 initialfitBoundscall readmarker.lat/marker.lngonly 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 beforepanToWaypointflew 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βgetCachedUniversespreviously sourced the dropdown from the distinctuniverse: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) intogetCachedUniversesalongside 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 theiruniverse: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 withcr_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.extractPersonNoderan an exclusion list β checking for the known non-person types (source, event, place, organization, proof_summary, universe, citation) and treating anycr_id-bearing note that didn't match as a person. User-defined custom types likehexorfactionfell through every exclusion and got coerced into people. - Fix adds an explicit
isPersonNoteinclusion check after the exclusion list.isPersonNotealready handles the unknown-cr_typecase correctly (returns false whencr_typeis set to anything other thanperson), while preserving the legacy "cr_id with nocr_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) βisPersonNoteinclusion 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.
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 amin="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 totype="text". Change handler now routes throughDateService.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 (newcurrentYearUniversefield) so renders β including month-boundary rollovers β format viaformatCanonicalYear. Persisted state gains ayearUniversefield so era context survives view reopens. - The
year > 0constraint 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 underlyingflex-shrink: 0on.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 ownwidth: 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-basedshouldFlipPathLabelheuristic. 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 theLatLng[][]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βextractYearhad four sign-bearing branches but no rule for custom-era formats with explicit standalone minus signs. The 4-digit-year regex\b(\d{4})\bmatched the digits and dropped the leading-because\b(word boundary) eats it. - New
(?:^|[^0-9])-(\d+)\bbranch captures standalone negatives while excluding ISO date separators (where the hyphen is between digits). 5 new tests intests/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:setTexterrors withtransform="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
orientationkey entirely when no flip is needed (per leaflet-textpath line 128 sourceif (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.
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 fromMapData.yearRange(which already returns canonical signed years for fictional eras via #454). Previously hardcoded to1800/2000, making the feature unusable on fictional-era universes β the slider's range never intersected the data. -
src/dates/services/date-service.tsβ newformatCanonicalYear(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 toString(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 ingatherFamilyEventshad 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'sisEventAfterFocalDeath. Same-year siblings (twins, close births) still surface β the guard fires only on unambiguous before-focal-birth viaDateService.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
gatherFamilyEventsblock, two symmetric helpers (isEventBeforeFocalBirth/isEventAfterFocalDeath). - Added 4 tests to
tests/timeline-reality-window.test.tsmirroring 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 onCRS.Simpleimage 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 vialatLngToLayerPoint) 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βpanToWaypointonly consultedwaypoint.lat/waypoint.lngfor the camera fly-to and rich-popup placement. Pixel-coord places default lat/lng to0(per the journey waypoint construction), so the camera flew to(0, 0)β bottom-left corner ofCRS.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.extractPlaceNodeearly-returns when a place-shaped note lacks acr_id, so such notes never enterplaceCache. 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_idplaces 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_idat 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.
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βextractYearnow defers toDateService.parseDatefirst when a fictional calendar resolves the input. Constructor takes an optionalpluginref 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'sparseYearthroughDateServicefor fictional-era awareness, but this Statistics Dashboard surface is a separate code path with its ownextractYearregex. For BBY descending eras, the digit-run got read as a positive number (1045 BBYβ 1045) and the naivebirthYear > deathYearcheck 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.extractYearpreviously required a 4-digit numeric year, so fictional-era timestamps under 1000 like82 BBY/41 BBYparsed asundefined. 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.extractYearnow defers toDateService.parseDatefirst, 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<= 80upper bound that bypassed themaxAgegetter the rest of the engine uses. WithenableFictionalDateson,maxAgereturnsInfinityto admit long-lived characters; the hardcoded80silently dropped marriages over that threshold.getMarriagePatternAnalysis(age at first marriage) andcomputeLongestMarriages(marriage duration) now both defer tothis.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βbuildJourneyPathspreviously deduped consecutive waypoints by comparinglatandlngonly. On custom image maps, every pixel-coord place usespixel_x/pixel_yand defaultslat/lngto0, 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β newjourneyWaypointDedupKeyhelper prefersplaceIdwhen 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 oflinks_viewby 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 insrc/family-tree/family-chart-overlay-z.tsreturnstrueonly 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'schildrenCrIdswithout 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
stepchildrenCrIdsfrom the sibling-births iteration), plus a newisEventAfterFocalDeathhelper usingDateService.getCanonicalYearso 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 listedstepchild_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 β usesstep_child_id(underscore betweenstepandchild). 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'sstep_child_idarray even though every other relationship array got cleaned. Renamed the entry tostep_child_idso the cleanup matches the field the rest of the plugin actually writes; the droppedstepchild_idform 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 staleplaceGraphcache root cause.PlaceGraphService.ensureCacheLoadedonly 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 theparent_placewikilink without the companionparent_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 viagetPlaceByName, populateparentPlaceIdandparentPlacefrom the resolved node and clearpendingParentPlacebefore 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_idcorrectly 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βLifeEventandMapMarkerboth gaincustomLabel?: string. Carries the raw user-authored event type (or event-note title) when the resolvedMarkerTypecollapses tocustom. -
src/maps/map-data-service.tsβparseEventsArray(inline events on the person's frontmatter) andloadExternalEventsForPerson(externalcr_type: eventnotes) populatecustomLabelfrom the rawevent_typeor theEventNote.titlewhen the resolved type iscustom. Marker construction threads the field through. -
src/maps/map-controller.tsβcreatePopupContentusescustomLabel(capitalized) as the popup type label whendata.type === 'custom'and a label is available; falls back to the literalCustom: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.tsandsrc/maps/map-view.tsβ both hardcoded defaults forcustomMarkerColorchanged 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 existingflex-shrink: 0rule on.setting-item-infomeant 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'sapp://renderer, so infobox photos rendered as broken-image icons in reading mode. Added areplacefilter to theselectorHtml:.infoboxextraction 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.
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 ofDEFAULT_DATE_SYSTEMS. When the universe name slug-matches a built-in'suniversefield, 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 whendefault_calendaris set. -
src/universes/ui/calendar-suggest.tsβ extracteduniverseNameToSlugandsuggestBuiltinForUniverseNameinto their own module so unit tests can exercise the slug-match logic without pullingModalin via the wizard's main file. - Layered contract per docs/planning/universe-calendar-linking.md:
universe.default_calendaris the explicit pointer, the parser is unchanged and continues to use global era-abbreviation matching at parse time. Existing universes with nodefault_calendarcontinue 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.timelineShowSpouseDeathsflipped fromfalsetotrue. 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) βplanFrontmatterCleanuprules engine over a single note's frontmatter,cleanupPersonReferencesAfterDeletewalks the vault and applies the plan viafileManager.processFrontMatter,getDeletedPersonCrIdrecovers the cr_id frommetadataCache'sprevCachecallback argument. -
main.tsβ registers ametadataCache.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 polymorphicspouse_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 gainsstepchildrenCrIds: string[], populated in the second pass by inverting each child'sstepfatherCrIds/stepmotherCrIds. Mirrors the existingadoptedChildCrIdsreverse-walk pattern. -
src/core/cross-import-detection.tsandsrc/core/canvas-split.tsβ initialize and filter the new field at the construction sites that build PersonNodes. -
src/dynamic-content/renderers/timeline-renderer.tsβgatherFamilyEventsskips any childCrId instepchildrenCrIdsfrom 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βMapMarkergainsbirthDate?: string, plus a newformatPopupDateRangehelper that rendersfrom β tofor true durations and collapses identical / single-sided cases. -
src/maps/map-data-service.tsβ bothcreateMarkerFromPlaceandcreateMarkerFromEventpopulatebirthDatefromperson.born, mirroring the JourneyPath shape from #434. -
src/maps/map-controller.tsβcreatePopupContentformats the date row throughformatPopupDateRangeand appends(age N)for non-birth events whenDateService.calculateAgeresolves a non-negative age. Sameage.errorguard 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βapplyJourneyFilternow 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; reusesjourneyControlsElso existing teardown clears it cleanly. -
styles/map-view.cssβ.cr-map-journey-controls--emptyand.cr-map-journey-empty-messagemodifiers for the placeholder variant.
Fix: Map fullscreen control tooltip (#446):
-
src/maps/map-controller.tsβinitializeFullscreennow passestitletoleaflet-fullscreenas{ 'false': 'Enter fullscreen', 'true': 'Exit fullscreen' }(the option shape the plugin actually expects). The previous flat-string form causedoptions.title['false']to evaluate toundefined, 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) βformatPopupDateRangeandMapMarker.birthDatepopulation for #444. -
tests/universe-calendar-suggest.test.ts(12 tests) βuniverseNameToSlugandsuggestBuiltinForUniverseNamefor #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.
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βparseYearnow accepts an optionaluniverseand defers toDateService.parseDatefirst, 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). NewisFictionalDatehelper gatesFUTURE_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βgetPersonDatanow also callsEventService.getEventsForPersonfor each person and normalizes the returnedEventNotes into the sameLifeEventshape 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. NewcoerceDateValuehelper normalizes YAMLDateobjects (unquoted ISO dates likedate_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β addedgetDateService()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β newcomputeEventAge(birthDate, eventDate, universe)helper that defers toDateService.calculateAgeand falls back to naive year subtraction only for real-world dates or when DateService isn't available. The helper checksAgeCalculation.errorso 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 insidegatherFamilyEvents(children's births, spouse / parent deaths, sibling births, adoptions, adopted children's births), and the marriage / divorce paths inbuildTimelineEntries. Also dropped thebirthYear: numberparameter fromgatherFamilyEventsandparseContextNotesince they now readbirthDateanduniversefrom 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.
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β addedbirthDate?: stringtoJourneyPathso the raw input is preserved alongside the extractedbirthYear. -
src/maps/map-data-service.tsβ populatedbirthDatewhen assembling journey paths. -
src/maps/map-view.tsβ threaded the wholeJourneyPaththroughjourneyStep/toggleJourneyPlayback/panToWaypoint/buildRichWaypointPopup(replacing the barebirthYear?: numberparam), and swapped the numeric-subtraction age and duration calculations forDateService.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βisStandardDateFormatandnormalizeDateStringnow take an optionaluniverseargument and defer toDateService.parseDatewhen the real-world regex gates fail; a string that parses as fictional counts as recognized. All six callers (two incheckDataFormat, two in theFix allnormalizer, two in the preview path) pass the person's universe through.NON_STANDARD_DATEno longer fires for22 BBY-style inputs on persons scoped to a universe with an active fictional calendar, and the bulk-normalize path stops trying to rewrite them towardYYYY-MM-DD.
Fix: Dynamic-content processors skip cleanly when the source file is missing (#431):
-
src/dynamic-content/services/dynamic-content-service.tsβbuildContextnow returnsnullwith a warn log instead of throwingCould not find file: β¦whenapp.vault.getAbstractFileByPathcan't resolvectx.sourcePath. Matches the skip-and-warn patternBidirectionalLinkeralready uses. - All 18 callsites across
media-processor,timeline-processor,relationships-processor,sources-processor,transfers-processor, andextractions-processorβ initial renders and every re-render closure insidemetadataCache.on('changed')andvault.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 theMarkdownRenderChild.
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.
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β extendedLoadedRelationshipswithstepfatherName/IdandstepmotherName/Idsingletons; added extraction with the same wikilink-fallback pattern used for adoptive parents (supports explicitstepfather_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 theeditPersonDatapassed to the Edit Person modal. -
src/core/person-note-writer.tsβ added step-father and step-mother write branches toupdatePersonNote, mirroring the adjacent adoptive-parent pattern. Handles array inputs (ascreatePersonNotedoes), 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.
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.tsmerges 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
loadExistingCollectionsnow 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.
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
resolveCrIdToNamehelper is the inverse of the existingresolveNameToCrIdβ takes acr_idand returns the stored display name by looking up the person in the graph. -
loadAlignedArraynow falls back to walking the*_idarray when the wikilink key is genuinely absent (null / undefined). An explicitly emptychildren: []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
*_idbut 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.
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
isSpouseInFrontmatterpure helper scans every possible spouse location (flat + allspouse{N}slots) before the deletion detector firesremoveSpouseLink. - 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 notspouse{N}_marriage_location_idorspouse{N}_marriage_status, leaving orphaned metadata when a legitimate unlink occurred. - Cleanup is now complete, in parity with the existing
person-note-writer.tsclear 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.
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 flatspouse: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
syncSpouseand theaddBidirectionalSpouseLinkhelper 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: eventnotes 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 threwTypeError, surfacing as Obsidian's red code-block error. - Helpers now accept
string | number | undefined | nulland coerce at entry. Same-class fix applied toformatDisplayDateso 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 existingAdoptive father/Adoptive motherlabel 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
symmetricflag 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_dateandburial_placewere already recognized by the GEDCOM importer/exporter, map markers, and cleanup tooling β but thecharted-roots-timelineblock 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 whenburial_placeis set, and age computed fromborn. - 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.
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 || undefinedidiom converted a toggled-offfalsetoundefined, 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.
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
personsfrontmatter 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_dateetc.) - 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
relationshipsarray now check the target person's sex - Female step-parents correctly assigned to
stepmotherCrIdsinstead of alwaysstepfatherCrIds
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_mapset - Positioned at the center of
parent_regionif 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/hto 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
Full drill-down navigation between custom maps for multi-scale worldbuilding.
GitHub Issue: #361
Phase 1 β Place-level linking:
-
linked_mapproperty 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_mapproperty 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/hproperties 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
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.
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
sizeparameter (small/medium/large)
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_cropfrontmatter 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: 250Documentation: Frontmatter Reference β Image Crop Regions, Media Management
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-mediadynamic 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
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
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
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) andsource_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
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
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
Four new capabilities for controlling how timeline entries are displayed in dynamic content blocks.
GitHub Issue: #325
Features:
-
Layout modes:
layoutparameter withchronological(default),grouped(sections for personal/family/context), andpersonal-firstoptions -
Label customization: Six settings under Advanced > Timeline labels to override birth, death, and family event labels with
{name}placeholder support -
Format strings: Per-block
formatparameter 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
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
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
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)
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
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
-
defaultTimelineContextsetting applies a context note to all timelines globally - All timeline events display age annotations when the person's birth date is known
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
SOURblocks withPAGE/QUAYsub-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
citationsFoldersetting (default:Charted Roots/Citations)
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
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:
- Book Builder β Usage documentation
- Book & Narrative Compilation Planning β Design and implementation plan
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:
- Entity Profile View β Usage documentation
- Entity Profile View Implementation β Developer implementation guide
- Entity Profile Views Planning β Full specifications for all phases
- Community contributors: @prentissw (workflow feedback, Relationships section)
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
- MaesterList 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:
- Block-level
role-orderconfig (explicit override) - Organization's
rolesproperty (inherited from note) - Alphabetical order (default)
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)
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)
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.
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-righticon
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.
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:
- Unified Place Lookup Planning β Design and implementation plan
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.
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.
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)
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.
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.
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.
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 andgetResearchStatistics() -
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:
- Research Workflow β User documentation
- Research Workflow Integration Planning β Detailed specifications
Community Contributors: @ANYroots (IRN structure, GPS methodology), @wilbry (lightweight approach, research journal concept)
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β AddedenableDnaTrackingsetting -
src/ui/settings-tab.tsβ Added DNA tracking toggle in Advanced section -
src/models/person.tsβ AddedpersonTypeand 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β Addeddnacategory,requiresSettingproperty -
src/relationships/constants/default-relationship-types.tsβ Addeddna_matchrelationship type -
src/core/bidirectional-linker.tsβ AddedsyncDnaMatch()andremoveDnaMatchLink()methods -
src/ui/person-picker-modal.tsβ Added DNA badge rendering
Documentation:
Explicit name component properties in frontmatter for multi-surname cultures (Hispanic, Portuguese) and maiden/married name tracking.
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Γ³pezMaiden 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():
- If
surnamesarray exists β count each surname - Else if
maiden_nameexists β count that (for maiden-name-primary users) - 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β UsesextractSurnames() -
src/ui/split-wizard-modal.tsβ UsesmatchesSurname()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:
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-mapFiltering Logic:
- If place has no
mapsproperty: Shows on all maps with matching universe (existing behavior) - If place has
mapsproperty: 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)
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}.mdin Notes folder - Person note contains wikilink:
- [[Note for John Smith]]
Files Modified:
-
src/gedcom/gedcom-types.tsβ Addednotes,noteRefsto individual interface; addedGedcomNoteRecord -
src/gedcom/gedcom-parser-v2.tsβ Parse1 NOTEunder INDI; parse0 @N001@ NOTErecords -
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 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 inrenderTimelineList()andgenerateMarkdown()
Related:
- #183 β Birth event role filtering (tracked separately)
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:
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β AddedformatDisplayDate()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
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:
- Settings β BRAT β Beta Plugin List
- Remove
banisterious/obsidian-canvas-roots - Add
banisterious/obsidian-charted-roots
Files Added:
-
src/migration/plugin-rename-migration-service.tsβ One-time vault migration service
Documentation:
- Plugin Rename Planning Document
- Community discussion: #58
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.mdFiles 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:
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 &lt; β <, &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β AddedpreprocessingAppliedandpreprocessingFixesto result types -
src/settings.tsβ AddedgedcomCompatibilityModesetting and UI control -
src/ui/gedcom-import-modal.tsβ Display preprocessing info in import results
Documentation:
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β Madenameproperty optional inPersonDatainterface -
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β AddedNO_NAMEwarning andwithNamecompleteness metric
Documentation:
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β AddedenableDMSCoordinatessetting -
src/ui/create-place-modal.tsβ Integrated DMS parser into coordinate inputs
Documentation:
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.
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:
- Stats summary β Only counts matching entities
- Batch cards β Only shows batches containing matches
- 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:
- Web Clipper Integration β User guide with setup and workflow
- Data Entry β Comparison with other data entry methods
- Web Clipper Integration Planning β Implementation details
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
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:
- Import Workflow β Staging folder configuration
- Staging Management Planning β Implementation details
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:
- Privacy & Security β User guide
- SECURITY.md β Security policy
- Export Privacy Planning β Implementation details
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 |
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:
- See Gramps Notes & Family Integration Planning for detailed specifications
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:
- See Edit Person Events & Sources Planning for detailed specifications
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
DataQualityServiceand 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:
- See Cleanup Wizard Phase 4 Planning for detailed specifications
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, matcheschildren_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
childproperty - Merges with existing
childrenif both exist (deduplicates) - Removes legacy
childproperty after migration
Backward Compatibility:
- Plugin reads both
childandchildrenduring transition - Future breaking change to remove
childread support planned
Documentation:
- See Deprecate Child Property Planning for detailed specifications
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:
- Select Image β Browse vault for map image with preview and auto-detected dimensions
- Configure Map β Set name, universe (optional), coordinate system (pixel default for fantasy maps)
- Add Places β Click on map preview to add initial locations (optional, can skip)
- 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:
- See Custom Map Authoring Planning for detailed specifications
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- Newsourced_*andlife_eventsproperties -
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
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
parentsproperty
- Conditional visibility: label setting only shown when toggle enabled
2. Schema Changes
- New
parentsproperty (wikilinks, can be array for multiple parents) - New
parents_idproperty (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_idrelationships - 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
parentsarray, automatically adds to each parent'schildrenarray - 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:
- Opt-in, not replacement β Father/mother fields remain; this adds alongside
- Configurable β Users customize terminology to their preference
- Non-disruptive β Users with traditional setups see no UI changes
- 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:
- src/settings.ts β Settings schema and UI
- src/types/frontmatter.ts β Schema definition
- src/core/family-graph.ts β Family graph integration
- src/core/bidirectional-linker.ts β Bidirectional sync
- src/core/person-note-writer.ts β Frontmatter writing
- src/dynamic-content/renderers/relationships-renderer.ts β Relationships display
- src/ui/views/family-chart-view.ts β Family Chart View
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 traversalPriority Order (Fallback Logic):
- Check biological parents (father/mother)
- If none, check gender-neutral parents
- If none, check adoptive parents
Bidirectional Sync:
- Uses same
childrenarray as father/mother relationships - Each parent in
parentsarray gets child added to theirchildren - Deduplication by both cr_id and wikilink
- Deletion detection for relationship cleanup
Planning Documentation:
- See planning document for detailed specifications and design decisions
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:
- See archived planning document for detailed specifications
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:
- See Timeline Export Consolidation Planning for detailed specifications
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:
- Create Person A β Save β Close
- Create Person B β Save β Close
- Edit Person A to link B as spouse β Save β Close
- Create Child C β Save β Close
- Edit Child C to set parents β Save β Close
- ...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
PersonDatainterface - 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:
- Create Person Enhancements Planning - Detailed specifications
- Data Entry - User documentation
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:
- Open the Cleanup Wizard (Control Center β Data Quality, or command palette)
- Navigate to Step 11: "Migrate Event Person Properties"
- Review detected notes and click "Apply All"
The legacy person property continues to be read indefinitely for backward compatibility.
Documentation:
- Events And Timelines - Updated property documentation
- Frontmatter Reference - Updated event properties
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.
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
boxparameter fromaddText) - 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
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:
-
CleanupWizardModalorchestrates 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.
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:
-
SourceMigrationServicehandles detection and migration - GEDCOM and Gramps importers now write array format by default
- Statistics service only reads
sourcesarray (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.
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
lastSeenVersionsetting
Files Added:
| File | Purpose |
|---|---|
src/ui/views/migration-notice-view.ts |
Workspace view for upgrade notice |
styles/migration-notice.css |
Notice styling |
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
ReferenceNumberingServicefor numbering step -
.gpkgformat 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.
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.
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.
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.
Display events with Calendarium fc-* date fields on Charted Roots timelines, with calendar filtering support.
Problem Solved:
- Events using Calendarium's
fc-dateformat 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:
- Enable Calendarium integration in Preferences β Integrations
- Enable "Show Calendarium dates on timelines" toggle
- Event notes with
fc-datefields will now appear on timelines - The
fc-calendarvalue 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:
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 | 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.
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.
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
mediaproperty 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:
- Media files are extracted to your configured media folder
-
objrefelements in the Gramps XML are resolved to vault paths -
mediawikilinks are added to Person, Event, Place, and Source frontmatter - 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.
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.
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
DashboardTabcomponent insrc/ui/dashboard-tab.ts - New
RecentFilesServiceinsrc/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.
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.
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.
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
universefield 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-mapFeatures:
| 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
universefield 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
universevalues 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.
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.
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
PEDItags) 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-456GEDCOM 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.
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.
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/excludeevent 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/excluderelationship 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
registerMarkdownCodeBlockProcessorAPI -
DynamicContentServiceprovides shared utilities for config parsing and data resolution -
TimelineProcessorandRelationshipsProcessorhandle 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
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.
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:
-
ImageFilenameParserservice 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
SourceServicefor note creation and updates
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:
- Install Calendarium plugin
- Open Control Center β Preferences β Integrations
- Set Integration mode to "Read-only (import calendars)"
- Calendarium calendars appear in Date Systems card and Create Event modal
Technical Details:
- Uses
window.Calendariumglobal API - Waits for Calendarium settings to load before importing
- Converts Calendarium eras to Charted Roots
FictionalEraformat - 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.
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βBIRTin GEDCOM) - Value aliases: Map custom event types, sex values, place categories to canonical values before export
-
Gender identity field: New
gender_identityproperty exported appropriately for each format- GEDCOM: Custom
_GENDtag - GEDCOM X:
genderfield - Gramps XML: Custom attribute
- CSV: Dedicated column
- GEDCOM: Custom
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
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
sexfield 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_identityproperty on person notes - Separate from biological
sexfield for historical record accuracy - Displayed in People tab person details
- Included in all export formats:
- GEDCOM: Custom
_GENDtag - GEDCOM X:
genderfield - Gramps XML: Custom attribute
- CSV: Dedicated column
- GEDCOM: Custom
Schema-Based Definitions (Phase 2):
- Schema system supports custom sex/gender values via
enumtypes - Scoped by collection or universe for worldbuilding
- Example:
sexvalues 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
sexfield with GEDCOM M/F values; optionallygender_identityfor living relatives or LGBTQ+ research -
Fiction writer / Worldbuilder: Custom sex values via Schema, separate
gender_identityfield for character development
Respectful Trans Documentation: When documenting trans individuals:
-
namefield holds chosen/current name (displayed by default) - Optional
birth_namefield for birth records if needed for research -
gender_identitycaptures current identity -
sexcaptures what appears on historical records - Privacy options can exclude
birth_nameandsexfrom exports
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 |
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 ofbirthPlace: "[[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_placeproperties - Scans event notes for
placeproperties - 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/deathDateproperties - Re-parse GEDCOM for Sources: Re-import GEDCOM to extract sources, matching to existing person notes
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
Support multiple methods for identifying Charted Roots note types, avoiding conflicts with other plugins that use the type property.
Problem Solved:
- The generic
typeproperty 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: personThis aligns with the existing cr_id convention and avoids conflicts with other plugins.
Detection Methods (checked in order):
-
cr_typeproperty - New default, namespaced to avoid conflicts (e.g.,cr_type: person) -
typeproperty - Legacy fallback for existing vaults (e.g.,type: person) -
Tags - Additional fallback via tags (
#person,#place,#event,#source,#map,#organization)- Supports nested tags (e.g.,
#genealogy/person)
- Supports nested tags (e.g.,
Settings:
-
Primary type property: Choose between
cr_type(default) ortype(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
typeas their primary (migrated on first load) - Both properties are always checked (primary first, then fallback)
- Person notes with
cr_idbut no explicit type are still detected as persons - No migration of existing notes required
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
SOURrecords and@S1@-style source references - Create source notes (
cr_type: source) with available metadata - Support for
TITL,AUTH,PUBL,REPOfields
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
-
Core (4):
- 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_nameproperty - 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)
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/afterconstraints - 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_systemfield, 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
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
-
Event type:
- Graceful fallback: unknown event types treated as
custom - Unified "Aliases" card in Preferences with property names and property values sections
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
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
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:
#22c55egreen) - Secondary source color (default:
#f59e0bamber) - Derivative source color (default:
#ef4444red) - Research coverage color bands (well-researched, moderate, needs research)
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 unsourcedSource 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
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
resolvedLinksmetadata 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: highDefine 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
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"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
relationshipsarray - 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"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:
requiredIfconditions 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"
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
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)
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