lantern

neovim-wanderland

Neovim Wanderland Integration

Edit Oculus nodes and execute fences directly from Neovim.

Overview

The wanderland.lua plugin provides Telescope-based integration with:

  • Oculus - Graph-based knowledge system (port 7778)
  • Stuffy - Live markdown streaming/preview (port 8080)

Installation

Canonical Source: ~/.config/nvim/lua/wanderland.lua The source file contains @oculus annotations pointing back to this documentation.

1. Add to Neovim Config

Copy wanderland.lua to ~/.config/nvim/lua/:

# The file location
~/.config/nvim/lua/wanderland.lua

2. Load in init.lua

-- In ~/.config/nvim/init.lua
local wanderland = require("wanderland")
wanderland.setup()

3. Register Keybindings (Which-Key)

local wk = require("which-key")

wk.add({
  -- Wanderland (SPC W)
  { "<leader>W", group = "wanderland" },

  -- Stuffy (SPC W s)
  { "<leader>Ws", group = "stuffy" },
  { "<leader>Wsu", wanderland.stuffy_upload_buffer, desc = "Upload buffer" },
  { "<leader>Wsd", wanderland.stuffy_download_to_buffer, desc = "Download channel" },
  { "<leader>Wsc", function() -- channel picker end, desc = "Set channel" },
  { "<leader>Wsb", wanderland.stuffy_open_browser, desc = "Open in browser" },
  { "<leader>Wss", wanderland.stuffy_toggle_auto_sync, desc = "Toggle auto-sync" },

  -- Oculus (SPC W o)
  { "<leader>Wo", group = "oculus" },
  { "<leader>Wop", wanderland.oculus_download_node, desc = "Pull node (raw)" },
  { "<leader>Wov", wanderland.oculus_download_node_rendered, desc = "View node (rendered)" },
  { "<leader>WoP", wanderland.oculus_publish, desc = "Publish node" },
  { "<leader>Woe", wanderland.oculus_show_edges, desc = "Navigate edges" },
  { "<leader>Wob", wanderland.oculus_open_browser, desc = "Open in browser" },
  { "<leader>Wow", wanderland.oculus_edit_with_preview, desc = "Edit with preview" },
  { "<leader>Wof", wanderland.oculus_browse_fences, desc = "Browse fences" },
  { "<leader>Wor", wanderland.oculus_run_fence, desc = "Run fence" },
  { "<leader>WoR", wanderland.oculus_run_fence_results, desc = "Run fence (results)" },
  { "<leader>Wok", wanderland.oculus_poke_quick, desc = "Quick poke" },
})

Keybindings Reference

Stuffy (SPC W s)

Key Action Description
SPC W s u Upload buffer Push current buffer to Stuffy channel
SPC W s d Download channel Pick channel and load to new buffer
SPC W s c Set channel Choose active Stuffy channel
SPC W s b Open browser View current channel in browser
SPC W s s Toggle auto-sync Auto-upload on save

Oculus (SPC W o)

Key Action Description
SPC W o p Pull node (raw) Download node source for editing
SPC W o v View rendered Download executed/rendered view
SPC W o P Publish node Save buffer back to Oculus
SPC W o e Navigate edges Telescope picker for N/S/E/W links
SPC W o b Open browser View node in Stuffy browser
SPC W o w Edit with preview Open raw + browser + auto-sync
SPC W o f Browse fences Telescope fence picker
SPC W o r Run fence Prompt for ID, execute, notify
SPC W o R Run fence (results) Prompt for ID, execute → split pane
SPC W o k Quick poke Poke value at path (shows current)

Fence Picker (SPC W o f)

In the fence picker Telescope window:

Key Action
Enter Go to fence location in node
Ctrl-x Execute fence (prompts for params if needed)
Ctrl-p Execute with params (always prompts)

Workflows

1. Quick Node Edit

SPC W o p    → Pick node
(edit)
SPC W o P    → Publish changes

2. Live Preview Editing

SPC W o w    → Pick node (opens browser + enables auto-sync)
(edit & save → browser updates automatically)
SPC W o P    → Publish when done

3. Execute Labeled Fence

SPC W o f    → Open fence picker
/magic-8Filter to magic-8-ball
Ctrl-p      → Execute with params
{"question": "Should I deploy?"} → Enter JSON

4. Navigate Graph

SPC W o p    → Open a node
SPC W o e    → Show edges (N/S/E/W)
(select)    → Navigate to linked node

5. Client-Side Composition

Build custom views by requesting specific fences at different levels:

-- Lua example: selective fence execution
local result = wanderland.oculus_compose_sparse("jenkins-job-config", {
  ["job-list"] = "rendered",    -- Execute: get live Jenkins data
  ["config"] = "stalk",         -- Don't execute: just show YAML
})

-- NeoVim can now stitch these together:
-- - Show config fence as editable code block
-- - Show job-list fence as live data table

This enables "living documents" where some fences show live data while others remain editable source.

Configuration

wanderland.setup({
  oculus_url = "http://localhost:7778",
  stuffy_url = "http://localhost:8080",
  browser = "firefox",  -- or "open" on macOS, "xdg-open" on Linux
})

API Functions

All functions are available on the wanderland module:

Stuffy

wanderland.stuffy_upload(channel, content, filename)
wanderland.stuffy_download(channel)  -- returns content string
wanderland.stuffy_channels()         -- returns list of channel names
wanderland.stuffy_set_channel(name)
wanderland.stuffy_upload_buffer()
wanderland.stuffy_download_to_buffer()
wanderland.stuffy_open_browser()
wanderland.stuffy_toggle_auto_sync()

Oculus

Oculus

