Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save thejoester/6a419ecbe3fd1e06542cd36654cd8dff to your computer and use it in GitHub Desktop.
Save thejoester/6a419ecbe3fd1e06542cd36654cd8dff to your computer and use it in GitHub Desktop.
FoundryVTT Macro: display world data
/*
====================================================================================
World Data Usage Snapshot Macro (Manual Tabs Version)
------------------------------------------------------------------------------------
This macro scans all world-level documents in Foundry VTT (v12.x), including:
- Actors
- Items
- Scenes
- Journals
- RollTables
- Playlists
- Macros
It estimates the data size (via JSON serialization), then displays:
- A summary table of total counts and size per document type
- Individual tabs for each type listing entity name + estimated size
- Entries ≥ 100 KB are highlighted in red for visibility
This version is designed to be compatible with Foundry VTT v12.343+
and does not rely on `DialogV2` or the `Tabs` class functioning properly.
Author: TheJoester | Last updated: 2025-05-28
====================================================================================
*/
function getSizeInKB(obj) {
return (new Blob([JSON.stringify(obj)])).size / 1024;
}
function formatSizeKB(kb) {
return kb < 1024 ? `${kb.toFixed(1)} KB` : `${(kb / 1024).toFixed(2)} MB`;
}
const LARGE_THRESHOLD_KB = 100;
const collections = {
Actors: game.actors,
Items: game.items,
Scenes: game.scenes,
Journals: game.journal,
RollTables: game.tables,
Playlists: game.playlists,
Macros: game.macros
};
const tabs = [];
const contents = [];
let totalWorldSize = 0;
let summaryHTML = "<table><thead><tr><th>Type</th><th>Count</th><th>Total Size</th></tr></thead><tbody>";
for (const [label, collection] of Object.entries(collections)) {
const entries = [];
let totalSize = 0;
for (const doc of collection.contents) {
const size = getSizeInKB(doc.toObject());
totalSize += size;
entries.push({ name: doc.name, size });
}
totalWorldSize += totalSize;
entries.sort((a, b) => b.size - a.size);
summaryHTML += `<tr><td>${label}</td><td>${entries.length}</td><td>${formatSizeKB(totalSize)}</td></tr>`;
const entryList = entries.map(e => {
const highlight = e.size >= LARGE_THRESHOLD_KB ? ' style="color:red;"' : "";
return `<li${highlight}><strong>${e.name}</strong>: ${formatSizeKB(e.size)}</li>`;
}).join("");
const sectionHTML = `
<div class="tab-content" data-group="perf-tabs" data-tab="${label}" style="display:none;">
<ul>${entryList}</ul>
<p><em>Total size for ${label}: ${formatSizeKB(totalSize)}</em></p>
</div>
`;
tabs.push(`<a class="item" data-tab="${label}"> [ ${label} ] </a>`);
contents.push(sectionHTML);
}
summaryHTML += `</tbody></table><p><strong>Total World Size:</strong> ${formatSizeKB(totalWorldSize)}</p>`;
tabs.unshift(`<a class="item active" data-tab="Summary">[ Summary ] </a>`);
contents.unshift(`<div class="tab-content" data-group="perf-tabs" data-tab="Summary" style="display:block;">${summaryHTML}</div>`);
new Dialog({
title: "World Data Usage Snapshot",
content: `
<nav class="tabs" data-group="perf-tabs">
${tabs.join("")}
</nav>
${contents.join("")}
`,
buttons: {
close: { label: "Close" }
},
render: html => {
const tabNav = html.find("nav.tabs");
const contents = html.find(".tab-content");
tabNav.on("click", "a.item", event => {
const tabId = event.currentTarget.dataset.tab;
tabNav.find("a.item").removeClass("active");
contents.hide();
event.currentTarget.classList.add("active");
html.find(`.tab-content[data-tab="${tabId}"]`).show();
});
}
}).render(true, { width: 600, resizable: true });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment