README file from
GithubNovelr
Long-form writing in Obsidian with a structure you define and a compile pipeline that understands it.
Novelr is inspired by Longform and novelWriter. Where Longform gives you a flat list of scenes, Novelr lets you decide the shape of your project: a novel made of chapters made of scenes, a novel with parts, a screenplay with acts and sequences, or anything else. Containers are real folders and content nodes are real notes, so your vault stays plain Markdown that any other tool can read.
Installation
Requires Obsidian 1.13 or newer.
- Community plugins: search for "Novelr" in Settings › Community plugins once it is listed.
- Manual: download
main.js,manifest.jsonandstyles.cssfrom the latest release into<vault>/.obsidian/plugins/novelr/, then enable the plugin. - BRAT: add
william-saxton/novlrin the BRAT plugin to follow releases before the directory listing.
Concepts
- Node type: either a container (a folder with a title, holding other nodes) or content (a note with a title and a body). Each container type can restrict which types it accepts.
- Schema: the list of node types for a project plus the root type. The default preset is Novel › Chapter › Scene. Presets ship for "Novel with parts" and "Short story", and you can save your own.
- Project: a folder with an index note (default name
novelr.md) whosenovelrproperty holds the title, schema, ordered tree, and compile settings. Order lives in the index, so reordering never renames files. - Workflow: a list of compile steps. Workflows are shared across the vault and each project picks one.
Getting started
- Enable the plugin and run Novelr: Create new project (or click the plus in the pane).
- Pick a title, a location, and a structure preset. Novelr creates the folder and the index note.
- Open the structure pane (ribbon icon or Novelr: Open structure pane). Add chapters and scenes, drag to reorder or move between chapters, right-click for more.
- Switch to the Compile tab, pick a workflow, and press Compile.
Notes that already exist in the project folder but are not in the index appear under Needs attention; add them with one click or ignore them with a glob pattern.
The index note
---
novelr:
version: 1
title: The Hollow Road
workflow: Manuscript (default)
ignore:
- _notes/**
schema:
rootType: novel
types:
- id: novel
name: Novel
kind: container
allowedChildren: [chapter]
- id: chapter
name: Chapter
kind: container
allowedChildren: [scene]
- id: scene
name: Scene
kind: content
tree:
- chapter: Chapter One
children:
- scene: Opening
- scene: The Call
- chapter: Chapter Two
children:
- scene: Aftermath
---
Each tree entry is <typeId>: <name>, optionally with children. Paths are derived from the tree: Chapter One/Opening.md is a scene inside the Chapter One folder. You can edit this by hand; Novelr tolerates mistakes and reports them in the pane.
Content notes get a novelr-type property when Novelr creates them, so Dataview and friends can query by type. Add novelr-skip: true to a note to leave it out of every compile.
Statuses
Every node can carry a status such as New, In progress or Done. Statuses are defined per project in the Project tab and each one has:
- a color shown as a dot next to the node,
- an optional parent status that it pushes onto the node containing it,
- a default flag for newly created nodes.
Pushing works all the way up. With the defaults, a chapter marked Done that gains a New scene shows In progress (hollow dot, tooltip says why), and so does the novel above it. When the scene is finished, the chapter shows Done again. If several children push different statuses, the one listed first wins, so order the list from "most attention needed" down.
Colors are the eight theme accent colors or any hex value: the plus swatch opens a color picker, and custom colors are kept in a vault-wide palette (right-click a custom swatch to edit or remove it).
Click a node's dot, right-click and choose Set status…, or run Novelr: Set status of current node. Content notes also get a novelr-status property when "Write node type and status to files" is on.
novelr:
statuses:
- { id: new, name: New, color: blue, parent: in-progress, default: true }
- { id: in-progress, name: In progress, color: yellow, parent: in-progress }
- { id: done, name: Done, color: green }
tree:
- chapter: Chapter One
status: done
children:
- scene: Opening
status: new
Comments
Proof readers and editors who share the vault can leave comments on your notes, and you can action them from a sidebar. Comments are built to travel over Obsidian Sync (or git, or any file sync):
- One Markdown note per comment, stored in the project's comments folder (default
_comments, changeable in the Project tab and saved in the index note so everyone uses the same folder). Markdown always syncs, and separate files mean two people commenting at the same time never overwrite each other. Nothing lives in the plugin'sdata.json. - Anchored by text, not by position. Each comment records the selected passage plus a little context on either side. When the note is opened, Novelr finds the passage again even if the text above it changed on another device; if the quoted words themselves were rewritten, it shows the closest match (dashed underline) or reports that the text is gone.
- Your name is remembered per device, in Obsidian's local storage rather than plugin settings, so syncing settings never turns everyone into the same person.
Select text in a note that belongs to a project, then right-click › Add comment to selection or run Novelr: Add comment to selection (or note). With nothing selected the comment applies to the whole note. Commented passages are highlighted in the editor (turn this off in settings); click a highlight to show its comment.
The Novelr comments pane (right sidebar, Novelr: Open comments pane) lists comments for the active note or the whole project, filtered by open or resolved, searchable and sortable by position or age. Each comment can be jumped to, replied to, edited, resolved or deleted. Resolving keeps the file with status: resolved so the author of the comment sees what happened; deleting moves it to the trash. The structure pane shows a count of open comments next to each note.
A comment file looks like this:
---
novelr-comment: 1
id: "20260926-134012-k3x9"
note: "Chapter One/Opening.md"
author: "Jane"
created: "2026-09-26T13:40:12.000Z"
status: "open"
quote: "The rain fell in torrents"
before: "It was a dark and stormy night. "
after: ", except at occasional intervals"
offset: 32
---
Too many adverbs here. Consider cutting the second clause.
note is relative to the project root and follows the note when it is renamed or moved. Replies carry reply-to: "<id>". You can write or fix these files by hand; anything the plugin cannot read is simply not shown.
Compile
A workflow is a sequence of steps of four kinds:
| Kind | What it sees | Built-in steps |
|---|---|---|
| Node | every node whose type matches the step's Apply to list (empty = all) | Strip frontmatter, Remove links, Remove comments, Remove strikethroughs, Remove headings, Insert before, Insert after, Find and replace, Trim whitespace |
| Structure | the whole tree, before it is built | Filter by status |
| Build | the whole tree, once | Build manuscript |
| Manuscript | the flattened text | Normalize blank lines, Find and replace, Add frontmatter, Save as note |
Node and structure steps come first, then one build step, then manuscript steps. Filter by status leaves out nodes with chosen statuses (a container's effective status counts, so a whole In progress chapter can be dropped) or keeps only content with chosen statuses, and renumbers what remains. Insert before and Insert after attach text to nodes of a given type, which is how you get chapter headings, part pages or scene separators:
- Chapter headings: Insert before on
chapterwith# Chapter {number}: {title} - Scene separators: Insert before on
scenewith* * *and Skip the first on - Part pages: Insert before on
partwith{PB}# Part {number:Roman}{BR}{BR}## {title}
Placeholders
| Placeholder | Meaning |
|---|---|
{title} {type} {status} {status.id} |
the node's name, type id, effective status name and id |
{number} |
position among siblings of the same type (1-based) |
{count} {index} {absolute} {depth} |
siblings of this type, 0-based position among all siblings, position of this type across the whole project, nesting depth |
{number:word} {number:Word} {number:WORD} {number:roman} {number:Roman} {number:pad2} |
number formatting (works on any numeric placeholder) |
{parent.title} {parent.number} |
the containing node |
{chapter.title} {chapter.number} |
the nearest ancestor of that type (any type id works) |
{project.title} {date} |
project title, today's date |
{BR} {PB} |
line break, page break (configurable in settings) |
---- |
as the whole value, a horizontal rule |
Steps from other plugins
Novelr does not load or evaluate script files. Other plugins (including a small personal one) can register compile steps through the public API instead:
// In another plugin, after Novelr has loaded:
const novelr = this.app.plugins.plugins["novelr"];
novelr?.api.registerStep({
description: {
canonicalID: "my-plugin:shout", // prefix with your plugin id
name: "Shout",
description: "Uppercases every targeted node.",
kind: "node", // "node" | "tree" | "join" | "manuscript"
external: true,
options: [{ id: "suffix", name: "Suffix", description: "", type: "text", default: "!" }],
},
compile(node, ctx) {
if (node.kind === "content") node.text = node.text.toUpperCase() + String(ctx.options.suffix);
},
});
// and in onunload: novelr?.api.unregisterStep("my-plugin:shout");
Registered steps appear under "From other plugins" in the workflow editor. Node steps receive (node, ctx) and mutate node.text, node.before or node.after. Tree steps receive (root, ctx) and may prune or reorder children. Build steps receive (root, ctx) and return a string. Manuscript steps receive (text, ctx) and return a string. ctx.format(fmt, node) expands placeholders and ctx.app is the Obsidian app.
What Novelr touches
- On startup it checks the cached frontmatter of every Markdown note once to find index notes (the ones with a
novelrproperty). It does not read file contents to do this. - It only reads and writes notes inside project folders: the index note, content nodes, comment files in the comments folder, and the manuscript written by a compile step.
- It never runs code from your vault and never touches the clipboard or the network.
Commands
- Open structure pane
- Create new project
- Compile current project
- Open project index note
- Open next / previous content node
- Reveal active file in structure pane
- New node in current container
- Set status of current node
- Add comment to selection (or note)
- Open comments pane
Development
npm install
npm run dev # rebuilds on change and copies into test-vault/.obsidian/plugins/novelr when that folder exists
npm test
npm run lint
npm run build