From 92c7a2a9b90a6bfad9b0e9b9116a1d4db3c1b107 Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 Jul 2026 20:45:15 +0200 Subject: [PATCH 01/11] fixed button icon position and clarified countdown UI --- scripts/cosmos-dh-tweaks.mjs | 2 +- styles/cosmos-dh-tweaks.css | 56 +++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/scripts/cosmos-dh-tweaks.mjs b/scripts/cosmos-dh-tweaks.mjs index 3d110ce..658f467 100644 --- a/scripts/cosmos-dh-tweaks.mjs +++ b/scripts/cosmos-dh-tweaks.mjs @@ -53,7 +53,7 @@ Hooks.once("init", () => { addBtn = document.createElement("a"); addBtn.className = "cosmo-add-countdown"; addBtn.setAttribute("data-tooltip", game.i18n.localize("COSMOS_DH_TWEAKS.Countdown.add")); - addBtn.innerHTML = ''; + addBtn.innerHTML = '' + game.i18n.localize("COSMOS_DH_TWEAKS.Countdown.add") + ''; addBtn.addEventListener("click", (e) => { e.preventDefault(); new game.system.api.applications.ui.CountdownEdit().render(true); diff --git a/styles/cosmos-dh-tweaks.css b/styles/cosmos-dh-tweaks.css index 39486a5..3357712 100644 --- a/styles/cosmos-dh-tweaks.css +++ b/styles/cosmos-dh-tweaks.css @@ -9,21 +9,22 @@ border-radius: 4px; align-self: flex-end; overflow: hidden; + transition: width 0.25s cubic-bezier(0.4, 0, 0.2, 1) !important; } -.daggerheart.dh-style.countdowns.cosmo-minimal-countdown::before { - opacity: 1 !important; +.daggerheart.dh-style.countdowns.cosmo-minimal-countdown:hover { + width: 140px !important; } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown .countdowns-header { display: flex !important; - width: 36px !important; + width: 100% !important; height: 36px !important; flex: 0 0 36px !important; padding: 0 !important; margin: 0 !important; align-items: center; - justify-content: center; + justify-content: flex-start; border: none !important; background: transparent !important; } @@ -38,25 +39,58 @@ .daggerheart.dh-style.countdowns.cosmo-minimal-countdown .cosmo-add-countdown { display: flex !important; - align-items: center; - justify-content: center; - width: 36px !important; + align-items: center !important; + justify-content: center !important; height: 36px !important; + width: 36px; font-size: 16px !important; - color: var(--golden, #c5a45e) !important; cursor: pointer; - transition: color 0.2s ease, transform 0.2s ease, text-shadow 0.2s ease; + transition: color 0.2s ease, transform 0.2s ease; +} + +.daggerheart.dh-style.countdowns.cosmo-minimal-countdown:hover .cosmo-add-countdown { + width: 100% !important; + justify-content: flex-start !important; + padding-left: 10px !important; } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown .cosmo-add-countdown:hover { color: #ffffff !important; - transform: scale(1.15); - text-shadow: 0 0 5px var(--golden, #c5a45e) !important; + transform: scale(1.05); +} + +.daggerheart.dh-style.countdowns.cosmo-minimal-countdown .cosmo-add-label { + max-width: 0; + opacity: 0; + overflow: hidden; + white-space: nowrap; + display: inline-block; + transition: max-width 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s ease, margin-left 0.25s ease; + font-size: 13px; + font-family: inherit; + font-weight: bold; +} + +.daggerheart.dh-style.countdowns.cosmo-minimal-countdown:hover .cosmo-add-label { + max-width: 95px; + opacity: 1; + margin-left: 8px; } /* Token HUD Spotlight Button styling */ .control-icon.cosmo-spotlight-hud-btn { transition: color 0.2s ease, text-shadow 0.2s ease, background-color 0.2s ease; + display: flex !important; + align-items: center !important; + justify-content: center !important; +} + +.control-icon.cosmo-spotlight-hud-btn i { + margin: 0 !important; + padding: 0 !important; + display: block !important; + line-height: 1 !important; + text-align: center !important; } .control-icon.cosmo-spotlight-hud-btn:hover { From 2aa19a77e638f63b3e407540bec139986433c74f Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 Jul 2026 20:45:39 +0200 Subject: [PATCH 02/11] updated module version --- module.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module.json b/module.json index 1f1cf55..f16d5ce 100644 --- a/module.json +++ b/module.json @@ -2,10 +2,10 @@ "id": "cosmos-dh-tweaks", "title": "Cosmo's Daggerheart Tweaks", "description": "A selection of quality of life tweaks for the Daggerheart system on Foundry VTT.", - "version": "1.0.0", + "version": "1.0.1", "url": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks", "manifest": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/raw/branch/main/module.json", - "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.0/cosmos-dh-tweaks.zip", + "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.1/cosmos-dh-tweaks.zip", "compatibility": { "minimum": "14.000", "verified": "14.364" From f22bb6d9e22ad3863f0eccc89079fb153b9613e4 Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 Jul 2026 20:48:59 +0200 Subject: [PATCH 03/11] fixed countdown UI hover width --- styles/cosmos-dh-tweaks.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/cosmos-dh-tweaks.css b/styles/cosmos-dh-tweaks.css index 3357712..4138b45 100644 --- a/styles/cosmos-dh-tweaks.css +++ b/styles/cosmos-dh-tweaks.css @@ -13,7 +13,7 @@ } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown:hover { - width: 140px !important; + width: 150px !important; } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown .countdowns-header { From e0a9b7fcae414cb8d27cd526a62403a33e85882f Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 Jul 2026 20:49:27 +0200 Subject: [PATCH 04/11] version bumpp --- module.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module.json b/module.json index f16d5ce..fdabe26 100644 --- a/module.json +++ b/module.json @@ -2,10 +2,10 @@ "id": "cosmos-dh-tweaks", "title": "Cosmo's Daggerheart Tweaks", "description": "A selection of quality of life tweaks for the Daggerheart system on Foundry VTT.", - "version": "1.0.1", + "version": "1.0.2", "url": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks", "manifest": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/raw/branch/main/module.json", - "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.1/cosmos-dh-tweaks.zip", + "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.2/cosmos-dh-tweaks.zip", "compatibility": { "minimum": "14.000", "verified": "14.364" From 373b02a8fa98a711e9f2914e8ecf898547aae07b Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 Jul 2026 20:52:23 +0200 Subject: [PATCH 05/11] actually fix the countdown UI hover width --- module.json | 4 ++-- styles/cosmos-dh-tweaks.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/module.json b/module.json index fdabe26..d42c109 100644 --- a/module.json +++ b/module.json @@ -2,10 +2,10 @@ "id": "cosmos-dh-tweaks", "title": "Cosmo's Daggerheart Tweaks", "description": "A selection of quality of life tweaks for the Daggerheart system on Foundry VTT.", - "version": "1.0.2", + "version": "1.0.3", "url": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks", "manifest": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/raw/branch/main/module.json", - "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.2/cosmos-dh-tweaks.zip", + "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.3/cosmos-dh-tweaks.zip", "compatibility": { "minimum": "14.000", "verified": "14.364" diff --git a/styles/cosmos-dh-tweaks.css b/styles/cosmos-dh-tweaks.css index 4138b45..fbcc568 100644 --- a/styles/cosmos-dh-tweaks.css +++ b/styles/cosmos-dh-tweaks.css @@ -13,7 +13,7 @@ } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown:hover { - width: 150px !important; + width: 165px !important; } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown .countdowns-header { @@ -72,7 +72,7 @@ } .daggerheart.dh-style.countdowns.cosmo-minimal-countdown:hover .cosmo-add-label { - max-width: 95px; + max-width: 120px; opacity: 1; margin-left: 8px; } From e020a4efd73732c44897f0670c17a14b1b47816c Mon Sep 17 00:00:00 2001 From: cosmo Date: Fri, 10 Jul 2026 20:58:25 +0200 Subject: [PATCH 06/11] add spotlight request display on token option for GM --- README.md | 16 ++-- lang/en.json | 3 +- module.json | 4 +- scripts/cosmos-dh-tweaks.mjs | 149 +++++++++++++++++++++++++++++++---- 4 files changed, 149 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index cbf7e51..0121ba8 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,21 @@ A Foundry VTT v14 module that adds quality-of-life tweaks for the **Daggerheart* ### 1. Token HUD Spotlight (On by default) Adds a new Spotlight button directly to the default Token HUD menu: -* **For Players**: Allows requesting or cancelling a spotlight request for their character. -* **For GMs**: Allows granting spotlight to or removing spotlight from any token. -* **Real-time Updates**: The HUD button automatically reflects status changes in real-time if updated via the combat tracker or other canvas actions. +* **Works Everywhere**: Spotlight can be requested, granted, and taken both in and out of combat (an active encounter is not required). +* **For Players**: Allows requesting or cancelling a spotlight request for their character. Out-of-combat requests are saved securely on the character actor. +* **For GMs**: Allows granting spotlight to or removing spotlight from any token. Toggling spotlight on a token will automatically clear its active spotlight request. +* **Real-time Updates**: The HUD button automatically reflects status changes in real-time if updated via the combat tracker, canvas settings, or other players. -### 2. Minimal Countdown UI (On by default) +### 2. Spotlight Request Canvas Indicator (On by default) +Displays a visual indicator on the canvas to make managing spotlight requests easier for the GM: +* **For GMs**: Shows a small, gold hand-sparkles badge overlay in the top-right corner of any token that is currently requesting spotlight. +* **Canvas Synchronization**: Instantly appears or disappears as players toggle their spotlight requests. + +### 3. Minimal Countdown UI (On by default) Reduces screen clutter by optimizing the Countdown Tracker widget: * **For Players**: Hides the countdown widget completely if there are no active/visible countdowns. * **For GMs**: Minimizes the countdown widget to a tiny square featuring a `+` icon when empty. Clicking the `+` icon opens the system's native Countdown Edit dialog. ## Configuration -Both features can be toggled on or off independently under **Configure Settings** > **Cosmo's Daggerheart Tweaks**. +All features can be toggled on or off independently under **Configure Settings** > **Cosmo's Daggerheart Tweaks**. The *Spotlight Request Canvas Indicator* is grouped together with the HUD button option. diff --git a/lang/en.json b/lang/en.json index 5034839..2f7b71d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,12 +1,13 @@ { "COSMOS_DH_TWEAKS.Settings.TokenHudSpotlight.Name": "Token HUD Spotlight Button", "COSMOS_DH_TWEAKS.Settings.TokenHudSpotlight.Hint": "Add a Spotlight button to the Token HUD. Players can request/cancel spotlight, while GMs can grant/take it.", + "COSMOS_DH_TWEAKS.Settings.SpotlightRequestIndicator.Name": "Spotlight Request Canvas Indicator", + "COSMOS_DH_TWEAKS.Settings.SpotlightRequestIndicator.Hint": "For GMs, display a small hand icon overlay on tokens that are requesting spotlight.", "COSMOS_DH_TWEAKS.Settings.MinimalCountdownUI.Name": "Minimal Countdown UI", "COSMOS_DH_TWEAKS.Settings.MinimalCountdownUI.Hint": "When no countdowns are active, hides the UI completely for players and minimizes it to a '+' button for GMs.", "COSMOS_DH_TWEAKS.Spotlight.request": "Request Spotlight", "COSMOS_DH_TWEAKS.Spotlight.cancel": "Cancel Spotlight Request", "COSMOS_DH_TWEAKS.Spotlight.grant": "Grant Spotlight", "COSMOS_DH_TWEAKS.Spotlight.take": "Take Spotlight", - "COSMOS_DH_TWEAKS.Spotlight.warnNoCombatant": "This token is not in combat. You can only request spotlight in combat.", "COSMOS_DH_TWEAKS.Countdown.add": "Add Countdown" } diff --git a/module.json b/module.json index d42c109..b86b62e 100644 --- a/module.json +++ b/module.json @@ -2,10 +2,10 @@ "id": "cosmos-dh-tweaks", "title": "Cosmo's Daggerheart Tweaks", "description": "A selection of quality of life tweaks for the Daggerheart system on Foundry VTT.", - "version": "1.0.3", + "version": "1.1.0", "url": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks", "manifest": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/raw/branch/main/module.json", - "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.0.3/cosmos-dh-tweaks.zip", + "download": "https://git.geeks.gay/cosmo/cosmos-dh-tweaks/releases/download/1.1.0/cosmos-dh-tweaks.zip", "compatibility": { "minimum": "14.000", "verified": "14.364" diff --git a/scripts/cosmos-dh-tweaks.mjs b/scripts/cosmos-dh-tweaks.mjs index 658f467..385c295 100644 --- a/scripts/cosmos-dh-tweaks.mjs +++ b/scripts/cosmos-dh-tweaks.mjs @@ -9,6 +9,23 @@ Hooks.once("init", () => { default: true }); + game.settings.register("cosmos-dh-tweaks", "spotlightRequestIndicator", { + name: "COSMOS_DH_TWEAKS.Settings.SpotlightRequestIndicator.Name", + hint: "COSMOS_DH_TWEAKS.Settings.SpotlightRequestIndicator.Hint", + scope: "world", + config: true, + type: Boolean, + default: true, + onChange: () => { + if (canvas.ready) { + for (const token of canvas.tokens.placeables) { + if (token.refresh) token.refresh(); + else if (token.renderFlags) token.renderFlags.set({ refreshState: true }); + } + } + } + }); + game.settings.register("cosmos-dh-tweaks", "minimalCountdownUI", { name: "COSMOS_DH_TWEAKS.Settings.MinimalCountdownUI.Name", hint: "COSMOS_DH_TWEAKS.Settings.MinimalCountdownUI.Hint", @@ -97,7 +114,9 @@ Hooks.on("renderTokenHUD", (tokenHUD, html, tokenData) => { // Players: only proceed if they own the token if (!token.document.isOwner) return; const combatant = token.combatant; - active = combatant?.system.spotlight?.requesting || false; + active = combatant + ? (combatant.system.spotlight?.requesting || false) + : (token.actor?.getFlag("cosmos-dh-tweaks", "requestingSpotlight") || false); } const title = isGM @@ -154,24 +173,31 @@ Hooks.on("renderTokenHUD", (tokenHUD, html, tokenData) => { token.renderFlags.set({ refreshTurnMarker: true }); } } + + // Clear spotlight request indicator if GM grants/takes spotlight + if (token.actor?.getFlag("cosmos-dh-tweaks", "requestingSpotlight")) { + await token.actor.setFlag("cosmos-dh-tweaks", "requestingSpotlight", false); + } } else { // Player requesting / cancelling spotlight const combatant = token.combatant; - if (!combatant) { - ui.notifications.warn(game.i18n.localize("COSMOS_DH_TWEAKS.Spotlight.warnNoCombatant")); - return; - } - const isRequesting = combatant.system.spotlight.requesting; - const characters = game.combat.turns?.filter(x => !x.isNPC) ?? []; - const orderValues = characters.map(character => character.system.spotlight.requestOrderIndex || 0); - const maxRequestIndex = Math.max(...orderValues, 0); + if (combatant) { + const isRequesting = combatant.system.spotlight.requesting; + const characters = game.combat.turns?.filter(x => !x.isNPC) ?? []; + const orderValues = characters.map(character => character.system.spotlight.requestOrderIndex || 0); + const maxRequestIndex = Math.max(...orderValues, 0); - await combatant.update({ - 'system.spotlight': { - requesting: !isRequesting, - requestOrderIndex: !isRequesting ? maxRequestIndex + 1 : 0 - } - }); + await combatant.update({ + 'system.spotlight': { + requesting: !isRequesting, + requestOrderIndex: !isRequesting ? maxRequestIndex + 1 : 0 + } + }); + } else { + // Out of combat request + const isRequesting = !!token.actor?.getFlag("cosmos-dh-tweaks", "requestingSpotlight"); + await token.actor?.setFlag("cosmos-dh-tweaks", "requestingSpotlight", !isRequesting); + } } // Rerender HUD to update icon state @@ -206,3 +232,96 @@ Hooks.on("updateSetting", (setting, change, options, userId) => { } } }); + +// Trigger token refresh when Actor changes their requestingSpotlight flag +Hooks.on("updateActor", (actor, change, options, userId) => { + if (!game.ready) return; + const tokens = actor.getActiveTokens(); + for (const token of tokens) { + if (token.refresh) token.refresh(); + else if (token.renderFlags) token.renderFlags.set({ refreshState: true }); + } + if (canvas.tokens.hud.rendered && canvas.tokens.hud.object?.actor?.id === actor.id) { + canvas.tokens.hud.render(true); + } +}); + +// Trigger token refresh when Token changes +Hooks.on("updateToken", (tokenDoc, change, options, userId) => { + if (!game.ready) return; + const token = tokenDoc.object; + if (token) { + if (token.refresh) token.refresh(); + else if (token.renderFlags) token.renderFlags.set({ refreshState: true }); + } + if (canvas.tokens.hud.rendered && canvas.tokens.hud.object?.id === tokenDoc.id) { + canvas.tokens.hud.render(true); + } +}); + +// Render the GM's spotlight request indicator on the token canvas +Hooks.on("refreshToken", (token, options) => { + if (!game.ready) return; + if (!game.user?.isGM) return; + if (!game.settings.get("cosmos-dh-tweaks", "tokenHudSpotlight")) return; + if (!game.settings.get("cosmos-dh-tweaks", "spotlightRequestIndicator")) { + if (token.cosmoSpotlightIndicator) { + token.cosmoSpotlightIndicator.visible = false; + } + return; + } + + const inCombat = !!token.combatant; + const requestingInCombat = token.combatant?.system.spotlight?.requesting; + const requestingOutOfCombat = token.actor?.getFlag("cosmos-dh-tweaks", "requestingSpotlight"); + const isRequesting = inCombat ? requestingInCombat : requestingOutOfCombat; + + if (isRequesting) { + // Safe check to reconstruct if it was destroyed or detached + const isDestroyed = token.cosmoSpotlightIndicator && (token.cosmoSpotlightIndicator.destroyed || !token.children.includes(token.cosmoSpotlightIndicator)); + if (isDestroyed) { + token.cosmoSpotlightIndicator = null; + } + + if (!token.cosmoSpotlightIndicator) { + const container = new PIXI.Container(); + container.name = "cosmoSpotlightIndicator"; + + // Draw a background circle (gold) + const bg = new PIXI.Graphics(); + bg.beginFill(0xc5a45e, 0.9); + bg.lineStyle(1.5, 0xffffff, 1); + bg.drawCircle(0, 0, 11); + bg.endFill(); + container.addChild(bg); + + // Draw FontAwesome solid e05d hand sparkles icon + const style = new PIXI.TextStyle({ + fontFamily: '"Font Awesome 6 Free"', + fontWeight: "900", + fontSize: 13, + fill: "#ffffff" + }); + const text = new PIXI.Text("\ue05d", style); + text.anchor.set(0.5); + container.addChild(text); + + token.cosmoSpotlightIndicator = token.addChild(container); + } + + // Position the indicator at the top-right of the token container + const size = token.w || 50; + token.cosmoSpotlightIndicator.position.set(size - 10, 10); + token.cosmoSpotlightIndicator.visible = true; + } else { + if (token.cosmoSpotlightIndicator) { + token.cosmoSpotlightIndicator.visible = false; + } + } +}); + +Hooks.on("destroyToken", (token) => { + if (token.cosmoSpotlightIndicator) { + token.cosmoSpotlightIndicator = null; + } +}); From 7a20b05c76cd04efad8304cb712c8088617553bd Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 12 Jul 2026 02:12:09 +0200 Subject: [PATCH 07/11] add fear tracker options --- lang/en.json | 10 +- scripts/cosmos-dh-tweaks.mjs | 352 ++++++++++++++++++++++++++++++++++- styles/cosmos-dh-tweaks.css | 74 ++++++++ 3 files changed, 425 insertions(+), 11 deletions(-) diff --git a/lang/en.json b/lang/en.json index 2f7b71d..a6dde5c 100644 --- a/lang/en.json +++ b/lang/en.json @@ -9,5 +9,13 @@ "COSMOS_DH_TWEAKS.Spotlight.cancel": "Cancel Spotlight Request", "COSMOS_DH_TWEAKS.Spotlight.grant": "Grant Spotlight", "COSMOS_DH_TWEAKS.Spotlight.take": "Take Spotlight", - "COSMOS_DH_TWEAKS.Countdown.add": "Add Countdown" + "COSMOS_DH_TWEAKS.Countdown.add": "Add Countdown", + "COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Name": "Remove Fear Tracker Title Bar", + "COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Hint": "Hides the title bar of the Fear Tracker window by default, allowing you to drag it by clicking on empty space.", + "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Name": "Reset Fear Tracker Position", + "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Hint": "Resets the Fear Tracker window back to its default position and size.", + "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.ButtonText": "Reset Position", + "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success": "Fear tracker position has been reset.", + "COSMOS_DH_TWEAKS.ContextMenu.LockPosition": "Lock Position", + "COSMOS_DH_TWEAKS.ContextMenu.UnlockPosition": "Unlock Position" } diff --git a/scripts/cosmos-dh-tweaks.mjs b/scripts/cosmos-dh-tweaks.mjs index 385c295..15b6a17 100644 --- a/scripts/cosmos-dh-tweaks.mjs +++ b/scripts/cosmos-dh-tweaks.mjs @@ -38,6 +38,303 @@ Hooks.once("init", () => { } }); + game.settings.register("cosmos-dh-tweaks", "removeFearTitleBar", { + name: "COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Name", + hint: "COSMOS_DH_TWEAKS.Settings.RemoveFearTitleBar.Hint", + scope: "client", + config: true, + type: Boolean, + default: true, + onChange: () => { + if (ui.resources) { + ui.resources.updateTitleBarStatus(); + ui.resources.adjustSizeToMaxFear(); + } + } + }); + + game.settings.register("cosmos-dh-tweaks", "lockFearTrackerPosition", { + scope: "client", + config: false, + type: Boolean, + default: false + }); + + game.settings.register("cosmos-dh-tweaks", "resetFearPosition", { + name: "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Name", + hint: "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Hint", + scope: "client", + config: true, + type: Boolean, + default: false + }); + + // Override the FearTracker class in CONFIG + const OriginalFearTracker = CONFIG.ui.resources; + if (OriginalFearTracker) { + class CosmoDhTweaksFearTracker extends OriginalFearTracker { + async _onRender(context, options) { + await super._onRender(context, options); + + if (!this._eventsBound) { + this._eventsBound = true; + + this.element.addEventListener('contextmenu', (e) => { + this._showContextMenu(e); + }); + + this.element.addEventListener('mousedown', (e) => { + const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition"); + if (isLocked) { + if (e.target.closest('header, .window-resize-handle')) { + e.preventDefault(); + e.stopPropagation(); + return; + } + } + + const removeTitleBar = game.settings.get("cosmos-dh-tweaks", "removeFearTitleBar"); + if (removeTitleBar && !isLocked) { + if (e.button !== 0) return; + if (e.target.closest('i, .controls, button, input, a, .window-resize-handle')) return; + + e.preventDefault(); + this._startDrag(e); + } + }, { capture: true }); + } + + this.updateTitleBarStatus(); + this.updateLockStatus(); + this.adjustSizeToMaxFear(); + } + + updateTitleBarStatus() { + if (!this.element) return; + const removeTitleBar = game.settings.get("cosmos-dh-tweaks", "removeFearTitleBar"); + if (removeTitleBar) { + this.element.classList.add("cosmo-no-title-bar"); + } else { + this.element.classList.remove("cosmo-no-title-bar"); + } + } + + updateLockStatus() { + if (!this.element) return; + const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition"); + if (isLocked) { + this.element.classList.add("cosmo-position-locked"); + } else { + this.element.classList.remove("cosmo-position-locked"); + } + } + + getLayoutMeasurements() { + const content = this.element.querySelector('.window-content'); + const grid = this.element.querySelector('#resource-fear'); + const skulls = grid ? grid.querySelectorAll('i') : []; + + let skullW = 48; + let skullH = 48; + let gapX = 4; + let gapY = 8; + let padX = 16; + let padY = 16; + let headerH = 0; + + if (content) { + const style = window.getComputedStyle(content); + const parsedPadX = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight); + const parsedPadY = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom); + if (!isNaN(parsedPadX)) padX = parsedPadX; + if (!isNaN(parsedPadY)) padY = parsedPadY; + } + + const header = this.element.querySelector('header.window-header'); + if (header && window.getComputedStyle(header).display !== 'none') { + headerH = header.offsetHeight || 30; + } + + if (skulls.length > 0) { + const first = skulls[0]; + const firstRect = first.getBoundingClientRect(); + if (firstRect.width > 0) skullW = firstRect.width; + if (firstRect.height > 0) skullH = firstRect.height; + + if (skulls.length > 1) { + let second = null; + for (let i = 1; i < skulls.length; i++) { + const rect = skulls[i].getBoundingClientRect(); + if (Math.abs(rect.top - firstRect.top) < 2 && rect.width > 0) { + second = rect; + break; + } + } + if (second) { + gapX = second.left - firstRect.right; + } + + let nextRow = null; + for (let i = 1; i < skulls.length; i++) { + const rect = skulls[i].getBoundingClientRect(); + if (rect.top - firstRect.bottom > 2 && rect.width > 0) { + nextRow = rect; + break; + } + } + if (nextRow) { + gapY = nextRow.top - firstRect.bottom; + } + } + } + + return { skullW, skullH, gapX, gapY, padX, padY, headerH }; + } + + calculatePerfectSize(targetWidth, measurements) { + const { skullW, skullH, gapX, gapY, padX, padY, headerH } = measurements; + const display = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).displayFear; + const maxFear = this.maxFear; + + if (display === 'bar') { + const barElement = this.element.querySelector('#resource-fear'); + const barH = barElement ? barElement.getBoundingClientRect().height : 30; + const perfectH = barH + padY + headerH; + const minW = 200; + const perfectW = Math.max(minW, targetWidth || 222); + + return { + width: perfectW, + height: perfectH + }; + } else { + const w = targetWidth || 222; + let cols = Math.round((w - padX + gapX) / (skullW + gapX)); + cols = Math.max(1, Math.min(maxFear, cols)); + + const perfectW = cols * skullW + (cols - 1) * gapX + padX; + const rows = Math.ceil(maxFear / cols); + const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH; + + return { + width: perfectW, + height: perfectH + }; + } + } + + adjustSizeToMaxFear() { + if (!this.element) return; + + requestAnimationFrame(() => { + if (!this.element) return; + const measurements = this.getLayoutMeasurements(); + const pos = this.position; + + const perfect = this.calculatePerfectSize(pos.width, measurements); + + if (Math.abs(pos.width - perfect.width) > 1 || Math.abs(pos.height - perfect.height) > 1) { + this.setPosition(perfect); + } + }); + } + + setPosition(position = {}) { + if (!this.element) return super.setPosition(position); + + const measurements = this.getLayoutMeasurements(); + if (position.width !== undefined || position.height !== undefined) { + const currentW = position.width !== undefined ? position.width : this.position.width; + const perfect = this.calculatePerfectSize(currentW, measurements); + + position.width = perfect.width; + position.height = perfect.height; + } + + return super.setPosition(position); + } + + _startDrag(e) { + let isDragging = true; + const startX = e.clientX; + const startY = e.clientY; + const pos = this.position; + const startLeft = pos.left; + const startTop = pos.top; + + const onMouseMove = (event) => { + if (!isDragging) return; + const dx = event.clientX - startX; + const dy = event.clientY - startY; + + this.setPosition({ + left: startLeft + dx, + top: startTop + dy + }); + }; + + const onMouseUp = () => { + isDragging = false; + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } + + _showContextMenu(e) { + e.preventDefault(); + const existing = document.querySelector('.cosmo-fear-context-menu'); + if (existing) existing.remove(); + + const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition"); + const menu = document.createElement('div'); + menu.className = 'cosmo-fear-context-menu'; + + const lockText = isLocked + ? ` ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.UnlockPosition")}` + : ` ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.LockPosition")}`; + + menu.innerHTML = ` +
    +
  • + ${lockText} +
  • +
+ `; + + menu.style.position = 'fixed'; + menu.style.left = `${e.clientX}px`; + menu.style.top = `${e.clientY}px`; + menu.style.zIndex = 100000; + + document.body.appendChild(menu); + + menu.querySelector('[data-action="toggle-lock"]').addEventListener('click', async (event) => { + event.preventDefault(); + const currentLock = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition"); + await game.settings.set("cosmos-dh-tweaks", "lockFearTrackerPosition", !currentLock); + this.updateLockStatus(); + menu.remove(); + }); + + const closeMenu = (event) => { + if (!menu.contains(event.target)) { + menu.remove(); + document.removeEventListener('click', closeMenu); + document.removeEventListener('contextmenu', closeMenu); + } + }; + setTimeout(() => { + document.addEventListener('click', closeMenu); + document.addEventListener('contextmenu', closeMenu); + }, 0); + } + } + CONFIG.ui.resources = CosmoDhTweaksFearTracker; + } + // Override the Countdown class in CONFIG const OriginalCountdowns = CONFIG.ui.countdowns; if (OriginalCountdowns) { @@ -295,16 +592,20 @@ Hooks.on("refreshToken", (token, options) => { bg.endFill(); container.addChild(bg); - // Draw FontAwesome solid e05d hand sparkles icon - const style = new PIXI.TextStyle({ - fontFamily: '"Font Awesome 6 Free"', - fontWeight: "900", - fontSize: 13, - fill: "#ffffff" - }); - const text = new PIXI.Text("\ue05d", style); - text.anchor.set(0.5); - container.addChild(text); + // FontAwesome hand-sparkles SVG data URI + const handSparklesSvg = ` + + + +`; + const svgDataUri = "data:image/svg+xml;base64," + btoa(unescape(encodeURIComponent(handSparklesSvg))); + const handTexture = PIXI.Texture.from(svgDataUri); + const handSprite = new PIXI.Sprite(handTexture); + handSprite.anchor.set(0.5); + handSprite.width = 13; + handSprite.height = 13 / (640 / 512); + handSprite.position.set(0, -0.5); + container.addChild(handSprite); token.cosmoSpotlightIndicator = token.addChild(container); } @@ -325,3 +626,34 @@ Hooks.on("destroyToken", (token) => { token.cosmoSpotlightIndicator = null; } }); + +Hooks.on("renderSettingsConfig", (app, html, data) => { + const input = html.querySelector + ? html.querySelector('[name="cosmos-dh-tweaks.resetFearPosition"]') + : html.find('[name="cosmos-dh-tweaks.resetFearPosition"]')[0]; + + if (input) { + const formGroup = input.closest('.form-group'); + if (formGroup) { + const formFields = formGroup.querySelector('.form-fields'); + if (formFields) { + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'cosmo-reset-fear-pos-btn'; + button.innerHTML = ` ${game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearPosition.ButtonText")}`; + button.addEventListener('click', async (e) => { + e.preventDefault(); + await game.user.unsetFlag(CONFIG.DH.id, 'app.resources.position'); + if (ui.resources && ui.resources.rendered) { + const defaultPos = CONFIG.ui.resources.DEFAULT_OPTIONS.position; + await ui.resources.setPosition(defaultPos); + } + ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success")); + }); + + formFields.innerHTML = ''; + formFields.appendChild(button); + } + } + } +}); diff --git a/styles/cosmos-dh-tweaks.css b/styles/cosmos-dh-tweaks.css index fbcc568..38264d7 100644 --- a/styles/cosmos-dh-tweaks.css +++ b/styles/cosmos-dh-tweaks.css @@ -103,3 +103,77 @@ border: 1px solid var(--golden, #c5a45e) !important; box-shadow: 0 0 5px rgba(197, 164, 94, 0.5) !important; } + +/* Fear Tracker Window Customizations */ +#resources.cosmo-no-title-bar header.window-header { + display: none !important; +} + +#resources.cosmo-no-title-bar { + min-height: 4rem !important; +} + +#resources.cosmo-position-locked .window-resize-handle { + display: none !important; +} + +/* Context menu styling */ +.cosmo-fear-context-menu { + background: rgba(24, 22, 46, 0.95); + backdrop-filter: blur(10px); + border: 2px solid rgb(153, 122, 79); + border-radius: 6px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5); + padding: 4px 0; + min-width: 150px; + font-family: inherit; +} + +.cosmo-context-menu-list { + list-style: none; + margin: 0; + padding: 0; +} + +.cosmo-context-menu-item { + padding: 8px 12px; + color: #d3d3d3; + font-size: 13px; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + transition: background-color 0.2s, color 0.2s; +} + +.cosmo-context-menu-item:hover { + background-color: rgba(197, 164, 94, 0.2); + color: #ffffff; +} + +.cosmo-context-menu-item i { + width: 16px; + text-align: center; +} + +/* Reset button styling */ +button.cosmo-reset-fear-pos-btn { + background: rgba(197, 164, 94, 0.1); + border: 1px solid rgb(153, 122, 79); + color: #d3d3d3; + padding: 4px 12px; + border-radius: 4px; + cursor: pointer; + display: inline-flex; + align-items: center; + gap: 6px; + font-size: 13px; + font-family: inherit; + transition: background-color 0.2s, color 0.2s, border-color 0.2s; +} + +button.cosmo-reset-fear-pos-btn:hover { + background: rgba(197, 164, 94, 0.25); + border-color: #ffffff; + color: #ffffff; +} From 318498489bd08b370af7393d5b208d07ad58ec3b Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 12 Jul 2026 02:29:09 +0200 Subject: [PATCH 08/11] updated horizontal width to accomodate all icons --- scripts/cosmos-dh-tweaks.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/cosmos-dh-tweaks.mjs b/scripts/cosmos-dh-tweaks.mjs index 15b6a17..6240cff 100644 --- a/scripts/cosmos-dh-tweaks.mjs +++ b/scripts/cosmos-dh-tweaks.mjs @@ -212,9 +212,9 @@ Hooks.once("init", () => { let cols = Math.round((w - padX + gapX) / (skullW + gapX)); cols = Math.max(1, Math.min(maxFear, cols)); - const perfectW = cols * skullW + (cols - 1) * gapX + padX; + const perfectW = cols * skullW + (cols - 1) * gapX + padX + 2; const rows = Math.ceil(maxFear / cols); - const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH; + const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH + 2; return { width: perfectW, From 737690f42a2d686a64958425f6dbc5abe1c26d59 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 12 Jul 2026 19:22:20 +0200 Subject: [PATCH 09/11] add scaling option for fear tracker --- lang/en.json | 11 ++- scripts/cosmos-dh-tweaks.mjs | 146 ++++++++++++++++++++++++++++++++--- styles/cosmos-dh-tweaks.css | 121 ++++++++++++++++++++++++++++- 3 files changed, 262 insertions(+), 16 deletions(-) diff --git a/lang/en.json b/lang/en.json index a6dde5c..4a235b7 100644 --- a/lang/en.json +++ b/lang/en.json @@ -17,5 +17,14 @@ "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.ButtonText": "Reset Position", "COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success": "Fear tracker position has been reset.", "COSMOS_DH_TWEAKS.ContextMenu.LockPosition": "Lock Position", - "COSMOS_DH_TWEAKS.ContextMenu.UnlockPosition": "Unlock Position" + "COSMOS_DH_TWEAKS.ContextMenu.UnlockPosition": "Unlock Position", + "COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Name": "Fear Tracker Scale", + "COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Hint": "Scale the size of the Fear Tracker window and its icons.", + "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Name": "Reset Fear Tracker Scale", + "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Hint": "Resets the Fear Tracker scale back to 100%.", + "COSMOS_DH_TWEAKS.Settings.ResetFearScale.ButtonText": "Reset Scale", + "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Success": "Fear tracker scale has been reset.", + "COSMOS_DH_TWEAKS.ContextMenu.ResetPosition": "Reset Position", + "COSMOS_DH_TWEAKS.ContextMenu.ResetScale": "Reset Scale", + "COSMOS_DH_TWEAKS.ContextMenu.ScaleHeader": "Scale" } diff --git a/scripts/cosmos-dh-tweaks.mjs b/scripts/cosmos-dh-tweaks.mjs index 6240cff..eac433a 100644 --- a/scripts/cosmos-dh-tweaks.mjs +++ b/scripts/cosmos-dh-tweaks.mjs @@ -69,6 +69,35 @@ Hooks.once("init", () => { default: false }); + game.settings.register("cosmos-dh-tweaks", "fearTrackerScale", { + name: "COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Name", + hint: "COSMOS_DH_TWEAKS.Settings.FearTrackerScale.Hint", + scope: "client", + config: true, + type: Number, + range: { + min: 0.5, + max: 1.5, + step: 0.05 + }, + default: 1.0, + onChange: () => { + if (ui.resources && ui.resources.rendered) { + ui.resources.updateScale(); + ui.resources.adjustSizeToMaxFear(); + } + } + }); + + game.settings.register("cosmos-dh-tweaks", "resetFearScale", { + name: "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Name", + hint: "COSMOS_DH_TWEAKS.Settings.ResetFearScale.Hint", + scope: "client", + config: true, + type: Boolean, + default: false + }); + // Override the FearTracker class in CONFIG const OriginalFearTracker = CONFIG.ui.resources; if (OriginalFearTracker) { @@ -106,9 +135,16 @@ Hooks.once("init", () => { this.updateTitleBarStatus(); this.updateLockStatus(); + this.updateScale(); this.adjustSizeToMaxFear(); } + updateScale() { + if (!this.element) return; + const scale = game.settings.get("cosmos-dh-tweaks", "fearTrackerScale") ?? 1.0; + this.element.style.setProperty('--cosmo-fear-scale', scale); + } + updateTitleBarStatus() { if (!this.element) return; const removeTitleBar = game.settings.get("cosmos-dh-tweaks", "removeFearTitleBar"); @@ -192,24 +228,35 @@ Hooks.once("init", () => { } calculatePerfectSize(targetWidth, measurements) { - const { skullW, skullH, gapX, gapY, padX, padY, headerH } = measurements; + const scale = game.settings.get("cosmos-dh-tweaks", "fearTrackerScale") ?? 1.0; + + // Divide measurements by scale to work with unscaled base measurements + const skullW = measurements.skullW / scale; + const skullH = measurements.skullH / scale; + const gapX = measurements.gapX / scale; + const gapY = measurements.gapY / scale; + const padX = measurements.padX / scale; + const padY = measurements.padY / scale; + const headerH = measurements.headerH / scale; + const display = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).displayFear; const maxFear = this.maxFear; if (display === 'bar') { const barElement = this.element.querySelector('#resource-fear'); - const barH = barElement ? barElement.getBoundingClientRect().height : 30; + const barH = barElement ? (barElement.getBoundingClientRect().height / scale) : 30; const perfectH = barH + padY + headerH; const minW = 200; - const perfectW = Math.max(minW, targetWidth || 222); + const targetWUnscaled = targetWidth ? (targetWidth / scale) : 222; + const perfectW = Math.max(minW, targetWUnscaled); return { - width: perfectW, - height: perfectH + width: perfectW * scale, + height: perfectH * scale }; } else { - const w = targetWidth || 222; - let cols = Math.round((w - padX + gapX) / (skullW + gapX)); + const targetWUnscaled = targetWidth ? (targetWidth / scale) : 222; + let cols = Math.round((targetWUnscaled - padX + gapX) / (skullW + gapX)); cols = Math.max(1, Math.min(maxFear, cols)); const perfectW = cols * skullW + (cols - 1) * gapX + padX + 2; @@ -217,8 +264,8 @@ Hooks.once("init", () => { const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH + 2; return { - width: perfectW, - height: perfectH + width: perfectW * scale, + height: perfectH * scale }; } } @@ -289,6 +336,7 @@ Hooks.once("init", () => { if (existing) existing.remove(); const isLocked = game.settings.get("cosmos-dh-tweaks", "lockFearTrackerPosition"); + const currentScale = game.settings.get("cosmos-dh-tweaks", "fearTrackerScale") ?? 1.0; const menu = document.createElement('div'); menu.className = 'cosmo-fear-context-menu'; @@ -301,6 +349,20 @@ Hooks.once("init", () => {
  • ${lockText}
  • +
  • + ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.ResetPosition")} +
  • +
  • + ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.ResetScale")} +
  • +
  • ${game.i18n.localize("COSMOS_DH_TWEAKS.ContextMenu.ScaleHeader")}
  • +
  • + + + + + +
  • `; @@ -319,6 +381,37 @@ Hooks.once("init", () => { menu.remove(); }); + menu.querySelector('[data-action="reset-position"]').addEventListener('click', async (event) => { + event.preventDefault(); + await game.user.unsetFlag(CONFIG.DH.id, 'app.resources.position'); + if (this.rendered) { + const defaultPos = CONFIG.ui.resources.DEFAULT_OPTIONS.position; + await this.setPosition(defaultPos); + } + ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearPosition.Success")); + menu.remove(); + }); + + menu.querySelector('[data-action="reset-scale"]').addEventListener('click', async (event) => { + event.preventDefault(); + await game.settings.set("cosmos-dh-tweaks", "fearTrackerScale", 1.0); + this.updateScale(); + this.adjustSizeToMaxFear(); + ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearScale.Success")); + menu.remove(); + }); + + menu.querySelectorAll('.scale-btn').forEach(btn => { + btn.addEventListener('click', async (event) => { + event.preventDefault(); + const newScale = parseFloat(btn.dataset.scale); + await game.settings.set("cosmos-dh-tweaks", "fearTrackerScale", newScale); + this.updateScale(); + this.adjustSizeToMaxFear(); + menu.remove(); + }); + }); + const closeMenu = (event) => { if (!menu.contains(event.target)) { menu.remove(); @@ -628,12 +721,12 @@ Hooks.on("destroyToken", (token) => { }); Hooks.on("renderSettingsConfig", (app, html, data) => { - const input = html.querySelector + const posInput = html.querySelector ? html.querySelector('[name="cosmos-dh-tweaks.resetFearPosition"]') : html.find('[name="cosmos-dh-tweaks.resetFearPosition"]')[0]; - if (input) { - const formGroup = input.closest('.form-group'); + if (posInput) { + const formGroup = posInput.closest('.form-group'); if (formGroup) { const formFields = formGroup.querySelector('.form-fields'); if (formFields) { @@ -656,4 +749,33 @@ Hooks.on("renderSettingsConfig", (app, html, data) => { } } } + + const scaleInput = html.querySelector + ? html.querySelector('[name="cosmos-dh-tweaks.resetFearScale"]') + : html.find('[name="cosmos-dh-tweaks.resetFearScale"]')[0]; + + if (scaleInput) { + const formGroup = scaleInput.closest('.form-group'); + if (formGroup) { + const formFields = formGroup.querySelector('.form-fields'); + if (formFields) { + const button = document.createElement('button'); + button.type = 'button'; + button.className = 'cosmo-reset-fear-scale-btn'; + button.innerHTML = ` ${game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearScale.ButtonText")}`; + button.addEventListener('click', async (e) => { + e.preventDefault(); + await game.settings.set("cosmos-dh-tweaks", "fearTrackerScale", 1.0); + if (ui.resources && ui.resources.rendered) { + ui.resources.updateScale(); + ui.resources.adjustSizeToMaxFear(); + } + ui.notifications.info(game.i18n.localize("COSMOS_DH_TWEAKS.Settings.ResetFearScale.Success")); + }); + + formFields.innerHTML = ''; + formFields.appendChild(button); + } + } + } }); diff --git a/styles/cosmos-dh-tweaks.css b/styles/cosmos-dh-tweaks.css index 38264d7..6847e08 100644 --- a/styles/cosmos-dh-tweaks.css +++ b/styles/cosmos-dh-tweaks.css @@ -156,8 +156,8 @@ text-align: center; } -/* Reset button styling */ -button.cosmo-reset-fear-pos-btn { +button.cosmo-reset-fear-pos-btn, +button.cosmo-reset-fear-scale-btn { background: rgba(197, 164, 94, 0.1); border: 1px solid rgb(153, 122, 79); color: #d3d3d3; @@ -172,8 +172,123 @@ button.cosmo-reset-fear-pos-btn { transition: background-color 0.2s, color 0.2s, border-color 0.2s; } -button.cosmo-reset-fear-pos-btn:hover { +button.cosmo-reset-fear-pos-btn:hover, +button.cosmo-reset-fear-scale-btn:hover { background: rgba(197, 164, 94, 0.25); border-color: #ffffff; color: #ffffff; } + +/* Fear Tracker Scaling */ +#resources { + --cosmo-fear-scale: 1; +} + +/* Scale the skull icons */ +#resources #resource-fear i { + width: calc(3rem * var(--cosmo-fear-scale, 1)) !important; + height: calc(3rem * var(--cosmo-fear-scale, 1)) !important; + font-size: calc(1.125rem * var(--cosmo-fear-scale, 1)) !important; +} + +/* Scale hover state font-size for GMs */ +#resources.isGM #resource-fear i:hover { + font-size: calc(1.25rem * var(--cosmo-fear-scale, 1)) !important; +} + +/* Scale the control buttons (+ and -) */ +#resources #resource-fear .controls { + width: calc(30px * var(--cosmo-fear-scale, 1)) !important; + height: calc(30px * var(--cosmo-fear-scale, 1)) !important; + font-size: calc(1.25rem * var(--cosmo-fear-scale, 1)) !important; +} + +#resources #resource-fear .controls:hover { + font-size: calc(1.5rem * var(--cosmo-fear-scale, 1)) !important; +} + +/* Scale the resource bar */ +#resources #resource-fear .resource-bar { + font-size: calc(1.25rem * var(--cosmo-fear-scale, 1)) !important; + padding: calc(0.25rem * var(--cosmo-fear-scale, 1)) calc(0.5rem * var(--cosmo-fear-scale, 1)) !important; +} + +/* Scale the window padding and flex gap */ +#resources .window-content { + padding: calc(0.5rem * var(--cosmo-fear-scale, 1)) !important; +} + +#resources #resource-fear { + gap: calc(0.5rem * var(--cosmo-fear-scale, 1)) calc(0.25rem * var(--cosmo-fear-scale, 1)) !important; +} + +/* Scale the window header */ +#resources header.window-header { + font-size: calc(14px * var(--cosmo-fear-scale, 1)) !important; + min-height: calc(30px * var(--cosmo-fear-scale, 1)) !important; + height: calc(30px * var(--cosmo-fear-scale, 1)) !important; +} + +#resources header.window-header h1 { + font-size: calc(14px * var(--cosmo-fear-scale, 1)) !important; +} + +#resources header.window-header button, +#resources header.window-header a, +#resources header.window-header i { + font-size: calc(14px * var(--cosmo-fear-scale, 1)) !important; +} + +#resources header.window-header button, +#resources header.window-header a { + width: calc(30px * var(--cosmo-fear-scale, 1)) !important; + height: calc(30px * var(--cosmo-fear-scale, 1)) !important; + line-height: calc(30px * var(--cosmo-fear-scale, 1)) !important; +} + +/* Context menu scale controls styling */ +.cosmo-context-menu-header { + padding: 6px 12px 2px 12px; + font-size: 11px; + text-transform: uppercase; + color: #a49a85; + font-weight: bold; + letter-spacing: 0.5px; + border-top: 1px solid rgba(153, 122, 79, 0.3); + margin-top: 4px; +} + +.cosmo-context-menu-scale-controls { + display: flex; + padding: 4px 12px 8px 12px; + gap: 4px; + justify-content: space-between; +} + +.cosmo-context-menu-scale-controls button.scale-btn { + background: rgba(255, 255, 255, 0.05); + border: 1px solid rgba(153, 122, 79, 0.4); + color: #d3d3d3; + padding: 2px 6px; + border-radius: 3px; + cursor: pointer; + font-size: 11px; + font-family: inherit; + transition: all 0.2s ease; + flex: 1; + text-align: center; +} + +.cosmo-context-menu-scale-controls button.scale-btn:hover { + background: rgba(197, 164, 94, 0.25); + border-color: #ffffff; + color: #ffffff; +} + +.cosmo-context-menu-scale-controls button.scale-btn.active { + background: rgba(197, 164, 94, 0.4); + border-color: rgb(197, 164, 94); + color: #ffffff; + font-weight: bold; +} + From 0eb8ed5430e7588040c0aa90a27e9b62d58008e6 Mon Sep 17 00:00:00 2001 From: cosmo Date: Sun, 12 Jul 2026 19:27:07 +0200 Subject: [PATCH 10/11] fix window size on <100% scale --- scripts/cosmos-dh-tweaks.mjs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/scripts/cosmos-dh-tweaks.mjs b/scripts/cosmos-dh-tweaks.mjs index eac433a..df6088c 100644 --- a/scripts/cosmos-dh-tweaks.mjs +++ b/scripts/cosmos-dh-tweaks.mjs @@ -242,30 +242,31 @@ Hooks.once("init", () => { const display = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance).displayFear; const maxFear = this.maxFear; + // Subtract the fixed border buffer from targetWidth before unscaling to get exact content size + const targetWUnscaled = targetWidth ? Math.max(0, (targetWidth - 8) / scale) : 222; + if (display === 'bar') { const barElement = this.element.querySelector('#resource-fear'); const barH = barElement ? (barElement.getBoundingClientRect().height / scale) : 30; const perfectH = barH + padY + headerH; const minW = 200; - const targetWUnscaled = targetWidth ? (targetWidth / scale) : 222; const perfectW = Math.max(minW, targetWUnscaled); return { - width: perfectW * scale, - height: perfectH * scale + width: Math.ceil(perfectW * scale) + 8, + height: Math.ceil(perfectH * scale) + 6 }; } else { - const targetWUnscaled = targetWidth ? (targetWidth / scale) : 222; let cols = Math.round((targetWUnscaled - padX + gapX) / (skullW + gapX)); cols = Math.max(1, Math.min(maxFear, cols)); - const perfectW = cols * skullW + (cols - 1) * gapX + padX + 2; + const perfectW = cols * skullW + (cols - 1) * gapX + padX; const rows = Math.ceil(maxFear / cols); - const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH + 2; + const perfectH = rows * skullH + (rows - 1) * gapY + padY + headerH; return { - width: perfectW * scale, - height: perfectH * scale + width: Math.ceil(perfectW * scale) + 8, + height: Math.ceil(perfectH * scale) + 6 }; } } From 566a9ad8458dbcc8eba93d4a04454a253f1d88e2 Mon Sep 17 00:00:00 2001 From: cosmo Date: Mon, 13 Jul 2026 16:23:50 +0200 Subject: [PATCH 11/11] update readme with fear tracker features --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0121ba8..b74d562 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,18 @@ Reduces screen clutter by optimizing the Countdown Tracker widget: * **For Players**: Hides the countdown widget completely if there are no active/visible countdowns. * **For GMs**: Minimizes the countdown widget to a tiny square featuring a `+` icon when empty. Clicking the `+` icon opens the system's native Countdown Edit dialog. +### 4. Fear Tracker Customizations (On by default) +Enhances the Daggerheart Fear Tracker with improved layout options, sizing, and a convenient context menu: +* **Clean Borderless Layout**: Hides the Fear Tracker's title bar for a cleaner look. When the title bar is hidden, you can still move the tracker by clicking and dragging any empty space on it. +* **Right-Click Context Menu**: Right-clicking the Fear Tracker opens a quick-access menu to lock/unlock position, reset position or scale, and scale the UI. +* **Position Locking**: Lock the Fear Tracker's position to prevent accidental dragging or resizing. +* **Flexible Sizing & Scaling**: Scale the Fear Tracker and all of its elements (70%, 85%, 100%, 115%, or 130%) to fit your screen. +* **Perfect-Fit Auto-Resizing**: Dynamically resizes the tracker window to perfectly fit the current number of Fear tokens/rows without leaving empty borders or margins. +* **Position & Scale Resets**: Settings and buttons to instantly restore the tracker back to its default position and scale. + ## Configuration -All features can be toggled on or off independently under **Configure Settings** > **Cosmo's Daggerheart Tweaks**. The *Spotlight Request Canvas Indicator* is grouped together with the HUD button option. +All features can be toggled on or off independently under **Configure Settings** > **Cosmo's Daggerheart Tweaks**. +* The *Spotlight Request Canvas Indicator* is grouped together with the HUD button option. +* The Fear Tracker settings allow you to hide the title bar, scale the tracker, or reset its scale and position. Position locking can also be toggled directly from the tracker's right-click context menu. +