Architecture#
Source layout#
All of the plugin’s logic lives under src/:
File |
Responsibility |
|---|---|
|
Plugin entry point: loads settings, wires up the settings tab, editor suggest, live preview extension, and post-processor; adds the Refresh contacts command. |
|
Shared types: |
|
Default settings and the settings UI. |
|
Looks up the current Obsidian UI language via the official
|
|
A minimal read-only CardDAV client: address book discovery
( |
|
A small vCard 3/4 parser extracting |
|
|
|
|
|
A CodeMirror 6 |
|
A Markdown post-processor that does the same replacement for Reading view, by walking rendered text nodes. |
|
The chip DOM element, and the click-to-open detail card (a manually positioned floating panel, closed on outside click or Esc). |
Data flow#
settingsTab.ts --> CardDavProfile[] (server URL, credentials)
|
v
carddav/client.ts --> PROPFIND/REPORT over requestUrl
| (discovery + address book fetch)
v
carddav/vcard.ts --> parses each vCard into a Contact
|
v
carddav/store.ts --> merged, cached Contact[] across profiles
|
+--> editorSuggest.ts (HrcardEditorSuggest) --> autocomplete while typing
|
+--> render/livePreview.ts --> chip widgets (Live Preview)
|
+--> render/postProcessor.ts --> chip elements (Reading view)
|
v
render/chip.ts + render/card.ts --> click-to-open detail card
Reference syntax#
A resolved reference is stored as {{hrcard:<profileName>:<uid>}}.
profileName and uid are used to look the contact up precisely, so
two contacts that happen to share a display name (on the same server or
different ones) never get confused with each other - the display name
itself isn’t part of the stored syntax at all, only fetched at render time
from whatever the store currently has for that uid. The profile’s
user-assigned name is used rather than its internal id (a randomly
generated string, stable only for the lifetime of that settings entry) so
references keep resolving across a profile being deleted and re-added
under the same name. {{...}} isn’t Obsidian wikilink syntax, so unlike
a hypothetical [[...]] form it’s never intercepted by Obsidian’s own
link parser - Reading view and Live Preview both match it directly against
raw text. A hand-typed reference needs the exact CardDAV UID to resolve,
which isn’t practical outside of copying it from an existing reference -
in practice, always insert references via the {{hrcard: autocomplete.
No runtime dependencies#
Beyond what Obsidian itself provides (the obsidian package, CodeMirror
6), the plugin has no runtime dependencies — the CardDAV client and vCard
parser are both hand-written rather than pulled in from npm, keeping the
bundle small and avoiding exposure to a third-party HTTP/XML parsing
library’s own vulnerabilities.