chore: bump tracking ledger version to 1.3.0

This commit is contained in:
centron\schwoerer
2025-11-25 09:23:49 +01:00
parent a48f058583
commit e20df693d7

View File

@@ -1,6 +1,6 @@
const MODULE_ID = "gowlers-tracking-ledger"; const MODULE_ID = "gowlers-tracking-ledger";
const MODULE_VERSION = "1.2.1"; const MODULE_VERSION = "1.3.0";
const TRACK_SETTING = "actorSettings"; const TRACK_SETTING = "actorSettings";
const FLAG_SCOPE = "world"; const FLAG_SCOPE = "world";
const MAX_HISTORY_ROWS = 100; const MAX_HISTORY_ROWS = 100;
@@ -2095,6 +2095,7 @@ class TrackingLedgerConfig extends FormApplication {
this._pageMeta = { totalPages: 1, hasPrev: false, hasNext: false }; this._pageMeta = { totalPages: 1, hasPrev: false, hasNext: false };
this._actorRefs = null; this._actorRefs = null;
this._filterDebounceTimer = null; this._filterDebounceTimer = null;
this._pendingFilterCaret = null;
} }
get pageSize() { get pageSize() {
@@ -2248,6 +2249,10 @@ class TrackingLedgerConfig extends FormApplication {
this._page = 0; this._page = 0;
TrackingLedgerConfig._lastFilter = this._filter; TrackingLedgerConfig._lastFilter = this._filter;
TrackingLedgerConfig._lastPage = this._page; TrackingLedgerConfig._lastPage = this._page;
const el = event.currentTarget;
const selStart = Number.isFinite(el.selectionStart) ? el.selectionStart : (this._filter?.length ?? 0);
const selEnd = Number.isFinite(el.selectionEnd) ? el.selectionEnd : selStart;
this._pendingFilterCaret = { start: selStart, end: selEnd };
// Debounce render to preserve focus // Debounce render to preserve focus
clearTimeout(this._filterDebounceTimer); clearTimeout(this._filterDebounceTimer);
@@ -2255,8 +2260,17 @@ class TrackingLedgerConfig extends FormApplication {
this.render(false); this.render(false);
}, 300); }, 300);
}); });
// Keep focus on filter input after render // Keep focus/caret on filter input after render
filterInput.trigger("focus"); setTimeout(() => {
const el = filterInput[0];
if (!el) return;
const caret = this._pendingFilterCaret;
el.focus();
const posStart = Number.isFinite(caret?.start) ? caret.start : el.value.length;
const posEnd = Number.isFinite(caret?.end) ? caret.end : posStart;
try { el.setSelectionRange(posStart, posEnd); } catch (e) { /* ignore */ }
this._pendingFilterCaret = null;
}, 0);
html.find("[data-page-size]").on("change", (event) => { html.find("[data-page-size]").on("change", (event) => {
const value = event.currentTarget.value; const value = event.currentTarget.value;
@@ -2399,38 +2413,21 @@ async function clearDocumentHistory(doc) {
DAMAGE_DEALT_FLAG, DAMAGE_DEALT_FLAG,
ENCOUNTER_FLAG, ENCOUNTER_FLAG,
]; ];
for (const key of flagKeys) { await Promise.all(
flagKeys.map(async (key) => {
try { try {
await doc.unsetFlag(FLAG_SCOPE, key); await doc.unsetFlag(FLAG_SCOPE, key);
} catch (err) { } catch (err) {
console.warn(`[GowlersTracking] Failed to clear flag ${key} for ${doc.name ?? doc.id}`, err); console.warn(`[GowlersTracking] Failed to clear flag ${key} for ${doc.name ?? doc.id}`, err);
} }
} })
);
primeDocBaseline(doc);
} }
async function clearAllHistoriesWithProgress() { async function clearAllHistoriesWithProgress() {
try { try {
const targets = new Map(); const entries = collectClearTargets();
// 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) { if (!entries.length) {
ui.notifications?.info?.("No actor or token histories found to clear."); ui.notifications?.info?.("No actor or token histories found to clear.");
return; return;
@@ -2473,6 +2470,40 @@ async function clearAllHistoriesWithProgress() {
} }
} }
function collectClearTargets() {
const targets = new Map();
// Core actors in the directory (player characters and linked NPCs)
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} (Actor)` });
}
// Tokens on scenes (covers unlinked NPCs and token-specific flags)
for (const token of collectAllTokens()) {
const tokenKey = `token:${token.uuid ?? token.id}:${token.parent?.uuid ?? "scene"}`;
if (!targets.has(tokenKey)) {
const sceneName = token.parent?.name ? ` - ${token.parent.name}` : "";
targets.set(tokenKey, { doc: token, label: `${token.name ?? token.id}${sceneName} (Token)` });
}
// Include the token's actor (unlinked actor copies need clearing too)
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} (Actor)` });
}
}
return Array.from(targets.values());
}
function primeDocBaseline(doc) {
if (!doc) return;
const actor = doc instanceof Actor ? doc : doc.actor;
if (actor) primeActor(actor);
}
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 ?? []) {