From f58c8411aa710780e9fb6c95db071c3f2dba5e01 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 14 Dec 2025 15:27:45 -0700 Subject: [PATCH 01/17] Adds a keybind into the module that allows GMs to temporarily hide their cursor from everyone else (including other GMs) --- langs/en-ca.json | 12 +++ module/hooks/init.mjs | 2 + module/settings/toggleMouseBroadcast.mjs | 130 +++++++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 module/settings/toggleMouseBroadcast.mjs diff --git a/langs/en-ca.json b/langs/en-ca.json index 02cd095..777e3c9 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -65,6 +65,12 @@ "label": "Configure Hotbar" } }, + "keybindings": { + "toggleMouseBroadcast": { + "name": "Toggle Mouse Position", + "hint": "(v13+) Temporarily turns off or on the mouse cursor position that players see." + } + }, "apps": { "no-settings-to-display": "No settings to display", "make-global-reference": "Make Global Reference" @@ -77,6 +83,12 @@ "ignoreAndDontShowAgain": "", "disableEntirely": "" } + }, + "notifs": { + "toggleMouseBroadcast": { + "hidingCursor": "Hiding your cursor from others!", + "showingCursor": "Showing your cursor to others!" + } } } } diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index 60cde1b..d8bae1f 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -1,5 +1,6 @@ // Settings import { preventMovementHistory } from "../settings/preventMovementHistory.mjs"; +import { toggleMouseBroadcast } from "../settings/toggleMouseBroadcast.mjs"; // Utils import { Logger } from "../utils/Logger.mjs"; @@ -14,4 +15,5 @@ Hooks.on(`init`, () => { Logger.log(`Initializing`); preventMovementHistory(); + toggleMouseBroadcast(); }); diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/settings/toggleMouseBroadcast.mjs new file mode 100644 index 0000000..6f70692 --- /dev/null +++ b/module/settings/toggleMouseBroadcast.mjs @@ -0,0 +1,130 @@ +import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; +import { __ID__ } from "../consts.mjs"; +import { Logger } from "../utils/Logger.mjs"; + +const key = `toggleMouseBroadcast`; + +/** @type {number | null} */ +let notifID = null; + +export function toggleMouseBroadcast() { + status[key] = SettingStatusEnum.Unknown; + + // #region Registration + const prevented = Hooks.call(`${__ID__}.preventSetting`, key); + if (!prevented) { + Logger.log(`Preventing setting "${key}" from being registered`); + status[key] = SettingStatusEnum.Blocked; + return; + }; + Logger.log(`Registering setting: ${key}`); + + // MARK: setting + game.settings.register(__ID__, key, { + scope: `client`, + config: false, + default: true, + }); + + // MARK: keybind + game.keybindings.register(__ID__, key, { + name: `OFT.keybindings.${key}.name`, + hint: `OFT.keybindings.${key}.hint`, + precedence: CONST.KEYBINDING_PRECEDENCE.NORMAL, + restricted: true, + editable: [ + { key: `KeyH` }, + ], + onDown: (event) => { + if (!game.user.hasPermission(`SHOW_CURSOR`)) { return }; + event.preventDefault?.(); + const current = game.settings.get(__ID__, key); + + if (current) { + notifID = ui.notifications.warn( + `OFT.notifs.${key}.hidingCursor`, + { console: false, localize: true, permanent: true }, + ).id; + } else { + if (notifID != null) { + ui.notifications.remove(notifID); + }; + ui.notifications.success( + `OFT.notifs.${key}.showingCursor`, + { console: false, localize: true }, + ); + }; + + game.settings.set(__ID__, key, !current); + + // Hide the existing cursor + game.user.broadcastActivity({ cursor: null }); + return true; + }, + }); + // #endregion Registration + + // #region Implementation + Hooks.on(`renderControlsConfig`, renderControlsConfigHandler); + Hooks.once(`ready`, readyHandler); + + class OFTControlsLayer extends CONFIG.Canvas.layers.controls.layerClass { + _onMouseMove(currentPos) { + if (!game.settings.get(__ID__, key)) { + game.user.broadcastActivity({}); + }; + super._onMouseMove(currentPos); + }; + }; + CONFIG.Canvas.layers.controls.layerClass = OFTControlsLayer; + + // #endregion Implementation + + status[key] = SettingStatusEnum.Registered; +}; + +// #region Helpers +const tabGroup = `categories`; + +/** + * Handle showing the "hiding your cursor" notification when the user + * connects to the server initially, allowing the current state to be + * correctly described to the user. + */ +function readyHandler() { + const hideCursor = !game.settings.get(__ID__, key); + const canShowCursor = game.user.hasPermission(`SHOW_CURSOR`); + if (hideCursor && canShowCursor) { + notifID = ui.notifications.warn( + `OFT.notifs.${key}.hidingCursor`, + { console: false, localize: true, permanent: true }, + ).id; + }; +}; + +/** + * Handles hiding the keybinding from the configuration if and when the user + * does not have the SHOW_CURSOR permission since this keybinding is really + * useless if they can't even broadcast their cursor in the first place. + */ +function renderControlsConfigHandler(_app, element) { + + if (game.user.hasPermission(`SHOW_CURSOR`)) { return }; + + const keybindingList = element.querySelector(`section[data-group="${tabGroup}"][data-tab="${__ID__}"]`); + const tabButton = element.querySelector(`button[data-group="${tabGroup}"][data-tab="${__ID__}"]`); + + if (keybindingList.childElementCount === 0) { + tabButton?.remove(); + return; + }; + + const keybind = keybindingList.querySelector(`.form-group[data-action-id="${__ID__}.${key}"]`); + keybind?.remove(); + + if (keybindingList.childElementCount === 0) { + keybindingList.remove(); + tabButton.remove(); + }; +}; +// #endregion Helpers From e6e02301ab66259c4d4c4d9e96e9c66b0a4c7506 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 20:02:55 -0700 Subject: [PATCH 02/17] Improve wording of keybinding name and description --- langs/en-ca.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/langs/en-ca.json b/langs/en-ca.json index 777e3c9..d2e091a 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -67,8 +67,8 @@ }, "keybindings": { "toggleMouseBroadcast": { - "name": "Toggle Mouse Position", - "hint": "(v13+) Temporarily turns off or on the mouse cursor position that players see." + "name": "Toggle Show Cursor", + "hint": "(v13+) Temporarily turns off the mouse cursor position that other players can see. Hides the cursor until you activate this keybind again." } }, "apps": { From 8c12c60815fed61ce57e721c7a07ad3623e0cab4 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 20:03:29 -0700 Subject: [PATCH 03/17] Unset the notification ID when we remove it --- module/settings/toggleMouseBroadcast.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/settings/toggleMouseBroadcast.mjs index 6f70692..189f6db 100644 --- a/module/settings/toggleMouseBroadcast.mjs +++ b/module/settings/toggleMouseBroadcast.mjs @@ -48,6 +48,7 @@ export function toggleMouseBroadcast() { } else { if (notifID != null) { ui.notifications.remove(notifID); + notifID = null; }; ui.notifications.success( `OFT.notifs.${key}.showingCursor`, From f088cc474e953e58f9fb6d19eacc10e162b85d7d Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 20:04:32 -0700 Subject: [PATCH 04/17] Remove excess DOM manipulation --- module/settings/toggleMouseBroadcast.mjs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/settings/toggleMouseBroadcast.mjs index 189f6db..3761f47 100644 --- a/module/settings/toggleMouseBroadcast.mjs +++ b/module/settings/toggleMouseBroadcast.mjs @@ -115,11 +115,6 @@ function renderControlsConfigHandler(_app, element) { const keybindingList = element.querySelector(`section[data-group="${tabGroup}"][data-tab="${__ID__}"]`); const tabButton = element.querySelector(`button[data-group="${tabGroup}"][data-tab="${__ID__}"]`); - if (keybindingList.childElementCount === 0) { - tabButton?.remove(); - return; - }; - const keybind = keybindingList.querySelector(`.form-group[data-action-id="${__ID__}.${key}"]`); keybind?.remove(); From 9c8b6f37f9aa24b7e4fcdd13e47e9e3a15c2dfb8 Mon Sep 17 00:00:00 2001 From: Oliver Date: Thu, 18 Dec 2025 22:32:46 -0700 Subject: [PATCH 05/17] Move the tweak prevention into a helper function and rename the module hooks w/ compatibility code --- module/hooks/init.mjs | 4 +-- module/hooks/oft.preRegisterTweak.mjs | 19 +++++++++++ module/hooks/oft.preventSetting.mjs | 12 ------- module/hooks/oft.settingStatuses.mjs | 13 -------- module/hooks/oft.tweakStatuses.mjs | 9 +++++ module/hooks/setup.mjs | 33 ++++++++++++------- .../addGlobalDocReferrer.mjs | 2 ++ .../autoUnpauseOnLoad.mjs | 9 ++--- .../{settings => tweaks}/chatImageLinks.mjs | 11 ++----- .../chatSidebarBackground.mjs | 2 ++ .../{settings => tweaks}/hotbarButtonGap.mjs | 9 ++--- .../{settings => tweaks}/hotbarButtonSize.mjs | 9 ++--- .../preventMovementHistory.mjs | 9 ++--- .../preventTokenRotation.mjs | 9 ++--- .../preventUserConfigOpen.mjs | 9 ++--- .../{settings => tweaks}/repositionHotbar.mjs | 9 ++--- .../startSidebarExpanded.mjs | 2 ++ .../startingSidebarTab.mjs | 2 ++ .../toggleMouseBroadcast.mjs | 8 ++--- module/utils/preRegisterTweak.mjs | 24 ++++++++++++++ 20 files changed, 102 insertions(+), 102 deletions(-) create mode 100644 module/hooks/oft.preRegisterTweak.mjs delete mode 100644 module/hooks/oft.preventSetting.mjs delete mode 100644 module/hooks/oft.settingStatuses.mjs create mode 100644 module/hooks/oft.tweakStatuses.mjs rename module/{settings => tweaks}/addGlobalDocReferrer.mjs (89%) rename module/{settings => tweaks}/autoUnpauseOnLoad.mjs (82%) rename module/{settings => tweaks}/chatImageLinks.mjs (92%) rename module/{settings => tweaks}/chatSidebarBackground.mjs (89%) rename module/{settings => tweaks}/hotbarButtonGap.mjs (84%) rename module/{settings => tweaks}/hotbarButtonSize.mjs (84%) rename module/{settings => tweaks}/preventMovementHistory.mjs (81%) rename module/{settings => tweaks}/preventTokenRotation.mjs (84%) rename module/{settings => tweaks}/preventUserConfigOpen.mjs (80%) rename module/{settings => tweaks}/repositionHotbar.mjs (87%) rename module/{settings => tweaks}/startSidebarExpanded.mjs (87%) rename module/{settings => tweaks}/startingSidebarTab.mjs (92%) rename module/{settings => tweaks}/toggleMouseBroadcast.mjs (94%) create mode 100644 module/utils/preRegisterTweak.mjs diff --git a/module/hooks/init.mjs b/module/hooks/init.mjs index d8bae1f..729a004 100644 --- a/module/hooks/init.mjs +++ b/module/hooks/init.mjs @@ -1,6 +1,6 @@ // Settings -import { preventMovementHistory } from "../settings/preventMovementHistory.mjs"; -import { toggleMouseBroadcast } from "../settings/toggleMouseBroadcast.mjs"; +import { preventMovementHistory } from "../tweaks/preventMovementHistory.mjs"; +import { toggleMouseBroadcast } from "../tweaks/toggleMouseBroadcast.mjs"; // Utils import { Logger } from "../utils/Logger.mjs"; diff --git a/module/hooks/oft.preRegisterTweak.mjs b/module/hooks/oft.preRegisterTweak.mjs new file mode 100644 index 0000000..47de773 --- /dev/null +++ b/module/hooks/oft.preRegisterTweak.mjs @@ -0,0 +1,19 @@ +/* +This hook is used to give external modules or systems the ability to interact +with the tweak registration lifecycle to do something before a tweak is registered +or being able to prevent registration of incompatible tweaks. + +The hook receives a string indicating which tweak this hook is being called for +and a boolean value indicating if the tweak is considered invasive. Returning +explicit false prevents that tweak from being registered. + +Invasive tweaks are additions that manipulate or override Document or helper +classes. An example of an invasive tweak is the "toggleMouseBroadcast", +tweak which replaces the existing "CONFIG.Canvas.layers.controls.layerClass" +class, most of these tweaks do smartly extend from the same CONFIG class +that they replace, however if they override a part of the class that +other modules/systems rely on, then that is a good time to block that +specific tweak's registration. + +Call Signature: (tweakKey: string, isInvasive: boolean) => (void | boolean) +*/ diff --git a/module/hooks/oft.preventSetting.mjs b/module/hooks/oft.preventSetting.mjs deleted file mode 100644 index 500dc8e..0000000 --- a/module/hooks/oft.preventSetting.mjs +++ /dev/null @@ -1,12 +0,0 @@ -/* -This hook is used for invasive hooks that we want to provide the -option for systems and other modules to be able to disable in case -of incompatabilities for whatever reason. This can also be used -internally within this module if we discover incompatabilites with -systems and want to disable it on our side. - -This file is meant more documentation than anything at this point in -time. - -Call Signature: (settingKey: string) => (void | boolean) -*/ diff --git a/module/hooks/oft.settingStatuses.mjs b/module/hooks/oft.settingStatuses.mjs deleted file mode 100644 index 46bef18..0000000 --- a/module/hooks/oft.settingStatuses.mjs +++ /dev/null @@ -1,13 +0,0 @@ -/* -This hook is used to enable any modules that attempt to disable settings -or just want to investigate what settings are enabled to be able to get -a ping with information about which settings where registered entirely -and which weren't. The object that is passed to this is frozen and is -not meant to be edited as you cannot de-register nor prevent setting -registration from this hook. For that see the "oft.preventSetting" hook. - -This file is meant more documentation than anything at this point in -time. - -Call Signature: (settings: Record) => void -*/ diff --git a/module/hooks/oft.tweakStatuses.mjs b/module/hooks/oft.tweakStatuses.mjs new file mode 100644 index 0000000..966c521 --- /dev/null +++ b/module/hooks/oft.tweakStatuses.mjs @@ -0,0 +1,9 @@ +/* +This hook is used to broadcast the final status of all tweaks within the module, +allowing them to either confirm their registration didn't happen or to do +something once all of the module setup has been finalized. Tweak statuses cannot +be blocked or changed from this hook, to prevent a tweak from being registered +you should use the "oft.preRegisterTweak" hook. + +Call Signature: (settings: Record) => void +*/ diff --git a/module/hooks/setup.mjs b/module/hooks/setup.mjs index ae63ec6..206867a 100644 --- a/module/hooks/setup.mjs +++ b/module/hooks/setup.mjs @@ -1,15 +1,15 @@ // Settings -import { addGlobalDocReferrer } from "../settings/addGlobalDocReferrer.mjs"; -import { autoUnpauseOnLoad } from "../settings/autoUnpauseOnLoad.mjs"; -import { chatImageLinks } from "../settings/chatImageLinks.mjs"; -import { chatSidebarBackground } from "../settings/chatSidebarBackground.mjs"; -import { hotbarButtonGap } from "../settings/hotbarButtonGap.mjs"; -import { hotbarButtonSize } from "../settings/hotbarButtonSize.mjs"; -import { preventTokenRotation } from "../settings/preventTokenRotation.mjs"; -import { preventUserConfigOpen } from "../settings/preventUserConfigOpen.mjs"; -import { repositionHotbar } from "../settings/repositionHotbar.mjs"; -import { startingSidebarTab } from "../settings/startingSidebarTab.mjs"; -import { startSidebarExpanded } from "../settings/startSidebarExpanded.mjs"; +import { addGlobalDocReferrer } from "../tweaks/addGlobalDocReferrer.mjs"; +import { autoUnpauseOnLoad } from "../tweaks/autoUnpauseOnLoad.mjs"; +import { chatImageLinks } from "../tweaks/chatImageLinks.mjs"; +import { chatSidebarBackground } from "../tweaks/chatSidebarBackground.mjs"; +import { hotbarButtonGap } from "../tweaks/hotbarButtonGap.mjs"; +import { hotbarButtonSize } from "../tweaks/hotbarButtonSize.mjs"; +import { preventTokenRotation } from "../tweaks/preventTokenRotation.mjs"; +import { preventUserConfigOpen } from "../tweaks/preventUserConfigOpen.mjs"; +import { repositionHotbar } from "../tweaks/repositionHotbar.mjs"; +import { startingSidebarTab } from "../tweaks/startingSidebarTab.mjs"; +import { startSidebarExpanded } from "../tweaks/startSidebarExpanded.mjs"; // Apps import { DevSettingsMenu } from "../apps/DevSettingsMenu.mjs"; @@ -51,7 +51,16 @@ Hooks.on(`setup`, () => { preventTokenRotation(); preventUserConfigOpen(); - Hooks.callAll(`oft.settingStatuses`, deepFreeze(status)); + // Compatibility Code + if (Hooks.events[`oft.settingStatuses`] != null) { + foundry.utils.logCompatibilityWarning( + `The hook "${__ID__}.settingStatuses" has been renamed "${__ID__}.tweakStatuses".`, + { since: `v1.2.0`, until: `v2.0.0`, stack: false, once: true }, + ); + Hooks.callAll(`oft.settingStatuses`, deepFreeze(status)); + }; + + Hooks.callAll(`${__ID__}.tweakStatuses`, deepFreeze(status)); game.modules.get(__ID__).api = deepFreeze({ settings: status, }); diff --git a/module/settings/addGlobalDocReferrer.mjs b/module/tweaks/addGlobalDocReferrer.mjs similarity index 89% rename from module/settings/addGlobalDocReferrer.mjs rename to module/tweaks/addGlobalDocReferrer.mjs index d955d40..34850b9 100644 --- a/module/settings/addGlobalDocReferrer.mjs +++ b/module/tweaks/addGlobalDocReferrer.mjs @@ -1,12 +1,14 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerDevSetting } from "../utils/SubMenuSettings.mjs"; const key = `addGlobalDocReferrer`; export function addGlobalDocReferrer() { status[key] = SettingStatusEnum.Unknown; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/autoUnpauseOnLoad.mjs b/module/tweaks/autoUnpauseOnLoad.mjs similarity index 82% rename from module/settings/autoUnpauseOnLoad.mjs rename to module/tweaks/autoUnpauseOnLoad.mjs index de15fa4..afe057e 100644 --- a/module/settings/autoUnpauseOnLoad.mjs +++ b/module/tweaks/autoUnpauseOnLoad.mjs @@ -1,19 +1,14 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerDevSetting } from "../utils/SubMenuSettings.mjs"; const key = `autoUnpauseOnLoad`; export function autoUnpauseOnLoad() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/chatImageLinks.mjs b/module/tweaks/chatImageLinks.mjs similarity index 92% rename from module/settings/chatImageLinks.mjs rename to module/tweaks/chatImageLinks.mjs index c25d56f..1e6f708 100644 --- a/module/settings/chatImageLinks.mjs +++ b/module/tweaks/chatImageLinks.mjs @@ -1,8 +1,9 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const { DialogV2 } = foundry.applications.api; +// const { DialogV2 } = foundry.applications.api; const key = `chatImageLinks`; const IMAGE_TYPES = [ @@ -15,13 +16,7 @@ const IMAGE_TYPES = [ export function chatImageLinks() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/chatSidebarBackground.mjs b/module/tweaks/chatSidebarBackground.mjs similarity index 89% rename from module/settings/chatSidebarBackground.mjs rename to module/tweaks/chatSidebarBackground.mjs index 5fce77a..d138ef5 100644 --- a/module/settings/chatSidebarBackground.mjs +++ b/module/tweaks/chatSidebarBackground.mjs @@ -1,11 +1,13 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `chatSidebarBackground`; export function chatSidebarBackground() { status[key] = SettingStatusEnum.Unknown; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/hotbarButtonGap.mjs b/module/tweaks/hotbarButtonGap.mjs similarity index 84% rename from module/settings/hotbarButtonGap.mjs rename to module/tweaks/hotbarButtonGap.mjs index 98c7cbf..f41b02d 100644 --- a/module/settings/hotbarButtonGap.mjs +++ b/module/tweaks/hotbarButtonGap.mjs @@ -1,19 +1,14 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerCategorySetting } from "../utils/SubMenuSettings.mjs"; const key = `hotbarButtonGap`; export function hotbarButtonGap() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/hotbarButtonSize.mjs b/module/tweaks/hotbarButtonSize.mjs similarity index 84% rename from module/settings/hotbarButtonSize.mjs rename to module/tweaks/hotbarButtonSize.mjs index 7b4797b..78ed9d3 100644 --- a/module/settings/hotbarButtonSize.mjs +++ b/module/tweaks/hotbarButtonSize.mjs @@ -1,19 +1,14 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerCategorySetting } from "../utils/SubMenuSettings.mjs"; const key = `hotbarButtonSize`; export function hotbarButtonSize() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/preventMovementHistory.mjs b/module/tweaks/preventMovementHistory.mjs similarity index 81% rename from module/settings/preventMovementHistory.mjs rename to module/tweaks/preventMovementHistory.mjs index 8f4d18c..ffee599 100644 --- a/module/settings/preventMovementHistory.mjs +++ b/module/tweaks/preventMovementHistory.mjs @@ -1,18 +1,13 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `preventMovementHistory`; export function preventMovementHistory() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/preventTokenRotation.mjs b/module/tweaks/preventTokenRotation.mjs similarity index 84% rename from module/settings/preventTokenRotation.mjs rename to module/tweaks/preventTokenRotation.mjs index 258920c..08daa35 100644 --- a/module/settings/preventTokenRotation.mjs +++ b/module/tweaks/preventTokenRotation.mjs @@ -1,18 +1,13 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `preventTokenRotation`; export function preventTokenRotation() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; /** @type {number|null} */ let hookID = null; diff --git a/module/settings/preventUserConfigOpen.mjs b/module/tweaks/preventUserConfigOpen.mjs similarity index 80% rename from module/settings/preventUserConfigOpen.mjs rename to module/tweaks/preventUserConfigOpen.mjs index 3b7646d..cccbe51 100644 --- a/module/settings/preventUserConfigOpen.mjs +++ b/module/tweaks/preventUserConfigOpen.mjs @@ -1,18 +1,13 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `preventUserConfigOpen`; export function preventUserConfigOpen() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/repositionHotbar.mjs b/module/tweaks/repositionHotbar.mjs similarity index 87% rename from module/settings/repositionHotbar.mjs rename to module/tweaks/repositionHotbar.mjs index cce0c5f..fc24d4f 100644 --- a/module/settings/repositionHotbar.mjs +++ b/module/tweaks/repositionHotbar.mjs @@ -1,19 +1,14 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerCategorySetting } from "../utils/SubMenuSettings.mjs"; const key = `repositionHotbar`; export function repositionHotbar() { status[key] = SettingStatusEnum.Unknown; - - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/startSidebarExpanded.mjs b/module/tweaks/startSidebarExpanded.mjs similarity index 87% rename from module/settings/startSidebarExpanded.mjs rename to module/tweaks/startSidebarExpanded.mjs index 4cf9f2d..7e220c0 100644 --- a/module/settings/startSidebarExpanded.mjs +++ b/module/tweaks/startSidebarExpanded.mjs @@ -1,11 +1,13 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `startSidebarExpanded`; export function startSidebarExpanded() { status[key] = SettingStatusEnum.Unknown; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/startingSidebarTab.mjs b/module/tweaks/startingSidebarTab.mjs similarity index 92% rename from module/settings/startingSidebarTab.mjs rename to module/tweaks/startingSidebarTab.mjs index 02fab17..6b9e10a 100644 --- a/module/settings/startingSidebarTab.mjs +++ b/module/tweaks/startingSidebarTab.mjs @@ -1,11 +1,13 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `startingSidebarTab`; export function startingSidebarTab() { status[key] = SettingStatusEnum.Unknown; + if (preventTweakRegistration(key)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/settings/toggleMouseBroadcast.mjs b/module/tweaks/toggleMouseBroadcast.mjs similarity index 94% rename from module/settings/toggleMouseBroadcast.mjs rename to module/tweaks/toggleMouseBroadcast.mjs index 3761f47..63a4df6 100644 --- a/module/settings/toggleMouseBroadcast.mjs +++ b/module/tweaks/toggleMouseBroadcast.mjs @@ -1,6 +1,7 @@ import { SettingStatusEnum, status } from "../utils/SettingStatus.mjs"; import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; const key = `toggleMouseBroadcast`; @@ -9,14 +10,9 @@ let notifID = null; export function toggleMouseBroadcast() { status[key] = SettingStatusEnum.Unknown; + if (preventTweakRegistration(key)) { return }; // #region Registration - const prevented = Hooks.call(`${__ID__}.preventSetting`, key); - if (!prevented) { - Logger.log(`Preventing setting "${key}" from being registered`); - status[key] = SettingStatusEnum.Blocked; - return; - }; Logger.log(`Registering setting: ${key}`); // MARK: setting diff --git a/module/utils/preRegisterTweak.mjs b/module/utils/preRegisterTweak.mjs new file mode 100644 index 0000000..8d6875b --- /dev/null +++ b/module/utils/preRegisterTweak.mjs @@ -0,0 +1,24 @@ +import { SettingStatusEnum, status } from "./SettingStatus.mjs"; +import { __ID__ } from "../consts.mjs"; +import { Logger } from "./Logger.mjs"; + +export function preventTweakRegistration(key, invasive = false) { + let prevented = Hooks.call(`${__ID__}.preRegisterTweak`, key, invasive); + + // Compatibility Code + if (Hooks.events[`${__ID__}.preventSetting`] != null) { + foundry.utils.logCompatibilityWarning( + `The hook "${__ID__}.preventSetting" has been renamed "${__ID__}.registerTweak".`, + { since: `v1.2.0`, until: `v2.0.0`, stack: false, once: true }, + ); + prevented &&= Hooks.call(`${__ID__}.preventSetting`); + }; + + if (!prevented) { + Logger.log(`Preventing setting "${key}" from being registered`); + status[key] = SettingStatusEnum.Blocked; + return true; + }; + + return false; +}; From 037533401bf145ea3fd3f8c1aad115a0f27c76dd Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 19 Dec 2025 00:23:00 -0700 Subject: [PATCH 06/17] Add the invasive flag to the relevant settings --- module/tweaks/preventMovementHistory.mjs | 2 +- module/tweaks/toggleMouseBroadcast.mjs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/tweaks/preventMovementHistory.mjs b/module/tweaks/preventMovementHistory.mjs index ffee599..c70382e 100644 --- a/module/tweaks/preventMovementHistory.mjs +++ b/module/tweaks/preventMovementHistory.mjs @@ -7,7 +7,7 @@ const key = `preventMovementHistory`; export function preventMovementHistory() { status[key] = SettingStatusEnum.Unknown; - if (preventTweakRegistration(key)) { return }; + if (preventTweakRegistration(key, true)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); diff --git a/module/tweaks/toggleMouseBroadcast.mjs b/module/tweaks/toggleMouseBroadcast.mjs index 63a4df6..765b168 100644 --- a/module/tweaks/toggleMouseBroadcast.mjs +++ b/module/tweaks/toggleMouseBroadcast.mjs @@ -10,7 +10,7 @@ let notifID = null; export function toggleMouseBroadcast() { status[key] = SettingStatusEnum.Unknown; - if (preventTweakRegistration(key)) { return }; + if (preventTweakRegistration(key, true)) { return }; // #region Registration Logger.log(`Registering setting: ${key}`); From 9ffa1cee06b78d4e934d1cc74b6679a21d4d6ef4 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 19 Dec 2025 00:23:56 -0700 Subject: [PATCH 07/17] Fix grammar --- module/hooks/oft.tweakStatuses.mjs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/module/hooks/oft.tweakStatuses.mjs b/module/hooks/oft.tweakStatuses.mjs index 966c521..622d6f4 100644 --- a/module/hooks/oft.tweakStatuses.mjs +++ b/module/hooks/oft.tweakStatuses.mjs @@ -1,9 +1,9 @@ /* This hook is used to broadcast the final status of all tweaks within the module, -allowing them to either confirm their registration didn't happen or to do -something once all of the module setup has been finalized. Tweak statuses cannot -be blocked or changed from this hook, to prevent a tweak from being registered -you should use the "oft.preRegisterTweak" hook. +allowing other modules to either confirm their registration didn't happen or to +do something once all of the module setup has been finalized. Tweak statuses +cannot be blocked or changed from this hook, to prevent a tweak from being +registered you should use the "oft.preRegisterTweak" hook. Call Signature: (settings: Record) => void */ From 3527286c7fc607789dda807fbce0085bee21e8d9 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 19 Dec 2025 18:38:54 -0700 Subject: [PATCH 08/17] Fix bug and call the hook with the tweak key --- module/utils/preRegisterTweak.mjs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/module/utils/preRegisterTweak.mjs b/module/utils/preRegisterTweak.mjs index 8d6875b..f97bebb 100644 --- a/module/utils/preRegisterTweak.mjs +++ b/module/utils/preRegisterTweak.mjs @@ -11,7 +11,9 @@ export function preventTweakRegistration(key, invasive = false) { `The hook "${__ID__}.preventSetting" has been renamed "${__ID__}.registerTweak".`, { since: `v1.2.0`, until: `v2.0.0`, stack: false, once: true }, ); - prevented &&= Hooks.call(`${__ID__}.preventSetting`); + if (prevented !== false) { + prevented = Hooks.call(`${__ID__}.preventSetting`, key); + }; }; if (!prevented) { From 064b4c130432dd7a0212799a25368e3a3d64fa2c Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 20 Dec 2025 00:28:58 -0700 Subject: [PATCH 09/17] Move away from an enricher in favour of the chatMessage hook handling (closes #23) --- module/tweaks/chatImageLinks.mjs | 125 ++++++++++++++++--------------- 1 file changed, 64 insertions(+), 61 deletions(-) diff --git a/module/tweaks/chatImageLinks.mjs b/module/tweaks/chatImageLinks.mjs index 1e6f708..b75ad9c 100644 --- a/module/tweaks/chatImageLinks.mjs +++ b/module/tweaks/chatImageLinks.mjs @@ -3,8 +3,6 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -// const { DialogV2 } = foundry.applications.api; - const key = `chatImageLinks`; const IMAGE_TYPES = [ `png`, @@ -18,6 +16,9 @@ export function chatImageLinks() { status[key] = SettingStatusEnum.Unknown; if (preventTweakRegistration(key)) { return }; + /** @type {number|null} */ + let hookID = null; + // #region Registration Logger.log(`Registering setting: ${key}`); game.settings.register(__ID__, key, { @@ -28,6 +29,13 @@ export function chatImageLinks() { default: true, config: true, requiresReload: true, + onChange: (newValue) => { + if (newValue) { + hookID = Hooks.on(`chatMessage`, chatMessageHandler); + } else if (hookID != null) { + Hooks.off(`chatMessage`, hookID); + }; + }, }); game.settings.register(__ID__, key + `-showPromptAgain`, { @@ -40,76 +48,71 @@ export function chatImageLinks() { // #region Implementation if (game.settings.get(__ID__, key)) { - Logger.log(`setting:${key} | Adding text enricher`); - - // MARK: Enricher - const pattern = new RegExp( - `(? { - Logger.debug(url); - url = url[0].replace(/^image:\/\//, ``); - const secure = `https://${url}`; - const insecure = `http://${url}`; - - if (await isAcceptableImage(secure)) { - const img = document.createElement(`img`); - img.src = secure; - img.alt = secure; - return img; - }; - - if (await isAcceptableImage(insecure)) { - const img = document.createElement(`img`); - img.src = insecure; - img.alt = insecure; - return img; - }; - - return null; - }, - }); - - // MARK: Chat Input - // Hooks.on(`chatMessage`, (chatLog, message, options) => { - // if (!game.settings.get(__ID__, key)) { return }; - - // const match = message.match(pattern); - // if (!match) { return }; - - // DialogV2.wait({ - // rejectClose: false, - // content: game.i18n.localize(`OFT.dialogs.chatImageLinks.didYouKnowImageLink`), - // buttons: [ - // { action: ``, label: `OFT.dialogs.chatImageLinks.convertAndDontShowAgain` }, - // { action: ``, label: `OFT.dialogs.chatImageLinks.justConvert` }, - // { action: ``, label: `OFT.dialogs.chatImageLinks.ignoreAndDontShowAgain` }, - // { action: ``, label: `OFT.dialogs.chatImageLinks.disableEntirely` }, - // ], - // }) - // .then((selected) => { - // chatLog.processMessage(message, options); - // }); - - // return false; - // }); - } + Logger.log(`setting:${key} | Adding chat message listener`); + Hooks.on(`chatMessage`, chatMessageHandler); + }; // #endregion Implementation status[key] = SettingStatusEnum.Registered; }; // #region Helpers +const pattern = new RegExp( + `https?:\\/\\/\\S*\\.(?:${IMAGE_TYPES.join(`|`)})`, + `gi`, +); + +// MARK: Mutate & Resend +const handled = new Set(); +async function mutateAndResendMessage(chatLog, message, options) { + const match = message.match(pattern); + if (!match) { return }; + + const validMatches = new Set(); + + const matches = message.match(pattern); + for (const match of matches) { + if (await isAcceptableImage(match)) { + validMatches.add(match); + }; + }; + + message = message.replaceAll( + pattern, + (url) => { + if (!validMatches.has(url)) { + return url; + }; + return `${url}`; + }, + ); + + handled.add(message); + chatLog.processMessage(message, options); +}; + +// MARK: Chat Message +/** + * Must be synchronous since it is a hook handler, but the mutation + + * resending can be done asynchronously since it doesn't matter how + * long it takes. + */ +function chatMessageHandler(chatLog, message, options) { + if (!game.settings.get(__ID__, key)) { return }; + if (handled.has(message)) { return }; + mutateAndResendMessage(chatLog, message, options); + return false; +}; + +// MARK: isAcceptableImage async function isAcceptableImage(url) { + if (!URL.canParse(url)) { return false }; + try { const response = await fetch(url, { method: `HEAD` }); const contentType = response.headers.get(`Content-Type`); Logger.debug(`Image data:`, { url, contentType }); + let [ superType, subtype ] = contentType.split(`/`); if (superType !== `image`) { return false; From 1ca6da7c91814e3b5be2903f9a6f352dac559494 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 20 Dec 2025 00:32:00 -0700 Subject: [PATCH 10/17] Update the localization for the chatImageLinks --- langs/en-ca.json | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/langs/en-ca.json b/langs/en-ca.json index d2e091a..2ead115 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -11,7 +11,7 @@ }, "chatImageLinks": { "name": "Image Shortcuts", - "hint": "(v13+) When attempting to send an image/gif in chat, this allows you to easily embed the actual image in the text by changing \"http\"/\"https\" into \"image\", automatically displaying the image after sending the message." + "hint": "(v13+) Automatically embeds image and gif links when posted in the chat. The link must point to a specific image file (e.g. png, webp, gif), otherwise it will not be embedded at all." }, "chatSidebarBackground": { "name": "Chat Background", @@ -75,15 +75,6 @@ "no-settings-to-display": "No settings to display", "make-global-reference": "Make Global Reference" }, - "dialogs": { - "chatImageLinks": { - "didYouKnowImageLink": "", - "convertAndDontShowAgain": "", - "justConvert": "", - "ignoreAndDontShowAgain": "", - "disableEntirely": "" - } - }, "notifs": { "toggleMouseBroadcast": { "hidingCursor": "Hiding your cursor from others!", From 455a301875d00975aaf3ba0d6b121e8e8f689c8c Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 21 Dec 2025 21:25:41 -0700 Subject: [PATCH 11/17] Add Foundrylock file to the repo --- oft.lock | 1 + 1 file changed, 1 insertion(+) create mode 100644 oft.lock diff --git a/oft.lock b/oft.lock new file mode 100644 index 0000000..82ef623 --- /dev/null +++ b/oft.lock @@ -0,0 +1 @@ +🔒 \ No newline at end of file From 1ce2e01f5c4ce6d5c9351ae6d6c289d8ac3b91aa Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 21 Dec 2025 21:30:54 -0700 Subject: [PATCH 12/17] Fix bug preventing all non-image messages from being sent (closes #30) --- module/tweaks/chatImageLinks.mjs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/module/tweaks/chatImageLinks.mjs b/module/tweaks/chatImageLinks.mjs index b75ad9c..6b4b226 100644 --- a/module/tweaks/chatImageLinks.mjs +++ b/module/tweaks/chatImageLinks.mjs @@ -28,7 +28,7 @@ export function chatImageLinks() { type: Boolean, default: true, config: true, - requiresReload: true, + requiresReload: false, onChange: (newValue) => { if (newValue) { hookID = Hooks.on(`chatMessage`, chatMessageHandler); @@ -37,13 +37,6 @@ export function chatImageLinks() { }; }, }); - - game.settings.register(__ID__, key + `-showPromptAgain`, { - scope: `user`, - type: Boolean, - default: true, - config: false, - }); // #endregion Registration // #region Implementation @@ -65,9 +58,6 @@ const pattern = new RegExp( // MARK: Mutate & Resend const handled = new Set(); async function mutateAndResendMessage(chatLog, message, options) { - const match = message.match(pattern); - if (!match) { return }; - const validMatches = new Set(); const matches = message.match(pattern); @@ -99,7 +89,14 @@ async function mutateAndResendMessage(chatLog, message, options) { */ function chatMessageHandler(chatLog, message, options) { if (!game.settings.get(__ID__, key)) { return }; + + // Don't re-process the same message if (handled.has(message)) { return }; + + // Prevent cancellation for non-matching messages + const match = message.match(pattern); + if (!match) { return }; + mutateAndResendMessage(chatLog, message, options); return false; }; From 1a0cf21742275e5fa88ecf12635f3df8fc7132ec Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 21 Dec 2025 21:39:15 -0700 Subject: [PATCH 13/17] Fixes the sidebar width in systems that might tweak the sidebar width (closes #27) --- styles/main.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/styles/main.css b/styles/main.css index 2312044..1d0fec5 100644 --- a/styles/main.css +++ b/styles/main.css @@ -6,4 +6,4 @@ @import url("./apps.css"); /* Make the chat sidebar the same width as all the other tabs */ -.chat-sidebar:not(.sidebar-popout) { width: unset; } +.chat-sidebar:not(.sidebar-popout) { width: var(--sidebar-width); } From 3bbe8a58edc49e35408cdcae4ef960f83b14f550 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 21 Dec 2025 21:39:50 -0700 Subject: [PATCH 14/17] Version bump for release --- module.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module.json b/module.json index 61d9791..92e7f6e 100644 --- a/module.json +++ b/module.json @@ -1,7 +1,7 @@ { "id": "oft", "title": "Oliver's Foundry Tweaks", - "version": "1.1.0", + "version": "1.2.0", "authors": [ { "name": "Oliver" } ], From ce994f5dafee2c90b1e264d28519ea7e6e3c17d7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 21 Dec 2025 21:59:12 -0700 Subject: [PATCH 15/17] Export all of the tweak keys --- module/tweaks/addGlobalDocReferrer.mjs | 2 +- module/tweaks/autoUnpauseOnLoad.mjs | 2 +- module/tweaks/chatImageLinks.mjs | 2 +- module/tweaks/chatSidebarBackground.mjs | 2 +- module/tweaks/hotbarButtonGap.mjs | 2 +- module/tweaks/hotbarButtonSize.mjs | 2 +- module/tweaks/preventMovementHistory.mjs | 2 +- module/tweaks/preventTokenRotation.mjs | 2 +- module/tweaks/preventUserConfigOpen.mjs | 2 +- module/tweaks/repositionHotbar.mjs | 2 +- module/tweaks/startSidebarExpanded.mjs | 2 +- module/tweaks/startingSidebarTab.mjs | 2 +- module/tweaks/toggleMouseBroadcast.mjs | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/module/tweaks/addGlobalDocReferrer.mjs b/module/tweaks/addGlobalDocReferrer.mjs index 34850b9..9ee5f81 100644 --- a/module/tweaks/addGlobalDocReferrer.mjs +++ b/module/tweaks/addGlobalDocReferrer.mjs @@ -4,7 +4,7 @@ import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerDevSetting } from "../utils/SubMenuSettings.mjs"; -const key = `addGlobalDocReferrer`; +export const key = `addGlobalDocReferrer`; export function addGlobalDocReferrer() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/autoUnpauseOnLoad.mjs b/module/tweaks/autoUnpauseOnLoad.mjs index afe057e..db72e4d 100644 --- a/module/tweaks/autoUnpauseOnLoad.mjs +++ b/module/tweaks/autoUnpauseOnLoad.mjs @@ -4,7 +4,7 @@ import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerDevSetting } from "../utils/SubMenuSettings.mjs"; -const key = `autoUnpauseOnLoad`; +export const key = `autoUnpauseOnLoad`; export function autoUnpauseOnLoad() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/chatImageLinks.mjs b/module/tweaks/chatImageLinks.mjs index 6b4b226..8d17d0b 100644 --- a/module/tweaks/chatImageLinks.mjs +++ b/module/tweaks/chatImageLinks.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `chatImageLinks`; +export const key = `chatImageLinks`; const IMAGE_TYPES = [ `png`, `jpg`, diff --git a/module/tweaks/chatSidebarBackground.mjs b/module/tweaks/chatSidebarBackground.mjs index d138ef5..8e98f55 100644 --- a/module/tweaks/chatSidebarBackground.mjs +++ b/module/tweaks/chatSidebarBackground.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `chatSidebarBackground`; +export const key = `chatSidebarBackground`; export function chatSidebarBackground() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/hotbarButtonGap.mjs b/module/tweaks/hotbarButtonGap.mjs index f41b02d..96c8239 100644 --- a/module/tweaks/hotbarButtonGap.mjs +++ b/module/tweaks/hotbarButtonGap.mjs @@ -4,7 +4,7 @@ import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerCategorySetting } from "../utils/SubMenuSettings.mjs"; -const key = `hotbarButtonGap`; +export const key = `hotbarButtonGap`; export function hotbarButtonGap() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/hotbarButtonSize.mjs b/module/tweaks/hotbarButtonSize.mjs index 78ed9d3..829b0bd 100644 --- a/module/tweaks/hotbarButtonSize.mjs +++ b/module/tweaks/hotbarButtonSize.mjs @@ -4,7 +4,7 @@ import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerCategorySetting } from "../utils/SubMenuSettings.mjs"; -const key = `hotbarButtonSize`; +export const key = `hotbarButtonSize`; export function hotbarButtonSize() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/preventMovementHistory.mjs b/module/tweaks/preventMovementHistory.mjs index c70382e..782e7f3 100644 --- a/module/tweaks/preventMovementHistory.mjs +++ b/module/tweaks/preventMovementHistory.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `preventMovementHistory`; +export const key = `preventMovementHistory`; export function preventMovementHistory() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/preventTokenRotation.mjs b/module/tweaks/preventTokenRotation.mjs index 08daa35..53b8e3e 100644 --- a/module/tweaks/preventTokenRotation.mjs +++ b/module/tweaks/preventTokenRotation.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `preventTokenRotation`; +export const key = `preventTokenRotation`; export function preventTokenRotation() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/preventUserConfigOpen.mjs b/module/tweaks/preventUserConfigOpen.mjs index cccbe51..e13a237 100644 --- a/module/tweaks/preventUserConfigOpen.mjs +++ b/module/tweaks/preventUserConfigOpen.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `preventUserConfigOpen`; +export const key = `preventUserConfigOpen`; export function preventUserConfigOpen() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/repositionHotbar.mjs b/module/tweaks/repositionHotbar.mjs index fc24d4f..dd694d1 100644 --- a/module/tweaks/repositionHotbar.mjs +++ b/module/tweaks/repositionHotbar.mjs @@ -4,7 +4,7 @@ import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; import { registerCategorySetting } from "../utils/SubMenuSettings.mjs"; -const key = `repositionHotbar`; +export const key = `repositionHotbar`; export function repositionHotbar() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/startSidebarExpanded.mjs b/module/tweaks/startSidebarExpanded.mjs index 7e220c0..14b39f4 100644 --- a/module/tweaks/startSidebarExpanded.mjs +++ b/module/tweaks/startSidebarExpanded.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `startSidebarExpanded`; +export const key = `startSidebarExpanded`; export function startSidebarExpanded() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/startingSidebarTab.mjs b/module/tweaks/startingSidebarTab.mjs index 6b9e10a..aba7170 100644 --- a/module/tweaks/startingSidebarTab.mjs +++ b/module/tweaks/startingSidebarTab.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `startingSidebarTab`; +export const key = `startingSidebarTab`; export function startingSidebarTab() { status[key] = SettingStatusEnum.Unknown; diff --git a/module/tweaks/toggleMouseBroadcast.mjs b/module/tweaks/toggleMouseBroadcast.mjs index 765b168..0fa123d 100644 --- a/module/tweaks/toggleMouseBroadcast.mjs +++ b/module/tweaks/toggleMouseBroadcast.mjs @@ -3,7 +3,7 @@ import { __ID__ } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; import { preventTweakRegistration } from "../utils/preRegisterTweak.mjs"; -const key = `toggleMouseBroadcast`; +export const key = `toggleMouseBroadcast`; /** @type {number | null} */ let notifID = null; From 00af9286d40bcfad8852d4d9c4bcc7729d7e003e Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 21 Dec 2025 22:02:39 -0700 Subject: [PATCH 16/17] Add tweak blocking for when Crucible is the game system --- module/conflicts/crucible.mjs | 10 ++++++++++ module/oft.mjs | 3 +++ 2 files changed, 13 insertions(+) create mode 100644 module/conflicts/crucible.mjs diff --git a/module/conflicts/crucible.mjs b/module/conflicts/crucible.mjs new file mode 100644 index 0000000..6df24db --- /dev/null +++ b/module/conflicts/crucible.mjs @@ -0,0 +1,10 @@ +import { key as preventMovementHistoryKey } from "../tweaks/preventMovementHistory.mjs"; + +Hooks.on(`oft.preRegisterTweak`, (tweakID) => { + if (game.system.id !== `crucible`) { return }; + + switch (tweakID) { + case preventMovementHistoryKey: + return false; + }; +}); diff --git a/module/oft.mjs b/module/oft.mjs index 8740d00..12a83bf 100644 --- a/module/oft.mjs +++ b/module/oft.mjs @@ -2,3 +2,6 @@ import "./hooks/init.mjs"; import "./hooks/setup.mjs"; import "./hooks/renderSettingsConfig.mjs"; + +// Compatibility w/ other packages +import "./conflicts/crucible.mjs"; From 9c19306cc17e592d7df5397dca04a10cac1e5c15 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 26 Dec 2025 00:43:53 -0700 Subject: [PATCH 17/17] Hide empty sub-menus from Foundry's settings app --- module/apps/DevSettingsMenu.mjs | 1 + module/apps/HotbarSettingsMenu.mjs | 1 + module/apps/OFTSettingsMenu.mjs | 4 ++++ module/hooks/renderSettingsConfig.mjs | 12 ++++++++++-- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/module/apps/DevSettingsMenu.mjs b/module/apps/DevSettingsMenu.mjs index 81a14e8..bee0b96 100644 --- a/module/apps/DevSettingsMenu.mjs +++ b/module/apps/DevSettingsMenu.mjs @@ -9,6 +9,7 @@ export class DevSettingsMenu extends OFTSettingsMenu { }; static get _SETTINGS() { + if (!categories.has(`dev`)) { return [] }; const devSettings = categories.get(`dev`); const settingIDs = []; for (const [settingID, shown] of devSettings.entries()) { diff --git a/module/apps/HotbarSettingsMenu.mjs b/module/apps/HotbarSettingsMenu.mjs index 0c7fb9e..5a722e3 100644 --- a/module/apps/HotbarSettingsMenu.mjs +++ b/module/apps/HotbarSettingsMenu.mjs @@ -9,6 +9,7 @@ export class HotbarSettingsMenu extends OFTSettingsMenu { }; static get _SETTINGS() { + if (!categories.has(`hotbar`)) { return [] }; const settings = categories.get(`hotbar`); const settingIDs = []; for (const [settingID, shown] of settings.entries()) { diff --git a/module/apps/OFTSettingsMenu.mjs b/module/apps/OFTSettingsMenu.mjs index 74ddf97..387dc0a 100644 --- a/module/apps/OFTSettingsMenu.mjs +++ b/module/apps/OFTSettingsMenu.mjs @@ -47,6 +47,10 @@ export class OFTSettingsMenu extends HAM(ApplicationV2) { }; static _SETTINGS = []; + + static get isEmpty() { + return this._SETTINGS.length === 0; + }; // #endregion Options // #region Data Prep diff --git a/module/hooks/renderSettingsConfig.mjs b/module/hooks/renderSettingsConfig.mjs index b4e28d8..b0ec83c 100644 --- a/module/hooks/renderSettingsConfig.mjs +++ b/module/hooks/renderSettingsConfig.mjs @@ -6,11 +6,19 @@ prevent it from being as attention-grabbing compared to being at the top of the list. */ Hooks.on(`renderSettingsConfig`, (app) => { + // MARK: Hide empty menus + for (const [key, config] of game.settings.menus) { + if (!key.startsWith(__ID__)) { continue }; + if (config.type.isEmpty) { + const entry = app.element.querySelector(`.form-group:has(button[data-key="${key}"])`); + entry?.remove(); + }; + }; + + // MARK: devSettings Menu /** @type {Node | undefined} */ const settingList = app.element.querySelector(`.tab[data-group="categories"][data-tab="oft"]`); - - // MARK: devSettings Menu /** @type {Node | undefined} */ const devSettingsMenu = app.element.querySelector(`.form-group:has(button[data-key="${__ID__}.devSettings"])`); if (settingList && devSettingsMenu) {