Function Description
oculus_list_nodes() Get all node slugs
oculus_get_node(slug, mode) Get node content (raw/rendered)
oculus_download_node(mode) Telescope picker → buffer
oculus_show_edges() Navigate via directional links
oculus_publish() Save buffer to Oculus
oculus_refresh_node() Reload raw from server
oculus_rerender_node() Reload rendered from server

Peek/Poke

Read and write values at paths. Values are parsed as JSON first (for booleans, numbers, arrays, dicts), falling back to string.

Function Description
oculus_peek(slug, path, opts) Get value at path
oculus_peek_interactive() Prompt for slug, path → result split
oculus_poke(slug, path, value, opts) Write value at path
oculus_poke_interactive() Prompt for slug, path, value
oculus_poke_quick() Current node: path, value (shows current)

Value Parsing Examples:

  • true / false → boolean
  • 42 / 3.14 → number
  • [1, 2, 3] → array
  • {"key": "val"} → dict
  • hello world → string (not valid JSON)

Fences

wanderland.oculus_list_fences(type_filter)  -- returns list of fences
wanderland.oculus_execute_fence(fence_id, params)
wanderland.oculus_browse_fences()
wanderland.oculus_run_node(slug)

Compose API (Sparse Mode)

Request specific fences at different render levels - the client controls composition:

-- Sparse compose: request only the fences you need
wanderland.oculus_compose_sparse(slug, {
  ["magic-8-ball"] = "rendered",  -- Execute this one (by label)
  ["config"] = "stalk",           -- Raw content only (by label)
  ["3"] = "rendered",             -- By index
})

-- Returns:
-- {
--   sparse = true,
--   fences = {
--     ["magic-8-ball"] = { executed = true, data = {...} },
--     ["config"] = { executed = false, content = "..." },
--   }
-- }

Endpoint: POST /api/oculus/nodes/{slug}/compose

{
  "fences": {
    "fence-label": "rendered",
    "0": "stalk"
  }
}

Addressing modes:

  • By label: "magic-8-ball" → resolves via fence index
  • By index: "0", "3" → direct fence position

Levels:

Level Alias Result
seed raw Raw markdown
sprout substituted Variables resolved
stalk interpolated Fences visible, not executed
clouds rendered Fences executed

Runbook safety: Fences with execute=false never execute via compose, even at rendered level.

State Variables

wanderland.current_channel  -- Active Stuffy channel
wanderland.current_node     -- Currently loaded Oculus node slug
wanderland.auto_sync        -- Auto-sync enabled?
vim.b.oculus_mode           -- Buffer-local: "raw" or "rendered"

Safety Features

  • Publish protection: Cannot publish rendered buffers (would overwrite source with executed content)
  • Mode tracking: Buffer names show [raw] or [rendered] to indicate mode
  • Result reuse: Fence results open in named buffers that get reused

Dependencies

  • telescope.nvim
  • curl (for API calls)
  • jq (optional, for pretty-printing JSON results)

See Also

Markdown Editing

Since we do a lot of markdown editing with Oculus nodes, here's the recommended setup.

Plugins

-- Table editing (Tab/Shift-Tab to navigate, auto-reflow)
{ "SCJangra/table-nvim", ft = "markdown", ... }

-- Editing tools (links, lists, heading navigation)
{ "tadmccorkle/markdown.nvim", ft = "markdown", ... }

-- Subtle rendering (headings, bullets - raw in insert mode)
{ "MeanderingProgrammer/render-markdown.nvim", ft = "markdown", ... }

Virtual Buffer Fix

Oculus buffers are virtual (buftype=nofile) and don't match *.md patterns. Add these autocmds to make table-nvim work:

-- Keymaps for virtual markdown buffers
vim.api.nvim_create_autocmd("FileType", {
  pattern = "markdown",
  callback = function(opts)
    local ok, maps = pcall(require, "table-nvim.keymaps")
    if ok then maps.set_keymaps(opts.buf) end
  end,
})

-- Reflow tables on InsertLeave for virtual buffers
vim.api.nvim_create_autocmd("InsertLeave", {
  callback = function()
    if vim.bo.filetype ~= "markdown" then return end
    local ok, utils = pcall(require, "table-nvim.utils")
    if not ok then return end
    local MdTable = require("table-nvim.md_table")
    local root = utils.get_tbl_root(vim.treesitter.get_node())
    if root then MdTable:new(root):render() end
  end,
})

Keybindings Summary

Key Action Plugin
Tab Next table cell table-nvim
S-Tab Prev table cell table-nvim
Alt-j Insert row below table-nvim
Alt-k Insert row above table-nvim
]] Next heading markdown.nvim
[[ Prev heading markdown.nvim
[p Parent heading markdown.nvim
gx Follow link markdown.nvim

Alternatives Researched

Plugin Style Notes
table-nvim Auto-format as you type Tab navigation, Lua
markdown-table-mode.nvim Format on insert leave Lightweight, :Mtm toggle
vim-table-mode Manual reflow Classic, <leader>tr to realign

Related Cases

  • [[reactive-markdown-tables]] - Oculus virtual fence for programmatic table manipulation (A1 cell addressing, formulas)

Slots

North

slots:
- context:
  - Linking client registry to neovim implementation docs
  slug: wanderland-clients

South

slots:
- context:
  - Case implements Living Runbooks feature documented in parent
  slug: neovim-living-runbooks
- context:
  - Tutorial child of main docs
  slug: neovim-living-runbooks-tutorial

West

slots:
- context:
  - Editor integration for fence params
  slug: fence-params
- context:
  - Linking neovim guide to fence params docs
  slug: fence-params

East

slots:
- context:
  - Linking sibling editor integrations - both implement SPC W keybindings for Wanderland
  slug: emacs-integration

Oculus

3 Register Keybindings Which Key

Oculus Spc W O

Fence Picker Spc W O F