Grid-first viewport for Idle Artisan 2 with smaller viewport-fit tiles and live tile sizing
// ==UserScript==
// @name IdleArtisan2Grid
// @namespace http://tampermonkey.net/
// @version 1.0.3
// @description Grid-first viewport for Idle Artisan 2 with smaller viewport-fit tiles and live tile sizing
// @match https://idleartisan.com/myGameHome2.html
// @grant none
// @run-at document-idle
// ==/UserScript==
// Injected as a <script> element so the code runs in the page's global
// lexical scope, giving direct access to let/const variables like
// GAME_MODULES, activeModules, and pinnedModules.
(function () {
'use strict';
const el = document.createElement('script');
el.textContent = '(' + function () {
// =================================================================
// Constants & Defaults
// =================================================================
var GRID_STATE_KEY = 'ia2g_grid_state';
var PREFERRED_SPANS_KEY = 'ia2g_preferred_spans';
var DEBUG_DETACH_SPANS = false;
var SETTING_PREFIX = 'ia2g_';
var PANEL_VISUAL_CSS_MAP_KEY = 'ia2g_panel_visual_css_map';
var ACTIVE_MODULES_KEY = 'ia2g_active_modules';
var ACTIVE_MODULES_SIDES_KEY = 'ia2g_active_modules_sides';
var PRESETS_KEY = 'ia2g_presets';
var DEFAULTS = {
gridCols: 6,
gridRows: 4,
cellWidth: 25,
cellHeight: 25,
gridGap: 1,
showGrid: true,
headerHeight: 24,
roundedTabs: true,
enablePanelVisualCss: true,
enableLogMessageTransforms: true
};
var PROD_VERBOSE = false;
function _prodLog() {
if (!PROD_VERBOSE || typeof console === 'undefined' || typeof console.log !== 'function') return;
console.log.apply(console, arguments);
}
// =================================================================
// Settings
// =================================================================
var _settings = {};
var _panelVisualCssMap = {};
var _dynamicPanelCssStyle = null;
function _loadSettings() {
_settings = {};
var keys = Object.keys(DEFAULTS);
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
var stored = localStorage.getItem(SETTING_PREFIX + k);
if (stored !== null) {
if (typeof DEFAULTS[k] === 'number') {
_settings[k] = parseInt(stored, 10);
} else if (typeof DEFAULTS[k] === 'boolean') {
_settings[k] = stored === 'true';
} else {
_settings[k] = stored;
}
} else {
_settings[k] = DEFAULTS[k];
}
}
}
function _saveSetting(key, value) {
_settings[key] = value;
localStorage.setItem(SETTING_PREFIX + key, String(value));
}
function _loadPanelVisualCssMap() {
_panelVisualCssMap = {};
try {
var raw = localStorage.getItem(PANEL_VISUAL_CSS_MAP_KEY);
if (!raw) return;
var parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object') return;
Object.keys(parsed).forEach(function (key) {
if (!_isPersistableModuleKey(key)) return;
_panelVisualCssMap[key] = parsed[key] !== false;
});
} catch (e) {
console.error('[MP] panel visual css map load error', e);
_panelVisualCssMap = {};
}
}
function _savePanelVisualCssMap() {
try {
localStorage.setItem(PANEL_VISUAL_CSS_MAP_KEY, JSON.stringify(_panelVisualCssMap));
} catch (e) {
console.error('[MP] panel visual css map save error', e);
}
}
function _isPanelVisualCssEnabled(moduleKey) {
if (!_settings.enablePanelVisualCss) return false;
return _getPanelVisualCssPreference(moduleKey);
}
function _getPanelVisualCssPreference(moduleKey) {
if (!_isPersistableModuleKey(moduleKey)) return true;
if (!Object.prototype.hasOwnProperty.call(_panelVisualCssMap, moduleKey)) return true;
return _panelVisualCssMap[moduleKey] !== false;
}
function _setPanelVisualCssEnabled(moduleKey, enabled) {
if (!_isPersistableModuleKey(moduleKey)) return;
_panelVisualCssMap[moduleKey] = enabled !== false;
_savePanelVisualCssMap();
}
// =================================================================
// Variable Track Sizes
// =================================================================
var _lastColWidths = [];
var _lastRowHeights = [];
var _lastGap = 0;
var _EDGE_TOGGLE_MIN_WIDTH = 12;
var _EDGE_TOGGLE_MIN_HEIGHT = 12;
var _BRP_TOGGLE_BASE_SIZE = 20;
function _computeTrackSizes(baseCellW, baseCellH, viewportW, viewportH, gap) {
var cols = _settings.gridCols;
var rows = _settings.gridRows;
var colWidths = [];
var rowHeights = [];
var colOverridden = [];
var rowOverridden = [];
var ci, ri;
for (ci = 0; ci < cols; ci++) { colWidths.push(baseCellW); colOverridden.push(false); }
for (ri = 0; ri < rows; ri++) { rowHeights.push(baseCellH); rowOverridden.push(false); }
// Keep all tracks identical; do not stretch tracks to fill viewport.
// Any remainder stays as unused space at the right/bottom edge.
return { colWidths: colWidths, rowHeights: rowHeights };
}
function _getGridViewportInsets(dc) {
if (!dc) return { right: 0, bottom: 0 };
var dcRect = dc.getBoundingClientRect();
var insets = { right: 0, bottom: 0 };
function _stableInsetPx(overlap) {
// Reserve enough pixels to avoid clipping under overlay controls.
return Math.max(0, Math.ceil(overlap - 0.01));
}
function _overlapWithViewport(el) {
if (!el) return null;
var r = el.getBoundingClientRect();
var overlapW = Math.max(0, Math.min(dcRect.right, r.right) - Math.max(dcRect.left, r.left));
var overlapH = Math.max(0, Math.min(dcRect.bottom, r.bottom) - Math.max(dcRect.top, r.top));
if (overlapW <= 0 || overlapH <= 0) return null;
return { w: overlapW, h: overlapH };
}
var isPortrait = !!(window.matchMedia && window.matchMedia('(max-aspect-ratio: 1/1)').matches);
var logBar = document.getElementById('mp-log-toggle');
// Reserve right inset only in landscape where the log bar sits on the right edge.
if (logBar && !isPortrait) {
var logOverlap = _overlapWithViewport(logBar);
if (logOverlap) insets.right = Math.max(insets.right, _stableInsetPx(Math.min(logOverlap.w, _EDGE_TOGGLE_MIN_WIDTH)));
}
// Keep bottom-edge collapse controls out of the tile viewport so large fixed-size
// tiles remain visually consistent and do not appear clipped on the last row.
var chatBar = document.getElementById('mp-chat-collapse-bar');
var chatOverlap = _overlapWithViewport(chatBar);
if (chatOverlap) insets.bottom = Math.max(insets.bottom, _stableInsetPx(Math.min(chatOverlap.h, _EDGE_TOGGLE_MIN_HEIGHT)));
var brpToggle = document.getElementById('mp-brp-toggle');
var brpOverlap = _overlapWithViewport(brpToggle);
if (brpOverlap) {
insets.right = Math.max(insets.right, _stableInsetPx(Math.min(brpOverlap.w, _BRP_TOGGLE_BASE_SIZE)));
insets.bottom = Math.max(insets.bottom, _stableInsetPx(Math.min(brpOverlap.h, _BRP_TOGGLE_BASE_SIZE)));
}
return insets;
}
function _getGridViewportRemainders() {
var dc = document.getElementById('dual-menu-container');
if (!dc || !dc.classList.contains('mp-grid-viewport')) {
return { right: 0, bottom: 0 };
}
var insets = _getGridViewportInsets(dc);
var gap = Number(_settings.gridGap || 0);
if (!isFinite(gap) || gap < 0) gap = 0;
var tileW = Math.max(5, Number(_settings.cellWidth) || 5);
var tileH = Math.max(5, Number(_settings.cellHeight) || 5);
var vpW = Math.max(1, dc.clientWidth - insets.right);
var vpH = Math.max(1, dc.clientHeight - insets.bottom);
var cols = Math.max(1, Math.floor((vpW + gap) / (tileW + gap)));
var rows = Math.max(1, Math.floor((vpH + gap) / (tileH + gap)));
var gridW = cols * tileW + gap * (cols - 1);
var gridH = rows * tileH + gap * (rows - 1);
return {
right: Math.max(0, Math.round(dc.clientWidth - gridW)),
bottom: Math.max(0, Math.round(dc.clientHeight - gridH))
};
}
function _trackPos(sizes, index, gap) {
var pos = 0;
for (var i = 0; i < index; i++) {
pos += sizes[i] + gap;
}
return pos;
}
function _trackSpanSize(sizes, start, span, gap) {
var total = 0;
for (var i = start; i < start + span && i < sizes.length; i++) {
total += sizes[i];
}
total += gap * (span - 1);
return total;
}
function _effectiveDevicePixelRatio() {
var dpr = (typeof window.devicePixelRatio === 'number') ? window.devicePixelRatio : 1;
if (!isFinite(dpr) || dpr <= 0) return 1;
return dpr;
}
function _snapCssPxToDevicePixel(px, dpr) {
return Math.round(px * dpr) / dpr;
}
function _gridStepForAxis(axis) {
var gap = (typeof _lastGap === 'number') ? _lastGap : (_settings.gridGap || 0);
var tracks = axis === 'x' ? _lastColWidths : _lastRowHeights;
var fallback = axis === 'x' ? _settings.cellWidth : _settings.cellHeight;
var trackSize = (tracks && tracks.length) ? tracks[0] : Math.max(5, fallback);
return Math.max(1, trackSize + gap);
}
function _snapToGridAxis(value, axis, min, max) {
var step = _gridStepForAxis(axis);
var bounded = value;
if (typeof min === 'number' && bounded < min) bounded = min;
if (typeof max === 'number' && bounded > max) bounded = max;
if (!step || step <= 1) return Math.round(bounded);
var minUnits = (typeof min === 'number') ? Math.ceil(min / step) : -Infinity;
var maxUnits = (typeof max === 'number') ? Math.floor(max / step) : Infinity;
// If no snapped value can exist in-range, keep smooth bounded sizing.
if (minUnits > maxUnits) return Math.round(bounded);
var snappedUnits = Math.round(bounded / step);
if (snappedUnits < minUnits) snappedUnits = minUnits;
if (snappedUnits > maxUnits) snappedUnits = maxUnits;
var snapped = snappedUnits * step;
if (typeof min === 'number' && snapped < min) snapped = min;
if (typeof max === 'number' && snapped > max) snapped = max;
return Math.round(snapped);
}
function _hitTestTrack(sizes, gap, coord) {
var pos = 0;
for (var i = 0; i < sizes.length; i++) {
if (coord < pos + sizes[i] + gap / 2) return i;
pos += sizes[i] + gap;
}
return sizes.length - 1;
}
// =================================================================
// Grid State Model
// =================================================================
var _gridState = { panels: [] };
var _preferredGridSpans = {};
var _panX = 0, _panY = 0;
var _addTileMenuEl = null;
var _addTileOutsideHandler = null;
var _addTileKeyHandler = null;
var _panelTabAddMenuEl = null;
var _panelTabAddMenuOutsideHandler = null;
var _panelTabAddMenuKeyHandler = null;
var _panelTabAddMenuContext = null;
var _hamburgerAddInterceptorInstalled = false;
var _presets = [];
var _presetsDropdownEl = null;
var _presetsDropdownAnchorEl = null;
var _presetsOutsideHandler = null;
var _presetsKeyHandler = null;
var _presetsEditingId = null;
var _menuButtonsHidden = true;
function _isPersistableModuleKey(key) {
return typeof key === 'string' && key.trim().length > 0;
}
function _isRegisteredModuleKey(key) {
return _isPersistableModuleKey(key) && !!GAME_MODULES[key];
}
function _normalizePanelModules(panel) {
if (!panel) return null;
var tabs = [];
var seen = {};
if (Array.isArray(panel.tabs)) {
for (var i = 0; i < panel.tabs.length; i++) {
var t = panel.tabs[i];
if (_isPersistableModuleKey(t) && !seen[t]) {
tabs.push(t);
seen[t] = true;
}
}
}
if (!tabs.length && _isPersistableModuleKey(panel.key)) {
tabs.push(panel.key);
}
if (!tabs.length) return null;
panel.tabs = tabs;
panel.activeTab = (panel.activeTab && tabs.indexOf(panel.activeTab) !== -1) ? panel.activeTab : tabs[0];
panel.key = tabs[0];
return panel;
}
function _getPanelTabs(panel) {
return panel && panel.tabs && panel.tabs.length ? panel.tabs : (panel && panel.key ? [panel.key] : []);
}
function _forEachPanelModule(panel, fn) {
var tabs = _getPanelTabs(panel);
for (var i = 0; i < tabs.length; i++) fn(tabs[i]);
}
function _collectActiveModuleKeys() {
var keys = [];
var seen = {};
var panels = _getActivePanels();
for (var i = 0; i < panels.length; i++) {
_forEachPanelModule(panels[i], function (k) {
if (!seen[k]) {
seen[k] = true;
keys.push(k);
}
});
}
return keys;
}
function _panelHasModule(panel, key) {
return _getPanelTabs(panel).indexOf(key) !== -1;
}
function _setPanelActiveTab(panel, key) {
if (!panel) return;
if (_panelHasModule(panel, key)) panel.activeTab = key;
_normalizePanelModules(panel);
}
function _getPanelPrimaryKey(panel) {
var tabs = _getPanelTabs(panel);
return tabs[0] || null;
}
function _normalizeStoredSpan(value) {
var n = parseInt(value, 10);
if (!isFinite(n)) n = 1;
return Math.max(1, n);
}
function _getPanelPreferredColSpan(panel) {
if (!panel) return 1;
var raw = (panel.preferredColSpan !== undefined && panel.preferredColSpan !== null)
? panel.preferredColSpan
: panel.colSpan;
return Math.max(_normalizeStoredSpan(raw), _normalizeStoredSpan(panel.colSpan));
}
function _getPanelPreferredRowSpan(panel) {
if (!panel) return 1;
var raw = (panel.preferredRowSpan !== undefined && panel.preferredRowSpan !== null)
? panel.preferredRowSpan
: panel.rowSpan;
return Math.max(_normalizeStoredSpan(raw), _normalizeStoredSpan(panel.rowSpan));
}
function _loadPreferredGridSpans() {
_preferredGridSpans = {};
try {
var raw = localStorage.getItem(PREFERRED_SPANS_KEY);
if (!raw) return;
var parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object') return;
Object.keys(parsed).forEach(function (key) {
if (!_isPersistableModuleKey(key)) return;
var span = parsed[key];
if (!span || typeof span !== 'object') return;
_preferredGridSpans[key] = {
colSpan: _normalizeStoredSpan(span.colSpan),
rowSpan: _normalizeStoredSpan(span.rowSpan)
};
});
} catch (e) {
console.error('[MP] preferred spans load error', e);
_preferredGridSpans = {};
}
}
function _savePreferredGridSpans() {
try {
localStorage.setItem(PREFERRED_SPANS_KEY, JSON.stringify(_preferredGridSpans));
} catch (e) {
console.error('[MP] preferred spans save error', e);
}
}
function _setPreferredGridSpan(key, colSpan, rowSpan) {
if (!_isPersistableModuleKey(key)) return;
_preferredGridSpans[key] = {
colSpan: _normalizeStoredSpan(colSpan),
rowSpan: _normalizeStoredSpan(rowSpan)
};
if (DEBUG_DETACH_SPANS) {
_prodLog('[IA2G][span] setPreferred', key, _preferredGridSpans[key]);
}
}
function _debugSpanLog(stage, payload) {
if (!DEBUG_DETACH_SPANS) return;
try {
_prodLog('[IA2G][span][' + stage + ']', payload);
} catch (e) {
_prodLog('[IA2G][span][' + stage + ']');
}
}
function _shouldReplacePreferredSpan(existingSpan, colSpan, rowSpan) {
if (!existingSpan) return true;
var existingCol = _normalizeStoredSpan(existingSpan.colSpan);
var existingRow = _normalizeStoredSpan(existingSpan.rowSpan);
var existingArea = existingCol * existingRow;
var incomingCol = _normalizeStoredSpan(colSpan);
var incomingRow = _normalizeStoredSpan(rowSpan);
var incomingArea = incomingCol * incomingRow;
return incomingArea > existingArea;
}
function _rememberPreferredGridSpanForPanel(panel) {
if (!panel) return;
var storedColSpan = _getPanelPreferredColSpan(panel);
var storedRowSpan = _getPanelPreferredRowSpan(panel);
panel.preferredColSpan = storedColSpan;
panel.preferredRowSpan = storedRowSpan;
var tabs = _getPanelTabs(panel);
if (!tabs.length) return;
if (tabs.length === 1) {
_setPreferredGridSpan(tabs[0], storedColSpan, storedRowSpan);
return;
}
// For grouped tabs, only upgrade remembered sizes so a temporary
// small container does not erase each tab's detached size memory.
tabs.forEach(function (k) {
if (_shouldReplacePreferredSpan(_preferredGridSpans[k], storedColSpan, storedRowSpan)) {
_setPreferredGridSpan(k, storedColSpan, storedRowSpan);
}
});
}
function _syncPreferredGridSpansFromPanels() {
_gridState.panels.forEach(function (p) {
_rememberPreferredGridSpanForPanel(p);
});
}
function _getPreferredGridSpan(key) {
var span = _preferredGridSpans[key];
if (!span) return { colSpan: 1, rowSpan: 1 };
return {
colSpan: _normalizeStoredSpan(span.colSpan),
rowSpan: _normalizeStoredSpan(span.rowSpan)
};
}
function _getPanelLabel(panel, key) {
var tabKey = key || panel.activeTab || _getPanelPrimaryKey(panel);
var mod = tabKey ? GAME_MODULES[tabKey] : null;
return mod ? mod.label : (tabKey || '');
}
function _loadGridState() {
try {
var raw = localStorage.getItem(GRID_STATE_KEY);
if (raw) {
var parsed = JSON.parse(raw);
if (parsed && Array.isArray(parsed.panels)) {
_gridState.panels = parsed.panels.map(function (p) {
if (!p) return null;
return _normalizePanelModules({
key: p.key,
tabs: p.tabs,
activeTab: p.activeTab,
col: parseInt(p.col, 10) || 0,
row: parseInt(p.row, 10) || 0,
colSpan: parseInt(p.colSpan, 10) || 1,
rowSpan: parseInt(p.rowSpan, 10) || 1,
preferredColSpan: parseInt(p.preferredColSpan, 10) || parseInt(p.colSpan, 10) || 1,
preferredRowSpan: parseInt(p.preferredRowSpan, 10) || parseInt(p.rowSpan, 10) || 1
});
}).filter(Boolean);
if (_gridState.panels.length) return;
}
}
_migrateOldState();
} catch (e) {
console.error('[MP] grid state load error', e);
_gridState.panels = [];
}
}
function _migrateOldState() {
try {
var raw = localStorage.getItem(ACTIVE_MODULES_KEY);
if (raw) {
var arr = JSON.parse(raw);
if (Array.isArray(arr)) {
_gridState.panels = [];
for (var i = 0; i < arr.length; i++) {
if (!_isPersistableModuleKey(arr[i])) continue;
var col = i % _settings.gridCols;
var row = Math.floor(i / _settings.gridCols);
if (row < _settings.gridRows) {
_gridState.panels.push(_normalizePanelModules({
key: arr[i], col: col, row: row,
colSpan: 1, rowSpan: 1
}));
}
}
if (_gridState.panels.length) return;
}
}
var gs = localStorage.getItem(ACTIVE_MODULES_SIDES_KEY);
if (gs) {
var obj = JSON.parse(gs);
_gridState.panels = [];
if (_isPersistableModuleKey(obj.left))
_gridState.panels.push(_normalizePanelModules({ key: obj.left, col: 0, row: 0, colSpan: 1, rowSpan: 1 }));
if (_isPersistableModuleKey(obj.right))
_gridState.panels.push(_normalizePanelModules({ key: obj.right, col: 1, row: 0, colSpan: 1, rowSpan: 1 }));
if (_gridState.panels.length) return;
}
} catch (e) { /* ignore */ }
_gridState.panels = [
_normalizePanelModules({ key: 'actions', col: 0, row: 0, colSpan: 1, rowSpan: 1 }),
_normalizePanelModules({ key: 'info', col: 1, row: 0, colSpan: 1, rowSpan: 1 })
];
}
function _saveGridState() {
try {
_gridState.panels = _gridState.panels.map(_normalizePanelModules).filter(Boolean);
_syncPreferredGridSpansFromPanels();
localStorage.setItem(GRID_STATE_KEY, JSON.stringify({ panels: _gridState.panels }));
_savePreferredGridSpans();
var keys = _collectActiveModuleKeys();
localStorage.setItem(ACTIVE_MODULES_KEY, JSON.stringify(keys));
localStorage.setItem(ACTIVE_MODULES_SIDES_KEY, JSON.stringify({
left: keys[0] || null,
right: keys[1] || null
}));
} catch (e) {
console.error('[MP] grid state save error', e);
}
}
function _clampAllPanels() {
var cols = _settings.gridCols;
var rows = _settings.gridRows;
_gridState.panels.forEach(function (p) {
_normalizePanelModules(p);
p.col = Math.max(0, Math.min(p.col, cols - 1));
p.row = Math.max(0, Math.min(p.row, rows - 1));
p.colSpan = Math.max(1, Math.min(p.colSpan || 1, cols - p.col));
p.rowSpan = Math.max(1, Math.min(p.rowSpan || 1, rows - p.row));
});
}
// =================================================================
// Free-Form State Model
// =================================================================
var FREEFORM_STATE_KEY = 'ia2g_freeform_state';
var _freeformState = { panels: [] };
var _nextZIndex = 1;
function _loadFreeformState() {
try {
var raw = localStorage.getItem(FREEFORM_STATE_KEY);
if (raw) {
var parsed = JSON.parse(raw);
if (parsed && Array.isArray(parsed.panels)) {
_freeformState.panels = parsed.panels.map(function (p) {
if (!p) return null;
return _normalizePanelModules({
key: p.key,
tabs: p.tabs,
activeTab: p.activeTab,
x: parseInt(p.x, 10) || 0,
y: parseInt(p.y, 10) || 0,
width: parseInt(p.width, 10) || 450,
height: parseInt(p.height, 10) || 380,
zIndex: parseInt(p.zIndex, 10) || 1
});
}).filter(Boolean);
var maxZ = 0;
_freeformState.panels.forEach(function (p) {
if (p.zIndex > maxZ) maxZ = p.zIndex;
});
_nextZIndex = maxZ + 1;
return;
}
}
} catch (e) {
console.error('[MP] freeform state load error', e);
}
_freeformState.panels = [];
}
function _saveFreeformState() {
try {
_freeformState.panels = _freeformState.panels.map(_normalizePanelModules).filter(Boolean);
localStorage.setItem(FREEFORM_STATE_KEY, JSON.stringify({ panels: _freeformState.panels }));
var keys = _collectActiveModuleKeys();
localStorage.setItem(ACTIVE_MODULES_KEY, JSON.stringify(keys));
localStorage.setItem(ACTIVE_MODULES_SIDES_KEY, JSON.stringify({
left: keys[0] || null,
right: keys[1] || null
}));
} catch (e) {
console.error('[MP] freeform state save error', e);
}
}
function _normalizePresetPanel(panel, isFreeform) {
if (!panel || typeof panel !== 'object') return null;
var normalized = _normalizePanelModules({
key: panel.key,
tabs: panel.tabs,
activeTab: panel.activeTab,
col: parseInt(panel.col, 10) || 0,
row: parseInt(panel.row, 10) || 0,
colSpan: parseInt(panel.colSpan, 10) || 1,
rowSpan: parseInt(panel.rowSpan, 10) || 1,
preferredColSpan: parseInt(panel.preferredColSpan, 10) || parseInt(panel.colSpan, 10) || 1,
preferredRowSpan: parseInt(panel.preferredRowSpan, 10) || parseInt(panel.rowSpan, 10) || 1,
x: parseInt(panel.x, 10) || 0,
y: parseInt(panel.y, 10) || 0,
width: parseInt(panel.width, 10) || 450,
height: parseInt(panel.height, 10) || 380,
zIndex: parseInt(panel.zIndex, 10) || 1
});
if (!normalized) return null;
var tabs = _getPanelTabs(normalized).filter(_isPersistableModuleKey);
if (!tabs.length) return null;
normalized.tabs = tabs;
normalized.key = tabs[0];
normalized.activeTab = (normalized.activeTab && tabs.indexOf(normalized.activeTab) !== -1)
? normalized.activeTab
: tabs[0];
if (isFreeform) {
normalized.x = parseInt(normalized.x, 10) || 0;
normalized.y = parseInt(normalized.y, 10) || 0;
normalized.width = Math.max(150, parseInt(normalized.width, 10) || 450);
normalized.height = Math.max(100, parseInt(normalized.height, 10) || 380);
normalized.zIndex = Math.max(1, parseInt(normalized.zIndex, 10) || 1);
} else {
normalized.col = Math.max(0, parseInt(normalized.col, 10) || 0);
normalized.row = Math.max(0, parseInt(normalized.row, 10) || 0);
normalized.colSpan = Math.max(1, parseInt(normalized.colSpan, 10) || 1);
normalized.rowSpan = Math.max(1, parseInt(normalized.rowSpan, 10) || 1);
normalized.preferredColSpan = Math.max(1, parseInt(normalized.preferredColSpan, 10) || normalized.colSpan);
normalized.preferredRowSpan = Math.max(1, parseInt(normalized.preferredRowSpan, 10) || normalized.rowSpan);
}
return normalized;
}
function _clonePanelsForPreset(panels, isFreeform) {
var src = Array.isArray(panels) ? panels : [];
var out = [];
for (var i = 0; i < src.length; i++) {
var p = src[i];
if (!p) continue;
var cloned = _normalizePresetPanel({
key: p.key,
tabs: Array.isArray(p.tabs) ? p.tabs.slice() : undefined,
activeTab: p.activeTab,
col: p.col,
row: p.row,
colSpan: p.colSpan,
rowSpan: p.rowSpan,
preferredColSpan: p.preferredColSpan,
preferredRowSpan: p.preferredRowSpan,
x: p.x,
y: p.y,
width: p.width,
height: p.height,
zIndex: p.zIndex
}, isFreeform);
if (cloned) out.push(cloned);
}
return out;
}
function _clonePreferredSpansForPreset(source) {
var src = source && typeof source === 'object' ? source : {};
var out = {};
Object.keys(src).forEach(function (key) {
if (!_isPersistableModuleKey(key)) return;
var span = src[key];
if (!span || typeof span !== 'object') return;
out[key] = {
colSpan: _normalizeStoredSpan(span.colSpan),
rowSpan: _normalizeStoredSpan(span.rowSpan)
};
});
return out;
}
function _captureLayoutSnapshot() {
return {
gridPanels: _clonePanelsForPreset(_gridState.panels, false),
freeformPanels: _clonePanelsForPreset(_freeformState.panels, true),
preferredSpans: _clonePreferredSpansForPreset(_preferredGridSpans)
};
}
function _normalizePresetSnapshot(snapshot) {
var src = snapshot && typeof snapshot === 'object' ? snapshot : {};
return {
gridPanels: _clonePanelsForPreset(src.gridPanels, false),
freeformPanels: _clonePanelsForPreset(src.freeformPanels, true),
preferredSpans: _clonePreferredSpansForPreset(src.preferredSpans)
};
}
function _normalizePresetName(name) {
var next = (name === undefined || name === null) ? '' : String(name).trim();
if (!next) return null;
if (next.length > 40) next = next.slice(0, 40);
return next;
}
function _normalizePresetEntry(entry) {
if (!entry || typeof entry !== 'object') return null;
var normalizedName = _normalizePresetName(entry.name);
if (!normalizedName) return null;
var normalizedSnapshot = _normalizePresetSnapshot(entry.snapshot);
return {
id: (typeof entry.id === 'string' && entry.id.trim()) ? entry.id : ('preset_' + Date.now() + '_' + Math.floor(Math.random() * 1000000)),
name: normalizedName,
snapshot: normalizedSnapshot,
createdAt: parseInt(entry.createdAt, 10) || Date.now(),
updatedAt: parseInt(entry.updatedAt, 10) || Date.now()
};
}
function _loadPresets() {
_presets = [];
try {
var raw = localStorage.getItem(PRESETS_KEY);
if (!raw) return;
var parsed = JSON.parse(raw);
if (!Array.isArray(parsed)) return;
parsed.forEach(function (entry) {
var normalized = _normalizePresetEntry(entry);
if (normalized) _presets.push(normalized);
});
} catch (e) {
console.error('[MP] presets load error', e);
_presets = [];
}
}
function _savePresets() {
try {
localStorage.setItem(PRESETS_KEY, JSON.stringify(_presets));
} catch (e) {
console.error('[MP] presets save error', e);
}
}
function _getPresetById(presetId) {
for (var i = 0; i < _presets.length; i++) {
if (_presets[i].id === presetId) return _presets[i];
}
return null;
}
function _nextPresetName() {
var used = {};
_presets.forEach(function (preset) {
used[String((preset && preset.name) || '').toLowerCase()] = true;
});
var idx = 1;
var next = 'Preset ' + idx;
while (used[next.toLowerCase()]) {
idx++;
next = 'Preset ' + idx;
}
return next;
}
function _getActivePanels() {
return _gridState.panels;
}
function _getFreeformPanel(key) {
for (var i = 0; i < _freeformState.panels.length; i++) {
if (_panelHasModule(_freeformState.panels[i], key)) return _freeformState.panels[i];
}
return null;
}
function _bringToFront(key) {
var p = _getFreeformPanel(key);
if (!p) return;
p.zIndex = _nextZIndex++;
var el = document.querySelector('.mp-panel-freeform[data-panel-key="' + _getPanelPrimaryKey(p) + '"]');
if (el) el.style.zIndex = p.zIndex;
}
function _generateFreeformFromGrid() {
_freeformState.panels = [];
_gridState.panels.forEach(function (gp) {
_normalizePanelModules(gp);
var x = _trackPos(_lastColWidths, gp.col, _lastGap);
var y = _trackPos(_lastRowHeights, gp.row, _lastGap);
var w = _trackSpanSize(_lastColWidths, gp.col, gp.colSpan, _lastGap);
var h = _trackSpanSize(_lastRowHeights, gp.row, gp.rowSpan, _lastGap);
_freeformState.panels.push(_normalizePanelModules({
key: gp.key,
tabs: gp.tabs,
activeTab: gp.activeTab,
x: Math.round(x),
y: Math.round(y),
width: Math.round(w),
height: Math.round(h),
zIndex: _nextZIndex++
}));
});
_saveFreeformState();
}
// =================================================================
// Occupancy Helpers
// =================================================================
function _buildOccupancyGrid(excludeKey) {
var cols = _settings.gridCols;
var rows = _settings.gridRows;
var grid = [];
for (var r = 0; r < rows; r++) {
grid[r] = [];
for (var c = 0; c < cols; c++) {
grid[r][c] = null;
}
}
_gridState.panels.forEach(function (p) {
if (excludeKey && _panelHasModule(p, excludeKey)) return;
for (var r2 = p.row; r2 < p.row + p.rowSpan && r2 < rows; r2++) {
for (var c2 = p.col; c2 < p.col + p.colSpan && c2 < cols; c2++) {
grid[r2][c2] = _getPanelPrimaryKey(p);
}
}
});
return grid;
}
function _findFirstEmptyCell(excludeKey) {
var occ = _buildOccupancyGrid(excludeKey);
for (var r = 0; r < _settings.gridRows; r++) {
for (var c = 0; c < _settings.gridCols; c++) {
if (occ[r][c] === null) return { col: c, row: r };
}
}
return null;
}
function _isPanelActive(key) {
var panels = _getActivePanels();
for (var i = 0; i < panels.length; i++) {
if (_panelHasModule(panels[i], key)) return true;
}
return false;
}
function _getPanelByKey(key) {
for (var i = 0; i < _gridState.panels.length; i++) {
if (_panelHasModule(_gridState.panels[i], key)) return _gridState.panels[i];
}
return null;
}
function _findPanelAt(col, row, excludeKey) {
for (var i = 0; i < _gridState.panels.length; i++) {
var p = _gridState.panels[i];
if (excludeKey && _panelHasModule(p, excludeKey)) continue;
if (col >= p.col && col < p.col + p.colSpan &&
row >= p.row && row < p.row + p.rowSpan) {
return p;
}
}
return null;
}
function _canResize(key, col, row, colSpan, rowSpan) {
if (col + colSpan > _settings.gridCols) return false;
if (row + rowSpan > _settings.gridRows) return false;
for (var r = row; r < row + rowSpan; r++) {
for (var c = col; c < col + colSpan; c++) {
if (_findPanelAt(c, r, key)) return false;
}
}
return true;
}
function _canPlacePanelRect(col, row, colSpan, rowSpan, excludeKey) {
if (col < 0 || row < 0) return false;
if (col + colSpan > _settings.gridCols) return false;
if (row + rowSpan > _settings.gridRows) return false;
for (var r = row; r < row + rowSpan; r++) {
for (var c = col; c < col + colSpan; c++) {
if (_findPanelAt(c, r, excludeKey)) return false;
}
}
return true;
}
function _findPlacementForSpan(preferredCol, preferredRow, colSpan, rowSpan, excludeKey) {
if (_canPlacePanelRect(preferredCol, preferredRow, colSpan, rowSpan, excludeKey)) {
return { col: preferredCol, row: preferredRow, colSpan: colSpan, rowSpan: rowSpan };
}
for (var r = 0; r <= _settings.gridRows - rowSpan; r++) {
for (var c = 0; c <= _settings.gridCols - colSpan; c++) {
if (_canPlacePanelRect(c, r, colSpan, rowSpan, excludeKey)) {
return { col: c, row: r, colSpan: colSpan, rowSpan: rowSpan };
}
}
}
return null;
}
function _findBestPlacementWithShrink(preferredCol, preferredRow, colSpan, rowSpan, excludeKey) {
var cs = Math.max(1, colSpan || 1);
var rs = Math.max(1, rowSpan || 1);
while (cs >= 1 && rs >= 1) {
var placement = _findPlacementForSpan(preferredCol, preferredRow, cs, rs, excludeKey);
if (placement) return placement;
if (cs >= rs && cs > 1) cs--;
else if (rs > 1) rs--;
else break;
}
return null;
}
// =================================================================
// Game State Sync
// =================================================================
function _syncGameState() {
var keys = _collectActiveModuleKeys();
var liveKeys = keys.filter(_isRegisteredModuleKey);
activeModules = {
left: liveKeys[0] || null,
right: liveKeys[1] || null
};
isModuleActive = _isPanelActive;
}
// =================================================================
// CSS
// =================================================================
(function injectStyles() {
var css = [
'* {',
' scrollbar-width: thin;',
' scrollbar-color: #444 #1a1a1a;',
'}',
'*::-webkit-scrollbar {',
' width: 6px;',
' height: 6px;',
'}',
'*::-webkit-scrollbar-track {',
' background: #1a1a1a;',
' border-radius: 3px;',
'}',
'*::-webkit-scrollbar-thumb {',
' background: #444;',
' border-radius: 3px;',
'}',
'*::-webkit-scrollbar-thumb:hover {',
' background: #555;',
'}',
'*::-webkit-scrollbar-corner {',
' background: #1a1a1a;',
'}',
'body {',
' padding: 0 !important;',
'}',
'#game-layout-row {',
' border: none !important;',
'}',
'#container-wrapper.chat-collapsed {',
' border: none !important;',
'}',
'#container-wrapper.chat-collapsed #log-container {',
' padding-bottom: 0 !important;',
'}',
'#bottom-chat-panel {',
' border: none !important;',
'}',
'canvas {',
' border: none !important;',
'}',
'#dual-menu-container.mp-grid-viewport {',
' display: block !important;',
' position: absolute !important;',
' top: 0; left: 0;',
' width: 100% !important;',
' height: 100% !important;',
' z-index: 100;',
' pointer-events: auto !important;',
' overflow: hidden;',
'}',
'#dual-menu-container.mp-grid-viewport > #menu-left,',
'#dual-menu-container.mp-grid-viewport > #menu-right {',
' display: none !important;',
'}',
'#mp-grid-inner {',
' display: grid;',
' position: relative;',
' will-change: transform;',
'}',
'.mp-panel {',
' display: flex;',
' flex-direction: column;',
' overflow: hidden;',
' position: relative;',
' box-sizing: border-box;',
' border: 1px solid #444;',
' background: rgba(30, 30, 30, 0.95);',
' border-radius: 4px;',
' z-index: 1;',
'}',
'.mp-panel-header {',
' display: flex;',
' align-items: center;',
' justify-content: flex-start;',
' min-height: 24px;',
' background: #333;',
' border-bottom: 1px solid #555;',
' padding: 0 8px 0 0;',
' cursor: grab;',
' user-select: none;',
' flex-shrink: 0;',
'}',
'.mp-panel-header:active { cursor: grabbing; }',
'.mp-panel-title {',
' font-size: 12px;',
' color: #ccc;',
' font-weight: bold;',
' white-space: nowrap;',
' overflow: hidden;',
' text-overflow: ellipsis;',
'}',
'.mp-panel-tabs {',
' display: flex;',
' align-items: center;',
' gap: 4px;',
' min-width: 0;',
' flex: 1;',
' overflow-x: auto;',
' overflow-y: hidden;',
' padding: 2px 0;',
' position: relative;',
' justify-content: flex-start;',
'}',
'.mp-panel-tab.mp-tab-insert-preview {',
' border-style: dashed;',
' border-color: #7aa2ff;',
' color: #dce6ff;',
' background: #2b3347;',
' opacity: 0.95;',
' pointer-events: none;',
' cursor: default;',
'}',
'.mp-panel-tab {',
' display: inline-flex;',
' align-items: center;',
' gap: 4px;',
' max-width: 180px;',
' border: 1px solid #4b4b4b;',
' background: #252525;',
' color: #aaa;',
' border-radius: 0;',
' font-size: 11px;',
' line-height: 1.2;',
' padding: 3px 6px;',
' cursor: pointer;',
' white-space: nowrap;',
' text-overflow: ellipsis;',
' overflow: hidden;',
'}',
'.mp-panel-tab.mp-tab-dragging { opacity: 0.85; }',
'.mp-panel-tab:hover { border-color: #666; color: #ddd; }',
'.mp-panel-tab.is-active {',
' border-color: #777;',
' background: #3a3a3a;',
' color: #f0f0f0;',
'}',
'.mp-panel-tab.mp-panel-tab-add {',
' flex: 0 0 auto;',
' width: 24px;',
' justify-content: center;',
' font-weight: bold;',
' padding: 3px 0;',
' color: #cfd7ff;',
'}',
'.mp-panel-tab.mp-panel-tab-add:hover {',
' border-color: #7a9bff;',
' color: #fff;',
' background: #2e3a57;',
'}',
'.mp-panel-tab.mp-panel-tab-add.is-open {',
' border-color: #7a9bff;',
' color: #fff;',
' background: #35508a;',
'}',
'.mp-panel-tab-close {',
' border: none;',
' background: none;',
' color: inherit;',
' opacity: 0.7;',
' cursor: pointer;',
' font-size: 12px;',
' line-height: 1;',
' padding: 0;',
'}',
'.mp-panel-tab-close:hover { opacity: 1; color: #ff9b9b; }',
'.mp-panel-close {',
' background: none;',
' border: none;',
' color: #888;',
' font-size: 16px;',
' cursor: pointer;',
' padding: 0 4px;',
' line-height: 1;',
' margin-left: auto;',
'}',
'.mp-panel-close:hover { color: #ff4444; }',
'.mp-panel-content {',
' flex: 1;',
' overflow-y: auto;',
' overflow-x: hidden;',
' position: relative;',
' z-index: 0;',
' min-height: 0;',
' contain: strict;',
'}',
'.mp-panel-content > .menu-overlay {',
' display: block !important;',
' position: relative !important;',
' top: auto !important;',
' left: auto !important;',
' width: 100% !important;',
' max-width: none !important;',
' height: 100% !important;',
' box-sizing: border-box;',
' z-index: auto !important;',
'}',
'.mp-panel-content .menu-close-btn {',
' display: none !important;',
'}',
'.mp-resize-handle {',
' position: absolute;',
' z-index: 10;',
' background: transparent;',
'}',
'.mp-resize-t {',
' top: 0; left: 10px; right: 10px;',
' height: 8px;',
' cursor: n-resize;',
'}',
'.mp-resize-b {',
' bottom: 0; left: 10px; right: 10px;',
' height: 8px;',
' cursor: s-resize;',
'}',
'.mp-resize-l {',
' left: 0; top: 10px; bottom: 10px;',
' width: 8px;',
' cursor: w-resize;',
'}',
'.mp-resize-r {',
' right: 0; top: 10px; bottom: 10px;',
' width: 8px;',
' cursor: e-resize;',
'}',
'.mp-resize-tl {',
' top: 0; left: 0;',
' width: 10px; height: 10px;',
' cursor: nw-resize;',
'}',
'.mp-resize-tr {',
' top: 0; right: 0;',
' width: 10px; height: 10px;',
' cursor: ne-resize;',
'}',
'.mp-resize-bl {',
' bottom: 0; left: 0;',
' width: 10px; height: 10px;',
' cursor: sw-resize;',
'}',
'.mp-resize-br {',
' bottom: 0; right: 0;',
' width: 10px; height: 10px;',
' cursor: se-resize;',
'}',
'.mp-resize-br::after {',
' content: "";',
' position: absolute;',
' bottom: 2px; right: 2px;',
' width: 0; height: 0;',
' border-style: solid;',
' border-width: 0 0 7px 7px;',
' border-color: transparent transparent #555 transparent;',
'}',
'.mp-panel:hover .mp-resize-br::after {',
' border-color: transparent transparent #888 transparent;',
'}',
'.mp-drag-ghost {',
' position: absolute;',
' z-index: 300;',
' border: 2px dashed #3366ff;',
' background: rgba(51,102,255,0.15);',
' border-radius: 4px;',
' pointer-events: none;',
' transition: left 0.08s, top 0.08s;',
'}',
'#mp-grid-inner.mp-freeform-mode {',
' display: block !important;',
' position: relative;',
'}',
'.mp-panel-freeform {',
' position: absolute !important;',
' min-width: 150px;',
' min-height: 100px;',
'}',
'.mp-panel-freeform.mp-ff-dragging {',
' opacity: 0.9;',
' cursor: grabbing !important;',
'}',
'.mp-toast {',
' position: fixed;',
' top: 60px;',
' left: 50%;',
' transform: translateX(-50%);',
' background: #333;',
' color: #fff;',
' padding: 8px 16px;',
' border-radius: 6px;',
' font-size: 13px;',
' z-index: 10000;',
' pointer-events: none;',
' opacity: 0;',
' transition: opacity 0.3s;',
'}',
'.mp-toast-visible { opacity: 1; }',
'.mp-add-tile-menu {',
' position: fixed;',
' min-width: 220px;',
' background: #222;',
' border: 1px solid #555;',
' border-radius: 6px;',
' box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);',
' color: #ddd;',
' padding: 10px;',
' z-index: 10020;',
'}',
'.mp-add-tile-title {',
' font-weight: bold;',
' margin-bottom: 8px;',
' color: #fff;',
'}',
'.mp-add-tile-row {',
' display: flex;',
' align-items: center;',
' gap: 8px;',
' margin: 6px 0;',
'}',
'.mp-add-tile-row label {',
' font-size: 12px;',
' color: #bbb;',
' min-width: 58px;',
'}',
'.mp-add-tile-inputs {',
' display: flex;',
' align-items: center;',
' gap: 8px;',
' flex: 1;',
'}',
'.mp-add-tile-slider {',
' flex: 1;',
' accent-color: #2f6bff;',
'}',
'.mp-add-tile-row input[type="number"] {',
' width: 70px;',
' background: #111;',
' border: 1px solid #555;',
' color: #eee;',
' border-radius: 4px;',
' padding: 4px 6px;',
'}',
'.mp-add-tile-actions {',
' margin-top: 10px;',
' display: flex;',
' justify-content: flex-end;',
' gap: 8px;',
'}',
'.mp-add-tile-actions button {',
' border: none;',
' border-radius: 4px;',
' padding: 6px 10px;',
' cursor: pointer;',
' font-size: 12px;',
'}',
'.mp-add-tile-cancel {',
' background: #3a3a3a;',
' color: #ddd;',
'}',
'.mp-add-tile-confirm {',
' background: #2f6bff;',
' color: #fff;',
'}',
'.mp-panel-tab-add-menu {',
' position: fixed;',
' min-width: 190px;',
' max-width: 260px;',
' max-height: 320px;',
' overflow-y: auto;',
' background: #202020;',
' border: 1px solid #555;',
' border-radius: 6px;',
' box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);',
' z-index: 10030;',
'}',
'.mp-panel-tab-add-title {',
' font-size: 11px;',
' font-weight: bold;',
' color: #9fb9ff;',
' padding: 7px 10px 6px;',
' border-bottom: 1px solid #404040;',
' text-transform: uppercase;',
' letter-spacing: 0.02em;',
'}',
'.mp-panel-tab-add-menu .menu-item-row {',
' padding: 6px 8px;',
'}',
'.mp-panel-tab-add-menu .menu-item-btn {',
' font-size: 13px;',
' color: #ccc;',
' padding: 4px 5px;',
'}',
'.mp-panel-tab-add-menu .menu-item-btn.is-in-panel {',
' color: #87b2ff;',
'}',
'.mp-panel-tab-add-empty {',
' padding: 10px;',
' color: #888;',
' font-size: 12px;',
' font-style: italic;',
'}',
'.mp-presets-dropdown {',
' position: fixed;',
' min-width: 260px;',
' max-width: 320px;',
' max-height: 360px;',
' background: #202020;',
' border: 1px solid #555;',
' border-radius: 6px;',
' box-shadow: 0 10px 24px rgba(0, 0, 0, 0.45);',
' z-index: 10035;',
' padding: 8px;',
' display: flex;',
' flex-direction: column;',
' gap: 8px;',
'}',
'.mp-presets-new-btn {',
' border: 1px solid #4f76c7;',
' border-radius: 4px;',
' background: #2f6bff;',
' color: #fff;',
' font-size: 12px;',
' font-weight: bold;',
' padding: 6px 8px;',
' cursor: pointer;',
'}',
'.mp-presets-new-btn:hover {',
' background: #3b74ff;',
'}',
'.mp-presets-list {',
' display: flex;',
' flex-direction: column;',
' gap: 5px;',
' overflow-y: auto;',
'}',
'.mp-presets-empty {',
' color: #888;',
' font-size: 12px;',
' padding: 6px 4px;',
' font-style: italic;',
'}',
'.mp-preset-row {',
' display: flex;',
' align-items: center;',
' gap: 6px;',
'}',
'.mp-preset-load {',
' flex: 1 1 auto;',
' min-width: 0;',
' border: 1px solid #4a4a4a;',
' border-radius: 4px;',
' background: #2a2a2a;',
' color: #ddd;',
' font-size: 12px;',
' padding: 5px 6px;',
' text-align: left;',
' cursor: pointer;',
' white-space: nowrap;',
' overflow: hidden;',
' text-overflow: ellipsis;',
'}',
'.mp-preset-load:hover {',
' border-color: #5f85d6;',
' color: #fff;',
'}',
'.mp-preset-actions {',
' display: flex;',
' align-items: center;',
' gap: 4px;',
'}',
'.mp-preset-action, .mp-preset-delete {',
' border: 1px solid #555;',
' border-radius: 4px;',
' background: #2c2c2c;',
' color: #ccc;',
' font-size: 11px;',
' padding: 4px 6px;',
' cursor: pointer;',
'}',
'.mp-preset-action:hover, .mp-preset-delete:hover {',
' border-color: #777;',
' color: #fff;',
'}',
'.mp-preset-delete {',
' min-width: 24px;',
' padding: 4px;',
' font-size: 14px;',
' line-height: 1;',
'}',
'.mp-preset-rename-wrap {',
' display: flex;',
' align-items: center;',
' gap: 4px;',
' width: 100%;',
'}',
'.mp-preset-rename-input {',
' flex: 1 1 auto;',
' min-width: 0;',
' border: 1px solid #666;',
' border-radius: 4px;',
' background: #151515;',
' color: #eee;',
' font-size: 12px;',
' padding: 4px 6px;',
'}',
'#mp-log-toggle {',
' position: absolute;',
' top: 0;',
' bottom: 0;',
' width: 12px;',
' right: 0;',
' transform: translateX(50%);',
' display: flex;',
' align-items: center;',
' justify-content: center;',
' background: #1a1a1a;',
' border: none;',
' border-left: 1px solid #555;',
' border-right: 1px solid #444;',
' color: #888;',
' cursor: ew-resize;',
' font-size: 14px;',
' user-select: none;',
' padding: 0;',
' transition: background 0.15s, color 0.15s;',
' z-index: 180;',
'}',
'#mp-log-toggle:hover {',
' background: #2a2a2a;',
' color: #ccc;',
'}',
'body.mp-log-resizing {',
' cursor: ew-resize !important;',
' user-select: none !important;',
'}',
'body.mp-log-resizing * {',
' cursor: ew-resize !important;',
'}',
'#log-container {',
' transition: flex 0.3s ease, min-width 0.3s ease,',
' border-width 0.3s ease, padding 0.3s ease;',
'}',
'#mp-log-brp-host {',
' flex: 0 0 auto;',
' border-top: 1px solid #333;',
' background: #151515;',
' min-height: 0;',
' margin-bottom: var(--mp-chat-bar-reserve, 0px);',
'}',
'#mp-log-brp-host:empty {',
' display: none !important;',
' border-top: 0;',
'}',
'#mp-log-brp-host > #bottom-right-panel {',
' position: relative !important;',
' flex: 0 0 auto !important;',
' width: 100% !important;',
' min-width: 0 !important;',
' border: none !important;',
' border-radius: 0 !important;',
'}',
'#log-container.mp-log-collapsed {',
' flex: 0 0 0px !important;',
' min-width: 0 !important;',
' overflow: hidden !important;',
' border-left-width: 0 !important;',
' padding: 0 !important;',
'}',
'#log-messages {',
' --log-font-size: 100%;',
' padding: 4px 6px !important;',
' overflow-y: auto !important;',
' min-height: 0 !important;',
'}',
'#log-messages .log-entry {',
' white-space: nowrap;',
' overflow: hidden;',
' font-size: var(--log-font-size);',
' margin-bottom: 1px !important;',
' padding-bottom: 1px !important;',
' flex-shrink: 0 !important;',
'}',
'#log-messages .log-entry .log-timestamp {',
' display: inline-block;',
' max-width: 0;',
' overflow: hidden;',
' vertical-align: bottom;',
' opacity: 0;',
' margin-right: 0 !important;',
' transition: max-width 0.2s ease, opacity 0.15s ease, margin-right 0.2s ease;',
'}',
'#log-messages .log-entry:hover .log-timestamp {',
' max-width: 5em;',
' opacity: 1;',
' margin-right: 4px !important;',
'}',
'#log-messages .log-entry[data-category="offline"] {',
' white-space: normal;',
'}',
'#log-messages .log-entry[data-category="offline"] > div {',
' margin-bottom: 2px !important;',
' padding-bottom: 1px !important;',
'}',
'#log-messages .log-entry[data-category="offline"] div[style*="grid"] {',
' gap: 1px !important;',
' margin-bottom: 2px !important;',
'}',
'#bottom-chat-panel.collapsed {',
' z-index: 150 !important;',
'}',
'#mp-brp-toggle {',
' position: absolute;',
' top: 2px; right: 4px;',
' width: 20px; height: 20px;',
' background: #333;',
' border: 1px solid #555;',
' border-radius: 3px;',
' color: #888;',
' cursor: pointer;',
' font-size: 12px;',
' line-height: 1;',
' display: flex;',
' align-items: center;',
' justify-content: center;',
' z-index: 200;',
' padding: 0;',
' transition: color 0.15s;',
'}',
'#mp-brp-toggle:hover { color: #ccc; }',
'#bottom-right-panel.mp-brp-collapsed > *:not(#mp-brp-toggle) {',
' display: none !important;',
'}',
'#bottom-right-panel.mp-brp-collapsed {',
' min-width: 28px !important;',
' width: 28px !important;',
' height: 28px !important;',
' border-radius: 4px;',
' background: #222 !important;',
'}',
// --- BRP adaptive layout: default tightening + scrollbar fallback ---
'#xp-hunger-container {',
' padding: 4px 6px !important;',
' min-height: 0 !important;',
' overflow-y: auto !important;',
' gap: 2px !important;',
'}',
'#bottom-right-panel { min-height: 0 !important; min-width: 0 !important; }',
'#bottom-right-panel #hunger-bar-wrapper,',
'#bottom-right-panel #xp-bar-wrapper {',
' flex: 1 !important;',
' width: auto !important;',
' min-width: 60px !important;',
'}',
'#bottom-right-panel #merc-bar-wrapper,',
'#bottom-right-panel #tithe-barn-bar-wrapper {',
' flex: 1 !important;',
' width: auto !important;',
' min-width: 50px !important;',
'}',
// --- BRP compact mode (content row 55-114px) ---
'.mp-brp-compact {',
' padding: 2px 4px !important;',
' gap: 0 !important;',
'}',
'.mp-brp-compact #buffs-hunger-separator,',
'.mp-brp-compact #xp-hunger-separator,',
'.mp-brp-compact #xp-merc-separator {',
' display: none !important;',
'}',
'.mp-brp-compact #buffs-section {',
' display: flex !important;',
' flex-direction: column !important;',
' align-items: stretch !important;',
' gap: 2px !important;',
'}',
'.mp-brp-compact #buffs-section > div:first-child {',
' display: flex !important;',
' align-items: center !important;',
' gap: 6px !important;',
'}',
'.mp-brp-compact #buffs-section > #timers-section {',
' display: flex !important;',
' width: 100% !important;',
' margin-left: 0 !important;',
' justify-content: space-between !important;',
' flex-wrap: nowrap !important;',
' gap: 8px !important;',
' flex-shrink: 0 !important;',
'}',
'.mp-brp-compact #hunger-section {',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 4px !important;',
' flex-wrap: wrap !important;',
'}',
'.mp-brp-compact #hunger-section > div {',
' display: contents !important;',
'}',
'.mp-brp-compact #hunger-label { order: 1; }',
'.mp-brp-compact #hunger-bar-wrapper { order: 2; }',
'.mp-brp-compact #hunger-controls { order: 3; display: flex !important; gap: 2px; }',
'.mp-brp-compact #rations-display { order: 4; }',
'.mp-brp-compact #xp-section {',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 0 !important;',
' flex-wrap: nowrap !important;',
'}',
'.mp-brp-compact #xp-section > div:first-child {',
' display: flex !important;',
' flex-direction: column !important;',
' align-items: flex-start !important;',
' justify-content: center !important;',
' flex: 0 0 auto !important;',
' min-width: max-content !important;',
'}',
'.mp-brp-compact #xp-bar-wrapper {',
' flex: 1 1 auto !important;',
' min-width: 0 !important;',
'}',
'.mp-brp-compact #xp-skill-name,',
'.mp-brp-compact #xp-timer {',
' display: block !important;',
' line-height: 1.15;',
'}',
'.mp-brp-compact #xp-timer::before {',
' content: "\\21B3 ";',
'}',
'.mp-brp-compact #merc-section,',
'.mp-brp-compact #tithe-barn-section {',
' flex: 1 1 0 !important;',
' min-width: 0 !important;',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 4px !important;',
'}',
'.mp-brp-compact #timers-section {',
' gap: 4px !important;',
'}',
'.mp-brp-compact #hunger-bar-wrapper,',
'.mp-brp-compact #xp-bar-wrapper,',
'.mp-brp-compact #merc-bar-wrapper,',
'.mp-brp-compact #tithe-barn-bar-wrapper {',
' height: 10px !important;',
'}',
'.mp-brp-compact #hunger-text,',
'.mp-brp-compact #xp-text,',
'.mp-brp-compact #merc-text,',
'.mp-brp-compact #tithe-barn-text,',
'.mp-brp-compact #hunger-label,',
'.mp-brp-compact #xp-skill-name,',
'.mp-brp-compact #xp-timer,',
'.mp-brp-compact #merc-label,',
'.mp-brp-compact #merc-type,',
'.mp-brp-compact #tithe-barn-label,',
'.mp-brp-compact #tithe-barn-type,',
'.mp-brp-compact #rations-display,',
'.mp-brp-compact #active-buffs-container,',
'.mp-brp-compact .hunger-btn {',
' font-size: 10px !important;',
'}',
// --- BRP minimal mode (content row < 55px) ---
'.mp-brp-minimal {',
' padding: 1px 4px !important;',
' gap: 0 !important;',
'}',
'.mp-brp-minimal #merc-bar-wrapper,',
'.mp-brp-minimal #tithe-barn-bar-wrapper {',
' display: none !important;',
'}',
'.mp-brp-minimal #hunger-bar-wrapper,',
'.mp-brp-minimal #xp-bar-wrapper {',
' height: 8px !important;',
'}',
'#bottom-chat-panel {',
' flex-direction: column !important;',
' overflow: visible !important;',
' transition: none !important;',
'}',
'#mp-chat-collapse-bar {',
' height: 12px;',
' display: flex;',
' align-items: center;',
' justify-content: center;',
' background: #1a1a1a;',
' border-top: 1px solid #555;',
' border-bottom: 1px solid #444;',
' cursor: ns-resize;',
' color: #888;',
' font-size: 10px;',
' user-select: none;',
' transition: background 0.15s, color 0.15s;',
' position: absolute;',
' bottom: 0;',
' left: 0;',
' right: 0;',
' transform: translateY(50%);',
' z-index: 180;',
'}',
'#mp-chat-collapse-bar:hover {',
' background: #2a2a2a;',
' color: #ccc;',
'}',
'#mp-bar-pop {',
' position: absolute;',
' left: 8px;',
' top: 0;',
' bottom: 0;',
' display: flex;',
' align-items: center;',
' font-size: 10px;',
' font-weight: bold;',
' color: #aaa;',
' pointer-events: none;',
'}',
'body.mp-chat-resizing {',
' cursor: ns-resize !important;',
' user-select: none !important;',
'}',
'body.mp-chat-resizing * {',
' cursor: ns-resize !important;',
'}',
'#mp-chat-content-row {',
' flex: 1;',
' display: flex;',
' min-height: 0;',
' padding-top: 6px;',
' box-sizing: border-box;',
'}',
'#bottom-chat-panel.mp-chat-collapsed {',
' height: 0 !important;',
' min-height: 0 !important;',
' border: none !important;',
' overflow: visible !important;',
'}',
'#bottom-chat-panel.mp-chat-collapsed #mp-chat-content-row {',
' display: none !important;',
'}',
'@media (max-aspect-ratio: 1/1) {',
' #mp-portrait-controls {',
' display: flex !important;',
' align-items: stretch;',
' gap: 0;',
' padding: 2px 6px;',
' background: #151515;',
' border-top: 1px solid #333;',
' border-bottom: 1px solid #333;',
' z-index: 35;',
' }',
' #log-container { position: relative !important; }',
' #log-container > #mp-log-toggle {',
' position: absolute !important;',
' top: 0 !important;',
' left: 50% !important;',
' right: auto !important;',
' bottom: auto !important;',
' transform: translate(-50%, -50%) !important;',
' width: 52px !important;',
' min-width: 52px !important;',
' height: 12px !important;',
' border: 1px solid #444 !important;',
' border-radius: 4px !important;',
' border-left: 1px solid #444 !important;',
' border-right: 1px solid #444 !important;',
' cursor: ns-resize !important;',
' z-index: 40 !important;',
' }',
' body.mp-log-resizing {',
' cursor: ns-resize !important;',
' }',
' body.mp-log-resizing * {',
' cursor: ns-resize !important;',
' }',
' #log-container.mp-log-collapsed {',
' flex: 0 0 0px !important;',
' min-height: 0 !important;',
' height: 0 !important;',
' overflow: hidden !important;',
' }',
' #mp-brp-toggle { display: none !important; }',
' #mp-portrait-controls #mp-chat-collapse-bar {',
' position: relative !important;',
' top: auto !important;',
' bottom: auto !important;',
' left: auto !important;',
' right: auto !important;',
' transform: none !important;',
' flex: 1 1 auto;',
' min-width: 0;',
' height: 18px !important;',
' border: 1px solid #444;',
' border-radius: 3px;',
' z-index: auto !important;',
' }',
' #bottom-chat-panel {',
' min-height: 0 !important;',
' max-height: none !important;',
' }',
' #mp-portrait-controls #mp-bar-pop {',
' position: static !important;',
' margin-right: auto;',
' padding-left: 4px;',
' font-size: 10px;',
' }',
' #container-wrapper > #mp-chat-collapse-bar { display: none !important; }',
' #bottom-chat-panel { flex-direction: row !important; }',
' #mp-grid-inner {',
' display: flex !important;',
' flex-direction: column !important;',
' width: 100% !important;',
' height: 100% !important;',
' transform: none !important;',
' }',
' .mp-panel { flex: 1 1 100% !important; min-height: 100% !important; }',
' .mp-panel ~ .mp-panel { display: none !important; }',
' .mp-panel-freeform ~ .mp-panel-freeform { display: none !important; }',
' .mp-panel-freeform { position: relative !important; left: auto !important; top: auto !important; width: 100% !important; height: 100% !important; }',
' .mp-resize-handle { display: none !important; }',
' .mp-drag-ghost { display: none !important; }',
'}',
'/* === Single-Row UI Bar === */',
'#ui-bar {',
' --rows: 1 !important;',
' padding: 2px 6px !important;',
' gap: 5px !important;',
' font-size: min(calc(1.2vw - var(--pinned-columns) * 0.03vw), 14px) !important;',
' height: auto !important;',
' min-height: 0 !important;',
' max-height: none !important;',
' border: none !important;',
' border-bottom: 1px solid #444 !important;',
' align-items: stretch !important;',
'}',
'.ui-bar-3-rows #ui-bar {',
' --rows: 1 !important;',
' padding: 2px 6px !important;',
' gap: 5px !important;',
'}',
'.menu-grid {',
' gap: 2px !important;',
' align-items: stretch !important;',
'}',
'.menu-card, .resource-card {',
' font-size: 0.9em !important;',
' padding: 3px 7px !important;',
' min-width: 3em !important;',
' line-height: 1.2 !important;',
' box-shadow: none !important;',
' border-radius: 3px !important;',
' border-width: 1px !important;',
' margin: 0 !important;',
' display: flex !important;',
' align-items: center !important;',
' justify-content: center !important;',
'}',
'#btn-hamburger {',
' padding: 3px 6px !important;',
' font-size: 1.2em !important;',
' line-height: 1 !important;',
' margin: 0 !important;',
' display: flex !important;',
' align-items: center !important;',
'}',
'.ui-bar-3-rows #btn-hamburger {',
' margin: 0 !important;',
'}',
'#btn-presets {',
' padding: 3px 8px !important;',
' font-size: 12px !important;',
' line-height: 1.15 !important;',
' margin: 0 !important;',
' display: flex !important;',
' align-items: center !important;',
'}',
'#btn-menu-buttons-toggle {',
' padding: 3px 8px !important;',
' font-size: 12px !important;',
' line-height: 1.15 !important;',
' margin: 0 !important;',
' display: flex !important;',
' align-items: center !important;',
'}',
'#btn-presets.is-open {',
' background: #2c3f67 !important;',
' border-color: #4f76c7 !important;',
' color: #dbe7ff !important;',
'}',
'#ui-bar.ia2g-hide-menu-buttons #pinned-menu-grid {',
' display: none !important;',
'}',
'.resource-card {',
' min-width: 60px !important;',
' gap: 4px !important;',
' font-size: 13px !important;',
' padding: 3px 6px !important;',
'}',
'.resource-card.res-paired {',
' flex-direction: column !important;',
' padding: 0 !important;',
' gap: 1px !important;',
' background: none !important;',
' border: none !important;',
' box-shadow: none !important;',
' min-width: 60px !important;',
'}',
'.mp-res-card {',
' display: flex !important;',
' align-items: center !important;',
' justify-content: center !important;',
' gap: 3px !important;',
' font-size: 9px !important;',
' line-height: 1.15 !important;',
' width: 100% !important;',
' background: rgba(30,30,30,0.9) !important;',
' border: 1px solid #555 !important;',
' border-radius: 3px !important;',
' padding: 1px 4px !important;',
' box-sizing: border-box !important;',
'}',
'.mp-res-card .res-icon {',
' font-size: 9px !important;',
'}',
'.res-icon {',
' font-size: 13px !important;',
'}',
'#event-bar {',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 5px !important;',
' padding: 2px 8px 11px !important;',
' min-width: auto !important;',
' height: auto !important;',
' min-height: 0 !important;',
' flex-shrink: 1 !important;',
' position: relative !important;',
' border-radius: 3px !important;',
' margin: 0 !important;',
'}',
'#event-bar > div:first-child {',
' flex-wrap: nowrap !important;',
' gap: 4px !important;',
' flex-shrink: 1 !important;',
' min-width: 0 !important;',
'}',
'#event-timer-bar {',
' position: absolute !important;',
' bottom: 0 !important;',
' left: 0 !important;',
' right: 0 !important;',
' height: 3px !important;',
' margin: 0 !important;',
'}',
'#event-boss-hp-bar {',
' position: absolute !important;',
' bottom: 3px !important;',
' left: 0 !important;',
' right: 0 !important;',
' height: 8px !important;',
' margin: 0 !important;',
'}',
'#event-next {',
' font-size: 10px !important;',
' margin: 0 0 0 4px !important;',
' white-space: nowrap !important;',
'}',
'#event-name {',
' max-width: 160px !important;',
' font-size: max(0.9em, 12px) !important;',
'}',
'#event-timer {',
' font-size: max(0.9em, 12px) !important;',
'}',
'#event-bar .event-btn {',
' padding: 2px 6px !important;',
' font-size: max(0.8em, 11px) !important;',
' line-height: 1.2 !important;',
'}',
'/* === Compact Resources Panel === */',
'.mp-panel-content #resources-menu {',
' padding: 8px !important;',
' border-width: 1px !important;',
'}',
'.mp-panel-content #resources-menu > div:first-child {',
' margin-bottom: 6px !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #resources-menu > div:first-child h2 {',
' display: none !important;',
'}',
'.mp-panel-content #resources-menu > div:first-child button {',
' padding: 1px 6px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #resources-menu > p {',
' display: none !important;',
'}',
'.mp-panel-content #resources-menu #resource-pin-list > [id^="res-cat-"] {',
' margin-top: 8px !important;',
' margin-bottom: 4px !important;',
' padding-bottom: 2px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #resources-menu #resource-pin-list > [id^="res-cat-"]:first-child {',
' margin-top: 0 !important;',
'}',
'.mp-panel-content #resources-menu #resource-pin-list > [id^="res-list-"] {',
' gap: 2px !important;',
'}',
'.mp-panel-content #resources-menu .res-row {',
' gap: 4px !important;',
' margin-bottom: 0 !important;',
' padding: 2px 5px !important;',
' border-radius: 3px !important;',
' min-height: 24px !important;',
'}',
'.mp-panel-content #resources-menu .res-name {',
' width: 122px !important;',
' gap: 3px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
' min-width: 0 !important;',
'}',
'.mp-panel-content #resources-menu .res-name > span:first-child {',
' font-size: 1em !important;',
' line-height: 1 !important;',
'}',
'.mp-panel-content #resources-menu .res-bar-container {',
' height: 16px !important;',
' border-radius: 8px !important;',
'}',
'.mp-panel-content #resources-menu .res-bar-text {',
' font-size: 10px !important;',
' line-height: 1 !important;',
' white-space: nowrap !important;',
' text-overflow: ellipsis !important;',
' overflow: hidden !important;',
' padding: 0 4px !important;',
'}',
'.mp-panel-content #resources-menu .res-pin-btn {',
' font-size: 12px !important;',
' line-height: 1 !important;',
' padding: 1px 4px !important;',
' min-width: 24px !important;',
' margin: 0 !important;',
'}',
'/* === Compact Skills Panel === */',
'.mp-panel-content #skills-menu {',
' padding: 8px !important;',
' border-width: 1px !important;',
'}',
'.mp-panel-content #skills-menu > h2 {',
' display: none !important;',
'}',
'.mp-panel-content #skills-menu #skills-list {',
' gap: 2px !important;',
'}',
'.mp-panel-content #skills-menu #skills-list > [id^="skill-cat-"] {',
' margin-top: 8px !important;',
' margin-bottom: 4px !important;',
' padding-bottom: 2px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #skills-menu #skills-list > [id^="skill-cat-"]:first-child {',
' margin-top: 0 !important;',
'}',
'.mp-panel-content #skills-menu #skills-list > [id^="skill-list-"] {',
' gap: 2px !important;',
'}',
'.mp-panel-content #skills-menu .skill-row {',
' gap: 4px !important;',
' margin-bottom: 0 !important;',
' padding: 2px 5px !important;',
' border-radius: 3px !important;',
' min-height: 24px !important;',
'}',
'.mp-panel-content #skills-menu .skill-name {',
' width: 98px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
' min-width: 0 !important;',
'}',
'.mp-panel-content #skills-menu .skill-lvl {',
' width: 44px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #skills-menu .skill-bar-container {',
' height: 16px !important;',
' border-radius: 8px !important;',
'}',
'.mp-panel-content #skills-menu .skill-bar-text {',
' font-size: 10px !important;',
' line-height: 1 !important;',
' white-space: nowrap !important;',
' text-overflow: ellipsis !important;',
' overflow: hidden !important;',
' padding: 0 4px !important;',
'}',
'@media (max-aspect-ratio: 1/1) {',
' .mp-panel-content #resources-menu {',
' padding: 6px !important;',
' }',
' .mp-panel-content #resources-menu .res-row {',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 4px !important;',
' padding: 2px 4px !important;',
' }',
' .mp-panel-content #resources-menu .res-name {',
' width: 108px !important;',
' margin-bottom: 0 !important;',
' font-size: 11px !important;',
' }',
' .mp-panel-content #resources-menu .res-bar-container {',
' width: auto !important;',
' height: 15px !important;',
' flex: 1 1 auto !important;',
' }',
' .mp-panel-content #resources-menu .res-pin-btn {',
' width: auto !important;',
' margin-top: 0 !important;',
' padding: 1px 4px !important;',
' }',
' .mp-panel-content #skills-menu {',
' padding: 6px !important;',
' }',
' .mp-panel-content #skills-menu .skill-row {',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 4px !important;',
' padding: 2px 4px !important;',
' }',
' .mp-panel-content #skills-menu .skill-name {',
' width: 88px !important;',
' margin-bottom: 0 !important;',
' font-size: 11px !important;',
' text-align: left !important;',
' }',
' .mp-panel-content #skills-menu .skill-lvl {',
' width: 40px !important;',
' margin-bottom: 0 !important;',
' font-size: 11px !important;',
' text-align: right !important;',
' }',
' .mp-panel-content #skills-menu .skill-bar-container {',
' width: auto !important;',
' height: 15px !important;',
' flex: 1 1 auto !important;',
' }',
'}',
'body.mp-square-tabs .menu-card,',
'body.mp-square-tabs .resource-card,',
'body.mp-square-tabs #event-bar,',
'body.mp-square-tabs .event-btn,',
'body.mp-square-tabs #btn-hamburger,',
'body.mp-square-tabs .ui-btn,',
'body.mp-square-tabs .mp-panel {',
' border-radius: 0 !important;',
'}',
'/* === Compact Actions Tab — Horizontal Rows === */',
'.mp-panel-content #actions-menu {',
' padding: 2px !important;',
' border: none !important;',
'}',
'.mp-panel-content #actions-menu .menu-row:not(#opt-fallback-row):not(#qol-fallback-container) {',
' display: flex !important;',
' flex-direction: column !important;',
' flex-wrap: nowrap !important;',
' margin: 0 !important;',
' gap: 2px !important;',
' padding: 0 !important;',
'}',
'.mp-panel-content #actions-menu .menu-row > span {',
' display: none !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content .assign-btn {',
' width: 100% !important;',
' height: auto !important;',
' min-height: 0 !important;',
' flex-direction: row !important;',
' align-items: center !important;',
' text-align: left !important;',
' padding: 2px 5px !important;',
' gap: 6px !important;',
' font-size: 11px !important;',
' line-height: 1.3 !important;',
' border-radius: 2px !important;',
' border-width: 1px !important;',
' box-sizing: border-box !important;',
'}',
'.mp-panel-content .assign-btn.active {',
' border-left-width: 3px !important;',
'}',
'.mp-panel-content .assign-btn > span:first-child {',
' flex-shrink: 0 !important;',
' white-space: nowrap !important;',
' min-width: 80px !important;',
'}',
'.mp-panel-content .assign-btn .skill-info {',
' flex: 1 !important;',
' display: flex !important;',
' flex-wrap: wrap !important;',
' align-items: baseline !important;',
' gap: 0 6px !important;',
' font-size: 10px !important;',
' line-height: 1.3 !important;',
' min-width: 0 !important;',
'}',
'.mp-panel-content .assign-btn .skill-info br {',
' display: none !important;',
'}',
'.mp-panel-content .assign-btn .skill-info span[style*="font-size: 0.8em"],',
'.mp-panel-content .assign-btn .skill-info span[style*="font-size:0.8em"],',
'.mp-panel-content .assign-btn .skill-info span[style*="font-size: 0.9em"],',
'.mp-panel-content .assign-btn .skill-info span[style*="font-size:0.9em"] {',
' font-size: inherit !important;',
'}',
'.mp-panel-content .assign-btn .item-count {',
' display: flex !important;',
' flex-wrap: nowrap !important;',
' align-items: baseline !important;',
' gap: 0 4px !important;',
' font-size: 10px !important;',
' margin: 0 !important;',
' flex-shrink: 0 !important;',
'}',
'.mp-panel-content .assign-btn .item-count br {',
' display: none !important;',
'}',
'.mp-panel-content .assign-btn .durability-bar-container {',
' width: 72px !important;',
' min-width: 56px !important;',
' max-width: 72px !important;',
' height: 8px !important;',
' margin: 0 0 0 auto !important;',
' flex: 0 1 72px !important;',
' align-self: center !important;',
'}',
'.mp-panel-content .assign-btn .durability-bar-text {',
' font-size: 7px !important;',
' line-height: 8px !important;',
' white-space: nowrap !important;',
' overflow: hidden !important;',
' text-overflow: ellipsis !important;',
'}',
'.mp-header-actions {',
' display: flex;',
' align-items: center;',
' gap: 8px;',
' flex-shrink: 1;',
' min-width: 0;',
'}',
'.mp-header-actions > #qol-fallback-container,',
'.mp-header-actions > #opt-fallback-row {',
' background: none !important;',
' border: none !important;',
' padding: 0 !important;',
' margin: 0 !important;',
' border-radius: 0 !important;',
' gap: 4px !important;',
' justify-content: flex-start !important;',
' align-items: center !important;',
' flex-shrink: 1 !important;',
' min-width: 0 !important;',
'}',
'.mp-header-actions > #qol-fallback-container > div,',
'.mp-header-actions > #opt-fallback-row > div {',
' line-height: 1 !important;',
'}',
'.mp-header-actions > #qol-fallback-container > div > span:nth-child(2),',
'.mp-header-actions > #opt-fallback-row > div > span:nth-child(2) {',
' display: none !important;',
'}',
'.mp-header-actions > #qol-fallback-container > div > span:first-child,',
'.mp-header-actions > #opt-fallback-row > div > span:first-child {',
' font-size: 10px !important;',
' font-weight: normal !important;',
' white-space: nowrap !important;',
'}',
'.mp-header-actions select {',
' min-width: 0 !important;',
' width: auto !important;',
' padding: 1px 4px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'/* === Compact Options Menu === */',
'#options-menu.mp-options-compact {',
' padding: 8px !important;',
' border-width: 1px !important;',
'}',
'#options-menu.mp-options-compact h2 {',
' margin: 0 0 6px 0 !important;',
' font-size: 15px !important;',
' line-height: 1.15 !important;',
'}',
'#options-menu.mp-options-compact #options-list {',
' gap: 6px !important;',
'}',
'#options-menu.mp-options-compact #options-list .menu-row:not(#opt-fallback-row):not(#qol-fallback-container) {',
' margin: 0 !important;',
' gap: 6px !important;',
' line-height: 1.2 !important;',
' padding: 0 !important;',
'}',
'#options-menu.mp-options-compact #options-list label,',
'#options-menu.mp-options-compact #options-list p,',
'#options-menu.mp-options-compact #options-list div,',
'#options-menu.mp-options-compact #options-list span {',
' line-height: 1.2 !important;',
'}',
'#options-menu.mp-options-compact #options-list select,',
'#options-menu.mp-options-compact #options-list button,',
'#options-menu.mp-options-compact #options-list input[type="text"],',
'#options-menu.mp-options-compact #options-list input[type="email"],',
'#options-menu.mp-options-compact #options-list input[type="password"],',
'#options-menu.mp-options-compact #options-list input[type="number"] {',
' font-size: 12px !important;',
' padding: 3px 6px !important;',
' line-height: 1.2 !important;',
'}',
'#options-menu.mp-options-compact #options-list input[type="checkbox"],',
'#options-menu.mp-options-compact #options-list input[type="radio"] {',
' width: 14px !important;',
' height: 14px !important;',
'}',
'#options-menu.mp-options-compact #opt-font-size-row,',
'#options-menu.mp-options-compact #opt-personal-description-row,',
'#options-menu.mp-options-compact #opt-mute-row,',
'#options-menu.mp-options-compact #opt-dungeon-prefs-row {',
' gap: 4px !important;',
'}',
'#options-menu.mp-options-compact #opt-personal-description-row > div,',
'#options-menu.mp-options-compact #opt-mute-row > div {',
' font-size: 11px !important;',
'}',
'#options-menu.mp-options-compact #opt-personal-description,',
'#options-menu.mp-options-compact #opt-mute-input {',
' min-height: 44px !important;',
' min-width: 0 !important;',
' width: 100% !important;',
' padding: 4px 6px !important;',
' font-size: 12px !important;',
'}',
'#options-menu.mp-options-compact #opt-dungeon-prefs-row > div:last-child {',
' gap: 10px !important;',
' flex-wrap: wrap !important;',
'}',
'#options-menu.mp-options-compact #mp-settings-section {',
' margin-top: 6px !important;',
' padding-top: 8px !important;',
' border-top-width: 1px !important;',
'}',
'#options-menu.mp-options-compact #mp-settings-section h3,',
'#options-menu.mp-options-compact #opt-auto-dungeon-header,',
'#options-menu.mp-options-compact #opt-dungeon-prefs-row h4 {',
' margin: 0 0 4px 0 !important;',
' font-size: 13px !important;',
' line-height: 1.2 !important;',
'}',
'#options-menu.mp-options-compact #mp-settings-section .menu-row {',
' margin: 0 !important;',
' gap: 6px !important;',
'}',
'#options-menu.mp-options-compact #opt-change-username-row,',
'#options-menu.mp-options-compact #opt-delete-account-row,',
'#options-menu.mp-options-compact #opt-register-row {',
' margin-top: 8px !important;',
' padding: 8px !important;',
' gap: 6px !important;',
' border-radius: 6px !important;',
'}',
'#options-menu.mp-options-compact #opt-change-username-row h3,',
'#options-menu.mp-options-compact #opt-delete-account-row h3,',
'#options-menu.mp-options-compact #opt-register-row h3 {',
' margin: 0 !important;',
' font-size: 13px !important;',
'}',
'#options-menu.mp-options-compact #mp-settings-section label {',
' min-width: 130px !important;',
'}',
'.mp-panel-content #mercenary-post-section {',
' padding: 0 !important;',
' margin: 0 !important;',
' background: transparent !important;',
' border: none !important;',
' border-radius: 0 !important;',
' display: flex !important;',
' flex-direction: column !important;',
' gap: 2px !important;',
'}',
'.mp-panel-content #mercenary-post-section h3 {',
' display: flex !important;',
' align-items: center !important;',
' font-size: 11px !important;',
' gap: 6px !important;',
' margin: 0 !important;',
' padding: 2px 5px !important;',
' background: #333 !important;',
' border: 1px solid #3366ff !important;',
' border-radius: 2px !important;',
' line-height: 1.3 !important;',
' font-weight: bold !important;',
'}',
'.mp-panel-content #mercenary-post-section h3 span {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mercenary-post-section > p {',
' display: none !important;',
'}',
'.mp-panel-content #merc-post-cut-display {',
' font-size: 10px !important;',
' margin: 0 !important;',
' padding: 1px 5px !important;',
' line-height: 1.3 !important;',
'}',
'.mp-panel-content #merc-post-controls {',
' display: flex !important;',
' flex-wrap: nowrap !important;',
' align-items: center !important;',
' gap: 6px !important;',
' margin: 0 !important;',
' padding: 2px 5px !important;',
' background: #2a2a2a !important;',
' border: 1px solid #444 !important;',
' border-radius: 2px !important;',
' line-height: 1.3 !important;',
'}',
'.mp-panel-content #merc-post-controls > div {',
' display: flex !important;',
' align-items: center !important;',
' gap: 4px !important;',
'}',
'.mp-panel-content #merc-post-controls label {',
' font-size: 10px !important;',
' margin: 0 !important;',
' white-space: nowrap !important;',
'}',
'.mp-panel-content #merc-post-controls select {',
' padding: 1px 3px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #merc-dungeon-progress {',
' font-size: 9px !important;',
' margin: 0 0 0 4px !important;',
' color: #666 !important;',
'}',
'.mp-panel-content #merc-boss-filters {',
' padding: 2px 5px !important;',
' margin: 0 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #merc-boss-filters > div:first-child {',
' font-size: 10px !important;',
' margin-bottom: 1px !important;',
'}',
'.mp-panel-content #merc-boss-filters > div:last-child {',
' gap: 2px 8px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #merc-boss-filters label {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mercenary-post-section > div[style*="margin-bottom: 15px"] {',
' margin: 0 !important;',
' padding: 0 5px !important;',
'}',
'.mp-panel-content #mercenary-post-section > div[style*="margin-bottom: 15px"] label {',
' font-size: 10px !important;',
' gap: 4px !important;',
'}',
'.mp-panel-content #mercenary-post-section > div[style*="justify-content: space-between"] {',
' display: flex !important;',
' align-items: center !important;',
' padding: 2px 5px !important;',
' background: #2a2a2a !important;',
' border: 1px solid #444 !important;',
' border-radius: 2px !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #mercenary-post-section > div[style*="justify-content: space-between"] > div {',
' gap: 4px !important;',
'}',
'.mp-panel-content #merc-hire-btn,',
'.mp-panel-content #merc-cancel-btn {',
' padding: 2px 8px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #merc-status {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #merc-dungeon-cooldown {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mercenary-post-section h3 {',
' position: relative !important;',
' flex-wrap: nowrap !important;',
' justify-content: flex-start !important;',
' padding-right: 20px !important;',
'}',
'#mp-merc-toggle {',
' background: none !important;',
' border: none !important;',
' color: #aaa !important;',
' cursor: pointer !important;',
' font-size: 10px !important;',
' padding: 0 4px !important;',
' line-height: 1 !important;',
' position: absolute !important;',
' top: 6px !important;',
' right: 6px !important;',
'}',
'#mp-merc-toggle:hover { color: #fff !important; }',
'.mp-panel-content #mercenary-post-section.mp-merc-collapsed > *:not(h3) {',
' display: none !important;',
'}',
'#mp-merc-compact-status {',
' display: none;',
' font-weight: bold;',
' font-size: 10px;',
' white-space: nowrap;',
' margin-left: auto;',
'}',
'#mp-merc-compact-hire {',
' display: none;',
' padding: 0px 6px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
' border: none !important;',
' color: white !important;',
' cursor: pointer !important;',
' font-weight: bold !important;',
' line-height: 1.4 !important;',
'}',
'.mp-merc-collapsed #mp-merc-compact-status,',
'.mp-merc-collapsed #mp-merc-compact-hire {',
' display: inline !important;',
'}',
'/* === Compact Crafting Tab === */',
'.mp-panel-content #crafting-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #crafting-menu > div:first-child {',
' gap: 6px !important;',
' margin-bottom: 4px !important;',
' flex-wrap: wrap !important;',
'}',
'.mp-panel-content #crafting-menu > div:first-child h2 {',
' display: none !important;',
'}',
'.mp-panel-content #crafting-menu > div:first-child > div {',
' gap: 3px !important;',
'}',
'.mp-panel-content #crafting-menu > div:first-child button {',
' padding: 2px 6px !important;',
' font-size: 11px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #crafting-item-select {',
' padding: 2px 6px !important;',
' font-size: 11px !important;',
' min-width: 120px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #crafting-interface {',
' gap: 4px !important;',
'}',
'.mp-panel-content #crafting-interface > div {',
' min-width: 0 !important;',
'}',
'.mp-panel-content #crafting-interface h3 {',
' font-size: 11px !important;',
' font-weight: bold !important;',
' margin: 0 0 1px 0 !important;',
'}',
'.mp-panel-content #crafting-interface > div > div[style*="italic"] {',
' font-size: 9px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #crafting-interface .menu-row {',
' display: flex !important;',
' flex-direction: row !important;',
' flex-wrap: nowrap !important;',
' align-items: center !important;',
' margin: 0 !important;',
' padding: 1px 0 !important;',
' gap: 3px !important;',
'}',
'.mp-panel-content #crafting-interface .menu-row[style*="column"] > div {',
' display: contents !important;',
'}',
'.mp-panel-content #crafting-qty-input {',
' order: 2 !important;',
'}',
'.mp-panel-content #crafting-interface .menu-row[style*="column"] > div:last-child > button:nth-child(3) {',
' order: 3 !important;',
'}',
'.mp-panel-content #crafting-max-btn {',
' order: 4 !important;',
'}',
'.mp-panel-content #crafting-interface .menu-row span {',
' font-size: 10px !important;',
' white-space: nowrap !important;',
'}',
'.mp-panel-content #crafting-interface .menu-row button {',
' padding: 1px 5px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #crafting-level-select {',
' padding: 1px 3px !important;',
' font-size: 10px !important;',
' width: 40px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #crafting-qty-input {',
' width: 70px !important;',
' padding: 1px 3px !important;',
' font-size: 10px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #crafting-cost-display {',
' padding: 3px 5px !important;',
' margin-top: 3px !important;',
' font-size: 10px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #crafting-cost-display strong {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #crafting-prob-display {',
' margin-top: 3px !important;',
' font-size: 10px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #crafting-prob-display strong {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #crafting-craft-btn {',
' padding: 1px 10px !important;',
' font-size: 9px !important;',
' margin-top: 2px !important;',
' margin-bottom: 0 !important;',
' border-radius: 2px !important;',
' line-height: 1.2 !important;',
' width: auto !important;',
' display: block !important;',
' margin-left: auto !important;',
' margin-right: auto !important;',
' min-height: 0 !important;',
'}',
'.mp-panel-content #crafting-craft-btn span {',
' font-size: 8px !important;',
' line-height: 1.1 !important;',
'}',
'/* === Compact Workshop Tab === */',
'.mp-panel-content #personal-buildings-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #personal-buildings-menu > div:first-child {',
' gap: 6px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #personal-buildings-menu > div:first-child h2 {',
' display: none !important;',
'}',
'.mp-panel-content #personal-building-select {',
' padding: 2px 6px !important;',
' font-size: 11px !important;',
' min-width: 120px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #personal-buildings-list {',
' gap: 4px !important;',
'}',
'.mp-panel-content #personal-buildings-list > div[id^="personal-buildings-card-"] {',
' padding: 4px 6px !important;',
' gap: 4px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #personal-buildings-list > div[id^="personal-buildings-card-"] > div:first-child {',
' gap: 4px !important;',
'}',
'.mp-panel-content div[id^="personal-buildings-header-"] {',
' font-size: 11px !important;',
'}',
'.mp-panel-content button[id^="btn-enhance-"] {',
' font-size: 9px !important;',
' padding: 1px 5px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="italic"] {',
' font-size: 9px !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content div[id^="personal-buildings-effect-"] {',
' padding: 3px 5px !important;',
' font-size: 10px !important;',
' line-height: 1.4 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content div[id^="personal-buildings-effect-"] strong {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="border-top"] {',
' gap: 4px !important;',
' margin-top: 2px !important;',
' padding-top: 2px !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="border-top"] div[style*="column"] {',
' gap: 2px !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="border-top"] div[style*="stretch"] {',
' gap: 3px !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="border-top"] span {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="border-top"] button {',
' padding: 1px 5px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #personal-buildings-list div[style*="border-top"] select {',
' padding: 1px 3px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content button[id^="btn-upgrade-"] {',
' padding: 1px 6px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content div[id^="personal-buildings-cost-"] {',
' font-size: 10px !important;',
' margin-top: 2px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content div[id^="personal-buildings-cost-"] strong {',
' font-size: 10px !important;',
'}',
'/* === Compact Community Tab === */',
'.mp-panel-content #community-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #community-menu > div:first-child {',
' gap: 6px !important;',
' margin-bottom: 4px !important;',
' padding-right: 0 !important;',
' flex-wrap: wrap !important;',
'}',
'.mp-panel-content #community-menu > div:first-child h2 {',
' margin: 0 !important;',
' font-size: 13px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #community-building-select {',
' padding: 2px 6px !important;',
' font-size: 11px !important;',
' min-width: 130px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #community-buildings-list {',
' gap: 4px !important;',
'}',
'.mp-panel-content #community-buildings-list > div[id^="community-building-card-"] {',
' display: flex !important;',
' flex-direction: column !important;',
' align-items: stretch !important;',
' justify-content: flex-start !important;',
' min-height: 0 !important;',
' height: auto !important;',
' padding: 4px 6px !important;',
' gap: 2px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #community-buildings-list > div[id^="community-building-card-"] > div:first-child {',
' gap: 3px !important;',
'}',
'.mp-panel-content div[id^="cb-header-"] {',
' font-size: 10px !important;',
' line-height: 1.15 !important;',
'}',
'.mp-panel-content button[id^="cb-vote-btn-"] {',
' padding: 0 5px !important;',
' font-size: 8px !important;',
' border-radius: 2px !important;',
' line-height: 1.25 !important;',
' min-height: 16px !important;',
'}',
'.mp-panel-content div[id^="cb-flavor-"] {',
' display: none !important;',
'}',
'.mp-panel-content div[id^="cb-effect-"] {',
' margin: 0 !important;',
' padding: 2px 4px !important;',
' font-size: 8px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content div[id^="cb-effect-"] strong {',
' font-size: 8px !important;',
'}',
'.mp-panel-content #community-buildings-list > div[id^="community-building-card-"] > div[style*="border-top"] {',
' margin-top: 1px !important;',
' padding-top: 2px !important;',
' gap: 3px !important;',
' grid-template-columns: repeat(auto-fill, minmax(146px, 1fr)) !important;',
'}',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] {',
' padding: 1px 3px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] > div:first-child {',
' margin-bottom: 1px !important;',
'}',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] > div:first-child span:first-child {',
' font-size: 9px !important;',
' line-height: 1.1 !important;',
'}',
'.mp-panel-content #community-buildings-list span[id^="cb-percent-"] {',
' font-size: 8px !important;',
'}',
'.mp-panel-content #community-buildings-list span[id^="cb-needed-"],',
'.mp-panel-content #community-buildings-list span[id^="cb-have-"] {',
' font-size: 8px !important;',
' line-height: 1.1 !important;',
'}',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] > div[style*="font-size: 0.8em"] {',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] > div:last-child {',
' gap: 2px !important;',
'}',
'.mp-panel-content input[id^="cb-contrib-input-"] {',
' width: 54px !important;',
' padding: 0 2px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
' min-height: 16px !important;',
'}',
'.mp-panel-content button[id^="cb-contrib-btn-"],',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] > div:last-child button {',
' padding: 0 4px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
' min-height: 16px !important;',
'}',
'.mp-panel-content #community-buildings-list div[id^="cb-resource-row-"] > div:last-child button:not([id^="cb-contrib-btn-"]) {',
' min-width: 30px !important;',
'}',
'.mp-panel-content button[id^="cb-contrib-btn-"] {',
' min-width: 56px !important;',
'}',
'/* === Compact Conclave Tab === */',
'.mp-panel-content #conclave-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #conclave-container {',
' display: flex !important;',
' flex-direction: column !important;',
' gap: 2px !important;',
'}',
'.mp-panel-content #conclave-container > div:first-child {',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #conclave-main-header {',
' font-size: 12px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-container > div:first-child button {',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
' min-height: 18px !important;',
' padding: 1px 6px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content .conclave-tab-btn {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-container > div:nth-child(2) {',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content .conclave-content-pane h3,',
'.mp-panel-content .conclave-content-pane h4 {',
' margin: 0 0 4px 0 !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-content-overview > div[style*="margin-bottom: 15px"] {',
' padding: 6px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #conclave-content-overview p,',
'.mp-panel-content #conclave-content-overview span {',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-content-overview table {',
' margin-top: 4px !important;',
'}',
'.mp-panel-content #conclave-content-overview th,',
'.mp-panel-content #conclave-content-overview td {',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-transactions-container {',
' margin-top: 8px !important;',
'}',
'.mp-panel-content #conclave-transactions-container h4 {',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #conclave-content-buildings > div:first-child {',
' gap: 6px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #conclave-content-buildings > div:first-child h3 {',
' font-size: 11px !important;',
'}',
'.mp-panel-content #conclave-building-select {',
' min-width: 130px !important;',
' padding: 1px 5px !important;',
' border-radius: 2px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-buildings-list {',
' gap: 4px !important;',
'}',
'.mp-panel-content #conclave-buildings-list > div[id^="conclave-building-card-"] {',
' display: flex !important;',
' flex-direction: column !important;',
' align-items: stretch !important;',
' justify-content: flex-start !important;',
' min-height: 0 !important;',
' height: auto !important;',
' padding: 4px 6px !important;',
' gap: 2px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #conclave-buildings-list > div[id^="conclave-building-card-"] > div:first-child {',
' display: flex !important;',
' align-items: center !important;',
' justify-content: space-between !important;',
' flex-wrap: nowrap !important;',
' gap: 3px !important;',
'}',
'.mp-panel-content div[id^="cn-header-"] {',
' font-size: 10px !important;',
' line-height: 1.15 !important;',
' flex: 1 1 auto !important;',
' min-width: 0 !important;',
'}',
'.mp-panel-content button[id^="cn-vote-btn-"] {',
' padding: 0 5px !important;',
' min-height: 16px !important;',
' font-size: 8px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content div[id^="cn-flavor-"] {',
' display: none !important;',
'}',
'.mp-panel-content div[id^="cn-effect-"] {',
' margin: 0 !important;',
' padding: 2px 4px !important;',
' font-size: 8px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content div[id^="cn-effect-"] strong {',
' font-size: 8px !important;',
'}',
'.mp-panel-content #conclave-buildings-list > div[id^="conclave-building-card-"] > div[style*="border-top"] {',
' margin-top: 1px !important;',
' padding-top: 2px !important;',
' gap: 3px !important;',
' grid-template-columns: repeat(auto-fill, minmax(146px, 1fr)) !important;',
'}',
'.mp-panel-content div[id^="cn-resource-row-"] {',
' padding: 1px 3px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content div[id^="cn-resource-row-"] > div:first-child {',
' margin-bottom: 1px !important;',
'}',
'.mp-panel-content div[id^="cn-resource-row-"] > div:first-child span:first-child {',
' font-size: 9px !important;',
' line-height: 1.1 !important;',
'}',
'.mp-panel-content span[id^="cn-percent-"] {',
' font-size: 8px !important;',
'}',
'.mp-panel-content span[id^="cn-needed-"],',
'.mp-panel-content span[id^="cn-have-"] {',
' font-size: 8px !important;',
' line-height: 1.1 !important;',
'}',
'.mp-panel-content div[id^="cn-resource-row-"] > div[style*="font-size: 0.8em"] {',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content div[id^="cn-resource-row-"] > div:last-child {',
' gap: 2px !important;',
'}',
'.mp-panel-content input[id^="cn-contrib-input-"] {',
' width: 54px !important;',
' min-height: 16px !important;',
' padding: 0 2px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content button[id^="cn-contrib-btn-"],',
'.mp-panel-content div[id^="cn-resource-row-"] > div:last-child button {',
' min-height: 16px !important;',
' padding: 0 4px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content button[id^="cn-contrib-btn-"] {',
' min-width: 56px !important;',
'}',
'.mp-panel-content div[id^="cn-resource-row-"] > div:last-child button:not([id^="cn-contrib-btn-"]) {',
' min-width: 30px !important;',
'}',
'.mp-panel-content #conclave-management-content {',
' display: flex !important;',
' flex-direction: column !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #conclave-management-content > div {',
' margin: 0 !important;',
' padding: 4px !important;',
' border: 1px solid #333 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #conclave-management-content label,',
'.mp-panel-content #conclave-management-content p,',
'.mp-panel-content #conclave-management-content span,',
'.mp-panel-content #conclave-management-content div {',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #conclave-management-content textarea,',
'.mp-panel-content #conclave-management-content select,',
'.mp-panel-content #conclave-management-content input[type="text"],',
'.mp-panel-content #conclave-management-content input[type="range"] {',
' font-size: 10px !important;',
' padding: 2px 4px !important;',
'}',
'.mp-panel-content #conclave-management-content textarea {',
' height: 44px !important;',
' min-height: 44px !important;',
'}',
'.mp-panel-content #conclave-management-content button {',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
' min-height: 16px !important;',
' padding: 1px 6px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #conclave-management-content .mp-conclave-collapse-toggle {',
' margin-left: auto !important;',
' min-width: 16px !important;',
' padding: 0 4px !important;',
'}',
'.mp-panel-content #conclave-management-content .mp-conclave-section-collapsed > *:not(h3) {',
' display: none !important;',
'}',
'.mp-panel-content #conclave-management-content > div > h3 {',
' display: flex !important;',
' align-items: center !important;',
' gap: 4px !important;',
' margin: 0 !important;',
' font-size: 11px !important;',
'}',
'.mp-panel-content #conclave-management-content > div > h3[style*="margin-top"] {',
' margin-top: 0 !important;',
'}',
'/* === Compact Anvil Tab === */',
'.mp-panel-content #anvil-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #anvil-menu select,',
'.mp-panel-content #anvil-menu input[type="number"] {',
' padding: 1px 3px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #anvil-menu input[type="number"] {',
' width: 52px !important;',
'}',
'.mp-panel-content #anvil-menu > div:first-child {',
' gap: 6px !important;',
' margin-bottom: 4px !important;',
' flex-wrap: wrap !important;',
'}',
'.mp-panel-content #anvil-menu > div:first-child h2 {',
' font-size: 13px !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content #anvil-menu > div:first-child > div {',
' gap: 4px !important;',
'}',
'.mp-panel-content #anvil-menu > div:first-child button {',
' padding: 2px 6px !important;',
' font-size: 11px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #anvil-menu > div:nth-child(2) {',
' gap: 6px !important;',
' grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) !important;',
'}',
'.mp-panel-content #anvil-menu > div:nth-child(2) > div {',
' min-width: 0 !important;',
' padding: 8px 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #anvil-menu > div:nth-child(2) h3 {',
' margin: 0 0 3px 0 !important;',
' padding-bottom: 3px !important;',
' font-size: 11px !important;',
'}',
'.mp-panel-content #anvil-menu > div:nth-child(2) > div > div[style*="display: grid"] {',
' gap: 4px !important;',
'}',
'.mp-panel-content #anvil-menu .stat-row {',
' margin: 0 !important;',
' display: flex !important;',
' align-items: center !important;',
' justify-content: space-between !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #anvil-menu .stat-row > span {',
' font-size: 10px !important;',
' white-space: nowrap !important;',
'}',
'.mp-panel-content #anvil-menu > div:nth-child(2) label {',
' font-size: 10px !important;',
' gap: 4px !important;',
'}',
'.mp-panel-content #anvil-menu > div:nth-child(2) > div > div[style*="display: flex; gap: 10px"] {',
' gap: 6px !important;',
' margin-top: 2px !important;',
'}',
'.mp-panel-content #anvil-gathering-output,',
'.mp-panel-content #anvil-refining-output {',
' margin-top: 8px !important;',
' padding-top: 6px !important;',
'}',
'.mp-panel-content #anvil-preview-name {',
' font-size: 11px !important;',
' margin-bottom: 3px !important;',
'}',
'.mp-panel-content #anvil-output-secondary {',
' font-size: 9px !important;',
' margin-top: 3px !important;',
' min-height: 14px !important;',
'}',
'.mp-panel-content #anvil-gathering-output > div[style*="justify-content: space-between"],',
'.mp-panel-content #anvil-refining-output > div > div[style*="justify-content: space-between"] {',
' margin-bottom: 2px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #anvil-gathering-output > div[style*="margin-top: 10px"],',
'.mp-panel-content #anvil-refining-output > div[style*="margin-top: 10px"] {',
' margin-top: 6px !important;',
'}',
'.mp-panel-content #anvil-gathering-output > div[style*="margin-top: 10px"] > div:first-child,',
'.mp-panel-content #anvil-refining-output > div[style*="margin-top: 10px"] > div:first-child {',
' font-size: 9px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #anvil-output-cost,',
'.mp-panel-content #anvil-enhance-cost,',
'.mp-panel-content #anvil-scrap-value,',
'.mp-panel-content #anvil-ref-enhance-cost {',
' gap: 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #anvil-refining-output > div[style*="flex-direction: column"] {',
' gap: 6px !important;',
'}',
'.mp-panel-content #anvil-refining-output > div[style*="flex-direction: column"] > div[style*="justify-content: space-between"] {',
' padding: 3px 5px !important;',
'}',
'.mp-panel-content #anvil-ref-input,',
'.mp-panel-content #anvil-ref-output,',
'.mp-panel-content #anvil-output-power,',
'.mp-panel-content #anvil-output-yield {',
' font-size: 10px !important;',
'}',
'/* === Compact Repair Tab === */',
'.mp-panel-content #repair-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #repair-menu h2 {',
' margin: 0 0 6px 0 !important;',
' font-size: 13px !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #repair-menu h3 {',
' margin: 0 0 4px 0 !important;',
' font-size: 11px !important;',
'}',
'.mp-panel-content #repair-menu p {',
' margin: 0 !important;',
' font-size: 10px !important;',
' line-height: 1.25 !important;',
'}',
'.mp-panel-content #repair-tabs-header {',
' gap: 4px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #repair-tabs-header .repair-tab-btn {',
' font-size: 10px !important;',
' padding: 2px 6px !important;',
' border-radius: 2px !important;',
' line-height: 1.3 !important;',
'}',
'.mp-panel-content #repair-view-workshop > div:first-child,',
'.mp-panel-content #repair-view-market > div:first-child {',
' margin-bottom: 6px !important;',
' padding: 6px !important;',
' border-radius: 2px !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #workshop-bench-stats {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #workshop-damaged-list {',
' grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)) !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #workshop-repair-all-container button {',
' max-width: 320px !important;',
' margin-bottom: 6px !important;',
' font-size: 10px !important;',
' padding: 3px 6px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] > div {',
' padding: 6px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] strong {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] span {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] > div > div[style*="justify-content: space-between"] {',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] > div > div[style*="position: relative; background: #444"] {',
' height: 8px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] > div > div[style*="position: relative; height: 15px"] {',
' height: 11px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] > div > div[style*="position: relative; height: 15px"] > div {',
' font-size: 8px !important;',
'}',
'.mp-panel-content #repair-menu [id^="repair-tool-card-"] button[id^="btn-repair-"] {',
' font-size: 10px !important;',
' padding: 2px 4px !important;',
' border-radius: 2px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #repair-view-market > div[style*="margin-bottom: 15px"] {',
' margin-bottom: 6px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #repair-view-market label {',
' margin-bottom: 2px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #repair-price-public,',
'.mp-panel-content #repair-price-conclave {',
' width: 46px !important;',
' padding: 1px 2px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #btn-repair-public,',
'.mp-panel-content #btn-cancel-public,',
'.mp-panel-content #btn-repair-conclave,',
'.mp-panel-content #btn-cancel-conclave {',
' min-width: 0 !important;',
' padding: 1px 6px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #repair-market-tool-select {',
' width: 220px !important;',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #repair-market-container table {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #repair-market-container th {',
' padding: 3px 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #repair-market-container td {',
' padding: 2px 4px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #repair-market-container td button {',
' padding: 1px 4px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #repair-view-active > h3 {',
' margin: 0 0 4px 0 !important;',
'}',
'.mp-panel-content #repair-view-active h3[style*="margin-top: 30px"] {',
' margin-top: 8px !important;',
'}',
'.mp-panel-content #active-repair-contracts {',
' gap: 6px !important;',
'}',
'.mp-panel-content #active-repair-contracts > div {',
' padding: 6px !important;',
' border-radius: 2px !important;',
' gap: 6px !important;',
' align-items: flex-start !important;',
'}',
'.mp-panel-content #active-repair-contracts > div > div {',
' font-size: 10px !important;',
' line-height: 1.25 !important;',
'}',
'.mp-panel-content #active-repair-contracts > div strong {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #active-repair-contracts > div span[style*="font-size: 14px"] {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #active-repair-contracts > div > div > div[style*="margin-top: 8px"] {',
' margin-top: 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #active-repair-contracts > div button {',
' padding: 1px 6px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #repair-log-container {',
' height: 130px !important;',
' padding: 6px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #repair-log-container .log-entry {',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #repair-log-container .log-timestamp {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #repair-view-active > div[style*="display: flex; gap: 5px; margin-top: 10px"] {',
' gap: 4px !important;',
' margin-top: 6px !important;',
'}',
'.mp-panel-content #repair-view-active > div[style*="display: flex; gap: 5px; margin-top: 10px"] button {',
' padding: 1px 6px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
'}',
'/* === Compact Inventory Tab — Row-Based Layout === */',
'.mp-panel-content #inventory-menu {',
' padding: 2px !important;',
' border: none !important;',
'}',
'.mp-panel-content #inventory-menu > div:first-child {',
' gap: 2px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #inventory-menu > div:first-child h2 {',
' font-size: 11px !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content #inventory-filters {',
' gap: 2px !important;',
'}',
'.mp-panel-content #inventory-filters > div {',
' gap: 2px !important;',
' margin-top: 0 !important;',
' padding-top: 1px !important;',
'}',
'.mp-panel-content #inventory-filters button {',
' padding: 0px 4px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
' line-height: 1.5 !important;',
'}',
'.mp-panel-content #inventory-filters > div > div[style*="height: 20px"] {',
' height: 12px !important;',
' margin: 0 1px !important;',
'}',
'.mp-panel-content #inventory-filters > div > span {',
' font-size: 9px !important;',
' margin-right: 1px !important;',
'}',
'.mp-panel-content #inventory-grid {',
' gap: 2px !important;',
'}',
'.mp-panel-content #inventory-grid > h3 {',
' font-size: 10px !important;',
' margin: 0 !important;',
' padding-bottom: 1px !important;',
'}',
'.mp-panel-content #inventory-grid > div[style*="margin-top"] {',
' margin-top: 4px !important;',
' padding-bottom: 1px !important;',
'}',
'.mp-panel-content #inventory-grid > div[style*="margin-top"] h3 {',
' font-size: 10px !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content #inventory-grid > div[style*="margin-top"] > button {',
' padding: 1px 4px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #inventory-grid > h3[style*="margin-top"] {',
' margin-top: 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content .inventory-worker-group {',
' margin-bottom: 1px !important;',
'}',
'.mp-panel-content .inventory-grid {',
' display: flex !important;',
' flex-direction: column !important;',
' gap: 1px !important;',
'}',
'.mp-panel-content .inventory-worker-group .inventory-grid {',
' display: grid !important;',
' grid-template-columns: repeat(3, 1fr) !important;',
' gap: 2px !important;',
'}',
'.mp-panel-content .inventory-worker-group .inventory-slot {',
' flex-direction: column !important;',
' align-items: stretch !important;',
' gap: 1px !important;',
' padding: 2px 4px !important;',
'}',
'.mp-panel-content .inventory-worker-group .inventory-slot .item-name {',
' white-space: normal !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content .inventory-worker-group .inventory-actions {',
' margin-left: 0 !important;',
' flex-direction: row !important;',
'}',
'.mp-panel-content .inventory-worker-group .inventory-slot.empty {',
' flex-direction: column !important;',
' align-items: center !important;',
' justify-content: center !important;',
' padding: 2px 4px !important;',
'}',
'.mp-panel-content .inventory-slot {',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 6px !important;',
' padding: 2px 5px !important;',
' min-height: 0 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content .inventory-slot.empty {',
' flex-direction: row !important;',
' gap: 4px !important;',
' padding: 2px 5px !important;',
' min-height: 0 !important;',
'}',
'.mp-panel-content .inventory-slot.empty > div {',
' font-size: 9px !important;',
' margin-bottom: 0 !important;',
'}',
'.mp-panel-content .inventory-slot.empty > div[style*="font-size: 0.8em"] {',
' font-size: 8px !important;',
'}',
'.mp-panel-content .inventory-slot .item-name {',
' font-size: 9px !important;',
' line-height: 1.3 !important;',
' white-space: nowrap !important;',
' flex-shrink: 0 !important;',
' padding-right: 0 !important;',
' min-width: 0 !important;',
'}',
'.mp-panel-content .inventory-slot .item-name > div[style*="position:absolute"] {',
' position: static !important;',
' display: inline !important;',
' background: none !important;',
' border: none !important;',
' padding: 0 !important;',
' margin-left: 2px !important;',
' font-size: 9px !important;',
' color: #aaa !important;',
'}',
'.mp-panel-content .inventory-qty-row {',
' gap: 2px !important;',
' margin: 0 !important;',
' flex-shrink: 0 !important;',
'}',
'.mp-panel-content .inventory-qty-row button {',
' padding: 0px 3px !important;',
' font-size: 8px !important;',
' border-radius: 2px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content .inventory-qty-row input {',
' width: 22px !important;',
' padding: 0px 1px !important;',
' font-size: 9px !important;',
' line-height: 1.3 !important;',
'}',
'.mp-panel-content .inventory-actions {',
' flex-direction: row !important;',
' flex-wrap: nowrap !important;',
' gap: 2px !important;',
' margin: 0 !important;',
' margin-left: auto !important;',
' flex-shrink: 0 !important;',
' align-items: center !important;',
'}',
'.mp-panel-content .inventory-actions > div[style*="border-top"] {',
' display: none !important;',
'}',
'.mp-panel-content .inventory-actions button {',
' padding: 1px 4px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
' line-height: 1.3 !important;',
' margin: 0 !important;',
' white-space: nowrap !important;',
' flex-shrink: 0 !important;',
'}',
'.mp-panel-content .inventory-actions button[style*="padding: 8px"] {',
' padding: 1px 4px !important;',
' white-space: nowrap !important;',
' display: inline !important;',
'}',
'.mp-panel-content .inventory-actions button span {',
' display: none !important;',
'}',
'.mp-panel-content .enhanced-item::before {',
' display: none !important;',
'}',
'.mp-panel-content .enhancement-badge {',
' font-size: 7px !important;',
' padding: 0px 2px !important;',
' top: -3px !important;',
' right: -3px !important;',
'}',
'/* === Compact Market Tab === */',
'.mp-panel-content #market-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #market-menu > h2 {',
' font-size: 13px !important;',
' margin: 0 0 2px 0 !important;',
'}',
'.mp-panel-content #market-menu > div:first-of-type {',
' gap: 4px !important;',
' margin-bottom: 4px !important;',
' padding-bottom: 4px !important;',
' flex-wrap: wrap !important;',
'}',
'.mp-panel-content #market-menu > div:first-of-type button {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #market-filter-controls {',
' gap: 4px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #market-filter-controls span {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #market-filter-controls button {',
' padding: 2px 5px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #market-filter-controls select {',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
' min-width: 100px !important;',
'}',
'.mp-panel-content #market-filter-controls input {',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
' min-width: 80px !important;',
'}',
'.mp-panel-content #market-table-wrapper table {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #market-table-wrapper th {',
' padding: 3px 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #market-table-wrapper td {',
' padding: 2px 4px !important;',
'}',
'.mp-panel-content #market-table-wrapper td:nth-child(5) {',
' max-width: 90px !important;',
' overflow: hidden !important;',
' text-overflow: ellipsis !important;',
' white-space: nowrap !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #market-table-wrapper td:last-child > div {',
' gap: 4px !important;',
'}',
'.mp-panel-content #market-table-wrapper td:last-child > div > div {',
' gap: 2px !important;',
'}',
'.mp-panel-content #market-table-wrapper td:last-child button {',
' padding: 1px 4px !important;',
' font-size: 9px !important;',
' min-width: 18px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #market-table-wrapper td:last-child input {',
' width: 40px !important;',
' padding: 1px 2px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #market-table-wrapper td:last-child button[style*="background:#2e7d32"] {',
' padding: 1px 6px !important;',
' font-size: 9px !important;',
'}',
'/* === Compact Mailbox Tab === */',
'.mp-panel-content #mailbox-menu {',
' padding: 4px !important;',
' border-width: 1px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #mailbox-menu > div:first-child {',
' gap: 4px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #mailbox-menu > div:first-child > div:first-child {',
' gap: 6px !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content #mailbox-menu h2 {',
' font-size: 13px !important;',
' line-height: 1.1 !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content #mailbox-menu #btn-mailbox-take-all,',
'.mp-panel-content #mailbox-menu #btn-mailbox-trash-all {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' line-height: 1.3 !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #mailbox-menu > div:first-child > div:last-child {',
' border-bottom-width: 1px !important;',
' gap: 2px !important;',
'}',
'.mp-panel-content #mailbox-menu > div:first-child > div:last-child button {',
' padding: 3px 6px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-content-inbox,',
'.mp-panel-content #mailbox-content-send,',
'.mp-panel-content #mailbox-content-info {',
' font-size: 11px !important;',
'}',
'.mp-panel-content #mailbox-list {',
' gap: 4px !important;',
' max-height: calc(100vh - 260px) !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot {',
' padding: 4px 6px !important;',
' padding-right: 96px !important;',
' border-left-width: 2px !important;',
' border-radius: 2px !important;',
' min-height: 0 !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > div:first-child {',
' margin-bottom: 2px !important;',
' padding-right: 92px !important;',
' font-size: 10px !important;',
' gap: 4px !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > div:first-child > span:first-child {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > div:first-child > span:last-child {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > button {',
' top: 4px !important;',
' right: 4px !important;',
' padding: 1px 5px !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > button[onclick^="replyToMessage"] {',
' right: 38px !important;',
' font-size: 9px !important;',
' padding: 1px 5px !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > div[style*="line-height: 1.4"] {',
' font-size: 10px !important;',
' line-height: 1.25 !important;',
' padding-right: 4px !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > div[style*="margin-top: 10px; padding: 10px; background: #222"] {',
' margin-top: 4px !important;',
' padding: 3px 5px !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #mailbox-list > .inventory-slot > div[style*="margin-top: 10px; padding: 10px; background: #222"] button,',
'.mp-panel-content #mailbox-list > .inventory-slot > button[onclick^="takeAttachment("] {',
' padding: 1px 5px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-list > div[style*="text-align:center"] {',
' padding: 10px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mailbox-content-send > div {',
' gap: 8px !important;',
'}',
'.mp-panel-content #mailbox-content-send label {',
' margin-bottom: 3px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-content-send select,',
'.mp-panel-content #mailbox-content-send input,',
'.mp-panel-content #mailbox-content-send textarea {',
' padding: 3px 6px !important;',
' font-size: 10px !important;',
' line-height: 1.25 !important;',
'}',
'.mp-panel-content #mailbox-content-send #dm-attachment-type {',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #mailbox-content-send #dm-qty-controls {',
' gap: 3px !important;',
'}',
'.mp-panel-content #mailbox-content-send #dm-qty-controls button {',
' padding: 2px 5px !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-content-send #dm-attachment-qty {',
' width: 74px !important;',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mailbox-content-send #dm-attachment-info,',
'.mp-panel-content #mailbox-content-send #dm-char-count,',
'.mp-panel-content #mailbox-content-send #dm-status {',
' font-size: 9px !important;',
' margin-top: 2px !important;',
' min-height: 12px !important;',
'}',
'.mp-panel-content #mailbox-content-send #dm-message {',
' min-height: 82px !important;',
'}',
'.mp-panel-content #mailbox-content-send > div > button[onclick="sendDirectMessage()"] {',
' padding: 3px 6px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-content-info > div {',
' line-height: 1.35 !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mailbox-content-info h3 {',
' margin: 0 0 4px 0 !important;',
' font-size: 11px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mailbox-content-info ul {',
' margin: 4px 0 !important;',
' padding-left: 16px !important;',
'}',
'.mp-panel-content #mailbox-content-info li {',
' margin: 1px 0 !important;',
' line-height: 1.25 !important;',
'}',
'.mp-panel-content #mailbox-content-info div[style*="Auto-Delete Mail Types"] {',
' padding: 4px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #mailbox-content-info div[style*="Auto-Delete Mail Types"] > div {',
' margin-bottom: 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mailbox-content-info label {',
' gap: 5px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mailbox-content-info input[type="checkbox"] {',
' width: 12px !important;',
' height: 12px !important;',
'}',
'.mp-panel-content #mailbox-content-info p {',
' margin-top: 6px !important;',
' font-size: 9px !important;',
'}',
'/* === Compact Farming Tab === */',
'.mp-panel-content #farming-menu {',
' padding: 4px !important;',
' border: none !important;',
'}',
'.mp-panel-content #farming-menu > h2 {',
' display: none !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="justify-content: space-between"][style*="align-items: center"],',
'#farming-menu > div[style*="justify-content: space-between"][style*="align-items: center"] {',
' display: none !important;',
'}',
'.mp-panel-content #farming-menu > p,',
'#farming-menu > p,',
'.mp-panel-content #farming-menu > #farming-description,',
'#farming-menu > #farming-description {',
' display: none !important;',
'}',
'.mp-panel-content #farming-menu button[onclick*="Info"],',
'.mp-panel-content #farming-menu button[onclick*="info"],',
'.mp-panel-content #farming-menu button[id*="farm"][id*="info"],',
'.mp-panel-content #farming-menu button[id*="farming"][id*="info"],',
'#farming-menu #btn-toggle-farm-desc {',
' display: none !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] {',
' padding: 6px !important;',
' margin-bottom: 4px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] span {',
' font-size: 11px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:first-child {',
' display: flex !important;',
' align-items: center !important;',
' column-gap: 6px !important;',
' gap: 6px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:first-child > div:first-child {',
' display: contents !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:first-child > div:first-child > span:first-child {',
' order: 1 !important;',
' flex: 0 0 auto !important;',
' white-space: nowrap !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:first-child > div:first-child > span:last-child {',
' order: 3 !important;',
' flex: 0 0 auto !important;',
' white-space: nowrap !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:first-child > div:last-child {',
' order: 2 !important;',
' flex: 1 1 auto !important;',
' margin: 0 !important;',
' width: auto !important;',
' min-width: 14px !important;',
' height: 4px !important;',
' border-radius: 999px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:first-child > div:last-child #farming-xp-bar {',
' height: 100% !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="background: #333"] > div:last-child {',
' display: block !important;',
' width: 100% !important;',
' text-align: center !important;',
' font-size: 9px !important;',
' line-height: 1.2 !important;',
' margin-top: 2px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="gap: 10px"] {',
' gap: 4px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="gap: 10px"] button {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="gap: 30px"] {',
' gap: 6px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #farming-menu > div[style*="gap: 30px"] button {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #farm-plot-grid {',
' gap: 4px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot {',
' min-height: 0 !important;',
' height: auto !important;',
' padding: 2px !important;',
' border-width: 1px !important;',
' border-radius: 4px 6px 3px 5px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot > div[style*="position: absolute"][style*="top: -2px"][style*="right: -2px"][style*="font-size: 10px"][style*="rotate(20deg)"] {',
' display: none !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot > div[style*="position: absolute"][style*="top: -2px"][style*="left: -2px"][style*="font-size: 10px"][style*="rotate(-15deg)"] {',
' display: none !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot > div[style*="position: absolute"][style*="bottom: -2px"][style*="left: 2px"][style*="font-size: 10px"][style*="rotate(10deg)"] {',
' display: none !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot > div[style*="position: absolute"][style*="bottom: -2px"][style*="right: 2px"][style*="font-size: 10px"][style*="rotate(-25deg)"] {',
' display: none !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot div[id^="plot-content-"] > div:first-child {',
' font-size: 16px !important;',
'}',
'.mp-panel-content #farm-plot-grid .farm-plot div[id^="plot-timer-"] {',
' font-size: 8px !important;',
' margin-top: 2px !important;',
'}',
'.mp-panel-content #tithe-barn-section {',
' padding: 6px !important;',
' margin-bottom: 4px !important;',
' border-radius: 3px !important;',
' position: relative !important;',
'}',
'.mp-panel-content #tithe-barn-section > h3 {',
' font-size: 11px !important;',
' margin: 0 0 4px 0 !important;',
' gap: 6px !important;',
' flex-wrap: nowrap !important;',
' justify-content: flex-start !important;',
' align-self: stretch !important;',
' padding-right: 20px !important;',
'}',
'.mp-panel-content #tithe-barn-section > h3 span {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #tithe-barn-section > p {',
' display: none !important;',
'}',
'.mp-panel-content #auto-farm-controls {',
' gap: 6px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #auto-farm-controls label {',
' font-size: 10px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #auto-farm-controls select {',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #auto-farm-toggle-btn,',
'.mp-panel-content #auto-farm-cancel-btn {',
' padding: 4px 10px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #auto-farm-status {',
' font-size: 10px !important;',
'}',
'#mp-tithe-toggle {',
' background: none !important;',
' border: none !important;',
' color: #aaa !important;',
' cursor: pointer !important;',
' font-size: 10px !important;',
' padding: 0 4px !important;',
' line-height: 1 !important;',
' position: absolute !important;',
' top: 6px !important;',
' right: 6px !important;',
'}',
'#mp-tithe-toggle:hover { color: #fff !important; }',
'.mp-panel-content #tithe-barn-section.mp-tithe-collapsed > *:not(h3) {',
' display: none !important;',
'}',
'#mp-tithe-compact-status {',
' display: none;',
' font-weight: bold;',
' font-size: 10px;',
' white-space: nowrap;',
' margin-left: auto;',
'}',
'#mp-tithe-compact-start {',
' display: none;',
' padding: 0px 6px !important;',
' font-size: 9px !important;',
' border-radius: 2px !important;',
' border: none !important;',
' color: white !important;',
' cursor: pointer !important;',
' font-weight: bold !important;',
' line-height: 1.4 !important;',
'}',
'.mp-tithe-collapsed #mp-tithe-compact-status,',
'.mp-tithe-collapsed #mp-tithe-compact-start {',
' display: inline !important;',
'}',
'/* === Compact Mastery Tab === */',
'.mp-panel-content #mastery-menu {',
' padding: 6px !important;',
' border-width: 1px !important;',
'}',
'.mp-panel-content #mastery-menu > div:first-child {',
' margin-bottom: 4px !important;',
' gap: 4px !important;',
' justify-content: flex-start !important;',
'}',
'.mp-panel-content #mastery-menu > div:first-child h2 {',
' display: none !important;',
'}',
'.mp-panel-content #mastery-soms-available {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mastery-soms-available {',
' letter-spacing: 0 !important;',
'}',
'.mp-panel-content #mastery-soms-available,',
'.mp-panel-content #mastery-total-spent {',
' font-variant-numeric: tabular-nums;',
'}',
'.mp-panel-content #mastery-soms-available {',
' font-weight: 700 !important;',
'}',
'.mp-panel-content #mastery-soms-available {',
' color: #ffcc00 !important;',
'}',
'.mp-panel-content #mastery-soms-available {',
' text-shadow: none !important;',
'}',
'.mp-panel-content #mastery-menu > div:first-child > div:last-child {',
' display: inline-flex !important;',
' align-items: center !important;',
' gap: 3px !important;',
' white-space: nowrap !important;',
' padding: 2px 6px !important;',
' border-radius: 3px !important;',
' border-width: 1px !important;',
' font-size: 8px !important;',
' line-height: 1 !important;',
'}',
'#mastery-menu > div:first-child {',
' justify-content: flex-start !important;',
' gap: 4px !important;',
' margin-bottom: 4px !important;',
'}',
'#mastery-menu > div:first-child > h2 {',
' display: none !important;',
'}',
'#mastery-menu > div:first-child > div {',
' display: inline-flex !important;',
' align-items: center !important;',
' gap: 3px !important;',
' white-space: nowrap !important;',
' padding: 2px 6px !important;',
' border-radius: 3px !important;',
' border-width: 1px !important;',
' font-size: 8px !important;',
' line-height: 1 !important;',
'}',
'#mastery-soms-available {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mastery-boosts-container {',
' grid-template-columns: repeat(auto-fit, minmax(185px, 1fr)) !important;',
' gap: 5px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] {',
' padding: 5px !important;',
' border-radius: 4px !important;',
' gap: 4px !important;',
' border-width: 1px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:first-child {',
' gap: 6px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:first-child > span:first-child {',
' font-size: 1.05em !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:first-child > div > div:first-child {',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:first-child > div > div[id^="mastery-level-display-"] {',
' font-size: 9px !important;',
' line-height: 1.1 !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(2) {',
' font-size: 9px !important;',
' line-height: 1.15 !important;',
' min-height: 0 !important;',
' max-height: 4.7em !important;',
' overflow: hidden !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div[id^="mastery-synergy-status-"] {',
' padding: 4px !important;',
' margin-bottom: 3px !important;',
' border-radius: 3px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div[id^="mastery-synergy-status-"] h4 {',
' margin: 0 0 3px 0 !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div[id^="mastery-synergy-status-"] ul {',
' font-size: 9px !important;',
' line-height: 1.15 !important;',
' padding-left: 12px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(4) {',
' padding: 3px 4px !important;',
' border-radius: 3px !important;',
' font-size: 9px !important;',
' line-height: 1.15 !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(5) {',
' margin-top: 2px !important;',
' gap: 3px !important;',
'}',
'.mp-panel-content #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(5) > div {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #mastery-boosts-container [id^="mastery-input-"] {',
' width: 46px !important;',
' padding: 1px 2px !important;',
' border-radius: 2px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mastery-boosts-container [id^="mastery-cost-"] {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #mastery-boosts-container [id^="mastery-apply-"] {',
' padding: 2px 6px !important;',
' border-radius: 2px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'.mp-panel-content #mastery-menu > div:last-child {',
' margin-top: 8px !important;',
' padding-top: 6px !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #mastery-total-spent {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #reset-mastery-btn {',
' padding: 4px 8px !important;',
' border-radius: 2px !important;',
' font-size: 10px !important;',
' line-height: 1.2 !important;',
'}',
'#mp-mastery-toggle {',
' background: none !important;',
' border: none !important;',
' color: #aaa !important;',
' cursor: pointer !important;',
' font-size: 10px !important;',
' line-height: 1 !important;',
' padding: 0 3px !important;',
'}',
'#mp-mastery-toggle:hover { color: #fff !important; }',
'#mp-mastery-compact-summary {',
' display: none;',
' font-size: 10px;',
' color: #bbb;',
' margin-left: auto;',
' white-space: nowrap;',
' max-width: 50%;',
' overflow: hidden;',
' text-overflow: ellipsis;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(2),',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div[id^="mastery-synergy-status-"],',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed > div:last-child {',
' display: none !important;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] {',
' display: flex !important;',
' flex-direction: row !important;',
' flex-wrap: wrap !important;',
' align-items: center !important;',
' gap: 4px !important;',
' padding: 4px !important;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div:first-child {',
' flex: 1 1 120px;',
' min-width: 0;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(4) {',
' background: none !important;',
' padding: 0 !important;',
' font-size: 9px !important;',
' color: #7fd08f !important;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(5) {',
' display: flex !important;',
' flex: 1 1 100%;',
' flex-direction: row !important;',
' align-items: center !important;',
' gap: 4px !important;',
' margin-top: 0 !important;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(5) > div {',
' display: flex !important;',
' align-items: center !important;',
' gap: 3px !important;',
' margin: 0 !important;',
' white-space: nowrap;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container > [id^="mastery-card-"] > div:nth-of-type(5) > div > span:first-child {',
' display: none !important;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mastery-boosts-container [id^="mastery-apply-"] {',
' margin-left: auto;',
' width: auto !important;',
' padding: 2px 5px !important;',
'}',
'.mp-panel-content #mastery-menu.mp-mastery-collapsed #mp-mastery-compact-summary {',
' display: inline !important;',
'}',
'/* === Compact Dungeons Tab === */',
'.mp-panel-content #dungeons-menu {',
' padding: 2px !important;',
' border: none !important;',
'}',
'.mp-panel-content #dungeons-menu > div:first-child {',
' display: none !important;',
'}',
'.mp-panel-content #dungeon-lobby-view {',
' gap: 2px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child {',
' order: 2 !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:last-child {',
' order: 1 !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child {',
' padding: 3px 4px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child > h3 {',
' font-size: 10px !important;',
' margin: 0 0 2px 0 !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child > div[style*="grid-template-columns"] {',
' display: flex !important;',
' flex-wrap: wrap !important;',
' align-items: flex-end !important;',
' gap: 4px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child > div[style*="grid-template-columns"] > div {',
' flex: 0 1 auto !important;',
' min-width: 0 !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child > div[style*="grid-template-columns"] > div:first-child {',
' flex: 0 1 100px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:first-child > div[style*="grid-template-columns"] > div:nth-child(2) {',
' flex: 0 1 110px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view label {',
' font-size: 9px !important;',
' margin-bottom: 0 !important;',
' gap: 2px !important;',
'}',
'.mp-panel-content #create-dungeon-level,',
'.mp-panel-content #create-dungeon-role {',
' padding: 2px 3px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view div[style*="grid-column: span 2"] {',
' gap: 6px !important;',
' grid-column: auto !important;',
'}',
'.mp-panel-content #btn-create-party {',
' margin-top: 3px !important;',
' padding: 3px 8px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #dungeon-lobby-view > div:last-child > h3 {',
' font-size: 10px !important;',
' margin: 0 0 2px 0 !important;',
'}',
'.mp-panel-content #dungeon-lobby-list {',
' max-height: none !important;',
'}',
'.mp-panel-content #dungeon-lobby-list > div {',
' padding: 3px 6px !important;',
' margin-bottom: 2px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #dungeon-lobby-list > div > div > div:first-child {',
' font-size: 11px !important;',
'}',
'.mp-panel-content #dungeon-lobby-list > div > div > div:last-child {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #dungeon-lobby-list button {',
' padding: 2px 8px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #dungeon-lobby-list button[id^="btn-join-party"] {',
' font-size: 0 !important;',
'}',
'.mp-panel-content #dungeon-lobby-list button[id^="btn-join-party"]::after {',
' content: "Join" !important;',
' font-size: 10px !important;',
' font-weight: bold !important;',
'}',
'.mp-panel-content #dungeon-lobby-list > div > div[style*="flex-direction: column"] {',
' flex-direction: row !important;',
' align-items: stretch !important;',
' gap: 0 !important;',
'}',
'.mp-panel-content #dungeon-lobby-list > div > div[style*="flex-direction: column"] > input {',
' width: 50px !important;',
' min-width: 0 !important;',
' padding: 2px 3px !important;',
' border-radius: 2px 0 0 2px !important;',
' border-right: none !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #dungeon-lobby-list > div > div[style*="flex-direction: column"] > button {',
' border-radius: 0 2px 2px 0 !important;',
'}',
'.mp-panel-content #dungeon-lobby-list input[type="text"] {',
' padding: 2px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #dungeon-content > div[style*="flex-direction: column"] {',
' gap: 3px !important;',
'}',
'.mp-panel-content #dungeon-content > div > div[style*="gap: 15px"] {',
' gap: 3px !important;',
' min-height: 0 !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="background: #333"][style*="padding: 15px"],',
'.mp-panel-content #dungeon-content div[style*="background: #333"][style*="padding: 20px"] {',
' padding: 4px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #dungeon-content h3 {',
' font-size: 10px !important;',
' margin: 0 0 2px 0 !important;',
'}',
'.mp-panel-content #dungeon-content h4 {',
' font-size: 10px !important;',
' margin: 0 0 2px 0 !important;',
' padding-bottom: 2px !important;',
'}',
'.mp-panel-content #dungeon-content span[style*="font-size: 14px"] {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="height: 20px"] {',
' height: 10px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="height: 10px"][style*="border-radius: 5px"] {',
' height: 5px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="border-radius: 10px"] {',
' border-radius: 5px !important;',
'}',
'.mp-panel-content #dungeon-consumables-container {',
' padding: 3px !important;',
' gap: 4px !important;',
' margin-top: 3px !important;',
'}',
'.mp-panel-content #dungeon-consumables-container button {',
' width: 32px !important;',
' height: 32px !important;',
' border-radius: 3px !important;',
' border-width: 1px !important;',
'}',
'.mp-panel-content #dungeon-consumables-container button > span:first-child {',
' font-size: 14px !important;',
'}',
'.mp-panel-content #dungeon-consumables-container span[id^="count-dungeon-consumable"] {',
' font-size: 8px !important;',
' padding: 0 1px !important;',
'}',
'.mp-panel-content #dungeon-consumables-container div[id^="cd-dungeon-consumable"] {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="background: #222"][style*="padding: 15px"] {',
' padding: 4px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="background: #222"][style*="padding: 10px"] {',
' padding: 3px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="flex-direction: column"][style*="gap: 8px"] {',
' gap: 2px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="background: #222"][style*="padding: 10px"][style*="border: 1px solid #444"][style*="border-radius: 4px"] {',
' padding: 3px 4px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="font-size: 12px"][style*="color: #888"] {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="font-size: 12px"][style*="flex-wrap: wrap"] {',
' font-size: 9px !important;',
' padding: 2px !important;',
' margin-bottom: 2px !important;',
' gap: 3px !important;',
'}',
'.mp-panel-content #dungeon-content button[id^="btn-leave-party"],',
'.mp-panel-content #dungeon-content button[id^="btn-start-dungeon"] {',
' padding: 4px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="background: #111"][style*="border: 1px solid #444"][style*="flex-direction: column"] {',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="background: #222"][style*="border-bottom: 1px solid #444"] {',
' padding: 3px 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #dungeon-log-messages {',
' padding: 3px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #dungeon-log-messages > div {',
' font-size: 9px !important;',
' margin-bottom: 1px !important;',
' padding-bottom: 1px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="font-size: 11px"][style*="margin-bottom: 10px"] {',
' font-size: 9px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #dungeon-content div[id="passcode-in-party"],',
'.mp-panel-content #dungeon-content div[id="lobby-passcode"] {',
' font-size: 10px !important;',
' margin-top: 2px !important;',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="margin-bottom: 5px"] {',
' margin-bottom: 1px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="margin-bottom: 10px"] {',
' margin-bottom: 2px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="margin-top: 15px"] {',
' margin-top: 3px !important;',
'}',
'.mp-panel-content #dungeon-content div[style*="margin-bottom: 15px"] {',
' margin-bottom: 3px !important;',
'}',
'.mp-panel-content #dungeon-content p {',
' font-size: 10px !important;',
' margin: 2px 0 !important;',
'}',
'/* === Compact Exchange Tab === */',
'.mp-panel-content #exchange-menu {',
' padding: 2px !important;',
' border: none !important;',
'}',
'.mp-panel-content #exchange-menu > h2 {',
' font-size: 13px !important;',
' margin: 0 0 2px 0 !important;',
'}',
'.mp-panel-content #exchange-tabs-container {',
' gap: 3px !important;',
' margin-bottom: 4px !important;',
' padding-bottom: 4px !important;',
' flex-wrap: wrap !important;',
'}',
'.mp-panel-content #exchange-tabs-container button {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #exchange-recipes-balance,',
'.mp-panel-content #glyphs-balance,',
'.mp-panel-content #sharpening-resources-display {',
' margin-bottom: 4px !important;',
' padding: 4px 6px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #exchange-recipes-balance > div:first-child,',
'.mp-panel-content #glyphs-balance > div:first-child,',
'.mp-panel-content #sharpening-resources-display > div:first-child {',
' margin-bottom: 2px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #exchange-recipes-balance > div:last-child,',
'.mp-panel-content #glyphs-balance > div:last-child,',
'.mp-panel-content #sharpening-resources-display > div:last-child {',
' gap: 6px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #exchange-recipes-list {',
' gap: 4px !important;',
'}',
'.mp-panel-content div[id^="recipe-card-"] {',
' padding: 4px 6px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content div[id^="recipe-card-"] > div > div:first-child {',
' font-size: 11px !important;',
' margin-bottom: 1px !important;',
'}',
'.mp-panel-content div[id^="recipe-card-"] > div > div[style*="0.9em"] {',
' font-size: 10px !important;',
' margin-bottom: 1px !important;',
'}',
'.mp-panel-content div[id^="recipe-card-"] > div > div[style*="0.85em"] {',
' font-size: 9px !important;',
' margin-top: 1px !important;',
'}',
'.mp-panel-content div[id^="recipe-card-"] > button {',
' padding: 2px 6px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #anti-glyph-section {',
' margin-bottom: 6px !important;',
' padding: 6px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #anti-glyph-section div[style*="1.1em"] {',
' font-size: 11px !important;',
'}',
'.mp-panel-content #anti-glyph-section div[style*="0.9em"] {',
' font-size: 9px !important;',
' margin-top: 2px !important;',
'}',
'.mp-panel-content #anti-glyph-section button {',
' padding: 3px 8px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #anti-glyph-section div[style*="flex-direction:column"] {',
' gap: 3px !important;',
'}',
'.mp-panel-content #glyphs-list {',
' gap: 6px !important;',
'}',
'.mp-panel-content #glyphs-list > div {',
' padding: 6px !important;',
' border-radius: 4px !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:first-child {',
' font-size: 11px !important;',
' margin-bottom: 4px !important;',
' padding-bottom: 4px !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child {',
' gap: 4px !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div {',
' padding: 4px 6px !important;',
' border-radius: 3px !important;',
' display: flex !important;',
' flex-direction: row !important;',
' align-items: flex-start !important;',
' gap: 6px !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div div[style*="1.1em"] {',
' font-size: 11px !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div div[style*="0.9em"] {',
' font-size: 9px !important;',
' margin-top: 2px !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div > div:first-child {',
' flex: 1 !important;',
' margin-bottom: 0 !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div > div:last-child {',
' display: grid !important;',
' grid-template-columns: auto 1fr !important;',
' gap: 3px 0px !important;',
' align-items: center !important;',
' background: none !important;',
' padding: 0 !important;',
' flex: 0 0 auto !important;',
'}',
'.mp-panel-content #glyphs-list div[id^="glyph-cost-"] {',
' grid-row: 1 !important;',
' grid-column: 1 !important;',
' font-size: 9px !important;',
' text-align: right !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div > div:last-child > button {',
' grid-row: 1 !important;',
' grid-column: 2 !important;',
' padding: 2px 8px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
' justify-self: stretch !important;',
' text-align: center !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div > div:last-child > label {',
' grid-row: 2 !important;',
' grid-column: 1 !important;',
' font-size: 9px !important;',
' margin: 0 !important;',
' padding: 0 !important;',
' justify-self: end !important;',
'}',
'.mp-panel-content #glyphs-list > div > div:last-child > div > div:last-child > input[type="number"] {',
' grid-row: 2 !important;',
' grid-column: 2 !important;',
' width: auto !important;',
' justify-self: stretch !important;',
' padding: 2px 4px !important;',
' font-size: 10px !important;',
' text-align: center !important;',
' margin: 0 !important;',
'}',
'.mp-panel-content #glyphs-list div[id^="glyph-limit-"] {',
' font-size: 9px !important;',
' margin-top: 1px !important;',
'}',
'.mp-panel-content #sharpening-selector {',
' margin-bottom: 6px !important;',
' padding: 6px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #sharpening-selector label {',
' font-size: 10px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #sharpening-level-select {',
' width: 140px !important;',
' padding: 3px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #sharpening-cost-display {',
' margin-top: 4px !important;',
' padding: 4px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #sharpening-cost-display div[style*="margin-bottom:5px"] {',
' margin-bottom: 2px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #sharpening-cost-display div[style*="margin-top:5px"] {',
' margin-top: 2px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #sharpening-purchase-btn {',
' padding: 4px 10px !important;',
' font-size: 10px !important;',
' border-radius: 3px !important;',
' margin-top: 4px !important;',
'}',
'.mp-panel-content #sharpening-info {',
' margin-top: 6px !important;',
' padding: 6px !important;',
' border-radius: 3px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #sharpening-info div[style*="margin-bottom"] {',
' margin-bottom: 3px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #sharpening-info div[style*="font-weight:bold"] {',
' font-size: 10px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #lost-shipments-header {',
' margin-bottom: 6px !important;',
' padding: 6px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #lost-shipments-header h3 {',
' font-size: 11px !important;',
' margin: 0 0 3px 0 !important;',
'}',
'.mp-panel-content #lost-shipments-header p {',
' font-size: 9px !important;',
' margin: 0 0 3px 0 !important;',
' line-height: 1.3 !important;',
'}',
'.mp-panel-content #lost-shipments-header div[style*="margin-top:15px"] {',
' margin-top: 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #lost-shipments-controls {',
' gap: 4px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #lost-shipments-controls button {',
' padding: 3px 8px !important;',
' font-size: 10px !important;',
' border-radius: 2px !important;',
'}',
'.mp-panel-content #lost-shipments-table h4 {',
' font-size: 10px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #lost-shipments-table table {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #lost-shipments-table th {',
' padding: 3px 4px !important;',
' font-size: 9px !important;',
'}',
'.mp-panel-content #lost-shipments-table td {',
' padding: 2px 4px !important;',
'}',
'.mp-panel-content #lost-shipments-table p {',
' font-size: 8px !important;',
' margin-top: 4px !important;',
'}',
'.mp-panel-content #inner-circle-header {',
' padding: 6px !important;',
' border-radius: 4px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #inner-circle-header h3 {',
' font-size: 11px !important;',
' margin: 0 0 3px 0 !important;',
'}',
'.mp-panel-content #inner-circle-header p {',
' font-size: 9px !important;',
' margin: 0 0 3px 0 !important;',
' line-height: 1.3 !important;',
'}',
'.mp-panel-content #inner-circle-header div[style*="margin-top:10px"] {',
' margin-top: 4px !important;',
' padding: 4px !important;',
' border-radius: 3px !important;',
' font-size: 9px !important;',
' line-height: 1.4 !important;',
'}',
'.mp-panel-content #inner-circle-status {',
' padding: 8px !important;',
' border-radius: 4px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #inner-circle-status div[style*="1.2em"] {',
' font-size: 11px !important;',
' margin-bottom: 4px !important;',
'}',
'.mp-panel-content #inner-circle-status div[style*="color:#aaa"] {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #inner-circle-exchange {',
' gap: 6px !important;',
'}',
'.mp-panel-content #inner-circle-exchange-controls {',
' gap: 4px !important;',
'}',
'.mp-panel-content #inner-circle-exchange-btn {',
' padding: 6px 12px !important;',
' font-size: 10px !important;',
' border-radius: 3px !important;',
'}',
'.mp-panel-content #inner-circle-exchange-btn span {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #inner-circle-exchange > div[style*="color:"] {',
' font-size: 10px !important;',
'}',
'.mp-panel-content #inner-circle-paypal-section {',
' margin-top: 8px !important;',
' padding: 8px !important;',
' border-radius: 4px !important;',
'}',
'.mp-panel-content #inner-circle-paypal-section div[style*="1.1em"] {',
' font-size: 10px !important;',
' margin-bottom: 6px !important;',
'}',
'.mp-panel-content #inner-circle-paypal-section label {',
' font-size: 9px !important;',
'}',
'.mp-panel-content #inner-circle-paypal-section select {',
' padding: 4px !important;',
' font-size: 10px !important;',
'}',
'.mp-panel-content #inner-circle-paypal-section div[style*="background: #222"] {',
' padding: 8px !important;',
' border-radius: 4px !important;',
'}',
'.mp-panel-content #inner-circle-paypal-section p {',
' font-size: 8px !important;',
' margin-bottom: 6px !important;',
' line-height: 1.3 !important;',
'}',
'#mp-activity-tracker {',
' flex: 0 0 auto;',
' background: #1a1a1a;',
' border-bottom: 1px solid #333;',
' padding: 4px 6px;',
' font-size: 11px;',
' color: #ccc;',
' overflow-y: auto;',
' max-height: 40%;',
'}',
'#mp-activity-tracker .tracker-header {',
' display: flex;',
' justify-content: space-between;',
' align-items: center;',
' margin-bottom: 2px;',
' font-size: 10px;',
' color: #888;',
'}',
'#mp-activity-tracker .tracker-header span:first-child {',
' font-weight: bold;',
' color: #aaa;',
'}',
'#mp-activity-tracker .tracker-row {',
' white-space: nowrap;',
' overflow: hidden;',
' line-height: 1.5;',
' font-size: 11px;',
' position: relative;',
' padding-right: 16px;',
'}',
'#mp-activity-tracker .rate { color: #8f8; }',
'#mp-activity-tracker .xp-rate { color: #8af; }',
'#mp-activity-tracker .sep { color: #444; margin: 0 2px; }',
'#mp-activity-tracker .refine-arrow { color: #666; margin: 0 1px; }',
'#mp-activity-tracker .tracker-per-action { padding-left:1.2em; opacity:.72; font-size:.85em; color:#777; }',
'#mp-activity-tracker .tracker-x { position: absolute; right: 0; top: 0; cursor: pointer; color: #555; font-size: 10px; line-height: 1.5; padding: 0 2px; }',
'#mp-activity-tracker .tracker-x:hover { color: #f66; }',
'#mp-activity-tracker .tracker-empty {',
' color: #555;',
' font-style: italic;',
' font-size: 10px;',
'}',
].join('\n');
var s = document.createElement('style');
s.textContent = css;
document.head.appendChild(s);
})();
function _ensureDynamicPanelCssStyle() {
if (_dynamicPanelCssStyle && _dynamicPanelCssStyle.isConnected) return _dynamicPanelCssStyle;
_dynamicPanelCssStyle = document.getElementById('ia2g-dynamic-panel-css');
if (_dynamicPanelCssStyle) return _dynamicPanelCssStyle;
_dynamicPanelCssStyle = document.createElement('style');
_dynamicPanelCssStyle.id = 'ia2g-dynamic-panel-css';
document.head.appendChild(_dynamicPanelCssStyle);
return _dynamicPanelCssStyle;
}
function _applyDynamicPanelCss() {
var css = [
'.mp-panel-content-plain {',
' flex: 1;',
' overflow-y: auto;',
' overflow-x: hidden;',
' position: relative;',
' z-index: 0;',
' min-height: 0;',
' contain: strict;',
'}',
'.mp-panel-content-plain > .menu-overlay {',
' display: block !important;',
' position: relative !important;',
' top: auto !important;',
' left: auto !important;',
' width: 100% !important;',
' max-width: none !important;',
' height: 100% !important;',
' box-sizing: border-box;',
' z-index: auto !important;',
'}',
'.mp-panel-content-plain .menu-close-btn {',
' display: none !important;',
'}',
'#mp-panel-css-options {',
' margin-top: 6px;',
' padding-top: 6px;',
' border-top: 1px solid #454545;',
'}',
'#mp-panel-css-options .mp-panel-css-header {',
' margin-bottom: 4px;',
'}',
'#mp-panel-css-options .mp-panel-css-toggle {',
' width: 100%;',
' display: flex;',
' align-items: center;',
' justify-content: space-between;',
' gap: 6px;',
' border: 1px solid #4a4a4a;',
' border-radius: 4px;',
' background: #232323;',
' color: #9fb9ff;',
' font-size: 11px;',
' line-height: 1.2;',
' text-transform: uppercase;',
' letter-spacing: 0.02em;',
' padding: 4px 6px;',
' cursor: pointer;',
'}',
'#mp-panel-css-options .mp-panel-css-toggle:hover {',
' border-color: #5d7ecd;',
' color: #c8d8ff;',
'}',
'#mp-panel-css-options .mp-panel-css-caret {',
' font-size: 10px;',
' color: #8fa8df;',
'}',
'#mp-panel-css-options.is-collapsed .mp-panel-css-caret {',
' transform: rotate(-90deg);',
'}',
'#mp-panel-css-list {',
' max-height: 170px;',
' overflow-y: auto;',
' border: 1px solid #444;',
' border-radius: 4px;',
' padding: 4px 5px;',
' background: rgba(0, 0, 0, 0.18);',
'}',
'#mp-panel-css-options.is-collapsed #mp-panel-css-list {',
' display: none;',
'}',
'#mp-panel-css-list .mp-panel-css-row {',
' display: flex;',
' align-items: center;',
' gap: 5px;',
' margin: 1px 0;',
'}',
'#mp-panel-css-list .mp-panel-css-row label {',
' flex: 1 1 auto;',
' min-width: 0;',
' overflow: hidden;',
' text-overflow: ellipsis;',
' white-space: nowrap;',
' color: #c8c8c8;',
' font-size: 11px;',
' line-height: 1.25;',
' cursor: pointer;',
'}',
'#mp-panel-css-options.mp-disabled {',
' opacity: 0.5;',
'}'
].join('\n');
_ensureDynamicPanelCssStyle().textContent = css;
}
function _getPanelContentClassName(moduleKey) {
return _isPanelVisualCssEnabled(moduleKey) ? 'mp-panel-content' : 'mp-panel-content-plain';
}
function _refreshPanelCssControlsState() {
var root = document.getElementById('mp-panel-css-options');
if (!root) return;
var enabled = !!_settings.enablePanelVisualCss;
root.classList.toggle('mp-disabled', !enabled);
var inputs = root.querySelectorAll('input[type="checkbox"][data-panel-css-key]');
for (var i = 0; i < inputs.length; i++) {
inputs[i].disabled = !enabled;
}
}
function _applyPanelCssSettings() {
_applyDynamicPanelCss();
_refreshPanelCssControlsState();
}
// =================================================================
// Toast
// =================================================================
function _showToast(msg) {
var old = document.querySelector('.mp-toast');
if (old) old.remove();
var t = document.createElement('div');
t.className = 'mp-toast';
t.textContent = msg;
document.body.appendChild(t);
requestAnimationFrame(function () { t.classList.add('mp-toast-visible'); });
setTimeout(function () {
t.classList.remove('mp-toast-visible');
setTimeout(function () { t.remove(); }, 300);
}, 2500);
}
function _hidePresetsDropdown() {
if (_presetsOutsideHandler) {
document.removeEventListener('pointerdown', _presetsOutsideHandler, true);
_presetsOutsideHandler = null;
}
if (_presetsKeyHandler) {
document.removeEventListener('keydown', _presetsKeyHandler, true);
_presetsKeyHandler = null;
}
if (_presetsDropdownAnchorEl && _presetsDropdownAnchorEl.classList) {
_presetsDropdownAnchorEl.classList.remove('is-open');
}
if (_presetsDropdownEl && _presetsDropdownEl.parentNode) _presetsDropdownEl.remove();
_presetsDropdownEl = null;
_presetsDropdownAnchorEl = null;
_presetsEditingId = null;
}
function _applyPresetSnapshot(snapshot) {
var normalized = _normalizePresetSnapshot(snapshot);
var oldKeys = _collectActiveModuleKeys();
for (var i = 0; i < oldKeys.length; i++) stopModuleTimers(oldKeys[i]);
_gridState.panels = normalized.gridPanels;
_freeformState.panels = normalized.freeformPanels;
_preferredGridSpans = normalized.preferredSpans;
var maxZ = 0;
_freeformState.panels.forEach(function (p) {
if (p.zIndex > maxZ) maxZ = p.zIndex;
});
_nextZIndex = maxZ + 1;
_clampAllPanels();
_syncGameState();
var newKeys = _collectActiveModuleKeys();
for (var j = 0; j < newKeys.length; j++) {
if (_isRegisteredModuleKey(newKeys[j])) initializeModule(newKeys[j]);
}
_saveGridState();
_saveFreeformState();
_mpUpdateDisplay();
renderUIBar();
}
function _createPresetFromCurrentLayout() {
var now = Date.now();
var preset = {
id: 'preset_' + now + '_' + Math.floor(Math.random() * 1000000),
name: _nextPresetName(),
snapshot: _captureLayoutSnapshot(),
createdAt: now,
updatedAt: now
};
_presets.push(preset);
_savePresets();
return preset;
}
function _renderPresetsDropdownList(listEl) {
if (!listEl) return;
listEl.innerHTML = '';
if (!_presets.length) {
var empty = document.createElement('div');
empty.className = 'mp-presets-empty';
empty.textContent = 'No presets yet';
listEl.appendChild(empty);
return;
}
_presets.forEach(function (preset) {
var row = document.createElement('div');
row.className = 'mp-preset-row';
row.setAttribute('data-preset-id', preset.id);
if (_presetsEditingId === preset.id) {
var renameWrap = document.createElement('div');
renameWrap.className = 'mp-preset-rename-wrap';
var input = document.createElement('input');
input.type = 'text';
input.className = 'mp-preset-rename-input';
input.value = preset.name;
input.maxLength = 40;
renameWrap.appendChild(input);
var saveBtn = document.createElement('button');
saveBtn.type = 'button';
saveBtn.className = 'mp-preset-action';
saveBtn.textContent = 'Save';
saveBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var next = _normalizePresetName(input.value);
if (!next) {
_showToast('Preset name is required');
input.focus();
return;
}
preset.name = next;
preset.updatedAt = Date.now();
_presetsEditingId = null;
_savePresets();
_renderPresetsDropdownList(listEl);
_showToast('Preset renamed');
});
renameWrap.appendChild(saveBtn);
var cancelBtn = document.createElement('button');
cancelBtn.type = 'button';
cancelBtn.className = 'mp-preset-action';
cancelBtn.textContent = 'Cancel';
cancelBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
_presetsEditingId = null;
_renderPresetsDropdownList(listEl);
});
renameWrap.appendChild(cancelBtn);
input.addEventListener('keydown', function (e) {
if (e.key === 'Enter') saveBtn.click();
if (e.key === 'Escape') cancelBtn.click();
});
row.appendChild(renameWrap);
listEl.appendChild(row);
setTimeout(function () {
input.focus();
input.select();
}, 0);
return;
}
var loadBtn = document.createElement('button');
loadBtn.type = 'button';
loadBtn.className = 'mp-preset-load';
loadBtn.textContent = preset.name;
loadBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
_hidePresetsDropdown();
_applyPresetSnapshot(preset.snapshot);
_showToast('Preset loaded: ' + preset.name);
});
row.appendChild(loadBtn);
var actions = document.createElement('div');
actions.className = 'mp-preset-actions';
var renameBtn = document.createElement('button');
renameBtn.type = 'button';
renameBtn.className = 'mp-preset-action';
renameBtn.textContent = '✎';
renameBtn.title = 'Rename preset';
renameBtn.setAttribute('aria-label', 'Rename preset');
renameBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
_presetsEditingId = preset.id;
_renderPresetsDropdownList(listEl);
});
actions.appendChild(renameBtn);
var deleteBtn = document.createElement('button');
deleteBtn.type = 'button';
deleteBtn.className = 'mp-preset-delete';
deleteBtn.textContent = '-';
deleteBtn.title = 'Delete preset';
deleteBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
if (!window.confirm('Delete preset "' + preset.name + '"?')) return;
_presets = _presets.filter(function (p) { return p.id !== preset.id; });
if (_presetsEditingId === preset.id) _presetsEditingId = null;
_savePresets();
_renderPresetsDropdownList(listEl);
_showToast('Preset deleted');
});
actions.appendChild(deleteBtn);
row.appendChild(actions);
listEl.appendChild(row);
});
}
function _showPresetsDropdown(anchorEl) {
if (!anchorEl) return;
_hidePresetsDropdown();
_presetsDropdownAnchorEl = anchorEl;
anchorEl.classList.add('is-open');
var menu = document.createElement('div');
menu.className = 'mp-presets-dropdown';
menu.setAttribute('role', 'menu');
var newBtn = document.createElement('button');
newBtn.type = 'button';
newBtn.className = 'mp-presets-new-btn';
newBtn.textContent = 'New Preset';
menu.appendChild(newBtn);
var list = document.createElement('div');
list.className = 'mp-presets-list';
menu.appendChild(list);
_renderPresetsDropdownList(list);
newBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var preset = _createPresetFromCurrentLayout();
_presetsEditingId = preset.id;
_renderPresetsDropdownList(list);
_showToast('Preset saved');
});
document.body.appendChild(menu);
_presetsDropdownEl = menu;
var rect = anchorEl.getBoundingClientRect();
var menuRect = menu.getBoundingClientRect();
var pad = 8;
var x = Math.round(rect.left);
var y = Math.round(rect.bottom + 4);
x = Math.max(pad, Math.min(x, window.innerWidth - menuRect.width - pad));
y = Math.max(pad, Math.min(y, window.innerHeight - menuRect.height - pad));
menu.style.left = x + 'px';
menu.style.top = y + 'px';
_presetsOutsideHandler = function (ev) {
if (!_presetsDropdownEl) return;
if (_presetsDropdownEl.contains(ev.target)) return;
if (_presetsDropdownAnchorEl && _presetsDropdownAnchorEl.contains(ev.target)) return;
_hidePresetsDropdown();
};
_presetsKeyHandler = function (ev) {
if (ev.key === 'Escape') _hidePresetsDropdown();
};
document.addEventListener('pointerdown', _presetsOutsideHandler, true);
document.addEventListener('keydown', _presetsKeyHandler, true);
}
function _togglePresetsDropdown(anchorEl) {
if (!anchorEl) return;
if (_presetsDropdownEl && _presetsDropdownAnchorEl === anchorEl) {
_hidePresetsDropdown();
return;
}
_showPresetsDropdown(anchorEl);
}
function _ensurePresetsButton() {
var uiBar = document.getElementById('ui-bar');
var hamburger = document.getElementById('btn-hamburger');
if (_presetsDropdownAnchorEl && !_presetsDropdownAnchorEl.isConnected) _hidePresetsDropdown();
if (!uiBar || !hamburger) {
_hidePresetsDropdown();
return;
}
var btn = document.getElementById('btn-presets');
if (!btn) {
btn = document.createElement('button');
btn.id = 'btn-presets';
btn.className = 'ui-btn';
btn.textContent = 'Presets';
btn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
_togglePresetsDropdown(btn);
});
}
if (btn.parentNode !== uiBar || btn.previousSibling !== hamburger) {
uiBar.insertBefore(btn, hamburger.nextSibling);
}
}
function _applyMenuButtonsVisibility() {
var uiBar = document.getElementById('ui-bar');
if (!uiBar) return;
uiBar.classList.toggle('ia2g-hide-menu-buttons', !!_menuButtonsHidden);
var toggleBtn = document.getElementById('btn-menu-buttons-toggle');
if (toggleBtn) toggleBtn.textContent = _menuButtonsHidden ? 'Show Buttons' : 'Hide Buttons';
}
function _ensureMenuButtonsToggle() {
var uiBar = document.getElementById('ui-bar');
var hamburger = document.getElementById('btn-hamburger');
if (!uiBar || !hamburger) return;
var btn = document.getElementById('btn-menu-buttons-toggle');
if (!btn) {
btn = document.createElement('button');
btn.id = 'btn-menu-buttons-toggle';
btn.className = 'ui-btn';
btn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
_menuButtonsHidden = !_menuButtonsHidden;
_applyMenuButtonsVisibility();
});
}
var anchor = document.getElementById('btn-presets') || hamburger;
if (btn.parentNode !== uiBar || btn.previousSibling !== anchor) {
uiBar.insertBefore(btn, anchor.nextSibling);
}
_applyMenuButtonsVisibility();
}
function _clampAddTileSpan(value, maxAllowed) {
var n = parseInt(value, 10);
if (!isFinite(n)) n = 1;
return Math.max(1, Math.min(Math.max(1, maxAllowed || 1), n));
}
function _hideAddTileMenu() {
if (_addTileOutsideHandler) {
document.removeEventListener('pointerdown', _addTileOutsideHandler, true);
_addTileOutsideHandler = null;
}
if (_addTileKeyHandler) {
document.removeEventListener('keydown', _addTileKeyHandler, true);
_addTileKeyHandler = null;
}
if (_addTileMenuEl && _addTileMenuEl.parentNode) _addTileMenuEl.remove();
_addTileMenuEl = null;
}
function _hidePanelTabAddMenu() {
if (_panelTabAddMenuOutsideHandler) {
document.removeEventListener('pointerdown', _panelTabAddMenuOutsideHandler, true);
_panelTabAddMenuOutsideHandler = null;
}
if (_panelTabAddMenuKeyHandler) {
document.removeEventListener('keydown', _panelTabAddMenuKeyHandler, true);
_panelTabAddMenuKeyHandler = null;
}
if (_panelTabAddMenuContext && _panelTabAddMenuContext.anchorEl && _panelTabAddMenuContext.anchorEl.classList) {
_panelTabAddMenuContext.anchorEl.classList.remove('is-open');
}
if (_panelTabAddMenuEl && _panelTabAddMenuEl.parentNode) _panelTabAddMenuEl.remove();
_panelTabAddMenuEl = null;
_panelTabAddMenuContext = null;
}
function _saveLayoutState(isFreeform) {
if (isFreeform) _saveFreeformState();
else _saveGridState();
}
function _resolveLayoutPanel(panelKey, isFreeform) {
if (!panelKey) return null;
return isFreeform ? _getFreeformPanel(panelKey) : _getPanelByKey(panelKey);
}
function _insertModuleIntoPanel(moduleKey, targetPanel, isFreeform) {
if (!moduleKey || !GAME_MODULES[moduleKey] || !targetPanel) return;
var targetKey = _getPanelPrimaryKey(targetPanel);
var resolvedTarget = _resolveLayoutPanel(targetKey, isFreeform) || targetPanel;
if (_panelHasModule(resolvedTarget, moduleKey)) {
_setPanelActiveTab(resolvedTarget, moduleKey);
_saveLayoutState(isFreeform);
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
return;
}
var sourcePanel = _resolveLayoutPanel(moduleKey, isFreeform);
if (sourcePanel && sourcePanel !== resolvedTarget) {
_mergeModuleIntoPanel(moduleKey, sourcePanel, resolvedTarget, isFreeform, null);
return;
}
_insertTabsIntoPanel(resolvedTarget, [moduleKey], null);
initializeModule(moduleKey);
_saveLayoutState(isFreeform);
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
}
function _showPanelTabAddMenu(panelData, isFreeform, anchorEl) {
_hidePanelTabAddMenu();
if (!panelData || !anchorEl) return;
var panelKey = _getPanelPrimaryKey(panelData);
if (!panelKey) return;
var targetPanel = _resolveLayoutPanel(panelKey, isFreeform);
if (!targetPanel) return;
var menu = document.createElement('div');
menu.className = 'mp-panel-tab-add-menu';
menu.setAttribute('role', 'menu');
var title = document.createElement('div');
title.className = 'mp-panel-tab-add-title';
title.textContent = 'Add Tab';
menu.appendChild(title);
var moduleKeys = Object.keys(GAME_MODULES).sort(function (a, b) {
var la = (GAME_MODULES[a] && GAME_MODULES[a].label) ? GAME_MODULES[a].label : a;
var lb = (GAME_MODULES[b] && GAME_MODULES[b].label) ? GAME_MODULES[b].label : b;
return la.localeCompare(lb);
});
if (!moduleKeys.length) {
var empty = document.createElement('div');
empty.className = 'mp-panel-tab-add-empty';
empty.textContent = 'No modules available';
menu.appendChild(empty);
} else {
moduleKeys.forEach(function (moduleKey) {
var row = document.createElement('div');
row.className = 'menu-item-row';
var btn = document.createElement('button');
btn.type = 'button';
btn.className = 'menu-item-btn';
btn.textContent = (GAME_MODULES[moduleKey] && GAME_MODULES[moduleKey].label)
? GAME_MODULES[moduleKey].label
: moduleKey;
if (_panelHasModule(targetPanel, moduleKey)) btn.classList.add('is-in-panel');
btn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var liveTarget = _resolveLayoutPanel(panelKey, isFreeform);
if (!liveTarget) {
_hidePanelTabAddMenu();
_showToast('Panel is no longer available');
return;
}
_hidePanelTabAddMenu();
_insertModuleIntoPanel(moduleKey, liveTarget, isFreeform);
});
row.appendChild(btn);
menu.appendChild(row);
});
}
document.body.appendChild(menu);
anchorEl.classList.add('is-open');
_panelTabAddMenuEl = menu;
_panelTabAddMenuContext = {
panelKey: panelKey,
isFreeform: !!isFreeform,
anchorEl: anchorEl
};
var rect = anchorEl.getBoundingClientRect();
var menuRect = menu.getBoundingClientRect();
var pad = 8;
var x = Math.round(rect.left);
var y = Math.round(rect.bottom + 4);
x = Math.max(pad, Math.min(x, window.innerWidth - menuRect.width - pad));
y = Math.max(pad, Math.min(y, window.innerHeight - menuRect.height - pad));
menu.style.left = x + 'px';
menu.style.top = y + 'px';
_panelTabAddMenuOutsideHandler = function (ev) {
if (!_panelTabAddMenuEl) return;
if (_panelTabAddMenuEl.contains(ev.target)) return;
if (anchorEl && anchorEl.contains && anchorEl.contains(ev.target)) return;
_hidePanelTabAddMenu();
};
_panelTabAddMenuKeyHandler = function (ev) {
if (ev.key === 'Escape') _hidePanelTabAddMenu();
};
document.addEventListener('pointerdown', _panelTabAddMenuOutsideHandler, true);
document.addEventListener('keydown', _panelTabAddMenuKeyHandler, true);
}
function _showAddTileMenu(moduleKey, clientX, clientY) {
_hideAddTileMenu();
if (!moduleKey || !GAME_MODULES[moduleKey]) return;
if (_isPanelActive(moduleKey)) {
_showToast('Module is already open');
return;
}
var menu = document.createElement('div');
menu.className = 'mp-add-tile-menu';
menu.setAttribute('role', 'dialog');
var title = document.createElement('div');
title.className = 'mp-add-tile-title';
title.textContent = 'Add ' + (GAME_MODULES[moduleKey].displayName || moduleKey) + ' Tile';
var preferredSpan = _getPreferredGridSpan(moduleKey);
var colRow = document.createElement('div');
colRow.className = 'mp-add-tile-row';
var colLabel = document.createElement('label');
colLabel.textContent = 'Columns';
var maxCols = Math.max(1, _settings.gridCols);
var initialCols = _clampAddTileSpan(preferredSpan.colSpan, maxCols);
var colInputs = document.createElement('div');
colInputs.className = 'mp-add-tile-inputs';
var colSlider = document.createElement('input');
colSlider.type = 'range';
colSlider.className = 'mp-add-tile-slider';
colSlider.min = '1';
colSlider.max = String(maxCols);
colSlider.step = '1';
colSlider.value = String(initialCols);
var colInput = document.createElement('input');
colInput.type = 'number';
colInput.min = '1';
colInput.max = String(maxCols);
colInput.step = '1';
colInput.value = String(initialCols);
colRow.appendChild(colLabel);
colInputs.appendChild(colSlider);
colInputs.appendChild(colInput);
colRow.appendChild(colInputs);
var rowRow = document.createElement('div');
rowRow.className = 'mp-add-tile-row';
var rowLabel = document.createElement('label');
rowLabel.textContent = 'Rows';
var maxRows = Math.max(1, _settings.gridRows);
var initialRows = _clampAddTileSpan(preferredSpan.rowSpan, maxRows);
var rowInputs = document.createElement('div');
rowInputs.className = 'mp-add-tile-inputs';
var rowSlider = document.createElement('input');
rowSlider.type = 'range';
rowSlider.className = 'mp-add-tile-slider';
rowSlider.min = '1';
rowSlider.max = String(maxRows);
rowSlider.step = '1';
rowSlider.value = String(initialRows);
var rowInput = document.createElement('input');
rowInput.type = 'number';
rowInput.min = '1';
rowInput.max = String(maxRows);
rowInput.step = '1';
rowInput.value = String(initialRows);
rowRow.appendChild(rowLabel);
rowInputs.appendChild(rowSlider);
rowInputs.appendChild(rowInput);
rowRow.appendChild(rowInputs);
function syncSpanInputs(numberInput, sliderInput, maxSpan) {
function setClamped(rawValue) {
var clamped = _clampAddTileSpan(rawValue, maxSpan);
var nextValue = String(clamped);
if (numberInput.value !== nextValue) numberInput.value = nextValue;
if (sliderInput.value !== nextValue) sliderInput.value = nextValue;
return clamped;
}
numberInput.addEventListener('input', function () {
setClamped(numberInput.value);
});
numberInput.addEventListener('change', function () {
setClamped(numberInput.value);
});
sliderInput.addEventListener('input', function () {
setClamped(sliderInput.value);
});
sliderInput.addEventListener('change', function () {
setClamped(sliderInput.value);
});
setClamped(numberInput.value);
}
syncSpanInputs(colInput, colSlider, maxCols);
syncSpanInputs(rowInput, rowSlider, maxRows);
var actions = document.createElement('div');
actions.className = 'mp-add-tile-actions';
var cancelBtn = document.createElement('button');
cancelBtn.type = 'button';
cancelBtn.className = 'mp-add-tile-cancel';
cancelBtn.textContent = 'Cancel';
cancelBtn.addEventListener('click', function () {
_hideAddTileMenu();
});
var addBtn = document.createElement('button');
addBtn.type = 'button';
addBtn.className = 'mp-add-tile-confirm';
addBtn.textContent = 'Add';
addBtn.addEventListener('click', function () {
var colSpan = _clampAddTileSpan(colInput.value, _settings.gridCols);
var rowSpan = _clampAddTileSpan(rowInput.value, _settings.gridRows);
var cell = _findFirstEmptyCell();
if (!cell) {
_showToast('Grid is full — reduce tile size in Options');
return;
}
_addPanel(moduleKey, cell.col, cell.row, colSpan, rowSpan);
_hideAddTileMenu();
var dd = document.getElementById('hamburger-dropdown');
if (dd) dd.style.display = 'none';
});
actions.appendChild(cancelBtn);
actions.appendChild(addBtn);
menu.appendChild(title);
menu.appendChild(colRow);
menu.appendChild(rowRow);
menu.appendChild(actions);
document.body.appendChild(menu);
var menuRect = menu.getBoundingClientRect();
var pad = 8;
var x = Math.round(typeof clientX === 'number' ? clientX : (window.innerWidth / 2));
var y = Math.round(typeof clientY === 'number' ? clientY : (window.innerHeight / 2));
x = Math.max(pad, Math.min(x, window.innerWidth - menuRect.width - pad));
y = Math.max(pad, Math.min(y, window.innerHeight - menuRect.height - pad));
menu.style.left = x + 'px';
menu.style.top = y + 'px';
_addTileOutsideHandler = function (ev) {
if (_addTileMenuEl && !_addTileMenuEl.contains(ev.target)) _hideAddTileMenu();
};
_addTileKeyHandler = function (ev) {
if (ev.key === 'Escape') _hideAddTileMenu();
};
document.addEventListener('pointerdown', _addTileOutsideHandler, true);
document.addEventListener('keydown', _addTileKeyHandler, true);
_addTileMenuEl = menu;
colInput.focus();
colInput.select();
}
function _getMenusByTargetId(targetId) {
if (!targetId) return [];
var safeId = String(targetId).replace(/\\/g, '\\\\').replace(/"/g, '\\"');
var nodes = document.querySelectorAll('[id="' + safeId + '"]');
return Array.prototype.slice.call(nodes);
}
function _resolveModuleMenuElement(moduleKey, targetId) {
var matches = _getMenusByTargetId(targetId);
if (!matches.length) return null;
// Mastery can appear twice in source DOM; prefer the populated one.
if (moduleKey === 'mastery') {
for (var i = 0; i < matches.length; i++) {
if (matches[i].querySelector('#mastery-boosts-container')) return matches[i];
}
}
return matches[0];
}
// =================================================================
// Grid Viewport Rendering
// =================================================================
function _mpUpdateDisplay() {
var dc = document.getElementById('dual-menu-container');
var ca = document.getElementById('canvas-area');
if (!dc || !ca) return;
var portrait = window.matchMedia('(max-aspect-ratio: 1/1)').matches;
// Return menus to canvas-area before tearing down
var oldMenus = dc.querySelectorAll('.mp-panel-content > .menu-overlay, .mp-panel-content-plain > .menu-overlay');
for (var i = 0; i < oldMenus.length; i++) ca.appendChild(oldMenus[i]);
// Move fallback rows back into actions-menu before destroying inner
var _actMenu = document.getElementById('actions-menu');
if (_actMenu) {
var _hdrFb = document.getElementById('opt-fallback-row');
var _hdrQol = document.getElementById('qol-fallback-container');
if (_hdrFb && _hdrFb.closest('.mp-header-actions')) _actMenu.appendChild(_hdrFb);
if (_hdrQol && _hdrQol.closest('.mp-header-actions')) _actMenu.appendChild(_hdrQol);
}
var oldInner = document.getElementById('mp-grid-inner');
if (oldInner) oldInner.remove();
// Reset all module menus to their original game styles
var mods = Object.values(GAME_MODULES);
for (var m = 0; m < mods.length; m++) {
var menus = _getMenusByTargetId(mods[m].targetId);
for (var mi = 0; mi < menus.length; mi++) {
var mel = menus[mi];
mel.style.display = 'none';
mel.style.position = 'absolute';
mel.style.top = '0';
mel.style.left = '0';
mel.style.width = '100%';
mel.style.maxWidth = '1000px';
mel.style.height = '';
mel.style.zIndex = '';
}
}
var fm = document.getElementById('filter-menu');
if (fm) fm.style.display = 'none';
dc.classList.remove('mp-flex-container');
dc.classList.add('mp-grid-viewport');
dc.style.display = 'block';
var inner = document.createElement('div');
inner.id = 'mp-grid-inner';
var insets = _getGridViewportInsets(dc);
var vpW = Math.max(1, dc.clientWidth - insets.right);
var vpH = Math.max(1, dc.clientHeight - insets.bottom);
// ---- Grid mode ----
var gap = _settings.gridGap;
var tileW = Math.max(5, _settings.cellWidth);
var tileH = Math.max(5, _settings.cellHeight);
var cols = Math.max(1, Math.floor((vpW + gap) / (tileW + gap)));
var rows = Math.max(1, Math.floor((vpH + gap) / (tileH + gap)));
_settings.gridCols = cols;
_settings.gridRows = rows;
_repositionOutOfBoundsPanels();
var tracks = _computeTrackSizes(tileW, tileH, vpW, vpH, gap);
_lastColWidths = tracks.colWidths;
_lastRowHeights = tracks.rowHeights;
_lastGap = gap;
var gridW = 0;
for (var wi = 0; wi < cols; wi++) gridW += _lastColWidths[wi];
gridW += gap * (cols - 1);
var gridH = 0;
for (var hi = 0; hi < rows; hi++) gridH += _lastRowHeights[hi];
gridH += gap * (rows - 1);
inner.style.gridTemplateColumns = _lastColWidths.map(function (w) { return w + 'px'; }).join(' ');
inner.style.gridTemplateRows = _lastRowHeights.map(function (h) { return h + 'px'; }).join(' ');
inner.style.gap = gap + 'px';
inner.style.width = gridW + 'px';
inner.style.height = gridH + 'px';
inner.style.transform = 'none';
inner.classList.add(_settings.showGrid ? 'mp-grid-lines' : 'mp-grid-nolines');
if (portrait && _gridState.panels.length) {
var fp = _gridState.panels[0];
var pnl = _createPanelElement(fp);
inner.appendChild(pnl);
dc.appendChild(inner);
return;
}
// Place panels
_gridState.panels.forEach(function (p) {
inner.appendChild(_createPanelElement(p));
});
// Grid lines via CSS background (avoids per-cell DOM elements).
// Render lines in physical-pixel units so browser zoom / OS scaling
// does not create periodic thick/thin moire artifacts.
if (_settings.showGrid) {
var dpr = _effectiveDevicePixelRatio();
var lineWidthCss = 1 / dpr;
var bgParts = [];
var xAcc = 0;
for (var gi = 0; gi < cols - 1; gi++) {
xAcc += _lastColWidths[gi];
var rawX = xAcc + gap * gi + Math.floor(gap / 2);
var lx = _snapCssPxToDevicePixel(rawX, dpr);
bgParts.push('linear-gradient(180deg,#333 0%,#333 100%) ' + lx + 'px 0/' + lineWidthCss + 'px 100% no-repeat');
}
var yAcc = 0;
for (var gj = 0; gj < rows - 1; gj++) {
yAcc += _lastRowHeights[gj];
var rawY = yAcc + gap * gj + Math.floor(gap / 2);
var ly = _snapCssPxToDevicePixel(rawY, dpr);
bgParts.push('linear-gradient(90deg,#333 0%,#333 100%) 0 ' + ly + 'px/100% ' + lineWidthCss + 'px no-repeat');
}
if (bgParts.length) inner.style.background = bgParts.join(',');
}
dc.appendChild(inner);
}
// =================================================================
// Panel Element
// =================================================================
function _mountPanelContent(panelData, content) {
var activeKey = panelData.activeTab || _getPanelPrimaryKey(panelData);
var mod = activeKey ? GAME_MODULES[activeKey] : null;
if (!mod) return;
var menuEl = _resolveModuleMenuElement(activeKey, mod.targetId);
if (menuEl) {
content.appendChild(menuEl);
menuEl.style.display = 'block';
menuEl.style.position = 'relative';
menuEl.style.top = 'auto';
menuEl.style.left = 'auto';
menuEl.style.width = '100%';
menuEl.style.maxWidth = 'none';
menuEl.style.height = '100%';
menuEl.style.zIndex = 'auto';
menuEl.style.pointerEvents = 'auto';
var gameCloseBtns = menuEl.querySelectorAll('.menu-close-btn');
for (var ci = 0; ci < gameCloseBtns.length; ci++) gameCloseBtns[ci].remove();
}
}
function _buildHeaderTabs(panelData, header, isFreeform) {
var tabsWrap = document.createElement('div');
tabsWrap.className = 'mp-panel-tabs';
var tabs = _getPanelTabs(panelData);
tabs.forEach(function (tabKey) {
var tabBtn = document.createElement('button');
tabBtn.className = 'mp-panel-tab' + (panelData.activeTab === tabKey ? ' is-active' : '');
tabBtn.textContent = _getPanelLabel(panelData, tabKey);
tabBtn.setAttribute('data-tab-key', tabKey);
tabBtn.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
e.preventDefault();
e.stopPropagation();
_startTabDrag(panelData, tabKey, isFreeform, e, tabBtn);
});
tabBtn.addEventListener('click', function (e) {
e.stopPropagation();
_activateTab(panelData, tabKey, isFreeform);
});
var tabClose = document.createElement('button');
tabClose.className = 'mp-panel-tab-close';
tabClose.innerHTML = '×';
tabClose.addEventListener('mousedown', function (e) {
e.stopPropagation();
});
tabClose.addEventListener('click', function (e) {
e.stopPropagation();
_removeModuleFromLayout(tabKey, isFreeform);
});
tabBtn.appendChild(tabClose);
tabsWrap.appendChild(tabBtn);
});
var addTabBtn = document.createElement('button');
addTabBtn.type = 'button';
addTabBtn.className = 'mp-panel-tab mp-panel-tab-add';
addTabBtn.textContent = '+';
addTabBtn.title = 'Add tab to this panel';
addTabBtn.addEventListener('mousedown', function (e) {
e.stopPropagation();
});
addTabBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
var panelKey = _getPanelPrimaryKey(panelData);
var isOpenForSamePanel = _panelTabAddMenuEl &&
_panelTabAddMenuContext &&
_panelTabAddMenuContext.panelKey === panelKey &&
_panelTabAddMenuContext.isFreeform === !!isFreeform;
if (isOpenForSamePanel) {
_hidePanelTabAddMenu();
return;
}
_showPanelTabAddMenu(panelData, isFreeform, addTabBtn);
});
tabsWrap.appendChild(addTabBtn);
header.appendChild(tabsWrap);
}
function _createPanelElement(panelData) {
_normalizePanelModules(panelData);
if (!_getPanelPrimaryKey(panelData)) {
var stub = document.createElement('div');
stub.className = 'mp-panel';
return stub;
}
var panel = document.createElement('div');
panel.className = 'mp-panel';
panel.setAttribute('data-key', panelData.key);
panel.setAttribute('data-panel-key', _getPanelPrimaryKey(panelData));
panel.setAttribute('data-layout', 'grid');
panel.style.gridColumn = (panelData.col + 1) + ' / span ' + panelData.colSpan;
panel.style.gridRow = (panelData.row + 1) + ' / span ' + panelData.rowSpan;
// Header
var header = document.createElement('div');
header.className = 'mp-panel-header';
header.style.height = _settings.headerHeight + 'px';
var closeBtn = document.createElement('button');
closeBtn.className = 'mp-panel-close';
closeBtn.innerHTML = '×';
closeBtn.addEventListener('click', function (e) {
e.stopPropagation();
_removePanelGroup(panelData.key);
});
_buildHeaderTabs(panelData, header, false);
if (_panelHasModule(panelData, 'actions')) {
var headerRight = document.createElement('div');
headerRight.className = 'mp-header-actions';
var qolFb = document.getElementById('qol-fallback-container');
var optFb = document.getElementById('opt-fallback-row');
if (qolFb) {
var qolLabel = qolFb.querySelector('div > span:first-child');
if (qolLabel) qolLabel.textContent = 'Inner Circle Fallback:';
headerRight.appendChild(qolFb);
}
if (optFb) {
var fbLabel = optFb.querySelector('div > span:first-child');
if (fbLabel) fbLabel.textContent = 'Fallback:';
headerRight.appendChild(optFb);
}
headerRight.appendChild(closeBtn);
headerRight.addEventListener('mousedown', function (e) {
if (e.target.closest('select')) e.stopPropagation();
});
header.appendChild(headerRight);
} else {
header.appendChild(closeBtn);
}
header.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
if (e.target.closest('.mp-panel-tab') || e.target.closest('.mp-panel-tab-close')) return;
e.preventDefault();
_startPanelDrag(_getPanelPrimaryKey(panelData), e);
});
// Content
var content = document.createElement('div');
var contentClass = _getPanelContentClassName(panelData.activeTab || _getPanelPrimaryKey(panelData));
content.className = contentClass;
_mountPanelContent(panelData, content);
panel.appendChild(header);
panel.appendChild(content);
// Resize handles — all 4 edges + all 4 corners
var _handles = [
['mp-resize-t', 'top'],
['mp-resize-b', 'bottom'],
['mp-resize-l', 'left'],
['mp-resize-r', 'right'],
['mp-resize-tl', 'top-left'],
['mp-resize-tr', 'top-right'],
['mp-resize-bl', 'bottom-left'],
['mp-resize-br', 'bottom-right']
];
_handles.forEach(function (h) {
var el = document.createElement('div');
el.className = 'mp-resize-handle ' + h[0];
el.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
e.preventDefault(); e.stopPropagation();
_startPanelResize(_getPanelPrimaryKey(panelData), h[1], e);
});
panel.appendChild(el);
});
return panel;
}
// =================================================================
// Free-Form Panel Element
// =================================================================
function _createFreeformPanelElement(panelData) {
_normalizePanelModules(panelData);
if (!_getPanelPrimaryKey(panelData)) {
var stub = document.createElement('div');
stub.className = 'mp-panel mp-panel-freeform';
return stub;
}
var panel = document.createElement('div');
panel.className = 'mp-panel mp-panel-freeform';
panel.setAttribute('data-key', panelData.key);
panel.setAttribute('data-panel-key', _getPanelPrimaryKey(panelData));
panel.setAttribute('data-layout', 'freeform');
panel.style.left = panelData.x + 'px';
panel.style.top = panelData.y + 'px';
panel.style.width = panelData.width + 'px';
panel.style.height = panelData.height + 'px';
panel.style.zIndex = panelData.zIndex || 1;
var header = document.createElement('div');
header.className = 'mp-panel-header';
header.style.height = _settings.headerHeight + 'px';
var closeBtn = document.createElement('button');
closeBtn.className = 'mp-panel-close';
closeBtn.innerHTML = '×';
closeBtn.addEventListener('click', function (e) {
e.stopPropagation();
_removeFreeformPanelGroup(panelData.key);
});
_buildHeaderTabs(panelData, header, true);
if (_panelHasModule(panelData, 'actions')) {
var headerRight = document.createElement('div');
headerRight.className = 'mp-header-actions';
var qolFb = document.getElementById('qol-fallback-container');
var optFb = document.getElementById('opt-fallback-row');
if (qolFb) {
var qolLabel = qolFb.querySelector('div > span:first-child');
if (qolLabel) qolLabel.textContent = 'Inner Circle Fallback:';
headerRight.appendChild(qolFb);
}
if (optFb) {
var fbLabel = optFb.querySelector('div > span:first-child');
if (fbLabel) fbLabel.textContent = 'Fallback:';
headerRight.appendChild(optFb);
}
headerRight.appendChild(closeBtn);
headerRight.addEventListener('mousedown', function (e) {
if (e.target.closest('select')) e.stopPropagation();
});
header.appendChild(headerRight);
} else {
header.appendChild(closeBtn);
}
header.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
if (e.target.closest('.mp-panel-tab') || e.target.closest('.mp-panel-tab-close')) return;
e.preventDefault();
_bringToFront(_getPanelPrimaryKey(panelData));
_startFreeformDrag(_getPanelPrimaryKey(panelData), e);
});
var content = document.createElement('div');
var ffContentClass = _getPanelContentClassName(panelData.activeTab || _getPanelPrimaryKey(panelData));
content.className = ffContentClass;
content.addEventListener('mousedown', function () {
_bringToFront(_getPanelPrimaryKey(panelData));
});
_mountPanelContent(panelData, content);
panel.appendChild(header);
panel.appendChild(content);
var _handles = [
['mp-resize-t', 'top'],
['mp-resize-b', 'bottom'],
['mp-resize-l', 'left'],
['mp-resize-r', 'right'],
['mp-resize-tl', 'top-left'],
['mp-resize-tr', 'top-right'],
['mp-resize-bl', 'bottom-left'],
['mp-resize-br', 'bottom-right']
];
_handles.forEach(function (h) {
var el = document.createElement('div');
el.className = 'mp-resize-handle ' + h[0];
el.addEventListener('mousedown', function (e) {
if (e.button !== 0) return;
e.preventDefault(); e.stopPropagation();
_bringToFront(_getPanelPrimaryKey(panelData));
_startFreeformResize(_getPanelPrimaryKey(panelData), h[1], e);
});
panel.appendChild(el);
});
return panel;
}
// =================================================================
// Tab Dragging / Detach
// =================================================================
var _tabDragState = null;
var TAB_DRAG_THRESHOLD = 6;
var TAB_INSERT_AT_START = '__mp_insert_at_start__';
function _activateTab(panelData, tabKey, isFreeform) {
_setPanelActiveTab(panelData, tabKey);
if (isFreeform) _saveFreeformState();
else _saveGridState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
}
function _startTabDrag(panelData, tabKey, isFreeform, e, tabEl) {
_tabDragState = {
panelData: panelData,
tabKey: tabKey,
isFreeform: !!isFreeform,
startX: e.clientX,
startY: e.clientY,
dragged: false,
ghost: null,
_dragTabEl: tabEl || null,
_insertTabEl: null,
_insertPreviewEl: null,
_insertTargetKey: null,
_insertAfterTabKey: null,
_reorderTabEl: null,
_reorderInsertBefore: null
};
document.addEventListener('mousemove', _onTabDragMove);
document.addEventListener('mouseup', _onTabDragEnd);
}
function _onTabDragMove(e) {
if (!_tabDragState) return;
var s = _tabDragState;
var dx = e.clientX - s.startX;
var dy = e.clientY - s.startY;
if (!s.dragged && Math.hypot(dx, dy) < TAB_DRAG_THRESHOLD) return;
var sourcePanel = s.isFreeform ? _getFreeformPanel(s.tabKey) : _getPanelByKey(s.tabKey);
if (!s.dragged) s.dragged = true;
var outsideSourcePanel = true;
if (sourcePanel) {
var sourcePanelKey = _getPanelPrimaryKey(sourcePanel);
var sourcePanelEl = sourcePanelKey
? document.querySelector('.mp-panel[data-panel-key="' + sourcePanelKey + '"]')
: null;
if (sourcePanelEl) {
var panelRect = sourcePanelEl.getBoundingClientRect();
outsideSourcePanel = e.clientX < panelRect.left ||
e.clientX > panelRect.right ||
e.clientY < panelRect.top ||
e.clientY > panelRect.bottom;
}
}
if (outsideSourcePanel) {
if (!s.ghost) {
var ghost = document.createElement('div');
ghost.className = 'mp-drag-ghost';
ghost.style.width = '170px';
ghost.style.height = (_settings.headerHeight || 28) + 'px';
ghost.style.borderColor = '#88aaff';
ghost.style.background = 'rgba(136,170,255,0.18)';
document.body.appendChild(ghost);
s.ghost = ghost;
}
s.ghost.style.left = (e.clientX - 80) + 'px';
s.ghost.style.top = (e.clientY - ((_settings.headerHeight || 28) / 2)) + 'px';
} else if (s.ghost) {
s.ghost.remove();
s.ghost = null;
}
var target = _findPanelDropTarget(e.clientX, e.clientY, s.isFreeform, sourcePanel, true);
if (target && sourcePanel) {
if (target === sourcePanel) {
if (s._insertPreviewEl) _clearTabInsertIndicator(s);
_clearTabReorderIndicator(s);
_applyLiveTabReorder(s, sourcePanel, s.isFreeform, e.clientX, e.clientY);
} else {
if (s._dragTabEl && s._dragTabEl.classList) s._dragTabEl.classList.remove('mp-tab-dragging');
_clearTabReorderIndicator(s);
var previewLabel = _getPanelLabel(sourcePanel, s.tabKey) || 'New Tab';
_setTabInsertIndicator(s, target, s.isFreeform, e.clientX, e.clientY, previewLabel);
}
} else {
if (s._dragTabEl && s._dragTabEl.classList) s._dragTabEl.classList.remove('mp-tab-dragging');
_clearTabInsertIndicator(s);
}
}
function _findPanelDropTarget(clientX, clientY, isFreeform, sourcePanel, allowSourcePanel) {
var el = document.elementFromPoint(clientX, clientY);
var node = el ? el.closest('.mp-panel') : null;
if (!node) return null;
var targetKey = node.getAttribute('data-panel-key');
if (!targetKey) return null;
var target = isFreeform ? _getFreeformPanel(targetKey) : _getPanelByKey(targetKey);
if (!target) return null;
if (!allowSourcePanel && target === sourcePanel) return null;
return target;
}
function _onTabDragEnd(e) {
if (!_tabDragState) return;
var s = _tabDragState;
if (s._dragTabEl && s._dragTabEl.classList) s._dragTabEl.classList.remove('mp-tab-dragging');
if (s.ghost && s.ghost.parentNode) s.ghost.remove();
_clearTabInsertIndicator(s);
document.removeEventListener('mousemove', _onTabDragMove);
document.removeEventListener('mouseup', _onTabDragEnd);
_tabDragState = null;
if (!s.dragged) {
_activateTab(s.panelData, s.tabKey, s.isFreeform);
return;
}
var sourcePanel = s.isFreeform ? _getFreeformPanel(s.tabKey) : _getPanelByKey(s.tabKey);
if (!sourcePanel) return;
var target = _findPanelDropTarget(e.clientX, e.clientY, s.isFreeform, sourcePanel, true);
if (target) {
if (target === sourcePanel) {
s._insertAfterTabKey = s._dragTabEl
? _getInsertAfterFromTabElement(s._dragTabEl)
: _resolveInsertAfterTabKey(sourcePanel, s.isFreeform, e.clientX, e.clientY);
if (_reorderTabWithinPanel(sourcePanel, s.tabKey, s._insertAfterTabKey, s.isFreeform)) return;
_activateTab(s.panelData, s.tabKey, s.isFreeform);
return;
}
_mergeModuleIntoPanel(s.tabKey, sourcePanel, target, s.isFreeform, s._insertAfterTabKey);
return;
}
_detachTabToNewPanel(s.tabKey, sourcePanel, s.isFreeform, e.clientX, e.clientY);
}
// =================================================================
// Panel Add / Remove
// =================================================================
function _removeTabFromPanel(panel, key) {
if (panel && _isPersistableModuleKey(key)) {
var sourceCol = _getPanelPreferredColSpan(panel);
var sourceRow = _getPanelPreferredRowSpan(panel);
_debugSpanLog('removeTabFromPanel:before', {
key: key,
panelKey: _getPanelPrimaryKey(panel),
panelTabs: _getPanelTabs(panel).slice(),
panelColSpan: panel.colSpan,
panelRowSpan: panel.rowSpan,
panelPreferredColSpan: panel.preferredColSpan,
panelPreferredRowSpan: panel.preferredRowSpan,
remembered: _preferredGridSpans[key] || null,
incoming: { colSpan: sourceCol, rowSpan: sourceRow }
});
if (_shouldReplacePreferredSpan(_preferredGridSpans[key], sourceCol, sourceRow)) {
_setPreferredGridSpan(key, sourceCol, sourceRow);
}
}
var tabs = _getPanelTabs(panel).filter(function (k) { return k !== key; });
panel.tabs = tabs;
panel.key = tabs[0] || null;
if (panel.activeTab === key) panel.activeTab = tabs[0] || null;
_normalizePanelModules(panel);
return tabs.length;
}
function _removePanelByRef(panels, panelRef) {
for (var i = 0; i < panels.length; i++) {
if (panels[i] === panelRef) {
if (panels === _gridState.panels) _rememberPreferredGridSpanForPanel(panelRef);
panels.splice(i, 1);
return true;
}
}
return false;
}
function _insertTabsIntoPanel(targetPanel, tabsToInsert, insertAfterTabKey) {
var targetTabs = _getPanelTabs(targetPanel).slice();
var insertAt = targetTabs.length;
if (insertAfterTabKey === TAB_INSERT_AT_START) {
insertAt = 0;
} else if (insertAfterTabKey) {
var idx = targetTabs.indexOf(insertAfterTabKey);
if (idx !== -1) insertAt = idx + 1;
}
targetTabs.splice.apply(targetTabs, [insertAt, 0].concat(tabsToInsert));
targetPanel.tabs = targetTabs;
targetPanel.activeTab = tabsToInsert[0] || targetPanel.activeTab;
_normalizePanelModules(targetPanel);
}
function _reorderTabWithinPanel(panel, tabKey, insertAfterTabKey, isFreeform) {
var tabs = _getPanelTabs(panel).slice();
var currentTabs = _getPanelTabs(panel);
var fromIdx = tabs.indexOf(tabKey);
if (fromIdx === -1 || tabs.length <= 1) return false;
var insertAt = tabs.length;
if (insertAfterTabKey === TAB_INSERT_AT_START) {
insertAt = 0;
} else if (insertAfterTabKey) {
var targetIdx = tabs.indexOf(insertAfterTabKey);
if (targetIdx !== -1) insertAt = targetIdx + 1;
}
tabs.splice(fromIdx, 1);
if (insertAt > fromIdx) insertAt -= 1;
insertAt = Math.max(0, Math.min(insertAt, tabs.length));
tabs.splice(insertAt, 0, tabKey);
var changed = false;
for (var i = 0; i < tabs.length; i++) {
if (tabs[i] !== currentTabs[i]) {
changed = true;
break;
}
}
if (!changed) return false;
panel.tabs = tabs;
panel.key = tabs[0] || null;
_normalizePanelModules(panel);
if (isFreeform) _saveFreeformState();
else _saveGridState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
return true;
}
function _mergeModuleIntoPanel(tabKey, sourcePanel, targetPanel, isFreeform, insertAfterTabKey) {
if (!sourcePanel || !targetPanel || sourcePanel === targetPanel) return;
if (_panelHasModule(targetPanel, tabKey)) {
_setPanelActiveTab(targetPanel, tabKey);
} else {
_insertTabsIntoPanel(targetPanel, [tabKey], insertAfterTabKey);
}
var remaining = _removeTabFromPanel(sourcePanel, tabKey);
if (!remaining) {
if (isFreeform) _removePanelByRef(_freeformState.panels, sourcePanel);
else _removePanelByRef(_gridState.panels, sourcePanel);
}
if (isFreeform) _saveFreeformState();
else _saveGridState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
}
function _detachTabToNewPanel(tabKey, sourcePanel, isFreeform, clientX, clientY) {
if (!sourcePanel) return;
if (_getPanelTabs(sourcePanel).length <= 1) return;
if (isFreeform) {
var inner = document.getElementById('mp-grid-inner');
if (!inner) return;
var rect = inner.getBoundingClientRect();
var defaultW = Math.max(260, Math.min(sourcePanel.width || 450, 520));
var defaultH = Math.max(200, Math.min(sourcePanel.height || 380, 620));
var x = Math.round((clientX - rect.left) - defaultW / 2);
var y = Math.round((clientY - rect.top) - (_settings.headerHeight || 28) / 2);
_removeTabFromPanel(sourcePanel, tabKey);
if (!_getPanelTabs(sourcePanel).length) _removePanelByRef(_freeformState.panels, sourcePanel);
_freeformState.panels.push(_normalizePanelModules({
key: tabKey,
tabs: [tabKey],
activeTab: tabKey,
x: x,
y: y,
width: defaultW,
height: defaultH,
zIndex: _nextZIndex++
}));
_saveFreeformState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
return;
}
var innerGrid = document.getElementById('mp-grid-inner');
if (!innerGrid) return;
var rectGrid = innerGrid.getBoundingClientRect();
var col = _hitTestTrack(_lastColWidths, _lastGap, clientX - rectGrid.left);
var row = _hitTestTrack(_lastRowHeights, _lastGap, clientY - rectGrid.top);
col = Math.max(0, Math.min(col, _settings.gridCols - 1));
row = Math.max(0, Math.min(row, _settings.gridRows - 1));
var rememberedSpan = _preferredGridSpans[tabKey];
var rememberedCol = rememberedSpan ? _normalizeStoredSpan(rememberedSpan.colSpan) : 1;
var rememberedRow = rememberedSpan ? _normalizeStoredSpan(rememberedSpan.rowSpan) : 1;
var sourceActualCol = _normalizeStoredSpan(sourcePanel.colSpan);
var sourceActualRow = _normalizeStoredSpan(sourcePanel.rowSpan);
var sourcePreferredCol = _getPanelPreferredColSpan(sourcePanel);
var sourcePreferredRow = _getPanelPreferredRowSpan(sourcePanel);
var preferredColSpan = rememberedCol;
var preferredRowSpan = rememberedRow;
_debugSpanLog('detach:inputs', {
tabKey: tabKey,
sourcePanelKey: _getPanelPrimaryKey(sourcePanel),
sourceTabs: _getPanelTabs(sourcePanel).slice(),
sourceActual: { colSpan: sourceActualCol, rowSpan: sourceActualRow },
sourcePreferred: { colSpan: sourcePreferredCol, rowSpan: sourcePreferredRow },
remembered: rememberedSpan || null
});
if (_shouldReplacePreferredSpan({ colSpan: preferredColSpan, rowSpan: preferredRowSpan }, sourceActualCol, sourceActualRow)) {
preferredColSpan = sourceActualCol;
preferredRowSpan = sourceActualRow;
}
if (_shouldReplacePreferredSpan({ colSpan: preferredColSpan, rowSpan: preferredRowSpan }, sourcePreferredCol, sourcePreferredRow)) {
preferredColSpan = sourcePreferredCol;
preferredRowSpan = sourcePreferredRow;
}
_debugSpanLog('detach:chosenSpan', {
tabKey: tabKey,
chosen: { colSpan: preferredColSpan, rowSpan: preferredRowSpan }
});
// Do not exclude the source panel here. During detach the source panel still
// occupies grid cells (with its remaining tabs), so excluding by tab key can
// pick an overlapping rect that later gets collapsed by conflict repair.
var placement = _findPlacementForSpan(col, row, preferredColSpan, preferredRowSpan, null);
_debugSpanLog('detach:placement', {
tabKey: tabKey,
requested: { colSpan: preferredColSpan, rowSpan: preferredRowSpan },
placement: placement
});
if (placement) {
_removeTabFromPanel(sourcePanel, tabKey);
if (!_getPanelTabs(sourcePanel).length) _removePanelByRef(_gridState.panels, sourcePanel);
_gridState.panels.push(_normalizePanelModules({
key: tabKey,
tabs: [tabKey],
activeTab: tabKey,
col: placement.col,
row: placement.row,
colSpan: placement.colSpan,
rowSpan: placement.rowSpan,
preferredColSpan: preferredColSpan,
preferredRowSpan: preferredRowSpan
}));
_saveGridState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
return;
}
_showToast('No space for a ' + preferredColSpan + 'x' + preferredRowSpan + ' detached tile');
_mpUpdateDisplay();
}
function _removePanelGroup(key) {
var panel = _getPanelByKey(key);
if (!panel) return;
var tabs = _getPanelTabs(panel).slice();
_removePanelByRef(_gridState.panels, panel);
tabs.forEach(function (k) { stopModuleTimers(k); });
_syncGameState();
_saveGridState();
_mpUpdateDisplay();
renderUIBar();
}
function _removeFreeformPanelGroup(key) {
var panel = _getFreeformPanel(key);
if (!panel) return;
var tabs = _getPanelTabs(panel).slice();
_freeformState.panels = _freeformState.panels.filter(function (p) { return p !== panel; });
tabs.forEach(function (k) { stopModuleTimers(k); });
_syncGameState();
_saveFreeformState();
_mpUpdateDisplay();
renderUIBar();
}
function _removeModuleFromLayout(key, isFreeform) {
if (isFreeform) _removeFreeformPanel(key);
else _removePanel(key);
}
function _addPanel(key, col, row, requestedColSpan, requestedRowSpan) {
if (_isPanelActive(key)) return;
var preferredSpan = _getPreferredGridSpan(key);
var rawColSpan = (requestedColSpan === undefined || requestedColSpan === null) ? preferredSpan.colSpan : requestedColSpan;
var rawRowSpan = (requestedRowSpan === undefined || requestedRowSpan === null) ? preferredSpan.rowSpan : requestedRowSpan;
var colSpan = _clampAddTileSpan(rawColSpan, _settings.gridCols);
var rowSpan = _clampAddTileSpan(rawRowSpan, _settings.gridRows);
var placement = _findPlacementForSpan(col, row, colSpan, rowSpan, key);
if (!placement) {
_showToast('No space available for a ' + colSpan + 'x' + rowSpan + ' tile');
return;
}
var panel = _normalizePanelModules({
key: key,
tabs: [key],
activeTab: key,
col: placement.col,
row: placement.row,
colSpan: placement.colSpan || 1,
rowSpan: placement.rowSpan || 1,
preferredColSpan: colSpan,
preferredRowSpan: rowSpan
});
_gridState.panels.push(panel);
initializeModule(key);
_syncGameState();
_saveGridState();
_mpUpdateDisplay();
renderUIBar();
}
function _removePanel(key) {
var panel = _getPanelByKey(key);
if (!panel) return;
var left = _removeTabFromPanel(panel, key);
if (!left) {
_removePanelByRef(_gridState.panels, panel);
}
stopModuleTimers(key);
_syncGameState();
_saveGridState();
_mpUpdateDisplay();
renderUIBar();
}
// Panning is intentionally disabled in IdleArtisan2Grid.
// =================================================================
// Panel Dragging (Reposition)
// =================================================================
var _dragPanelState = null;
function _getPanelTabBarElement(panel, isFreeform) {
var panelKey = panel ? _getPanelPrimaryKey(panel) : null;
if (!panelKey) return null;
var selector = isFreeform
? '.mp-panel-freeform[data-panel-key="' + panelKey + '"]'
: '.mp-panel[data-key="' + panelKey + '"]';
var panelEl = document.querySelector(selector);
return panelEl ? panelEl.querySelector('.mp-panel-tabs') : null;
}
function _clearTabInsertIndicator(state) {
if (!state) return;
if (state._insertPreviewEl && state._insertPreviewEl.parentNode) state._insertPreviewEl.remove();
state._insertTabEl = null;
state._insertPreviewEl = null;
state._insertTargetKey = null;
state._insertAfterTabKey = null;
_clearTabReorderIndicator(state);
}
function _clearTabReorderIndicator(state) {
if (!state) return;
if (state._reorderTabEl && state._reorderTabEl.classList) {
state._reorderTabEl.classList.remove('mp-tab-reorder-before');
state._reorderTabEl.classList.remove('mp-tab-reorder-after');
}
state._reorderTabEl = null;
state._reorderInsertBefore = null;
}
function _buildInsertPreviewLabel(panel) {
if (!panel) return 'New Tab';
var tabs = _getPanelTabs(panel);
var activeKey = panel.activeTab || tabs[0] || null;
var baseLabel = activeKey ? _getPanelLabel(panel, activeKey) : '';
if (!baseLabel) baseLabel = 'New Tab';
if (tabs.length > 1) return baseLabel + ' +' + (tabs.length - 1);
return baseLabel;
}
function _resolveInsertTargetTab(panel, isFreeform, clientX, clientY) {
var tabsEl = _getPanelTabBarElement(panel, !!isFreeform);
if (!tabsEl) return null;
var hit = document.elementFromPoint(clientX, clientY);
var hitTab = hit ? hit.closest('.mp-panel-tab') : null;
if (hitTab && hitTab.classList.contains('mp-tab-insert-preview')) {
hitTab = hitTab.previousElementSibling && hitTab.previousElementSibling.classList &&
hitTab.previousElementSibling.classList.contains('mp-panel-tab')
? hitTab.previousElementSibling
: null;
}
if (hitTab && tabsEl.contains(hitTab) && !hitTab.classList.contains('mp-tab-insert-preview')) return hitTab;
var tabs = tabsEl.querySelectorAll('.mp-panel-tab:not(.mp-tab-insert-preview)');
if (!tabs.length) return null;
var chosen = tabs[tabs.length - 1];
for (var i = 0; i < tabs.length; i++) {
var rect = tabs[i].getBoundingClientRect();
if (clientX <= rect.left + (rect.width / 2)) {
chosen = tabs[i];
break;
}
chosen = tabs[i];
}
return chosen;
}
function _resolveInsertPlacement(panel, isFreeform, clientX, clientY) {
var targetTab = _resolveInsertTargetTab(panel, isFreeform, clientX, clientY);
if (!targetTab) return null;
var rect = targetTab.getBoundingClientRect();
var insertBefore = clientX <= rect.left + (rect.width / 2);
var insertAfterTabKey = targetTab.getAttribute('data-tab-key') || null;
if (insertBefore) {
var prevTab = targetTab.previousElementSibling;
while (prevTab && (!prevTab.classList ||
!prevTab.classList.contains('mp-panel-tab') ||
prevTab.classList.contains('mp-tab-insert-preview'))) {
prevTab = prevTab.previousElementSibling;
}
insertAfterTabKey = prevTab ? (prevTab.getAttribute('data-tab-key') || null) : TAB_INSERT_AT_START;
}
return {
targetTab: targetTab,
insertBefore: insertBefore,
insertAfterTabKey: insertAfterTabKey
};
}
function _resolveInsertAfterTabKey(panel, isFreeform, clientX, clientY) {
var placement = _resolveInsertPlacement(panel, isFreeform, clientX, clientY);
return placement ? placement.insertAfterTabKey : null;
}
function _getInsertAfterFromTabElement(tabEl) {
if (!tabEl) return null;
var prevTab = tabEl.previousElementSibling;
while (prevTab && (!prevTab.classList ||
!prevTab.classList.contains('mp-panel-tab') ||
prevTab.classList.contains('mp-tab-insert-preview'))) {
prevTab = prevTab.previousElementSibling;
}
return prevTab ? (prevTab.getAttribute('data-tab-key') || null) : TAB_INSERT_AT_START;
}
function _applyLiveTabReorder(state, panel, isFreeform, clientX, clientY) {
if (!state || !panel || !state._dragTabEl || !state._dragTabEl.isConnected) return;
var placement = _resolveInsertPlacement(panel, isFreeform, clientX, clientY);
if (!placement || !placement.targetTab) return;
var dragTab = state._dragTabEl;
dragTab.classList.add('mp-tab-dragging');
if (placement.targetTab !== dragTab) {
placement.targetTab.insertAdjacentElement(placement.insertBefore ? 'beforebegin' : 'afterend', dragTab);
}
state._insertAfterTabKey = _getInsertAfterFromTabElement(dragTab);
}
function _setTabReorderIndicator(state, panel, isFreeform, clientX, clientY) {
if (!state) return;
var placement = _resolveInsertPlacement(panel, isFreeform, clientX, clientY);
if (!placement || !placement.targetTab) {
_clearTabReorderIndicator(state);
return;
}
if (state._reorderTabEl === placement.targetTab &&
state._reorderInsertBefore === placement.insertBefore &&
state._insertAfterTabKey === placement.insertAfterTabKey &&
state._reorderTabEl &&
state._reorderTabEl.isConnected) {
return;
}
_clearTabReorderIndicator(state);
state._insertAfterTabKey = placement.insertAfterTabKey;
state._reorderTabEl = placement.targetTab;
state._reorderInsertBefore = placement.insertBefore;
placement.targetTab.classList.add(placement.insertBefore ? 'mp-tab-reorder-before' : 'mp-tab-reorder-after');
}
function _setTabInsertIndicator(state, panel, isFreeform, clientX, clientY, previewLabel) {
if (!state) return;
var panelKey = panel ? _getPanelPrimaryKey(panel) : null;
if (!panelKey) {
_clearTabInsertIndicator(state);
return;
}
var targetTab = _resolveInsertTargetTab(panel, isFreeform, clientX, clientY);
if (!targetTab) {
_clearTabInsertIndicator(state);
return;
}
var rect = targetTab.getBoundingClientRect();
var insertBefore = clientX <= rect.left + (rect.width / 2);
var insertAfterTabKey = _resolveInsertAfterTabKey(panel, isFreeform, clientX, clientY);
if (state._insertTargetKey === panelKey &&
state._insertTabEl === targetTab &&
state._insertAfterTabKey === insertAfterTabKey &&
state._insertTabEl &&
state._insertTabEl.isConnected &&
state._insertPreviewEl &&
state._insertPreviewEl.isConnected) {
if (state._insertPreviewEl.textContent !== previewLabel) state._insertPreviewEl.textContent = previewLabel;
return;
}
_clearTabInsertIndicator(state);
var previewTab = document.createElement('button');
previewTab.type = 'button';
previewTab.className = 'mp-panel-tab mp-tab-insert-preview';
previewTab.setAttribute('aria-hidden', 'true');
previewTab.tabIndex = -1;
previewTab.textContent = previewLabel;
targetTab.insertAdjacentElement(insertBefore ? 'beforebegin' : 'afterend', previewTab);
state._insertTabEl = targetTab;
state._insertPreviewEl = previewTab;
state._insertTargetKey = panelKey;
state._insertAfterTabKey = insertAfterTabKey;
}
function _applyPanelDragMove() {
if (!_dragPanelState) return;
var s = _dragPanelState;
s.raf = null;
var inner = document.getElementById('mp-grid-inner');
if (!inner) return;
var rect = inner.getBoundingClientRect();
// Keep the tile anchored to the same grab point within the panel.
var relX = (s.mouseX - rect.left) - s.grabOffsetX;
var relY = (s.mouseY - rect.top) - s.grabOffsetY;
var col = _hitTestTrack(_lastColWidths, s.gap, relX);
var row = _hitTestTrack(_lastRowHeights, s.gap, relY);
col = Math.max(0, Math.min(col, _settings.gridCols - s.colSpan));
row = Math.max(0, Math.min(row, _settings.gridRows - s.rowSpan));
s.targetCol = col;
s.targetRow = row;
var conflicts = [];
for (var r = row; r < row + s.rowSpan; r++) {
for (var c = col; c < col + s.colSpan; c++) {
var occ = _findPanelAt(c, r, s.key);
if (occ && conflicts.indexOf(occ) === -1) conflicts.push(occ);
}
}
var canSwap = conflicts.length === 1 &&
conflicts[0].colSpan === 1 && conflicts[0].rowSpan === 1 &&
s.colSpan === 1 && s.rowSpan === 1;
var canCombine = conflicts.length === 1 && conflicts[0] !== s.panel;
s.blocked = conflicts.length > 0 && !canSwap && !canCombine;
s.swapTarget = canSwap ? conflicts[0] : null;
s.combineTarget = canCombine ? conflicts[0] : null;
if (s.combineTarget) _setTabInsertIndicator(
s, s.combineTarget, false, s.mouseX, s.mouseY, _buildInsertPreviewLabel(s.panel)
);
else _clearTabInsertIndicator(s);
s.ghost.style.left = _trackPos(_lastColWidths, col, s.gap) + 'px';
s.ghost.style.top = _trackPos(_lastRowHeights, row, s.gap) + 'px';
s.ghost.style.width = _trackSpanSize(_lastColWidths, col, s.colSpan, s.gap) + 'px';
s.ghost.style.height = _trackSpanSize(_lastRowHeights, row, s.rowSpan, s.gap) + 'px';
if (s.blocked) {
s.ghost.style.borderColor = '#ff4444';
s.ghost.style.background = 'rgba(255,68,68,0.15)';
} else if (s.swapTarget) {
s.ghost.style.borderColor = '#ffaa00';
s.ghost.style.background = 'rgba(255,170,0,0.15)';
} else {
s.ghost.style.borderColor = '#3366ff';
s.ghost.style.background = 'rgba(51,102,255,0.15)';
}
}
function _startPanelDrag(key, e) {
var inner = document.getElementById('mp-grid-inner');
if (!inner) return;
var panel = _getPanelByKey(key);
if (!panel) return;
var gap = _lastGap;
var ghost = document.createElement('div');
ghost.className = 'mp-drag-ghost';
var panelWidth = _trackSpanSize(_lastColWidths, panel.col, panel.colSpan, gap);
var panelHeight = _trackSpanSize(_lastRowHeights, panel.row, panel.rowSpan, gap);
var panelLeft = _trackPos(_lastColWidths, panel.col, gap);
var panelTop = _trackPos(_lastRowHeights, panel.row, gap);
ghost.style.width = panelWidth + 'px';
ghost.style.height = panelHeight + 'px';
ghost.style.left = panelLeft + 'px';
ghost.style.top = panelTop + 'px';
inner.appendChild(ghost);
var innerRect = inner.getBoundingClientRect();
var grabOffsetX = e.clientX - (innerRect.left + panelLeft);
var grabOffsetY = e.clientY - (innerRect.top + panelTop);
grabOffsetX = Math.max(0, Math.min(panelWidth, grabOffsetX));
grabOffsetY = Math.max(0, Math.min(panelHeight, grabOffsetY));
_dragPanelState = {
key: key, ghost: ghost, gap: gap,
panel: panel,
startCol: panel.col, startRow: panel.row,
colSpan: panel.colSpan, rowSpan: panel.rowSpan,
targetCol: panel.col, targetRow: panel.row,
blocked: false, swapTarget: null, combineTarget: null,
_insertTabEl: null, _insertPreviewEl: null, _insertTargetKey: null,
grabOffsetX: grabOffsetX, grabOffsetY: grabOffsetY,
mouseX: e.clientX, mouseY: e.clientY, raf: null
};
document.addEventListener('mousemove', _onPanelDragMove);
document.addEventListener('mouseup', _onPanelDragEnd);
}
function _onPanelDragMove(e) {
if (!_dragPanelState) return;
var s = _dragPanelState;
s.mouseX = e.clientX;
s.mouseY = e.clientY;
if (!s.raf) s.raf = requestAnimationFrame(_applyPanelDragMove);
}
function _onPanelDragEnd() {
if (!_dragPanelState) return;
var s = _dragPanelState;
if (s.raf) {
cancelAnimationFrame(s.raf);
s.raf = null;
_applyPanelDragMove();
}
if (s.ghost && s.ghost.parentNode) s.ghost.remove();
_clearTabInsertIndicator(s);
if (s.combineTarget && s.panel && s.combineTarget !== s.panel) {
var movedTabs = _getPanelTabs(s.panel).slice();
_insertTabsIntoPanel(s.combineTarget, movedTabs, s._insertAfterTabKey);
_removePanelByRef(_gridState.panels, s.panel);
_saveGridState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
_dragPanelState = null;
document.removeEventListener('mousemove', _onPanelDragMove);
document.removeEventListener('mouseup', _onPanelDragEnd);
return;
}
var moved = s.targetCol !== s.startCol || s.targetRow !== s.startRow;
if (moved && !s.blocked) {
var panel = _getPanelByKey(s.key);
if (panel) {
if (s.swapTarget) {
s.swapTarget.col = s.startCol;
s.swapTarget.row = s.startRow;
}
panel.col = s.targetCol;
panel.row = s.targetRow;
_saveGridState();
_syncGameState();
_mpUpdateDisplay();
}
}
_dragPanelState = null;
document.removeEventListener('mousemove', _onPanelDragMove);
document.removeEventListener('mouseup', _onPanelDragEnd);
}
// =================================================================
// Panel Resizing (Multi-cell)
// =================================================================
var _resizeState = null;
function _startPanelResize(key, direction, e) {
var inner = document.getElementById('mp-grid-inner');
if (!inner) return;
var panel = _getPanelByKey(key);
if (!panel) return;
_resizeState = {
key: key, direction: direction,
origCol: panel.col, origRow: panel.row,
origColSpan: panel.colSpan, origRowSpan: panel.rowSpan,
gap: _lastGap
};
document.addEventListener('mousemove', _onResizeMove);
document.addEventListener('mouseup', _onResizeEnd);
}
function _onResizeMove(e) {
if (!_resizeState) return;
var s = _resizeState;
var panel = _getPanelByKey(s.key);
if (!panel) return;
var inner = document.getElementById('mp-grid-inner');
if (!inner) return;
var rect = inner.getBoundingClientRect();
var relX = e.clientX - rect.left;
var relY = e.clientY - rect.top;
var mouseCol = _hitTestTrack(_lastColWidths, s.gap, relX);
var mouseRow = _hitTestTrack(_lastRowHeights, s.gap, relY);
var dir = s.direction;
var hasRight = dir.indexOf('right') !== -1;
var hasLeft = dir === 'left' || dir === 'top-left' || dir === 'bottom-left';
var hasBottom = dir.indexOf('bottom') !== -1;
var hasTop = dir === 'top' || dir === 'top-left' || dir === 'top-right';
var newCol = s.origCol;
var newRow = s.origRow;
var newCS = s.origColSpan;
var newRS = s.origRowSpan;
if (hasRight) {
newCS = Math.max(1, mouseCol - s.origCol + 1);
newCS = Math.min(newCS, _settings.gridCols - newCol);
}
if (hasLeft) {
var rightEdge = s.origCol + s.origColSpan;
newCol = Math.max(0, Math.min(rightEdge - 1, mouseCol));
newCS = rightEdge - newCol;
}
if (hasBottom) {
newRS = Math.max(1, mouseRow - s.origRow + 1);
newRS = Math.min(newRS, _settings.gridRows - newRow);
}
if (hasTop) {
var bottomEdge = s.origRow + s.origRowSpan;
newRow = Math.max(0, Math.min(bottomEdge - 1, mouseRow));
newRS = bottomEdge - newRow;
}
if (_canResize(s.key, newCol, newRow, newCS, newRS)) {
panel.col = newCol;
panel.row = newRow;
panel.colSpan = newCS;
panel.rowSpan = newRS;
var el = document.querySelector('.mp-panel[data-key="' + s.key + '"]');
if (el) {
el.style.gridColumn = (panel.col + 1) + ' / span ' + panel.colSpan;
el.style.gridRow = (panel.row + 1) + ' / span ' + panel.rowSpan;
}
}
}
function _onResizeEnd() {
if (_resizeState) {
var panel = _getPanelByKey(_resizeState.key);
if (panel) {
panel.preferredColSpan = panel.colSpan;
panel.preferredRowSpan = panel.rowSpan;
_rememberPreferredGridSpanForPanel(panel);
}
_saveGridState();
_syncGameState();
_mpUpdateDisplay();
}
_resizeState = null;
document.removeEventListener('mousemove', _onResizeMove);
document.removeEventListener('mouseup', _onResizeEnd);
}
// =================================================================
// Free-Form Dragging
// =================================================================
var _ffDragState = null;
function _applyFreeformDragMove() {
if (!_ffDragState) return;
var s = _ffDragState;
s.raf = null;
var dx = s.mouseX - s.startMouseX;
var dy = s.mouseY - s.startMouseY;
var newX = s.origX + dx;
var newY = s.origY + dy;
s.el.style.transform = 'translate3d(' + dx + 'px,' + dy + 'px,0)';
s._lastX = newX;
s._lastY = newY;
var source = s.panel || _getFreeformPanel(s.key);
var srcW = source ? source.width : 0;
var srcH = source ? source.height : 0;
var best = null;
var bestArea = 0;
if (source) {
_freeformState.panels.forEach(function (p) {
if (p === source) return;
var xOverlap = Math.max(0, Math.min(newX + srcW, p.x + p.width) - Math.max(newX, p.x));
var yOverlap = Math.max(0, Math.min(newY + srcH, p.y + p.height) - Math.max(newY, p.y));
var area = xOverlap * yOverlap;
if (area > bestArea) {
bestArea = area;
best = p;
}
});
}
s.combineTarget = bestArea > 2000 ? best : null;
if (s.combineTarget) _setTabInsertIndicator(
s, s.combineTarget, true, s.mouseX, s.mouseY, _buildInsertPreviewLabel(s.panel)
);
else _clearTabInsertIndicator(s);
}
function _startFreeformDrag(key, e) {
var p = _getFreeformPanel(key);
if (!p) return;
var el = document.querySelector('.mp-panel-freeform[data-panel-key="' + _getPanelPrimaryKey(p) + '"]');
if (!el) return;
el.classList.add('mp-ff-dragging');
_ffDragState = {
key: key,
panel: p,
el: el,
startMouseX: e.clientX,
startMouseY: e.clientY,
origX: p.x,
origY: p.y,
combineTarget: null,
_insertTabEl: null,
_insertPreviewEl: null,
_insertTargetKey: null,
mouseX: e.clientX,
mouseY: e.clientY,
raf: null
};
document.addEventListener('mousemove', _onFreeformDragMove);
document.addEventListener('mouseup', _onFreeformDragEnd);
}
function _onFreeformDragMove(e) {
if (!_ffDragState) return;
var s = _ffDragState;
s.mouseX = e.clientX;
s.mouseY = e.clientY;
if (!s.raf) s.raf = requestAnimationFrame(_applyFreeformDragMove);
}
function _onFreeformDragEnd() {
if (!_ffDragState) return;
var s = _ffDragState;
if (s.raf) {
cancelAnimationFrame(s.raf);
s.raf = null;
_applyFreeformDragMove();
}
s.el.classList.remove('mp-ff-dragging');
_clearTabInsertIndicator(s);
s.el.style.transform = '';
var p = _getFreeformPanel(s.key);
if (p) {
if (s.combineTarget && s.combineTarget !== p) {
var movedTabs = _getPanelTabs(p).slice();
_insertTabsIntoPanel(s.combineTarget, movedTabs, s._insertAfterTabKey);
_freeformState.panels = _freeformState.panels.filter(function (pp) { return pp !== p; });
_saveFreeformState();
_syncGameState();
_mpUpdateDisplay();
renderUIBar();
_ffDragState = null;
document.removeEventListener('mousemove', _onFreeformDragMove);
document.removeEventListener('mouseup', _onFreeformDragEnd);
return;
}
p.x = s._lastX !== undefined ? s._lastX : s.origX;
p.y = s._lastY !== undefined ? s._lastY : s.origY;
_saveFreeformState();
}
_ffDragState = null;
document.removeEventListener('mousemove', _onFreeformDragMove);
document.removeEventListener('mouseup', _onFreeformDragEnd);
}
// =================================================================
// Free-Form Resizing
// =================================================================
var _ffResizeState = null;
function _startFreeformResize(key, direction, e) {
var p = _getFreeformPanel(key);
if (!p) return;
var el = document.querySelector('.mp-panel-freeform[data-panel-key="' + _getPanelPrimaryKey(p) + '"]');
if (!el) return;
_ffResizeState = {
key: key,
el: el,
direction: direction,
startMouseX: e.clientX,
startMouseY: e.clientY,
origX: p.x,
origY: p.y,
origW: p.width,
origH: p.height
};
document.addEventListener('mousemove', _onFreeformResizeMove);
document.addEventListener('mouseup', _onFreeformResizeEnd);
}
function _onFreeformResizeMove(e) {
if (!_ffResizeState) return;
var s = _ffResizeState;
var dx = e.clientX - s.startMouseX;
var dy = e.clientY - s.startMouseY;
var dir = s.direction;
var hasRight = dir.indexOf('right') !== -1;
var hasLeft = dir === 'left' || dir === 'top-left' || dir === 'bottom-left';
var hasBottom = dir.indexOf('bottom') !== -1;
var hasTop = dir === 'top' || dir === 'top-left' || dir === 'top-right';
var newX = s.origX;
var newY = s.origY;
var newW = s.origW;
var newH = s.origH;
if (hasRight) {
newW = Math.max(150, s.origW + dx);
}
if (hasLeft) {
var maxDx = s.origW - 150;
var clampedDx = Math.min(dx, maxDx);
newX = s.origX + clampedDx;
newW = s.origW - clampedDx;
}
if (hasBottom) {
newH = Math.max(100, s.origH + dy);
}
if (hasTop) {
var maxDy = s.origH - 100;
var clampedDy = Math.min(dy, maxDy);
newY = s.origY + clampedDy;
newH = s.origH - clampedDy;
}
s.el.style.left = newX + 'px';
s.el.style.top = newY + 'px';
s.el.style.width = newW + 'px';
s.el.style.height = newH + 'px';
s._lastX = newX;
s._lastY = newY;
s._lastW = newW;
s._lastH = newH;
}
function _onFreeformResizeEnd() {
if (!_ffResizeState) return;
var s = _ffResizeState;
var p = _getFreeformPanel(s.key);
if (p) {
if (s._lastX !== undefined) {
p.x = s._lastX;
p.y = s._lastY;
p.width = s._lastW;
p.height = s._lastH;
}
_saveFreeformState();
}
_ffResizeState = null;
document.removeEventListener('mousemove', _onFreeformResizeMove);
document.removeEventListener('mouseup', _onFreeformResizeEnd);
}
// =================================================================
// Free-Form Panel Add / Remove
// =================================================================
function _addFreeformPanel(key) {
if (_isPanelActive(key)) return;
var dc = document.getElementById('dual-menu-container');
var vpW = dc ? dc.clientWidth : 800;
var vpH = dc ? dc.clientHeight : 600;
var defaultW = 450;
var defaultH = 380;
var baseX = (-_panX) + (vpW - defaultW) / 2;
var baseY = (-_panY) + (vpH - defaultH) / 2;
var offset = 0;
_freeformState.panels.forEach(function (p) {
if (Math.abs(p.x - baseX - offset) < 20 && Math.abs(p.y - baseY - offset) < 20) {
offset += 30;
}
});
_freeformState.panels.push(_normalizePanelModules({
key: key,
tabs: [key],
activeTab: key,
x: Math.round(baseX + offset),
y: Math.round(baseY + offset),
width: defaultW,
height: defaultH,
zIndex: _nextZIndex++
}));
initializeModule(key);
_syncGameState();
_saveFreeformState();
_mpUpdateDisplay();
renderUIBar();
}
function _removeFreeformPanel(key) {
var panel = _getFreeformPanel(key);
if (!panel) return;
var left = _removeTabFromPanel(panel, key);
if (!left) {
_freeformState.panels = _freeformState.panels.filter(function (p) { return p !== panel; });
}
stopModuleTimers(key);
_syncGameState();
_saveFreeformState();
_mpUpdateDisplay();
renderUIBar();
}
function _hamburgerButtonToModuleKey(btn) {
if (!btn) return null;
var row = btn.closest('.menu-item-row');
if (!row) return null;
var pinBtn = row.querySelector('.pin-btn[id^="menu-pin-"]');
if (!pinBtn || !pinBtn.id) return null;
return pinBtn.id.replace('menu-pin-', '');
}
function _installHamburgerAddInterceptor() {
if (_hamburgerAddInterceptorInstalled) return;
_hamburgerAddInterceptorInstalled = true;
document.addEventListener('click', function (e) {
var dd = document.getElementById('hamburger-dropdown');
if (!dd || !dd.contains(e.target)) return;
var btn = e.target.closest ? e.target.closest('.menu-item-btn') : null;
if (!btn || !dd.contains(btn)) return;
var moduleKey = _hamburgerButtonToModuleKey(btn);
if (!moduleKey) return;
if (window.matchMedia('(max-aspect-ratio: 1/1)').matches) return;
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
_showAddTileMenu(moduleKey, e.clientX, e.clientY);
}, true);
}
// =================================================================
// Patched toggleModule
// =================================================================
function _mpToggleModule(key) {
_hideAddTileMenu();
_hidePanelTabAddMenu();
_hidePresetsDropdown();
var dd = document.getElementById('hamburger-dropdown');
if (dd && dd.style.display === 'flex') dd.style.display = 'none';
var ep = document.getElementById('event-info-popup');
if (ep && ep.style.display === 'block') {
ep.style.display = 'none';
var b = document.getElementById('btn-event-info');
if (b) b.classList.remove('active');
}
if (key === 'filter-menu') {
var fmenu = document.getElementById('filter-menu');
fmenu.style.display = (fmenu.style.display === 'flex') ? 'none' : 'flex';
return;
}
var portrait = window.matchMedia('(max-aspect-ratio: 1/1)').matches;
if (_isPanelActive(key)) {
_removePanel(key);
} else {
if (portrait) {
_gridState.panels.forEach(function (p) {
_rememberPreferredGridSpanForPanel(p);
_forEachPanelModule(p, function (k) { stopModuleTimers(k); });
});
var preferredSpan = _getPreferredGridSpan(key);
_gridState.panels = [_normalizePanelModules({
key: key,
col: 0,
row: 0,
colSpan: _clampAddTileSpan(preferredSpan.colSpan, _settings.gridCols),
rowSpan: _clampAddTileSpan(preferredSpan.rowSpan, _settings.gridRows)
})];
initializeModule(key);
_syncGameState();
_saveGridState();
_mpUpdateDisplay();
renderUIBar();
} else {
var cell = _findFirstEmptyCell();
if (cell) {
_addPanel(key, cell.col, cell.row);
} else {
_showToast('Grid is full \u2014 reduce tile size in Options');
}
}
}
}
// =================================================================
// Patched closeMenu
// =================================================================
function _mpCloseMenu(menuElement) {
var menuId = menuElement.id;
var entry = Object.entries(GAME_MODULES).find(
function (e) { return e[1].targetId === menuId; }
);
if (entry) {
_removePanel(entry[0]);
} else {
menuElement.style.display = 'none';
if (menuId === 'event-info-popup') {
var b1 = document.getElementById('btn-event-info');
if (b1) b1.classList.remove('active');
}
if (menuId === 'event-leaderboard-popup') {
var b2 = document.getElementById('btn-event-leaderboard');
if (b2) b2.classList.remove('active');
}
}
}
// =================================================================
// Patch renderUIBar
// =================================================================
function _patchRenderUIBar() {
var orig = renderUIBar;
renderUIBar = function () {
_syncGameState();
var added = [];
var keys = _collectActiveModuleKeys();
keys.forEach(function (key) {
if (!_isRegisteredModuleKey(key)) return;
if (pinnedModules.indexOf(key) === -1) {
pinnedModules.push(key);
added.push(key);
}
});
orig.call(this);
added.forEach(function (key) {
var idx = pinnedModules.indexOf(key);
if (idx !== -1) pinnedModules.splice(idx, 1);
});
_ensurePresetsButton();
_ensureMenuButtonsToggle();
};
}
// =================================================================
// Settings Injection into Options Page
// =================================================================
function _patchOptionsMenu() {
var orig = renderOptionsMenu;
renderOptionsMenu = function () {
orig.call(this);
_injectSettings();
_compactOptionsMenu();
};
}
function _getModuleKeysSortedByLabel() {
return Object.keys(GAME_MODULES || {}).sort(function (a, b) {
var la = (GAME_MODULES[a] && GAME_MODULES[a].label) ? GAME_MODULES[a].label : a;
var lb = (GAME_MODULES[b] && GAME_MODULES[b].label) ? GAME_MODULES[b].label : b;
return la.localeCompare(lb);
});
}
function _addPerPanelCssOptions(parent) {
var wrap = document.createElement('div');
wrap.id = 'mp-panel-css-options';
wrap.classList.add('is-collapsed');
var hdr = document.createElement('div');
hdr.className = 'mp-panel-css-header';
var toggleBtn = document.createElement('button');
toggleBtn.type = 'button';
toggleBtn.className = 'mp-panel-css-toggle';
toggleBtn.setAttribute('aria-expanded', 'false');
var title = document.createElement('span');
title.textContent = 'Per-Panel Visual CSS';
var caret = document.createElement('span');
caret.className = 'mp-panel-css-caret';
caret.textContent = '▼';
toggleBtn.appendChild(title);
toggleBtn.appendChild(caret);
hdr.appendChild(toggleBtn);
wrap.appendChild(hdr);
var list = document.createElement('div');
list.id = 'mp-panel-css-list';
var keys = _getModuleKeysSortedByLabel();
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
var label = (GAME_MODULES[key] && GAME_MODULES[key].label) ? GAME_MODULES[key].label : key;
var row = document.createElement('div');
row.className = 'mp-panel-css-row';
var cb = document.createElement('input');
cb.type = 'checkbox';
cb.checked = _getPanelVisualCssPreference(key);
cb.setAttribute('data-panel-css-key', key);
cb.style.cssText = 'width:13px;height:13px;accent-color:#3366ff;flex:0 0 auto;';
var lbl = document.createElement('label');
lbl.textContent = label;
lbl.title = label;
lbl.addEventListener('click', function (checkbox) {
return function () { checkbox.click(); };
}(cb));
cb.addEventListener('change', function (moduleKey, checkbox) {
return function () {
_setPanelVisualCssEnabled(moduleKey, checkbox.checked);
_onSettingsChanged('panelCssMap');
};
}(key, cb));
row.appendChild(cb);
row.appendChild(lbl);
list.appendChild(row);
}
wrap.appendChild(list);
parent.appendChild(wrap);
toggleBtn.addEventListener('click', function () {
var collapsed = wrap.classList.toggle('is-collapsed');
toggleBtn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
});
_refreshPanelCssControlsState();
}
function _injectSettings() {
var list = document.getElementById('options-list');
if (!list || document.getElementById('mp-settings-section')) return;
var section = document.createElement('div');
section.id = 'mp-settings-section';
section.style.cssText = 'border-top:1px solid #555;padding-top:8px;margin-top:6px;';
var heading = document.createElement('h3');
heading.style.cssText = 'color:#3366ff;margin:0 0 6px 0;font-size:13px;line-height:1.2;';
heading.textContent = 'IdleArtisan2Grid Layout';
section.appendChild(heading);
_addSliderSetting(section, 'Tile Width (px)', 'cellWidth', 5, 420);
_addSliderSetting(section, 'Tile Height (px)', 'cellHeight', 5, 320);
_addNumberSetting(section, 'Grid Gap (px)', 'gridGap', 0, 20, false);
_addCheckboxSetting(section, 'Show Grid Lines', 'showGrid');
_addCheckboxSetting(section, 'Rounded Tab Corners', 'roundedTabs');
_addCheckboxSetting(section, 'Enable Panel Visual CSS', 'enablePanelVisualCss', function () {
_onSettingsChanged('enablePanelVisualCss');
});
_addCheckboxSetting(section, 'Enable Log Message Rewrites', 'enableLogMessageTransforms');
_addPerPanelCssOptions(section);
var applyBtn = document.createElement('button');
applyBtn.textContent = 'Update Grid';
applyBtn.style.cssText = 'margin-top:6px;padding:5px 12px;background:#3366ff;color:#fff;' +
'border:none;border-radius:4px;cursor:pointer;font-size:12px;font-weight:bold;' +
'transition:background 0.15s;';
applyBtn.addEventListener('mouseenter', function () { applyBtn.style.background = '#2255dd'; });
applyBtn.addEventListener('mouseleave', function () { applyBtn.style.background = '#3366ff'; });
applyBtn.addEventListener('click', function () {
_onSettingsChanged('all');
_showToast('Grid updated');
});
section.appendChild(applyBtn);
var usernameRow = document.getElementById('opt-change-username-row');
if (usernameRow) {
list.insertBefore(section, usernameRow);
} else {
list.appendChild(section);
}
}
function _compactOptionsMenu() {
var menu = document.getElementById('options-menu');
if (!menu) return;
menu.classList.add('mp-options-compact');
var list = document.getElementById('options-list');
if (!list) return;
var textareas = list.querySelectorAll('textarea');
for (var i = 0; i < textareas.length; i++) {
var ta = textareas[i];
ta.style.minWidth = '0';
ta.style.maxWidth = '100%';
ta.style.width = '100%';
ta.style.minHeight = '44px';
}
var settingsLabels = list.querySelectorAll('#mp-settings-section label');
for (var j = 0; j < settingsLabels.length; j++) {
if (settingsLabels[j].closest('#mp-panel-css-list')) {
settingsLabels[j].style.minWidth = '0';
settingsLabels[j].style.fontSize = '11px';
continue;
}
settingsLabels[j].style.minWidth = '130px';
settingsLabels[j].style.fontSize = '12px';
}
}
function _addNumberSetting(parent, label, key, min, max, disabled) {
var row = document.createElement('div');
row.className = 'menu-row';
row.id = 'mp-setting-row-' + key;
row.style.cssText = 'margin:0;display:flex;align-items:center;gap:6px;';
var lbl = document.createElement('label');
lbl.textContent = label;
lbl.style.cssText = 'color:#ccc;min-width:130px;font-size:12px;line-height:1.2;';
var input = document.createElement('input');
input.type = 'number';
input.min = min;
input.max = max;
input.value = _settings[key];
input.style.cssText = 'width:60px;padding:3px 5px;background:#333;color:#ccc;' +
'border:1px solid #555;border-radius:4px;font-size:12px;line-height:1.2;';
if (disabled) {
input.disabled = true;
input.style.opacity = '0.4';
}
input.addEventListener('change', function () {
var val = parseInt(input.value, 10);
if (isNaN(val)) val = DEFAULTS[key];
val = Math.max(min, Math.min(max, val));
input.value = val;
_saveSetting(key, val);
});
row.appendChild(lbl);
row.appendChild(input);
parent.appendChild(row);
}
function _addSliderSetting(parent, label, key, min, max) {
var row = document.createElement('div');
row.className = 'menu-row';
row.style.cssText = 'margin:0;display:flex;align-items:center;gap:6px;';
var lbl = document.createElement('label');
lbl.textContent = label;
lbl.style.cssText = 'color:#ccc;min-width:130px;font-size:12px;line-height:1.2;';
var slider = document.createElement('input');
slider.type = 'range';
slider.min = min;
slider.max = max;
slider.value = _settings[key];
slider.style.cssText = 'flex:1 1 auto;min-width:0;height:14px;';
var number = document.createElement('input');
number.type = 'number';
number.min = min;
number.max = max;
number.value = _settings[key];
number.style.cssText = 'width:60px;padding:3px 5px;background:#333;color:#ccc;border:1px solid #555;border-radius:4px;font-size:12px;line-height:1.2;';
function applyLive(nextValue) {
var val = parseInt(nextValue, 10);
if (isNaN(val)) val = DEFAULTS[key];
val = Math.max(min, Math.min(max, val));
slider.value = val;
number.value = val;
_saveSetting(key, val);
}
slider.addEventListener('input', function () { applyLive(slider.value); });
number.addEventListener('input', function () { applyLive(number.value); });
number.addEventListener('change', function () { applyLive(number.value); });
row.appendChild(lbl);
row.appendChild(slider);
row.appendChild(number);
parent.appendChild(row);
}
function _addSelectSetting(parent, label, key, options) {
var row = document.createElement('div');
row.className = 'menu-row';
row.style.cssText = 'margin:0;display:flex;align-items:center;gap:6px;';
var lbl = document.createElement('label');
lbl.textContent = label;
lbl.style.cssText = 'color:#ccc;min-width:130px;font-size:12px;line-height:1.2;';
var sel = document.createElement('select');
sel.style.cssText = 'padding:3px 5px;background:#333;color:#ccc;' +
'border:1px solid #555;border-radius:4px;font-size:12px;line-height:1.2;';
for (var i = 0; i < options.length; i++) {
var o = document.createElement('option');
o.value = options[i].value;
o.textContent = options[i].label;
if (_settings[key] === options[i].value) o.selected = true;
sel.appendChild(o);
}
sel.addEventListener('change', function () {
_saveSetting(key, sel.value);
});
row.appendChild(lbl);
row.appendChild(sel);
parent.appendChild(row);
}
function _addCheckboxSetting(parent, label, key, onChange) {
var row = document.createElement('div');
row.className = 'menu-row';
row.style.cssText = 'margin:0;display:flex;align-items:center;gap:6px;';
var cb = document.createElement('input');
cb.type = 'checkbox';
cb.checked = _settings[key];
cb.style.cssText = 'width:14px;height:14px;accent-color:#3366ff;';
var lbl = document.createElement('label');
lbl.textContent = label;
lbl.style.cssText = 'color:#ccc;font-size:12px;line-height:1.2;cursor:pointer;';
lbl.addEventListener('click', function () { cb.click(); });
cb.addEventListener('change', function () {
_saveSetting(key, cb.checked);
if (onChange) onChange(cb.checked);
});
row.appendChild(cb);
row.appendChild(lbl);
parent.appendChild(row);
}
function _onSettingsChanged(key) {
if (key === 'cellWidth' || key === 'cellHeight' || key === 'all') {
_repositionOutOfBoundsPanels();
}
_applyRoundedTabs(_settings.roundedTabs);
_applyPanelCssSettings();
_mpUpdateDisplay();
}
function _repositionOutOfBoundsPanels() {
_clampAllPanels();
// Resolve overlaps after clamping
var seen = {};
var toFix = [];
_gridState.panels.forEach(function (p) {
var conflict = false;
for (var r = p.row; r < p.row + p.rowSpan; r++) {
for (var c = p.col; c < p.col + p.colSpan; c++) {
var ck = r + ',' + c;
if (seen[ck]) { conflict = true; break; }
}
if (conflict) break;
}
if (conflict) {
toFix.push(p);
} else {
for (var r2 = p.row; r2 < p.row + p.rowSpan; r2++) {
for (var c2 = p.col; c2 < p.col + p.colSpan; c2++) {
seen[r2 + ',' + c2] = _getPanelPrimaryKey(p);
}
}
}
});
toFix.forEach(function (p) {
p.colSpan = 1;
p.rowSpan = 1;
var panelKey = _getPanelPrimaryKey(p);
var cell = _findFirstEmptyCell(panelKey);
if (cell) {
p.col = cell.col;
p.row = cell.row;
} else {
_gridState.panels = _gridState.panels.filter(function (pp) {
return pp !== p;
});
_forEachPanelModule(p, function (k) { stopModuleTimers(k); });
}
});
_saveGridState();
}
// =================================================================
// Wheel Scroll Routing (Hovered/Active Panel)
// =================================================================
var _lastWheelPanelKey = null;
function _isEditableWheelTarget(target) {
if (!(target instanceof Element)) return false;
return !!target.closest('input, textarea, select, [contenteditable="true"]');
}
function _isScrollableY(el) {
if (!(el instanceof HTMLElement)) return false;
var cs = window.getComputedStyle(el);
return /(auto|scroll|overlay)/.test(cs.overflowY || '');
}
function _canScrollY(el, deltaY) {
if (!(el instanceof HTMLElement)) return false;
if (el.scrollHeight <= el.clientHeight + 1) return false;
if (deltaY < 0) return el.scrollTop > 0;
if (deltaY > 0) return (el.scrollTop + el.clientHeight) < (el.scrollHeight - 1);
return false;
}
function _findScrollableAncestorForDelta(startEl, stopEl, deltaY) {
var cur = (startEl instanceof Element) ? startEl : null;
while (cur) {
if (_isScrollableY(cur) && _canScrollY(cur, deltaY)) return cur;
if (cur === stopEl || cur === document.body || cur === document.documentElement) break;
cur = cur.parentElement;
}
return null;
}
function _getPanelContentFromKey(dc, panelKey) {
if (!dc || !panelKey) return null;
var panelEl = dc.querySelector('.mp-panel[data-panel-key="' + panelKey + '"]');
return panelEl ? panelEl.querySelector('.mp-panel-content, .mp-panel-content-plain') : null;
}
function _resolveWheelTargetContent(dc, eventTarget) {
var panelEl = (eventTarget instanceof Element) ? eventTarget.closest('.mp-panel') : null;
if (panelEl) {
var panelKey = panelEl.getAttribute('data-panel-key');
if (panelKey) _lastWheelPanelKey = panelKey;
var contentEl = panelEl.querySelector('.mp-panel-content, .mp-panel-content-plain');
if (contentEl) return contentEl;
}
if (_lastWheelPanelKey) {
var lastContent = _getPanelContentFromKey(dc, _lastWheelPanelKey);
if (lastContent) return lastContent;
}
var hoveredPanel = dc ? dc.querySelector('.mp-panel:hover') : null;
if (hoveredPanel) return hoveredPanel.querySelector('.mp-panel-content, .mp-panel-content-plain');
return null;
}
function _initWheelScrollRouting(dc) {
if (!dc || dc.__mpWheelRoutingBound) return;
dc.__mpWheelRoutingBound = true;
dc.addEventListener('mousedown', function (e) {
var panel = e.target && e.target.closest ? e.target.closest('.mp-panel') : null;
if (!panel) return;
var panelKey = panel.getAttribute('data-panel-key');
if (panelKey) _lastWheelPanelKey = panelKey;
}, true);
dc.addEventListener('mouseover', function (e) {
var panel = e.target && e.target.closest ? e.target.closest('.mp-panel') : null;
if (!panel) return;
var panelKey = panel.getAttribute('data-panel-key');
if (panelKey) _lastWheelPanelKey = panelKey;
}, true);
dc.addEventListener('wheel', function (e) {
if (!e.deltaY || e.ctrlKey || _isEditableWheelTarget(e.target)) return;
var directScrollable = _findScrollableAncestorForDelta(e.target, dc, e.deltaY);
if (directScrollable) return;
var targetContent = _resolveWheelTargetContent(dc, e.target);
if (!(targetContent instanceof HTMLElement)) return;
if (!_canScrollY(targetContent, e.deltaY)) return;
e.preventDefault();
targetContent.scrollTop += e.deltaY;
}, { passive: false, capture: true });
}
// =================================================================
// Log Panel Collapse
// =================================================================
var LOG_COLLAPSED_KEY = 'ia2g_log_collapsed';
var LOG_WIDTH_KEY = 'ia2g_log_width';
var LOG_HEIGHT_KEY = 'ia2g_log_height';
var LOG_MIN_WIDTH = 60;
var LOG_MIN_HEIGHT = 70;
var PORTRAIT_QUERY = '(max-aspect-ratio: 1/1)';
function _isPortraitMode() {
return !!(window.matchMedia && window.matchMedia(PORTRAIT_QUERY).matches);
}
function _ensurePortraitControlsHost() {
var container = document.getElementById('container-wrapper');
var chatPanel = document.getElementById('bottom-chat-panel');
if (!container || !chatPanel) return null;
var host = document.getElementById('mp-portrait-controls');
if (!host) {
host = document.createElement('div');
host.id = 'mp-portrait-controls';
}
if (host.parentNode !== container || host.nextSibling !== chatPanel) {
container.insertBefore(host, chatPanel);
}
return host;
}
function _syncPortraitControlPlacement() {
var logToggle = document.getElementById('mp-log-toggle');
var chatBar = document.getElementById('mp-chat-collapse-bar');
var host = document.getElementById('mp-portrait-controls');
var portrait = _isPortraitMode();
if (portrait) {
host = _ensurePortraitControlsHost();
if (!host) return;
if (logToggle) {
var portraitLogContainer = document.getElementById('log-container');
if (portraitLogContainer && logToggle.parentNode !== portraitLogContainer) {
portraitLogContainer.insertBefore(logToggle, portraitLogContainer.firstChild);
}
}
if (chatBar && chatBar.parentNode !== host) host.appendChild(chatBar);
_attachBrpToLog();
return;
}
if (logToggle) {
var gameRow = document.getElementById('game-layout-row');
var logContainer = document.getElementById('log-container');
if (gameRow && logContainer && logToggle.parentNode !== gameRow) {
gameRow.insertBefore(logToggle, logContainer);
}
}
if (chatBar) {
var defaultChatBarParent = chatBar.__mpDefaultParent || document.getElementById('container-wrapper');
if (defaultChatBarParent && chatBar.parentNode !== defaultChatBarParent) {
defaultChatBarParent.appendChild(chatBar);
}
}
if (host && host.parentNode) host.parentNode.removeChild(host);
_attachBrpToLog();
}
function _initLogToggle() {
var logContainer = document.getElementById('log-container');
var gameRow = document.getElementById('game-layout-row');
if (!logContainer || !gameRow) return;
if (document.getElementById('mp-log-toggle')) return;
var btn = document.createElement('button');
btn.id = 'mp-log-toggle';
btn.title = 'Toggle log panel';
var collapsed = localStorage.getItem(LOG_COLLAPSED_KEY) === 'true';
function getSavedWidth() {
var w = parseInt(localStorage.getItem(LOG_WIDTH_KEY), 10);
return (w && w >= LOG_MIN_WIDTH) ? w : 0;
}
function getSavedHeight() {
var h = parseInt(localStorage.getItem(LOG_HEIGHT_KEY), 10);
return (h && h >= LOG_MIN_HEIGHT) ? h : 220;
}
function syncTogglePosition() {
if (_isPortraitMode()) {
btn.style.right = '';
btn.style.width = '';
btn.style.transform = '';
return;
}
var remainders = _getGridViewportRemainders();
var targetWidth = Math.max(_EDGE_TOGGLE_MIN_WIDTH, remainders.right);
var logWidth = collapsed ? 0 : logContainer.offsetWidth;
btn.style.right = Math.max(0, logWidth) + 'px';
btn.style.width = targetWidth + 'px';
btn.style.transform = 'none';
}
function applyState(animate) {
var portrait = _isPortraitMode();
if (!animate) {
logContainer.style.transition = 'none';
}
if (collapsed) {
logContainer.classList.add('mp-log-collapsed');
if (portrait) {
logContainer.style.flex = '0 0 0px';
logContainer.style.minHeight = '0';
logContainer.style.height = '0';
logContainer.style.minWidth = '';
btn.innerHTML = '▼';
} else {
logContainer.style.flex = '';
logContainer.style.minWidth = '';
logContainer.style.minHeight = '';
logContainer.style.height = '';
btn.innerHTML = '‹';
}
} else {
logContainer.classList.remove('mp-log-collapsed');
if (portrait) {
var savedH = clampHeight(getSavedHeight());
logContainer.style.flex = '0 0 ' + savedH + 'px';
logContainer.style.minHeight = savedH + 'px';
logContainer.style.height = savedH + 'px';
logContainer.style.minWidth = '';
btn.innerHTML = '▲';
} else {
var savedW = getSavedWidth();
if (savedW) {
logContainer.style.flex = '0 0 ' + savedW + 'px';
logContainer.style.minWidth = savedW + 'px';
} else {
logContainer.style.flex = '';
logContainer.style.minWidth = '';
}
logContainer.style.minHeight = '';
logContainer.style.height = '';
btn.innerHTML = '›';
}
}
if (!animate) {
logContainer.offsetHeight;
logContainer.style.transition = '';
}
syncTogglePosition();
if (animate) setTimeout(syncTogglePosition, 330);
clearTimeout(_resizeTimer);
var delay = animate ? 320 : 50;
_resizeTimer = setTimeout(function () {
_mpUpdateDisplay();
_scheduleLogFontRecalc(30);
}, delay);
}
function toggleCollapse() {
collapsed = !collapsed;
localStorage.setItem(LOG_COLLAPSED_KEY, String(collapsed));
applyState(true);
}
// --- Drag-to-resize logic ---
var dragState = null;
var DRAG_THRESHOLD = 3;
function clampWidth(w) {
var maxW = Math.floor(window.innerWidth * 0.6);
return _snapToGridAxis(w, 'x', LOG_MIN_WIDTH, maxW);
}
function clampHeight(h) {
var maxH = Math.floor(window.innerHeight * 0.55);
return _snapToGridAxis(h, 'y', LOG_MIN_HEIGHT, maxH);
}
function onDragMove(e) {
if (!dragState) return;
var clientCoord = dragState.axis === 'y'
? (e.touches ? e.touches[0].clientY : e.clientY)
: (e.touches ? e.touches[0].clientX : e.clientX);
var delta = dragState.startCoord - clientCoord;
if (!dragState.dragged && Math.abs(delta) < DRAG_THRESHOLD) return;
dragState.dragged = true;
if (dragState.axis === 'y') {
var newHeight = clampHeight(dragState.startSize + delta);
logContainer.style.flex = '0 0 ' + newHeight + 'px';
logContainer.style.minHeight = newHeight + 'px';
logContainer.style.height = newHeight + 'px';
} else {
var newWidth = clampWidth(dragState.startSize + delta);
logContainer.style.flex = '0 0 ' + newWidth + 'px';
logContainer.style.minWidth = newWidth + 'px';
}
syncTogglePosition();
}
function onDragEnd() {
if (!dragState) return;
var wasDragged = dragState.dragged;
var axis = dragState.axis;
var finalSize = (axis === 'y') ? logContainer.offsetHeight : logContainer.offsetWidth;
dragState = null;
logContainer.style.transition = '';
document.body.classList.remove('mp-log-resizing');
document.removeEventListener('mousemove', onDragMove);
document.removeEventListener('mouseup', onDragEnd);
document.removeEventListener('touchmove', onDragMove);
document.removeEventListener('touchend', onDragEnd);
document.removeEventListener('touchcancel', onDragEnd);
if (wasDragged) {
if (axis === 'y') {
var h = clampHeight(finalSize);
localStorage.setItem(LOG_HEIGHT_KEY, String(h));
logContainer.style.flex = '0 0 ' + h + 'px';
logContainer.style.minHeight = h + 'px';
logContainer.style.height = h + 'px';
} else {
var w = clampWidth(finalSize);
localStorage.setItem(LOG_WIDTH_KEY, String(w));
logContainer.style.flex = '0 0 ' + w + 'px';
logContainer.style.minWidth = w + 'px';
}
syncTogglePosition();
clearTimeout(_resizeTimer);
_resizeTimer = setTimeout(function () { _mpUpdateDisplay(); _recalcLogFontSize(); }, 50);
} else {
toggleCollapse();
}
}
function onDragStart(e) {
if (collapsed) {
toggleCollapse();
return;
}
var portrait = _isPortraitMode();
var clientCoord = portrait
? (e.touches ? e.touches[0].clientY : e.clientY)
: (e.touches ? e.touches[0].clientX : e.clientX);
dragState = {
axis: portrait ? 'y' : 'x',
startCoord: clientCoord,
startSize: portrait ? logContainer.offsetHeight : logContainer.offsetWidth,
dragged: false
};
logContainer.style.transition = 'none';
document.body.classList.add('mp-log-resizing');
if (e.touches) {
document.addEventListener('touchmove', onDragMove, { passive: false });
document.addEventListener('touchend', onDragEnd);
document.addEventListener('touchcancel', onDragEnd);
} else {
document.addEventListener('mousemove', onDragMove);
document.addEventListener('mouseup', onDragEnd);
}
e.preventDefault();
}
btn.addEventListener('mousedown', onDragStart);
btn.addEventListener('touchstart', onDragStart, { passive: false });
gameRow.insertBefore(btn, logContainer);
window.addEventListener('resize', function () { setTimeout(syncTogglePosition, 180); });
applyState(false);
requestAnimationFrame(syncTogglePosition);
setTimeout(_syncPortraitControlPlacement, 0);
_ensureLogFontResizeObserver();
_scheduleLogFontRecalc(30);
}
// =================================================================
// Bottom-Right Panel Collapse
// =================================================================
var BRP_COLLAPSED_KEY = 'ia2g_brp_collapsed';
var BRP_LOG_HOST_ID = 'mp-log-brp-host';
function _ensureBrpLogHost() {
var logContainer = document.getElementById('log-container');
if (!logContainer) return null;
var host = document.getElementById(BRP_LOG_HOST_ID);
if (!host) {
host = document.createElement('div');
host.id = BRP_LOG_HOST_ID;
}
if (host.parentNode !== logContainer || host !== logContainer.lastElementChild) {
logContainer.appendChild(host);
}
return host;
}
function _attachBrpToLog() {
var brp = document.getElementById('bottom-right-panel');
var host = _ensureBrpLogHost();
if (!brp || !host) return;
if (brp.parentNode !== host) host.appendChild(brp);
}
function _initBrpToggle() {
var brp = document.getElementById('bottom-right-panel');
if (!brp || document.getElementById('mp-brp-toggle')) return;
var collapsed = localStorage.getItem(BRP_COLLAPSED_KEY) === 'true';
var toggleBtn = document.createElement('button');
toggleBtn.id = 'mp-brp-toggle';
toggleBtn.title = 'Minimize panel';
function applyState() {
if (collapsed) {
brp.classList.add('mp-brp-collapsed');
toggleBtn.innerHTML = '▢';
toggleBtn.title = 'Restore panel';
} else {
brp.classList.remove('mp-brp-collapsed');
toggleBtn.innerHTML = '‒';
toggleBtn.title = 'Minimize panel';
}
}
toggleBtn.addEventListener('click', function (e) {
e.stopPropagation();
collapsed = !collapsed;
localStorage.setItem(BRP_COLLAPSED_KEY, String(collapsed));
applyState();
});
brp.insertBefore(toggleBtn, brp.firstChild);
applyState();
_attachBrpToLog();
}
// =================================================================
// BRP Adaptive Layout — always maximally condensed
// =================================================================
function _updateBrpLayout() {
var container = document.getElementById('xp-hunger-container');
if (!container) return;
container.classList.add('mp-brp-compact', 'mp-brp-minimal');
var buffs = document.getElementById('buffs-section');
var timers = document.getElementById('timers-section');
if (buffs && timers && timers.parentNode !== buffs) {
buffs.appendChild(timers);
}
}
function _setCollapseToggleVisual(toggleBtn, collapsed, expandTitle, collapseTitle) {
if (!toggleBtn) return;
toggleBtn.innerHTML = collapsed ? '▼' : '▲';
toggleBtn.title = collapsed ? expandTitle : collapseTitle;
}
function _applySectionCollapsedState(section, collapsedClass, collapsed, toggleBtn, expandTitle, collapseTitle) {
if (!section) return;
if (collapsed) {
section.classList.add(collapsedClass);
} else {
section.classList.remove(collapsedClass);
}
_setCollapseToggleVisual(toggleBtn, collapsed, expandTitle, collapseTitle);
}
function _togglePersistentCollapsedState(stateKey, collapsed, applyState) {
collapsed = !collapsed;
localStorage.setItem(stateKey, String(collapsed));
if (typeof applyState === 'function') applyState();
return collapsed;
}
// =================================================================
// Mastery Menu Collapse
// =================================================================
var MASTERY_COLLAPSED_KEY = 'ia2g_mastery_collapsed';
function _initMasteryToggle() {
var collapsed = localStorage.getItem(MASTERY_COLLAPSED_KEY) === 'true';
var _masterySummaryText = '';
function _getMasteryMenu() {
var boostsContainer = document.getElementById('mastery-boosts-container');
if (boostsContainer) {
return boostsContainer.closest('#mastery-menu') || document.getElementById('mastery-menu');
}
return document.getElementById('mastery-menu');
}
function _getMasteryPanelScrollTarget(menu) {
if (!menu) return null;
return menu.closest('.mp-panel-content, .mp-panel-content-plain');
}
function _ensureMasteryToggleElements(menu) {
if (!menu) return null;
var boostsContainer = menu.querySelector('#mastery-boosts-container') || document.getElementById('mastery-boosts-container');
if (!boostsContainer) return null;
var header = boostsContainer.previousElementSibling;
if (!header) return null;
var summarySpan = document.getElementById('mp-mastery-compact-summary');
if (!summarySpan) {
summarySpan = document.createElement('span');
summarySpan.id = 'mp-mastery-compact-summary';
summarySpan.textContent = 'SOM 0 | Spent 0';
}
if (summarySpan.parentNode !== header) header.appendChild(summarySpan);
var toggleBtn = document.getElementById('mp-mastery-toggle');
if (!toggleBtn) {
toggleBtn = document.createElement('button');
toggleBtn.id = 'mp-mastery-toggle';
}
if (toggleBtn.parentNode !== header) header.appendChild(toggleBtn);
return {
summarySpan: summarySpan,
toggleBtn: toggleBtn
};
}
function updateCompactSummary(summarySpan) {
if (!summarySpan) return;
var somEl = document.getElementById('mastery-soms-available');
var spentEl = document.getElementById('mastery-total-spent');
var somText = somEl ? String(somEl.textContent || '').trim() : '0';
var spentText = spentEl ? String(spentEl.textContent || '').trim() : '0';
var nextText = 'SOM ' + somText + ' | Spent ' + spentText;
if (_masterySummaryText !== nextText) {
_masterySummaryText = nextText;
summarySpan.textContent = nextText;
}
}
function applyState() {
collapsed = localStorage.getItem(MASTERY_COLLAPSED_KEY) === 'true';
var menu = _getMasteryMenu();
var parts = _ensureMasteryToggleElements(menu);
if (!menu || !parts) return;
if (!parts.toggleBtn.__ia2gMasteryToggleBound) {
parts.toggleBtn.addEventListener('click', function (e) {
e.stopPropagation();
e.preventDefault();
collapsed = _togglePersistentCollapsedState(MASTERY_COLLAPSED_KEY, collapsed, applyState);
});
parts.toggleBtn.__ia2gMasteryToggleBound = true;
}
var scrollTarget = _getMasteryPanelScrollTarget(menu);
var previousScrollTop = scrollTarget ? scrollTarget.scrollTop : null;
_applySectionCollapsedState(
menu,
'mp-mastery-collapsed',
collapsed,
parts.toggleBtn,
'Show mastery details',
'Hide mastery details'
);
updateCompactSummary(parts.summarySpan);
if (scrollTarget && previousScrollTop !== null) {
requestAnimationFrame(function () {
scrollTarget.scrollTop = previousScrollTop;
});
}
}
applyState();
var origRenderMasteryMenu = typeof renderMasteryMenu === 'function' ? renderMasteryMenu : null;
if (origRenderMasteryMenu && !origRenderMasteryMenu.__ia2gMasteryCompactWrapped) {
var wrappedRenderMasteryMenu = function () {
origRenderMasteryMenu.apply(this, arguments);
applyState();
};
wrappedRenderMasteryMenu.__ia2gMasteryCompactWrapped = true;
renderMasteryMenu = wrappedRenderMasteryMenu;
}
}
// =================================================================
// Mercenary Post Collapse
// =================================================================
var MERC_COLLAPSED_KEY = 'ia2g_merc_collapsed';
function _initMercPostToggle() {
var section = document.getElementById('mercenary-post-section');
if (!section || document.getElementById('mp-merc-toggle')) return;
var h3 = section.querySelector('h3');
if (!h3) return;
var collapsed = localStorage.getItem(MERC_COLLAPSED_KEY) === 'true';
var hireBtn = document.createElement('button');
hireBtn.id = 'mp-merc-compact-hire';
hireBtn.textContent = 'Hire';
hireBtn.addEventListener('click', function (e) {
e.stopPropagation();
if (typeof hireMercenary === 'function') hireMercenary();
});
var statusSpan = document.createElement('span');
statusSpan.id = 'mp-merc-compact-status';
statusSpan.textContent = 'Inactive';
var toggleBtn = document.createElement('button');
toggleBtn.id = 'mp-merc-toggle';
h3.appendChild(statusSpan);
h3.appendChild(hireBtn);
h3.appendChild(toggleBtn);
function updateCompact() {
var ticks = parseInt((typeof gameState !== 'undefined' && gameState.merc_ticks_remaining) || 0);
var contractType = (typeof gameState !== 'undefined' && gameState.auto_contract_type) || '';
if (contractType === 'disabled') contractType = '';
if (ticks > 0 && contractType) {
var mins = Math.ceil(ticks / 30);
var label;
if (mins < 60) {
label = mins + 'm';
} else {
var h = Math.floor(mins / 60);
var m = mins % 60;
label = m > 0 ? h + 'h ' + m + 'm' : h + 'h';
}
var ct = contractType.charAt(0).toUpperCase() + contractType.slice(1);
statusSpan.textContent = ct + ': ' + label + ' left';
statusSpan.style.color = '#4CAF50';
hireBtn.textContent = 'Update';
hireBtn.style.background = '#2196F3';
} else {
statusSpan.textContent = 'Inactive';
statusSpan.style.color = '#ccc';
hireBtn.textContent = 'Hire';
hireBtn.style.background = '#4CAF50';
}
}
function applyState() {
_applySectionCollapsedState(
section,
'mp-merc-collapsed',
collapsed,
toggleBtn,
'Show mercenary details',
'Hide mercenary details'
);
if (collapsed) {
updateCompact();
}
}
toggleBtn.addEventListener('click', function (e) {
e.stopPropagation();
collapsed = _togglePersistentCollapsedState(MERC_COLLAPSED_KEY, collapsed, applyState);
});
applyState();
// Keep compact view in sync with game state updates
var origUpdateMercUI = typeof updateMercenaryPostUI === 'function' ? updateMercenaryPostUI : null;
if (origUpdateMercUI) {
updateMercenaryPostUI = function () {
origUpdateMercUI.apply(this, arguments);
if (collapsed) updateCompact();
};
}
}
// =================================================================
// Tithe Barn Collapse
// =================================================================
var TITHE_COLLAPSED_KEY = 'ia2g_tithe_collapsed';
function _initTitheToggle() {
var section = document.getElementById('tithe-barn-section');
if (!section || document.getElementById('mp-tithe-toggle')) return;
var h3 = section.querySelector('h3');
if (!h3) return;
var collapsed = localStorage.getItem(TITHE_COLLAPSED_KEY) === 'true';
var startBtn = document.createElement('button');
startBtn.id = 'mp-tithe-compact-start';
startBtn.textContent = 'Start';
startBtn.addEventListener('click', function (e) {
e.stopPropagation();
if (typeof startAutoFarm === 'function') startAutoFarm();
});
var statusSpan = document.createElement('span');
statusSpan.id = 'mp-tithe-compact-status';
statusSpan.textContent = 'Inactive';
var toggleBtn = document.createElement('button');
toggleBtn.id = 'mp-tithe-toggle';
h3.appendChild(statusSpan);
h3.appendChild(startBtn);
h3.appendChild(toggleBtn);
function updateCompact() {
var ticks = parseInt((typeof gameState !== 'undefined' && gameState.auto_farm_ticks_remaining) || 0);
var crop = (typeof gameState !== 'undefined' && gameState.auto_farm_preferred_crop) || '';
if (ticks > 0) {
var mins = Math.ceil(ticks / 30);
var label;
if (mins < 60) {
label = mins + 'm';
} else {
var hr = Math.floor(mins / 60);
var m = mins % 60;
label = m > 0 ? hr + 'h ' + m + 'm' : hr + 'h';
}
var cropLabel = crop ? crop.charAt(0).toUpperCase() + crop.slice(1) : 'Active';
statusSpan.textContent = cropLabel + ': ' + label + ' left';
statusSpan.style.color = '#4CAF50';
startBtn.textContent = 'Update';
startBtn.style.background = '#2196F3';
} else {
statusSpan.textContent = 'Inactive';
statusSpan.style.color = '#ccc';
startBtn.textContent = 'Start';
startBtn.style.background = '#4CAF50';
}
}
function applyState() {
_applySectionCollapsedState(
section,
'mp-tithe-collapsed',
collapsed,
toggleBtn,
'Show tithe barn details',
'Hide tithe barn details'
);
if (collapsed) {
updateCompact();
}
}
toggleBtn.addEventListener('click', function (e) {
e.stopPropagation();
collapsed = _togglePersistentCollapsedState(TITHE_COLLAPSED_KEY, collapsed, applyState);
});
applyState();
var origUpdateTitheUI = typeof updateTitheBarnUI === 'function' ? updateTitheBarnUI : null;
if (origUpdateTitheUI) {
updateTitheBarnUI = function () {
origUpdateTitheUI.apply(this, arguments);
if (collapsed) updateCompact();
};
}
}
// =================================================================
// Chat Panel Restructure & Collapse
// =================================================================
var CHAT_COLLAPSED_KEY = 'ia2g_chat_collapsed';
var CHAT_HEIGHT_KEY = 'ia2g_chat_height';
var CHAT_DEFAULT_HEIGHT = 195;
var CHAT_MIN_HEIGHT = 60;
function _initChatRestructure() {
var chatPanel = document.getElementById('bottom-chat-panel');
var chatSidebar = document.getElementById('chat-sidebar');
var chatMain = document.getElementById('chat-main');
if (!chatPanel || !chatSidebar || !chatMain) return;
var _chatLayoutSyncTimer = null;
var _lastObservedChatHeight = -1;
var _isChatDragResizing = false;
var containerW = document.getElementById('container-wrapper');
function _detectInitialGameHiddenState() {
var style = window.getComputedStyle(chatPanel);
if (style && (style.display === 'none' || style.visibility === 'hidden')) return true;
if (chatPanel.classList.contains('collapsed')) return true;
var nativeHideBtn = document.getElementById('btn-chat-hide');
var label = nativeHideBtn ? String(nativeHideBtn.textContent || nativeHideBtn.innerText || '').trim().toLowerCase() : '';
return label === 'show';
}
var startedHiddenByGame = _detectInitialGameHiddenState();
// Remove game's collapsed class — we use our own mechanism
chatPanel.classList.remove('collapsed');
if (containerW) containerW.classList.remove('chat-collapsed');
// If script is installed while vanilla chat is hidden, ensure the panel exists
// in layout so our external collapse bar can still provide a way to re-open it.
if (startedHiddenByGame) {
chatPanel.style.display = '';
chatPanel.style.visibility = '';
}
// Wrap existing children in a flex-row container
var contentRow = document.createElement('div');
contentRow.id = 'mp-chat-content-row';
contentRow.appendChild(chatSidebar);
contentRow.appendChild(chatMain);
// Create collapse bar
var bar = document.createElement('div');
bar.id = 'mp-chat-collapse-bar';
bar.title = 'Toggle chat panel';
// Move the game's population display into the bar
var popDiv = chatSidebar.querySelector('#online-count-display');
if (popDiv) popDiv = popDiv.parentElement;
if (popDiv) {
popDiv.id = 'mp-bar-pop';
popDiv.style.marginBottom = '0';
bar.appendChild(popDiv);
}
var barArrow = document.createElement('span');
bar.appendChild(barArrow);
// Rebuild panel as column: bar on top, content row below
chatPanel.appendChild(contentRow);
var barHost = containerW || chatPanel.parentElement || chatPanel;
barHost.appendChild(bar);
bar.__mpDefaultParent = barHost;
// Re-scroll chat to bottom (DOM move resets scrollTop to 0)
var chatArea = document.getElementById('chat-messages-area');
if (chatArea) chatArea.scrollTop = chatArea.scrollHeight;
// Determine initial state from IA2G-specific key only
var stored = localStorage.getItem(CHAT_COLLAPSED_KEY);
var collapsed = stored === 'true' || startedHiddenByGame;
if (startedHiddenByGame && stored !== 'true') {
localStorage.setItem(CHAT_COLLAPSED_KEY, 'true');
}
function getSavedHeight() {
var h = parseInt(localStorage.getItem(CHAT_HEIGHT_KEY), 10);
return (h && h >= CHAT_MIN_HEIGHT) ? h : CHAT_DEFAULT_HEIGHT;
}
function _isChatHiddenByGame() {
if (!chatPanel) return true;
var style = window.getComputedStyle(chatPanel);
if (!style) return false;
if (style.display === 'none' || style.visibility === 'hidden') return true;
if (chatPanel.classList.contains('collapsed')) return true;
// If vanilla hide button says "Show" while IA2G chat is not collapsed,
// trust vanilla state and hide our collapse bar too.
if (!collapsed) {
var nativeHideBtn = document.getElementById('btn-chat-hide');
var label = nativeHideBtn ? String(nativeHideBtn.textContent || nativeHideBtn.innerText || '').trim().toLowerCase() : '';
if (label === 'show') return true;
}
return false;
}
function syncChatBarPosition(explicitHeight) {
if (_isChatHiddenByGame()) {
bar.style.display = 'none';
document.documentElement.style.setProperty('--mp-chat-bar-reserve', '0px');
return;
}
bar.style.display = 'flex';
var portraitHost = document.getElementById('mp-portrait-controls');
if (portraitHost && bar.parentNode === portraitHost) {
bar.style.bottom = '';
bar.style.height = '';
bar.style.transform = '';
document.documentElement.style.setProperty('--mp-chat-bar-reserve', '0px');
return;
}
var remainders = _getGridViewportRemainders();
var targetHeight = Math.max(_EDGE_TOGGLE_MIN_HEIGHT, remainders.bottom);
var chatHeight = (typeof explicitHeight === 'number') ? explicitHeight : chatPanel.offsetHeight;
bar.style.bottom = Math.max(0, Math.round(chatHeight)) + 'px';
bar.style.height = targetHeight + 'px';
bar.style.transform = 'none';
document.documentElement.style.setProperty('--mp-chat-bar-reserve', Math.max(0, Math.round(targetHeight)) + 'px');
}
function _scheduleChatLayoutSync(delay) {
if (_isChatDragResizing) return;
if (_chatLayoutSyncTimer) clearTimeout(_chatLayoutSyncTimer);
_chatLayoutSyncTimer = setTimeout(function () {
if (_isChatDragResizing) return;
syncChatBarPosition();
_mpUpdateDisplay();
_updateBrpLayout();
}, (typeof delay === 'number') ? delay : 50);
}
function applyState() {
var hideBtn = document.getElementById('btn-chat-hide');
var container = document.getElementById('container-wrapper');
if (collapsed) {
chatPanel.classList.add('mp-chat-collapsed');
chatPanel.style.height = '';
if (container) container.classList.add('chat-collapsed');
if (hideBtn) hideBtn.innerText = 'Show';
barArrow.innerHTML = '▲';
bar.title = 'Expand chat panel';
syncChatBarPosition(0);
} else {
chatPanel.classList.remove('mp-chat-collapsed');
if (container) container.classList.remove('chat-collapsed');
if (hideBtn) hideBtn.innerText = 'Hide';
barArrow.innerHTML = '▼';
bar.title = 'Collapse chat panel';
var initialH = clampHeight(getSavedHeight());
chatPanel.style.height = initialH + 'px';
syncChatBarPosition(initialH);
var chatArea = document.getElementById('chat-messages-area');
if (chatArea) setTimeout(function () { chatArea.scrollTop = chatArea.scrollHeight; }, 50);
}
syncChatBarPosition();
requestAnimationFrame(syncChatBarPosition);
setTimeout(syncChatBarPosition, 360);
clearTimeout(_resizeTimer);
_resizeTimer = setTimeout(_mpUpdateDisplay, 50);
setTimeout(_updateBrpLayout, 60);
_scheduleChatLayoutSync(120);
setTimeout(function () { _scheduleChatLayoutSync(0); }, 520);
}
function toggleCollapse() {
collapsed = !collapsed;
localStorage.setItem(CHAT_COLLAPSED_KEY, String(collapsed));
applyState();
}
// --- Drag-to-resize logic ---
var dragState = null;
var DRAG_THRESHOLD = 3;
function clampHeight(h) {
var maxH = Math.floor(window.innerHeight * 0.7);
return _snapToGridAxis(h, 'y', CHAT_MIN_HEIGHT, maxH);
}
function onDragMove(e) {
if (!dragState) return;
var clientY = e.touches ? e.touches[0].clientY : e.clientY;
var delta = dragState.startY - clientY;
if (!dragState.dragged && Math.abs(delta) < DRAG_THRESHOLD) return;
dragState.dragged = true;
var newHeight = clampHeight(dragState.startHeight + delta);
chatPanel.style.height = newHeight + 'px';
syncChatBarPosition(newHeight);
}
function onDragEnd(e) {
if (!dragState) return;
var state = dragState;
var wasDragged = state.dragged;
var finalHeight = chatPanel.offsetHeight;
dragState = null;
_isChatDragResizing = false;
chatPanel.style.transition = '';
document.body.classList.remove('mp-chat-resizing');
document.removeEventListener('mousemove', onDragMove);
document.removeEventListener('mouseup', onDragEnd);
document.removeEventListener('touchmove', onDragMove);
document.removeEventListener('touchend', onDragEnd);
document.removeEventListener('touchcancel', onDragEnd);
if (wasDragged) {
var h = clampHeight(finalHeight);
localStorage.setItem(CHAT_HEIGHT_KEY, String(h));
chatPanel.style.height = h + 'px';
syncChatBarPosition(h);
clearTimeout(_resizeTimer);
_resizeTimer = null;
_mpUpdateDisplay();
_updateBrpLayout();
} else {
toggleCollapse();
}
}
function onDragStart(e) {
if (collapsed) {
toggleCollapse();
return;
}
var clientY = e.touches ? e.touches[0].clientY : e.clientY;
dragState = {
startY: clientY,
startHeight: chatPanel.offsetHeight,
dragged: false
};
if (_chatLayoutSyncTimer) {
clearTimeout(_chatLayoutSyncTimer);
_chatLayoutSyncTimer = null;
}
_isChatDragResizing = true;
chatPanel.style.transition = 'none';
document.body.classList.add('mp-chat-resizing');
if (e.touches) {
document.addEventListener('touchmove', onDragMove, { passive: false });
document.addEventListener('touchend', onDragEnd);
document.addEventListener('touchcancel', onDragEnd);
} else {
document.addEventListener('mousemove', onDragMove);
document.addEventListener('mouseup', onDragEnd);
}
e.preventDefault();
}
bar.addEventListener('mousedown', onDragStart);
bar.addEventListener('touchstart', onDragStart, { passive: false });
window.addEventListener('resize', function () { setTimeout(syncChatBarPosition, 180); });
window.addEventListener('load', function () { _scheduleChatLayoutSync(120); });
chatPanel.addEventListener('transitionend', function (e) {
if (!e || e.propertyName === 'height') syncChatBarPosition();
});
var nativeHideBtn = document.getElementById('btn-chat-hide');
if (nativeHideBtn) {
nativeHideBtn.addEventListener('click', function () {
// Let vanilla handler run first, then sync our bar visibility.
setTimeout(syncChatBarPosition, 0);
setTimeout(syncChatBarPosition, 120);
});
}
if (typeof ResizeObserver !== 'undefined') {
var chatResizeObserver = new ResizeObserver(function () {
if (_isChatDragResizing) return;
var h = chatPanel.offsetHeight;
if (h === _lastObservedChatHeight) return;
_lastObservedChatHeight = h;
_scheduleChatLayoutSync(80);
});
chatResizeObserver.observe(chatPanel);
}
if (typeof MutationObserver !== 'undefined') {
var chatVisibilityObserver = new MutationObserver(function () {
syncChatBarPosition();
});
chatVisibilityObserver.observe(chatPanel, {
attributes: true,
attributeFilter: ['class', 'style']
});
if (containerW) {
chatVisibilityObserver.observe(containerW, {
attributes: true,
attributeFilter: ['class', 'style']
});
}
}
// Override game's toggleChatCollapse to use our mechanism
toggleChatCollapse = function () {
toggleCollapse();
};
applyState();
requestAnimationFrame(syncChatBarPosition);
setTimeout(_syncPortraitControlPlacement, 0);
}
// =================================================================
// Window Resize Handler
// =================================================================
var _resizeTimer = null;
function _onWindowResize() {
if (_resizeTimer) clearTimeout(_resizeTimer);
_resizeTimer = setTimeout(function () {
_hideAddTileMenu();
_hidePanelTabAddMenu();
_repositionOutOfBoundsPanels();
_attachBrpToLog();
_mpUpdateDisplay();
_updateBrpLayout();
_syncPortraitControlPlacement();
_scheduleLogFontRecalc(30);
}, 150);
}
// =================================================================
// Rounded Tabs Toggle
// =================================================================
function _applyRoundedTabs(rounded) {
if (rounded) {
document.body.classList.remove('mp-square-tabs');
} else {
document.body.classList.add('mp-square-tabs');
}
}
// =================================================================
// Log Font Auto-Sizing
// =================================================================
var _logMinFont = 100;
var _logFontRecalcTimer = null;
var _logFontResizeObserver = null;
function _scheduleLogFontRecalc(delay) {
if (_logFontRecalcTimer) clearTimeout(_logFontRecalcTimer);
_logFontRecalcTimer = setTimeout(_recalcLogFontSize, (typeof delay === 'number' ? delay : 0));
}
function _ensureLogFontResizeObserver() {
if (_logFontResizeObserver || typeof ResizeObserver === 'undefined') return;
var container = document.getElementById('log-messages');
if (!container) return;
_logFontResizeObserver = new ResizeObserver(function () {
_scheduleLogFontRecalc(30);
});
_logFontResizeObserver.observe(container);
}
function _recalcLogFontSize() {
var container = document.getElementById('log-messages');
if (!container) return;
container.style.setProperty('--log-font-size', '100%');
_logMinFont = 100;
var entries = container.querySelectorAll('.log-entry');
for (var i = 0; i < entries.length; i++) {
if (entries[i].style.whiteSpace === 'normal') continue;
if (entries[i].scrollWidth > entries[i].clientWidth) {
var r = Math.max(entries[i].clientWidth / entries[i].scrollWidth * 100, 60);
if (r < _logMinFont) _logMinFont = r;
}
}
if (_logMinFont < 100) {
container.style.setProperty('--log-font-size', _logMinFont + '%');
}
}
var CONCLAVE_MGMT_COLLAPSE_PREFIX = 'ia2g_conclave_mgmt_';
function _normalizeConclaveSectionKey(text) {
return String(text || '')
.toLowerCase()
.replace(/[^a-z0-9]+/g, '_')
.replace(/^_+|_+$/g, '') || 'section';
}
function _bindConclaveSectionToggle(section, sectionName, defaultCollapsed) {
if (!section) return;
var heading = section.querySelector('h3');
if (!heading) return;
var sectionKey = _normalizeConclaveSectionKey(sectionName);
var stateKey = CONCLAVE_MGMT_COLLAPSE_PREFIX + sectionKey;
var storedValue = localStorage.getItem(stateKey);
var collapsed = storedValue === null ? !!defaultCollapsed : storedValue === 'true';
var toggleBtn = heading.querySelector('.mp-conclave-collapse-toggle');
if (!toggleBtn) {
toggleBtn = document.createElement('button');
toggleBtn.className = 'mp-conclave-collapse-toggle';
heading.appendChild(toggleBtn);
}
function applyState() {
_applySectionCollapsedState(
section,
'mp-conclave-section-collapsed',
collapsed,
toggleBtn,
'Show section',
'Hide section'
);
}
if (!toggleBtn.__ia2gConclaveToggleBound) {
toggleBtn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
collapsed = _togglePersistentCollapsedState(stateKey, collapsed, applyState);
});
toggleBtn.__ia2gConclaveToggleBound = true;
}
applyState();
}
function _applyConclaveManagementCompaction() {
var root = document.getElementById('conclave-management-content');
if (!root) return;
var sections = root.children;
for (var i = 0; i < sections.length; i++) {
var section = sections[i];
if (!section || section.nodeType !== 1) continue;
var heading = section.querySelector('h3');
if (!heading) continue;
var title = String(heading.textContent || '').trim();
var lower = title.toLowerCase();
var defaultCollapsed =
lower.indexOf('icon unlock') !== -1 ||
lower.indexOf('pending application') !== -1 ||
lower.indexOf('leadership transfer') !== -1 ||
lower.indexOf('member management') !== -1;
_bindConclaveSectionToggle(section, title, defaultCollapsed);
}
}
function _applyConclaveCompactState() {
var menu = document.getElementById('conclave-menu');
if (!menu) return;
menu.classList.add('mp-conclave-compact');
_applyConclaveManagementCompaction();
}
function _wrapConclaveRendererByName(fnName) {
if (typeof window === 'undefined') return;
var original = window[fnName];
if (typeof original !== 'function' || original.__ia2gConclaveCompactWrapped) return;
var wrapped = function () {
var result = original.apply(this, arguments);
_applyConclaveCompactState();
return result;
};
wrapped.__ia2gConclaveCompactWrapped = true;
window[fnName] = wrapped;
}
function _initConclaveCompact() {
_wrapConclaveRendererByName('renderConclaveMenu');
_wrapConclaveRendererByName('renderConclaveOverview');
_wrapConclaveRendererByName('renderConclaveBuildings');
_wrapConclaveRendererByName('renderConclaveManagement');
_applyConclaveCompactState();
}
// =================================================================
// Patch Game Functions (diagnostics + fixes)
// =================================================================
function _patchGameFunctions() {
// High-frequency debug wrappers can cause visible UI jank; keep disabled by default.
var DEBUG_GAME_WRAPPERS = false;
if (DEBUG_GAME_WRAPPERS) {
// Wrap moveItems to log calls and catch errors
if (typeof moveItems === 'function') {
var origMoveItems = moveItems;
moveItems = function (ids, target, qty) {
_prodLog('[MP] moveItems called:', { ids: ids, target: target, qty: qty });
try {
return origMoveItems.call(this, ids, target, qty);
} catch (e) {
console.error('[MP] moveItems error:', e);
}
};
}
// Wrap queueBundledRequest to log calls
if (typeof queueBundledRequest === 'function') {
var origQueue = queueBundledRequest;
queueBundledRequest = function (action, data) {
_prodLog('[MP] queueBundledRequest:', action, data);
try {
return origQueue.call(this, action, data);
} catch (e) {
console.error('[MP] queueBundledRequest error:', e);
}
};
}
// Wrap syncWithServer to log when bundled requests are sent
if (typeof syncWithServer === 'function') {
var origSync = syncWithServer;
syncWithServer = function (action, extraData) {
if (!action && typeof pendingRequests !== 'undefined' && pendingRequests.length > 0) {
_prodLog('[MP] force_sync sending', pendingRequests.length, 'bundled requests:', pendingRequests.map(function (r) { return r.action; }));
}
try {
return origSync.apply(this, arguments);
} catch (e) {
console.error('[MP] syncWithServer error:', e);
}
};
}
}
// Prevent game's MutationObserver from adding close buttons inside panels
if (typeof addCloseButtonsToMenus === 'function') {
var origAddClose = addCloseButtonsToMenus;
addCloseButtonsToMenus = function () {
origAddClose.call(this);
// Immediately remove any close buttons inside panel content
var panelCloses = document.querySelectorAll('.mp-panel-content .menu-close-btn, .mp-panel-content-plain .menu-close-btn');
for (var i = 0; i < panelCloses.length; i++) {
panelCloses[i].remove();
}
};
}
// =============================================================
// Log Message Shorteners
// =============================================================
function _stripHtmlText(s) {
return String(s || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
}
function _compactInlineHtml(s) {
return String(s || '')
.replace(/<(?!\/?span\b)[^>]*>/gi, ' ')
.replace(/\s+/g, ' ')
.replace(/>\s+</g, '><')
.trim();
}
function _shortenOfflineSummary(message) {
if (typeof message !== 'string') return message;
if (!/Offline Summary|While you were away/i.test(message)) return message;
var entries = [];
var re = /<div>\s*([+\-]?[\d,.]+(?:\.\d+)?)\s*([^<]+?)\s*<\/div>/gi;
var m;
while ((m = re.exec(message)) !== null) {
var value = (m[1] || '').trim();
var label = _stripHtmlText(m[2] || '');
if (!value || !label) continue;
entries.push(value + ' ' + label);
}
return entries.length ? ('Offline: ' + entries.join(', ')) : message;
}
var LOG_SHORTENERS = [
// World boss spawns
{ pattern: /darkens the coastal waters.*Leviathan/i, replacement: 'Spawn: Leviathan' },
{ pattern: /ancient forest groans.*Treant/i, replacement: 'Spawn: Ancient Treant' },
{ pattern: /low hum echoes.*Runic Golem/i, replacement: 'Spawn: Runic Golem' },
{ pattern: /war horn.*wastelands.*Warlord/i, replacement: 'Spawn: Warlord' },
{ pattern: /chilling howl.*Alpha Wolf has begun/i, replacement: 'Spawn: Alpha Wolf' },
{ pattern: /grinding gears.*Clatter-Gnash.*assembled/i, replacement: 'Spawn: Clatter-Gnash' },
// World boss despawns
{ pattern: /flick of its colossal tail.*Leviathan dives/i, replacement: 'Despawn: Leviathan' },
{ pattern: /Clatter-Gnash falls apart into a heap/i, replacement: 'Despawn: Clatter-Gnash' },
{ pattern: /(?:^|.*?\b)(Alpha Wolf|Ancient Treant|Runic Golem|Warlord)\b.*(?:will return|vow to return|will slumber).*$/i, replacement: function (m) { return 'Despawn: ' + m[1]; } },
// Dungeon parties
{
pattern: /Dungeon Party Formed\**(?:\s|<br\s*\/?>)*(.+?)\s+has created a\s+(?:Level|Lvl)\s*(\d+)\s+Dungeon Party/i,
replacement: function (m) {
return '🏰 ' + (m[1] || '').trim() + ' L' + m[2];
}
},
// Merchant
{ pattern: /The Merchant has arrived! A (.+?) is up for auction/i, replacement: function (m) { return 'Merchant: ' + m[1]; } },
{ pattern: /The Merchant has left\. The (.+?) was not sold\./i, replacement: function (m) { return 'Merchant left; ' + m[1] + ' unsold.'; } },
// Thief
{ pattern: /A thief is lurking in the shadows/i, replacement: 'Thief incoming...' },
{ pattern: /The Thief struck! A (.+?) was stolen from (\S+?)(?:'s)? stash/i, replacement: function (m) { return '🥷 ' + m[1] + ' ← ' + m[2]; } },
{ pattern: /The Town Watch arrested the Thief!.*Join the Raid in the Dungeon Lobby!?/i, replacement: 'Thief arrested!' },
// Leprechaun / rainbow
{ pattern: /magical rainbow.*Leprechaun's Wager has begun/i, replacement: "🍀 Leprechaun's Wager has begun!" },
{
pattern: /rainbow has faded! (\S+) found the pot of gold and won (.+)$/i,
replacement: function (m) {
var rewardText = _stripHtmlText(m[2]);
var amountMatch = rewardText.match(/([+\-]?[\d,.]+(?:\.\d+)?)/);
if (amountMatch) return m[1] + ' +' + amountMatch[1] + ' 🍀';
return m[1] + ' ' + _compactInlineHtml(m[2]) + ' 🍀';
}
},
{
pattern: /^You won The Leprechaun's Wager!\s*You received\s+(.+?)\.?$/i,
replacement: function (m) {
var rewardText = _stripHtmlText(m[1]);
var amountMatch = rewardText.match(/([+\-]?[\d,.]+(?:\.\d+)?)/);
if (amountMatch) return 'You won +' + amountMatch[1] + ' 🍀';
return 'You won 🍀';
}
},
// Tournaments
{ pattern: /The Artisan Tournament for (\S+) has begun.*$/i, replacement: function (m) { return 'Tournament: ' + m[1] + ' started'; } },
{ pattern: /Tournament Results \((\w+)\): (.+)$/i, replacement: function (m) { return 'Tournament: ' + m[1] + ' ended<br>' + m[2].split(', ').join('<br>'); } },
{
pattern: /^You placed #?(\d+)\s+in the\s+(.+?)\s+Tournament!\s+You have been awarded:\s+(.+?)\.?$/i,
replacement: function (m) {
var position = m[1];
var rewardText = _stripHtmlText(m[3]);
var amountMatch = rewardText.match(/([+\-]?[\d,.]+(?:\.\d+)?)/);
var rewardName = amountMatch ? rewardText.replace(amountMatch[0], '').trim() : rewardText;
rewardName = rewardName.replace(/^\+/, '').trim();
if (amountMatch && rewardName) return 'You placed #' + position + '<br>+ ' + amountMatch[1] + ' ' + rewardName;
if (amountMatch) return 'You placed #' + position + '<br>+ ' + amountMatch[1];
return 'You placed #' + position + '<br>+ ' + rewardText;
}
},
// Great Smeltdown
{ pattern: /The Great Smeltdown has begun!.*calling for (.+?) to fuel/i, replacement: function (m) { return 'Great Smeltdown begun! (' + m[1] + ')'; } },
{ pattern: /The Great Smeltdown has ended\..*$/i, replacement: 'Great Smeltdown ended.' },
// Dungeon Delve
{ pattern: /The Mayor has declared a week of exploration!.*double Artisan(?:'|’)?s Marks.*every room cleared\./i, replacement: 'Dungeon Delve started' },
{ pattern: /The Dungeon Delve event has ended\.\s*Rewards return to normal\./i, replacement: 'Dungeon Delve ended.' },
// Eating
{ pattern: /^You ate\s+([\d,]+)\s+\S+\s+Rations?\s+and your hunger is now\s+(.+)$/i, replacement: function (m) { return 'Ate ' + m[1] + ' rations'; } },
// Farming (generic crops/items)
{
pattern: /^(?:\[Farm\]\s*)?Harvested\s+([\d,]+)\s+(.+?)\s+and gained\s+([\d,]+)\s+\S+\s+XP\.?$/i,
replacement: function (m) { return 'Harvested ' + m[1] + ' ' + m[2].replace(/\(s\)/gi, '').trim() + ' (+' + m[3] + ' XP)'; }
},
{
pattern: /^(?:\[Farm\]\s*)?Planted\s+([\d,]+)\s+(.+?)\.?$/i,
replacement: function (m) { return 'Planted ' + m[1] + ' ' + m[2].replace(/\(s\)/gi, '').trim(); }
},
// Scrap summary
{
pattern: /^Scrapped\s+([\d,]+)\s+items?\.\s*Received:\s*(.+)$/i,
replacement: function (m) {
var itemCount = m[1];
var rawRewards = String(m[2] || '').replace(/<br\s*\/?>/gi, '\n');
var rewardLines = rawRewards
.split(/,|\n/g)
.map(function (part) { return _stripHtmlText(part); })
.map(function (part) { return part.replace(/\.$/, '').trim(); })
.filter(Boolean)
.map(function (part) { return part + '.'; });
if (!rewardLines.length) return 'Scrapped ' + itemCount + ' items.';
return 'Scrapped ' + itemCount + ' items.<br>Received:<br>' + rewardLines.join('<br>');
}
},
// Item sold
{ pattern: /^(?:The )?(.+?) was sold to (\S+) for ([\d,]+) gold!$/i, replacement: function (m) { return m[1] + ' → ' + m[2] + ' ' + m[3] + 'g'; } },
// Defeated boss rewards
{
pattern: /You defeated (?:the )?(.+?)! You have been awarded (.+?)\.$/i,
replacement: function (m) {
var rewardText = _stripHtmlText(m[2]);
var amountMatch = rewardText.match(/([+\-]?[\d,.]+(?:\.\d+)?)/);
var emojiMatch = rewardText.match(/[\p{Extended_Pictographic}][\uFE0F]?/u);
if (amountMatch && emojiMatch) return 'Defeated ' + m[1] + ' +' + amountMatch[1] + ' ' + emojiMatch[0];
if (amountMatch) return 'Defeated ' + m[1] + ' +' + amountMatch[1];
if (emojiMatch) return 'Defeated ' + m[1] + ' +' + emojiMatch[0];
return 'Defeated ' + m[1] + ' +' + _compactInlineHtml(m[2]);
}
},
// Escaped boss rewards
{
pattern: /^The (.+?) escaped, but you recovered (.+?) for your efforts\.?$/i,
replacement: function (m) {
var rewardText = _stripHtmlText(m[2]);
var amountMatch = rewardText.match(/([+\-]?[\d,.]+(?:\.\d+)?)/);
var emojiMatch = rewardText.match(/[\p{Extended_Pictographic}][\uFE0F]?/u);
if (amountMatch && emojiMatch) return m[1] + ' escaped +' + amountMatch[1] + ' ' + emojiMatch[0];
if (amountMatch) return m[1] + ' escaped +' + amountMatch[1];
if (emojiMatch) return m[1] + ' escaped +' + emojiMatch[0];
return m[1] + ' escaped +' + _compactInlineHtml(m[2]);
}
},
// Crafting bonus
{ pattern: /^Crafting Bonus Begins$/i, replacement: 'Craft bonus: start' },
// Generic spawn lines
{ pattern: /^(.+?) has spawned!$/i, replacement: function (m) { return 'Spawn: ' + m[1]; } },
// Generic despawn lines
{ pattern: /^(.+?) has despawned\.$/i, replacement: function (m) { return 'Despawn: ' + m[1]; } },
// Mercenary joins
{ pattern: /^\[Merc\]\s*Your mercenary has joined you in fighting (.+)!$/i, replacement: function (m) { return '[Merc] +' + m[1]; } },
];
if (typeof addLog === 'function') {
var origAddLog = addLog;
addLog = function (message, category) {
var _ownWagerWinAmount = null;
var _enableLogMessageTransforms = _settings.enableLogMessageTransforms !== false;
if (_enableLogMessageTransforms) {
message = message.replace(/\[Event\] /g, '').replace(/\[Dungeon\] /g, '').replace(/^System: /i, '');
if (category === 'offline') {
message = _shortenOfflineSummary(message);
}
for (var i = 0; i < LOG_SHORTENERS.length; i++) {
var _m = message.match(LOG_SHORTENERS[i].pattern);
if (_m) {
var _r = LOG_SHORTENERS[i].replacement;
message = typeof _r === 'function' ? _r(_m) : _r;
break;
}
}
var _ownWagerMatch = message.match(/^You won \+([+\-]?[\d,.]+(?:\.\d+)?)\s+🍀$/i);
if (_ownWagerMatch) _ownWagerWinAmount = _ownWagerMatch[1];
}
if (category === 'gathering') {
var _nb = message.match(_noBoltsRegex);
var _nh = !_nb ? message.match(_noHooksRegex) : null;
if (_nb || _nh) {
var _nrm = _nb || _nh;
var _nrAmt = _parseNum(_nrm[1]), _nrIcon = _nrm[2], _nrName = _nrm[3].trim();
var _nrXp = _parseNum(_nrm[4]), _nrSkill = _nrm[5].trim();
var _nrKey = _nrName;
if (!_trackerData.gathering[_nrKey]) {
var _nrColor = message.match(/color:\s*([^"';]+)/);
_trackerData.gathering[_nrKey] = { amount: 0, xp: 0, icon: _nrIcon, color: _nrColor ? _nrColor[1].trim() : '#ccc', skill: _nrSkill, startTime: Date.now() };
}
_trackerData.gathering[_nrKey].amount += _nrAmt;
_trackerData.gathering[_nrKey].xp += _nrXp;
_updateTrackerDisplay();
if (_enableLogMessageTransforms) {
message = _nb ? '🏹 no bolts!' : '🎣 no hooks!';
}
} else if (_trackGathering(message)) {
_updateTrackerDisplay();
return;
} else {
_trackDeposit(message);
}
}
if (category === 'rare_finds') {
if (_trackRareFind(message)) {
_updateTrackerDisplay();
return;
}
}
if (category === 'refining') {
if (_trackRefining(message)) {
_updateTrackerDisplay();
return;
}
}
if (category === 'crafting') {
if (_trackCrafting(message)) {
_updateTrackerDisplay();
return;
}
}
var _result = origAddLog.call(this, message, category);
var _logMsgs = document.getElementById('log-messages');
if (_logMsgs && _logMsgs.firstElementChild) {
var _entry = _logMsgs.firstElementChild;
if (_entry.innerHTML.indexOf('<br>') !== -1) {
_entry.style.whiteSpace = 'normal';
}
if (_enableLogMessageTransforms && _ownWagerWinAmount) {
var _prev = _entry.nextElementSibling;
if (_prev) {
var _prevText = String(_prev.textContent || '').replace(/\s+/g, ' ').trim();
var _ownSuffix = '+' + _ownWagerWinAmount + ' 🍀';
if (_prevText.indexOf('You won') === -1 && _prevText.endsWith(_ownSuffix)) {
_prev.remove();
}
}
}
}
_recalcLogFontSize();
return _result;
};
}
_initConclaveCompact();
if (DEBUG_GAME_WRAPPERS) _prodLog('[MP] Game function wrappers applied');
}
// =================================================================
// Activity Tracker (Gathering / Refining per-hour stats)
// =================================================================
var _trackerData = {
sessionStart: Date.now(),
gathering: {},
refining: {},
crafting: {},
rare: {}
};
var _gatherRegex = /\[Gathering\]\s*You gathered\s*<span[^>]*>([\d,.]+)<\/span>\s*(\S+)\s+([^,]+),\s*earning\s*([\d,.]+)\s+(\S+)\s*XP/i;
var _noBoltsRegex = /\[Gathering\]\s*You ran out of Bolts.*?gathered\s*<span[^>]*>([\d,.]+)<\/span>\s*(\S+)\s+([^,]+),\s*earning\s*([\d,.]+)\s+(\S+)\s*XP/i;
var _noHooksRegex = /\[Gathering\]\s*You ran out of Fishing Hooks.*?gathered\s*<span[^>]*>([\d,.]+)<\/span>\s*(\S+)\s+([^,]+),\s*earning\s*([\d,.]+)\s+(\S+)\s*XP/i;
var _refineRegex = /\[Refining\]\s*(?:Refined|You prepared)\s*<span[^>]*>([\d,.]+)<\/span>\s*(\S+)\s*into\s*<span[^>]*>([\d,.]+)<\/span>\s*(\S+),\s*earning\s*([\d,.]+)\s+(\S+)\s*XP/i;
var _depositRegex = /You deposited\s*<span[^>]*>([\d,.]+)<\/span>\s*(\S+)\s*at home/i;
var _craftRegex = /Crafting successful!?\s*You crafted ([\d,]+)\s+(.+?)\.\s*\(\+([\d,]+)\s+(\w+)\s*XP\)/i;
var _rareFindMeta = {
starfall_ore: { icon: '⭐', label: 'Starfall Ore', color: '#FF9900', match: ['starfall ore'], parentGathering: ['Iron Ore'] },
glimmerwood_sap: { icon: '✨', label: 'Glimmerwood Sap', color: '#00FF00', match: ['glimmerwood sap'], parentGathering: ['Wood'] },
crystallized_anima: { icon: '💎', label: 'Crystallized Anima', color: '#FF00FF', match: ['crystallized anima'], parentGathering: ['Gold'] },
oceanic_essence: { icon: '🌊', label: 'Oceanic Essence', color: '#00BFFF', match: ['oceanic essence'], parentGathering: ['Fish'] },
abyssal_eel: { icon: '🐍', label: 'Abyssal Eel', color: '#9370DB', match: ['abyssal eel'], parentGathering: ['Fish'] },
antlers: { icon: '🫎', label: 'Antlers', color: '#8B4513', match: ['antlers', 'antler'], parentGathering: ['Carcass'] },
lost_shipments: { icon: '📦', label: 'Lost Shipment', color: '#FFA500', match: ['lost shipment', 'lost shipments'], parentGathering: ['Salvage'] }
};
function _parseNum(s) {
return parseFloat(String(s).replace(/,/g, ''));
}
function _normalizeTrackerKey(s) {
return String(s || '').toLowerCase().replace(/[^a-z0-9]+/g, '');
}
function _rareBelongsToGathering(rareKey, gatheringKey) {
var meta = _rareFindMeta[rareKey];
if (!meta || !meta.parentGathering) return false;
var gNorm = _normalizeTrackerKey(gatheringKey);
for (var i = 0; i < meta.parentGathering.length; i++) {
if (_normalizeTrackerKey(meta.parentGathering[i]) === gNorm) return true;
}
return false;
}
function _clearRaresForGathering(gatheringKey) {
for (var rk in _trackerData.rare) {
if (_rareBelongsToGathering(rk, gatheringKey)) {
delete _trackerData.rare[rk];
}
}
}
function _trackGathering(message) {
var m = message.match(_gatherRegex);
if (!m) return false;
var amount = _parseNum(m[1]);
var icon = m[2];
var name = m[3].trim();
var xp = _parseNum(m[4]);
var skill = m[5].trim();
var key = name;
if (!_trackerData.gathering[key]) {
var colorMatch = message.match(/color:\s*([^"';]+)/);
_trackerData.gathering[key] = { amount: 0, xp: 0, actions: 0, icon: icon, color: colorMatch ? colorMatch[1].trim() : '#ccc', skill: skill, startTime: Date.now() };
}
_trackerData.gathering[key].amount += amount;
_trackerData.gathering[key].xp += xp;
_trackerData.gathering[key].actions += 1;
return true;
}
function _trackRefining(message) {
var m = message.match(_refineRegex);
if (!m) return false;
var inputAmt = _parseNum(m[1]);
var inputIcon = m[2];
var outputAmt = _parseNum(m[3]);
var outputIcon = m[4];
var xp = _parseNum(m[5]);
var skill = m[6].trim();
var key = outputIcon;
if (!_trackerData.refining[key]) {
_trackerData.refining[key] = { input: 0, output: 0, xp: 0, inputIcon: inputIcon, outputIcon: outputIcon, skill: skill, startTime: Date.now() };
}
_trackerData.refining[key].input += inputAmt;
_trackerData.refining[key].output += outputAmt;
_trackerData.refining[key].xp += xp;
return true;
}
function _trackDeposit(message) {
var m = message.match(_depositRegex);
if (!m) return false;
var amount = _parseNum(m[1]);
var icon = m[2];
var key = null;
for (var k in _trackerData.gathering) {
if (_trackerData.gathering[k].icon === icon) { key = k; break; }
}
if (key) {
_trackerData.gathering[key].deposited = (_trackerData.gathering[key].deposited || 0) + amount;
}
return false;
}
function _craftNameToKey(name) {
if (typeof CRAFTING_ITEMS === 'undefined') return null;
var lower = name.toLowerCase();
for (var k in CRAFTING_ITEMS) {
if (CRAFTING_ITEMS[k].name.toLowerCase() === lower) return k;
}
return null;
}
function _getCraftItemKey() {
var iface = document.getElementById('crafting-interface');
if (iface) {
var active = iface.getAttribute('data-active-item');
if (active) return active;
}
if (typeof playerWorkers !== 'undefined' && playerWorkers.length > 0) {
for (var i = 0; i < playerWorkers.length; i++) {
if (playerWorkers[i].activity === 'crafting' && playerWorkers[i].crafting_queue_item) {
return playerWorkers[i].crafting_queue_item;
}
}
}
return null;
}
function _getCraftBaseCost(itemKey) {
if (!itemKey || typeof CRAFTING_ITEMS === 'undefined') return null;
var conf = CRAFTING_ITEMS[itemKey];
return conf ? conf.baseCost : null;
}
function _getCraftStatus() {
var btn = document.getElementById('crafting-craft-btn');
if (btn) {
var text = btn.textContent || '';
var left = text.match(/([\d,]+)\s*left/);
var time = text.match(/(?:Crafting|Preparing)[:\s]*([\ddhms\s]+?)(?:\s*\(|$)/i);
if (left) {
return {
remaining: parseInt(left[1].replace(/,/g, '')) || 0,
timeText: time ? time[1].trim() : ''
};
}
}
return null;
}
function _trackCrafting(message) {
var m = message.match(_craftRegex);
if (!m) return false;
var amount = _parseNum(m[1]);
var itemName = m[2].trim();
var xp = _parseNum(m[3]);
var skill = m[4].trim();
var key = itemName;
if (!_trackerData.crafting[key]) {
var itemKey = _craftNameToKey(itemName) || _getCraftItemKey();
_trackerData.crafting[key] = { amount: 0, xp: 0, ticks: 0, skill: skill, itemKey: itemKey, baseCost: _getCraftBaseCost(itemKey), startTime: Date.now() };
}
var c = _trackerData.crafting[key];
if (!c.itemKey) { c.itemKey = _craftNameToKey(itemName) || _getCraftItemKey(); }
if (!c.baseCost && c.itemKey) { c.baseCost = _getCraftBaseCost(c.itemKey); }
c.amount += amount;
c.xp += xp;
c.ticks += 1;
return true;
}
function _trackRareFind(message) {
var text = String(message || '')
.replace(/<[^>]*>/g, ' ')
.replace(/\s+/g, ' ')
.trim()
.toLowerCase();
if (!text) return false;
text = text.replace(/\[rare\]/g, '').trim();
for (var key in _rareFindMeta) {
var meta = _rareFindMeta[key];
for (var i = 0; i < meta.match.length; i++) {
if (text.indexOf(meta.match[i]) !== -1) {
if (!_trackerData.rare[key]) {
_trackerData.rare[key] = {
amount: 0,
icon: meta.icon,
label: meta.label,
color: meta.color,
startTime: Date.now()
};
}
_trackerData.rare[key].amount += 1;
return true;
}
}
}
return false;
}
function _fmtNum(n) {
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'm';
if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
if (n % 1 !== 0) return n.toFixed(1);
return String(n);
}
function _fmtCount(n) {
return Math.max(0, Math.floor(n || 0)).toLocaleString('en-US');
}
function _iconToResourceKey(icon) {
if (typeof RESOURCE_META === 'undefined') return null;
for (var k in RESOURCE_META) {
if (RESOURCE_META[k].icon === icon) return k;
}
return null;
}
function _fmtEta(hours) {
if (hours >= 24) return Math.floor(hours / 24) + 'd ' + Math.floor(hours % 24) + 'h';
if (hours >= 1) return Math.floor(hours) + 'h ' + Math.floor((hours % 1) * 60) + 'm';
var mins = hours * 60;
if (mins >= 1) return Math.floor(mins) + 'm';
return '<1m';
}
function _fmtElapsed(ms) {
var s = Math.floor(ms / 1000);
var h = Math.floor(s / 3600);
var m = Math.floor((s % 3600) / 60);
if (h > 0) return h + 'h ' + m + 'm';
if (m > 0) return m + 'm ' + (s % 60) + 's';
return s + 's';
}
function _updateTrackerDisplay() {
var el = document.getElementById('mp-activity-tracker');
if (!el) return;
var elapsed = Date.now() - _trackerData.sessionStart;
var hrs = Math.max(elapsed / 3600000, 1 / 3600);
var html = '<div class="tracker-header"><span>Session Tracker</span><span>' + _fmtElapsed(elapsed) + '</span></div>';
var gKeys = Object.keys(_trackerData.gathering);
var rKeys = Object.keys(_trackerData.refining);
var cKeys = Object.keys(_trackerData.crafting);
var rareKeys = Object.keys(_trackerData.rare);
if (gKeys.length === 0 && rKeys.length === 0 && cKeys.length === 0 && rareKeys.length === 0) {
html += '<div class="tracker-empty">No activity data yet...</div>';
el.innerHTML = html;
return;
}
for (var i = 0; i < gKeys.length; i++) {
var g = _trackerData.gathering[gKeys[i]];
var gHrs = Math.max((Date.now() - (g.startTime || _trackerData.sessionStart)) / 3600000, 1 / 3600);
var perHr = g.amount / gHrs;
var xpHr = g.xp / gHrs;
var gActions = g.actions || 0;
if (!gActions && g.amount > 0) gActions = 1;
var gPerAct = g.amount / Math.max(gActions, 1);
var gXpPerAct = g.xp / Math.max(gActions, 1);
html += '<div class="tracker-row">';
html += '<span class="tracker-x" data-type="gathering" data-key="' + gKeys[i].replace(/"/g, '"') + '">×</span>';
html += '<span style="color:' + g.color + '">' + g.icon + '</span>';
html += ' <span class="rate">' + _fmtNum(g.amount) + ' (' + _fmtNum(perHr) + '/hr)</span>';
html += '<span class="sep">·</span>';
html += '<span class="xp-rate">' + _fmtNum(g.xp) + ' XP (' + _fmtNum(xpHr) + '/hr)</span>';
html += '</div>';
html += '<div class="tracker-row tracker-sub-row tracker-per-action">';
html += '↳ <span class="rate">' + _fmtNum(gPerAct) + ' /act</span>';
html += '<span class="sep">·</span>';
html += '<span class="xp-rate">' + _fmtNum(gXpPerAct) + ' XP/act</span>';
html += '<span class="sep">·</span>';
html += '<span style="color:#666">' + _fmtCount(gActions) + ' acts</span>';
html += '</div>';
for (var grk in _trackerData.rare) {
if (!_rareBelongsToGathering(grk, gKeys[i])) continue;
var gr = _trackerData.rare[grk];
var grHrs = Math.max((Date.now() - (gr.startTime || _trackerData.sessionStart)) / 3600000, 1 / 3600);
var grHr = gr.amount / grHrs;
html += '<div class="tracker-row tracker-sub-row" style="padding-left:1.2em;opacity:.65;font-size:.85em">';
html += '↳ <span style="color:' + gr.color + '">' + gr.icon + '</span> ';
html += '<span class="rate">' + _fmtNum(gr.amount) + ' (' + _fmtNum(grHr) + '/hr)</span>';
html += '</div>';
}
}
for (var j = 0; j < rKeys.length; j++) {
var r = _trackerData.refining[rKeys[j]];
var rHrs = Math.max((Date.now() - (r.startTime || _trackerData.sessionStart)) / 3600000, 1 / 3600);
var outHr = r.output / rHrs;
var inHr = r.input / rHrs;
var rxpHr = r.xp / rHrs;
html += '<div class="tracker-row">';
html += '<span class="tracker-x" data-type="refining" data-key="' + rKeys[j].replace(/"/g, '"') + '">×</span>';
html += r.inputIcon + '<span class="refine-arrow">→</span>' + r.outputIcon;
html += ' <span class="rate">' + _fmtNum(r.output) + ' (' + _fmtNum(outHr) + '/hr)</span>';
html += '<span class="sep">·</span>';
html += '<span class="xp-rate">' + _fmtNum(r.xp) + ' XP (' + _fmtNum(rxpHr) + '/hr)</span>';
html += '</div>';
html += '<div class="tracker-row tracker-sub-row" style="padding-left:1.2em;opacity:.65;font-size:.85em">';
html += '↳ ' + r.inputIcon + ' <span class="rate" style="color:#f66">' + _fmtNum(r.input) + ' (' + _fmtNum(inHr) + '/hr)</span>';
var _resKey = _iconToResourceKey(r.inputIcon);
if (_resKey && typeof resources !== 'undefined') {
var _stock = resources[_resKey] || 0;
if (inHr > 0 && _stock > 0) {
var _etaHrs = _stock / inHr;
html += '<span class="sep">·</span><span style="color:#fa0">ETA ' + _fmtEta(_etaHrs) + '</span>';
} else if (_stock === 0) {
html += '<span class="sep">·</span><span style="color:#f66">empty</span>';
}
}
html += '</div>';
}
for (var ci = 0; ci < cKeys.length; ci++) {
var c = _trackerData.crafting[cKeys[ci]];
var cHrs = Math.max((Date.now() - (c.startTime || _trackerData.sessionStart)) / 3600000, 1 / 3600);
var craftHr = c.amount / cHrs;
var cxpHr = c.xp / cHrs;
var ticksHr = c.ticks / cHrs;
if (!c.baseCost && c.itemKey) { c.baseCost = _getCraftBaseCost(c.itemKey); }
if (!c.itemKey) { c.itemKey = _craftNameToKey(cKeys[ci]) || _getCraftItemKey(); if (c.itemKey) c.baseCost = _getCraftBaseCost(c.itemKey); }
html += '<div class="tracker-row">';
html += '<span class="tracker-x" data-type="crafting" data-key="' + cKeys[ci].replace(/"/g, '"') + '">×</span>';
html += '<span style="color:#3366ff">⚒️</span>';
html += ' <span class="rate">' + _fmtNum(c.amount) + ' (' + _fmtNum(craftHr) + '/hr)</span>';
html += '<span class="sep">·</span>';
html += '<span class="xp-rate">' + _fmtNum(c.xp) + ' XP (' + _fmtNum(cxpHr) + '/hr)</span>';
html += '</div>';
var cStatus = _getCraftStatus();
if (cStatus && cStatus.remaining > 0) {
html += '<div class="tracker-row tracker-sub-row" style="padding-left:1.2em;opacity:.65;font-size:.85em">';
html += '↳ <span style="color:#fa0">' + cStatus.remaining + ' left';
if (cStatus.timeText) html += ' · ' + cStatus.timeText;
html += '</span></div>';
}
}
el.innerHTML = html;
var xBtns = el.querySelectorAll('.tracker-x');
for (var b = 0; b < xBtns.length; b++) {
xBtns[b].addEventListener('click', function () {
var type = this.getAttribute('data-type');
var key = this.getAttribute('data-key');
if (_trackerData[type] && _trackerData[type][key]) {
if (type === 'gathering') _clearRaresForGathering(key);
delete _trackerData[type][key];
_updateTrackerDisplay();
}
});
}
var rows = el.querySelectorAll('.tracker-row');
for (var ri = 0; ri < rows.length; ri++) {
var row = rows[ri];
row.style.fontSize = '';
var baseFontSize = parseFloat(getComputedStyle(row).fontSize);
var fs = baseFontSize;
while (row.scrollWidth > row.clientWidth && fs > 6) {
fs -= 0.5;
row.style.fontSize = fs + 'px';
}
}
}
function _initActivityTracker() {
var logContainer = document.getElementById('log-container');
var logMessages = document.getElementById('log-messages');
if (!logContainer || !logMessages) return;
if (document.getElementById('mp-activity-tracker')) return;
var tracker = document.createElement('div');
tracker.id = 'mp-activity-tracker';
logContainer.insertBefore(tracker, logMessages);
_trackerData.sessionStart = Date.now();
_updateTrackerDisplay();
setInterval(_updateTrackerDisplay, 5000);
}
// =================================================================
// Refined Resources in Top Bar
// =================================================================
var RAW_TO_REFINED = {
gold: 'goldBars',
ironOre: 'ironBars',
wood: 'planks',
fish: 'preparedFish',
Carcass: 'meat',
salvage: 'mayors_favor'
};
var _prevRefinedValues = {};
var _applyingRefined = false;
function _formatResValue(val) {
if (val < 100) {
return val.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
return Math.floor(val).toLocaleString();
}
function _applyRefinedRows() {
var grid = document.getElementById('pinned-resources-grid');
if (!grid) return;
_applyingRefined = true;
var cards = grid.querySelectorAll('.resource-card');
for (var i = 0; i < cards.length; i++) {
var card = cards[i];
if (card.classList.contains('res-paired')) continue;
var valEl = card.querySelector('.res-value');
if (!valEl) continue;
var rawKey = valEl.id.replace('stat-', '');
var refKey = RAW_TO_REFINED[rawKey];
if (!refKey) continue;
var meta = (typeof RESOURCE_META !== 'undefined') ? RESOURCE_META[refKey] : null;
if (!meta) continue;
var iconEl = card.querySelector('.res-icon');
var rawBox = document.createElement('div');
rawBox.className = 'mp-res-card';
if (iconEl) rawBox.appendChild(iconEl);
rawBox.appendChild(valEl);
var refVal = (typeof resources !== 'undefined') ? (resources[refKey] || 0) : 0;
_prevRefinedValues[refKey] = refVal;
var refBox = document.createElement('div');
refBox.className = 'mp-res-card';
refBox.innerHTML =
'<span class="res-icon">' + meta.icon + '</span>' +
'<span id="stat-ref-' + refKey + '" class="res-value" style="color:' + (meta.color || 'white') + ';">' + _formatResValue(refVal) + '</span>';
card.innerHTML = '';
card.appendChild(rawBox);
card.appendChild(refBox);
card.classList.add('res-paired');
}
_applyingRefined = false;
}
function _updateRefinedValues() {
if (typeof resources === 'undefined') return;
for (var rawKey in RAW_TO_REFINED) {
var refKey = RAW_TO_REFINED[rawKey];
var el = document.getElementById('stat-ref-' + refKey);
if (!el) continue;
var val = resources[refKey] || 0;
var display = _formatResValue(val);
if (el.innerText !== display) {
el.innerText = display;
var prev = _prevRefinedValues[refKey];
if (prev !== undefined && val !== prev) {
var cls = val > prev ? 'flash-green' : 'flash-red';
el.classList.remove('flash-green', 'flash-red');
requestAnimationFrame(function (e, c) {
return function () { e.classList.add(c); };
}(el, cls));
}
_prevRefinedValues[refKey] = val;
}
}
}
function _observeResourceGrid(grid) {
var observer = new MutationObserver(function () {
if (!_applyingRefined) _applyRefinedRows();
});
observer.observe(grid, { childList: true });
return observer;
}
function _initRefinedResources() {
var grid = document.getElementById('pinned-resources-grid');
if (grid) {
_applyRefinedRows();
_observeResourceGrid(grid);
} else {
var bodyObs = new MutationObserver(function () {
var g = document.getElementById('pinned-resources-grid');
if (g) {
bodyObs.disconnect();
_applyRefinedRows();
_observeResourceGrid(g);
}
});
bodyObs.observe(document.getElementById('ui-bar'), { childList: true, subtree: true });
}
setInterval(_updateRefinedValues, 1000);
}
// =================================================================
// Wait for game, then apply
// =================================================================
function _waitAndApply() {
if (typeof GAME_MODULES === 'undefined' ||
typeof toggleModule === 'undefined' ||
typeof activeModules === 'undefined' ||
typeof renderOptionsMenu === 'undefined' ||
!document.getElementById('dual-menu-container')) {
setTimeout(_waitAndApply, 250);
return;
}
_loadSettings();
_loadPanelVisualCssMap();
_loadPreferredGridSpans();
_loadGridState();
_syncPreferredGridSpansFromPanels();
_loadPresets();
_syncGameState();
toggleModule = _mpToggleModule;
updateDualMenuDisplay = _mpUpdateDisplay;
closeMenu = _mpCloseMenu;
isModuleActive = _isPanelActive;
_patchRenderUIBar();
_patchOptionsMenu();
_patchGameFunctions();
_installHamburgerAddInterceptor();
saveActiveModules = function () {
_saveGridState();
};
initializeActiveModules = function () {
_collectActiveModuleKeys().forEach(function (k) {
if (_isRegisteredModuleKey(k)) initializeModule(k);
});
};
_initWheelScrollRouting(document.getElementById('dual-menu-container'));
_initLogToggle();
_initActivityTracker();
_initBrpToggle();
_attachBrpToLog();
_initChatRestructure();
_syncPortraitControlPlacement();
if (window.matchMedia) {
var portraitMql = window.matchMedia(PORTRAIT_QUERY);
if (portraitMql && typeof portraitMql.addEventListener === 'function') {
portraitMql.addEventListener('change', _syncPortraitControlPlacement);
} else if (portraitMql && typeof portraitMql.addListener === 'function') {
portraitMql.addListener(_syncPortraitControlPlacement);
}
}
_initMercPostToggle();
_initTitheToggle();
_initMasteryToggle();
_initRefinedResources();
window.addEventListener('resize', _onWindowResize);
_applyRoundedTabs(_settings.roundedTabs);
_applyPanelCssSettings();
lastUIBarSignature = '';
_mpUpdateDisplay();
_attachBrpToLog();
_updateBrpLayout();
renderUIBar();
}
_waitAndApply();
}.toString() + ')();';
document.head.appendChild(el);
})();