# Randomness An [Obsidian](https://obsidian.md) plugin for IPP3-compatible random generators. Roll on tables inline with `` `rdm:[@Names]` ``, embed full generators in ````randomness```` codeblocks, and re-use existing `.ipt` files from twenty years of the [Inspiration Pad](https://nbos.com/products/inspiration-pad) ecosystem. ## Features - **`randomness` codeblocks** β€” embed a generator directly in a note. Rolls every render; supports the full IPP3 grammar including weighted tables, lookup tables, deck picks, conditionals, dice, expressions, and 21 filters. - **Inline `rdm:` calls** β€” one-shot rolls scattered through your prose. Preview first, then click πŸ”’ to commit the result as `` `rdm:[@Names]⟹Alice` `` β€” the lock survives reloads, syncs, and reopening the vault. Click 🎲 to re-roll. - **`Use:` other files** β€” share table libraries across notes. Reference `.ipt` files or `.md` notes containing `randomness` codeblocks; resolution follows the calling note's folder first, then a configurable generator root. - **Prompts** β€” generators that declare `Prompt:` controls render dropdowns or text inputs above the output; changing a value re-rolls with the new prompt set. - **Deterministic when you want it** β€” every codeblock can be configured to use a stable seed (off by default), so the same source at the same location produces the same roll on every render. Locks remain the strongest guarantee. - **Generator browser pane** β€” a right-sidebar view that displays every `.ipt` file in your configured Generator root (or whole vault) as a collapsible folder tree mirroring your vault's structure. Click a folder or file chevron to expand; click any table's **Roll** button to generate a result, or **πŸ“‹** to copy an inline `rdm:` reference for that table to your clipboard (paste into prose; the Notice shows the `Use:` line you'll need to add to your note). Click the result body to copy the rendered text. The tree starts fully collapsed and remembers what you expand across sessions. A "Collapse all" button resets the tree without clearing your search filter, so collapse-then-filter is a fast way to find a specific generator in a deep folder hierarchy. Open via the dice ribbon icon or the "Open generator browser" command. - **Existing `.ipt` files work as-is.** The engine survives the full AddCommas/Random-Treasure-CR1-CR30 stress test from the NBOS corpus. - **JavaScript API** β€” roll generators from other plugins or Templater scripts via `app.plugins.plugins["randomness"].api`. Scoped and unscoped rolls, prompt overrides, deterministic seeds, and a roll event stream. Ideal for generating notes from a shared generator library. See [API.md](API.md). ## Install ### Via BRAT (recommended while in beta) 1. Install the [BRAT](https://github.com/TfTHacker/obsidian42-brat) plugin if you don't have it. 2. Open BRAT settings β†’ Add Beta Plugin β†’ enter this repo's URL. 3. Enable "Randomness" under Community Plugins. ### Manually 1. Download `main.js`, `manifest.json` (and `styles.css` if present) from the latest [release](../../releases). 2. Copy them into `.obsidian/plugins/randomness/` inside your vault. 3. Enable "Randomness" under Settings β†’ Community Plugins. ## Usage ### Codeblocks ````markdown ```randomness Table: Settlement Riverbend Stonewatch Greenhollow ``` ```` Renders to one of "Riverbend", "Stonewatch", or "Greenhollow", chosen at random each time the codeblock renders. Use the full IPP3 grammar β€” multiple tables, weighted entries, lookup tables, `Set:`/`Define:`, prompts, conditionals, dice expressions, filters, the lot: ````markdown ```randomness Prompt: Tier {Easy|Normal|Hard}Normal Table: Encounter 1: A single goblin scout. 2: [@Group] goblins. 6: A goblin chieftain with [1d4+2] {$prompt1} guards. Table: Group 1-3: small group of {2d4} 4-6: warband of {3d6} ``` ```` The dropdown for `Tier` appears above the result; changing it re-rolls. ### Inline `rdm:` Anywhere in your prose, wrap an expression in backticks with the `rdm:` prefix: ```markdown The shopkeeper, a `rdm:[@Names]` from `rdm:[@Origin]`, eyed me suspiciously. ``` Each `` `rdm:...` `` renders inline with a preview, plus πŸ”’ (lock) and 🎲 (re-roll) buttons. Clicking πŸ”’ rewrites the underlying text to include the chosen result: ```markdown The shopkeeper, a `rdm:[@Names]⟹Tessith Vone` from `rdm:[@Origin]⟹Coppertown`, eyed me suspiciously. ``` The lock survives reloads, sync, and reopening the vault. To re-roll a locked call, click 🎲 β€” it strips the lock and shows a fresh preview. The expression's scope sees same-note `randomness` codeblocks plus any `Use:` declarations from those blocks, so you can keep table definitions alongside the prose that uses them. ### Sharing tables across notes In a shared `.ipt` file (e.g. `Generators/common-names.ipt`): ``` Table: Names Tessith Vone Korad the Blue Mira Thornhaven ``` In any note's codeblock: ````markdown ```randomness Use:common-names.ipt Table: NPC {1d2=1, A man named, A woman named} [@Names]. ``` ```` `Use:` paths resolve relative to the current note's folder first, then relative to the **Generator root** configured in Settings β†’ Randomness. ### Commands - **Lock all unfilled `rdm:` in current note** β€” evaluates every unfilled inline call (using cached previews where available, fresh evaluations otherwise) and writes all locks in one atomic save. - **Reroll all `rdm:` in current note** β€” strips every lock and clears cached previews. The next render shows fresh previews everywhere. - **Rebuild generator index** β€” rescans the vault for `.ipt` generators. Run this after adding or renaming generator files if the JS API's `rollUnscoped` / bare-filename `Use:` resolution looks stale. ## Scripting: the JS API Randomness exposes a JavaScript API for other plugins and for [Templater](https://github.com/SilentVoid13/Templater) scripts, so you can roll generators from code β€” for example, to populate a freshly created note from a shared generator library. ```js const api = app.plugins.plugins["randomness"].api; // Roll a generator found anywhere in the vault (ignores note scope): const r = await api.rollUnscoped("VillainName"); console.log(r.result); // -> "Mordred the Pale" ``` The two rolls you'll use most: - **`roll(name, opts?)`** β€” rolls a table **in note scope** (sees the note's same-note codeblocks and `Use:` imports). Use it when rolling from the context of a specific note. - **`rollUnscoped(name, opts?)`** β€” rolls a table found **anywhere in the vault**, ignoring scope. Use it for automation and note generation, where there's no scope wired up yet β€” e.g. a Templater template that builds a note from your shared generators. Both accept `promptValues` (override a generator's prompts by label) and `seed` (deterministic rolls). `rollUnscoped` also accepts `filePath` to disambiguate when two files define the same table name. ```js // Pass values into a generator's prompts, by label: const inn = await api.rollUnscoped("TF-Inn", { promptValues: { town: "Frostkey", shopName: "The Salty Anchor" }, }); ``` **Full reference, including every method, option, the `RollResult` shape, collision handling, and recipes: see [API.md](API.md).** ## Settings - **Generator root** β€” vault-relative folder used as the fallback for `Use:` paths that don't resolve next to the calling note. - **Default formatting** β€” `HTML (rich)` to enable bold/italic/list filters as visual formatting; `Plain text` to keep them as plain characters. Individual generators can override via the `Formatting:` directive. - **Stable codeblock seeds** β€” when on, codeblocks render the same result across reloads (until you reroll). Useful for keeping a generator "settled" without committing to a specific lock. Off by default. ## Where inline `rdm:` calls work Inline `rdm:` calls render in **Reading view** only. Obsidian's **Live Preview** uses a different rendering pipeline (CodeMirror 6 extensions, not markdown post-processors), and the plugin doesn't have a CM6 extension yet. In Live Preview the inline calls show as plain code spans β€” locks in the source survive, but the πŸ”’/🎲 buttons don't appear. Workflow recommendation: author your prose in Live Preview, switch to Reading view (Ctrl/Cmd-E or the read-eye icon) to roll/lock inline calls. Locks written from Reading view show up in Live Preview's underlying source immediately. Codeblock generators (`` ```randomness ``) work in both views β€” those use the codeblock processor, which Live Preview does handle. ## Security The plugin's HTML output passes through a tag whitelist before being attached to the DOM. Allowed tags: structural (p, div, ul, ol, li, hr, blockquote, pre, h1–h6), inline formatting (b, i, u, em, strong, s, code, br, span, and a few others), and tables. All attributes are stripped. Anything outside the whitelist β€” `