feat(bundle): mirror source changes in embedded userscript
This commit is contained in:
@@ -276,6 +276,7 @@
|
|||||||
// ===== ui.js (embedded) =====
|
// ===== ui.js (embedded) =====
|
||||||
const TAT_STYLE = `
|
const TAT_STYLE = `
|
||||||
.tat-root { position: fixed; z-index: 99999; min-width: 320px; max-width: 420px; background: #2b2b2b; color: #ddd; border: 1px solid #444; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.4); font: 13px/1.4 Tahoma, Verdana, sans-serif; padding: 12px 14px; }
|
.tat-root { position: fixed; z-index: 99999; min-width: 320px; max-width: 420px; background: #2b2b2b; color: #ddd; border: 1px solid #444; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.4); font: 13px/1.4 Tahoma, Verdana, sans-serif; padding: 12px 14px; }
|
||||||
|
.tat-root.tat-anchored { position: static; margin: 0 auto 12px; max-width: 720px; box-shadow: none; }
|
||||||
.tat-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 8px; margin-bottom: 10px; border-bottom: 1px solid #444; cursor: move; user-select: none; }
|
.tat-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 8px; margin-bottom: 10px; border-bottom: 1px solid #444; cursor: move; user-select: none; }
|
||||||
.tat-header strong { color: #fff; }
|
.tat-header strong { color: #fff; }
|
||||||
.tat-close { cursor: pointer; opacity: 0.6; padding: 0 4px; }
|
.tat-close { cursor: pointer; opacity: 0.6; padding: 0 4px; }
|
||||||
@@ -350,10 +351,36 @@
|
|||||||
setMode(mode, anchorInfo) {
|
setMode(mode, anchorInfo) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
if (!this.root) return;
|
if (!this.root) return;
|
||||||
|
// Clear all position styles
|
||||||
this.root.style.transform = ''; this.root.style.top = ''; this.root.style.bottom = ''; this.root.style.left = ''; this.root.style.right = '';
|
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.left = '20px'; }
|
this.root.classList.remove('tat-anchored');
|
||||||
else if (anchorInfo && anchorInfo.canAnchor) { this._positionAnchored(anchorInfo.rect); }
|
if (mode === 'free') {
|
||||||
else { this.root.style.top = '20px'; this.root.style.left = '50%'; this.root.style.transform = 'translateX(-50%)'; }
|
// Floating mode: ensure dialog is in body and position at bottom-left.
|
||||||
|
if (this.root.parentNode !== document.body) {
|
||||||
|
document.body.appendChild(this.root);
|
||||||
|
}
|
||||||
|
this.root.style.bottom = '20px';
|
||||||
|
this.root.style.left = '20px';
|
||||||
|
} else if (anchorInfo && anchorInfo.canAnchor) {
|
||||||
|
if (anchorInfo.insertBefore) {
|
||||||
|
// Docked mode: insert the dialog into the page flow before the
|
||||||
|
// given element, and add the tat-anchored class to switch to
|
||||||
|
// static positioning.
|
||||||
|
anchorInfo.insertBefore.parentNode.insertBefore(this.root, anchorInfo.insertBefore);
|
||||||
|
this.root.classList.add('tat-anchored');
|
||||||
|
} else if (anchorInfo.rect) {
|
||||||
|
// Fallback: position fixed above the rect (old behavior, used when
|
||||||
|
// no insertion point is available but a rect was given).
|
||||||
|
this._positionAnchored(anchorInfo.rect);
|
||||||
|
}
|
||||||
|
// If neither insertBefore nor rect, leave the dialog where it is
|
||||||
|
// (the caller will show an anchorError note).
|
||||||
|
} else {
|
||||||
|
// Top-center fallback (used when mode is anchored but no anchor info).
|
||||||
|
this.root.style.top = '20px';
|
||||||
|
this.root.style.left = '50%';
|
||||||
|
this.root.style.transform = 'translateX(-50%)';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_positionAnchored(rect) {
|
_positionAnchored(rect) {
|
||||||
if (!rect) return; // defensive: setMode may be called without a rect
|
if (!rect) return; // defensive: setMode may be called without a rect
|
||||||
@@ -439,20 +466,20 @@
|
|||||||
|
|
||||||
// ===== main.js (embedded) =====
|
// ===== main.js (embedded) =====
|
||||||
function findAnchorElement() {
|
function findAnchorElement() {
|
||||||
// Try several selectors in priority order. Torn's gym page renders the
|
// Return the element to insert the dialog BEFORE in the DOM.
|
||||||
// training UI as <ul class="properties___HASH"> (the list of attribute
|
// The user wants the dialog between the notification wrapper and the
|
||||||
// rows). Anchor the dialog above that list.
|
// gym content wrapper; we insert before gymContentWrapper.
|
||||||
const candidates = [
|
const candidates = [
|
||||||
'ul[class*="properties"]',
|
|
||||||
'[class*="gymContent"]',
|
|
||||||
'[class*="gymContentWrapper"]',
|
'[class*="gymContentWrapper"]',
|
||||||
|
'[class*="gymContent"]',
|
||||||
|
'ul[class*="properties"]',
|
||||||
// Legacy fallbacks (kept in case Torn ever wraps the list in a form):
|
// Legacy fallbacks (kept in case Torn ever wraps the list in a form):
|
||||||
'form[action*="train"]',
|
'form[action*="train"]',
|
||||||
'form.train-form',
|
'form.train-form',
|
||||||
'form[class*="train"]',
|
'form[class*="train"]',
|
||||||
'[class*="train-button"]',
|
'[class*="train-button"]',
|
||||||
'button[class*="train"]',
|
'button[class*="train"]',
|
||||||
'a[class*="train"]',
|
'a[href*="train"]',
|
||||||
'button[name="train"]',
|
'button[name="train"]',
|
||||||
'a[href*="train"]',
|
'a[href*="train"]',
|
||||||
];
|
];
|
||||||
@@ -516,20 +543,13 @@
|
|||||||
if (prefs.mode === 'anchored') {
|
if (prefs.mode === 'anchored') {
|
||||||
const el = findAnchorElement();
|
const el = findAnchorElement();
|
||||||
if (el) {
|
if (el) {
|
||||||
const rect = el.getBoundingClientRect();
|
dialog.setMode('anchored', { canAnchor: true, insertBefore: el });
|
||||||
dialog.setMode('anchored', { canAnchor: true, rect: rect });
|
|
||||||
if (typeof ResizeObserver !== 'undefined') {
|
|
||||||
const ro = new ResizeObserver(function () {
|
|
||||||
if (prefs.mode === 'anchored') dialog._positionAnchored(el.getBoundingClientRect());
|
|
||||||
});
|
|
||||||
ro.observe(el);
|
|
||||||
}
|
|
||||||
anchorError = null;
|
anchorError = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Anchor selector missed — don't snap to default, just keep current
|
// Anchor selector missed — don't snap to default, just keep current
|
||||||
// position and show a note.
|
// position and show a note.
|
||||||
anchorError = "Couldn't find the training form on this page.";
|
anchorError = "Couldn't find the training UI on this page.";
|
||||||
render();
|
render();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user