vault

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package vault is the filesystem engine behind every zn command and the terminal UI: it reads and writes a ZenNotes vault exactly the way the desktop app, the bundled `zn`, and the self-hosted server do.

SYNCED SEMANTICS. The parsers in this package (tags, wikilinks, excerpts, task lines, frontmatter, system folder paths) are deliberate copies of the ones in the ZenNotes repo: `apps/desktop/src/mcp/vault-ops.ts`, `packages/shared-domain/src/*.ts` and `apps/server/internal/vault/*.go`. A note must read the same everywhere, so a change to one of those has to land here too, byte for byte where the rules are byte rules.

Index

Constants

View Source
const (
	NoteCommentsDir            = "comments"
	NoteCommentsSuffix         = ".comments.json"
	MaxCommentAnchorTextLength = 500
	MaxCommentAuthorLength     = 80
)
View Source
const (
	AssetsDir             = "assets"
	PrimaryAttachmentsDir = "attachements" // intentional legacy spelling, never "fix" it
	InternalVaultDir      = ".zennotes"

	DefaultDailyNotesDirectory     = "Daily Notes"
	DefaultDailyNoteTitlePattern   = "yyyy-MM-dd"
	DefaultWeeklyNotesDirectory    = "Weekly Notes"
	DefaultWeeklyNoteTitlePattern  = "yyyy-'W'ww"
	DefaultMonthlyNotesDirectory   = "Monthly Notes"
	DefaultMonthlyNoteTitlePattern = "yyyy-MM"
	DefaultDateNoteLocale          = "system"
	DefaultTypstPreambleFolder     = "typst"
)
View Source
const DeepLinkScheme = "zennotes"

DeepLinkScheme is the URL scheme the desktop app registers.

View Source
const TaskFileTag = "task"

TaskFileTag is the frontmatter tag that marks a whole note as a task.

Variables

AllFolders lists the buckets in the order every listing walks them.

View Source
var ErrAnchorNotFound = errors.New("anchor_text was not found in the note. Pass the text exactly as it appears (read_note shows it), or omit it for a note-level comment.")

ErrAnchorNotFound says anchor text is not in the note.

View Source
var ErrInvalidTemplate = errors.New("invalid template request")

ErrInvalidTemplate marks a template path outside the templates directory.

View Source
var ErrPathEscape = errors.New("path escapes vault")

ErrPathEscape is returned for a vault-relative path that resolves outside the vault root, through `..` or a symbolic link.

View Source
var ErrTaskGone = errors.New("task no longer exists at that location")

ErrTaskGone says a task id no longer names a task in its note.

LiveFolders are the buckets searches and task scans cover: everything but the trash.

Functions

func AnchorForText

func AnchorForText(doc, anchorText string) (start, end int, text string, err error)

AnchorForText places a new comment: an exact match of anchorText first, then one ignoring case; empty text means a note-level comment at the top.

func AppendToBody

func AppendToBody(body, text string) string

AppendToBody is the body with text added after a blank line.

func BodyHasLocalAsset

func BodyHasLocalAsset(body string) bool

BodyHasLocalAsset reports whether the note embeds or links a local attachment (an image, a PDF, audio or video).

func BucketTasksByDueDate

func BucketTasksByDueDate(tasks []Task) map[string][]Task

BucketTasksByDueDate groups open tasks by due date; undated ones land under "unscheduled". An undated forwarded record is a record of a move, not unscheduled work, and is skipped.

func BuildExcerpt

func BuildExcerpt(body string) string

BuildExcerpt makes a short plaintext preview from markdown: no frontmatter, no code, links reduced to their text, markup stripped, whitespace collapsed, capped at 220 characters.

func BuildOpenNoteDeepLink(relPath string) string

BuildOpenNoteDeepLink is the shareable URL that opens a vault-relative note in the app. Segments are percent-encoded the way encodeURIComponent does it, with parentheses encoded as well because these URLs live inside markdown `[title](url)` links, where a bare `)` ends the link.

func ComposeTaskFile

func ComposeTaskFile(in ComposeTaskFileInput) string

ComposeTaskFile builds a task-file note in the TaskNotes shape.

func ExtractOpenTaskBlocks

func ExtractOpenTaskBlocks(markdown string) (moved []string, rest string)

ExtractOpenTaskBlocks pulls every open task line (`[ ]` and `[/]`) with its indented children out of markdown, for rolling unfinished work forward into today's daily note.

func ExtractTags

func ExtractTags(body string) []string

ExtractTags returns the unique tags of a note: frontmatter `tags` (a bare scalar splits on commas and whitespace) plus inline `#tags` outside code.

func ExtractWikilinks(body string) []string

ExtractWikilinks returns unique [[wikilink]] targets outside code, embeds included, exactly as the desktop CLI reports them.

func FolderSubpathOf

func FolderSubpathOf(rel string, folder NoteFolder, primaryAtRoot bool, paths map[string]string) string

FolderSubpathOf is the note's directory relative to its bucket root, or "" at the bucket root. `inbox/Work/Note.md` gives `Work`; in root primary mode `Work/Note.md` gives `Work`.

func ForwardTaskLines

