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 01/13] [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 02/13] 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 03/13] 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 04/13] 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 05/13] 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"
From 79d652261459eeed358ff597154e36c7e8fa9cd6 Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Tue, 14 Jul 2026 08:39:53 -0400
Subject: [PATCH 06/13] [Feature] Add support for GM Notes (#2082)
* Add support for GM Notes
* Localize GM Notes header label
* Fix active editor height and menu auto sizing
* Add tooltip to add gm note button
---
daggerheart.mjs | 4 +
lang/en.json | 6 ++
module/applications/sheets/api/base-item.mjs | 52 ++++++++++++-
module/data/item/base.mjs | 23 ++++--
styles/less/global/elements.less | 36 ++++++++-
styles/less/global/feature-section.less | 2 +-
styles/less/global/global.less | 4 +
styles/less/global/item-header.less | 35 +++++----
styles/less/global/tab-description.less | 77 ++++++++++++++++++-
.../sheets/actors/actor-sheet-shared.less | 4 -
styles/less/sheets/items/beastform.less | 9 +++
styles/less/sheets/items/feature.less | 10 +--
styles/less/sheets/items/index.less | 4 +-
.../less/sheets/items/item-sheet-shared.less | 20 ++++-
styles/less/utils/mixin.less | 2 +-
system.json | 20 ++---
.../sheets/global/tabs/tab-description.hbs | 34 +++++---
17 files changed, 278 insertions(+), 64 deletions(-)
diff --git a/daggerheart.mjs b/daggerheart.mjs
index 63127aa4..f91eedbe 100644
--- a/daggerheart.mjs
+++ b/daggerheart.mjs
@@ -267,6 +267,10 @@ Hooks.on('i18nInit', () => {
});
Hooks.on('setup', () => {
+ if (game.user.isGM) {
+ document.body.dataset.gm = true;
+ }
+
CONFIG.statusEffects = [
...CONFIG.statusEffects.filter(x => !['dead', 'unconscious'].includes(x.id)),
...Object.values(SYSTEM.GENERAL.conditions()).map(x => ({
diff --git a/lang/en.json b/lang/en.json
index 99b6baeb..508a771d 100755
--- a/lang/en.json
+++ b/lang/en.json
@@ -2550,6 +2550,9 @@
},
"identifier": {
"label": "Identifier"
+ },
+ "gmNotes": {
+ "label": "GM Notes"
}
},
"Ancestry": {
@@ -2565,6 +2568,9 @@
"severe": "Severe Threshold"
}
},
+ "Base": {
+ "addGMNote": "Add GM Note"
+ },
"Beastform": {
"FIELDS": {
"beastformType": { "label": "Beastform Type" },
diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs
index 1e08fc05..70a6bcc6 100644
--- a/module/applications/sheets/api/base-item.mjs
+++ b/module/applications/sheets/api/base-item.mjs
@@ -30,7 +30,8 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
addFeature: DHBaseItemSheet.#addFeature,
deleteFeature: DHBaseItemSheet.#deleteFeature,
addResource: DHBaseItemSheet.#addResource,
- removeResource: DHBaseItemSheet.#removeResource
+ removeResource: DHBaseItemSheet.#removeResource,
+ editGMNote: DHBaseItemSheet.#onEditGMNote
},
dragDrop: [
{ dragSelector: null, dropSelector: '.drop-section' },
@@ -76,10 +77,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
/**@inheritdoc */
async _preparePartContext(partId, context, options) {
await super._preparePartContext(partId, context, options);
+ const TextEditor = foundry.applications.ux.TextEditor.implementation;
switch (partId) {
case 'description':
- context.enrichedDescription = await this.document.system.getEnrichedDescription();
+ context.enrichedDescription = await this.document.system.getEnrichedDescription({ gmNotes: false });
+ context.enrichedGMNotes = await TextEditor.implementation.enrichHTML(this.item.system.gmNotes, {
+ relativeTo: this.item,
+ rollData: this.item.getRollData(),
+ secrets: this.item.isOwner
+ })
break;
case 'effects':
await this._prepareEffectsContext(context, options);
@@ -331,4 +338,45 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
}
}
}
+
+ /**
+ * Handles the Add GM Note button being pressed. This is only used when an item has no GM notes.
+ * Later edits to a GM note instead go through the normal editor toggle workflow.
+ * @this DHBaseItemSheet
+ */
+ static #onEditGMNote() {
+ // Open the editor, which might be hidden. We remove the css class to hide temporarily
+ // so that menu auto resizing functions properly.
+ const editor = this.element.querySelector('prose-mirror[name="system.gmNotes"]');
+ const wasHidden = editor.classList.contains('hide-if-inactive');
+ editor.classList.remove('hide-if-inactive');
+ editor.open = true;
+ window.setTimeout(() => {
+ if (wasHidden) editor.classList.add('hide-if-inactive');
+ }, 0);
+ }
+
+ /** @inheritdoc */
+ async _onRender(context, options) {
+ await super._onRender(context, options);
+
+ // Render an add gmnotes button if there are no set GM notes.
+ // We need to re-render on close since its possible to prosemirror to close *without* triggering a full re-render
+ if (game.user.isGM && !this.item.system.gmNotes) {
+ const description = this.element.querySelector('[name="system.description"]');
+ const addButton = () => {
+ if (description.querySelector('[data-action=editGMNote]')) return;
+
+ const button = document.createElement('button');
+ button.type = 'button';
+ button.classList.add('icon', 'toggle', 'fa-regular', 'fa-note-medical');
+ button.dataset.action = 'editGMNote';
+ button.dataset.tooltip = 'DAGGERHEART.ITEMS.Base.addGMNote';
+ description.appendChild(button);
+ }
+
+ addButton();
+ description.addEventListener('close', () => addButton());
+ }
+ }
}
diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs
index 131ef10f..095ba8f2 100644
--- a/module/data/item/base.mjs
+++ b/module/data/item/base.mjs
@@ -49,7 +49,10 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
})
};
- if (this.metadata.hasDescription) schema.description = new fields.HTMLField({ required: true, nullable: true });
+ if (this.metadata.hasDescription) {
+ schema.description = new fields.HTMLField({ required: true, nullable: true });
+ schema.gmNotes = new fields.HTMLField({ required: true, nullable: true });
+ }
if (this.metadata.hasResource) {
schema.resource = new fields.SchemaField(
@@ -134,7 +137,7 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
/**
* Augments the description for the item with type specific info to display. Implemented in applicable item subtypes.
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
- * @returns {string}
+ * @returns {Promise<{ prefix: string | null; value: string | null; suffix: string | null }>}
*/
async getDescriptionData(_options) {
return { prefix: null, value: this.description, suffix: null };
@@ -145,14 +148,24 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
* @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' }
* @returns {Promise}
*/
- async getEnrichedDescription() {
+ async getEnrichedDescription({ gmNotes = true } = {}) {
if (!this.metadata.hasDescription) return '';
const { prefix, value, suffix } = await this.getDescriptionData();
- const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n
\n');
+ let fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n
\n');
+ if (this.gmNotes && gmNotes) {
+ const gmNotesElement = document.createElement('section');
+ gmNotesElement.classList.add('gm-notes-section');
+ gmNotesElement.dataset.visibility = 'gm';
+ const header = document.createElement('header');
+ header.classList.add('gm-notes');
+ header.textContent = _loc('DAGGERHEART.ITEMS.FIELDS.gmNotes.label');
+ gmNotesElement.innerHTML = header.outerHTML + this.gmNotes;
+ fullDescription += gmNotesElement.outerHTML;
+ }
return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, {
- relativeTo: this,
+ relativeTo: this.parent,
rollData: this.getRollData(),
secrets: this.parent.isOwner
});
diff --git a/styles/less/global/elements.less b/styles/less/global/elements.less
index 1b7ed072..d31e09b9 100755
--- a/styles/less/global/elements.less
+++ b/styles/less/global/elements.less
@@ -595,7 +595,37 @@
margin-top: 4px;
color: light-dark(#14142599, #efe6d850);
font-size: var(--font-size-12);
- padding-left: 3px;
+ padding-left: 16px;
+ }
+
+ section.gm-notes-section {
+ padding-bottom: var(--spacer-4);
+ header.gm-notes + p {
+ margin-top: 0;
+ }
+ }
+
+ header.gm-notes {
+ position: relative;
+ display: flex;
+ gap: 6px;
+ align-items: center;
+ &::before,
+ &::after {
+ content: " ";
+ flex: 1;
+ border-bottom: 1px solid var(--color-dark-6);
+ }
+ &::before {
+ mask-image: linear-gradient(270deg, black 0%, black calc(100% - 10px), transparent 100%);
+ }
+ &::after {
+ mask-image: linear-gradient(270deg, transparent 0%, black 10px, black 100%);
+ }
+ margin-top: var(--spacer-8);
+ margin-bottom: var(--spacer-4);
+ font-size: var(--font-size-11);
+ text-transform: uppercase;
}
secret-block {
@@ -866,4 +896,8 @@
right: 2px;
}
}
+
+ .gm-notes {
+ font-style: italic;
+ }
}
diff --git a/styles/less/global/feature-section.less b/styles/less/global/feature-section.less
index 2fd4e20f..ecfb4ff6 100644
--- a/styles/less/global/feature-section.less
+++ b/styles/less/global/feature-section.less
@@ -3,7 +3,7 @@
.sheet.daggerheart.dh-style.item {
.tab.features {
- padding: 0 10px;
+ padding: 7px 10px;
overflow-y: auto;
.feature-list {
display: flex;
diff --git a/styles/less/global/global.less b/styles/less/global/global.less
index 19a9e519..2c44c94e 100644
--- a/styles/less/global/global.less
+++ b/styles/less/global/global.less
@@ -111,3 +111,7 @@ body.theme-light,
.themed.theme-light {
color-scheme: light;
}
+
+body:not([data-gm=true]) [data-visibility="gm"] {
+ display: none;
+}
\ No newline at end of file
diff --git a/styles/less/global/item-header.less b/styles/less/global/item-header.less
index f47ca7dc..1a8d7fce 100755
--- a/styles/less/global/item-header.less
+++ b/styles/less/global/item-header.less
@@ -12,12 +12,14 @@
});
.application.sheet.daggerheart.dh-style {
+ --portrait-size: 150px;
+
.item-sheet-header {
display: flex;
.profile {
- height: 150px;
- width: 150px;
+ height: var(--portrait-size);
+ width: var(--portrait-size);
object-fit: cover;
border-right: 1px solid light-dark(@dark-blue, @golden);
border-bottom: 1px solid light-dark(@dark-blue, @golden);
@@ -34,19 +36,24 @@
text-align: center;
width: 80%;
- .item-name input[type='text'] {
- font-size: var(--font-size-32);
- height: 42px;
- text-align: center;
- width: 90%;
- transition: all 0.3s ease;
- outline: 2px solid transparent;
- border: 1px solid transparent;
+ .item-name {
+ display: flex;
+ flex-direction: column;
+ margin: 10px 10px 0 10px;
+ input[type='text'] {
+ font-size: var(--font-size-30);
+ text-align: center;
+ width: 100%;
+ transition: all 0.3s ease;
+ outline: 2px solid transparent;
+ border: 1px solid transparent;
+ text-overflow: ellipsis;
- &:hover[type='text'],
- &:focus[type='text'] {
- box-shadow: none;
- outline: 2px solid light-dark(@dark-blue, @golden);
+ &:hover[type='text'],
+ &:focus[type='text'] {
+ box-shadow: none;
+ outline: 2px solid light-dark(@dark-blue, @golden);
+ }
}
}
diff --git a/styles/less/global/tab-description.less b/styles/less/global/tab-description.less
index 5c18e02b..e2869723 100644
--- a/styles/less/global/tab-description.less
+++ b/styles/less/global/tab-description.less
@@ -6,11 +6,80 @@
display: flex;
flex-direction: column;
flex: 1;
- overflow-y: hidden !important;
- padding-top: 10px;
+ overflow: hidden;
+ padding: 0;
+ margin: 0;
- prose-mirror.active + .artist-attribution {
- display: none;
+ .description-section {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: auto;
+ padding: 12px 16px 4px 16px;
+ .with-scroll-shadows();
+ prose-mirror {
+ button.toggle {
+ top: 0px;
+ right: 0;
+ }
+ button[data-action=editGMNote] {
+ right: calc(var(--button-size) + 4px);
+ }
+ &.inactive {
+ height: unset!important;
+ overflow: unset;
+ .editor-content {
+ position: relative;
+ overflow: unset;
+
+ // Allows content links to peek out
+ margin-top: -4px;
+ padding: 4px 0 0 0;
+ }
+ }
+ &.active {
+ --min-height: 250px;
+ padding: 8px 0 0 16px;
+ button[data-action=editGMNote] {
+ display: none;
+ }
+ .editor-content {
+ padding-right: 16px;
+ padding-bottom: 4px;
+ }
+ }
+ }
+ /** Hide editors that are empty when inactive if we need them to be */
+ prose-mirror.inactive.hide-if-inactive {
+ display: none;
+ }
+ &:has(prose-mirror.active) {
+ padding: 0;
+ }
+ /** Description should fill available room (with overriden exceptions) */
+ prose-mirror[name="system.description"] {
+ flex: 1 0;
+ }
+ &:has(prose-mirror[name="system.gmNotes"]:not(.hide-if-inactive)) {
+ prose-mirror.inactive {
+ --min-height: 3rem;
+ &[name="system.description"] {
+ flex: 0 0;
+ }
+ &[name="system.gmNotes"] {
+ flex: 1 0;
+ }
+ }
+ }
+ }
+
+ /** Hide other elements if an editor is open */
+ &:has(prose-mirror.active) {
+ prose-mirror.inactive,
+ header.gm-notes,
+ .artist-attribution {
+ display: none;
+ }
}
}
}
diff --git a/styles/less/sheets/actors/actor-sheet-shared.less b/styles/less/sheets/actors/actor-sheet-shared.less
index 3e233013..a464d7a1 100644
--- a/styles/less/sheets/actors/actor-sheet-shared.less
+++ b/styles/less/sheets/actors/actor-sheet-shared.less
@@ -93,10 +93,6 @@
padding: 8px 0 0 16px;
}
}
-
- .artist-attribution {
- padding-left: 16px;
- }
}
.search-section {
diff --git a/styles/less/sheets/items/beastform.less b/styles/less/sheets/items/beastform.less
index 100b024a..017c4ef0 100644
--- a/styles/less/sheets/items/beastform.less
+++ b/styles/less/sheets/items/beastform.less
@@ -1,4 +1,6 @@
.application.sheet.daggerheart.dh-style.beastform {
+ --portrait-size: 130px;
+
.settings.tab {
.advantage-on-section {
display: flex;
@@ -9,4 +11,11 @@
font-style: italic;
}
}
+ .tab.features.active {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px;
+ .stable-scroll-container();
+ }
}
diff --git a/styles/less/sheets/items/feature.less b/styles/less/sheets/items/feature.less
index f3c7cd49..9166fac1 100644
--- a/styles/less/sheets/items/feature.less
+++ b/styles/less/sheets/items/feature.less
@@ -2,17 +2,9 @@
@import '../../utils/fonts.less';
.application.sheet.daggerheart.dh-style.feature {
- .item-sheet-header {
- display: flex;
-
- .profile {
- height: 130px;
- width: 130px;
- }
- }
+ --portrait-size: 130px;
section.tab {
- height: 400px;
overflow-y: auto;
}
}
diff --git a/styles/less/sheets/items/index.less b/styles/less/sheets/items/index.less
index 7c40a2e3..7f9bb684 100644
--- a/styles/less/sheets/items/index.less
+++ b/styles/less/sheets/items/index.less
@@ -1,6 +1,6 @@
+@import './item-sheet-shared.less';
@import './beastform.less';
@import './class.less';
@import './domain-card.less';
@import './feature.less';
-@import './heritage.less';
-@import './item-sheet-shared.less';
\ No newline at end of file
+@import './heritage.less';
\ No newline at end of file
diff --git a/styles/less/sheets/items/item-sheet-shared.less b/styles/less/sheets/items/item-sheet-shared.less
index 5155ad70..63846b8e 100644
--- a/styles/less/sheets/items/item-sheet-shared.less
+++ b/styles/less/sheets/items/item-sheet-shared.less
@@ -1,4 +1,4 @@
-.application.sheet.daggerheart.dh-style.item {
+.item.daggerheart.dh-style:where(.application.sheet) {
&.minimized {
.attribution-header-label {
display: none;
@@ -14,4 +14,22 @@
button.plain.inline-control {
flex: 0 0 auto;
}
+
+ .tab-navigation {
+ margin-bottom: 0;
+ }
+
+ /** Default tab stylings */
+ .tab.active {
+ padding-top: 8px;
+ .with-scroll-shadows();
+
+ &.effects {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 8px calc(12px - var(--scrollbar-width)) 4px 12px;
+ .stable-scroll-container();
+ }
+ }
}
diff --git a/styles/less/utils/mixin.less b/styles/less/utils/mixin.less
index fb70d0a3..429fb3ef 100644
--- a/styles/less/utils/mixin.less
+++ b/styles/less/utils/mixin.less
@@ -226,7 +226,7 @@
ul,
ol {
- margin: 1rem 0;
+ margin: 0.5rem 0;
padding: 0 0 0 1.25rem;
li {
diff --git a/system.json b/system.json
index 93b1dea7..214ab2ee 100644
--- a/system.json
+++ b/system.json
@@ -256,34 +256,34 @@
},
"Item": {
"ancestry": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"community": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"class": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"subclass": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"feature": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"domainCard": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"loot": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"consumable": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"weapon": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"armor": {
- "htmlFields": ["description"]
+ "htmlFields": ["description", "gmNotes"]
},
"beastform": {}
},
diff --git a/templates/sheets/global/tabs/tab-description.hbs b/templates/sheets/global/tabs/tab-description.hbs
index 71995a51..3fdf1a93 100755
--- a/templates/sheets/global/tabs/tab-description.hbs
+++ b/templates/sheets/global/tabs/tab-description.hbs
@@ -1,11 +1,25 @@
-
- {{formInput systemFields.description value=document.system.description enriched=enrichedDescription toggled=true}}
-
- {{#if (and showAttribution document.system.attribution.artist)}}
-
- {{/if}}
+
+
+ {{formInput systemFields.description value=document.system.description enriched=enrichedDescription toggled=true}}
+ {{#if (and systemFields.gmNotes @root.user.isGM)}}
+
+ {{#if enrichedGMNotes}}
+ {{localize "DAGGERHEART.ITEMS.FIELDS.gmNotes.label"}}
+ {{/if}}
+ {{{enrichedGMNotes}}}
+
+ {{/if}}
+
+ {{#if (and showAttribution document.system.attribution.artist)}}
+
+ {{/if}}
\ No newline at end of file
From d76b4bb707fbfad3412c3a14882eeb7f4c4bcf64 Mon Sep 17 00:00:00 2001
From: Carlos Fernandez
Date: Sat, 18 Jul 2026 05:42:50 -0400
Subject: [PATCH 07/13] Fix chat being jumpy as timestamps get longer (#2095)
---
styles/less/global/chat.less | 87 +++++++++++++++++++-----------
templates/ui/chat/chat-message.hbs | 41 +++++++-------
2 files changed, 79 insertions(+), 49 deletions(-)
diff --git a/styles/less/global/chat.less b/styles/less/global/chat.less
index b9478ea4..4e29cfff 100644
--- a/styles/less/global/chat.less
+++ b/styles/less/global/chat.less
@@ -7,12 +7,12 @@
.chat-log .chat-message {
background-image: url('../assets/parchments/dh-parchment-light.png');
- .message-header .message-header-metadata .message-metadata,
- .message-header .message-header-main .message-sub-header-container {
+ .message-header .message-header-main .message-metadata,
+ .message-header .message-header-main .name {
color: @dark;
}
- .message-header .message-header-main .message-sub-header-container h4 {
+ .message-header .message-header-main h4 {
color: @dark-blue;
}
@@ -42,27 +42,12 @@
.message-header {
display: flex;
- gap: 4px;
+ gap: 8px;
padding: 8px;
+ align-items: center;
- .message-header-metadata {
- flex: none;
- display: flex;
- flex-direction: column;
-
- .message-metadata {
- font-family: @font-body;
- color: @beige;
- }
- }
-
- .message-header-main {
- display: flex;
- align-items: center;
- gap: 8px;
- flex: 1;
- overflow: hidden;
-
+ .portrait {
+ flex: 0 0 auto;
.actor-img {
border-radius: 50%;
width: 40px;
@@ -70,20 +55,60 @@
object-fit: cover;
object-position: top center;
}
+ }
- .message-sub-header-container {
+ .message-header-main {
+ display: flex;
+ align-items: center;
+ column-gap: 4px;
+ row-gap: 2px;
+ flex: 1;
+ overflow: hidden;
+
+ display: grid;
+ grid-template:
+ "title metadata"
+ "subtitle subtitle";
+
+ h4 {
+ font-size: var(--font-size-16);
+ font-weight: bold;
+ margin-bottom: 0;
+ font-family: @font-subtitle;
+ color: @golden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ flex: 1;
+ overflow: hidden;
+ align-self: flex-end;
+ }
+
+ .message-metadata {
+ font-family: @font-body;
+ color: @beige;
+ white-space: nowrap;
+ align-items: baseline;
+ .message-timestamp {
+ font-size: var(--font-size-11);
+ }
+ }
+
+ .subtitle {
+ grid-area: subtitle;
flex: 1;
display: flex;
- flex-direction: column;
justify-content: space-between;
color: @beige;
-
- h4 {
- font-size: var(--font-size-16);
- font-weight: bold;
- margin-bottom: 0;
- font-family: @font-subtitle;
- color: @golden;
+ gap: 4px;
+ align-items: baseline;
+ line-height: 1;
+ .name {
+ flex: 1;
+ }
+ .whisper-to {
+ color: @color-text-subtle;
+ flex: 0;
+ white-space: nowrap;
}
}
}
diff --git a/templates/ui/chat/chat-message.hbs b/templates/ui/chat/chat-message.hbs
index 87ecce39..92490cec 100644
--- a/templates/ui/chat/chat-message.hbs
+++ b/templates/ui/chat/chat-message.hbs
@@ -1,23 +1,18 @@