From 450287e4d05d0f3dd3f3d5f3e29558d8462e4c9c Mon Sep 17 00:00:00 2001
From: WBHarry <89362246+WBHarry@users.noreply.github.com>
Date: Mon, 13 Jul 2026 22:57:43 +0200
Subject: [PATCH 1/5] [Fix] Summon Wildcard Handling (#2086)
---
module/data/action/baseAction.mjs | 3 ++-
module/data/fields/action/summonField.mjs | 18 +++++++++--------
module/data/fields/actionField.mjs | 4 ++--
module/documents/tokenManager.mjs | 24 +++++++++++++----------
templates/ui/chat/action.hbs | 6 +++---
5 files changed, 31 insertions(+), 24 deletions(-)
diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs
index be7224cd..5b871c9a 100644
--- a/module/data/action/baseAction.mjs
+++ b/module/data/action/baseAction.mjs
@@ -256,7 +256,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (Hooks.call(`${CONFIG.DH.id}.postUseAction`, this, config) === false) return;
- if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled) await this.toChat();
+ if (this.chatDisplay && !config.skips.createMessage && !config.actionChatMessageHandled)
+ await this.toChat(null, config);
return config;
}
diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs
index 6845d2ba..fef6625a 100644
--- a/module/data/fields/action/summonField.mjs
+++ b/module/data/fields/action/summonField.mjs
@@ -23,7 +23,7 @@ export default class DHSummonField extends fields.ArrayField {
super(summonFields, options, context);
}
- static async execute() {
+ static async execute(config) {
if (!canvas.scene) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error'));
return;
@@ -36,6 +36,7 @@ export default class DHSummonField extends fields.ArrayField {
const rolls = [];
const summonData = [];
+ const chatMessageData = [];
for (const summon of this.summon) {
const roll = new Roll(itemAbleRollParse(summon.count, this.actor, this.item));
await roll.evaluate();
@@ -54,17 +55,18 @@ export default class DHSummonField extends fields.ArrayField {
tokenPreviewName: `${actor.prototypeToken.name}${remaining > 1 ? ` (${remaining}x)` : ''}`
});
}
+
+ chatMessageData.push({
+ data: actor,
+ quantity: countNumber
+ });
}
if (rolls.length) await triggerChatRollFx(rolls);
this.actor.sheet?.minimize();
- DHSummonField.handleSummon(summonData, this.actor);
- }
-
- static async handleSummon(summonData, actionActor) {
- await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: actionActor.token?.elevation });
-
- return actionActor.sheet?.maximize();
+ await CONFIG.ux.TokenManager.createTokensWithPreview(summonData, { elevation: this.actor.token?.elevation });
+ this.actor.sheet?.maximize();
+ config.summonData = chatMessageData;
}
}
diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs
index 83672c8e..af8f338e 100644
--- a/module/data/fields/actionField.mjs
+++ b/module/data/fields/actionField.mjs
@@ -269,7 +269,7 @@ export function ActionMixin(Base) {
return this.delete();
}
- async toChat(origin) {
+ async toChat(origin, config) {
const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
.expandRollMessage?.desc;
@@ -282,7 +282,7 @@ export function ActionMixin(Base) {
img: this.baseAction ? this.parent.parent.img : this.img,
tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'],
areas: this.areas,
- summon: this.summon
+ summon: config?.summonData
},
source: {
actor: this.actor.uuid,
diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs
index 7678d2c7..062a22ee 100644
--- a/module/documents/tokenManager.mjs
+++ b/module/documents/tokenManager.mjs
@@ -19,10 +19,10 @@ export default class DhTokenManager {
}
}
- return await canvas.tokens.placeTokens(
+ const placedData = await canvas.tokens.placeTokens(
[
{
- ...actor.prototypeToken.toObject(),
+ ...(await actor.getTokenDocument()).toObject(),
actorId: actor.id,
displayName: 50,
...tokenData
@@ -30,6 +30,8 @@ export default class DhTokenManager {
],
{ create: false }
);
+
+ return placedData[0] ?? null;
}
/**
@@ -46,22 +48,24 @@ export default class DhTokenManager {
const createElevation = elevation ?? level.elevation.bottom;
for (const tokenData of tokensData) {
- const previewTokens = await this.createPreview(tokenData.actor, {
+ const previewToken = await this.createPreview(tokenData.actor, {
name: tokenData.tokenPreviewName,
level: game.user.viewedLevel,
elevation: createElevation,
flags: { daggerheart: { createPlacement: true } }
});
- if (!previewTokens?.length) return null;
+ if (!previewToken) return null;
+
+ const finalTokenData = {
+ ...previewToken.toObject(),
+ name: tokenData.actor.prototypeToken.name,
+ displayName: tokenData.actor.prototypeToken.displayName,
+ flags: tokenData.actor.prototypeToken.flags
+ };
await canvas.scene.createEmbeddedDocuments(
'Token',
- previewTokens.map(x => ({
- ...x.toObject(),
- name: tokenData.actor.prototypeToken.name,
- displayName: tokenData.actor.prototypeToken.displayName,
- flags: tokenData.actor.prototypeToken.flags
- })),
+ [finalTokenData],
{ controlObject: true, parent: canvas.scene }
);
}
diff --git a/templates/ui/chat/action.hbs b/templates/ui/chat/action.hbs
index 51840363..d9ceb417 100644
--- a/templates/ui/chat/action.hbs
+++ b/templates/ui/chat/action.hbs
@@ -16,10 +16,10 @@
{{#each action.summon}}
-

-
+

+
-
# {{this.rolledCount}}
+
# {{this.quantity}}
{{/each}}
From 02a73d774a916f20b6816590fbc8680dcd60b032 Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Mon, 13 Jul 2026 18:31:42 -0400
Subject: [PATCH 2/5] Add guard for null placedData (#2087)
---
module/documents/tokenManager.mjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs
index 062a22ee..b21b56a0 100644
--- a/module/documents/tokenManager.mjs
+++ b/module/documents/tokenManager.mjs
@@ -31,7 +31,7 @@ export default class DhTokenManager {
{ create: false }
);
- return placedData[0] ?? null;
+ return placedData?.[0] ?? null;
}
/**
From 3a5529f1dc8730c217f5fa3cf033974775de556f Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Mon, 13 Jul 2026 18:56:34 -0400
Subject: [PATCH 3/5] Cleanup secret block styling (#2088)
---
styles/less/global/elements.less | 39 ++++++++++++++++++++------------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less
index edc02f9a..1b7ed072 100755
--- a/styles/less/global/elements.less
+++ b/styles/less/global/elements.less
@@ -599,27 +599,23 @@
}
secret-block {
- position: relative;
- section.secret {
- background-color: @red-10;
- padding: 0;
- margin-top: 0.375rem;
- &.revealed {
- background-color: @green-10;
- }
- p {
- margin: 0.5rem 0;
- }
- }
+ display: block;
+
+ /** A buffer to make the hover behavior work a bit better. The bottom in the button needs to compensate */
+ @buffer: 8px;
+ margin-top: -@buffer;
+ padding-top: @buffer;
+
button.reveal {
- --button-size: 0.875rem;
+ --button-size: 1rem;
+ height: var(--button-size);
position: absolute;
margin: auto;
left: 0;
right: 0;
width: min-content;
padding: 1px 8px 0 8px;
- bottom: calc(100% - 0.4375rem - 2px);
+ bottom: calc(100% - 0.4375rem - 1px);
background-color: var(--dh-window-button-color-bg); // todo: find a better var name
border-color: var(--color-secret-border);
@@ -627,6 +623,7 @@
font-size: var(--font-size-10);
user-select: none;
text-transform: uppercase;
+ white-space: nowrap;
visibility: hidden;
}
@@ -635,6 +632,20 @@
visibility: visible;
}
}
+
+ /**
+ * The element inside a secret-block.
+ * This is separate since during prosemirror editing, the secret-block container does not exist.
+ */
+ section.secret {
+ --color-secret-bg: @red-10;
+ --color-revealed-bg: @green-10;
+ position: relative;
+ padding: 0;
+ p {
+ margin: 0.5rem 0;
+ }
+ }
}
.system-daggerheart {
From 4974df16d071f67667df972d9fd27e0c4fee34e1 Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Tue, 14 Jul 2026 08:35:02 -0400
Subject: [PATCH 4/5] Preserve description expand state on re-render (#2089)
---
module/applications/dialogs/deathMove.mjs | 3 --
module/applications/dialogs/downtime.mjs | 6 +---
module/data/fields/actionField.mjs | 5 +--
module/documents/chatMessage.mjs | 42 ++++++++++++++---------
4 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/module/applications/dialogs/deathMove.mjs b/module/applications/dialogs/deathMove.mjs
index 8e0ed6af..cfd7687b 100644
--- a/module/applications/dialogs/deathMove.mjs
+++ b/module/applications/dialogs/deathMove.mjs
@@ -185,8 +185,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
if (result === undefined) return;
- const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
- .expandRollMessage?.desc;
const cls = getDocumentClass('ChatMessage');
const msg = {
@@ -202,7 +200,6 @@ export default class DhDeathMove extends HandlebarsApplicationMixin(ApplicationV
img: this.selectedMove.img,
description: game.i18n.localize(this.selectedMove.description),
result: result,
- open: autoExpandDescription ? 'open' : '',
showRiskItAllButton: this.showRiskItAllButton,
riskItAllButtonLabel: this.riskItAllButtonLabel,
riskItAllHope: this.riskItAllHope
diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs
index e209cc3b..5ba8e48e 100644
--- a/module/applications/dialogs/downtime.mjs
+++ b/module/applications/dialogs/downtime.mjs
@@ -196,9 +196,6 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
.filter(x => x.testUserPermission(game.user, 'LIMITED'))
.filter(x => x.uuid !== this.actor.uuid);
- const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
- .expandRollMessage?.desc;
-
const cls = getDocumentClass('ChatMessage');
const msg = {
user: game.user.id,
@@ -219,8 +216,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
actor: { name: this.actor.name, img: this.actor.img },
moves: moves,
characters: characters,
- selfId: this.actor.uuid,
- open: autoExpandDescription ? 'open' : ''
+ selfId: this.actor.uuid
}
),
flags: {
diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs
index af8f338e..ba2fa37e 100644
--- a/module/data/fields/actionField.mjs
+++ b/module/data/fields/actionField.mjs
@@ -270,9 +270,6 @@ export function ActionMixin(Base) {
}
async toChat(origin, config) {
- const autoExpandDescription = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance)
- .expandRollMessage?.desc;
-
const cls = getDocumentClass('ChatMessage');
const systemData = {
title: game.i18n.localize('DAGGERHEART.CONFIG.FeatureForm.action'),
@@ -307,7 +304,7 @@ export function ActionMixin(Base) {
system: systemData,
content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/ui/chat/action.hbs',
- { ...systemData, open: autoExpandDescription ? 'open' : '' }
+ systemData
),
flags: {
daggerheart: {
diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs
index fd68997c..d53a76bd 100644
--- a/module/documents/chatMessage.mjs
+++ b/module/documents/chatMessage.mjs
@@ -3,6 +3,13 @@ import { emitGMUpdate, emitGMCreate, GMUpdateEvent } from '../systemRegistration
export default class DhpChatMessage extends foundry.documents.ChatMessage {
targetHook = null;
+ static #EXPAND_SECTIONS = [
+ { selector: 'roll-section [data-action="expandRoll"]', key: 'roll' },
+ { selector: 'damage-section', key: 'damage' },
+ { selector: 'target-section', key: 'target' },
+ { selector: 'description-section', key: 'desc' }
+ ];
+
async renderHTML() {
const actor = game.actors.get(this.speaker.actor);
const actorData =
@@ -89,23 +96,26 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
}
}
+ // Check registered selectors and the main item section for expanding
+ // Preserving during re-render is handled by core foundry on anything with [data-action=expandRoll]
const autoExpandRoll = game.settings.get(
- CONFIG.DH.id,
- CONFIG.DH.SETTINGS.gameSettings.appearance
- ).expandRollMessage,
- rollSections = html.querySelectorAll('.roll-part'),
- itemDesc = html.querySelector('.domain-card-move');
- rollSections.forEach(s => {
- if (s.classList.contains('roll-section')) {
- const toExpand = s.querySelector('[data-action="expandRoll"]');
- toExpand.classList.toggle('expanded', autoExpandRoll.roll);
- } else if (s.classList.contains('damage-section'))
- s.classList.toggle('expanded', autoExpandRoll.damage);
- else if (s.classList.contains('target-section')) s.classList.toggle('expanded', autoExpandRoll.target);
- else if (s.classList.contains('description-section'))
- s.classList.toggle('expanded', autoExpandRoll.desc);
- });
- if (itemDesc && autoExpandRoll.desc) itemDesc.setAttribute('open', '');
+ CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance
+ ).expandRollMessage;
+ for (const { selector, key } of DhpChatMessage.#EXPAND_SECTIONS) {
+ const elements = html.querySelectorAll(selector);
+ for (const element of elements) {
+ element.classList.toggle('expanded', autoExpandRoll[key]);
+ }
+ }
+
+ // Auto expand the item description. These are not preserved by foundry during re-renders
+ const itemDesc = html.querySelector('details');
+ if (itemDesc) {
+ const existing = document.querySelector(`.chat-message[data-message-id="${this.id}"] details`);
+ if (existing?.hasAttribute('open') ?? autoExpandRoll.desc) {
+ itemDesc.setAttribute('open', '');
+ }
+ }
}
if (!this.isAuthor && !this.speakerActor?.isOwner) {
From 0c2d25787182f6ae9145ae0f8e4ba7592320e793 Mon Sep 17 00:00:00 2001
From: WBHarry
Date: Tue, 14 Jul 2026 14:36:56 +0200
Subject: [PATCH 5/5] Raised version
---
system.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/system.json b/system.json
index 37242137..93b1dea7 100644
--- a/system.json
+++ b/system.json
@@ -2,7 +2,7 @@
"id": "daggerheart",
"title": "Daggerheart",
"description": "An unofficial implementation of the Daggerheart system",
- "version": "2.5.3",
+ "version": "2.5.4",
"compatibility": {
"minimum": "14.364",
"verified": "14.364",
@@ -10,7 +10,7 @@
},
"url": "https://github.com/Foundryborne/daggerheart",
"manifest": "https://raw.githubusercontent.com/Foundryborne/daggerheart/v14/system.json",
- "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.3/system.zip",
+ "download": "https://github.com/Foundryborne/daggerheart/releases/download/2.5.4/system.zip",
"authors": [
{
"name": "WBHarry"