func ForwardTaskLines(markdown string, taskIndex int, linkToken string) (body string, moved []string)

ForwardTaskLines is ForwardTaskSubtree plus what the destination note receives: the task itself as a copy with its indent dropped and no link, then its subtree. A task without subtasks still yields its own line, so forwarding never leaves a `[>]` record with nothing on the other side.

func ForwardTaskSubtree

func ForwardTaskSubtree(markdown string, taskIndex int, linkToken string) (body string, childLines []string)

ForwardTaskSubtree flips the task at taskIndex to `[>]`, appends linkToken to it, and flips its open subtasks to `[>]` too. childLines is the subtree as it read before the flip, re-based to the parent's indent, so the caller can place it under a copy in the destination note.

func Frontmatter

func Frontmatter(body string) (block string, rest string, ok bool)

Frontmatter splits a leading `---` block off the body. ok is false when there is none; block is the text between the fences.

func InsertAtLineInBody

func InsertAtLineInBody(body string, lineNumber int, text string) string

InsertAtLineInBody inserts text before the zero-based lineNumber, clamped to the body.

func InsertTasksUnderTasksHeading

func InsertTasksUnderTasksHeading(body string, taskLines []string) string

InsertTasksUnderTasksHeading places task lines at the end of a `# Tasks` section when the note has one (before the next heading of the same or a higher level, or a horizontal rule), else appends them to the note.

func IsAtomicWriteTempPath

func IsAtomicWriteTempPath(p string) bool

IsAtomicWriteTempPath reports whether p is one of those scratch files.

func IsObsidianExcalidrawMarkdown

func IsObsidianExcalidrawMarkdown(content string) bool

IsObsidianExcalidrawMarkdown is true when the frontmatter carries Obsidian's `excalidraw-plugin` marker.

func IsObsidianExcalidrawPath

func IsObsidianExcalidrawPath(p string) bool

IsObsidianExcalidrawPath is true for `*.excalidraw.md` drawings.

func IsOverdue

func IsOverdue(t Task, today time.Time) bool

IsOverdue is true for an open, non-waiting task whose due date has passed.

func IsPathExcludedFromTasks

func IsPathExcludedFromTasks(relPath string, excluded []string) bool

IsPathExcludedFromTasks reports whether a vault-relative POSIX path lives inside any excluded folder (segment-prefix match, case-sensitive).

func IsTaskOpen

func IsTaskOpen(t Task) bool

IsTaskOpen is true for a task that is neither done nor cancelled.

func IsTypstPreamblePath

func IsTypstPreamblePath(relPath, folder string) bool

IsTypstPreamblePath reports whether a note sits in a directory named like the preamble folder, at any depth. Such notes hold Typst source and are left out of the tag index.

func IsValidFolder

func IsValidFolder(f NoteFolder) bool

IsValidFolder reports whether f names one of the four buckets.

func IsValidISODate

func IsValidISODate(s string) bool

IsValidISODate accepts `YYYY-MM-DD` that names a real day.

func LineOfOffset

func LineOfOffset(doc string, units int) int

LineOfOffset is the 1-based line of a UTF-16 offset in doc.

func MoveTaskLine

func MoveTaskLine(markdown string, fromTaskIndex, targetTaskIndex int, before bool) string

MoveTaskLine relocates the task line at fromTaskIndex before or after the task line at targetTaskIndex.

func NewCommentID

func NewCommentID() string

NewCommentID is a random UUID-shaped id.

func NormalizeCommentAuthor

func NormalizeCommentAuthor(value string) string

NormalizeCommentAuthor squeezes whitespace and caps the length.

func NormalizePriority

func NormalizePriority(raw string) string

NormalizePriority maps every accepted spelling onto high, med or low.

func NormalizeRelPath

func NormalizeRelPath(rel string) string

NormalizeRelPath turns `./inbox/Note.md` or `\inbox\Note.md` into `inbox/Note.md`, the form every listing reports.

func NormalizeSystemFolderPaths

func NormalizeSystemFolderPaths(raw map[string]string) map[string]string

NormalizeSystemFolderPaths validates a raw systemFolderPaths map the way every other runtime does: single directory names, no reserved names, no folder claiming another folder's default name, no two folders resolving to the same directory. Nil when nothing survives.

func NormalizeTasksExcludedFolders

func NormalizeTasksExcludedFolders(values []string) []string

NormalizeTasksExcludedFolders drops invalid entries and duplicates.

func NoteTasksMode

func NoteTasksMode(val string) string

NoteTasksMode reads the frontmatter `tasks:` value.

func ParseFrontmatterFields

func ParseFrontmatterFields(block string) map[string][]string

ParseFrontmatterFields parses a frontmatter block into flat fields: scalars, inline arrays (`tags: [a, b]`) and block lists (`tags:` then indented `- a`). Keys are lower-cased; every value is a slice (a scalar is a single-element slice). Best-effort, never fails.

func ParseFrontmatterScalars

func ParseFrontmatterScalars(raw string) (data map[string]string, body string)

ParseFrontmatterScalars is the template/record-page reader: flat `key: value` lines only, quotes stripped, keys kept as written.

func ParseTaskIndex

func ParseTaskIndex(taskID, indexStr string) (int, error)

ParseTaskIndex validates the `#<n>` half of a task id.

func PrependToBody

func PrependToBody(body, text string) string

PrependToBody is the body with text inserted at the top, below any frontmatter.

func RemoveTaskLine

func RemoveTaskLine(markdown string, taskIndex int) (line string, body string, ok bool)

RemoveTaskLine deletes the task line at taskIndex, returning it too.

func ReplaceInBody

func ReplaceInBody(body, find, replace string, all bool) (string, int, error)

ReplaceInBody is a literal find-and-replace; all replaces every occurrence, otherwise only the first.

func ResolveCommentAnchor

func ResolveCommentAnchor(c NoteComment, doc string) (from, to int)

ResolveCommentAnchor is where a comment's anchor sits in doc now: the stored offsets when the text there still matches, else the first occurrence of the anchored text, else the clamped stored offsets. The result is in UTF-16 units like the stored anchor.

func ResolveFolderPath

func ResolveFolderPath(folder NoteFolder, paths map[string]string) string

ResolveFolderPath is the on-disk directory name of a system folder.

func RetitleLeadingHeading

func RetitleLeadingHeading(body, title string) string

RetitleLeadingHeading rewrites the note's leading `# Heading` to title, skipping frontmatter and blank lines. A note whose first line is anything but an H1 is returned unchanged: the heading is never invented.

func RewriteWikilinksForRename

func RewriteWikilinksForRename(body string, notes []NoteMeta, oldPath, newTitle string) (string, int)

RewriteWikilinksForRename rewrites every `[[target]]` / `![[target]]` in body whose target resolves to the note at oldPath, pointing it at newTitle. Aliases, anchors and embeds are preserved; code is skipped. notes must reflect the vault BEFORE the rename.

func SafeJoin

func SafeJoin(root, rel string) (string, error)

SafeJoin cleans a user-supplied relative POSIX path and joins it onto root, refusing anything that resolves outside root. Existing symlinked components must still resolve inside root; components that do not exist yet are left alone (they cannot be links until they are created).

func SafeTemplateSlug

func SafeTemplateSlug(slug string) string

SafeTemplateSlug keeps lowercase letters, digits and dashes; every run of anything else becomes one dash, and leading and trailing dashes go.

func SanitizeTitle

func SanitizeTitle(raw string) string

SanitizeTitle makes a title safe as a filename on every platform.

func SetTaskCancelled

func SetTaskCancelled(markdown string, taskIndex int, cancelled bool) string

SetTaskCancelled marks the line `[-]`, or back to `[ ]`.

func SetTaskChecked

func SetTaskChecked(markdown string, taskIndex int, checked bool) string

SetTaskChecked is the editor's toggle: unchecking an in-progress `[/]` keeps the `/`, since it already is not done.

func SetTaskDue

func SetTaskDue(markdown string, taskIndex int, due string) string

SetTaskDue replaces, inserts or removes the `due:YYYY-MM-DD` token.

func SetTaskField

func SetTaskField(markdown string, taskIndex int, key, value string) string

SetTaskField replaces, inserts or removes an inline `@key:value` token.

func SetTaskFileCancelled

func SetTaskFileCancelled(body string, cancelled bool) string

SetTaskFileCancelled writes `status: cancelled`, or back to `open`.

func SetTaskFileField

func SetTaskFileField(body, key, value string) string

SetTaskFileField sets any frontmatter scalar (`due`, `priority` in TaskNotes vocabulary, `status`); an empty value removes the key.

func SetTaskFileInProgress

func SetTaskFileInProgress(body string, inProgress bool) string

SetTaskFileInProgress writes `status: in-progress`, or back to `open`.

func SetTaskFileStatus

func SetTaskFileStatus(body string, done bool, now time.Time) string

SetTaskFileStatus writes `status: done` plus `completedDate` when checking, and back to `open` (clearing `completedDate`) when unchecking.

func SetTaskInProgress

func SetTaskInProgress(markdown string, taskIndex int, inProgress bool) string

SetTaskInProgress marks the line `[/]`, or back to `[ ]`.

func SetTaskPriority

func SetTaskPriority(markdown string, taskIndex int, priority string) string

SetTaskPriority replaces, inserts or removes the `!priority` token.

func SetTaskText

func SetTaskText(markdown string, taskIndex int, text string) string

SetTaskText replaces everything after the checkbox.

func SetTaskWaiting

func SetTaskWaiting(markdown string, taskIndex int, waiting bool) string

SetTaskWaiting adds or removes the `@waiting` marker.

func SplitTaskID

func SplitTaskID(taskID string) (rel string, indexStr string, err error)

SplitTaskID splits `<path>#<index>` into its halves; indexStr is the literal `task` for a whole-note task.

func SplitWikilinkContent

func SplitWikilinkContent(content string) (target, anchor, alias string)

SplitWikilinkContent separates `Target#anchor|alias` into its parts; the anchor keeps its leading `#` or `^` and the alias its leading `|`.

func StrPtr

func StrPtr(s string) *string

StrPtr is a convenience for FrontmatterUpdate values.

func StripCodeContent

