Configuration & Filters
entities.json says what types exist and forms/ and views/ say how they are edited and shown.
config.json says how the app behaves for them, and filters.json says how the library
browses them.
config.json
Section titled “config.json”Per-entity configuration
Section titled “Per-entity configuration”The entities block is keyed by entity name, with a Default block applying to all of them:
{ "entities": { "Default": { "combatant": { "bars": [ { "attribute": "data.hp", "title": "Common.HitPoints", "label": "Common.HP", "temporary": true } ] } }, "Monster": { "collection": { "detail": "{{data.size|map: 'Size'}}{{data.type|map: 'MonsterType'|prefix: ' '}}{{data.alignment|map: 'Alignment'|prefix: ', '}}" }, "combatant": { "detail": "{{'Common.AC'|l}} {{data.armor}} • {{'Common.PP'|l}} {{data.passivePerception}} • {{'Common.CR'|l}} {{data.cr}}" } }, "NPC": { "role": "neutral" }, "Character": { "role": "friendly", "combatant": { "states": { "defeated": "#{{data.hp.current|default: 1}} <= -{{data.hp.maximum|default: 0}}" } } } }}Values resolve in four layers, each overriding the last: config.json’s Default, the user’s
settings Default, config.json’s entity block, the user’s settings for that entity. That is
what allows a system to ship a sensible default and still let a user change it from the
settings form.
| Key | Meaning |
|---|---|
role |
The side this type joins combat on — friendly, neutral, hostile. |
collection.detail |
Template for the subtitle in library lists. |
collection.image |
Which image a library row shows. |
combatant.detail |
Template for the subtitle in the combat tracker. |
combatant.image |
Which image a combatant row shows. |
combatant.bars |
Tracked value bars — see below. |
combatant.states |
Named state conditions — see below. |
view.renderer |
native (default), html, custom, sheet. |
view.template |
Which views/*.html file the html renderer uses. |
view.updateMode |
load, reload, event, none. |
collection.detail and combatant.detail are the one-line summaries under a name. They are
ordinary templates over the entity’s context, which means they
can read anything a transform computed — the AC,
passive Perception and CR in the example above are all derived values.
Tracked bars
Section titled “Tracked bars”combatant.bars declares what the combat tracker tracks. Each entry names an attribute holding a
current / maximum (and optionally temporary) group:
"bars": [ { "attribute": "data.hp", "title": "Common.HitPoints", "label": "Common.HP", "temporary": true }]The first bar is the primary one: it backs the defeated and bloodied states and is what the tracker shows most prominently. A system with more than one pool — stamina, stress, sanity — adds further entries.
States
Section titled “States”combatant.states maps a state name to a condition template. defeated is the one the app acts
on:
"states": { "defeated": "#{{data.hp.current|default: 1}} <= -{{data.hp.maximum|default: 0}}" }The leading # makes it a formula. This example is the
D&D 5E instant-death rule; without it, the default rule of reaching zero applies.
Status effect menus
Section titled “Status effect menus”"statusEffects": { "menuProvider": ["Rule:condition"] }Each entry names an entity type to offer as status effects, optionally narrowed by a
subtype after a colon. Rule:condition is what makes a 5E system’s conditions appear in the
tracker’s status effect menu — the conditions are ordinary content, not a hardcoded list.
Measurement
Section titled “Measurement”Declares the measurement systems the ruleset uses, their units, display format and conversions:
"measurement": { "imperial": { "default": true, "title": "MeasurementSystem.Imperial", "units": ["ft", "mi", "lb"], "format": "@value @unit.", "conversion": { "metric": { "ft": { "m": "@value * 0.3" }, "mi": { "km": "@value * 1.5" }, "lb": { "kg": "@value * 0.5" } } } }, "metric": { "title": "MeasurementSystem.Metric", "units": ["m", "km", "kg"], "format": "@value @unit", "conversion": { "imperial": { "m": { "ft": "@value / 0.3" }, "km": { "mi": "@value / 1.5" }, "kg": { "lb": "@value * 2" } } } }}Units are listed in matching order across systems, so the first unit of one maps to the first of
another. @value and @unit are the placeholders. The default system applies until the user
picks one in system settings.
Every value passed through the units filter, and every form field with a units key, is
converted accordingly:
{{ data.range | units: 'ft' }}A creature authored in feet displays in metres for a metric user, with no change to the content.
Initiative
Section titled “Initiative”"combat": { "initiative": { "mode": "roll", "rollFormula": "d20 + @entity.data.initiativeBonus", "autoRoll": "hostileAndNeutral", "group": null, "everyRound": false, "sortOrder": "desc" }}| Key | Values |
|---|---|
mode |
roll (default), draw, fixed |
rollFormula |
The expression rolled; defaults to d20 + @entity.data.initiativeBonus |
rollTimes |
How many times to roll |
drawPool |
Highest value in the draw pool, for draw mode (default 20) |
autoRoll |
all, hostile, hostileAndNeutral (default), none |
group |
How ties are grouped |
everyRound |
Re-roll initiative each round |
sortOrder |
desc (default) or asc |
A card-draw system sets "mode": "draw"; a system where initiative is a fixed derived score sets
"mode": "fixed".
Everything here can be exposed to users through the settings form, since settings merge over config.
filters.json
Section titled “filters.json”The library’s filter, group and sort controls, keyed by entity name:
{ "Monster": { "params": [ { "attribute": "data.size", "attributeType": "Size", "title": "Common.Size" }, { "attribute": "data.type", "attributeType": "MonsterType", "title": "Common.Type" }, { "attribute": "data.environments", "attributeType": "Environment", "title": "Common.Environment" }, { "attribute": "sources.name", "title": "Common.Source", "dynamic": true } ], "group": [ { "attribute": "data.cr", "attributeType": "ChallengeRating", "title": "Common.CR", "text": "CR {{value}} ({{value|map:'ChallengeRatingToXP', '0'}} XP)" }, { "attribute": "name", "title": "Common.Name" } ], "sort": [ { "attribute": "name", "title": "Common.Name" }, { "attribute": "data.cr", "title": "Common.CR", "text": "{{value|valueMap:'ChallengeRatingToXP'}}" } ] }}params— the filter controls. WithattributeType, options come from that type. With"dynamic": true, options are gathered from the values actually present in the user’s content — the right choice for sources, class names and anything open-ended.group— the group-by options.textis a template for the section header, with the group’s value bound tovalue.sort— the sort options.textsupplies a sort key when the stored value does not sort correctly on its own; sorting challenge ratings by their XP value is how"1/8"ends up before"1"instead of after it.
A filter over an attribute the entities do not actually carry simply produces no options, and a
block keyed by an entity name that entities.json does not define is skipped. If a type is
sourcable and you declare no params at all, a source filter is added automatically.
rules.json
Section titled “rules.json”Optional seed content shipped with a system — the conditions, senses, skills and other reference
entries a ruleset needs to function. It is content rather than definition, and it is what
statusEffects.menuProvider draws on.
Where to go next
Section titled “Where to go next”- Types & Collections — what
attributeTyperefers to. - Templates — the expressions in
detail,textandstates. - Forms — exposing config to users.

