
Who is it for?
For people like me who think that apps like Obsidian are holding them back and that terminal is the way to stay productive. neovim is your only text/code/config editor? You're [presumably] in the right place.
Installation
Go-way
go install github.com/dickus/dreadnotes@latest
Make sure your $GOPATH is in your $PATH.
Binary
- Download the archive from releases page.
- Extract the file.
- Put the file to your $PATH.
Build from source
Clone the repo.
Locally
Run make install PREFIX=$HOME/.local.
Make sure that $HOME/.local/bin is in your $PATH.
System-wide
Run sudo make install.
Configuration
Dreadnotes has minimal configuration setup. It looks into $HOME/.config/dreadnotes for config.toml by default.
Default config
notes_path = "$HOME/Documents/dreadnotes"
editor = "nvim"
templates_path = "$HOME/.config/dreadnotes/templates"
Multiple "vaults"
If you wish to split your notes into several "vaults", you can use the DREADNOTES_CONFIG environment variable. It will work as a different storage, so different Git repo, different search index, etc.
- Create a secondary config file (e.g.,
$HOME/.config/dreadnotes/work.toml) with different notes_path.
- Add an alias to your
~/.bashrc or ~/.zshrc:
# 'dn' for default notes vault
alias dn="dreadnotes"
# 'dnw' for work notes
alias dnw="DREADNOTES_CONFIG=$HOME/.config/dreadnotes/work.toml dreadnotes"
dnw open will only search your work documents, etc.
Usage
The general syntax for the CLI is:
dreadnotes <COMMAND> [FLAGS]
Run dreadnotes --help at any time to see the available commands.
Create (new)
You can create a note with a default layout, specify a template, or choose one interactively.
Avoid using / (slash) character in template and note names.
Usage:
dreadnotes new [FLAGS] "<NAME>"
Options:
| Flag |
Description |
-T <name> |
Use a specific template by name (e.g., -T daily) |
-i |
Pick a template via an interactive menu |
-h, --help |
Show help for this command |
Examples:
# Create a simple note
dreadnotes new "Project Idea"
# Create a note using the 'meeting' template
dreadnotes new -T meeting "Team Sync"
# Create a note using the 'meeting plan' template
dreadnotes new -T "meeting plan" tomorrow meeting plan
# Choose a template interactively
dreadnotes new -i "Refactoring Plan"
Find (open)
You can browse using titles and content filtered by creation and modification dates. You can also filter search by specific tags.
To move between found notes use Alt-j/k. It was made like this to avoid issues with using tmux.
To switch between search fields use Tab/Shift-Tab.
To switch between creation/modification dates filter use Alt-d.
Kind of a "vim-motions" mode
You can use kind of a "vim-motions" to navigate. Press Esc to switch to "normal" mode. In this mode you can:
- Use j/k to select notes.
- Press / to use search field.
- Press t to use tags field.
- Press d to use dates field.
- Press Esc to exit dreadnotes. Or you can always use Ctrl-C.
Usage:
dreadnotes open [FLAGS]
Options:
| Flag |
Description |
-h, --help |
Show help for this command |
Examples:
dreadnotes open
Rediscover (random)
Open a random note from your vault.
Usage:
dreadnotes random
Sync (sync)
Update your local notes repository. This command handles local git operations and pushes changes to a remote repository if one is configured.
You can add a remote repo if you want to. By default sync command will both initialize local repo if it doesn't exist yet and commit changes in it.
Usage:
dreadnotes sync
Fix (doctor)
Check your notes for broken wikilinks, duplicate titles and empty content.
Usage:
dreadnotes doctor
Neovim tips
If you're using Neovim, I suggest using several functions to make the experience a bit more pleasant.
Update time on write
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.md",
callback = function()
local save_cursor = vim.fn.getpos(".")
local n = math.min(10, vim.fn.line("$"))
local lines = vim.api.nvim_buf_get_lines(0, 0, n, false)
local now = os.time()
local tz = os.date("%z", now)
local tz_formatted = tz:sub(1, 3) .. ":" .. tz:sub(4, 5)
local new_date = os.date("updated: %Y-%m-%d %H:%M")
for i, line in ipairs(lines) do
if line:match("^updated:") then
vim.api.nvim_buf_set_lines(0, i-1, i, false, {new_date})
break
end
end
vim.fn.setpos(".", save_cursor)
end,
})
This function updates the modified time of the note when you save the file. It will help dreadnotes know that the note is updated based on frontmatter field.
Easy tagging
local function manage_tags()
local orig_buf = vim.api.nvim_get_current_buf()
local lines = vim.api.nvim_buf_get_lines(orig_buf, 0, -1, false)
local frontmatter_start = nil
local frontmatter_end = nil
local existing_tags_line_index = nil
local current_tags_text = ""
for i, line in ipairs(lines) do
if line:match("^%-%-%-") then
if not frontmatter_start then
frontmatter_start = i
else
frontmatter_end = i
break
end
end
if frontmatter_start and not frontmatter_end then
local tags_content = line:match("^tags:%s*%[(.*)%]")
if tags_content then
existing_tags_line_index = i
current_tags_text = tags_content
end
end
end
local input_buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(input_buf, 0, -1, false, { current_tags_text })
local width = 60
local height = 1
local col = math.floor((vim.o.columns - width) / 2)
local row = math.floor((vim.o.lines - height) / 2) - 2
local win = vim.api.nvim_open_win(input_buf, true, {
relative = "editor",
row = row,
col = col,
width = width,
height = height,
style = "minimal",
border = "rounded",
title = " Edit tags: ",
title_pos = "center"
})
local function apply()
local input = vim.api.nvim_buf_get_lines(input_buf, 0, 1, false)[1]
local tags = {}
if input then
for tag in input:gmatch("[^,]+") do
tag = tag:match("^%s*(.-)%s*$")
if tag ~= "" then
table.insert(tags, tag)
end
end
end
local new_tags_line = "tags: [" .. table.concat(tags, ", ") .. "]"
if existing_tags_line_index then
vim.api.nvim_buf_set_lines(orig_buf, existing_tags_line_index - 1, existing_tags_line_index, false, { new_tags_line })
elseif frontmatter_start and frontmatter_end then
vim.api.nvim_buf_set_lines(orig_buf, frontmatter_end - 1, frontmatter_end - 1, false, { new_tags_line })
else
local header = { "---", new_tags_line, "---", "" }
vim.api.nvim_buf_set_lines(orig_buf, 0, 0, false, header)
end
vim.api.nvim_win_close(win, true)
vim.cmd("stopinsert")
end
local function cancel()
vim.api.nvim_win_close(win, true)
vim.cmd("stopinsert")
end
local opts = { buffer = input_buf, silent = true }
vim.keymap.set({ "n", "i" }, "<CR>", apply, opts)
vim.keymap.set("n", "<Esc>", cancel, opts)
vim.cmd("startinsert!")
end
vim.keymap.set("n", "<leader>tt", manage_tags, { desc = " Edit frontmatter tags" })
This function will let you manage tags faster without having to move to frontmatter tags field. Be aware that it works only for tags placed in [].