func StripCodeContent(body string) string

StripCodeContent blanks fenced and inline code so the tag, link and excerpt scanners never read code as content. Line-based and indentation-tolerant: a fence nested under a list item is still a code block.

func TaskFilePriorityValue

func TaskFilePriorityValue(priority string) string

TaskFilePriorityValue maps a ZenNotes priority onto TaskNotes' vocabulary.

func ToPosix

func ToPosix(p string) string

ToPosix converts an OS path to forward slashes.

func TodayISO

func TodayISO(now time.Time) string

TodayISO is the local calendar date as `YYYY-MM-DD`.

func ToggleFileTaskInBody

func ToggleFileTaskInBody(body string, currentlyChecked bool, now time.Time) string

ToggleFileTaskInBody flips a task file's frontmatter `status` (and its `completedDate`).

func ToggleTaskInBody

func ToggleTaskInBody(body string, targetIndex int, dialect TaskDialect) (next string, ok bool)

ToggleTaskInBody flips the nth checkbox the way `zn task toggle` and the MCP tool do: open and done flip, an in-progress `[/]` checks off, and the forwarded and cancelled record markers are left alone. ok is false when the body holds no task at that index.

func UTF16Length

func UTF16Length(s string) int

UTF16Length counts the UTF-16 code units of s.

func Unquote

func Unquote(v string) string

Unquote strips one layer of matching quotes and surrounding whitespace.

func UpdateFrontmatterFields

func UpdateFrontmatterFields(body string, updates []FrontmatterUpdate) string

UpdateFrontmatterFields sets (or, with a nil value, removes) scalar frontmatter fields, preserving key order and every other line. Creates the block when the note has none. Keys match case-insensitively but are written with the casing given.

func YAMLValue

func YAMLValue(value string) string

YAMLValue quotes a scalar for a `key: value` frontmatter line whenever a YAML reader could misread it bare.

Types

type AssetMeta

type AssetMeta struct {
	Path      string `json:"path"`
	Name      string `json:"name"`
	Size      int64  `json:"size"`
	UpdatedAt int64  `json:"updatedAt"`
}

AssetMeta describes a file under the vault's attachment directories.

type ComposeTaskFileInput

type ComposeTaskFileInput struct {
	Title       string
	Status      string
	Priority    string
	Due         string
	Scheduled   string
	Tags        []string
	DateCreated string
	Body        string
}

ComposeTaskFileInput describes a new whole-note task.

type CustomTemplateFile

type CustomTemplateFile struct {
	SourcePath string `json:"sourcePath"`
	Raw        string `json:"raw"`
}

CustomTemplateFile is a raw custom template as it lives on disk.

type DateNotePattern

type DateNotePattern struct {
	Directory    string `json:"directory"`
	TitlePattern string `json:"titlePattern,omitempty"`
	Locale       string `json:"locale,omitempty"`
}

DateNotePattern is one (directory, title pattern, locale) triple a periodic-note kind has used; the current one plus any legacy ones.

type FolderEntry

type FolderEntry struct {
	Folder  NoteFolder `json:"folder"`
	Subpath string     `json:"subpath"`
}

FolderEntry is one subfolder under a bucket, as `zn folder list` reports it.

type FrontmatterUpdate

type FrontmatterUpdate struct {
	Key   string
	Value *string
}

FrontmatterUpdate is one field to set (Value non-nil) or remove (nil).

type NoteComment

type NoteComment struct {
	ID          string `json:"id"`
	NotePath    string `json:"notePath"`
	AnchorStart int    `json:"anchorStart"`
	AnchorEnd   int    `json:"anchorEnd"`
	AnchorText  string `json:"anchorText"`
	Body        string `json:"body"`
	CreatedAt   int64  `json:"createdAt"`
	UpdatedAt   int64  `json:"updatedAt"`
	ResolvedAt  *int64 `json:"resolvedAt"`
	Author      string `json:"author,omitempty"`
	ParentID    string `json:"parentId,omitempty"`
}

NoteComment is one stored comment or reply.

func NormalizeNoteComment

func NormalizeNoteComment(input NoteComment, notePath string, now int64) (NoteComment, bool)

NormalizeNoteComment validates one record; a comment with no body is dropped (ok false), everything else is coerced into shape.

func NormalizeNoteComments

func NormalizeNoteComments(raw []NoteComment, notePath string, now int64) []NoteComment

NormalizeNoteComments validates a whole list: drops malformed and duplicate records, keeps creation order, and turns a reply whose parent is missing into a comment of its own rather than losing it.

func ThreadRootOf

func ThreadRootOf(comments []NoteComment, id string) (NoteComment, bool)

ThreadRootOf is the top-level comment an id belongs to, or ok false.

type NoteCommentThread

type NoteCommentThread struct {
	Comment NoteComment
	Replies []NoteComment
}

NoteCommentThread is a top-level comment with its replies in order.

func ThreadNoteComments

func ThreadNoteComments(comments []NoteComment) []NoteCommentThread

ThreadNoteComments groups comments into threads. A reply to a reply lands in the same thread, so a thread stays one level deep.

type NoteContent

type NoteContent struct {
	NoteMeta
	Body string `json:"body"`
}

