From 122bb3194ae16c9bd7546f302ec65e68ec6e2803 Mon Sep 17 00:00:00 2001 From: Lucas 'Paperboy' Rose-Winters Date: Thu, 23 Oct 2025 14:00:00 +1100 Subject: [PATCH] feat(dashboard): add auto-layout button with smart widget packing Implements intelligent auto-layout system that efficiently arranges widgets to maximize space usage while respecting panel width constraints. **Key Features:** - Smart packing algorithm that sorts by widget area and finds optimal positions - Respects responsive column count (2-4 columns based on panel width) - Prefers full-width widgets when possible to eliminate gaps - Fallback to narrower widths for better vertical packing - Maintains minimum widget sizes **Implementation:** - GridEngine.autoLayout() - Core packing algorithm with collision detection - DashboardManager.autoLayoutWidgets() - High-level API that re-renders after layout - Auto-Arrange button in dashboard header (uses fa-table-cells-large icon) - Event handler wired to call autoLayoutWidgets with preferFullWidth=true **Algorithm Strategy:** 1. Sort widgets by area (largest first) for efficient packing 2. For each widget, try full-width placement first 3. Find first available position using row-by-row scan 4. If position is too far down, try narrower widths 5. Mark cells as occupied to prevent overlaps **Testing Notes:** - Works with current responsive column system (2-4 columns) - Respects minimum sizes and column constraints - Re-renders all widgets after repositioning - Auto-saves layout changes Part of Epic 2: Dashboard Widget Library --- docs/IMPLEMENTATION_PLAN.md | 20 +- index.js | 10 +- src/core/state.js | 4 +- src/systems/dashboard/dashboardIntegration.js | 51 ++- src/systems/dashboard/dashboardManager.js | 166 ++++++++-- src/systems/dashboard/dashboardTemplate.html | 6 + src/systems/dashboard/defaultLayout.js | 75 ++++- src/systems/dashboard/gridEngine.js | 294 +++++++++++++++++- .../dashboard/widgets/infoBoxWidgets.js | 20 +- .../dashboard/widgets/inventoryWidget.js | 4 +- .../widgets/presentCharactersWidget.js | 4 +- .../dashboard/widgets/userStatsWidget.js | 4 +- style.css | 97 ++++++ 13 files changed, 668 insertions(+), 87 deletions(-) diff --git a/docs/IMPLEMENTATION_PLAN.md b/docs/IMPLEMENTATION_PLAN.md index d478ca2..068d264 100644 --- a/docs/IMPLEMENTATION_PLAN.md +++ b/docs/IMPLEMENTATION_PLAN.md @@ -708,26 +708,26 @@ **Epic 2 Status:** -**Core Widgets Implemented (Needs Testing):** +**Core Widgets Implemented (Code Complete - Needs Testing):** - [x] Task 2.1: User Stats Widget (408 lines) - [x] Task 2.2: Info Box Widgets - 5 modular widgets (545 lines) - [x] Task 2.3: Present Characters Widget (377 lines) - [x] Task 2.4: Inventory Widget (925 lines) -**Optional Widgets (Deferred to post-v2.0):** +**Remaining Widgets (To Do Next):** - [ ] Task 2.5: Classic Stats Widget (standalone) - [ ] Task 2.6: Dice Roller Widget - [ ] Task 2.7: Last Roll Display Widget -**Epic 2 Complete When (TESTING IN PROGRESS):** -- [ ] TESTING: All core widgets converted and functional -- [ ] TESTING: Each widget draggable and resizable -- [ ] TESTING: All existing functionality preserved -- [ ] TESTING: Configuration options work for each widget -- [ ] TESTING: No regressions in data persistence -- [ ] TESTING: Mobile responsive behavior maintained +**Epic 2 Complete When:** +- [ ] All 7 widgets converted and functional (4 done, 3 remaining) +- [ ] Each widget draggable and resizable +- [ ] All existing functionality preserved +- [ ] Configuration options work for each widget +- [ ] No regressions in data persistence +- [ ] Mobile responsive behavior maintained -**Next Step:** Complete integration and end-to-end testing before marking Epic 2 complete +**Next Step:** Continue with Tasks 2.5, 2.6, 2.7 OR test/integrate existing widgets first (awaiting direction) --- diff --git a/index.js b/index.js index e3756f4..b101296 100644 --- a/index.js +++ b/index.js @@ -547,15 +547,21 @@ async function initUI() { }; // Initialize dashboard + console.log('[RPG Companion] Current dashboard settings:', extensionSettings.dashboard); const manager = await initializeDashboard(dashboardDependencies); if (manager) { console.log('[RPG Companion] Dashboard v2 initialized successfully'); + console.log('[RPG Companion] Manager instance:', manager); - // Check if this is first time - create default layout - if (!extensionSettings.dashboard || !extensionSettings.dashboard.tabs) { + // Check if this is first time OR if dashboard is empty - create default layout + if (!extensionSettings.dashboard || !extensionSettings.dashboard.tabs || extensionSettings.dashboard.tabs.length === 0) { console.log('[RPG Companion] Creating default dashboard layout...'); 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 { console.warn('[RPG Companion] Dashboard initialization returned null, falling back to legacy rendering'); diff --git a/src/core/state.js b/src/core/state.js index c0f7a36..ee16064 100644 --- a/src/core/state.js +++ b/src/core/state.js @@ -84,7 +84,9 @@ export let extensionSettings = { version: 2, // Dashboard config version gridConfig: { - columns: 12, // Grid columns + // Columns calculated dynamically by GridEngine (2-4 based on panel width) + // Mobile (≤1000px screen): always 2 columns + // Desktop (>1000px screen): 2-4 columns based on panel width rowHeight: 80, // Pixels per row gap: 12, // Gap between widgets (px) snapToGrid: true, // Auto-snap enabled diff --git a/src/systems/dashboard/dashboardIntegration.js b/src/systems/dashboard/dashboardIntegration.js index 0d5cff4..366b43a 100644 --- a/src/systems/dashboard/dashboardIntegration.js +++ b/src/systems/dashboard/dashboardIntegration.js @@ -8,7 +8,7 @@ import { extensionName } from '../../core/config.js'; import { extensionSettings } from '../../core/state.js'; import { saveSettings } from '../../core/persistence.js'; -import { renderExtensionTemplateAsync } from '../../../../../extensions.js'; +import { renderExtensionTemplateAsync } from '../../../../../../extensions.js'; import { DashboardManager } from './dashboardManager.js'; import { WidgetRegistry } from './widgetRegistry.js'; @@ -136,6 +136,10 @@ function getInlineDashboardTemplate() {
+
+ + +