fix(bundle): mirror source fixes in embedded userscript

Mirrors the three src/ changes into the embedded copy in
torn-attribute-tracker.user.js:

1. Hoist the four let bindings (lastSnapshot, lastAttr, lastDelta,
   anchorError) to the top of start(), before dialog.mount() and
   applyMode(), so the anchor-miss branch of applyMode() can write
   anchorError without tripping the TDZ.

2. Default the floating dialog to bottom-left (left: 20px, bottom: 20px)
   in both Dialog.mount() and Dialog.setMode()'s 'free' branch.

3. One-time migration: if prefs.pos has any non-zero coordinate on load
   (a residue of the bottom-right era), reset it to {x: 0, y: 0} and log
   to the console. Stored position from any subsequent drag is preserved.

4. Wrap the start() body in try/catch and log failures to the console,
   so an unexpected error (e.g. TornTools conflict, future regressions)
   does not prevent the dialog from appearing.

All four changes are byte-equivalent to the corresponding src/ changes;
the build-time drift tests in tests/build.test.js still pass.
This commit is contained in:
Claude
2026-06-01 22:24:57 -05:00
parent b03cc80665
commit 76e3ba2488
+22 -7
View File
@@ -315,7 +315,7 @@
this.mode = opts.initialMode || 'free';
if (this.mode === 'free') {
root.style.bottom = '20px';
root.style.right = '20px';
root.style.left = '20px';
if (opts.initialPos && (opts.initialPos.x || opts.initialPos.y)) {
root.style.transform = 'translate(' + opts.initialPos.x + 'px, ' + opts.initialPos.y + 'px)';
}
@@ -327,7 +327,7 @@
this.mode = mode;
if (!this.root) return;
this.root.style.transform = ''; this.root.style.top = ''; this.root.style.bottom = ''; this.root.style.left = ''; this.root.style.right = '';
if (mode === 'free') { this.root.style.bottom = '20px'; this.root.style.right = '20px'; }
if (mode === 'free') { this.root.style.bottom = '20px'; this.root.style.left = '20px'; }
else if (anchorInfo && anchorInfo.canAnchor) { this._positionAnchored(anchorInfo.rect); }
else { this.root.style.top = '20px'; this.root.style.left = '50%'; this.root.style.transform = 'translateX(-50%)'; }
}
@@ -446,9 +446,26 @@
function start() {
if (window.__tat_started) return; window.__tat_started = true;
try {
const store = new Store({ storage: localStorage, onWarn: function (m) { console.warn(m); } });
const prefs = store.getPrefs();
// State that applyMode() and render() may touch on first call.
// Declared up-front to avoid TDZ ReferenceError if applyMode()'s
// anchor-miss branch fires before the natural declaration point.
let lastSnapshot = null;
let lastAttr = null;
let lastDelta = 0;
let anchorError = null;
// One-time migration: dialog now defaults to bottom-left, so reset any
// previously-saved position from the bottom-right era.
if (prefs.pos && (prefs.pos.x !== 0 || prefs.pos.y !== 0)) {
console.info('[tat] resetting dialog position to new bottom-left default');
prefs.pos = { x: 0, y: 0 };
store.setPos(prefs.pos);
}
const dialog = new Dialog({
onTargetChange: function (v) {
const a = currentAttribute(); if (!a) return; store.setTarget(a.attr, v); render();
@@ -461,11 +478,6 @@
dialog.mount({ initialMode: prefs.mode, initialPos: prefs.pos });
applyMode();
let lastSnapshot = null;
let lastAttr = null;
let lastDelta = 0;
let anchorError = null;
function snapshot() {
const a = currentAttribute();
if (!a) return { error: "Couldn't read attribute — Torn may have updated the page." };
@@ -527,6 +539,9 @@
onTrain: function (e) { store.recordTrain(e.attr, e.delta, e.ts); lastDelta = e.delta; render(); },
onParseFail: function (url) { console.warn('[tat] could not parse train response from', url); },
});
} catch (e) {
console.error('[tat] failed to start:', e);
}
}
// ===== self-test (only when location.hash === '#tat-test') =====