docs: rewrite README + :help nuwiki for users
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 1m19s
CI / cargo test (push) Successful in 1m34s
CI / editor keymaps (push) Successful in 2m4s

Both files now describe what the plugin DOES, with no references to
internal phase work or SPEC.md (which is dev-only).

README.md
  * Reorganised the feature list by area (syntax, editor intelligence,
    editing, diary, other) and surfaced everything actually shipped:
    text objects, smart `<CR>` / `<Tab>` in insert, table alignment
    markers, colspan/rowspan, transclusion attributes, diary
    frequencies, list continuation, etc.
  * Updated the keymaps table to include the full insert-mode bindings,
    list-rewriter shortcuts (`gLh`/`gLl`, `glr`/`gLr`, `gl<Space>`),
    and all five text-object pairs (was "ah/ih + planned follow-ups").
  * Expanded the configuration example with the per-wiki keys (diary,
    HTML export, listsyms, nested_syntaxes) and the new Vim globals
    (`g:nuwiki_no_folding`, `g:nuwiki_mouse_mappings`).
  * Dropped the architecture / repository-layout / phase-log sections.
    Contributors who care about those still find them in the codebase.
  * Removed the "two known behavior deltas" note — the gln/glp claim
    is no longer accurate (both bindings ship as separate keymaps),
    and the lazy-index caveat moved into the Migrating section.

doc/nuwiki.txt
  * Grew from 7 sections to 14 covering installation, configuration
    (top-level + per-wiki + Vim globals), commands, keymaps (by area),
    text objects, insert-mode bindings, diary (with frequency formats),
    tables (with alignment / span syntax), HTML export, folding, health
    checks, and migrating from vimwiki.
  * Every public command has a help anchor.
  * Removed the §6.9 spec reference and the phase 9 caveat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 22:46:16 +00:00
