Created
May 28, 2025 07:22
-
-
Save thejoester/6a419ecbe3fd1e06542cd36654cd8dff to your computer and use it in GitHub Desktop.
FoundryVTT Macro: display world data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
==================================================================================== | |
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