Skip to content

chore(SmartAoE): Release version 1.0.0 - #2354

Merged
Alicekb merged 33 commits into
Roll20:masterfrom
steverobertsuk:sr/smartaoe-v1-0-0
Oct 28, 2026
Merged

Alicekb merged 33 commits into
Roll20:masterfrom
steverobertsuk:sr/smartaoe-v1-0-0

Conversation

@steverobertsuk

@steverobertsuk steverobertsuk commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Releases SmartAoE v1.0.0, marking the script as stable with no breaking changes for existing macros or AoEs. This version includes a major code refactor, new features, numerous bug fixes, and support for the D&D 2024 (Beacon) character sheet, improving reliability and the user experience.

Refactoring and Code Quality

  • Code has been restructured to adhere to the HealthColors standard, incorporating strict mode, JSDoc documentation for all functions, and modularising chat command handlers.
  • Improved variable scoping by localising many previously global working variables, reducing the risk of conflicts with other scripts.
  • Approximately 1,400 lines of duplicate and dead code have been removed, streamlining the codebase without altering functionality.
  • Standardised code formatting using Prettier, with configuration added to the repository.
  • Updates to script.json to accurately reflect the script and its object access.
  • New README.md, CHANGELOG.md and TESTING.md files provide documentation (including a D&D 2024 sheet section) and a manual testing checklist.

New Features

  • Introduces a GM-only SmartAoE — Help & Reference handout in the Journal, providing quick start guides, commands, parameters, examples, API integration details, and tips. This handout is rebuilt automatically on script load and can be accessed via the !smarthelp command.
  • --resource accepts an optional limit: --resource|attrName, cost, alias, limit, so a resource can count up to a limit instead of down to 0. The limit can be a number, max (the resource's own max) or another attribute. The D&D 2024 sheet needs this for spell slots, which count slots used: --resource|lvl3_slots_expended, 1, Level 3 slots, lvl3_slots_total. Without a limit, --resource works as before.
  • Exposes SmartAoE.ZeroHPMarkers() as a public API function, allowing other scripts to retrieve a list of 0 HP marker tags used by SmartAoE during the current session.
  • Enhances error logging by including stack traces in the API console, facilitating more effective diagnosis of issues.

Key Bug Fixes

  • D&D 2024 (Beacon) sheets (forum report):
    • SmartAoE only reads legacy attribute objects, which Beacon sheets don't use. When no legacy attribute is found, it now reads the sheet with Roll20's getSheetItem, then tries the custom-attribute form user.<name>. 2014-sheet games keep using attribute objects as before.
    • a{…} / @{…} references in --saveFormula, and the 5eSTR…5eCHA shorthands, now include the target's bonus instead of rolling a bare d20.
    • --resistAttr, --vulnerableAttr and --immunityAttr are now found, so resistances, vulnerabilities and immunities apply.
    • --resource no longer reports "Resource '…' is not found". Beacon resources are spent with setSheetItem. If the sheet refuses the change, the AoE isn't created, and the GM is told why.
    • --ignore now matches on Beacon sheets.
    • Empty sheet fields (e.g. an NPC with no resistances) don't fill the API console with "Could not find Custom Attribute" errors.
    • Requires Mod Script Sandbox v1.5, the current default. Magic Resistance detection still needs legacy attributes.
  • Robustness and User Feedback:
    • Prevents script crashes when !smarttrigger, !smartquery, !smartrotateorigin, or !smartremove commands are used with no selection, an invalid selection, or an unknown ID, instead providing clear whispers.
    • Ensures that targets whose characters no longer exist are gracefully skipped during processing, rather than causing a full trigger failure.
    • Fixes !smarttrigger to correctly activate the AoE when a specific control token ID is provided without a target.
    • Resolves smaller geometry, colour parsing, and startup issues, including handling angles outside 0-360°, stripping all non-hex characters from colour inputs, and preventing ReferenceError during initial setup.
    • Correctly matches !smartpinggm and !smartpingall commands and ensures only one command executes per chat message.
    • Improves error reporting for startup and command errors by logging full stack traces to the API console.
  • AoE Mechanics & Calculations:
    • Correctly handles damage and save calculations by ensuring inline roll results are consistently treated as numeric values, preventing unintended string concatenations (e.g., [5] + 2 yielding 52).
    • --damageFormula2 now works reliably with both plain numbers and inline rolls, independently of --damageFormula1, and displays correctly on the chat card even if --damageFormula1 is absent or zero.
    • Healing mechanics are now precise: leftover healing cascades correctly to subsequent bars, temporary HP bars are not reduced below their original values, and the (@Max) note for full healing appears as expected.
    • --removeAtZero functionality is restored, correctly removing tokens when their last designated health bar reaches zero.
    • Status marker management (--conditionPass, --conditionFail, --zeroHPmarker, --casterCondition, and chat-button markers) is now robust, preventing duplicate markers, accurately matching existing markers by tag, and avoiding extraneous commas. Observers are only notified when actual changes occur.
    • Geometric calculations for AoEs are more accurate: very small circles now correctly cover the single square they occupy, and edge squares are properly detected for AoEs originating off-centre from larger tokens.
    • The distBetweenPts function now correctly returns 0 for zero-length segments instead of NaN, and applyMathRule leaves values unchanged for unknown operators.
    • Fixes deleteLinkedPaths to prevent overwriting the shared path variable during AoE creation.
  • Interaction & Display:
    • !smartrotateorigin now redraws only the specific AoE being rotated, enhancing performance and preventing unintended changes to other AoEs.
    • Each AoE triggered from the same caster or selection now posts its own, independent chat card, preventing mixed results.
    • Resolves an issue where inline damage rolls containing special characters (e.g., *, {, >, " from libInline's HTML encoding) were not correctly evaluated; now decoding these entities for accurate roll processing.
    • Ensures that --aoeColor and --aoeOutlineColor values provided by other API scripts are preserved, addressing a previous bug where they were overwritten by player colours.
    • !smarttrigger in Jumpgate games now correctly identifies and triggers AoEs even when an AoE grid square is selected instead of the control token (due to layering issues).
    • Addresses the issue of !smartquery and !smarttrigger failing with "Cannot read properties of undefined" when encountering stale AoE paths.
    • Adds validation for --controlTokSide values, preventing crashes due to invalid side numbers.

Testing

  • Tested live in a Jumpgate game using D&D 2024 sheets on Mod Script Sandbox v1.5, including the forum reporter's Fireball macro:
    • saves included each target's DEX bonus;
    • fire immunity and poison resistance were applied;
    • a 2024 caster's 1st-level slots counted 1, 2, 3 of 3 used, and the next cast was blocked;
    • nothing was logged to the API console.
  • Re-tested in a Jumpgate game with 2014 (OGL) sheets, which also has 2024 characters:
    • --resource counts down as before;
    • the 5eDEX and a{…} save bonuses are correct for NPCs and PCs;
    • damage-type attributes are read from attribute objects;
    • nothing was logged to the API console.

…very combination

The second damage formula only worked when both formulas were rolls. A
plain number in formula 2 was parsed into damageBase1, overwriting
formula 1's damage, and formula 2 was dropped whenever formula 1 was a
plain number. The chat card also only showed damage when formula 1 had a
result, so formula 2 on its own never appeared.

Roll each formula independently, as an inline roll or a plain number,
and store formula 2's plain number in damageBase2. Build the card's
damage rows with buildDamageRows, which shows every damage roll with a
non-zero result. Plain-number damage goes through baseDamageFormula,
which writes negative amounts (healing) as "0 - n" because Roll20 cannot
start an inline roll with a negative number.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
A damage formula such as --damageFormula1|[[2*13d6]] dealt 2 damage.
libInline HTML-encodes roll expressions and SmartAoE only decoded [ and
], so the formula was re-rolled as [[2&Roll20#42;13d6]]. Reported on the
Roll20 forum:
https://app.roll20.net/forum/post/11924759/help-smartaoe-refuses-to-recognise-star-as-a-multiplier-any-ideas

Add decodeInlineExpression, which decodes the numeric, hex and named
entities libInline uses (including its full-width "<"), and use it for
both damage formulas, so *, {...} groups and > or < comparisons work.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
If any AoE on the page referred to a square that had since been deleted
(by hand, by copying pages, and so on), !smartquery and the
Trigger-Target token action stopped with "Cannot read properties of
undefined (reading 'get')" for every target. This was reported several
times on the forum thread:
https://app.roll20.net/forum/post/10485883/script-smartaoe-graphical-interface-for-implementing-aoes-on-gridded-maps/?pagenum=16

getAoELinks and the path checks now skip squares, tokens and pages that
no longer exist.

In Jumpgate the AoE squares sit above the control token, so clicking the
reticle can select a square instead and !smarttrigger then failed:
https://app.roll20.net/forum/post/12193888/script-smartaoe-anyone-using-it-in-a-jumpgate-campaign-successfully
https://app.roll20.net/forum/post/12349884/smartaoe-controltoken-spawns-behind-its-colored-squares
https://app.roll20.net/forum/post/12400598/smartaoe-and-jumpgate

findTriggerToken maps a selected square back to its AoE's control
token. "!smarttrigger <controlTokenID>" without a target id, which also
crashed, now triggers everything in that AoE; an unknown target id or an
empty selection is reported instead of throwing.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…Zero

Healing that cascaded to a second or third bar gave each later bar the
full heal rather than what was left over, and could pull a bar that was
above its max (temporary HP) down to the max. The "(@max)" note compared
two arrays with ===, so it never appeared, and --removeAtZero compared
the array of new bar values with 0, so no token was ever removed.

Heal each bar up to its max and pass only the remainder on, leaving bars
already above max alone; show "(@max)" when every bar is full; and
remove the token when its last bar reaches 0.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Adding markers (--conditionPass, --conditionFail, --zeroHPmarker,
--casterCondition and the chat-card buttons) spliced items out of the
list while looping over it, so some were skipped. A marker counted as
present when it appeared anywhere in the string, so "red" was treated as
present because of "red@2" or "redbeard", and stray leading or trailing
commas were left in statusmarkers. Removing markers missed markers with
a number badge and did not allow for spaces after commas.

Compare whole marker tags without their @Number badge (markerTag) on
trimmed entries (splitStatusMarkers), add each new marker once, write
the list back without stray commas, and only notify observers when a
marker was actually added or removed. Adding or removing markers on a
token that no longer exists no longer throws.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Other scripts, HealthColors in particular, need to know which status
markers SmartAoE uses as its 0 HP marker so they do not add their own
dead marker on top. They could only read the AoEs saved in state, which
misses --instant AoEs because those are deleted before their damage is
applied.

Remember every --zeroHPmarker when an AoE is created and before damage
is applied, and expose the list as SmartAoE.ZeroHPMarkers().

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
If creating the default AoEControlToken character or the AoEGenerator
macro failed, the error handler threw a ReferenceError of its own,
because the names it reported were declared inside the try block, and
hid the real error. Declare them before the try block.

Errors raised by chat commands were only whispered as a one-line
message. Also log the stack trace to the API console for diagnosis.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Add the same .prettierrc as HealthColors (print width 120, single
quotes, trailing commas, two-space indentation, LF line endings), so the
script can be formatted consistently, and a .gitignore for zip archives
and alpha/beta builds.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Bring the code in line with HealthColors without changing how AoEs are
drawn or resolved:

- add a header banner with the authors and a /* global */ list, run in
  strict mode, and group the code under section headers;
- add a JSDoc block to every function;
- split the 1,400-line chat handler into one handler per command
  (handleCreateAoeCommand, handleTriggerCommand, handleQueryCommand and
  so on), dispatched from a COMMAND_HANDLERS table;
- declare the 35 working variables that leaked into the shared Mod
  sandbox as globals inside SmartAoE, and replace var with let/const;
- remove about 600 lines of commented-out code and four functions that
  were never called, and three duplicate object keys whose earlier
  values were already overridden;
- format with Prettier.

An end-to-end comparison in a mocked game (every shape, saves,
resistances, apply buttons, markers, origin rotation, token moves,
query, remove and clear-cache) gives identical chat output, objects and
state before and after this change.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…her scripts

The noSave option had no break, so --noSave|false fell through into
saveFormula and set the save formula to the text "false". When another
script called SmartAoE with --playerID, "if (getFillColor = true)" and
"if (getOutlineColor = true)" assigned rather than compared, so the
player's colour always replaced colours passed explicitly. Both were
flagged by ESLint during the restructuring.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…own math rules

- buildSaveRow escaped backticks in names with a regular expression as
  the replacement text, which broke the chat card's apply buttons.
- distToSegmentSquared used a lowercase p for the point P when the
  segment had zero length, giving NaN distances.
- applyMathRule wrote to a shared variable, so an unknown operator
  returned the previous call's result instead of the value unchanged.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…ands

Several commands threw when given nothing usable:

- !smarttrigger on a token that is not part of an AoE now whispers that
  it is not part of a SmartAoE;
- !smartquery with nothing selected no longer reads an undefined
  selection, and returns after its "must select" message;
- !smartrotateorigin with nothing selected asks for a selection, and a
  token without AoEs is left alone;
- !smartremove with an unknown id reports that the token was not found.

A target whose character no longer exists is skipped rather than
breaking the results card.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Create a GM-only "SmartAoE — Help & Reference" handout in the Journal
with an overview, a quick start, the chat commands, every !smartaoe
parameter grouped by topic, example macros, notes on other scripts and
tips. It is rebuilt each time the script loads, and the new !smarthelp
command rebuilds it and whispers a link. Its body text inherits the
journal's colour, with mid-tone accents and translucent tints, so it
reads in both Roll20 light and dark mode.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
An AoE with neither --saveFormula nor --noSave silently did nothing when
triggered: each target sent an empty roll that Roll20 never answered, so
no card was posted, no damage was applied and no error was shown. Treat
it as no save, so every target takes full damage, as --noSave|true
does, and say so in the help handout. Found during live testing.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
For circles, squares and cones cast from a corner or face of a large
token, the area searched for covered squares was shifted by the origin
offset (subtracted at both ends), and the vertical offset and size were
ignored, so squares along an edge of the AoE could be missed. Widen the
search box by the size of the offset on each axis instead, which covers
the whole shape without changing it, and correct the parameter
documentation.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
A circle smaller than a grid square that sits inside it never crosses
the square's edges, so no square was marked and the AoE was empty. Keep
that square when the circle lies within it and covers at least
--minGridArea of it.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
When several AoEs from the same caster were triggered at once, the
damage rolls and targets were collected across all of them, so each
card also showed the earlier AoEs' damage rows and targets. Collect them
per AoE.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
!smartrotateorigin redrew every AoE linked to the selected token with
the rotated AoE's offset. Redraw only the AoE being rotated, skip AoEs
whose caster token has been deleted, and drop two unused variables.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
The --controlTokSide check used || where && was meant, so any number
passed and a side that did not exist failed later. On an invalid side
the function also returned an unrelated shared variable. Report the
side requested and the number available, stop creating the AoE, quote
the player's name in the whisper and let the caller stop cleanly.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
deleteLinkedPaths reused the shared "path" variable that AoE creation
was still using, so deleting old squares could clear the path of the
AoE being created. Use a local variable.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Roll results are arrays with one entry per inline roll, so a rule such
as "+2" joined them as text ([5] + 2 gave "52") and a line with two
rolls compared as NaN. Use the line's total for damage and its first
roll for a save. SmartAoE passes one roll per line in normal use, so
this is a safeguard.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
The !smartpinggm / !smartpingall pattern was a character class rather
than "gm or all" and was not anchored to the start of the message.
Match it properly, and stop after the first matching command so a
message runs only one handler.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
- getAngle2ControlToken returns 0 for two identical points instead of
  NaN, and getNewControlPt handles angles outside 0-360 degrees instead
  of returning undefined.
- getNearestOriginPt falls back to the control point if no edge
  crossing is found, instead of measuring from undefined.
- toFullColor strips every non-hex character, not just the first.
- checkInstall skips the collections macro with a log line when no GM
  is found, instead of throwing.
- Use && where a bitwise & was meant, show every slash in the chat
  card's damage with spaces, make dTheta and oY local, remove an unused
  variable and correct the getWallParams comments.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Remove dTheta, i, layer, newSideImg, oY and tokenPic from the shared
working variables, now that every use declares its own, and declare the
remaining loop counter and token values locally. Correct the JSDoc for
makeAoELink's path id.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
About 1,400 fewer lines, with no change in behaviour:

- circles, squares, walls and cones share one cell-clipping routine,
  getCoveredCells, instead of four near-identical copies of about 300
  lines each; each shape supplies only its inside test and edge
  crossings;
- the 56 abilities of the AoEControlToken character come from one
  table, CONTROL_TOKEN_ABILITIES;
- roll results for damage rolls and targets are formatted by one
  function, attachRollResults;
- the chat commands find their caller with one getCaller helper;
- the chat card's alternating row colours, P/F badge and damage cell
  are each built in one place;
- get5eConePt (a copy of getConeEndPt), processInlinerolls,
  sendChatNoarchive and processDamageMessages are removed, and
  processInlinerolls2 is renamed substituteInlineRolls;
- clearCache only ever cleared everything, so it becomes resetLinks(),
  which install also uses; makeAoELink takes the single path id it is
  always given; the flat-cone angle test spells out the short way round;
- unused parameters, variables and return values are removed.

28,500 random shape calculations give the same squares before and
after, and the end-to-end scenarios give identical chat output, objects
and state.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
README: features (including the help handout), installation, how it
works, every chat command and !smartaoe parameter grouped by topic,
example macros, integration with other scripts (including the script
API and ZeroHPMarkers), a Jumpgate section with the layering workaround
and a note on 2024 (Beacon) sheets, tips and known limitations collected
from the Roll20 forums, development notes and credits.

TESTING.md: a manual checklist for setup, the handout, creating and
triggering every shape, damage and healing, markers and removal,
Trigger-Target and stale links, calls from other scripts and error
reporting.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…json

Add MidNiteShadow7 to the authors, mention the help handout in the
description, and declare the objects and properties SmartAoE reads and
writes at property level: tokens, paths, pages, the AoEControlToken
character and its abilities, attributes, the AoEGenerator macro, the
help handout, players, jukebox tracks and the campaign's player page,
token markers and turn order.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Bump the version to 1.0.0 in the script and script.json, adding 0.30 to
the previous versions, add the 1.0.0 version folder, and record the
release in a new changelog: what was added, changed and fixed since
0.30, with links to the forum reports, plus the history of earlier
versions.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
Adds the release date for version 1.0.0 to the CHANGELOG.md.
Also applies a minor formatting adjustment to the credits section in the README.md.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
…heets

SmartAoE only read character data as legacy attribute objects, through
findObjs and getAttrByName. The D&D 2024 (Beacon) sheet doesn't store
its values that way, so on a 2024 sheet:

- a{...} / @{...} references in --saveFormula, and the 5eSTR...5eCHA
  shorthands, came out empty, so every save was a bare d20;
- --resistAttr, --vulnerableAttr and --immunityAttr were never found,
  so targets took full damage whatever their resistances;
- --ignore never matched.

When no legacy attribute is found, these now read the sheet with
Roll20's getSheetItem, then try the custom-attribute form user.<name>.
A sheet item that exists but is empty stops the lookup there, because
Roll20 logs an error to the API console for every missing user.*
attribute. Beacon values are turned into text for roll formulas: true
and false become 1 and 0, and lists are joined with commas.

replaceAttributes and processTokenRolls are now async, and the trigger
waits for each target. 2014-sheet characters keep using their attribute
objects and getAttrByName defaults as before. Magic Resistance
detection still needs legacy attributes.

The 1.0.0 snapshot is updated to match.

Reported on the Roll20 forums:
https://app.roll20.net/forum/post/12842711/script-update-smartaoe/?pageforid=12843607#post-12843607

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
--resource only looked for a legacy attribute object, so on a D&D 2024
(Beacon) sheet it always replied "Resource '...' is not found", even
when the value existed on the sheet.

A new readCharacterValue helper reads the legacy attribute if there is
one and otherwise the sheet item. A Beacon resource is then spent with
setSheetItem. If the sheet refuses the change, the AoE isn't created
and the GM is told why, so a spell is never cast without spending its
resource. 2014-sheet resources are still set on the attribute object.

The 1.0.0 snapshot is updated to match.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
The D&D 2024 sheet's spell slot fields count slots used, not slots
left: lvl3_slots_expended starts at 0, and the total is kept in
lvl3_slots_total. --resource only subtracted, so with no slots used it
always said there weren't enough.

An optional fourth value, --resource|attrName, cost, alias, limit,
makes the resource count up instead. The cost is added, and the AoE is
blocked if that would take the value past the limit. The limit can be
a number, "max" (the resource's own max) or another attribute, e.g.

  --resource|lvl3_slots_expended, 1, Level 3 slots, lvl3_slots_total

Without a limit, --resource counts down as before. An empty alias now
falls back to the attribute name, so "name, 1, , limit" works. If the
limit is missing or not a number, the cast stops and the GM is told
why. getSheetValue and readCharacterValue take a 'current' or 'max'
type for the "max" limit, and the help handout's --resource row
describes the new form.

Tested in a live D&D 2024 game: a caster with three 1st-level slots
went to 1, 2 and 3 of 3 used, and the fourth cast was blocked.

The 1.0.0 snapshot is updated to match.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
README:
- replace the 2024-sheet note, which advised a custom --saveFormula
  that could never have worked, with a "D&D 2024 (Beacon) sheets"
  section: what is read from the sheet, the need for Mod Script
  Sandbox v1.5, how Beacon resources are spent, that 2024 spell slots
  count up, and how to check an attribute name in chat;
- describe the --resource limit in the parameter table, and note that
  Magic Resistance is only detected on 2014 sheets.

CHANGELOG: add the --resource limit under Added, and the Beacon sheet
fixes under Fixed with a link to the forum report.

TESTING: add a D&D 2024 sheet section based on the reporter's Fireball
macro (counting slots up to the total, being blocked at the limit, the
save bonus and immunity, and a 2014-sheet comparison), and expect the
5eDEX save bonus on both sheets.

Signed-off-by: Steve Roberts <steve@shadowcomputers.co.uk>
@Alicekb
Alicekb merged commit 355390d into Roll20:master Oct 28, 2026
2 checks passed
@steverobertsuk
steverobertsuk deleted the sr/smartaoe-v1-0-0 branch October 28, 2026 21:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants