Dicey Table

The Topbar and Menu Bar

The top of Edit Mode is two rows, the same in every editor mode (Game, Room, Table, Asset and the Full Editor):

  1. The logo bar (<header className="em-topbar"> in TableEditModeShell.tsx): which mod you are in, whether your work is saved, and the three actions you use all the time — Save Draft, Preview and Publish.
  2. The menu bar under it (EditorMenuBar): File, Edit, View, Mod and Help, like a desktop application. Everything else lives here, including the actions that used to be buttons on the logo bar (Undo/Redo, Export, Mod Details, Collections, Asset Packs, Updates, Verify, Screenshots, Help and Exit). Save Draft, Preview and Publish are in the menus too.

The logo bar#

Left group — where am I?#

Element Symbol What it is
Dice mark and DiceyTable em-logo The DiceyTable logo mark and wordmark. Static, not a link. Editors that open in a tab (such as the advanced image editor) do not repeat it.
Mode pill em-mode-pill The editor mode — Game Editor, Room Editor, Table Editor, Asset Editor, or Edit in the Full Editor.
Project name and status em-project-name-btn "{project.displayName} · {project.status}", or No project loaded before a project opens. Click it to open File ▸ Open Project….
Read-only em-mode-pill-readonly Only when another tab owns this mod's draft. See One mod, one writing tab.

To change the mod's title you use Mod ▸ Mod Details…, below.

Right group — the actions#

The save-state pill, Save Draft, Preview (with its with / in room picker in the Room and Table Editors) and Publish. Each is described below.

The save-state pill#

A pill (em-save-pill) showing saveStateLabel, derived from saveState:

Pill saveState Meaning
Saved saved The draft on the server matches what is in front of you.
Dirty changes dirty You have unsaved edits to the scene, the manifest, the draft label, or an open editor tab that owns a backing file.
Invalid draft invalid Draft or scene validation is failing. This wins over dirty — an invalid draft is reported as invalid whether or not it is also unsaved.

"Dirty" is computed from hasUnsavedChanges, which compares three things against the copy loaded from the server — the draft label, the serialized scene document, and the canonical manifest text (canonicalised so whitespace and key-order differences do not read as an edit) — plus hasUnsavedItemTabs, which is true while a Deck, Model, File or Script tab holds edits that have not been written to its file.

That last term matters: those tabs write their own backing file (.deck.json, <model>.meta.json, the file's bytes) rather than going through the scene draft, so without it the pill could read Saved while a renamed deck sat unsaved in a tab — and Publish would then ship the previous file. Save Draft and Publish now flush every such tab first, and Publish refuses to run if one of them cannot save, naming it rather than publishing stale contents.

Whenever anything is wrong, the pill stops being inert text: it grows a badge counting the open errors (red) and warnings (amber), and clicking it opens the Problems panel with the full list — including failures the pill itself cannot express, such as a WIP checkpoint that could not reach GitHub. With nothing to report it stays a plain, non-interactive pill.

The same three signals are reported in more detail on the status bar.

Save Draft#

Writes your work back to the server. Disabled while busy, with no project loaded, or when there is nothing to save — neither draft changes nor an unsaved editor tab. Tooltip: "Save the draft (Ctrl+S)".

saveDraft does four things worth knowing:

  1. It flushes every dirty editor tab that owns a backing file — Deck, Model, File and Script. Those tabs do not go through the scene draft, so without this step their files would keep their previous contents while the pill reported Saved. A tab that fails to save is named in the result rather than passed over.
  2. It validates the scene document (editSceneSnapshotSchema.safeParse). If the parse fails it refuses to save and reports "Scene draft is invalid. Fix validation errors before saving." Nothing is sent.
  3. It PUTs the validated document — label, manifest text, the scene document as setupText, and editor metadata — to /api/editor/projects/{id}/draft.
  4. On success it re-baselines: the saved bytes become the new comparison point, so the pill returns to Saved and the dirty dots clear on open material tabs.

Material and Texture tabs are not in step 1, and do not need to be: their edits mutate the scene draft directly, so they are already inside step 3's payload.

Saving a draft is not publishing. It puts your work on the DiceyTable server as a draft; it does not push anything to GitHub. See Publish below.

Preview#

Full Editor (plain /editor), Game Editor, Room Editor and Table Editor. A Preview button with a play icon on the logo bar, directly before Publish, and File ▸ Preview in the menu bar. It lets you play your unpublished draft at a real table without creating a room or publishing anything. Tooltip: "Save the draft, then play it in a new tab — no room, no publish".

Clicking it does three things in order:

  1. Flushes every dirty editor tab that owns a backing file — the same gate Save Draft and Publish use. If a tab cannot save, Preview is not opened and the error names the tab, because the preview would otherwise show that file's previous contents.
  2. Saves the draft. If the save fails, nothing opens. The button reads Saving… meanwhile.
  3. Opens the preview tab at /table?preview=<projectId>. The tab is named per project, so a second click reuses (and reloads) the preview you already have instead of opening another.

In the Room Editor and Table Editor a small picker sits beside the button: with for a room pack, in room for a table pack. It chooses what your pack is previewed against, and it is a viewing choice only — it is never written into your pack and never published. It travels in the preview URL as &with=…, so the preview tab can be reloaded or shared with yourself and still show the same pairing.

  • A room may be previewed with the default (built-in) table, with any published table pack, with No table — useful for judging the room on its own; the physics table still exists, it is simply not drawn — or, under Games, with a whole published game set up in it.
  • A table is always previewed in a room: the default (built-in) room look, or any published room pack. There is no "no room" option, because a table floating in a void says nothing about scale, light or reach. A table is never previewed with a game — a game is what stands on a table, so the two would be two tables in one scene.

Picking a game is the same choice as picking a table, not a second one, so the two clear each other: a room has one thing standing in it. The game brings its own table, which is why the table options grey out of relevance — its table is part of the set-up you asked to see, and letting your table pick win would stand its pieces at a height it was never authored against. Choosing a game also changes the editor viewport, not just the preview tab: the game's pieces appear in the room you are authoring as locked scenery — not in the Hierarchy, not selectable, never saved into your pack. See Preview and photo mode.

An Asset Editor project has no Preview button: an asset pack is framed on a turntable in the editor viewport and has no scene to stand in.

If your browser blocks the new tab, the status line says so and an Open Preview link appears beside the button. A link click is always allowed, so clicking it opens the tab.

Preview is disabled while the tab is read-only (another tab holds this project's editor lease), with no project loaded, or while another operation is running.

What the preview tab loads, what it refuses, and why nothing in it is saved: Previewing a draft and photo mode.

Publish#

Opens the Publish to GitHub dialog, from the logo bar's Publish button or File ▸ Publish to GitHub…. Disabled with no project loaded.

Publishing is the step that makes a mod real for other people: files live in your GitHub repository, not on a DiceyTable server. The dialog itself, the OAuth connection and the wipmain model are documented in Publish to GitHub and The publish flow.

The menu bar#

Click a menu title to open it. While a menu is open, moving the pointer onto another title switches to it. From the keyboard, / move between menus, / between items, or Enter opens a submenu and closes it, and Enter or Space runs an item. Escape or a click anywhere else closes the menu. Escape only closes the menu; it does not also clear your selection.

The shortcut shown beside an item is the same key binding described on Keyboard shortcuts. The item and the key call the same handler. Items that do not apply right now are greyed out, and items for panels your editor mode does not have are not listed at all.

Notices appear at the right-hand end of the menu bar:

Notice When
Read-only Another tab owns this mod's draft. When that tab closes, it offers Take control (reloads)
This is a kind mod The mod is open in an editor its kind does not belong in. It offers a link to the right editor. Nothing redirects on its own
N pack updates A pack you reference has published a newer version. Opens the same list as Mod ▸ Pack Updates…

File#

Item Shortcut Does
New Mod… Opens the New Mod dialog — see Opening Projects
Open Project… Ctrl+O The project picker, with a folder per kind — see Opening Projects
Open Recent ▸ Your eight most recently updated other projects
Save Draft Ctrl+S Same as the Save Draft button
Preview Same as the Preview button. Not listed in the Asset Editor
Publish to GitHub… Same as the Publish button
Sync with GitHub… The GitHub sync dialog (conflicts, re-sync, reset). Greyed out until the mod is linked to a repository. The status bar's indicator opens the same dialog
Export Project JSON See Export
Preferences… Your own editor settings — currently the unit lengths are shown in. See Preferences and Display Units
Exit Editor See Exit

Export

Downloads a JSON snapshot of the project. exportProject fetches /api/editor/projects/{id}/export and saves it as {slug}.diceytable.json via a temporary object URL. It exports the server's copy, so anything you have not saved is not in the file.

Walkthrough: Export a project.

Exit

Leaves Edit Mode and returns to wherever you came from (the onExit callback the shell was mounted with). It does not prompt and it does not save — but leaving does not discard your draft either: the draft is on the server as of your last Save Draft, and the layout and undo history are flushed to localStorage on unmount.

There is a second Exit control, an icon button at the foot of the transform rail. Both call the same handler.

Edit#

Item Shortcut Does
Undo Ctrl+Z Undo the last scene change
Redo Ctrl+Shift+Z Redo. Ctrl+Y also works
Copy Ctrl+C Copy the selection
Paste Ctrl+V Paste what was copied
Duplicate Ctrl+D Duplicate the selection
Delete Del Delete the selection. A multi-selection asks once first
Rename F2 Focus the Inspector's Name field

These act on the scene, so they are greyed out while an item-editor tab (Deck, Model, File, Script…) is in front, the same way their keys are ignored there.

History is a snapshot history of the whole scene document plus the draft label, not a per-field command stack. A checkpoint is taken 450 ms after you stop changing things (serializeHistory behind a debounce), so one drag, or one burst of typing, becomes one undo step. The stack holds HISTORY_LIMIT = 50 steps and is persisted per user and per project in localStorage under diceytable.editor.history.{userId}.{projectId}, so it survives a reload. Because a checkpoint is a whole document, undo also reverses things that have no obvious "field" — a spawn, a delete, a reparent.

View#

Item Shortcut Does
Select / Move / Rotate / Scale Tool Q / W / E / R Switch the transform tool. The active one is ticked. Same as the transform rail
Frame Selection F Re-select the selection and re-attach the gizmo. The camera does not move yet — see Frame selection
Bake Static Lighting Bake the room and any static scenery into lightmaps, and capture a reflection probe. See Static lighting. Not listed in the Asset Editor
Auto Bake Re-bake by itself after a change that invalidates the bake. Ticked when on, and on by default. See Static lighting. Not listed in the Asset Editor
Problems The Problems panel. Greyed out when there is nothing to report
Screenshots… See Screenshots. Listed wherever Preview is

Static lighting#

A bake produces two things from the same inputs, which is why it is one action:

  • Lightmaps — the diffuse half. The room's surfaces, its decor, the table model and any model flagged Static scenery get their lighting burned into a second UV set. A mesh exported without TEXCOORD_1 is skipped, and the status line says how many were.
  • A reflection probe — the specular half. The room is rendered into a cubemap from just above the middle of the play surface, prefiltered, and published as the scene's environment. Every material picks it up automatically, so Metalness, Gloss and Reflectivity start reflecting the room the table is actually standing in. Without a bake there is nothing for them to reflect, which is why a metallic piece can look flat or black.

While it runs, the menu item shows Baking… and then what it did — for example 15 baked · probe 256² · 279ms. The same action is on the Environment inspector as Static lighting ▸ Bake in the Game and Room editors; the Table Editor reaches it only from this menu.

Auto Bake re-runs it for you after any change that makes the last bake wrong — a room edit, decor, lights, the table model. It waits for a pause before baking, so dragging a slider produces one bake and not one per frame, and it stands down while you are mid-drag or the tab is in the background. Turning it off keeps whatever is already baked; turning it back on bakes once straight away.

Three things worth knowing:

  • A bake is yours alone. Lightmaps and the probe are local render state — they are not saved into the mod, not written to the scene, and not sent to other players. Each client bakes its own.
  • A baked object is frozen. Move something after baking and its baked contact shadow stays where the object used to be. That is why only the room, the table and models you explicitly flag as static scenery are eligible.
  • It does nothing on the Low graphics tier, which captures no probe at all. The menu action still works; only the automatic re-bake stands down.

Mod#

Item Where Does
Mod Details… Every editor See Mod Details
New ▸ Every editor The asset explorer's create actions — Folder, Material, Material from Texture Maps…, Script, Deck, Board from Image… and Card Data — see The toolbar. Only the entries this editor offers are listed, and Card Data is listed only until the mod has card data
Open In ▸ Every editor, once a mod is open Switch this tab to another editor mode for this mod. Only the modes the mod's kind allows are listed — see Opening Projects
Collections… Game Editor See Collections
Browse Asset Packs… Game Editor Browse published Asset packs to reference or copy in — see Import an asset pack
Pack Updates… Game Editor Referenced packs and whether a newer version exists. Opening it changes nothing. When there are updates the item shows a count and the Mod title gets a dot
Verify as a New Player… Game Editor Re-fetch the published project cold — see Verify as a new player

Mod Details#

Mod ▸ Mod Details… opens the manifest editor — an item-editor tab named diceytable.mod.json, the same tab you get by double-clicking that file in the asset explorer. It is a form over the whole manifest: everything that ends up in diceytable.mod.json rather than in the scene is edited here, and nowhere else. Disabled with no project loaded.

The form is split into tabs by the question you arrive with. A tab with something worth seeing before you open it carries a count:

Tab Holds Count on the tab
Details How the pack is listed: title, Mod ID, version, public URL, summary, description, category, seat counts, tags, cover image, screenshots, and the performance budget for a room or an asset pack
Gameplay Entry points (the scene and the script, picked from the project's files so a path cannot be mistyped), Rules (the rulebooks players open from the table) and Lobby requirements Problems that would fail a publish
Files Every file in the project and what publishing does with it, plus Serves art from (outside asset repositories) — see the Files tab Declared files that do not exist
Plugins The plugins this pack uses, the functions its script may call, and its answers to their settings — see the Plugins tab. Absent for a room or a table pack, which run no script Plugins in the pack
Credits Where the pack's files came from and what it inherits from the packs it depends on Files with no credit recorded
File The manifest itself, as read-only JSON, so the file is never a black box

The red counts (Gameplay, Files) are things to fix; the others are information. Only the sections your pack's type can use are shown — a field the type cannot declare, left behind by a hand-written manifest, is listed above the tabs with the reason and a Remove button rather than silently hidden.

Closing the tab after making changes saves the draft and checkpoints it, the same way Save Draft does.

Once the project is published, a Published mod row under the URL field on the Details tab also offers Make private and Delete for the registered mod. These act on the server immediately, not through Save Draft. See Private and deleted mods.

The Details tab follows your pack's type. A game, a table, a room and an asset pack are not published the same way, so the fields and the copy differ:

type Category list Seat range Public URL
game-pack Game genres, mechanics and themes Players, min–max diceytable.com/games/<slug>
table-pack Table shapes and styles Seats, min–max diceytable.com/mods/<slug>
room-pack Room settings and styles diceytable.com/mods/<slug>
component-pack The kinds of part the pack carries diceytable.com/mods/<slug>

A room and an asset pack have no seat range at all, and that is a removal rather than an oversight: the range says how many people sit down to the thing, and a room seats nobody (the table in it does). /games/ is likewise reserved for game packs — it is the listing they are the only members of — so every other type is published under /mods/.

Mod ID is the pack's permanent identity — the value other mods and games use to refer to it, and the thing the library keys its record on. It is not the public web address; that is the URL field. It must be 3–96 characters, start and end with a letter or a digit, and otherwise contain only letters, digits, ., - and _. The editor picks one for you from the title when you create the pack; an id that does not meet the rule is called out in place, with a legal replacement one click away, and publishing is refused until it is fixed.

You can change it right up until you publish, and not afterwards. Once a pack is registered the field locks, because an id is not a label: it is what every game's room and table assignments pin, what collections list, and what the registry row is keyed by. Changing it then would not rename your pack — it would create a second one and leave the published one behind.

There is no way to free a published id, and deleting is not one: a deleted mod keeps its row as a tombstone precisely so its id stays reserved, and the project leaves your Projects rail with it. Publishing under a different id means starting a new mod. So it is worth reading the id once before your first publish.

Version is the one field on the Details tab that is not discovery metadata, and it is the one that decides whether a re-publish is accepted at all. A published version is immutable: the library refuses to repoint an already-published version at new bytes, so once you have published, changing anything means bumping the version before you publish again. Bump patch beside the field does the common case (1.0.01.0.1); type any semver value for anything else. A malformed version is listed alongside a missing cover in the pre-publish requirements rather than surfacing as a failed publish.

Edits made there are merged into the draft manifest, which means they are covered by the same Dirty changesSave Draft cycle as the scene.

Field-by-field reference: Manifest reference.

Mod Details: the Files tab#

The Files tab lists every file in the project and what publishing does with it. The list keeps itself up to date: adding, moving or deleting a file anywhere in the editor updates the manifest for you, because the manifest's assets is recomputed from the file tree on every save and again at publish. There is no checklist to maintain. The two things left to decide here are whether a game file is pre-loaded, and whether a file should exist at all.

Every file is in one of four states:

State Means
Pre-loaded Listed in assets. Fetched, size-checked and hashed when the pack is registered — so a broken file is a rejection you see at publish time — and downloaded by players before the table opens. Every table asset starts here automatically
On demand A game file you switched Pre-load off for. Saved in the manifest as excludedAssets. Still published and still works; it is fetched the first time something uses it, unverified. Worth it for a large file most sessions never touch
Entry The manifest, the scene and the script. Loaded first, on their own; chosen on the Gameplay tab. Cannot be deleted from here
Editor-only Thumbnails, original uploads, a deck's per-card source images, photo-mode captures, library link records, rulebook pages, and file types that are not table assets. Pushed to your repository so the project can be re-opened and re-edited, but never pre-loaded, so they can never be declared. Each row says which of these it is

Pre-loaded and On demand rows carry the Pre-load checkbox; that checkbox is the only manual edit to the file list that survives a save. A summary line above the list counts each state.

Finding a file. The list is a collapsible folder tree with a Filter by path… box, and five views, each with a count:

View Shows
Game files (default) What players download — pre-loaded, on-demand and entry files. Editor-only files are left out so several hundred thumbnails are not the first thing you scroll through
On demand Files you chose not to pre-load
Editor-only Thumbnails, originals, card sources and the rest of that state
Cleanup Leftovers, and files nothing seems to use — see below
Everything Every file in the project

Deleting. Any file except an entry file has a delete button on its row. It is a two-step inline confirm (Delete / Keep), and it goes through the editor's ordinary delete — the same one the asset explorer uses, so companion files and the manifest are kept in step. A whole-folder delete is offered only in the Cleanup and Editor-only views; in Game files it would be one slip from deleting assets/.

Cleanup separates what is certain from what is a guess:

  • Leftovers are certain: the card images and sheets of a deck that was deleted, and thumbnails whose source file no longer exists. Nothing uses them, but they are still published with your pack. Delete N leftover files removes them all, after the same confirm.
  • Scan for unused files is a best guess, and the panel says so. It reads your scene (including edits you have not saved yet), decks, scripts and glTF models, and lists the images, models and sounds that none of them name. It only judges binary assets — JSON and text files are loaded by convention too often for a text search to see — and it never judges media/, the folder of a deck that still exists, an entry file or an editor-only file. A file your script loads by a name it builds at runtime will look unused, so check before deleting. Flagged rows read No reference found.

Where are my materials? Materials, prefabs and table scripts are not files. They are stored inside the scene (setup.json) and ship with it, so they never appear in this list — only the textures a material uses do.

If the manifest declares a path that does not exist, an alert at the top of the tab lists it. Saving the draft clears a stale assets entry on its own (Remove does it immediately); a missing entry point is never removed for you — fix that one on the Gameplay tab.

Below the list, Serves art from declares the outside GitHub repositories the pack reads art from — see Art in somebody else's repository.

Mod Details: the Plugins tab#

Everything the pack has to do with plugins is one list. (Attaching a plugin and answering its settings used to be two sections on two surfaces, which read as two different plugin systems.)

  • In this pack (N) — one card per plugin the pack uses. A card shows the plugin's capability badge, Functions this game may call (a checkbox per function; these become the manifest's plugins and keep the plugin-call capability in step), a Settings form built from what the plugin declares (your answers become pluginSettings), and Details, which shows the plugin's attribution, the origins it reaches and a ready-to-paste api.callPlugin snippet. One Remove drops the attachment and the settings in a single write.
  • Plugin registry — search registered plugins that are not in the pack yet. Add to game attaches the plugin with its callable functions ticked. Configure only (offered when the plugin declares settings) records settings without attaching it, for a plugin used as a deck source that your script never calls; the card then reads Configured only — this pack's script cannot call it, with Add it to the game beside it.

Adding a plugin here does not switch it on at a table — a table's owner still chooses that in Room & table. How the call itself works: Calling a plugin from a mod.

Collections#

Game Editor only (Mod ▸ Collections…). Opens the Collections dialog, where a collection — a named bundle of mods that share one table — names the single member that supplies the room and the table. Every other member's room and table are ignored, and the dialog marks each of them so.

It is absent from the pre-mode editor (/editor) entirely, along with the registry request behind it.

Full walkthrough, including the four outcomes and why the provider is never chosen for you: Collections: which mod supplies the room and the table.

Screenshots#

View ▸ Screenshots…, listed wherever Preview is (so Room and Table packs get a screenshots gallery too). It opens the Screenshots gallery: every capture you took in Preview's photo mode, saved under the mod's screenshots/ folder. Disabled with no project loaded.

Help#

Item Opens
Developer Docs /docs
Editor Guide /docs/editor
Scripting API /docs/scripting-api
Keyboard Shortcuts /docs/editor/keyboard-shortcuts
Menus & Topbar This page

Every item is a real link that opens in a new tab (target="_blank", rel="noopener noreferrer"), so reading the docs can never navigate away from the tab holding your unsaved draft.

What the topbar and menus write#

Control Written where
Edit ▸ Undo / Redo The whole scene draft and the draft label, in memory; the history stack to localStorage
Edit ▸ Copy / Paste / Duplicate / Delete / Rename The scene draft, in memory until Save Draft
Save Draft The server draft (PUT /api/editor/projects/{id}/draft)
File ▸ Export Project JSON Nothing — read-only download
File ▸ New Mod… / Open Project… See Opening Projects
File ▸ Preferences… Your per-user preferences (editorUnits), plus a localStorage mirror. Never the mod — see Preferences and Display Units
Mod ▸ Mod Details… The draft manifest text, in memory until Save Draft (closing the tab saves). Deleting a file from the Files tab deletes it from the local draft tree
Preview The server draft (it saves first). The preview tab itself writes nothing to the draft
View ▸ Screenshots… screenshots/ and media/ in the local draft tree, depending on the action — see Screenshots
Publish Your GitHub repository, via the publish flow
View ▸ Auto Bake Your per-user preferences (editorBake), plus a localStorage mirror. Never the mod
View (tools, Frame Selection, Bake Static Lighting, Problems), Help, Exit Nothing