Omarchy shell plugin: RPG dice roller (standard, Fate, and custom dice)
  • QML 74%
  • JavaScript 26%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Christopher Rueber 4a03317286 Harden state read and write against FIFO, symlink, and TOCTOU attacks
The stateLoader Process stat-gated then cat'd the path — two separate
resolutions with a window in between where state.json could be swapped
for a FIFO (hanging the read) or a symlink (redirecting it). saveState's
fixed-path `>` redirection followed a symlink at the destination and
truncated its target.

Reads now go through a single dd open (iflag=nofollow,nonblock),
byte-capped at MAX_STATE_BYTES + 1 and rejected past the cap, wrapped in
a timeout deadline. Writes mktemp a file in the state dir and mv -f's it
over state.json, so the rename replaces a symlink instead of following
it. AGENTS.md and README now prescribe the hardened pattern.
2026-08-22 19:12:44 -05:00
assets Add RPG dice roller plugin 2026-08-22 08:00:17 -05:00
AGENTS.md Harden state read and write against FIFO, symlink, and TOCTOU attacks 2026-08-22 19:12:44 -05:00
BarWidget.qml Refine dice panel UX and results 2026-08-22 08:10:38 -05:00
LICENSE Add README, screenshot, and MIT license 2026-08-22 08:15:40 -05:00
manifest.json Add RPG dice roller plugin 2026-08-22 08:00:17 -05:00
Model.js Harden state read and write against FIFO, symlink, and TOCTOU attacks 2026-08-22 19:12:44 -05:00
Panel.qml Harden state read and write against FIFO, symlink, and TOCTOU attacks 2026-08-22 19:12:44 -05:00
preview.png Update panel screenshot 2026-08-22 08:19:48 -05:00
README.md Harden state read and write against FIFO, symlink, and TOCTOU attacks 2026-08-22 19:12:44 -05:00

RPG Dice

An Omarchy shell plugin that rolls tabletop RPG dice from the status bar — standard polyhedral dice, Fate dice, and your own custom dice, with an optional roll sound.

RPG Dice panel

Features

  • Standard dice — d4, d6, d8, d10, d12, d20, d% (d100).
  • Fate dice (dF) — six faces: two -, two blank, two +.
  • Custom dice — numeric (11,000,000 sides) or explicit sides (any labels, e.g. heads, tails or 1, 2, sword, shield).
  • Three-section panel — Settings, Dice, Results.
  • Debounced rolling — click any number of dice, then 1.5s after your last click everything rolls together and groups by type (d6: 2, 5, 3 d8: 8).
  • Sound — toggleable roll sound with adjustable volume.
  • Persistent state — sound, volume, and custom dice survive restarts.

Installation

omarchy plugin add ssh://git@git.packden.us:2288/crueber/omarchy-plugin-rpgdice.git --enable

Or clone manually:

git clone ssh://git@git.packden.us:2288/crueber/omarchy-plugin-rpgdice.git \
  ~/.config/omarchy/plugins/crueber.rpgdice
omarchy-shell shell rescanPlugins
omarchy plugin enable crueber.rpgdice center

Removal

omarchy plugin remove crueber.rpgdice --yes

This disables the widget, deletes the plugin, and rescans the shell. Because the plugin is a git checkout, the directory is removed outright (the source stays upstream). Omit --yes to be prompted for confirmation.

To keep it installed but hide it from the bar instead:

omarchy plugin disable crueber.rpgdice

Settings are kept under ~/.local/state/omarchy/rpgdice/state.json, so reinstalling preserves your custom dice.

Usage

Click the d20 icon in the bar to open the panel.

  • Roll — click one or more dice buttons. The panel waits 1.5s after your last click, then rolls everything together. Results group identical dice: d6: 3, 4, 5, joined per type for mixed rolls.
  • Custom dice — expand Add custom die in Settings. Pick Numeric and enter a side count (11,000,000), or Explicit sides and enter comma-separated labels, then Add die. Custom dice appear as roll buttons in the Dice section.
  • Sound — toggle it and adjust volume in Settings.

Configuration

The bar icon is overridable via the widget's entry in ~/.config/omarchy/shell.json (the default is the d20 glyph, U+F1155):

{ "id": "crueber.rpgdice", "icon": "d20" }

Development

The shell hot-reloads on save, so changes under ~/.config/omarchy/plugins/crueber.rpgdice/ apply automatically. If a change fails to land, force a reload:

omarchy-shell shell rescanPlugins
omarchy restart shell

Layout:

File Purpose
manifest.json Plugin manifest (id, kinds, entry points, widget metadata)
BarWidget.qml Bar pill (d20 icon), panel lifecycle, and IPC
Panel.qml Popup panel: Settings/Dice/Results sections, roll debounce, sound, persistence
Model.js Pure dice logic and state (de)serialization — no QML imports
assets/dice-roll.wav Bundled roll sound

Where the logic lives:

  • Dice modelSTANDARD_DICE and FATE_DIE in Model.js. Add a new built-in die by appending to STANDARD_DICE; the Dice grid picks it up automatically.
  • RollingModel.rollDie(die) returns { label, display, value }. Fate and explicit-sides dice are non-additive (value: null); only numeric dice contribute to the group total in Panel.qml:rollAll().
  • DebouncePanel.qml's debounceTimer (1.5s) restarts on each queueDie() and rolls the accumulated pending list on timeout.
  • Persistence — state is JSON under ~/.local/state/omarchy/rpgdice/state.json, read with a size-bounded Process (symlink- and FIFO-safe) and written atomically via Util.execDetached.

Validate before publishing:

omarchy plugin validate ~/.config/omarchy/plugins/crueber.rpgdice

License

MIT