Story Web

by thewintershadow
5
4
3
2
1
Score: 50/100

Description

Obsidian plugin: capture plot points and campaign notes as plain markdown, then connect and group them on an interactive graph.

Reviews

No reviews yet.

Stats

0
stars
59
downloads
0
forks
10
days
7
days
7
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
0
total issues
0
open issues
0
closed issues
9
commits

README file from

Github

Story Web

An Obsidian plugin for quickly capturing plot points and campaign notes. Each one is a plain markdown note. Afterwards you can arrange, connect, and group them on an interactive graph.

  • Quick capture: type a title and a one-line blurb, and you're done. Picking a group is optional and doesn't slow that down.
  • Card graph: each note is a card showing its blurb, wrapped to a few lines (configurable). A coloured stripe marks its type. Click or tap a card to open the real note. Hovering a card highlights what it's connected to.
  • Manual connections: in connect mode, tap a source note and then a target note. [[wikilinks]] in note bodies are shown automatically as dashed edges.
  • Groups are folders: a note's group is just the subfolder it's in. Subfolders can nest as deep as you like — Plots/Act 1/Heist/ shows up as a box inside a box. Moving, renaming or ungrouping a note through the graph moves or renames the real file/folder.
  • Desktop and mobile: built for iPad as well as desktop, including pinch-zoom, pan, drag, tap, and long-press.
  • No lock-in: everything else is stored in YAML frontmatter. If you disable the plugin, your notes are still ordinary markdown in ordinary folders.

Note format

Notes live in one configurable folder (default Plots/), in any arrangement of subfolders you like — a subfolder is a group, so Plots/Act 1/Heist/The blueprints.md shows up as a card in a "Heist" box nested inside an "Act 1" box. A note directly in Plots/ is ungrouped.

---
blurb: "The vault is already empty when the crew breaks in"   # node label; falls back to the filename
type: fiction                                                 # optional; the graph can filter by it
connects_to: ["[[Someone got there first]]"]                  # optional; manual edges, as wikilinks
x: 120                                                        # written by the plugin when you move a node
y: 340
---

The note body is yours. The plugin never modifies it.

Every field is optional. A note with no frontmatter still shows up, labelled with its filename. type can be any value you like, for example fiction, campaign, or worldbuilding.

Upgrading from a version before groups were folders? The old group: frontmatter field is no longer read. Move those notes into a matching subfolder (drag them in Obsidian's file explorer, or use "Move to group…" on the graph) and delete the now-unused group: line whenever you're touching that note next — it's otherwise harmless to leave behind.

Using it

Action Desktop Touch (iPad)
Open the graph Ribbon icon or Story Web: Open graph same
New plot point + New on the graph, Story Web: New plot point (bind a hotkey in Settings → Hotkeys), or the lightbulb ribbon icon same
Open a note Click (Cmd/Ctrl-click opens a new tab) Tap
Move a node Drag Drag
Pan / zoom Drag background / scroll wheel, or the zoom buttons bottom right One-finger drag / pinch, or the zoom buttons
Fit everything in view The frame button under the zoom buttons same
Connect Connect (top right), then click the note to start from, then the note it leads to same, with taps. Or long-press a note → Connect from here…
Delete a manual connection Select the edge, then press Delete, or right-click it Long-press the edge
Add/move to a group Right-click a note → Add to group… / Move to group… — moves the file Long-press a note
Remove from its group Right-click a note → Remove from "…" — moves the file back to the root Long-press a note
Rename a group Right-click the group box → Rename group… — renames the folder Long-press the group box
Ungroup Right-click the group box → Ungroup — moves its contents up one level, deletes the folder Long-press the group box
Leave connect mode Done (the Connect button while active), or Esc Done

Clicking a note reuses an open note pane when there is one, so the graph stays put. A good setup is to keep the graph in one split and your notes in the other.

The first time you open the graph, notes without a position are laid out automatically, and those positions are saved. After that, new notes appear in the middle of what you're looking at, or next to their group.

Settings

  • Folder: where the notes live. It can't be the vault root, because the plugin writes positions into every note it lays out and every subfolder inside it becomes a renameable, ungroupable group.
  • Default type: the type added to notes created with quick capture.
  • Lines per card: how many lines of blurb each card shows (1–6, default 3). Longer blurbs end in "…". Set it to 1 for compact one-line cards.
  • Open note after capture: off by default, so capture doesn't interrupt what you're doing.

Development

npm install
npm run dev      # watch build; also copies into test-vault/.obsidian/plugins/story-web
npm run lint     # the same eslint-plugin-obsidianmd rules the community directory's review runs
npm test         # unit tests (pure model + frontmatter logic)
npm run build    # typecheck + production bundle → main.js

Open test-vault/ as a vault in Obsidian, enable community plugins, and Story Web loads with some sample notes. If you also install the Hot Reload plugin in that vault, it reloads Story Web on every rebuild.

Don't point dev builds at a vault you care about until you've tested them. Use STORY_WEB_VAULT_PLUGIN_DIR to change where dev builds are copied.

Releasing

npm version patch   # or minor / major: bumps package.json, manifest.json, versions.json and tags (no "v" prefix)
git push --follow-tags

The Release workflow builds from the tag and publishes a GitHub release with main.js, manifest.json and styles.css attached, which is what Obsidian installs from. It refuses to publish if the tag doesn't match manifest.json. Each release also gets a signed build-provenance attestation, so anyone can check the files were built from this repo by CI:

gh attestation verify main.js --repo TheWinterShadow/story-web

Testing on iPad

Copy main.js, manifest.json, and styles.css into <vault>/.obsidian/plugins/story-web/ on a vault that syncs to the iPad, then enable the plugin there. Or install it with BRAT (a plugin for installing betas from GitHub) using TheWinterShadow/story-web.

Layout

src/
  main.ts         plugin entry: commands, ribbon, settings, view registration
  view.ts         ItemView + Cytoscape: rendering, diffing, gestures, menus
  store.ts        the only module that touches the vault (reads cache, writes frontmatter, moves/renames files for groups)
  model.ts        pure: notes → nodes/edges/group tree (+ type filter)
  frontmatter.ts  pure: frontmatter mutations used inside processFrontMatter
  folders.ts      pure: group-path arithmetic (a group is a folder — see model.ts + store.ts)
  links.ts        pure: parsing `connects_to` wikilinks
  theme.ts        visual design: theme colours → Cytoscape stylesheet, type colours
  wrap.ts         pure: word-wrapping blurbs into card lines
  layout.ts       pure: initial column layout
  modals.ts       quick capture + text prompt
  settings.ts     settings + tab
tests/            vitest suites for the pure modules
docs/DECISIONS.md why things are the way they are

The view and store depend on Obsidian and can only be tested manually in the test vault. Everything that decides what to render or what to write is kept in the pure modules, so it can be unit tested.