deletion works
This commit is contained in:
@@ -15,6 +15,33 @@ const SETTINGS_VERSION = 2;
|
|||||||
const COIN_ORDER = ["pp", "gp", "sp", "cp"];
|
const COIN_ORDER = ["pp", "gp", "sp", "cp"];
|
||||||
const ENCOUNTER_FLAG = "pf1EncounterHistory";
|
const ENCOUNTER_FLAG = "pf1EncounterHistory";
|
||||||
const DAMAGE_DEALT_FLAG = "pf1DamageDealtHistory";
|
const DAMAGE_DEALT_FLAG = "pf1DamageDealtHistory";
|
||||||
|
const DEBUG_LOGS = false;
|
||||||
|
const HISTORY_PAGE_OPTIONS = [10, 20, 50, "all"];
|
||||||
|
|
||||||
|
const DAMAGE_ICON_MAP = {
|
||||||
|
slashing: { icon: "ra ra-sword", color: "#e3c000" },
|
||||||
|
piercing: { icon: "ra ra-spear-head", color: "#2c7be5" },
|
||||||
|
bludgeoning: { icon: "ra ra-large-hammer", color: "#e03131" },
|
||||||
|
fire: { icon: "ra ra-fire", color: "#f76707" },
|
||||||
|
cold: { icon: "ra ra-snowflake", color: "#3bc9db" },
|
||||||
|
electricity: { icon: "ra ra-lightning-bolt", color: "#f0c419" },
|
||||||
|
acid: { icon: "ra ra-round-bottom-flask", color: "#2f9e44" },
|
||||||
|
sonic: { icon: "ra ra-megaphone", color: "#22b8cf" },
|
||||||
|
force: { icon: "ra ra-crystal-ball", color: "#845ef7" },
|
||||||
|
negative: { icon: "ra ra-skull", color: "#7950f2" },
|
||||||
|
positive: { icon: "ra ra-sun", color: "#fab005" },
|
||||||
|
healing: { icon: "ra ra-health", color: "#4caf50" },
|
||||||
|
precision: { icon: "ra ra-target-arrows", color: "#000" },
|
||||||
|
nonlethal: { icon: "ra ra-hand", color: "#000" },
|
||||||
|
untyped: { icon: "ra ra-uncertainty", color: "#666" },
|
||||||
|
};
|
||||||
|
|
||||||
|
const log = {
|
||||||
|
debug: (...args) => DEBUG_LOGS && console.debug("[GowlersTracking]", ...args),
|
||||||
|
info: (...args) => console.info("[GowlersTracking]", ...args),
|
||||||
|
warn: (...args) => console.warn("[GowlersTracking]", ...args),
|
||||||
|
error: (...args) => console.error("[GowlersTracking]", ...args),
|
||||||
|
};
|
||||||
|
|
||||||
const STAT_CONFIGS = {
|
const STAT_CONFIGS = {
|
||||||
hp: {
|
hp: {
|
||||||
@@ -116,12 +143,12 @@ function setActiveHistoryTab(actorId, tabId) {
|
|||||||
|
|
||||||
// Global tab switching function for history dialog
|
// Global tab switching function for history dialog
|
||||||
window.switchHistoryTab = function(tabId, el = null) {
|
window.switchHistoryTab = function(tabId, el = null) {
|
||||||
console.log("[GowlersTracking] Tab switched to:", tabId);
|
log.debug("Tab switched to:", tabId);
|
||||||
|
|
||||||
// Find the root element using the data-history-root attribute
|
// Find the root element using the data-history-root attribute
|
||||||
const root = el?.closest?.('[data-history-root]') ?? document.querySelector('[data-history-root]');
|
const root = el?.closest?.('[data-history-root]') ?? document.querySelector('[data-history-root]');
|
||||||
if (!root) {
|
if (!root) {
|
||||||
console.warn("[GowlersTracking] History root element not found");
|
log.warn("History root element not found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,16 +166,16 @@ window.switchHistoryTab = function(tabId, el = null) {
|
|||||||
|
|
||||||
if (activeButton) {
|
if (activeButton) {
|
||||||
activeButton.classList.add('active');
|
activeButton.classList.add('active');
|
||||||
console.log("[GowlersTracking] Tab button activated:", tabId);
|
log.debug("Tab button activated:", tabId);
|
||||||
} else {
|
} else {
|
||||||
console.warn("[GowlersTracking] Tab button not found for:", tabId);
|
log.warn("Tab button not found for:", tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activePanel) {
|
if (activePanel) {
|
||||||
activePanel.style.display = 'block';
|
activePanel.style.display = 'block';
|
||||||
console.log("[GowlersTracking] Tab panel displayed:", tabId);
|
log.debug("Tab panel displayed:", tabId);
|
||||||
} else {
|
} else {
|
||||||
console.warn("[GowlersTracking] Tab panel not found for:", tabId);
|
log.warn("Tab panel not found for:", tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actorId = root?.getAttribute('data-history-root');
|
const actorId = root?.getAttribute('data-history-root');
|
||||||
@@ -500,16 +527,15 @@ async function initializeModule() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log detailed inspection for debugging
|
if (DEBUG_LOGS) {
|
||||||
console.log("[GowlersTracking] Message inspection for damage details:");
|
log.debug("Message inspection for damage details:");
|
||||||
console.log("[GowlersTracking] - pf1Flags keys:", Object.keys(pf1Flags));
|
log.debug(" - pf1Flags keys:", Object.keys(pf1Flags));
|
||||||
console.log("[GowlersTracking] - Full metadata:", JSON.stringify(metadata, null, 2));
|
log.debug(" - Full metadata:", JSON.stringify(metadata, null, 2));
|
||||||
console.log("[GowlersTracking] - message.rolls:", message.rolls?.length > 0 ? "Present" : "None");
|
log.debug(" - message.rolls:", message.rolls?.length > 0 ? "Present" : "None");
|
||||||
console.log("[GowlersTracking] - message.content length:", message.content?.length ?? 0);
|
log.debug(" - message.content length:", message.content?.length ?? 0);
|
||||||
|
|
||||||
// Log HTML content snippet for analysis
|
|
||||||
if (message.content) {
|
if (message.content) {
|
||||||
console.log("[GowlersTracking] - HTML preview (first 1000 chars):", message.content.substring(0, 1000));
|
log.debug(" - HTML preview (first 1000 chars):", message.content.substring(0, 1000));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -982,7 +1008,7 @@ function buildXpBreakdownTooltip(actor, xpEntry) {
|
|||||||
|
|
||||||
function buildHistoryContent(actor, tabArg) {
|
function buildHistoryContent(actor, tabArg) {
|
||||||
const initialTab = tabArg ?? getActiveHistoryTab(actor.id, "hp"); // Explicitly capture the tab parameter
|
const initialTab = tabArg ?? getActiveHistoryTab(actor.id, "hp"); // Explicitly capture the tab parameter
|
||||||
console.log("[GowlersTracking] buildHistoryContent called with initialTab:", initialTab);
|
log.debug("buildHistoryContent called with initialTab:", initialTab);
|
||||||
setActiveHistoryTab(actor.id, initialTab);
|
setActiveHistoryTab(actor.id, initialTab);
|
||||||
const canConfigure = game.user?.isGM;
|
const canConfigure = game.user?.isGM;
|
||||||
const configs = [
|
const configs = [
|
||||||
@@ -994,7 +1020,7 @@ function buildHistoryContent(actor, tabArg) {
|
|||||||
{ label: "Timestamp", render: (entry) => formatDate(entry.timestamp) },
|
{ label: "Timestamp", render: (entry) => formatDate(entry.timestamp) },
|
||||||
{ label: "HP", render: (entry) => entry.value },
|
{ label: "HP", render: (entry) => entry.value },
|
||||||
{
|
{
|
||||||
label: "Δ",
|
label: "Delta",
|
||||||
render: (entry) => entry.diff,
|
render: (entry) => entry.diff,
|
||||||
getTitle: (entry) => entry.damageBreakdown ? `${entry.damageBreakdown}` : ""
|
getTitle: (entry) => entry.damageBreakdown ? `${entry.damageBreakdown}` : ""
|
||||||
},
|
},
|
||||||
@@ -1100,15 +1126,15 @@ function buildHistoryContent(actor, tabArg) {
|
|||||||
currentPage = totalPages;
|
currentPage = totalPages;
|
||||||
setHistoryPageState(actor.id, cfg.id, currentPage, rowsPerPage);
|
setHistoryPageState(actor.id, cfg.id, currentPage, rowsPerPage);
|
||||||
}
|
}
|
||||||
const selectOptions = [10, 20, 50, "all"]
|
const selectOptions = HISTORY_PAGE_OPTIONS
|
||||||
.map((value) => `<option value="${value}" ${String(value) === String(rowsPerPage) ? "selected" : ""}>${value === "all" ? "All" : `${value} rows`}</option>`)
|
.map((value) => `<option value="${value}" ${String(value) === String(rowsPerPage) ? "selected" : ""}>${value === "all" ? "All" : `${value} rows`}</option>`)
|
||||||
.join("");
|
.join("");
|
||||||
return `<div class="history-panel tab ${isActive}" data-history-panel="${cfg.id}" data-page="${currentPage}" data-page-size="${rowsPerPage}" style="display:${display}">
|
return `<div class="history-panel tab ${isActive}" data-history-panel="${cfg.id}" data-page="${currentPage}" data-page-size="${rowsPerPage}" style="display:${display}">
|
||||||
${renderHistoryTable(entries, cfg.columns, cfg.id, rowsPerPage, currentPage)}
|
${renderHistoryTable(entries, cfg.columns, cfg.id, rowsPerPage, currentPage)}
|
||||||
<div class="history-panel-pagination" style="display: flex; gap: 8px; align-items: center; justify-content: center; margin-top: 12px; padding-top: 8px; border-top: 1px solid #e0e0e0;">
|
<div class="history-panel-pagination" style="display: flex; gap: 8px; align-items: center; justify-content: center; margin-top: 12px; padding-top: 8px; border-top: 1px solid #e0e0e0;">
|
||||||
<button type="button" data-action="history-page" data-direction="prev" onclick="window.historyPageNav(this, 'prev')" style="padding: 4px 8px; cursor: pointer;" ${totalPages <= 1 ? 'disabled' : ''}>« Prev</button>
|
<button type="button" data-action="history-page" data-direction="prev" onclick="window.historyPageNav(this, 'prev')" style="padding: 4px 8px; cursor: pointer;" ${totalPages <= 1 ? 'disabled' : ''}>< Prev</button>
|
||||||
<span data-page-info style="font-size: 0.85em; min-width: 100px; text-align: center;">Page ${currentPage} / ${Math.max(1, totalPages)}</span>
|
<span data-page-info style="font-size: 0.85em; min-width: 100px; text-align: center;">Page ${currentPage} / ${Math.max(1, totalPages)}</span>
|
||||||
<button type="button" data-action="history-page" data-direction="next" onclick="window.historyPageNav(this, 'next')" style="padding: 4px 8px; cursor: pointer;" ${totalPages <= 1 ? 'disabled' : ''}>Next »</button>
|
<button type="button" data-action="history-page" data-direction="next" onclick="window.historyPageNav(this, 'next')" style="padding: 4px 8px; cursor: pointer;" ${totalPages <= 1 ? 'disabled' : ''}>Next ></button>
|
||||||
<select data-history-page-size onchange="window.historyChangePageSize(this)" style="padding: 2px 4px;">
|
<select data-history-page-size onchange="window.historyChangePageSize(this)" style="padding: 2px 4px;">
|
||||||
${selectOptions}
|
${selectOptions}
|
||||||
</select>
|
</select>
|
||||||
@@ -1185,23 +1211,23 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId, op
|
|||||||
|
|
||||||
const diffValue = config.diff(previous, nextValue);
|
const diffValue = config.diff(previous, nextValue);
|
||||||
|
|
||||||
// COMPREHENSIVE DEBUG LOGGING FOR DAMAGE REPORTING
|
if (DEBUG_LOGS) {
|
||||||
console.log("[GowlersTracking] ===== recordHistoryEntry DEBUG =====");
|
log.debug("===== recordHistoryEntry DEBUG =====");
|
||||||
console.log("[GowlersTracking] statId:", statId);
|
log.debug("statId:", statId);
|
||||||
console.log("[GowlersTracking] Previous:", previous, "Next:", nextValue, "Diff:", diffValue);
|
log.debug("Previous:", previous, "Next:", nextValue, "Diff:", diffValue);
|
||||||
console.log("[GowlersTracking] Actor:", actor.name, "(" + actor.id + ")");
|
log.debug("Actor:", actor.name, "(" + actor.id + ")");
|
||||||
console.log("[GowlersTracking] userId:", userId);
|
log.debug("userId:", userId);
|
||||||
console.log("[GowlersTracking] Full options object:", options);
|
log.debug("Full options object:", options);
|
||||||
console.log("[GowlersTracking] Full change object:", change);
|
log.debug("Full change object:", change);
|
||||||
|
|
||||||
// Log all keys in options for inspection
|
|
||||||
if (Object.keys(options).length > 0) {
|
if (Object.keys(options).length > 0) {
|
||||||
console.log("[GowlersTracking] Options keys:", Object.keys(options));
|
log.debug("Options keys:", Object.keys(options));
|
||||||
for (const key of Object.keys(options)) {
|
for (const key of Object.keys(options)) {
|
||||||
console.log(`[GowlersTracking] options.${key}:`, options[key]);
|
log.debug(`options.${key}:`, options[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("[GowlersTracking] ===== end debug =====");
|
log.debug("===== end debug =====");
|
||||||
|
}
|
||||||
|
|
||||||
// Determine encounter ID: use active combat, or if none, check if combat just ended
|
// Determine encounter ID: use active combat, or if none, check if combat just ended
|
||||||
let encounterId = game.combat?.id ?? null;
|
let encounterId = game.combat?.id ?? null;
|
||||||
@@ -1239,28 +1265,29 @@ async function recordHistoryEntry(actor, statId, previous, nextValue, userId, op
|
|||||||
else if (options?.encounter?.id) encounterId = options.encounter.id;
|
else if (options?.encounter?.id) encounterId = options.encounter.id;
|
||||||
else if (options?.encounter?._id) encounterId = options.encounter._id;
|
else if (options?.encounter?._id) encounterId = options.encounter._id;
|
||||||
|
|
||||||
// XP debug logging for data inspection
|
if (DEBUG_LOGS) {
|
||||||
try {
|
try {
|
||||||
console.log("[GowlersTracking][XP DEBUG] -----");
|
log.debug("[XP DEBUG] -----");
|
||||||
console.log("[GowlersTracking][XP DEBUG] encounterId:", encounterId, "manualXp:", manualXp, "hasExplicitEncounter:", hasExplicitEncounter);
|
log.debug("[XP DEBUG] encounterId:", encounterId, "manualXp:", manualXp, "hasExplicitEncounter:", hasExplicitEncounter);
|
||||||
console.log("[GowlersTracking][XP DEBUG] options keys:", Object.keys(options || {}));
|
log.debug("[XP DEBUG] options keys:", Object.keys(options || {}));
|
||||||
console.log("[GowlersTracking][XP DEBUG] change keys:", Object.keys(change || {}));
|
log.debug("[XP DEBUG] change keys:", Object.keys(change || {}));
|
||||||
if (options) console.log("[GowlersTracking][XP DEBUG] options JSON:", JSON.stringify(options, null, 2));
|
if (options) log.debug("[XP DEBUG] options JSON:", JSON.stringify(options, null, 2));
|
||||||
if (change) console.log("[GowlersTracking][XP DEBUG] change JSON keys:", Object.keys(change));
|
if (change) log.debug("[XP DEBUG] change JSON keys:", Object.keys(change));
|
||||||
if (change?.system?.details?.xp) console.log("[GowlersTracking][XP DEBUG] change.system.details.xp:", change.system.details.xp);
|
if (change?.system?.details?.xp) log.debug("[XP DEBUG] change.system.details.xp:", change.system.details.xp);
|
||||||
if (change?.system?.attributes?.hp) console.log("[GowlersTracking][XP DEBUG] change.system.attributes.hp (truncated):", JSON.stringify(change.system.attributes.hp, null, 2).substring(0, 500));
|
if (change?.system?.attributes?.hp) log.debug("[XP DEBUG] change.system.attributes.hp (truncated):", JSON.stringify(change.system.attributes.hp, null, 2).substring(0, 500));
|
||||||
const pf1Flags = actor.getFlag("pf1") ?? {};
|
const pf1Flags = actor.getFlag("pf1") ?? {};
|
||||||
if (pf1Flags && Object.keys(pf1Flags).length) console.log("[GowlersTracking][XP DEBUG] actor pf1 flags keys:", Object.keys(pf1Flags));
|
if (pf1Flags && Object.keys(pf1Flags).length) log.debug("[XP DEBUG] actor pf1 flags keys:", Object.keys(pf1Flags));
|
||||||
if (game.combat) console.log("[GowlersTracking][XP DEBUG] active combat flags:", game.combat.flags ?? "(none)");
|
if (game.combat) log.debug("[XP DEBUG] active combat flags:", game.combat.flags ?? "(none)");
|
||||||
const encounterFlag = actor.getFlag(FLAG_SCOPE, ENCOUNTER_FLAG) ?? [];
|
const encounterFlag = actor.getFlag(FLAG_SCOPE, ENCOUNTER_FLAG) ?? [];
|
||||||
console.log("[GowlersTracking][XP DEBUG] actor encounter flag count:", encounterFlag.length);
|
log.debug("[XP DEBUG] actor encounter flag count:", encounterFlag.length);
|
||||||
if (encounterFlag.length && encounterId) {
|
if (encounterFlag.length && encounterId) {
|
||||||
const encounterEntry = encounterFlag.find((e) => e.encounterID === encounterId);
|
const encounterEntry = encounterFlag.find((e) => e.encounterID === encounterId);
|
||||||
console.log("[GowlersTracking][XP DEBUG] matched encounter entry:", encounterEntry ?? "(not found)");
|
log.debug("[XP DEBUG] matched encounter entry:", encounterEntry ?? "(not found)");
|
||||||
}
|
}
|
||||||
console.log("[GowlersTracking][XP DEBUG] -----");
|
log.debug("[XP DEBUG] -----");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn("[GowlersTracking][XP DEBUG] logging failed:", e);
|
log.warn("[XP DEBUG] logging failed:", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1584,30 +1611,20 @@ function isNonlethalType(type) {
|
|||||||
return String(type ?? "").toLowerCase() === "nonlethal";
|
return String(type ?? "").toLowerCase() === "nonlethal";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPrimaryDamageType(part) {
|
||||||
|
return (part?.types && part.types[0]) || part?.customTypes?.[0] || part?.materials?.[0] || "untyped";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDamageIconMeta(type) {
|
||||||
|
return DAMAGE_ICON_MAP[type?.toLowerCase?.()] ?? DAMAGE_ICON_MAP.untyped;
|
||||||
|
}
|
||||||
|
|
||||||
function formatDamagePartsWithIcons(parts) {
|
function formatDamagePartsWithIcons(parts) {
|
||||||
if (!Array.isArray(parts) || !parts.length) return "";
|
if (!Array.isArray(parts) || !parts.length) return "";
|
||||||
const iconMap = {
|
|
||||||
slashing: { icon: "ra ra-sword", color: "#e3c000" },
|
|
||||||
piercing: { icon: "ra ra-spear-head", color: "#2c7be5" },
|
|
||||||
bludgeoning: { icon: "ra ra-large-hammer", color: "#e03131" },
|
|
||||||
fire: { icon: "ra ra-fire", color: "#f76707" },
|
|
||||||
cold: { icon: "ra ra-snowflake", color: "#3bc9db" },
|
|
||||||
electricity: { icon: "ra ra-lightning-bolt", color: "#f0c419" },
|
|
||||||
acid: { icon: "ra ra-round-bottom-flask", color: "#2f9e44" },
|
|
||||||
sonic: { icon: "ra ra-megaphone", color: "#22b8cf" },
|
|
||||||
force: { icon: "ra ra-crystal-ball", color: "#845ef7" },
|
|
||||||
negative: { icon: "ra ra-skull", color: "#7950f2" },
|
|
||||||
positive: { icon: "ra ra-sun", color: "#fab005" },
|
|
||||||
healing: { icon: "ra ra-health", color: "#4caf50" },
|
|
||||||
precision: { icon: "ra ra-target-arrows", color: "#000" },
|
|
||||||
nonlethal: { icon: "ra ra-hand", color: "#000" },
|
|
||||||
untyped: { icon: "ra ra-uncertainty", color: "#666" },
|
|
||||||
};
|
|
||||||
return parts
|
return parts
|
||||||
.map((p) => {
|
.map((p) => {
|
||||||
const baseType = (p.types && p.types[0]) || p.customTypes?.[0] || p.materials?.[0] || "untyped";
|
const meta = getDamageIconMeta(getPrimaryDamageType(p));
|
||||||
const mapEntry = iconMap[baseType?.toLowerCase?.()] ?? iconMap.untyped;
|
const icon = `<i class="${meta.icon}" style="color:${meta.color};"></i>`;
|
||||||
const icon = `<i class="${mapEntry.icon}" style="color:${mapEntry.color};"></i>`;
|
|
||||||
const amt = Number.isFinite(p.total) ? p.total : p.formula ?? "?";
|
const amt = Number.isFinite(p.total) ? p.total : p.formula ?? "?";
|
||||||
return `<span style="display:inline-flex; align-items:center; gap:4px; margin-right:6px;">${icon}<span>${amt}</span></span>`;
|
return `<span style="display:inline-flex; align-items:center; gap:4px; margin-right:6px;">${icon}<span>${amt}</span></span>`;
|
||||||
})
|
})
|
||||||
@@ -1615,27 +1632,10 @@ function formatDamagePartsWithIcons(parts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function renderDamageBar(composition = [], total = 0) {
|
function renderDamageBar(composition = [], total = 0) {
|
||||||
const iconMap = {
|
|
||||||
slashing: { icon: "ra ra-sword", color: "#e3c000" },
|
|
||||||
piercing: { icon: "ra ra-spear-head", color: "#2c7be5" },
|
|
||||||
bludgeoning: { icon: "ra ra-large-hammer", color: "#e03131" },
|
|
||||||
fire: { icon: "ra ra-fire", color: "#f76707" },
|
|
||||||
cold: { icon: "ra ra-snowflake", color: "#3bc9db" },
|
|
||||||
electricity: { icon: "ra ra-lightning-bolt", color: "#f0c419" },
|
|
||||||
acid: { icon: "ra ra-round-bottom-flask", color: "#2f9e44" },
|
|
||||||
sonic: { icon: "ra ra-megaphone", color: "#22b8cf" },
|
|
||||||
force: { icon: "ra ra-crystal-ball", color: "#845ef7" },
|
|
||||||
negative: { icon: "ra ra-skull", color: "#7950f2" },
|
|
||||||
positive: { icon: "ra ra-sun", color: "#fab005" },
|
|
||||||
healing: { icon: "ra ra-health", color: "#4caf50" },
|
|
||||||
precision: { icon: "ra ra-target-arrows", color: "#000" },
|
|
||||||
nonlethal: { icon: "ra ra-hand", color: "#000" },
|
|
||||||
untyped: { icon: "ra ra-uncertainty", color: "#666" },
|
|
||||||
};
|
|
||||||
if (!total || !composition.length) return "";
|
if (!total || !composition.length) return "";
|
||||||
const segments = composition.map((c) => {
|
const segments = composition.map((c) => {
|
||||||
const pct = Math.max(2, Math.round((c.value / total) * 100));
|
const pct = Math.max(2, Math.round((c.value / total) * 100));
|
||||||
const entry = iconMap[c.type?.toLowerCase?.()] ?? iconMap.untyped;
|
const entry = getDamageIconMeta(c.type);
|
||||||
return `<div style="width:${pct}%; background:${entry.color}; height:10px; display:flex; align-items:center; justify-content:center;">
|
return `<div style="width:${pct}%; background:${entry.color}; height:10px; display:flex; align-items:center; justify-content:center;">
|
||||||
<i class="${entry.icon}" style="color:#fff; font-size:10px;"></i>
|
<i class="${entry.icon}" style="color:#fff; font-size:10px;"></i>
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -2353,16 +2353,7 @@ class TrackingLedgerConfig extends FormApplication {
|
|||||||
defaultYes: false,
|
defaultYes: false,
|
||||||
});
|
});
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
try {
|
await clearAllHistoriesWithProgress();
|
||||||
const actors = collectAllActorDocuments();
|
|
||||||
const tokens = collectAllTokens();
|
|
||||||
for (const actor of actors) await clearDocumentHistory(actor);
|
|
||||||
for (const token of tokens) await clearDocumentHistory(token);
|
|
||||||
ui.notifications?.info?.("History cleared for all actors.");
|
|
||||||
} catch (err) {
|
|
||||||
console.error("[GowlersTracking] Failed to clear all histories", err);
|
|
||||||
ui.notifications?.error?.("Failed to clear all histories; see console.");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
html.find("[data-action=\"clear-history\"]").on("click", async (event) => {
|
html.find("[data-action=\"clear-history\"]").on("click", async (event) => {
|
||||||
@@ -2417,6 +2408,71 @@ async function clearDocumentHistory(doc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function clearAllHistoriesWithProgress() {
|
||||||
|
try {
|
||||||
|
const targets = new Map();
|
||||||
|
|
||||||
|
// Core actors in the directory
|
||||||
|
for (const actor of collectAllActorDocuments()) {
|
||||||
|
const key = `actor:${actor.uuid ?? actor.id}`;
|
||||||
|
if (!targets.has(key)) targets.set(key, { doc: actor, label: actor.name ?? actor.id });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tokens on scenes (covers unlinked NPCs) and their embedded actor documents
|
||||||
|
for (const token of collectAllTokens()) {
|
||||||
|
const tokenKey = `token:${token.uuid ?? token.id}:${token.parent?.uuid ?? "scene"}`;
|
||||||
|
if (!targets.has(tokenKey)) targets.set(tokenKey, { doc: token, label: token.name ?? token.id });
|
||||||
|
|
||||||
|
const tokenActor = token.actor;
|
||||||
|
if (tokenActor) {
|
||||||
|
const actorKey = `actor:${tokenActor.uuid ?? tokenActor.id}`;
|
||||||
|
if (!targets.has(actorKey)) targets.set(actorKey, { doc: tokenActor, label: tokenActor.name ?? tokenActor.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const entries = Array.from(targets.values());
|
||||||
|
if (!entries.length) {
|
||||||
|
ui.notifications?.info?.("No actor or token histories found to clear.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple progress dialog
|
||||||
|
let progressEl = null;
|
||||||
|
let statusEl = null;
|
||||||
|
const dialog = new Dialog({
|
||||||
|
title: "Clearing Histories",
|
||||||
|
content: `
|
||||||
|
<div style="display:flex; flex-direction:column; gap:8px; padding:4px;">
|
||||||
|
<progress value="0" max="${entries.length}" style="width:100%;"></progress>
|
||||||
|
<div data-status style="font-size:12px;">Starting...</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
buttons: {},
|
||||||
|
close: () => {},
|
||||||
|
render: (html) => {
|
||||||
|
progressEl = html[0].querySelector("progress");
|
||||||
|
statusEl = html[0].querySelector("[data-status]");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
dialog.render(true);
|
||||||
|
|
||||||
|
let completed = 0;
|
||||||
|
for (const { doc, label } of entries) {
|
||||||
|
if (statusEl) statusEl.textContent = `Clearing: ${label}`;
|
||||||
|
await clearDocumentHistory(doc);
|
||||||
|
completed += 1;
|
||||||
|
if (progressEl) progressEl.value = completed;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusEl) statusEl.textContent = "Done.";
|
||||||
|
setTimeout(() => dialog.close(), 500);
|
||||||
|
ui.notifications?.info?.("History cleared for all actors and tokens.");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[GowlersTracking] Failed to clear all histories", err);
|
||||||
|
ui.notifications?.error?.("Failed to clear all histories; see console.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function collectAllActorDocuments() {
|
function collectAllActorDocuments() {
|
||||||
const actors = new Map();
|
const actors = new Map();
|
||||||
for (const a of game.actors.contents ?? []) {
|
for (const a of game.actors.contents ?? []) {
|
||||||
@@ -2584,7 +2640,7 @@ function sendChatNotification(statId, actor, previous, nextValue, entry) {
|
|||||||
<div style="font-family: var(--font-primary); font-size: 12px; line-height: 1.4;">
|
<div style="font-family: var(--font-primary); font-size: 12px; line-height: 1.4;">
|
||||||
<div style="font-weight: bold; margin-bottom: 4px;">${title} Update</div>
|
<div style="font-weight: bold; margin-bottom: 4px;">${title} Update</div>
|
||||||
<div><strong>Actor:</strong> ${actor.name}</div>
|
<div><strong>Actor:</strong> ${actor.name}</div>
|
||||||
<div><strong>Value:</strong> ${prevText} → ${nextText} (${entry.diff})</div>
|
<div><strong>Value:</strong> ${prevText} -> ${nextText} (${entry.diff})</div>
|
||||||
<div><strong>Source:</strong> ${source}</div>
|
<div><strong>Source:</strong> ${source}</div>
|
||||||
<div><strong>Encounter:</strong> ${encounter}</div>
|
<div><strong>Encounter:</strong> ${encounter}</div>
|
||||||
${detailHtml}
|
${detailHtml}
|
||||||
|
|||||||
Reference in New Issue
Block a user