/** * DR Quick Button — Foundry VTT v13 * Adds a button near the chat dice/controls that runs the `/dr` command. */ Hooks.once("init", () => { console.log("DR Quick Button | Initializing"); }); // Sidebar chat Hooks.on("renderChatLog", (app, html) => addDRButton(html)); // Mini/Popout chat Hooks.on("renderChatPopout", (app, html) => addDRButton(html)); /** * Adds the DR button to the chat controls * @param {HTMLElement|jQuery} html - The application HTML */ function addDRButton(html) { if (!html) return; try { // Handle both jQuery (legacy) and HTMLElement (v14/AppV2) const root = html instanceof HTMLElement ? html : html[0]; if (!root) return; console.debug("DR Quick Button | addDRButton called. Root element:", root.tagName, root.className, root.getAttribute("data-application-part")); // Common selectors for chat controls/roll privacy across versions const targetSelectors = [ "#message-modes", // Foundry v14+ (Split button container) "#chat-controls", // Generic chat controls "#roll-privacy", // Legacy ID ".roll-privacy", // Legacy class ".roll-type-select", // System specific ".chat-controls" // Generic fallback ]; let found = false; for (const selector of targetSelectors) { // Find targets within the root, or check if the root itself is the target let targets = Array.from(root.querySelectorAll(selector)); if (root.matches && root.matches(selector)) targets.push(root); // Fallback: If not found in the provided root part, search the whole document // (In v14, the hook might pass only one part of the ApplicationV2) if (targets.length === 0) { const globalTargets = document.querySelectorAll(selector); // Only consider global targets that are actually visible/relevant targets = Array.from(globalTargets).filter(t => root.contains(t) || t.closest(".window-app") === root.closest(".window-app") || t.closest("#sidebar") === root.closest("#sidebar")); } if (targets.length === 0) continue; targets.forEach(div => { // Avoid duplicates if (div.querySelector(".dr-quick-button-container")) return; console.log(`DR Quick Button | Adding button to target: ${selector}`); const container = document.createElement("div"); container.className = "dr-quick-button-container"; const btn = document.createElement("button"); btn.type = "button"; btn.className = "ui-control icon fa-solid dr-quick-button"; btn.title = "Duality Dice Roll"; btn.setAttribute("aria-label", "Duality Dice Roll"); // Use the Daggerheart system icon btn.innerHTML = `DR`; btn.addEventListener("click", async (event) => { event.preventDefault(); await runDRCommand(); }); container.appendChild(btn); div.appendChild(container); found = true; }); if (found) break; } if (!found) { console.debug("DR Quick Button | No suitable target found in root or related containers."); } } catch (err) { console.error("DR Quick Button | addDRButton error:", err); } } /** Run `/dr` as if typed into chat */ async function runDRCommand() { try { await ui?.chat?.processMessage("/dr"); } catch (err) { console.error("DR Quick Button | Failed to send /dr command:", err); ui.notifications?.warn("Couldn't run /dr automatically. Try typing /dr in chat."); } }