NoteContent is a note with its body.

type NoteFolder

type NoteFolder string

NoteFolder is one of the four conceptual top-level buckets. Their on-disk directory names can be remapped per vault (`systemFolderPaths` in vault.json), so a NoteFolder is never joined onto a path directly; see (*Vault).folderRoot.

const (
	FolderInbox   NoteFolder = "inbox"
	FolderQuick   NoteFolder = "quick"
	FolderArchive NoteFolder = "archive"
	FolderTrash   NoteFolder = "trash"
)

func FolderForRelativePath

func FolderForRelativePath(rel string, paths map[string]string) (NoteFolder, bool)

FolderForRelativePath classifies a vault-relative path by its first segment. Root-level files belong to inbox (the root primary layout); hidden names and dotfiles are not notes.

func SystemFolderForDirName

func SystemFolderForDirName(name string, paths map[string]string) (NoteFolder, bool)

SystemFolderForDirName classifies a top-level directory name. Only the RESOLVED name of each folder counts, case-insensitively: with inbox remapped to `01 - Entry`, a directory literally named `inbox/` is an ordinary user folder.

type NoteMeta

type NoteMeta struct {
	// Path is vault-relative and always POSIX-separated.
	Path string `json:"path"`
	// Link is the `zennotes://open?path=…` URL that focuses the app on the
	// note. Presented to humans; tools are handed Path.
	Link      string     `json:"link"`
	Title     string     `json:"title"`
	Folder    NoteFolder `json:"folder"`
	CreatedAt int64      `json:"createdAt"`
	UpdatedAt int64      `json:"updatedAt"`
	Size      int64      `json:"size"`
	Tags      []string   `json:"tags"`
	Wikilinks []string   `json:"wikilinks"`
	Excerpt   string     `json:"excerpt"`
}

NoteMeta is what `zn list --json` prints for a note. Field names and order match the desktop CLI so scripts reading its JSON keep working.

func BacklinksIn

func BacklinksIn(notes []NoteMeta, relPath string) []NoteMeta

BacklinksIn lists which of notes wikilink to the note at relPath, matching on title and never counting the note itself. Pure, so the remote backend gets the same answer from a listing it fetched.

func ResolveWikilink(notes []NoteMeta, target string) (NoteMeta, bool)

ResolveWikilink finds the note a `[[target]]` points at, or false.

type OutlineItem

type OutlineItem struct {
	Level int
	Text  string
	// Line is 1-based.
	Line int
}

OutlineItem is one heading of a note.

func Outline

func Outline(body string) []OutlineItem

Outline lists the ATX and setext headings of a note, skipping a leading frontmatter block and anything inside fenced code. Lines are 1-based.

type ParseTasksOptions

type ParseTasksOptions struct {
	// IncludeExcluded scans past the vault's excluded-folders list and the
	// note-level frontmatter `tasks:` opt-out. The `--include-excluded`
	// escape hatch; listings never set it on their own.
	IncludeExcluded bool
	Dialect         TaskDialect
}

ParseTasksOptions controls scanning past the exclusions and the grammar.

type PeriodicNotes

type PeriodicNotes struct {
	Enabled        bool
	Directory      string
	TitlePattern   string
	Locale         string
	LegacyPatterns []DateNotePattern
	TemplateID     string
	// Daily only.
	TasksDueOnNoteDate      bool
	RolloverUnfinishedTasks bool
}

PeriodicNotes are the daily, weekly or monthly note settings.

type PrimaryNotesLocation

type PrimaryNotesLocation string

PrimaryNotesLocation says where the inbox bucket lives: under `inbox/` (the classic layout) or directly at the vault root (Obsidian-style).

const (
	PrimaryNotesInbox PrimaryNotesLocation = "inbox"
	PrimaryNotesRoot  PrimaryNotesLocation = "root"
)

type Task

type Task struct {
	// ID is `<path>#<taskIndex>` for a checkbox line and `<path>#task` for a
	// task file. Stable across plain edits, so it is what toggles take.
	ID         string     `json:"id"`
	SourcePath string     `json:"sourcePath"`
	Link       string     `json:"link"`
	NoteTitle  string     `json:"noteTitle"`
	NoteFolder NoteFolder `json:"noteFolder"`
	// LineNumber is zero-based over the whole file, frontmatter included.
	LineNumber int    `json:"lineNumber"`
	TaskIndex  int    `json:"taskIndex"`
	RawText    string `json:"rawText"`
	Content    string `json:"content"`
	Checked    bool   `json:"checked"`
	// Cancelled is a `[-]` line: intentionally abandoned.
	Cancelled bool `json:"cancelled"`
	// InProgress is a `[/]` line: started, not finished. Still open work.
	InProgress bool `json:"inProgress"`
	// Forwarded is a `[>]` record: the task moved to another note.
	Forwarded bool   `json:"forwarded"`
	Due       string `json:"due,omitempty"`
	Priority  string `json:"priority,omitempty"`
	Waiting   bool   `json:"waiting"`
	// Fields holds inline `@key:value` tokens (lower-cased), the note's
	// frontmatter `status:` folded in. Any key can drive a Kanban board.
	Fields map[string]string `json:"fields,omitempty"`
	Status string            `json:"status,omitempty"`
	Tags   []string          `json:"tags"`
	// Kind is "file" for a whole-note task; empty means an inline checkbox.
	Kind          string `json:"kind,omitempty"`
	Scheduled     string `json:"scheduled,omitempty"`
	CompletedDate string `json:"completedDate,omitempty"`
	// DueInferred marks a due date derived from the containing daily note
	// rather than written on the line. Set by the TUI, never by a scan.
	DueInferred bool `json:"dueInferred,omitempty"`
}

Task is a checkbox line or a whole-note task file, with its inline metadata parsed out. Mirrors the desktop's VaultTask.

func FilterTasksForDisplay

func FilterTasksForDisplay(tasks []Task, showArchived bool) []Task

FilterTasksForDisplay drops tasks from archived notes unless showArchived.

func InferDailyTaskDueDates

func InferDailyTaskDueDates(tasks []Task, dueByPath map[string]string) []Task

InferDailyTaskDueDates gives undated tasks living in a daily note an implicit due date equal to that note's date. Forwarded records are exempt.

func ParseTasks

func ParseTasks(path, title string, folder NoteFolder, body string, opts ParseTasksOptions) []Task

ParseTasks walks a markdown body and returns every task: the note's own task file first (when it is one), then each checkbox line outside fenced code, with its inline metadata parsed out. `tasks: false` in the frontmatter emits nothing, `tasks: note` keeps only the file task, unless opts asks to scan past the opt-out.

func (Task) MarshalJSON

func (t Task) MarshalJSON() ([]byte, error)

MarshalJSON keeps the desktop CLI's exact key set: a checkbox task carries `forwarded` (true or false), a task file never does.

type TaskDialect

type TaskDialect int

TaskDialect picks which task-line grammar a scan uses. The desktop app and its CLI disagree on the edges (numbered-list and blockquoted checkboxes exist for the app only, and the CLI leaves `@key:value` fields in the content), and a task id is only stable within one grammar, so each surface keeps the grammar it always had.

const (
	// DialectApp is the grammar of the app's Tasks views, which the terminal
	// UI shares so a toggle there edits the line the app would.
	DialectApp TaskDialect = iota
	// DialectCLI is the grammar of the desktop's `zn task` commands and MCP
	// tools, so scripts keep getting the ids they got before.
	DialectCLI
)

type TaskGroups

type TaskGroups struct {
	Today        []Task
	Upcoming     []Task
	Waiting      []Task
	Done         []Task
	Forwarded    []Task
	Cancelled    []Task
	OverdueCount int
}

TaskGroups is the Tasks list's bucketing: Today (undated, due today, and overdue), Upcoming, Waiting, Done, Forwarded, Cancelled.

func GroupTasks

func GroupTasks(tasks []Task, today time.Time) TaskGroups

GroupTasks buckets tasks for the list view. Waiting overrides everything but Done; tasks without a due date land in Today.

type TextSearchMatch

type TextSearchMatch struct {
	Path       string     `json:"path"`
	Link       string     `json:"link"`
	Title      string     `json:"title"`
	Folder     NoteFolder `json:"folder"`
	LineNumber int        `json:"lineNumber"`
	LineText   string     `json:"lineText"`
}

TextSearchMatch is one matching line from a full-text search.

type Vault

type Vault struct {

	// SyncTitleHeading rewrites a note's leading `# Heading` on rename, the
	// desktop's "Sync title heading on rename" preference. On by default.
	SyncTitleHeading bool
	// contains filtered or unexported fields
}

Vault is one vault root on disk. Mutating operations serialize through a mutex; reads run concurrently.

func Open

func Open(root string) (*Vault, error)

Open binds a Vault to an existing directory.

func (*Vault) AbsPath

func (v *Vault) AbsPath(rel string) (string, error)

AbsPath resolves a vault-relative path, refusing escapes.

func (*Vault) AppendToNote

func (v *Vault) AppendToNote(rel, text string) (NoteMeta, error)

AppendToNote adds text to the end of a note after a blank line.

func (*Vault) ArchiveNote

func (v *Vault) ArchiveNote(rel string) (NoteMeta, error)

ArchiveNote moves a note into the archive.

func (v *Vault) Backlinks(rel string) ([]NoteMeta, error)

Backlinks lists every note that wikilinks to rel.

func (*Vault) CreateFolder

func (v *Vault) CreateFolder(folder NoteFolder, subpath string) error

CreateFolder creates a subfolder under a bucket.

func (*Vault) CreateNote

func (v *Vault) CreateNote(folder NoteFolder, title, subpath string, body *string) (NoteMeta, error)

CreateNote creates a note in a bucket (and subpath), picking a non-colliding filename. body defaults to `# <title>` plus a blank line.

func (*Vault) DeleteFolder

func (v *Vault) DeleteFolder(folder NoteFolder, subpath string) error

DeleteFolder removes a subfolder and everything inside it.

func (*Vault) DeleteNote

func (v *Vault) DeleteNote(rel string) error

DeleteNote removes a note permanently; a missing file is not an error.

func (*Vault) DeleteTemplate

func (v *Vault) DeleteTemplate(sourcePath string) error

DeleteTemplate removes a template; one already gone is a success.

func (*Vault) DuplicateNote

func (v *Vault) DuplicateNote(rel string) (NoteMeta, error)

DuplicateNote copies a note next to itself with a " copy" suffix.

func (*Vault) EmptyTrash

func (v *Vault) EmptyTrash() error

EmptyTrash permanently deletes everything in the trash bucket.

func (*Vault) FolderDirName

func (v *Vault) FolderDirName(folder NoteFolder) string

FolderDirName is a bucket's on-disk directory name; "" for the inbox in root primary mode.

func (*Vault) FolderOf

func (v *Vault) FolderOf(rel string) (NoteFolder, bool)

FolderOf classifies a vault-relative path.

func (*Vault) FolderRoot

func (v *Vault) FolderRoot(folder NoteFolder) string

FolderRoot is the absolute directory holding a bucket's notes.

func (*Vault) Info

func (v *Vault) Info() VaultInfo

Info names the vault the way a server reports it.

func (*Vault) InsertAtLine

func (v *Vault) InsertAtLine(rel string, lineNumber int, text string) (NoteMeta, error)

InsertAtLine inserts text before a zero-based line number.

func (*Vault) ListAssets

func (v *Vault) ListAssets() ([]AssetMeta, error)

ListAssets lists every file under the attachment directories, newest first.

func (*Vault) ListCSVFiles

func (v *Vault) ListCSVFiles() ([]string, error)

ListCSVFiles finds every database CSV in the vault at any depth: loose `.csv` files, and the data.csv of `.base` folders wherever they sit, including places the bucket walk does not cover. Hidden directories are skipped and `.base` folders are not descended into.

func (*Vault) ListDatabaseDirs

func (v *Vault) ListDatabaseDirs() ([]FolderEntry, error)

ListDatabaseDirs enumerates every `.base` database folder.

func (*Vault) ListFolders

func (v *Vault) ListFolders() ([]FolderEntry, error)

ListFolders enumerates every subfolder under each bucket, database folders excluded.

func (*Vault) ListNotes

func (v *Vault) ListNotes() ([]NoteMeta, error)

ListNotes returns metadata for every note in every bucket.

func (*Vault) ListTemplates

func (v *Vault) ListTemplates() ([]CustomTemplateFile, error)

ListTemplates returns every custom template with its raw bytes.

func (*Vault) MetaFor

func (v *Vault) MetaFor(rel string) (NoteMeta, error)

MetaFor re-reads one note's metadata.

func (*Vault) MoveNote

func (v *Vault) MoveNote(rel string, target NoteFolder, targetSubpath string) (NoteMeta, error)

MoveNote moves a note to a bucket and subpath.

func (*Vault) MoveToTrash

func (v *Vault) MoveToTrash(rel string) (NoteMeta, error)

MoveToTrash soft-deletes a note, keeping its subfolder for a later restore.

func (*Vault) PrependToNote

func (v *Vault) PrependToNote(rel, text string) (NoteMeta, error)

PrependToNote inserts text at the top, below any frontmatter.

func (*Vault) PrimaryAtRoot

func (v *Vault) PrimaryAtRoot() bool

PrimaryAtRoot is true in the Obsidian-style layout.

func (*Vault) PrimaryNotesLocation

func (v *Vault) PrimaryNotesLocation() PrimaryNotesLocation

PrimaryNotesLocation decides whether the inbox lives under `inbox/` or at the vault root. An explicit vault.json setting wins, as it does in the desktop app. Only an absent setting needs disk inference. Cached briefly: every operation consults it and a root directory scan is not free.

func (*Vault) ReadFileTextOrNull

func (v *Vault) ReadFileTextOrNull(rel string) (*string, error)

ReadFileTextOrNull is the raw text of any vault file, database internals included; absent is (nil, "", nil) and every other failure is an error.

func (*Vault) ReadNote

func (v *Vault) ReadNote(rel string) (NoteContent, error)

ReadNote returns a note with its body.

func (*Vault) ReadNoteComments

func (v *Vault) ReadNoteComments(rel string) ([]NoteComment, error)

ReadNoteComments loads a note's comments; a missing sidecar is empty.

func (*Vault) ReadTemplate

func (v *Vault) ReadTemplate(sourcePath string) (string, error)

ReadTemplate returns one template's raw bytes.

func (*Vault) RelDirFor

func (v *Vault) RelDirFor(folder NoteFolder, subpath string) string

RelDirFor is the vault-relative directory for a (folder, subpath).

func (*Vault) RenameFile

func (v *Vault) RenameFile(oldRel, newRel string) error

RenameFile moves one vault file to a new vault-relative path.

func (*Vault) RenameFolder

func (v *Vault) RenameFolder(folder NoteFolder, oldSubpath, newSubpath string) (string, error)

RenameFolder renames or moves a subfolder within its bucket and returns the new subpath.

func (*Vault) RenameNote

func (v *Vault) RenameNote(rel, nextTitle string) (NoteMeta, error)

