Hooks.once('init', () => { // Register Module Settings game.settings.register('dh-hotbar-cardview', 'showTagsAll', { name: 'Show Tags on Tooltips', hint: 'Master toggle to display item tags (damage, properties, etc.) on hotbar card tooltips.', scope: 'client', config: true, type: Boolean, default: true }); game.settings.register('dh-hotbar-cardview', 'showTagsWeapons', { name: 'Show Tags: Weapons', scope: 'client', config: true, type: Boolean, default: true }); game.settings.register('dh-hotbar-cardview', 'showTagsArmor', { name: 'Show Tags: Armor', scope: 'client', config: true, type: Boolean, default: true }); game.settings.register('dh-hotbar-cardview', 'showTagsConsumables', { name: 'Show Tags: Consumables', scope: 'client', config: true, type: Boolean, default: true }); game.settings.register('dh-hotbar-cardview', 'showTagsFeatures', { name: 'Show Tags: Features', scope: 'client', config: true, type: Boolean, default: true }); game.settings.register('dh-hotbar-cardview', 'showTagsDomains', { name: 'Show Tags: Domain Cards', scope: 'client', config: true, type: Boolean, default: false }); // Hook to dynamically hide/show sub-settings based on the master toggle Hooks.on('renderSettingsConfig', (app, html) => { // Use a timeout to ensure the DOM is fully rendered and attached, // and to run after Foundry's own initial display/search logic. setTimeout(() => { // Find the master checkbox const showTagsAllCheckbox = document.querySelector('input[name="dh-hotbar-cardview.showTagsAll"]'); if (!showTagsAllCheckbox) return; const subSettings = ['Weapons', 'Armor', 'Consumables', 'Features', 'Domains']; function toggleSubSettings() { const isChecked = showTagsAllCheckbox.checked; subSettings.forEach(setting => { const input = document.querySelector(`input[name="dh-hotbar-cardview.showTags${setting}"]`); if (input) { const row = input.closest('.form-group'); if (row) { if (isChecked) { // Remove inline display style to let CSS handle it (usually flex) row.style.display = ''; } else { // Use !important to prevent Foundry's search or tab logic from overriding it row.style.setProperty('display', 'none', 'important'); } } } }); } // Run once to set initial state toggleSubSettings(); // Listen for changes showTagsAllCheckbox.addEventListener('change', toggleSubSettings); }, 100); }); const originalActivate = CONFIG.ux.TooltipManager.prototype.activate; CONFIG.ux.TooltipManager.prototype.activate = async function(element, options = {}) { if (element.dataset.tooltip?.startsWith('#dhcard#')) { const uuid = element.dataset.tooltip.slice(8); const item = await fromUuid(uuid); if (item) { // Use Daggerheart's enrichText to prepare the item's descriptions if (typeof this.enrichText === 'function') { await this.enrichText(item); } // Calculate Feature Source Category let featureSource = null; if (item.type === 'feature' && item.system?.originItemType) { const originType = item.system.originItemType; // Attempt to get the localized string for the origin type (e.g., "Ancestry") let originTypeString = game.i18n.localize(`TYPES.Item.${originType}`); // Fallback just in case if (originTypeString === `TYPES.Item.${originType}`) { originTypeString = originType.charAt(0).toUpperCase() + originType.slice(1); } if (item.parent && item.parent.items) { const sourceItem = item.parent.items.find(i => i.type === originType); if (sourceItem) { featureSource = `${originTypeString} - ${sourceItem.name}`; } else { featureSource = originTypeString; } } else { featureSource = originTypeString; } } const showTagsAll = game.settings.get('dh-hotbar-cardview', 'showTagsAll'); options.html = await renderTemplate('modules/dh-hotbar-cardview/templates/cardview.hbs', { item: item, description: item.system?.enrichedDescription ?? item.enrichedDescription ?? item.system?.description, config: CONFIG.DH, allDomains: CONFIG.DH.DOMAIN?.allDomains ? CONFIG.DH.DOMAIN.allDomains() : {}, featureSource: featureSource, settings: { weapon: showTagsAll && game.settings.get('dh-hotbar-cardview', 'showTagsWeapons'), armor: showTagsAll && game.settings.get('dh-hotbar-cardview', 'showTagsArmor'), consumable: showTagsAll && game.settings.get('dh-hotbar-cardview', 'showTagsConsumables'), feature: showTagsAll && game.settings.get('dh-hotbar-cardview', 'showTagsFeatures'), domain: showTagsAll && game.settings.get('dh-hotbar-cardview', 'showTagsDomains') } }); options.direction = this.constructor.TOOLTIP_DIRECTIONS.UP; options.cssClass = 'dh-hotbar-card-tooltip'; } } return originalActivate.call(this, element, options); }; }); // Use a MutationObserver to aggressively update the hotbar tooltips as soon as the DOM changes. // This ensures that the dataset is ready BEFORE the user hovers for the first time. Hooks.once('ready', () => { function processHotbar() { const hotbarEls = document.querySelectorAll('#hotbar, .hotbar, [id^="hotbar"]'); for (const hotbarEl of hotbarEls) { const slots = hotbarEl.querySelectorAll('[data-slot]'); for (const slotEl of slots) { const slot = slotEl.dataset.slot; if (!slot) continue; const macroId = slotEl.dataset.macroId || game.user.hotbar[slot]; if (!macroId) continue; const macro = game.macros.get(macroId); if (macro && macro.command) { const match = macro.command.match(/useItem\s*\(\s*["']([^"']+)["']\s*\)/); if (match) { const uuid = match[1]; const tooltipText = `#dhcard#${uuid}`; // Overwrite the tooltip attribute on the slot and all inner elements slotEl.dataset.tooltip = tooltipText; const innerTooltips = slotEl.querySelectorAll('[data-tooltip]'); for (const inner of innerTooltips) { inner.dataset.tooltip = tooltipText; } } } } } } // Process immediately processHotbar(); // Re-process on hotbar changes Hooks.on('updateUser', processHotbar); Hooks.on('updateMacro', processHotbar); Hooks.on('renderDhHotbar', processHotbar); // Watch for any dynamic DOM changes inside the hotbar container const observer = new MutationObserver((mutations) => { let shouldProcess = false; for (const mutation of mutations) { if (mutation.addedNodes.length > 0 || mutation.type === 'attributes') { shouldProcess = true; break; } } if (shouldProcess) processHotbar(); }); const hotbarWrapper = document.getElementById('ui-bottom') || document.body; observer.observe(hotbarWrapper, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-slot', 'data-macro-id'] }); });