parent ae96562969
commit 37af0803d8
2 changed files with 728 additions and 248 deletions
+191 -185
View File
@@ -5,33 +5,72 @@
nuwiki replaces the original [vimwiki](https://github.com/vimwiki/vimwiki)
plugin while keeping its file format, keymaps, and `:Vimwiki*` command
surface intact. The substantive work happens in a Rust LSP daemon — Vim
and Neovim are thin client layers that just wire up keystrokes and
display results.
and Neovim are thin client layers that wire up keystrokes and display
results.
- ✅ Full vimwiki syntax (`.wiki`) — headings, lists with checkboxes,
tables, math, code, tags, links (wiki / interwiki / diary / file /
raw URL / transclusion).
- ✅ Goto-definition, backlinks, hover, completion, workspace symbol
search, semantic highlighting — all over LSP.
- ✅ Diary (today/yesterday/tomorrow/next/prev), tags (search + link
generation), TOC generation, broken-link diagnostics, orphan finder.
- ✅ Multi-wiki with cross-wiki interwiki links (`[[wn.name:Page]]`).
- ✅ HTML export with templates, CSS, auto-export on save, RSS feed.
- ✅ Folding (LSP `foldingRange` + regex fallback), header & link
text objects, the `<CR>` two-step link-create-and-follow.
-`:Vimwiki*` command compat + `:Nuwiki*` canonical aliases.
## Features
See [`SPEC.md`](./SPEC.md) for the architecture, design decisions, and
the full phase log.
### Syntax & rendering
- Full vimwiki syntax (`.wiki`) — headings, lists with checkboxes,
numbered / lettered / Roman lists, tables (with `|---|`, `|:--|--:|:--:|`
alignment markers and `>` / `\/` colspan/rowspan), inline math, code
fences, tags, comments, and links (wiki, interwiki, diary, file, local,
raw URL, transclusion).
- Transclusions support attributes: `{{image.png|alt|style="border:1px"}}`.
- HTML export with templates, CSS, RSS feed, and incremental
`:VimwikiAll2HTML`.
### Editor intelligence (LSP)
- Goto-definition (follow wikilinks), backlinks, hover preview,
completion on `[[`, document outline, workspace symbol search.
- Semantic-token highlighting (no syntax-file fallback needed).
- Broken-link diagnostics and an orphan-page finder.
- Folding: server-driven `foldingRange` by default, regex fallback when
the server isn't ready.
### Editing
- Insert-mode smart `<CR>`: continues list markers (`-`, `*`, `1.`,
`a)`, `i)`, …) and checkboxes; breaks out of an empty bullet; inserts
a fresh row when inside a `|…|` table.
- Insert-mode `<Tab>` / `<S-Tab>` jumps between table cells, adding a
new row past the last column.
- `<C-D>` / `<C-T>` indent/dedent the current list item; `<C-L><C-J>`
/ `<C-L><C-K>` cycle through the marker styles; `<C-L><C-M>` toggles
or adds a checkbox.
- Five text-object pairs in operator-pending + visual mode:
`ah`/`ih` (heading section), `aH`/`iH` (heading + subtree),
`al`/`il` (list item), `a\`/`i\` (table cell), `ac`/`ic` (table column).
- Two-step `<CR>` on a bare word: wraps it as `[[word]]`, second press
follows the link.
### Diary
- Daily diary out of the box (`<Leader>ww`, `<C-Down>` / `<C-Up>`).
- Configurable cadence: set `diary_frequency = 'weekly'` (or `'monthly'`
/ `'yearly'`) per wiki and the commands address `2026-W19.wiki`,
`2026-05.wiki`, `2026.wiki` instead. Navigation stays at the same
cadence as the entry under the cursor.
- Diary index page generation (`:VimwikiDiaryGenerateLinks`).
### Other
- Multi-wiki with cross-wiki `[[wn.name:Page]]` interwiki links and a
`:VimwikiUISelect` picker.
- TOC generation, tag search, tag-to-page link lists.
- `:Vimwiki*` command compat + canonical `:Nuwiki*` aliases.
- Plain Vim and Neovim both supported with feature parity (text
objects, folding, smart insert-mode bindings).
---
## Installation
The plugin ships a `nuwiki-ls` binary that the editor talks to over
LSP stdio. The `install()` Lua helper downloads a pre-built release for
your platform, falling back to `cargo build --release` if no asset
matches.
The plugin ships a `nuwiki-ls` binary that the editor talks to over LSP
stdio. The `install()` helper downloads a pre-built release for your
platform, falling back to `cargo build --release` if no asset matches.
### lazy.nvim
@@ -74,31 +113,28 @@ cd ~/.vim/pack/gffranco/start/nuwiki && cargo build --release -p nuwiki-ls
mkdir -p bin && ln -s target/release/nuwiki-ls bin/nuwiki-ls
```
Then ensure an LSP client is loaded (Vim path prefers
[`vim-lsp`](https://github.com/prabirshrestha/vim-lsp), falls back to
[`coc.nvim`](https://github.com/neoclide/coc.nvim)). Neovim 0.11+ uses
its built-in LSP client — no extra plugin needed.
Plain Vim users also need an LSP client —
[`vim-lsp`](https://github.com/prabirshrestha/vim-lsp) is the
recommended path, with [`coc.nvim`](https://github.com/neoclide/coc.nvim)
as a fallback. Neovim 0.11+ uses its built-in LSP client and needs
no extra plugin.
### Trying it without touching your config
### Try it without touching your config
```sh
./start-nvim.sh # spawns Neovim with a scratch wiki under
# $XDG_CACHE_HOME/nuwiki-dev
./start-vim.sh # same, but for plain Vim — clones vim-lsp
# into the cache on first run
./start-nvim.sh # spawns Neovim against an isolated sample wiki
./start-vim.sh # same, for plain Vim (clones vim-lsp on first run)
```
Both scripts build the LSP binary, seed a sample wiki, and launch
the editor against an isolated state dir. Pass a wiki file path to
open it directly; `NUWIKI_DEV_WIKI=/path/to/wiki` points at your
own sandbox instead.
Both scripts build the LSP binary, seed a scratch wiki, and launch the
editor against an isolated state dir under `$XDG_CACHE_HOME/nuwiki-dev`.
---
## Configuration
Defaults are vimwiki-compatible. Full schema (with type annotations and
defaults) lives in [`lua/nuwiki/config.lua`](./lua/nuwiki/config.lua).
Defaults are vimwiki-compatible. Every key has a sensible default — the
example below shows the full surface for reference.
```lua
require('nuwiki').setup({
@@ -108,41 +144,55 @@ require('nuwiki').setup({
syntax = 'vimwiki', -- 'vimwiki' | 'markdown' (future)
log_level = 'warn',
-- Or the multi-wiki form. The single-wiki keys above desugar into
-- a one-entry `wikis = [...]` list, so you can pick either shape.
-- Multi-wiki: each entry honours every per-wiki key vimwiki understands.
-- Pick either this OR the single-wiki shorthand above. If both are set,
-- `wikis` wins.
wikis = {
{
name = 'personal',
root = '~/vimwiki',
file_extension = '.wiki',
diary_rel_path = 'diary',
diary_index = 'diary',
-- HTML export (Phase 17)
name = 'personal',
root = '~/vimwiki',
index = 'index', -- index page stem
file_extension = '.wiki',
-- Diary
diary_rel_path = 'diary',
diary_index = 'diary',
diary_frequency = 'daily', -- 'daily' | 'weekly' | 'monthly' | 'yearly'
diary_sort = 'desc', -- 'desc' | 'asc'
diary_header = 'Diary',
-- HTML export
html_path = '~/vimwiki/_html',
template_path = '~/vimwiki/_templates',
template_default = 'default',
template_ext = '.tpl',
css_name = 'style.css',
auto_export = false, -- export on save
exclude_files = {}, -- glob patterns
auto_export = false, -- export on save
auto_toc = false, -- auto-refresh TOC on save
exclude_files = {}, -- glob patterns
-- Checkbox progression characters: " .oOX" by default.
listsyms = ' .oOX',
listsyms_propagate = true,
-- Inline highlighting: 6 heading levels by default.
maxhi = 1,
-- Embed other syntaxes inside `{{{lang …}}}` fences.
nested_syntaxes = {},
},
},
-- Keymap groups — flip subgroups off independently. Defaults all on.
-- Keymap groups. Flip subgroups off independently. Defaults all on.
mappings = {
enabled = true,
wiki_prefix = true, -- <Leader>w*
links = true, -- <CR>, <S-CR>, <Tab>, <BS>, +, …
lists = true, -- <C-Space>, gln/glp/glx, gl<Space>, o/O
lists = true, -- <C-Space>, gln/glp/glx, gl<Space>, o/O, …
headers = true, -- =, -, ]], [[, ]=, [=, ]u, [u
table_editing = true, -- gqq, gq1, gww, gw1, <A-Left>, <A-Right>
table_editing = true, -- gqq, <A-Left>, <A-Right>, <Tab> in insert mode
diary = true, -- <C-Down>, <C-Up>
html_export = true, -- <Leader>wh, <Leader>whh
text_objects = true, -- ah, ih
text_objects = true, -- ah, ih, aH, iH, al, il, a\, i\, ac, ic
mouse = false, -- <2-LeftMouse>, <RightMouse>, … (opt-in)
},
-- Folding strategy: 'lsp' (server-driven foldingRange default on
-- Neovim 0.11+), 'expr' (pure regex), or 'off'.
-- 'lsp' (server-driven foldingRange, default) | 'expr' (regex) | 'off'.
folding = 'lsp',
-- Broken-link diagnostic severity: 'off' | 'hint' | 'warn' | 'error'.
@@ -152,14 +202,16 @@ require('nuwiki').setup({
})
```
Vim users without Lua should set the equivalent globals before sourcing
the plugin:
Vim users without Lua set the equivalent globals before loading the
plugin:
```vim
let g:nuwiki_wiki_root = '~/vimwiki'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_log_level = 'warn'
let g:nuwiki_no_default_mappings = 0 " set to 1 to skip the keymap layer
let g:nuwiki_wiki_root = '~/vimwiki'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_log_level = 'warn'
let g:nuwiki_no_default_mappings = 0 " set to 1 to skip the keymap layer
let g:nuwiki_no_folding = 0 " set to 1 to skip foldexpr setup
let g:nuwiki_mouse_mappings = 0 " set to 1 to enable mouse maps
```
---
@@ -170,89 +222,122 @@ let g:nuwiki_no_default_mappings = 0 " set to 1 to skip the keymap layer
Both `:Vimwiki*` (migration compat) and `:Nuwiki*` (canonical native)
are registered as buffer-local commands on every `.wiki` buffer.
Selected highlights — see `:Vimwiki<Tab>` for the full list.
Selected highlights — type `:Vimwiki<Tab>` for the full list.
| Command | What it does |
|---|---|
| `:VimwikiIndex [N]` | Open wiki N's `index.wiki` (default: 1) |
| `:VimwikiIndex [N]` | Open wiki N's index page (default: 1) |
| `:VimwikiTabIndex [N]` | Same, in a new tab |
| `:VimwikiUISelect` | Pick a wiki from a list (multi-wiki only) |
| `:VimwikiMakeDiaryNote` | Open today's diary page |
| `:VimwikiDiaryNextDay` / `:VimwikiDiaryPrevDay` | Chronological diary nav |
| `:VimwikiTOC` | Generate / refresh table of contents on the current page |
| `:VimwikiGenerateLinks` | Flat list of every page in the wiki |
| `:VimwikiGoto {page}` | Open `{page}.wiki` by name |
| `:VimwikiMakeDiaryNote` | Open today's diary entry (or this week / month / year, depending on `diary_frequency`) |
| `:VimwikiMakeYesterdayDiaryNote` / `:VimwikiMakeTomorrowDiaryNote` | Step the diary back / forward by one period |
| `:VimwikiDiaryIndex` | Open the diary index page |
| `:VimwikiDiaryGenerateLinks` | Rebuild the diary index from current entries |
| `:VimwikiDiaryNextDay` / `:VimwikiDiaryPrevDay` | Chronological diary nav (same cadence as the current entry) |
| `:VimwikiTOC` | Generate / refresh the table of contents on the current page |
| `:VimwikiGenerateLinks` | Insert a flat list of every page in the wiki |
| `:VimwikiCheckLinks` | Send broken links to the quickfix list |
| `:VimwikiSearchTags {tag}` | Quickfix of all occurrences of `:tag:` |
| `:VimwikiGenerateTagLinks [tag]` | Insert a section linking all pages with `<tag>` |
| `:VimwikiSearchTags {tag}` | Quickfix listing every occurrence of `:tag:` |
| `:VimwikiGenerateTagLinks [tag]` | Insert a section linking every page that has `<tag>` |
| `:VimwikiRebuildTags` | Force a full workspace re-index |
| `:VimwikiRenameFile` | Rename current page; rewrites incoming links |
| `:VimwikiDeleteFile` | Delete current page (server-side `DeleteFile`) |
| `:Vimwiki2HTML` | Render current page to HTML |
| `:Vimwiki2HTMLBrowse` | Render + open in default browser |
| `:VimwikiAll2HTML[!]` | Export the whole wiki (`!` forces, otherwise incremental) |
| `:VimwikiRss` | Write `rss.xml` of diary entries |
| `:VimwikiFollowLink` | Follow link under cursor (`<CR>`) |
| `:VimwikiBacklinks` | Show references to current page |
| `:VimwikiRenameFile` | Rename the current page, rewriting incoming links |
| `:VimwikiDeleteFile` | Delete the current page |
| `:Vimwiki2HTML` | Render the current page to HTML |
| `:Vimwiki2HTMLBrowse` | Render the current page and open it in the default browser |
| `:VimwikiAll2HTML[!]` | Export the whole wiki (`!` forces; otherwise incremental) |
| `:VimwikiRss` | Write `rss.xml` for diary entries |
| `:VimwikiFollowLink` | Follow the link under the cursor |
| `:VimwikiBacklinks` | Show all references to the current page |
| `:VimwikiNextLink` / `:VimwikiPrevLink` | Jump to the next / previous wikilink on the page |
| `:VimwikiBaddLink` | Add the target of the link under the cursor to the buffer list |
### Default keymaps
All buffer-local. Disable any group via `mappings.<group> = false` in
Neovim, or `let g:nuwiki_no_default_mappings = 1` in Vim.
**Links**
**Links** (normal mode)
| Key | Action |
|---|---|
| `<CR>` | Smart follow: inside `[[…]]` jump; on a bare word wrap as `[[word]]` (second `<CR>` follows) |
| `<CR>` | Smart follow inside `[[…]]` jumps; on a bare word wraps as `[[word]]` (second `<CR>` follows) |
| `<S-CR>` | Follow in horizontal split |
| `<C-CR>` | Follow in vertical split |
| `<C-S-CR>` | Follow in new tab |
| `<C-S-CR>` | Follow in a new tab |
| `<BS>` | Jump back (`<C-o>`) |
| `<Tab>` / `<S-Tab>` | Next / previous wikilink on or after the cursor |
| `+` | Wrap word / visual selection as a wikilink (no follow) |
**Lists**
**Lists** (normal mode)
| Key | Action |
|---|---|
| `<C-Space>` / `<C-@>` / `<Nul>` | Toggle checkbox (terminal byte aliases) |
| `gln` / `glp` | Cycle checkbox state (forward / backward) |
| `<C-Space>` / `<C-@>` / `<Nul>` | Toggle the checkbox under the cursor (the three aliases cover terminal byte differences) |
| `gln` / `glp` | Cycle the checkbox state forward / backward |
| `glx` | Toggle the rejected marker `[-]` |
| `gnt` | Jump to the next unfinished task |
| `o` / `O` | Open a new line and continue the list marker (and checkbox if any) |
| `glh` / `gll` | Dedent / indent the current list item |
| `gLh` / `gLl` | Dedent / indent including the item's sublist |
| `glr` | Renumber the current ordered list |
| `gLr` | Renumber every ordered list in the buffer |
| `gl<Space>` | Remove every completed item from the current list |
| `gL<Space>` | Remove every completed item in the buffer |
| `o` / `O` | Open a new line and continue the list marker (preserves any checkbox) |
**Lists** (insert mode)
| Key | Action |
|---|---|
| `<CR>` | Continue the list with the same marker (preserves checkbox), or break out on an empty bullet |
| `<C-D>` / `<C-T>` | Dedent / indent the current item |
| `<C-L><C-J>` / `<C-L><C-K>` | Cycle the marker style (`-``*``#``1.``1)``a)``A)``i)``I)`) |
| `<C-L><C-M>` | Toggle / add a checkbox on the current item |
**Headers**
| Key | Action |
|---|---|
| `=` / `-` | Add / remove a heading level |
| `]]` / `[[` | Next / previous heading |
| `=` / `-` | Increase / decrease the heading level |
| `]]` / `[[` | Next / previous heading (any level) |
| `]=` / `[=` | Next / previous sibling heading |
| `]u` / `[u` | Jump to parent heading |
| `]u` / `[u` | Jump to the parent heading |
**Wiki / diary / export**
**Tables**
| Key | Action |
|---|---|
| `<Leader>ww` | Today's diary |
| `<Leader>ws` | Pick a wiki (`vim.ui.select` / `inputlist`) |
| `<Leader>wi` | Diary index |
| `gqq` / `gq1` / `gww` / `gw1` | Align the table under the cursor |
| `<A-Left>` / `<A-Right>` | Move the column under the cursor left / right |
| `<Tab>` / `<S-Tab>` (insert) | Jump to the next / previous cell; `<Tab>` past the last cell inserts a fresh row |
| `<CR>` (insert) | Insert a fresh empty row below the current one |
**Wiki / diary / export** (normal mode)
| Key | Action |
|---|---|
| `<Leader>ww` | Open today's diary |
| `<Leader>ws` | Pick a wiki |
| `<Leader>wi` | Open the diary index |
| `<Leader>w<Leader>w` / `<Leader>w<Leader>y` / `<Leader>w<Leader>t` | Today / yesterday / tomorrow |
| `<Leader>w<Leader>i` | Rebuild diary index page |
| `<Leader>w<Leader>i` | Rebuild the diary index page |
| `<Leader>wn` / `<Leader>wd` / `<Leader>wr` | Goto / delete / rename page |
| `<Leader>wh` / `<Leader>whh` | Export current page HTML / open in browser |
| `<Leader>wh` / `<Leader>whh` | Export the current page to HTML / open in browser |
| `<Leader>wha` | Export every page |
| `<Leader>wc` | Wrap word (or visual selection) in a colour span |
| `<C-Down>` / `<C-Up>` | Next / previous diary entry |
**Text objects**
**Text objects** (operator-pending + visual)
| Key | Selection |
| Object | Selection |
|---|---|
| `ah` / `ih` | A heading (with / without the heading line itself) |
| `ah` / `ih` | The heading's section (`ih` excludes the heading line) |
| `aH` / `iH` | The heading's section plus every subheading underneath |
| `al` / `il` | The list item under the cursor (`il` excludes the marker / checkbox) |
| `a\` / `i\` | The table cell under the cursor (`i\` excludes the surrounding `\|`) |
| `ac` / `ic` | The table column under the cursor (`ic` excludes the header separator row) |
Additional text objects (`aH`, `iH`, `a\`, `i\`, `ac`, `ic`, `al`,
`il`) are planned follow-ups now that the table/list rewriter
commands they depend on have shipped.
### Health check
### Health check (Neovim)
```vim
:checkhealth nuwiki
@@ -265,96 +350,17 @@ whether the configured HTML output directory is writable.
### Migrating from vimwiki
The file extension, syntax, command names, and default keymaps all
match vimwiki. Drop the original `vimwiki` plugin from your config,
install nuwiki, and point `wiki_root` at the same directory. Existing
pages, diary entries, tags, and templates Just Work.
match vimwiki. To migrate:
Two known behavior deltas vs. upstream vimwiki:
1. Drop the original `vimwiki` plugin from your config.
2. Install nuwiki.
3. Point `wiki_root` (or each `wikis[i].root`) at your existing
directory.
- nuwiki's index is built lazily on workspace scan; the very first
`:VimwikiCheckLinks` after launch may be empty until the background
task completes. Subsequent runs are instant.
- `gln` and `glp` both call `nuwiki.list.cycleCheckbox`, which is
forward-only at present. A reverse cycle lands once the v1.1 list
rewriters do.
---
## Repository layout
```
nuwiki/
├── Cargo.toml workspace root
├── crates/
│ ├── nuwiki-core/ parser, AST, renderer (editor-independent)
│ ├── nuwiki-lsp/ LSP protocol bridge
│ └── nuwiki-ls/ stdio LSP binary entry point
├── plugin/ universal Vim/Neovim entry point
├── lua/nuwiki/ Neovim Lua glue
├── autoload/nuwiki/ Vim VimL glue
├── ftdetect/ filetype detection for .wiki
├── ftplugin/vimwiki.vim per-buffer settings + commands + keymaps
├── syntax/vimwiki.vim static fallback highlighting
├── doc/ `:h nuwiki`
├── scripts/ test harness + build helpers
└── .gitea/workflows/ CI / release pipelines
```
## Development
### Build + test
```sh
cargo build --workspace
cargo test --workspace # 381 Rust tests
./scripts/test-keymaps.sh # 19 Neovim keymap cases (LSP-driven)
./scripts/test-keymaps-vim.sh # 12 Vim keymap cases (pure-VimL)
```
CI runs all four on every push (`.gitea/workflows/ci.yaml`). MSRV: Rust
1.83.
### Trying changes against a real wiki
```sh
./start-nvim.sh # isolated state under
./start-vim.sh # $XDG_CACHE_HOME/nuwiki-dev
```
Both regenerate their minimal config on every launch and seed a
scratch wiki with sample pages, links, checkboxes, and tags.
### Implementation status
#### v1.0 — foundation
| Phase | Name | Status |
|---|---|---|
| 0 | Scaffolding | ✅ done |
| 1 | Core AST | ✅ done |
| 2 | Syntax Plugin Interface | ✅ done |
| 3 | Vimwiki Lexer | ✅ done |
| 4 | Vimwiki Parser | ✅ done |
| 5 | Renderer | ✅ done |
| 6 | LSP Foundation | ✅ done |
| 7 | Semantic Tokens | ✅ done |
| 8 | Navigation | ✅ done |
| 9 | Editor Glue | ✅ done |
| 10 | CI/CD release pipeline | ✅ done |
#### v1.1 — full vimwiki replacement
| Phase | Name | Status |
|---|---|---|
| 11 | Plumbing prerequisites | ✅ done |
| 12 | Tags | ✅ done |
| 13 | Workspace edits + executeCommand | ✅ done |
| 14 | List & table edit commands | ✅ done |
| 15 | Link health + TOC/index generation | ✅ done |
| 16 | Diary | ✅ done |
| 17 | HTML export commands | ✅ done |
| 18 | Multi-wiki | ✅ done |
| 19 | Editor glue v2 (`:Vimwiki*` compat, keymaps, text objects, folding) | ✅ done |
Existing pages, diary entries, tags, and templates work without
modification. The first workspace scan after launch may show an empty
`:VimwikiCheckLinks` result until the background index completes;
subsequent runs are instant.
---