RenameNote renames a note in place (same directory). The leading heading follows when SyncTitleHeading is on, and every `[[wikilink]]` in the vault that resolved to the note is rewritten to the new name.

func (*Vault) ReplaceInNote

func (v *Vault) ReplaceInNote(rel, find, replace string, all bool) (NoteMeta, int, error)

ReplaceInNote is a literal find-and-replace; no match means no write.

func (*Vault) RestoreFromTrash

func (v *Vault) RestoreFromTrash(rel string) (NoteMeta, error)

RestoreFromTrash moves a trashed note back to the inbox.

func (*Vault) Root

func (v *Vault) Root() string

Root is the absolute vault directory.

func (*Vault) ScanTasks

func (v *Vault) ScanTasks(opts ParseTasksOptions) ([]Task, error)

ScanTasks parses every task in every live note, honoring the vault's excluded folders and each note's `tasks:` opt-out unless asked not to.

func (*Vault) ScanTasksForPath

func (v *Vault) ScanTasksForPath(rel string, opts ParseTasksOptions) ([]Task, error)

ScanTasksForPath parses one note's tasks. A trashed or unclassifiable note contributes nothing, and neither does one under an excluded folder unless opts asks for it.

func (*Vault) SearchText

func (v *Vault) SearchText(query string, limit int) ([]TextSearchMatch, error)

SearchText is a case-insensitive substring search over every line of every live note, capped at limit matches.

func (*Vault) Settings

func (v *Vault) Settings() VaultSettings

Settings reads vault.json, reparsing only when the file changed. A missing or unreadable file yields the defaults: every read path must keep working for a vault that has never been opened in the app.

func (*Vault) SubpathOf

func (v *Vault) SubpathOf(rel string) string

SubpathOf is a note's directory relative to its bucket root.

func (*Vault) SwitchPrimaryMode

func (v *Vault) SwitchPrimaryMode(target PrimaryNotesLocation) ([]string, error)

SwitchPrimaryMode moves a vault between the classic layout, where notes live under the inbox directory, and root mode, where they sit at the vault root with the other system folders beside them. Files move and the explicit `primaryNotesLocation` setting is written to match. Favorites that name moved notes are rewritten. It refuses to run when a move would overwrite something.

func (*Vault) ToggleTask

func (v *Vault) ToggleTask(taskID string, dialect TaskDialect) (*Task, error)

ToggleTask flips the task named by a stable id, counted in the given grammar. Exclusion-blind on purpose: an explicit id is an explicit ask. Returns (nil, nil) when the id no longer matches a task.

func (*Vault) UnarchiveNote

func (v *Vault) UnarchiveNote(rel string) (NoteMeta, error)

UnarchiveNote moves an archived note back to the inbox.

func (*Vault) UpdateSettings

func (v *Vault) UpdateSettings(patch func(raw map[string]any)) error

UpdateSettings applies patch to the raw vault.json object and writes it back, preserving keys this process does not model. Used for favorites and the tasks exclusion list.

func (*Vault) WriteFileText

func (v *Vault) WriteFileText(rel, text string) error

WriteFileText writes any vault file's text, creating parents.

func (*Vault) WriteNote

func (v *Vault) WriteNote(rel, body string) (NoteMeta, error)

WriteNote replaces a note's body, creating parent directories.

func (*Vault) WriteNoteComments

func (v *Vault) WriteNoteComments(rel string, comments []NoteComment) ([]NoteComment, error)

WriteNoteComments replaces a note's comments; an empty list removes the sidecar.

func (*Vault) WriteTemplate

func (v *Vault) WriteTemplate(input WriteTemplateInput) (CustomTemplateFile, error)

WriteTemplate saves a template under a slug derived from the request and removes the file it replaces when an edit changed the slug.

type VaultInfo

type VaultInfo struct {
	Root string `json:"root"`
	Name string `json:"name"`
}

VaultInfo names the vault a server (or this process) is serving.

type VaultSettings

type VaultSettings struct {
	// ExplicitPrimary is the file's own primaryNotesLocation, empty when the
	// file says nothing. Only an absent value is inferred from the layout; see
	// (*Vault).PrimaryNotesLocation.
	ExplicitPrimary   PrimaryNotesLocation
	DailyNotes        PeriodicNotes
	WeeklyNotes       PeriodicNotes
	MonthlyNotes      PeriodicNotes
	Favorites         []string
	SystemFolderPaths map[string]string
	// TasksExcludedFolders are vault-relative directory paths whose notes
	// never feed the Tasks surfaces.
	TasksExcludedFolders []string
	TypstPreambleFolder  string
	// Raw is the decoded file, kept so a targeted write can preserve every
	// key this process does not model.
	Raw map[string]any
}

VaultSettings is the parsed, normalized `.zennotes/vault.json`.

func ParseSettings

func ParseSettings(raw map[string]any) VaultSettings

ParseSettings normalizes a raw vault.json object, for settings that arrive over the wire from a server rather than from disk.

type WriteTemplateInput

type WriteTemplateInput struct {
	Slug               string `json:"slug"`
	Raw                string `json:"raw"`
	PreviousSourcePath string `json:"previousSourcePath,omitempty"`
}

WriteTemplateInput saves a custom template under a slug.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL