Repository navigation
Feat(tui): frontmatter default mode definition - #28
Open
spirit-of-light wants to merge 14 commits into
Open
spirit-of-light wants to merge 14 commits into
spirit-of-light wants to merge 14 commits into
Conversation
zn tui is gaining a way to open a note in edit, split or preview from its own frontmatter. This adds the configuration behind it, without changing what any note does yet. `[terminal] view_mode_field` names the frontmatter field that sets a note's view mode; it is empty by default, which keeps the feature off. `[terminal.view_mode_rules]` maps `"field=value"` to a mode, so a field such as `page-status = done` can choose preview. Both live under `[terminal]`, which the desktop app ignores, so a save from the desktop cannot drop them. Rule keys and values are trimmed, unquoted and lower-cased, and rules are ordered by their lower-cased `field=value`, which is the order the first match will be taken in. A rule with a bad mode, no `=` or an empty field name is kept in the file and reported with its reason but never applied, and an invalid rule never stops the config from loading. SetViewModeRule and RemoveViewModeRule rewrite only that table; removing works on the key exactly as written, so a malformed rule can still be deleted, and removing the last rule drops the table. Tests, written red first, cover the defaults, loading the sub-table, invalid rules and field values, normalising and ordering, and writing and removing rules while keeping the rest of the file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
frontmatterPaneMode reads a note's frontmatter and says which pane mode it asks for, for the coming change that opens notes in that mode. Nothing calls it yet. A valid value in the configured field (`view-mode: preview`) wins. The field name and its value compare without regard to case; a list, an empty value or any word other than edit, split or preview gives no mode, and the rules are tried instead. A rule matches when its field holds its value, or any element of a list field does, compared case-insensitively after unquoting; rules are tried in the order they arrive in, so the first match wins. A note without frontmatter, or a vault with no field and no rules, gets no mode. Tests cover the field, every way it can fall through, rule matching with case, quotes and lists, rule order, and a field that beats a matching rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With `[terminal] view_mode_field` set or a valid rule in `[terminal.view_mode_rules]`, a note opened in a new tab now takes the pane mode its frontmatter asks for, both from openNote and from quiet opens such as the jump list and pickers. A note's body is often read in the background after its tab appears, so the tab opens in the mode it would have had anyway and is marked as waiting; once the text is loaded the frontmatter sets the mode, and the tab visibly switches on a slow read rather than holding up the open. A manual mode switch before the load clears the mark, so the user's choice is never overridden. The frontmatter mode beats the remembered per-note mode and the keep-across mode, but only writes the tab: neither of those is changed by it, so one finished note does not make every later note open in preview. A tab that is already open keeps its mode when the note is saved with new frontmatter; the new value applies the next time the note is opened. With the field empty and no valid rules, which is the default, no tab is marked and opening a note behaves exactly as before. Tests cover the default config, the field and a rule on open, both beating the remembered and keep-across modes, a deferred read, a manual switch before and after the load, saving and reopening, quiet opens, and that applying a mode leaves the remembered modes alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A tab opened in the mode its frontmatter asks for has three neighbours that must not lose or override that choice. Splitting a tab before its note has loaded now gives the copy the same waiting state, so both tabs take the frontmatter mode once the text is in. A copy of a tab whose mode was switched by hand keeps that mode, as before. A tab restored from the last session keeps the mode it was saved with; the frontmatter does not change it. A note pinned as a reference with `:ref` always stays in preview, whatever its frontmatter asks for. Tests cover a restored tab, splits before and after the load, a split of a manually switched tab, and a reference whose frontmatter asks for edit or split. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntmatter With a view-mode field or rules configured, a note created from a template that says `view-mode: preview`, or whose first fields match a preview rule, would otherwise open straight into preview. A note that has just been made is about to be written, so it should never start there. Every action that creates a note and opens it now marks the note first: a new note, a note from a template, a periodic note created on first open, a new task file, and today's daily note when a rollover creates it. The open that follows uses the mark up, starts in the editor and does not wait for the frontmatter. Notes created without being opened, such as a daily note made by adding a task to it or a saved capture, carry no mark; the first time they are opened they follow their frontmatter like any other note. With the feature off, a new note opens in the mode it does today. Tests cover each of the five creation paths, an existing periodic note, notes created without opening, the feature off, and reopening a new note after closing it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The view-mode rules live in `[terminal.view_mode_rules]`, a table inside `[terminal]` that `zn config` could not reach: it splits a key at the first dot and only knew the four top-level map tables. `zn config list` now shows every rule as `terminal.view_mode_rules.<field>=<value> = <mode>`, invalid ones too, so a hand-edited mistake is visible; `list --json` carries the same entries. `zn config get` reads a rule by that name, and `zn config set` adds a new rule or changes an existing one. Set checks the rule before writing: a bad mode, a key without `=`, an empty field or an empty value exits with an error and leaves config.toml as it was. The mode is stored lower-cased. `terminal.view_mode_field` reads and sets like any other terminal key. Removing a rule stays with the settings view and `zn config edit`, since `zn config` has no unset. Tests cover listing, reading, adding and changing a rule, every kind of bad input, and setting the field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`:settings` now shows and edits the frontmatter view-mode configuration, so it never needs config.toml opened by hand. `terminal.view_mode_field` appears with the other terminal settings and is edited like them; while it is empty its prompt suggests `view-mode` as an example rather than filling it in. Right after the terminal rows come the rules, as `terminal.view_mode_rules.<field>=<value> = <mode>`: the valid ones in the order they are matched, then the invalid ones, each ending in its reason, such as `(invalid: no "=")`. A rule shows its key as written in the file. Last comes an "(add rule)" row, which stays visible whatever the filter. Enter on the add row asks for the field, then the value, then offers edit, split and preview; nothing is written until a mode is picked, Esc at any step writes nothing, and an empty field, a field holding "=", an empty value or a rule that already exists in another case is refused. Enter on a rule offers the three modes and remove; `d` on a rule row also removes it, and the footer names `d` while one is selected. A rule with a bad mode can be fixed by picking a mode; one whose key is malformed offers only remove. Rules are changed and removed by their key as written, so hand-typed keys are found. After every change the preferences are reloaded and the rows refreshed. settingRow keeps its four fields: a rule row's key is the rule's key, and whether a row is a setting, a rule, an invalid rule or the add row is worked out from the row itself. Tests cover editing the field, rule order, invalid rules marked and ignored when a note opens, filtering, adding with every kind of bad input and cancel, changing, removing with the picker and with `d`, the footer hint, fixing a bad mode, and malformed rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hand-written rule such as `"status=" = "edit"` was read as valid and listed in `:settings` as an ordinary rule, yet choosing a new mode for it there failed, because writing a rule already refused an empty value. The rule looked usable but could only be removed. An empty value, including an empty quoted one, now makes a rule invalid when it is read, the same as a missing `=` or an empty field. Such a rule never matches a note; `:settings` and `zn config list` show it with the reason "empty value", and its picker offers only remove. The check that runs before a rule is written is now the same parse, so reading and writing agree on what a valid rule is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The README gains a section on opening notes in a pane mode from their frontmatter: `[terminal] view_mode_field`, off by default with `view-mode` suggested, the `[terminal.view_mode_rules]` format with a `page-status=done` example, what makes a rule invalid and how to remove one, the order in which the field, the rules, the remembered and keep-across modes, restored tabs, new notes and `:ref` win, and how to edit both in `:settings` or with `zn config`. It says only zn tui reads them. The settings table lists both under `[terminal]`. zn tui's help adds a row for the field and the rules next to the pane mode keys, and says a manual mode switch always wins over frontmatter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two end-to-end tests guard the feature as a whole. One opens notes in frontmatter modes, lets a slow read land, switches, splits and closes tabs, and adds and removes a rule from `:settings`, then checks that every file in the vault, `.zennotes` included, is byte-for-byte as it was: the configuration lives only in zn's own config.toml. The other adds a rule in `:settings`, opens a matching note in that mode, changes the rule's mode, and reopens the note in the new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three cases were covered only below the open path or not at all. A note whose frontmatter matches nothing (a bad field value, a list in the field, or no matching rule) now has a test that it opens exactly as it would with the feature off, against a preview default, a remembered mode and the keep-across mode, before and after a slow read. Invalid rules are shown to be ignored when a note opens while a valid rule beside them still applies, and an empty field is shown to leave the rules working after a slow read. A last test keeps the README's description of the settings from going missing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…frontmatter Two ways of opening a note set the tab's mode themselves right after opening it, and with frontmatter view modes on, a slow read of the note could then land and replace that mode. Peeking a note from the sidebar asks for preview, as `:ref` does; the frontmatter could then switch the peek to edit or split. Both now set their mode through one helper that also stops the frontmatter from taking it back. Reopening a closed tab put back the mode the tab was closed in, and the frontmatter replaced it only when the read happened to be slow. A reopened note is now treated like any note opened in a new tab: its frontmatter mode wins whether the read is quick or slow, and the closed tab's mode stands only when the frontmatter asks for none. Tests cover a peek of a note whose frontmatter asks for edit or split, and reopening a closed tab with a quick and a slow read, with and without a frontmatter mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two hand-edited or command-line cases slipped past the rule handling. `zn config set terminal.view_mode_rules.Page-Status=done split` added a second entry next to an existing `page-status=done`, although both are the same rule: the new one then won the tie, and `:settings` listed two rows for one rule. Writing a rule now looks for an entry with the same field and value, compared the way rules are matched, in any case or quoting and whether or not its mode is valid, and changes that entry under the key it already has. A rule such as `"a=b" = 1` or `= true` was dropped while loading, so it was ignored but also missing from `:settings` and `zn config list`, where invalid rules are shown with a reason and can be removed. Such a mode is now kept as text, so the rule shows up as invalid with "bad mode" and can be removed like any other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tab alone When the note behind a closed tab had been deleted, reopening it opened nothing, but the closed tab's mode was still written to whichever tab was active, and with frontmatter view modes on that tab was then given its own frontmatter mode, replacing one the user had picked. The reopened mode is now applied only to the reopened note's tab. The README's list of what wins now says that a reopened tab follows its frontmatter, and that a sidebar peek stays in preview like `:ref`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A note can now choose the pane mode it opens in from its own frontmatter. The main use is a writing workflow: a page carries
page-status: in-progresswhile it is being written, and once it is set todoneit opens in preview, since it no longer needs editing.Two
[terminal]settings drive it, and both are off by default, so nothing changes for anyone who does not set them:view_mode_fieldnames a frontmatter field (view-modeis the suggested name). A note withview-mode: preview,editorsplitopens in that mode. A list, an empty value or any other word is ignored.[terminal.view_mode_rules]maps a"field=value"pair to a mode, for example"page-status=done" = "preview". Keys and values compare case-insensitively after unquoting, and a list field matches when any element does. When several rules match, the first in sorted order wins, and a valid field value beats every rule.Both live under
[terminal], which the desktop app ignores. zn never writes them into the vault, and the desktop is unaffected.What wins when they disagree:
keep_view_mode_across_notes. Applying it never changes either of the last two; only a mode you switch to yourself does.:refand a sidebar peek stay in preview. A split copy follows the source tab, keeping a mode switched by hand. Reopening a closed tab follows the frontmatter, and the closed tab's mode comes back only when the frontmatter asks for none.:settingsshows the field and the rules. An "(add rule)" row asks for the field, the value and the mode in turn. Enter on a rule changes its mode or removes it, anddremoves it too. Invalid rules (no=, an empty field or value, or a bad mode) are never applied, never stop the config loading, and are listed with their reason so they can be fixed or removed.zn config list,getandsetwork onterminal.view_mode_rules.<field>=<value>, and setting a rule that differs only in case changes the existing one.This is independent of #26, which opens new notes in the editor whatever the default mode. Neither depends on the other, and both orders merge cleanly. With #26 and the open settings-page grouping branch merged on top, the build and the full test suite pass.
Tests cover the resolver, opening with quick and slow reads, the precedence above, every note-creation path, split copies, session restore,
:ref, the sidebar peek, reopening, the settings rows and editing,zn config, invalid and hand-edited rules, a check that nothing is written to the vault, and the README and help text. Each commit builds and passes the touched packages' tests on its own.go test -race ./...,go vet ./...,go build ./cmd/zn,git diff --checkandbash scripts/sandbox.sh --smokepass.Checked by hand in the TUI on a sandbox vault. With the default config, notes carrying
page-status: doneorview-mode: splitopen in the editor as before. After setting the field and adding thepage-status=donerule through:settings, the done page opens in preview, theview-mode: splitpage in split, and an in-progress page in the editor.🤖 Generated with Claude Code