PhoenixKitKnowledgeBase.Web.VaultLive (PhoenixKitKnowledgeBase v0.1.0)

Copy Markdown View Source

The vault workspace — one LiveView serving three routes, so moving between notes patches the note pane instead of remounting the sidebar:

  • :index — the vault with no note open
  • :show — a note open (read or edit)
  • :new — the "this note doesn't exist yet" page an unresolved [[wiki-link]] points at

Editing

Obsidian's model, and only that: clicking a note puts you in the editor, the editor is already a rendered view, and there is no second "reading" surface to switch to. That editor is Leaf — the same one PhoenixKit uses elsewhere — in hybrid mode, its Obsidian-style live preview, where formatting renders inline while the source markers stay editable. Someone with read-only access gets the same surface, not a different one.

[[wiki-links]] render as link tokens in the editor itself. Leaf decorates them and asks which targets exist — only this module knows that — and the answer goes back through the same resolver and same-folder tie-break that built the stored link index, so a link never renders as pointing somewhere the backlinks panel disagrees with. An unresolved target is styled distinctly and points at the create page.

Leaf never navigates on a click; it reports one and leaves the destination to us. The right-hand panel still lists outgoing links, which is the path for a reader who wants the whole set rather than the one under the cursor.

Visual and HTML modes are denied. Both round-trip the document through HTML, and a note's YAML front matter is ----delimited — which survives that trip as a horizontal rule and a paragraph, silently destroying every property on the note. Hybrid and markdown modes edit the source directly and are safe.

Edits autosave: Leaf debounces, the save writes the file and reindexes it, and the status pill reports where that got to.

[[ and # autocomplete

Leaf's inline-suggestion triggers do the real thing — a caret-anchored popup, arrow keys to move, Enter or Tab to accept, Escape to dismiss, and it works mid-paragraph because the client knows where the caret is.

[[ completes note titles and # completes tags. Both allow creating what does not exist yet, so you can link ahead of yourself exactly as in Obsidian. A suggestion whose title several notes share is inserted path-qualified, so the link says which note it meant rather than leaving it to the tie-break.

Summary

Functions

The Leaf component id for a note.

Whether a {:leaf_changed, …} payload should be written to disk.

Functions

editor_id(_)

@spec editor_id(map() | nil) :: String.t()

The Leaf component id for a note.

Keyed to the note, and both halves of that matter.

Leaf's editing surfaces are phx-update="ignore", so LiveView never patches them after mount and a changed content assign does nothing to the DOM. Under a constant id, patching from note A to note B reused the same component and the same hook, and the editor went on showing A's text while the server believed it was showing B's — every note opened with whatever was loaded first.

Keying the id to the note makes LiveView tear the old component down and mount a fresh one, and it makes a stale message identifiable: Leaf debounces, so a :leaf_changed for the note you just left can arrive after you have switched, and saving it would write A's text into B's file.

save_decision(payload, note, current_content)

@spec save_decision(map(), map() | nil, String.t()) :: :save | :stale | :unchanged

Whether a {:leaf_changed, …} payload should be written to disk.

Pure, and public, because it is the whole of the fix for two bugs that were invisible from the server's point of view — a stale editor writing into the wrong note, and an untouched note being rewritten just for being opened.