Root Cause:
After auto-arrange reorganized widgets, code attempted to call onResize()
for ALL tabs' widgets. However, switchTab() clears the this.widgets Map
and only re-renders the current tab. Result: this.widgets.get(widgetId)
returned undefined for non-active tabs, so onResize() was never called.
Additionally, User Attributes onResize() was receiving grid units (2)
instead of pixel width (~290px), causing calculateOptimalColumns() to
think only 2 columns would fit when 3 columns could easily fit.
The Fix:
1. Iterate over this.widgets Map (currently rendered widgets only)
instead of this.dashboard.tabs (includes non-rendered widgets)
2. Use container.offsetWidth (pixel width) in onResize instead of grid units
3. Enable DEBUG flags temporarily to reveal previously suppressed logs
Result:
- onResize() now called for all visible widgets after auto-arrange
- User Attributes correctly maintains 3×3 grid at 2 grid units wide
- No more 2×5 layout with orphaned last attribute
Fixes: User Attributes breaking into 2×5 grid after auto-arrange
The Add Widget modal is moved to document.body on first use (to escape
panel transform constraints), which causes it to lose the panel's solid
background context and become transparent.
Solution: After moving modal to body, apply the same theme-aware background
logic used in tab context menu and prompt dialogs:
- If themed: Copy data-theme attribute for CSS overrides
- If default: Read computed colors from panel, convert to opacity 1.0
Changes:
- dashboardIntegration.js: Add background fix after modal move to body
Fix Exit Edit Mode button:
- Move button before Lock button (left side as requested)
- Add visibility toggle in editModeManager.js
- Show done button when entering edit mode
- Hide done button when exiting edit mode
- Fixes critical bug where button stayed hidden permanently
Add tab management UI:
- Create promptDialog.js for text input modals (rename, create)
- Create tabContextMenu.js with right-click context menu on tabs
- Integrate with TabManager API for all operations:
- Add New Tab (with validation)
- Rename Tab (with validation)
- Change Icon (icon picker with 20 common icons)
- Duplicate Tab (copies widgets)
- Delete Tab (with confirmation, blocks if last tab)
- Auto-save after tab operations
- Event delegation for dynamic tab elements
Files changed:
- dashboardTemplate.html: Move done button to correct position
- editModeManager.js: Add visibility toggles in enter/exit methods
- promptDialog.js: New dialog system for text input
- tabContextMenu.js: New context menu system
- dashboardIntegration.js: Initialize tab context menu
Implements combined widget that merges Calendar, Weather, Temperature, Clock,
and Location into one tabbed interface, reducing Scene tab from 7 to 3 widgets.
Phase 2: Scene Info Multi-View Widget
New Features:
- sceneInfoWidget.js: Tab-based multi-view widget
- Reuses existing infoBox widget render functions (no code duplication)
- Tab bar with icon + label for each view (📅 Cal, 🌤️ Wea, 🌡️ Tmp, 🕐 Clk, 📍 Loc)
- View switching by toggling CSS display (preserves handlers and state)
- Smart empty state detection (hides tabs for widgets with no data)
- Configurable: select views, default view, show/hide empty views
- Per-instance state management (activeSubTab persists per widget)
- Size: 2×3 default (tab bar + content)
- Registered in dashboardIntegration.js
Default Layout Changes:
- Scene tab: 7 widgets → 3 widgets (57% reduction)
- Old: Calendar (1×1) + Weather (1×1) + Temp (1×1) + Clock (1×1) + Location (2×2)
- New: Scene Info (2×3) - combined multi-view widget
- Repositioned: Recent Events (y: 4 → 3), Present Characters (y: 6 → 5)
- Vertical space: 10 rows → 9 rows (10% reduction)
Benefits:
- Reduces mobile vertical scroll by ~30%
- Cleaner Scene tab layout
- Individual widgets still available for customization
- Consistent UX with Inventory/Quests tab patterns
- Leverages existing CSS (.rpg-inventory-subtabs)
Technical Approach:
- Render all views once on mount (not destroyed on tab switch)
- Toggle visibility with CSS display property
- Preserves widget edit handlers and state
- Empty views filtered based on data availability
Individual calendar/weather/temperature/clock/location widgets remain
available in registry for users who prefer separate widgets.
Testing Required:
- Tab switching between all 5 views
- Empty state detection (remove data from infoBox)
- Edit functionality in each view
- Config changes (remove views, change default)
- Mobile responsive behavior
- Theme compatibility
Reduces header button crowding by keeping edit controls in menu at all screen sizes.
Changes:
- Add .rpg-menu-only-btn class to Add/Export/Import/Done buttons
- Remove display toggling from EditModeManager (simpler state machine)
- Update HeaderOverflowManager to:
- Always hide menu-only buttons (never show inline)
- Mark them as available for menu (wasVisible = 'true')
- Filter menu items based on edit mode state
- Add setEditModeManager() for edit state access
- Wire editModeManager to headerOverflowManager in integration
Result:
- Full mode: 6 visible buttons (was 10 with edit mode active)
- Overflow mode: 3 priority + menu with 3 standard + 4 edit (when active)
- Compact mode: 3 priority + hamburger with all actions (filtered by edit state)
Edit mode controls (Add Widget, Export, Import, Done) now only appear in
dropdown/hamburger menu, never inline. This creates cleaner visual hierarchy
and eliminates layout jumping when toggling edit mode.
Existing menu refresh on edit mode toggle (line 305) ensures menu updates
with correct items when entering/exiting edit mode.
Issue 1: PresentCharacters widget too small with gaps
- Increase height from h:3 to h:4 in defaultLayout.js
- Widget's defaultSize is h:2 but layout had h:3, creating mismatch
- Now properly fills vertical space without gaps
Issue 2: Recent Events not appearing in reset/auto-arrange
Root cause: previousTrackerConfig starts as null, preventing widget detection
Fixes:
- Initialize previousTrackerConfig on dashboard load (dashboardIntegration.js)
- Deep clone trackerConfig after dashboard init
- Enables detectConfigChanges() to work on first load
- Reset previousTrackerConfig to null in resetLayout() (dashboardManager.js)
- Ensures fresh detection after layout reset
- Prevents stale comparison data
Verified: Recent Events enabled by default in tracker config (state.js:95)
Result:
✅ PresentCharacters fills space properly
✅ Recent Events appears in reset layout
✅ Recent Events appears in auto-arrange
✅ Tracker editor enable/disable now works correctly
- Add registerRecentEventsWidget() in infoBoxWidgets.js
- Implement notebook-style UI with rings, bullet points, and editable events
- Support max 3 events with + placeholders for new entries
- Parse 'Recent Events: event1, event2, event3' format from infoBox
- Register widget in dashboardIntegration.js
- Add to default layout Scene tab (row 4-5, below location)
- Integrate with tracker system:
- Add to WIDGET_TO_TAB_MAP (maps to tab-scene)
- Add to shouldWidgetBeRemoved() rules
- Add to detectConfigChanges() for re-addition support
- Completes v2 widget migration - all tracker features now have widgets
Implemented hierarchical customization where trackerConfig controls content
(fields, names, AI instructions) and dashboard controls layout (positioning,
tabs, widget instances). Both systems now work together instead of conflicting.
**Widget Integration:**
- userStatsWidget: Respects trackerConfig for stat names and enable/disable
- userStatsWidget: Supports per-widget stat filtering via config.visibleStats
- userStatsWidget: Dynamically generates config options from trackerConfig
- infoBoxWidgets: All widgets (calendar, weather, temperature, clock, location)
check trackerConfig.infoBox.widgets.*.enabled before rendering
- Widgets show "disabled" state with link to Tracker Settings when field disabled
**Dashboard UI:**
- Added Tracker Settings button to dashboard header (sliders icon)
- Button opens tracker editor modal for global field configuration
- Button positioned next to Edit Layout for clear separation of concerns
**Tracker Editor:**
- Added help text explaining relationship with dashboard system
- Help text clarifies: Tracker Settings = content, Edit Layout = positioning
- Styled with info banner at top of modal
**Migration:**
- Enhanced migrateV1ToV2Dashboard() to respect trackerConfig
- Removes userStats widget if all stats disabled in trackerConfig
- Removes presentCharacters widget if thoughts disabled in trackerConfig
- Ensures smooth upgrade path from v1.x
**CSS:**
- Added .rpg-editor-help styling for tracker editor help banner
- Added .rpg-widget-empty-state for disabled widget messaging
- Info-style banner with icon and clear typography
**Result:**
Two-level customization system:
1. Tracker Settings (global): What fields exist, their names, AI instructions
2. Edit Layout (local): Where widgets appear, per-widget overrides
Files modified:
- src/systems/dashboard/widgets/userStatsWidget.js (+75 lines)
- src/systems/dashboard/widgets/infoBoxWidgets.js (+67 lines)
- src/systems/dashboard/dashboardIntegration.js (+15 lines)
- src/systems/dashboard/dashboardTemplate.html (+4 lines)
- src/systems/dashboard/defaultLayout.js (+22 lines)
- template.html (+6 lines)
- style.css (+58 lines)
BREAKING CHANGE: applyDashboardConfig() now accepts optional second parameter for optimization
Add auto-arrange confirmation dialog:
- Add warning popup before auto-arranging widgets across all tabs
- Follows same pattern as reset layout confirmation
- Prevents accidental destructive operations
Fix text selection interference:
- Apply user-select: none to entire .rpg-widget in edit mode
- Previously only applied to .rpg-widget-content
- Prevents text selection when dragging widgets, especially in attribute boxes
- Improves drag interaction on both desktop and mobile
Fix edit controls visibility:
- Add CSS rule to completely hide edit controls outside edit mode
- Controls now properly hidden when not in edit mode
- Settings button (⚙) kept for future widget configuration
Fix input disabling in edit mode:
- Call disableContentEditing() after tab switches in onTabChange()
- Call disableContentEditing() in syncAllControls()
- Ensures text fields remain non-editable in edit mode after all operations
Fix resize grid background rendering:
- Copy drag handler grid logic EXACTLY to resize handler
- Use gridEngine.calculateGridHeight(widgets) instead of manual calculation
- Add proper remToPixels() conversions for gap and rowHeight
- Pass widgets array through initWidget() and startResize()
- Grid now renders correctly during resize, matching drag behavior
Optimize layout operations:
- Add skipInitialSwitch option to applyDashboardConfig()
- Prevents redundant clearGrid() calls during resetLayout()
- Defers rendering until after layout calculations complete
Files modified:
- dashboardIntegration.js: Auto-arrange confirmation
- dashboardManager.js: skipInitialSwitch option, input disabling, widgets array
- editModeManager.js: Input disabling in syncAllControls()
- resizeHandler.js: Grid rendering fixes with proper unit conversions
- style.css: Text selection prevention, edit controls visibility
This commit implements three major improvements to the dashboard system:
1. **Font Awesome Icons for Tabs**
- Replace emoji tab icons (📊, 🌍, 🎒) with Font Awesome classes
- Update defaultLayout.js with fa-solid icon classes
- Add automatic migration for existing saved dashboards with emoji icons
- Implement migrateEmojiIcons() to convert old emoji icons on load
- Update fallback icons throughout the system
2. **Custom Theme Support for Dashboard**
- Replace all --SmartTheme* variables with --rpg-* variables
- Ensure custom themes (sci-fi, fantasy, cyberpunk) apply to dashboard
- Update CSS for tabs, buttons, dropdowns, modals, and widget cards
- Dashboard now respects extension themes instead of main SillyTavern theme
3. **Styled Confirmation Dialogs**
- Create confirmDialog.js with showConfirmDialog() and showAlertDialog()
- Support three variants: danger (red), warning (yellow), info (blue)
- Add keyboard navigation (Enter/Escape) and accessibility features
- Replace all native confirm() and alert() calls with styled dialogs
- Add confirmation dialog modal to dashboardTemplate.html
Files Modified:
- src/systems/dashboard/confirmDialog.js (NEW)
- src/systems/dashboard/dashboardManager.js
- src/systems/dashboard/defaultLayout.js
- src/systems/dashboard/tabManager.js
- src/systems/dashboard/dashboardIntegration.js
- src/systems/dashboard/editModeManager.js
- src/systems/dashboard/widgets/inventoryWidget.js
- src/systems/dashboard/dashboardTemplate.html
- style.css
- Remove double left border accent on character cards by hiding inner border when inside widget container
- Increase maxAutoSize width from 3 to 4 columns to support large displays
- Fix viewport height calculation to use visible area instead of scrollable container height
- Change auto-layout boundary check from > to >= to prevent widgets extending beyond viewport
- Add Done button for cleaner edit mode exit UX
- Wire up Done button event listener in dashboardIntegration
- Add lock/unlock button to dashboard header (always visible)
- Lock state prevents dragging in both normal and edit modes
- Lock state prevents resizing in edit mode
- Icon changes: lock-open (unlocked) ↔ lock (locked)
- Hide resize handles and prevent grab cursor when locked
- Lock state persists across edit mode toggles
- Integrate lock checks in DragDropHandler and ResizeHandler
- Pass editManager reference to drag/resize handlers for lock state access
- Update onMessageReceived to populate extensionSettings.infoBoxData and characterThoughts for dashboard widgets
- Update updateRPGData (separate mode) with same extensionSettings population
- Add refreshDashboard() calls after data updates in both generation paths
- Fix onCharacterChanged to populate extensionSettings from loaded chat data
- Fix refreshDashboard() to use correct property name (registry not widgetRegistry)
- Reduce mood and weather widget font sizes to fit in 1x1 layout
This fixes Scene tab widgets not updating when receiving messages or loading chats from welcome screen.
Stats widget into 4 modular widgets
- Create userInfoWidget (avatar, name, level)
- Refactor userStatsWidget (stats bars only with smart sizing)
- Create userMoodWidget (mood emoji, conditions)
- Create userAttributesWidget (STR/DEX/CON/INT/WIS/CHA)
- Add category field to widgets for auto-layout grouping
- Register all new modular widgets in dashboardIntegration.js
All widgets include getOptimalSize() for smart content-aware auto-layout.
Part of Phase 1 & 3.1 of dashboard modularization plan.
Multiple critical fixes for dashboard v2:
**1. ResizeHandler error - updateContainerWidth is not a function**
- resizeHandler.js:288 was calling non-existent method
- Removed call - containerWidth tracked by ResizeObserver
- Resizing now functional
**2. DragDrop bug - widgets can't be released**
- endDrag() destructured widgets, originalX, originalY from dragState
- These fields were never added in startDrag()
- Added widgets parameter to initWidget() and startDrag()
- Store originalX, originalY, widgets in dragState
- dashboardManager now passes current tab widgets
- Widgets can now be dropped properly
**3. Widget content overflow**
- Added base .rpg-widget CSS: overflow: hidden, box-sizing: border-box
- Prevents content extending beyond widget bounds
- max-width: 100% on children
**4. Automatic layout migration**
- Old 12-column layouts (w: 8, w: 12) cause 500%+ widths in 2-4 column grid
- Added migrateOldLayouts() method
- Detects widgets with w > current column count
- Runs auto-layout to reposition for responsive grid
- Clears and re-renders current tab with new positions
- Saves migrated layout automatically
**5. Tab rendering**
- Implemented renderTabs() method
- Displays tab buttons with icons and names
- Active state highlighting
- Click handlers to switch tabs
**6. Collision prevention**
- Modified dragDrop endDrag() to check collisions
- Same-size widgets: swap positions
- Different sizes: revert to original
- Prevents overlapping widgets
**7. Edit mode fixes**
- Fixed edit button to call toggleEditMode()
- Added CSS to hide resize handles when not in edit mode
- Handles only visible in edit mode
**8. Icon-only header buttons**
- Auto-Arrange and Edit buttons now icon-only
- Saves horizontal space in header
All issues from user testing resolved.
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
- Create dashboardTemplate.html with dashboard container structure
- Dashboard header with tab navigation and control buttons
- Edit mode toggle, add widget, export/import layout buttons
- Add widget modal for selecting and adding widgets
- Widget configuration modal for widget settings
- Dashboard grid container for widget placement
- Create dashboardIntegration.js to handle dashboard initialization
- Initialize dashboard system and register all widgets
- Load dashboard template and inject into panel
- Set up event listeners for edit mode, add widget, export/import
- Create default layout with all core widgets
- Provide refreshDashboard() for updating widgets after data changes
- Support for fallback inline template if file load fails