v0.19.0
2026-04-30
- [x] Hosted-module fixture seeding silently drops
prop_typesandproperty_definitionsrows when their IDs collide with rows from a previously-seeded system. (Discovered in prod when system8 had 0 of 4 prop_types and 0 of 10 property_definitions seeded — IDs likehp,ac,str,number,string,ability-scoreall already existed underdnd5e-2014.) FixseedSystemFixtureinpackages/server-core/src/scripts/seedSystems.tsto namespace these IDs by systemId (e.g.system8_hp) the same way it already does forbars,light_source_types,vision_types, etc. Will require either bumping fixture versions and re-seeding, or migrating existing dnd5e rows to the namespaced IDs (and updating any FK references). A quick data patch was already applied to prod for system8 in v0.18.x; this TODO is the durable code fix. - [x] Long-term schema fix: change the primary key of
prop_typesandproperty_definitionsfrom(id)to(id, system_id)(composite PK), so two systems can legitimately define the same property id. Audit all FK references and lookup code that assumes id is globally unique. (More invasive than the namespacing fix above; consider deferring until needed.) - [x] Hosted modules with peer dependencies (e.g. system8's
express,@purple-cat/shared) only resolve at runtime because the Dockerfile manually symlinks them into the top-level/app/node_modules. Switch topublic-hoist-pattern[]=entries in.npmrc(or otherwise hoist published peer-dep packages automatically) so adding a new hosted module doesn't require Dockerfile changes for each new peer dep. - [x] Slack deploy notification step in the platform CI pipeline broke after commit
b94bfc3(fix(ci): don't run npm run build on installed hosted modules) because backticks in the commit message are interpolated when the shell builds the Slack JSON payload. Escape or otherwise sanitize commit messages before passing them into the Slack webhook step. - [x] Add labels such as "drag a source image here to create a token" in the token and portrait drop zone of the character art UI.
- [x] In the Share Character dialog, separate the Known User invitation into its own tab. Add a small hyperlink on the default tab that says "or, invite an existing user"
- [x] Background token LOD isn't working. I'm zoomed all the way in and I'm seeing a low-res image.
- This applies only if I Create Token > Set layer to Background > upload source art > drag to token drop zone. If I drag the source image onto the map and create the token that way, the high-res image appears.
- [/] Create Token > Set layer to Background > upload source art > drag to token drop zone. Unaltered image appears in thumbnail (correct). If I add token, the unaltered image will correctly be used. However, if I edit tokenization, Unaltered is not selected, but rather Tokenized. No matter what I do in this drawer, the token has a tokenized image on the map going forward.
- [/] When a background token is added to the map, intelligently size it based on the dimensions of the source art at max resolution. For example, if the source art is 500x500, and the grid size is 100, the token should be added at 5x5 squares. Don't change aspect ratio, as background images don't have to snap to the grid.
- This appears to already be happening when dragging the image from outside the browser into the app. But it doesn't happen when you Create Token > Set layer to Background > upload source art > drag to token drop zone.
- [x] If I have mo're than one source art for a token, there is a concept of "selected" source art. Regardless of what appears in the token drop zone thumbnail, the currently selected source art is used on the map. This is a vestige of a previous way artwork was handled. There should be no concept of "selected" source art; the thumbnail in the token drop zone should always match what's on the map.
- [x] Add an option when rolling initiative to group all copy/pasted tokens (e.g. Bandit 1, Bandit 2, etc) into a single initiative entry. This can be done after rolling, so there's no need to add a prompt before rolling. The initiative entry for grouped tokens should show the number of tokens in the group (e.g. "Bandit x3"), and when you click on the entry, it should expand to show each individual token and its HP, and allow you to select each one separately if needed (e.g. to apply damage to one token in the group but not the others).
- [x] On mobile, add a new option to the bottom tab: Actions. This pops up a modal with a list of all available actions for the currently impersonated token.
- [x] The tooltip on the "group" button in the Initiative Tracker is behind the chat log in terms of z-index and needs to be above it.
- [x] Show token's name on hover, centered above the token on the map. The old hover card had this information, but it's hidden now.
- [x] Add a property to System and Campaign property definitions called "show_on_hover", which if true will show the property value beneath the token name when hovering over the token. Default false. In fixtures, make HP and AC show on hover.
- [x] Ensure that 3d dice show for all users, not just the roller. The roller's theme should be used.
- [x] Update Action PATCH request contains the entire token. It should just contain the portion that's changing (i.e. the action).
- [x] Changing the system of a campaign in Edit Campaign doesn't import inherited properties, bars, etc
- [x] Numerator and Denominator fields in the Bar edit UI are not usable. The options don't appear, probably because of z-index (but maybe I had no properties in that campaign--in which case notify the user).
- [x] Change prod CI to trigger a manual backup of the DB before anything is pushed
- [x] HP slider in CurrentTokenBar is janky. May need to throttle updates.
- [x] Sort conditions alphabetically in all places they appear
- [x] In Inflict Damage and Heal, accept a mathematical expression in the number field (e.g. "32-14").
- [x] When editing action code, or any other modal field, suppress map hotkeys such as "h".
- [x] Make 'dying' state happen automatically at 0 hp
- [x] shift-clicking multiple tokens should select them all
- [x] roll() is not awaiting the 3d die roll, so the results in chat say things like "I hit a
- [x] in the Create Token modal, add an option to add multiple copies of the same token all at once, with a specified number of copies. They will have names like ${name} 1, ${name} 2, etc. The tokens will spawn in their own squares, rather than stacked on top of each other.
- [x] The UI shown when holding space and mousing over the map should be visible to the user holding space, as well as all other users. Also, the on-hover UI should show the user's name, and the UI should use their chat color.
- [x] Generate Encounter: a new option alongside Generate Monster and similar options.
- [x] Generate Encounter is an option in the map context menu, the + menu in the palette, the token menu in PlayTopBar, and the command palette (cmd+shift+p).
- [x] It has two tabs: Algorithm and AI.
- [x] Algorithm tab:
- Asks for overall encounter CR and preferences such as: all one type, varied types, swarm of one type, etc.
- The above options determine the number of individual monsters, their CRs, and their types. Once this is known, the existing Generate Monster algorithm can create them.
- The UI will show a list such as:
- 3 goblin brutes (CR 3)
- 7 goblin minions (CR 1/4)
- 1 goblin boss (CR 5)
- Buttons: insert these monsters, regenerate, or cancel
- [x] AI tab: a single text area to describe the preferences for the encounter, with CR being mandatory. The prompt will explain that multiple creatures should be created, with their total CR matching the specified CR, and that the response should be in a specific JSON format that the system can parse to create the monsters. The UI will show the parsed response with the same buttons as the Algorithm tab.
- After the AI generates the encounter, show the same UI listing the quantity, type, and CR of monsters, with the same 3 buttons
- [x] Artwork:
- In the list of monsters to be generated, show a small thumbnail which can be clicked to edit, bringing up the Edit Source Art drawer used by tokens. There is also a pencil icon which, when clicked, brings up the Tokenization drawer.
- To the right of that thumbnail is a portrait thumbnail, which works similarly.
- There is also an "auto select art", which will fire off a google search for each of the generated monster names, and pick the first result. When this option is used, add a caret next to the thumbnails which shows all the other search results. Clicking one replaces the source art for both, for that particular monster.
- [x] Add a "faction" property to Tokens, at the data, api, and UI level.
- Valid values:
- Party member
- Hostile
- Neutral
- Noncombatant
- This selector should be visible in the Create/Edit Token modal, the CurrentTokenBar, and the token context menu.
- The only business logic changes are:
- Noncombatants do not roll initiative or appear in the initiative list
- The default token outline color for each faction is:
- Party member: green
- Hostile: red
- Neutral: yellow
- Other UI changes:
- On hover, the token's name appears above the token. Show the faction as well, below the name, in parentheses.
- Valid values:
- [x] Export Map: an existing action that is currently unimplemented. Implement as follows:
- Serialize the map data, and all tokens on the map, into YML
- Package the YML and all source images for tokens into a ZIP file
- Download the ZIP file to the user's device
- [x] Import Map: implement as follows:
- A button on /campaigns/:id offers the option to import a map. This accepts a ZIP file in the same format as the Export Map output.
- The existing Import Map action in PlayTopBar and the Command Palette has the same behavior, creating a new map and switching to it immediately after import.
- If possible, unzip the package in-browser and upload images directly to S3 from the browser (if using S3). This may or may not be possible, as browser restrictions might limit the size of image files, some of which could be tens of MB.
- [x] draw(): a new function available to actions.
- inputs: shape(circle, square, cone, etc), origin(x,y relative to token), size (radius for circle, width and height for square, etc), duration (in rounds), fill color, stroke color, stroke thickness
- when invoked, it makes a drawing using the existing drawing functionality, with the specified parameters.