fix(dashboard): fix persistent px values, auto-layout, widget loss, gaps, and tabs
This commit resolves 6 critical dashboard issues reported by user: 1. **Persistent px values causing 264rem widget heights** - Root cause: state.js had hardcoded rowHeight: 80, gap: 12 (px) - Root cause: index.js double-loaded layout, overwriting migration - Fix: Changed state.js gridConfig to rem units (5, 0.75) - Fix: Removed redundant applyDashboardConfig in index.js - Fix: Added migration in layoutPersistence.js for old saves - Dashboard now uses rem consistently throughout 2. **Auto-layout on first load** - Added auto-layout in loadLayout() when no saved layout exists - Prevents overlap from hardcoded default positions - Saves auto-laid-out result as initial layout 3. **Reset layout causes overlap** - Added auto-layout loop in resetLayout() after applying config - Each tab auto-lays out to prevent widget overlap 4. **Auto-arrange loses inventory/social widgets** - Fixed autoLayoutWidgets to gather ALL widgets from ALL tabs - Previously only gathered current tab, lost other tabs - Now always uses multi-tab distribution to preserve all widgets 5. **Auto-arrange leaves 2x2 gaps** - Added compact pass in gridEngine.js after bin-packing - Moves widgets upward to fill gaps - Eliminates empty spaces at bottom of layout 6. **Tabs not compact (icon-only)** - Updated tab styling: icons only, names show on hover - Allows more tabs in compact space - min-width: 2.5rem, larger icon size Also added debug logging to track config values through initialization. Fixes refresh sizing bug, reset overlap, widget loss, and layout gaps.
This commit is contained in:
@@ -559,15 +559,9 @@ async function initUI() {
|
|||||||
console.log('[RPG Companion] Dashboard v2 initialized successfully');
|
console.log('[RPG Companion] Dashboard v2 initialized successfully');
|
||||||
console.log('[RPG Companion] Manager instance:', manager);
|
console.log('[RPG Companion] Manager instance:', manager);
|
||||||
|
|
||||||
// Check if this is first time OR if dashboard is empty - create default layout
|
// Dashboard manager already loaded its layout in init() via loadLayout()
|
||||||
if (!extensionSettings.dashboard || !extensionSettings.dashboard.tabs || extensionSettings.dashboard.tabs.length === 0) {
|
// No need to load again here - that would overwrite the migrated values
|
||||||
console.log('[RPG Companion] Creating default dashboard layout...');
|
console.log('[RPG Companion] Dashboard initialized and layout loaded via layoutPersistence');
|
||||||
createDefaultLayout(manager);
|
|
||||||
} else {
|
|
||||||
console.log('[RPG Companion] Loading saved dashboard layout with', extensionSettings.dashboard.tabs.length, 'tabs');
|
|
||||||
// Apply the saved layout to the manager
|
|
||||||
manager.applyDashboardConfig(extensionSettings.dashboard);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
console.warn('[RPG Companion] Dashboard initialization returned null, falling back to legacy rendering');
|
console.warn('[RPG Companion] Dashboard initialization returned null, falling back to legacy rendering');
|
||||||
throw new Error('Dashboard initialization failed');
|
throw new Error('Dashboard initialization failed');
|
||||||
|
|||||||
+2
-2
@@ -87,8 +87,8 @@ export let extensionSettings = {
|
|||||||
// Columns calculated dynamically by GridEngine (2-4 based on panel width)
|
// Columns calculated dynamically by GridEngine (2-4 based on panel width)
|
||||||
// Mobile (≤1000px screen): always 2 columns
|
// Mobile (≤1000px screen): always 2 columns
|
||||||
// Desktop (>1000px screen): 2-4 columns based on panel width
|
// Desktop (>1000px screen): 2-4 columns based on panel width
|
||||||
rowHeight: 80, // Pixels per row
|
rowHeight: 5, // rem units for responsive scaling
|
||||||
gap: 12, // Gap between widgets (px)
|
gap: 0.75, // rem units (was 12px)
|
||||||
snapToGrid: true, // Auto-snap enabled
|
snapToGrid: true, // Auto-snap enabled
|
||||||
showGrid: true // Show grid lines in edit mode
|
showGrid: true // Show grid lines in edit mode
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -57,6 +57,12 @@ export class DashboardManager {
|
|||||||
...config
|
...config
|
||||||
};
|
};
|
||||||
|
|
||||||
|
console.log('[DashboardManager] Constructor config:', {
|
||||||
|
rowHeight: this.config.rowHeight,
|
||||||
|
gap: this.config.gap,
|
||||||
|
columns: this.config.columns
|
||||||
|
});
|
||||||
|
|
||||||
// Dashboard state
|
// Dashboard state
|
||||||
this.currentTabId = null;
|
this.currentTabId = null;
|
||||||
this.widgets = new Map(); // widgetId => { widget data, element, tab }
|
this.widgets = new Map(); // widgetId => { widget data, element, tab }
|
||||||
@@ -1033,7 +1039,7 @@ export class DashboardManager {
|
|||||||
* @returns {Object} Dashboard configuration
|
* @returns {Object} Dashboard configuration
|
||||||
*/
|
*/
|
||||||
getDashboardConfig() {
|
getDashboardConfig() {
|
||||||
return {
|
const config = {
|
||||||
version: 2,
|
version: 2,
|
||||||
gridConfig: {
|
gridConfig: {
|
||||||
columns: this.config.columns,
|
columns: this.config.columns,
|
||||||
@@ -1049,6 +1055,12 @@ export class DashboardManager {
|
|||||||
})),
|
})),
|
||||||
defaultTab: this.dashboard.defaultTab
|
defaultTab: this.dashboard.defaultTab
|
||||||
};
|
};
|
||||||
|
console.log('[DashboardManager] getDashboardConfig() returning:', {
|
||||||
|
rowHeight: config.gridConfig.rowHeight,
|
||||||
|
gap: config.gridConfig.gap,
|
||||||
|
columns: config.gridConfig.columns
|
||||||
|
});
|
||||||
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1124,8 +1136,19 @@ export class DashboardManager {
|
|||||||
if (saved) {
|
if (saved) {
|
||||||
this.applyDashboardConfig(saved);
|
this.applyDashboardConfig(saved);
|
||||||
} else if (this.defaultLayout) {
|
} else if (this.defaultLayout) {
|
||||||
console.log('[DashboardManager] No saved layout, using default');
|
console.log('[DashboardManager] No saved layout, using default with auto-layout');
|
||||||
this.applyDashboardConfig(this.defaultLayout);
|
this.applyDashboardConfig(this.defaultLayout);
|
||||||
|
|
||||||
|
// Auto-layout each tab to prevent overlap (default positions may not fit screen)
|
||||||
|
this.dashboard.tabs.forEach(tab => {
|
||||||
|
if (tab.widgets && tab.widgets.length > 0) {
|
||||||
|
console.log(`[DashboardManager] Auto-laying out default tab "${tab.name}" (${tab.widgets.length} widgets)`);
|
||||||
|
this.gridEngine.autoLayout(tab.widgets, { preserveOrder: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Save the auto-laid-out default as the initial saved layout
|
||||||
|
await this.saveLayout(true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[DashboardManager] Failed to load layout:', error);
|
console.error('[DashboardManager] Failed to load layout:', error);
|
||||||
@@ -1172,10 +1195,25 @@ export class DashboardManager {
|
|||||||
await this.persistence.resetToDefault(this.defaultLayout);
|
await this.persistence.resetToDefault(this.defaultLayout);
|
||||||
this.applyDashboardConfig(this.defaultLayout);
|
this.applyDashboardConfig(this.defaultLayout);
|
||||||
|
|
||||||
|
// Auto-layout each tab to prevent overlap (default positions may have changed)
|
||||||
|
this.dashboard.tabs.forEach(tab => {
|
||||||
|
if (tab.widgets && tab.widgets.length > 0) {
|
||||||
|
console.log(`[DashboardManager] Auto-laying out tab "${tab.name}" (${tab.widgets.length} widgets)`);
|
||||||
|
this.gridEngine.autoLayout(tab.widgets, { preserveOrder: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Force re-render tabs
|
// Force re-render tabs
|
||||||
this.renderTabs();
|
this.renderTabs();
|
||||||
|
|
||||||
console.log('[DashboardManager] Reset complete');
|
// Re-render current tab's widgets
|
||||||
|
if (this.currentTabId) {
|
||||||
|
this.switchTab(this.currentTabId);
|
||||||
|
} else if (this.dashboard.tabs.length > 0) {
|
||||||
|
this.switchTab(this.dashboard.tabs[0].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[DashboardManager] Reset complete with auto-layout');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1199,15 +1237,24 @@ export class DashboardManager {
|
|||||||
autoLayoutWidgets(options = {}) {
|
autoLayoutWidgets(options = {}) {
|
||||||
console.log('[DashboardManager] Auto-layout widgets requested');
|
console.log('[DashboardManager] Auto-layout widgets requested');
|
||||||
|
|
||||||
// Get current tab
|
// Gather ALL widgets from ALL tabs (don't lose inventory, social, etc.)
|
||||||
const currentTab = this.tabManager.getTab(this.currentTabId);
|
const allWidgets = [];
|
||||||
if (!currentTab || !currentTab.widgets || currentTab.widgets.length === 0) {
|
this.dashboard.tabs.forEach(tab => {
|
||||||
|
if (tab.widgets && tab.widgets.length > 0) {
|
||||||
|
console.log(`[DashboardManager] Gathering ${tab.widgets.length} widgets from tab "${tab.name}"`);
|
||||||
|
allWidgets.push(...tab.widgets);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allWidgets.length === 0) {
|
||||||
console.warn('[DashboardManager] No widgets to auto-layout');
|
console.warn('[DashboardManager] No widgets to auto-layout');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(`[DashboardManager] Total widgets to layout: ${allWidgets.length}`);
|
||||||
|
|
||||||
// Smart category-aware sorting BEFORE auto-layout
|
// Smart category-aware sorting BEFORE auto-layout
|
||||||
const widgetsToLayout = this.sortWidgetsByCategory(currentTab.widgets);
|
const widgetsToLayout = this.sortWidgetsByCategory(allWidgets);
|
||||||
|
|
||||||
// Calculate estimated height to determine if multi-tab distribution is needed
|
// Calculate estimated height to determine if multi-tab distribution is needed
|
||||||
const estimatedHeight = this.estimateLayoutHeight(widgetsToLayout);
|
const estimatedHeight = this.estimateLayoutHeight(widgetsToLayout);
|
||||||
@@ -1215,37 +1262,12 @@ export class DashboardManager {
|
|||||||
|
|
||||||
console.log('[DashboardManager] Estimated height:', estimatedHeight + 'rem', 'Threshold:', heightThreshold + 'rem');
|
console.log('[DashboardManager] Estimated height:', estimatedHeight + 'rem', 'Threshold:', heightThreshold + 'rem');
|
||||||
|
|
||||||
// If widgets fit comfortably, use single-tab auto-layout
|
// Always use multi-tab distribution when we have many widgets
|
||||||
if (estimatedHeight <= heightThreshold) {
|
// This preserves all widgets (inventory, social, etc.)
|
||||||
console.log('[DashboardManager] Using single-tab auto-layout');
|
console.log('[DashboardManager] Using multi-tab distribution to preserve all widgets');
|
||||||
|
this.distributeWidgetsByCategory(widgetsToLayout);
|
||||||
|
|
||||||
// Run auto-layout algorithm on pre-sorted widgets
|
// distributeWidgetsByCategory handles rendering and tab switching
|
||||||
// (gridEngine will preserve this logical order instead of sorting by area)
|
|
||||||
this.gridEngine.autoLayout(widgetsToLayout, { preserveOrder: true });
|
|
||||||
|
|
||||||
// Update tab widgets with new positions
|
|
||||||
currentTab.widgets = widgetsToLayout;
|
|
||||||
} else {
|
|
||||||
// Too many widgets - distribute across multiple tabs by category
|
|
||||||
console.log('[DashboardManager] Height exceeds threshold, using multi-tab distribution');
|
|
||||||
this.distributeWidgetsByCategory(widgetsToLayout);
|
|
||||||
return; // distributeWidgetsByCategory handles rendering
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-render all widgets with new positions
|
|
||||||
this.clearGrid();
|
|
||||||
widgetsToLayout.forEach(widget => {
|
|
||||||
const definition = this.registry.get(widget.type);
|
|
||||||
if (definition) {
|
|
||||||
this.renderWidget(widget, definition);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('[DashboardManager] Auto-layout complete, re-rendered widgets');
|
|
||||||
|
|
||||||
// Save changes
|
|
||||||
this.triggerAutoSave();
|
|
||||||
this.notifyChange('autoLayoutApplied', { tabId: this.currentTabId });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -528,7 +528,41 @@ export class GridEngine {
|
|||||||
console.log(`[GridEngine] Auto-layout positioned: ${widget.id} at (${pos.x},${pos.y}) size ${targetW}×${targetH}`);
|
console.log(`[GridEngine] Auto-layout positioned: ${widget.id} at (${pos.x},${pos.y}) size ${targetW}×${targetH}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[GridEngine] Auto-layout complete');
|
// Compact pass: Move widgets up to fill gaps
|
||||||
|
console.log('[GridEngine] Compacting layout to fill gaps...');
|
||||||
|
let compactedCount = 0;
|
||||||
|
|
||||||
|
// Sort widgets by current Y position (process top to bottom)
|
||||||
|
const sortedForCompact = [...sorted].sort((a, b) => a.y - b.y);
|
||||||
|
|
||||||
|
sortedForCompact.forEach(widget => {
|
||||||
|
const originalY = widget.y;
|
||||||
|
|
||||||
|
// Try to move widget up as far as possible
|
||||||
|
for (let tryY = 0; tryY < originalY; tryY++) {
|
||||||
|
// Clear current position from occupied map
|
||||||
|
for (let row = originalY; row < originalY + widget.h; row++) {
|
||||||
|
for (let col = widget.x; col < widget.x + widget.w; col++) {
|
||||||
|
occupied.delete(`${col},${row}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if new position is free
|
||||||
|
if (isFree(widget.x, tryY, widget.w, widget.h)) {
|
||||||
|
// Move widget up
|
||||||
|
widget.y = tryY;
|
||||||
|
markOccupied(widget, widget.x, tryY, widget.w, widget.h);
|
||||||
|
compactedCount++;
|
||||||
|
console.log(`[GridEngine] Compacted ${widget.id} from y=${originalY} to y=${tryY}`);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
// Re-mark original position and continue
|
||||||
|
markOccupied(widget, widget.x, originalY, widget.w, widget.h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[GridEngine] Auto-layout complete (compacted ${compactedCount} widgets)`);
|
||||||
return widgets;
|
return widgets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,6 +148,17 @@ export class LayoutPersistence {
|
|||||||
|
|
||||||
const layoutData = JSON.parse(stored);
|
const layoutData = JSON.parse(stored);
|
||||||
|
|
||||||
|
// Migrate old pixel values to rem units
|
||||||
|
if (layoutData.gridConfig) {
|
||||||
|
// Check if we have old pixel values (rowHeight > 20 is likely pixels)
|
||||||
|
if (layoutData.gridConfig.rowHeight > 20) {
|
||||||
|
console.log('[LayoutPersistence] Migrating old px values to rem');
|
||||||
|
layoutData.gridConfig.rowHeight = 5; // 80px → 5rem
|
||||||
|
layoutData.gridConfig.gap = 0.75; // 12px → 0.75rem
|
||||||
|
console.log('[LayoutPersistence] Converted gridConfig: rowHeight=5rem, gap=0.75rem');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Validate loaded data
|
// Validate loaded data
|
||||||
if (!this.validateDashboard(layoutData)) {
|
if (!this.validateDashboard(layoutData)) {
|
||||||
throw new Error('Loaded layout is invalid');
|
throw new Error('Loaded layout is invalid');
|
||||||
|
|||||||
@@ -1083,8 +1083,10 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
.rpg-dashboard-tab {
|
.rpg-dashboard-tab {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 0.3rem;
|
gap: 0.3rem;
|
||||||
padding: 0.4rem 0.7rem;
|
padding: 0.5rem;
|
||||||
|
min-width: 2.5rem;
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -1108,11 +1110,18 @@ body:has(.rpg-panel.rpg-position-left) #sheld {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rpg-tab-icon {
|
.rpg-tab-icon {
|
||||||
font-size: 0.9rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rpg-tab-name {
|
.rpg-tab-name {
|
||||||
font-size: 0.75rem;
|
font-size: 0.75rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show name on hover */
|
||||||
|
.rpg-dashboard-tab:hover .rpg-tab-name {
|
||||||
|
display: inline;
|
||||||
|
margin-left: 0.3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rpg-dashboard-btn {
|
.rpg-dashboard-btn {
|
||||||
|
|||||||
Reference in New Issue
Block a user