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.
Added missing CSS for dashboard v2 header and container:
- .rpg-dashboard-container: Flexbox column layout with gap
- .rpg-dashboard-header: Flexbox row with space-between
- .rpg-dashboard-header-left/right: Flex containers for button groups
- .rpg-dashboard-btn: Button styling with theme variables
- .rpg-dashboard-grid: Grid container styling
Also fixed dashboardManager.js to preserve template structure:
- Changed createContainerStructure() to query existing elements first
- Only creates elements if template didn't provide them
- Prevents clearing the entire container and losing the header
This fixes the issue where all components (header, buttons, widgets)
were stacking on top of each other due to missing layout CSS.
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
- Add dashboard.tabs array and defaultTab to DashboardManager state
- Create default 'main' tab on initialization
- Pass dashboard object to TabManager instead of event handlers
- Register tab change listeners using onChange pattern
- Fix applyDashboardConfig to directly manipulate tabs array
- Fix getDashboardConfig to include all tab properties and defaultTab
- Remove non-existent deleteAllTabs() call
- 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
- Comprehensive inventory management with 3 sub-tabs (On Person/Stored/Assets)
- List/Grid view modes per sub-tab with toggle buttons
- Storage locations with add/remove/collapse functionality
- Full CRUD operations for items (add/edit/remove)
- Inline forms for adding items and locations with Enter/Escape support
- Per-widget instance state management for tabs and view modes
- Import parseItems/serializeItems utilities for data handling
- Import sanitizeItemName/sanitizeLocationName for security
- Vanilla JS implementation, no jQuery dependencies
- All 4 core widgets now complete
Created modular, independently draggable Info Box widgets:
1. Calendar Widget (2x2):
- Date/weekday/month/year display
- Abbreviated display with full edit
- Editable date components
2. Weather Widget (3x2):
- Weather emoji + forecast text
- Fully editable emoji and text
3. Temperature Widget (2x2):
- Animated thermometer visualization
- Color-coded (blue < 10°C, green < 25°C, red ≥ 25°C)
- Editable temperature value
4. Clock Widget (2x2):
- Analog clock with hour/minute hands
- Real-time hand positioning based on time
- Editable time display
5. Location Widget (6x2):
- Map background with marker
- Editable location text
- Responsive width
All widgets:
- Share common infoBox data source
- Parse mixed emoji/text formats
- Handle missing data gracefully
- Update shared data on edit
- Vanilla JS (no jQuery)
- Mobile-friendly editable fields
Epic 2 progress: 2/4 core widget groups complete
Total widgets created: 6 (1 User Stats + 5 Info Box widgets)
- Created LayoutPersistence class with full save/load/import/export
- Implemented debounced auto-save (500ms after changes)
- Added manual save, export (JSON download), import (file picker)
- Added reset to default with confirmation
- Comprehensive dashboard validation
- Event-driven architecture with onChange listeners
- Save status indicator with real-time updates
- Event log for all persistence operations
- Auto-load saved layout on startup
- Complete integration test with all systems
Task 1.8 complete in <15 minutes (estimated 2-3 days)
EPIC 1: DASHBOARD INFRASTRUCTURE COMPLETE! 🎉
- Change resize handles from hover-only to always visible in edit mode
- Handles now show at 60% opacity in edit mode
- Brighten to 100% opacity on hover for visual feedback
- Update UI hint to explicitly mention green dots on corners/edges
- Makes resize functionality more discoverable
- Improves UX by showing affordances clearly
- Add event target check in DragDropHandler to ignore resize handles
- Add event target check to ignore widget edit controls
- Use e.target.closest() to check parent elements
- Add e.stopPropagation() in resize handle event handlers
- Replace simplified ResizeHandler with fully functional version
- Now resize handles work correctly without triggering drag
- Both mouse and touch events properly handled
- Fixes integration issue where resizing always triggered dragging
- Add ResizeHandler class with 8 resize handles (4 corners + 4 edges)
- Implement unified mouse + touch resize events
- Add real-time dimension overlay showing current size
- Grid overlay with cell highlighting during resize
- Enforce min/max size constraints (2×2 to 12×10)
- Support resizing from all 8 directions with proper cursors
- Escape key cancels resize and restores original size
- Handle position adjustment when resizing from top/left
- Touch delay (150ms) for mobile scroll compatibility
- Create mobile-ready test harness with:
- Hover-activated resize handles with fade transitions
- Touch-optimized UI
- Real-time statistics
- Event logging
- Works on desktop and mobile
- 550 lines core code, 920 lines test suite
- Comprehensive JSDoc documentation
- Parser now detects when model returns multiple trackers in one code block
- Splits combined blocks using regex to extract each section individually
- Maintains backward compatibility with separate code blocks
- Prevents overwriting sections with duplicate checks
- Handles both correct format and model errors gracefully
The mobile refresh button was always visible on mobile. It should only
appear when BOTH conditions are met:
- RPG panel is open
- Generation mode is Separate (not Together)
Changes:
- Added opacity: 0 and pointer-events: none to base .rpg-mobile-refresh
- CSS shows button when panel open AND not .rpg-hidden-mode class
- Updated updateGenerationModeUI() to toggle .rpg-hidden-mode on mobile button
- Together mode: adds .rpg-hidden-mode class (keeps button hidden)
- Separate mode: removes .rpg-hidden-mode class (allows CSS to show it)
Result: Mobile refresh FAB only appears when panel is open AND in
Separate mode. Stays hidden when panel closed OR in Together mode.
Reverted HTML replacement approach and restored the cleaner CSS-based
animation from commit 1855085.
Previous (wrong) approach:
- Replaced button HTML with spinner
- Modified both desktop and mobile buttons in apiClient.js
- Messy and inconsistent
Restored (correct) approach:
- Add/remove .spinning CSS class in click handler
- CSS animates only the icon inside the button
- Button itself stays unchanged
- Much cleaner implementation
Changes:
- Reverted apiClient.js changes from commit 9a49433
- Added .spinning CSS class and @keyframes rpg-spin
- Updated index.js click handler to bind both buttons
- Uses addClass/removeClass for clean animation control
- Includes drag detection to prevent accidental clicks
Now the mobile FAB icon spins smoothly when refreshing!
The spinning animation when refreshing existed but only worked on
the desktop button. Mobile FAB was never updated with the spinner.
Changes:
- Update both desktop and mobile buttons when refresh starts
- Desktop shows: spinner + 'Updating...' text
- Mobile FAB shows: spinner icon only (no text)
- Both buttons restore properly when done
Now mobile users see the spinner animation when tapping refresh!
Character names containing regex special chars (like brackets) were
causing 'Invalid regular expression' errors when building character
thoughts HTML. Now properly escapes characters before RegExp creation.
- Updated month/weekday/year field handlers to check for both 'Date:' and '🗓️:' formats
- Field updates now preserve the existing format (text or emoji)
- New date lines created in text format to match current standard
- Updated all field type checks (temperature, time, location) for dual-format support
- Fixes issue where editing date fields didn't update the prompt
PROBLEM (from Salixfire's debug logs):
- Parser successfully extracted 5 characters
- Log showed complete characters array
- Log stopped abruptly before "✓ HTML rendered to container"
- This indicates exception thrown during HTML building (lines 217-281)
DIAGNOSIS:
- Parsing works perfectly (5 characters extracted)
- Code crashes somewhere in the HTML building loop
- User sees placeholder because exception prevents HTML from rendering
- No error logs because crash happens silently
LIKELY CAUSES:
- getGroupMembers() throwing exception
- Character avatar lookup failing
- getSafeThumbnailUrl() failing
- Missing null checks
SOLUTION:
Added comprehensive error handling and debug logging:
1. Added logging before HTML building starts
- "Starting HTML generation for N characters"
- This confirms code reaches HTML building phase
2. Wrapped each character in try-catch
- Logs each character being processed: "Building HTML for character 1/5: Lady Julia"
- Prevents one character error from crashing entire function
- Code continues with other characters even if one fails
3. Added detailed avatar lookup logging:
- "Looking up avatar for: {name}"
- "In group chat, checking group members..."
- "Group members count: N"
- "Found avatar in group members/all characters/current character"
- Shows final avatar URL (first 50 chars)
4. Wrapped getGroupMembers() in try-catch
- Catches group-specific errors
- Logs error but continues with regular character lookup
5. Added success/error logging for each character:
- "✓ Successfully built HTML for {name}"
- "✗ ERROR building HTML for {name}: {error.message}"
- Logs full error stack for debugging
6. Added completion log:
- "Finished building all character cards"
- Confirms loop completed successfully
EXPECTED OUTCOME:
Next debug log from Salixfire will show EXACTLY:
- Which character is causing the crash (if any)
- What operation is failing (avatar lookup, HTML building, etc.)
- Full error message and stack trace
- Whether code completes or crashes
This will allow us to identify and fix the root cause.
Files changed:
- src/systems/rendering/thoughts.js: Added try-catch blocks and comprehensive logging
- Merged remote changes from origin/main
- Updated time display to show end time (14:22) instead of start time (14:07)
- Clock widget now reflects the end time from Time: HH:MM → HH:MM format
- Changed time display to show timeEnd (second time in range) instead of timeStart
- Clock now displays 14:22 instead of 14:07 when time format is '14:07 → 14:22'
- Falls back to timeStart if timeEnd not available, then to '12:00' default
PROBLEM (reported by Salixfire):
- Present Characters panel showing placeholder instead of actual characters
- Thought bubbles work correctly but main panel doesn't
- Need to toggle settings off/on to get thoughts to appear
- No way to debug on mobile devices
CHANGES:
1. Added comprehensive debug logging to renderThoughts() (src/systems/rendering/thoughts.js):
- Log when function is called and with what data
- Log each line being parsed and how many parts it has
- Log character extraction (emoji, name, traits, relationship, thoughts)
- Log why characters are accepted or rejected
- Log final character count and whether showing placeholder
- All logs visible in mobile-friendly debug panel
2. Fixed toggle to refresh content (index.js:283-291):
- When user toggles "Show Present Characters" on, now calls renderThoughts()
- Previously only showed/hid container without refreshing content
- This ensures panel displays latest data when toggled
3. Normalized parsing logic (src/systems/rendering/thoughts.js:111):
- Changed renderThoughts() to require >= 3 parts (was >= 2)
- Now matches updateChatThoughts() requirement
- Consistent with current prompt format: Emoji:Name | Relationship | Thoughts
- Removed 2-part format fallback code (unreachable now)
- Both functions now use same validation rules
EXPECTED OUTCOME:
- User can enable debug mode and see exactly what data is being parsed
- Toggle will properly refresh the panel content
- We can diagnose from debug logs why placeholder is shown
- More consistent behavior between main panel and thought bubbles
Debug logs will help us identify:
- If characterThoughts data is empty/malformed when renderThoughts() is called
- If parsing is rejecting valid character data
- If there's a timing issue with data availability
- What the actual AI response format looks like
Related to previous commit (37878fc) that added debug mode toggle.
PROBLEM:
- Debug logs only accessible via browser console (impractical on mobile)
- User (Salixfire) reporting parsing issues but can't debug on mobile device
- Need mobile-friendly debug mode for troubleshooting data display issues
SOLUTION:
Implemented debug toggle FAB button following exact pattern of existing mobile FABs:
Files Changed:
- src/core/state.js: Added debugFabPosition and debugMode to extensionSettings
- src/core/config.js: Added debugFabPosition to defaultSettings (reference)
- index.js: Created debug toggle button, imported setupDebugButtonDrag
- style.css: Added debug toggle CSS matching mobile FAB pattern (44px, grab cursor, theme colors)
- src/systems/ui/mobile.js: Added setupDebugButtonDrag() with drag-to-reposition
- src/systems/ui/debug.js: Removed button creation, added just-dragged check, updated visibility control
Implementation Details:
- Button created in index.js (not debug.js) following mobile FAB pattern
- CSS matches mobile toggle/refresh buttons (44px, theme colors, grab cursor, user-select: none)
- Drag support with touch/mouse handlers, 200ms/10px threshold
- Position saved to extensionSettings.debugFabPosition
- Just-dragged flag prevents accidental clicks after drag
- Mobile (≤1000px): slide from right with rpg-mobile-open/closing classes
- Desktop (>1000px): slide from bottom with rpg-debug-open class
- Event delegation for reliable click handling
- Default position: bottom 140px, left 20px (below other FABs)
Bug Fix:
- Initial implementation had debugFabPosition only in config.js
- extensionSettings uses state.js as source, not config.js
- Without debugFabPosition in state.js, button had no position and was invisible
- Now properly initialized in both files
The debug button is hidden by default (debugMode: false) and shown when user enables debug mode in RPG Companion settings. This allows Salixfire to view parser logs on mobile and troubleshoot the data display issues.
Added event.preventDefault() and event.stopPropagation() to close button handler
to prevent any interference from parent elements.
Also added pointer-events: none to button icons to ensure clicks on the icon
register on the button itself, not the <i> element.
Changes:
- src/systems/ui/debug.js: Added e.preventDefault/stopPropagation to close handler
- src/systems/ui/debug.js: Added console.log for debugging
- style.css: Added pointer-events: none to .rpg-debug-actions button i
Add comprehensive debug logging system that's accessible on mobile devices
where browser console is impractical.
**New Features:**
- Debug mode toggle in extension settings (🔍 Debug Mode)
- Mobile-friendly debug panel with slide-up UI
- Red bug FAB button to toggle debug log viewer
- Copy logs to clipboard functionality
- Auto-scrolling log display with timestamps
- Stores last 100 log entries to prevent memory issues
**Parser Enhancements:**
- All parser logs now use debugLog() helper function
- Logs only appear in UI when debug mode is enabled
- Console.log still works for desktop debugging
- Full visibility into parsing pipeline:
- Raw AI response preview
- Code blocks found and matched
- Stats extraction (health, energy, mood, etc.)
- Inventory parsing (v1 and v2)
- Final values saved to settings
**UI Components:**
- src/systems/ui/debug.js: Debug panel creation and management
- style.css: Mobile-first debug panel styles (FAB + slide-up panel)
- Desktop view: Smaller panel in bottom-right corner
**Settings:**
- src/core/config.js: Added debugMode default (false)
- src/core/state.js: Added debug logs storage array
- settings.html: Added debug mode checkbox
- index.js: Wire up debug toggle and initialize UI
**Usage for Mobile Users:**
1. Enable "Debug Mode" in RPG Companion settings
2. Red bug button appears (bottom-left)
3. Tap bug button to view logs
4. Use "Copy" to share logs for troubleshooting
5. Logs show exactly what AI generated and how parser handled it
This addresses the issue where users on mobile can't access browser
console to diagnose parsing problems (vanishing attributes, placeholder
characters, etc.). Now they can view and share logs directly.
- Add detailed console logging throughout parseResponse() and parseUserStats()
to help diagnose parsing issues reported by users
- Make parser more resilient to format variations:
- Accept "Stats", "User Stats", "Player Stats" headers
- Accept "Info Box", "Scene Info", "Information" headers
- Accept "Present Characters", "Characters", "Character Thoughts" headers
- Add keyword-based fallback when headers are missing
- Support "Mood:" prefix in addition to "Status:" for mood/conditions
- Support dash separator in addition to comma
- Add length check (<=10 chars) for emoji/mood to avoid false matches
- Log full parsing pipeline: input -> matches -> extraction -> final values
- Log error stack traces for better debugging
This should help diagnose issues where attributes vanish, characters show
as placeholder, or data is generated but not displayed/refreshed correctly.
- Added parsedFields tracking to prevent parsing the same field twice
- Split combined if conditions into separate checks for text vs emoji format
- Text format (Temperature:, Time:, etc.) is now parsed first and preferred
- Emoji format (��️:, 🕒:, etc.) only parsed if text format not found
- Prevents duplicate entries when AI generates both formats in output
- Fixes duplicate Temperature, Time, Location lines in tracker data
- Moved refresh button creation from template.html to index.js (appended to body)
- Created new CSS class .rpg-mobile-refresh (exact copy of .rpg-mobile-toggle pattern)
- Uses opacity for show/hide instead of display (CSS controls visibility based on panel state)
- Show when panel open (body:has(.rpg-panel.rpg-mobile-open))
- Hide when panel closed (opacity: 0, pointer-events: none)
- Updated constrainFabToViewport() to accept optional button parameter
- Automatically detects which button and uses correct settings (mobileFabPosition or mobileRefreshPosition)
- Simplified updateGenerationModeUI() - CSS handles visibility
- Kept full drag functionality with touch and mouse support
- Button positioned via JavaScript with saved position
- z-index: 1001 (above panel, below toggle at 10002)
- Repositioned mobile refresh button to bottom-right (80px from bottom)
- Implemented full drag-to-reposition functionality
* Touch and mouse support with 200ms/10px threshold
* RequestAnimationFrame for smooth dragging
* Position saved to extensionSettings.mobileRefreshPosition
* Viewport constraints with 10px padding
- Fixed sticky tap highlight issue
* Added -webkit-tap-highlight-color: transparent
* Added blur() on click to remove focus
* Set user-select: none and touch-action: none
- Show/hide based on panel state
* Only visible when panel is expanded (rpg-mobile-open)
* Listens to rpg-panel-toggled events
* Auto-hides when panel closes
- Prevent accidental refresh after drag
* just-dragged flag prevents click for 100ms
* Click handler checks flag before executing
- Changed from absolute to fixed positioning for viewport-wide dragging
- Added mobileRefreshPosition to default settings (bottom: 80px, right: 20px)
- z-index: 99 (below FAB toggle at 100)
**Changes:**
1. Move button to float over all tabs (not just Stats)
- Removed from userStats.js HTML
- Added to template.html as floating absolute element
- Now visible on Status, Info, and Inventory tabs
2. Fix sticky black focus state
- Added :focus { outline: none } to CSS
- Call blur() after click to clear focus immediately
3. Add refresh animation
- Button spins during updateRPGData() call
- Smooth 0.8s rotation with @keyframes
- Uses .spinning class added/removed in JS
4. Improve theming and positioning
- Positioned absolute top-right (10px, 10px)
- Increased to 44px for better touch target
- z-index: 100 to float above content
- Already uses theme colors (--rpg-highlight, --rpg-text)
Mobile UX now:
✅ Button visible on all tabs (floating)
✅ Spins smoothly when refreshing
✅ No sticky black state after tap
✅ Properly themed across all themes
- Use SillyTavern macros ({{persona}}, {{description}}, {{personality}}) for character context
- Fix preset restoration after tracker generation using /preset command
- Fix weather editing bug by tracking specific weather line index
- Support both emoji and text formats for Info Box field editing
- Remove unused showdown import and fix missing semicolons
- Removed import of getCurrentPresetName (not available in SillyTavern)
- Simplified preset switching to not track/restore previous preset
- Removed restorePreset() function
- Fixes module loading error preventing extension activation
- Note: Users enabling separate preset will manually switch presets back