# Sheetsmith Design and use character sheets for any tabletop RPG in [Obsidian](https://obsidian.md). Build a layout on a grid, define your own formulas, and keep every character as a plain markdown note. > **Status: in the Obsidian community plugin list.** See [Install](#install). This page describes the code in this repository, which can run ahead of the version the list serves. The file model and the sheet view are in place, and so is the following. > > - **Components.** Twelve: Card, Card set, Group, Image, Passport, Pool, Record set, Rich text, Roster, Table, Tab set and Track. A Record set field can sit **Inside the opened record** rather than on its summary line, and can be **Shown when** a condition on another field holds. A Record set can **Group by** one of its fields, drawing its records under headers that collapse. The field is a level, a number or a word the player types, and typing one offers the group names already in the list. Rich text closes brackets and continues lists as you type. Faint text is lifted to a readable contrast, and Track and Table stay legible with forced colors on. > - **Formulas.** The formula engine, a per-layout function library, row aggregates, typed modifiers with definitions and provenance, and reset triggers. A Record set reset can apply **Only where** a condition holds and name the one field it **Acts on**, and the confirmation says how many records it reaches. Where a field's **Maximum from** is **Each record**, that ceiling can be a formula such as `prof`, with names suggested as you type. A modifier can carry a **Note**, with or without an amount, shown at the Card, Table or Roster value it targets. A Pool maximum that a modifier moved is marked and opens its breakdown. **Computed**, a read-only value fed entirely by a formula, is in the component picker. **Promoted fields** mirror chosen values into a character's frontmatter, so Bases and Dataview can query them. > - **Layout editor.** A pane of its own, with its tree, configuration panel, undo, live grid canvas, sample-value preview, layout import and export, and three ways to start a new layout. The tree nests like the layout, folds a container shut, and moves a component by drag, by its row menu or by Alt+arrow keys; a move it refuses, such as a reorder on a placed grid, says why. **Copy**, **Paste** and **Paste configuration** work within a layout and across layouts. A misconfigured field reports itself inline, both there and on the sheet; a broken formula does the same in the pane, and leaves the value it fed reading "?" with the reason on hover. > - **Characters and settings.** The **Create a character** command offers the vault's layouts by name, as does a note whose layout is missing, so no character needs its frontmatter typed by hand. The character folder is configurable, and on Obsidian 1.13 and later the settings tab's preferences turn up in settings search. > - **Data safety.** Reordering or shortening a level list says how many character notes on the layout hold that component's section, since each may now read its stored level differently, and offers **Undo**. The pane never silently drops an edit it could not save: it keeps the layout, says so, and offers **Try again** and **Copy layout**. > > Still to come: a phone layout. The sheet does not reflow for a phone, and the editor pane does not fit a narrow screen. ## What it is Sheetsmith is not a D&D character sheet. It is a character sheet **builder**. You place components on a grid, define the formulas that connect them, and save the result as a reusable layout. A character is an ordinary markdown note that names a layout and holds only values. One layout serves many characters. The plugin knows arithmetic and nothing about any game. Every rule specific to a system lives in the layout you build, so the same plugin serves D&D, Pathfinder, Call of Cthulhu, or something you wrote yourself. ## Why Every existing option fails on one of three axes: - **Static templates** look like a sheet but do nothing. - **System-specific renderers** work well for one game and do not transfer. - **Flexible tools** make you hand-author YAML to get anything on screen. Nothing combines a layout builder with a formula engine, and nothing is system-agnostic by design. ## How it works A character is a normal note. One property names its layout, and the values live in the body as readable markdown: ````markdown --- sheet-layout: DnD 5e Caster --- ## Abilities ```sheet STR: 8 DEX: 16 WIS: 12 ``` ## HP ```sheet current: 22 temp: 0 ``` ## Inventory | Item | Qty | Weight | Equipped | |---|---|---|---| | [[Bag of Holding]] | 1 | 15 | yes | ```` Two consequences worth stating: - **Your frontmatter stays clean.** One property, not thirty. Character data does not leak into the vault's property namespace or turn up in autocomplete on unrelated notes. - **Wikilinks work properly.** `[[Bag of Holding]]` is real markdown, so backlinks resolve, graph view sees it, hover preview works, and renaming the linked note updates the sheet. Clicking it in the rendered sheet navigates there. Formulas are defined per layout, so nothing about any game system is built into the plugin: ``` mod(score) = floor((score - 10) / 2) prof = ceil(level / 4) + 1 ``` A skill's computed total then reads: ``` ability + Training * prof + Bonus ``` One formula serves the whole skill list. `Training` is a graded column holding untrained, proficient or expertise, and each row says which ability it means, so the layout describes the system instead of repeating it eighteen times. ## Install Sheetsmith is in the [Obsidian community plugin list](https://community.obsidian.md/plugins/sheetsmith), so it installs from inside Obsidian. It needs Obsidian 1.9.0 or newer. 1. In **Settings → Community plugins**, select **Browse**, search for **Sheetsmith**, and install it. 2. Select **Enable**. 3. Open the command palette and run **Sheetsmith: Add a starter layout**. It writes one of the bundled layouts into your layout folder; nothing renders yet. 4. To see it as a sheet, create a note whose `sheet-layout` property names the layout, as in [How it works](#how-it-works) above, or run **Sheetsmith: Open layout editor** to see the layout filled with sample values. Do this in a new vault made for the purpose, not in one you care about. The plugin rewrites note bodies, and the parser has little mileage outside the author's own vaults. ### Layout files A layout is a file ending in `.sheetsmith`, kept in the layout folder (**Sheetsmith layouts** unless you change it in the plugin's settings). Layout files show in the file explorer and in Notebook Navigator, and selecting one opens it in the layout editor. Inside, it is plain JSON, so any text editor can open it too. Layouts made by earlier versions end in `.json`. They keep working. While any are left, Sheetsmith offers to convert them each time it loads, and the command **Sheetsmith: Convert JSON layout files** does the same. Update Sheetsmith on your other devices first: an older version reads only `.json` files, and it shows every layout as missing once the converted files sync. With the plugin disabled, or on a device that does not have it, layout files are hidden unless **Settings → Files and links → Detect all file extensions** is on. Where they do show, selecting one opens your operating system's app for it, or nothing on mobile. Notebook Navigator's **Documents** mode hides layout files whether or not the plugin is enabled. Its default **Supported** mode and **All** mode show them. ## Development ```bash npm install npm run dev # watch build npm run build # type-check and production build npm run lint npm test # run the test suite once npm run test:watch # re-run tests on change npm run harness # build the harness, then open harness/index.html ``` ### Test vault Develop against a throwaway vault, never a real one. Early builds rewrite note bodies, and the parser will get it wrong before it gets it right. ```bash ln -s /path/to/obsidian-sheetsmith /path/to/test-vault/.obsidian/plugins/sheetsmith touch /path/to/obsidian-sheetsmith/.hotreload ``` Install [Hot Reload](https://github.com/pjeby/hot-reload) in the test vault. Together with the `.hotreload` marker it reloads the plugin whenever `npm run dev` rewrites `main.js`, so there is no disable/enable cycle between builds. ### Testing The note parser and the formula engine import nothing from the Obsidian API, so they run under vitest without launching the app. The parser is also the one place where a bug destroys user data, so it is the part that carries the most tests. Round-tripping is the rule that matters most: parse then serialise must return an unchanged file byte for byte, or hand-edited notes drift on every save. `npm run harness` renders the sheet and the settings tab outside Obsidian against the real `styles.css`, in both themes and at any width. Appearance is reviewed by looking at it rather than by reading CSS. `main.js` is the compiled bundle and is deliberately not committed. Releases attach it alongside `manifest.json` and `styles.css`. ## License [MIT](LICENSE)