- 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 user testing on Xiaomi Redmi 11 Pro 5G):
- FAB buttons (mobile toggle, refresh, debug) rendering off-screen
- Users need to scroll right to find buttons and drag them back
- Debug button invisible on some devices (never seen on Xiaomi)
- Issue occurs on devices with different viewport handling (MIUI Chrome)
ROOT CAUSE:
- Default positions scattered (right side, bottom side)
- Right-side positioning: buttons pushed off-screen on some devices
- Bottom positioning: buttons below fold when browser UI visible
- Fixed pixel values don't account for different screen sizes/viewports
SOLUTION:
Changed all FAB default positions to top-left stacked layout:
1. Mobile toggle FAB:
- WAS: top + 60px, right: 12px (TOP-RIGHT)
- NOW: top + 20px, left: 12px (TOP-LEFT)
2. Refresh button:
- WAS: bottom: 80px, right: 20px (BOTTOM-RIGHT)
- NOW: top + 80px, left: 12px (BELOW toggle)
3. Debug button:
- WAS: bottom: 140px, left: 20px (BOTTOM-LEFT)
- NOW: top + 140px, left: 12px (BELOW refresh)
BENEFITS:
- All buttons stacked vertically on LEFT side (always visible)
- Positioned safely below SillyTavern top bar
- 60px spacing between buttons (44px button + 16px gap)
- No scrolling needed to find buttons on first load
- calc(var(--topBarBlockSize) + Npx) accounts for dynamic top bar
- Users can still drag to preferred positions (saved per user)
NOTE: Only affects NEW users or users who clear their settings.
Existing users with saved FAB positions will not be affected.
Files changed:
- src/core/state.js: Default extensionSettings positions
- src/core/config.js: Reference default positions
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
- Created inventoryEdit.js module with updateInventoryItem() function
- Made all inventory item names editable with contenteditable (mobile-friendly)
- Added rpg-editable class to 6 item rendering locations:
* On Person (grid and list views)
* Stored (grid and list views)
* Assets (grid and list views)
- Added blur event listener to save changes on edit
- Validates and sanitizes edited names using sanitizeItemName()
- Syncs changes to lastGeneratedData and committedTrackerData (AI-visible)
- Shows full item text when editing (not truncated)
- Consistent UX with other editable fields in extension (stats, character traits, etc.)
- Re-renders inventory after successful edit or reverts on invalid input
- Added namesMatch() helper function with three matching strategies:
1. Exact match (fast path)
2. Strip parentheses match (handles 'Sabrina' vs 'Sabrina (Avatar)')
3. Word boundary match (handles 'Sabrina' vs 'Princess Sabrina')
- Replaced exact string comparison with fuzzy matching in 3 places:
- Group member lookup
- All characters search
- Current character 1-on-1 chat
- Fixes issue where character portraits showed placeholder when AI added
parenthetical or title additions to character names
- Prevents false positives (e.g., 'Sabrina' won't match 'Sabrina's Mother')
- Added persistent '+ Add Item' button at bottom of each storage location
- Button is centered and always visible (whether location has 0 or many items)
- Removed redundant '+ Add' button from storage location header (kept trash button)
- Reverted empty state to simple message instead of special button
- Added CSS for .rpg-storage-add-item-container to center button with margin
- Matches UI pattern from On Person and Assets tabs
- Removed debug logging from inventoryActions.js
Fixes bug where empty storage locations (with "None" as items) were
being removed during validation, preventing users from adding items
to newly created locations.
Problem:
- User creates location "Spatial Pouch" with no items (stores as "None")
- On next load, validateStoredInventory() is called
- Previous logic: if cleanedValue === "None", remove location
- Result: Location deleted before user can add items to it
- Console: "Location 'Spatial Pouch' had no valid items, removing"
Root Cause:
Previous granular validation (commit dc603b8) was too aggressive:
```javascript
if (cleanedValue !== 'None') {
cleaned[sanitizedKey] = cleanedValue;
} else {
// Remove location ❌
}
```
Solution:
"None" is a VALID state - it means location exists but is empty.
Always keep locations, only warn if items were actually corrupted.
```javascript
// Always keep the location (even if empty/"None")
cleaned[sanitizedKey] = cleanedValue;
// Warn only if we cleaned corrupted items (not just "None")
if (value !== cleanedValue && value.toLowerCase() !== 'none') {
console.warn(`Cleaned corrupted items from "${sanitizedKey}"`);
}
```
Behavior Changes:
Before:
- Location with "None" → Removed ❌
- Location with "__proto__, Sword" → Removed (cleaned to "Sword") ❌
After:
- Location with "None" → Kept as "None" ✓
- Location with "__proto__, Sword" → Kept as "Sword" (warns about cleaning) ✓
Impact:
✓ Empty locations persist across loads
✓ Users can now add items to new locations
✓ Corrupted items still cleaned (just location kept)
✓ Better logging (warns when actual corruption cleaned)
Fixes: Cannot add items to newly created storage locations
Fixes critical issue where manual edits (add location, add item, change
stats, etc.) were invisible to AI in next generation, causing edits to be
immediately overwritten.
Root Cause:
- Manual edits updated extensionSettings and lastGeneratedData
- AI prompt builder used committedTrackerData (NOT extensionSettings)
- Manual edits were never synced to committedTrackerData
- Result: AI didn't see manual changes, overwrote them
Solution - Sync to Both Data Stores:
All manual edit points now update BOTH:
1. lastGeneratedData (for display)
2. committedTrackerData (for AI context)
Files Modified:
1. **src/systems/interaction/inventoryActions.js**
- updateLastGeneratedDataInventory() now sets committedTrackerData.userStats
- Affects: add/remove items, add/remove locations
2. **src/systems/rendering/userStats.js**
- All 3 edit handlers now set committedTrackerData.userStats
- Affects: stat values (health, etc.), mood emoji, conditions
- Also fixed: now uses buildInventorySummary() for proper v2 format
3. **src/systems/rendering/infoBox.js**
- updateInfoBoxField() now sets committedTrackerData.infoBox
- Affects: date, weather, temperature, time, location
4. **src/systems/rendering/thoughts.js**
- updateCharacterField() now sets committedTrackerData.characterThoughts
- Affects: character emoji, name, traits, thoughts, relationship
Impact - Manual Edits Now Persist:
Before:
- Add location "Home" → Next generation → Location gone ❌
- Add item "Sword" → Next generation → Item gone ❌
- Change health to 25% → AI ignores it ❌
After:
- Add location "Home" → Next generation → Location persists ✓
- Add item "Sword" → Next generation → Item included ✓
- Change health to 25% → AI acknowledges low health ✓
Works in Both Modes:
- Together mode: AI sees manual edits in injected prompt ✓
- Separate mode: AI sees manual edits in context ✓
User Experience:
- "I edited it, so it should stay" - now works as expected
- AI builds on manual changes instead of overwriting them
- Minimal overhead (just string copies)
Fixes: Manual inventory/stats edits being overwritten by AI generation
Fixed alignment of user portrait in the Status tab. The avatar was
previously aligned to the left side of its container.
Change:
- Added justify-content: center to the avatar's flex container
- Avatar now centered horizontally (align-items already centered it vertically)
Before: Avatar stuck to left edge of its space
After: Avatar centered in its allocated space
File: src/systems/rendering/userStats.js:56
Enhances validation to clean corrupted items at load time while preserving
valid ones, rather than discarding entire sections. Also auto-capitalizes
first letter of items for consistency.
New capability - Granular Item Cleaning:
1. **cleanItemString()** (src/utils/security.js):
- Parses item string, removes bad items, re-serializes clean ones
- Applies ALL parsing rules: markdown, sanitization, length limits
- Used at load time to clean persisted data immediately
- Returns "None" if no valid items remain
2. **Enhanced validateStoredInventory()**:
- Now cleans items within each location
- Only removes locations if ALL items are invalid
- Example: "Home": "Sword, __proto__, Shield" → "Home": "Sword, Shield"
- Example: "Bad": "__proto__, constructor" → location removed
3. **Enhanced validateInventoryStructure()** (src/core/persistence.js):
- Cleans onPerson, stored, and assets at load time
- Logs exactly what was cleaned for debugging
- Auto-saves cleaned data back to storage
Auto-Capitalization:
- Added to cleanSingleItem() in itemParser.js
- Capitalizes first letter of each item after all cleaning
- Preserves rest of case: "iPhone" → "iPhone" (not "Iphone")
- Examples: "sword" → "Sword", "3x potions" → "3x potions"
Behavior examples:
Before (threw away entire array):
- "Home": "Sword, " + "A".repeat(600) + ", Shield"
→ Entire location lost
After (granular cleaning):
- "Home": "Sword, " + "A".repeat(600) + ", Shield"
→ "Home": "Sword, AAA...(500 chars), Shield"
Before (kept corrupted data):
- onPerson: "sword, __proto__, shield"
→ Stored as-is, filtered only at render
After (cleaned at load):
- onPerson: "Sword, Shield"
→ Cleaned and saved immediately, capitalized
Benefits:
- ✓ Preserves valid items when some are corrupted
- ✓ Cleans data at source, not just at render
- ✓ Detailed logging of what was cleaned
- ✓ Consistent capitalization across all items
- ✓ Single source of truth for "valid item"
Fixes Bug #3: Locations disappearing when switching tabs or on reload.
Root cause: inventory.stored could become corrupted (null, array, or
undefined) due to incomplete validation during load/save operations.
Solution - Defense in Depth:
1. **Persistence Layer** (src/core/persistence.js):
- New validateInventoryStructure() function
- Validates on loadSettings() and loadChatData()
- Checks all v2 fields (onPerson, stored, assets, version)
- Ensures stored is always a plain object
- Validates stored keys/values using validateStoredInventory()
- Auto-repairs corrupted data with console warnings
- Persists repairs immediately
2. **Form State Management** (src/systems/interaction/inventoryActions.js):
- Enhanced restoreFormStates() to detect deleted locations
- Cleans up orphaned form states automatically
- Prevents errors from forms referencing non-existent locations
Validation checks:
- ✓ inventory.stored is object (not null/array/undefined)
- ✓ All stored keys are safe (no __proto__, constructor, etc.)
- ✓ All stored values are strings
- ✓ onPerson and assets are strings
- ✓ version field exists
Auto-repair scenarios:
- Corrupted stored → reset to {}
- Invalid onPerson/assets → reset to "None"
- Missing version → set to 2
- Dangerous keys → removed with warning
Result:
- Locations persist across tab switches ✓
- Empty locations persist ✓
- Data corruption auto-repaired on load ✓
- Orphaned form states cleaned up ✓
- No crashes from invalid data ✓
Fixes: Location disappears when switching tabs or reloading
Created comprehensive security layer to protect against malicious input
and resource exhaustion attacks.
New security.js module:
- sanitizeLocationName(): Blocks __proto__, constructor, toString, etc.
- sanitizeItemName(): Enforces max length (500 chars)
- validateStoredInventory(): Validates entire stored object structure
- MAX_ITEMS_PER_SECTION: Limit of 500 items per section
Protected attack vectors:
1. Prototype pollution via location names
- Blocked: "__proto__", "constructor", "prototype", etc.
- Alert shown to user if attempted
2. DoS via extremely long names
- Location names: max 200 chars (truncated with warning)
- Item names: max 500 chars (truncated with warning)
3. DoS via massive item lists
- Max 500 items per section (truncated with warning)
Integration:
- itemParser.js: Uses sanitizeItemName() and enforces max items
- inventoryActions.js: Validates all user input before saving
- Manual location creation: blocked dangerous names
- Manual item addition: length limits enforced
Security best practices (2025):
- No regex DoS vulnerabilities (character-by-character parsing)
- Explicit hasOwnProperty checks to avoid inherited properties
- Console warnings for all security events (auditing)
- Graceful degradation (truncate, don't crash)
- Defense in depth (validation at multiple layers)
This protects against both malicious actors and accidental abuse.
Fixes two parsing issues with inventory items:
1. Items with commas in parenthetical descriptions were incorrectly
split into multiple items. For example:
"Potato (Cursed, Sexy, Your Mum & Dick, Etc)" would become 3-4
separate items instead of one.
2. AI sometimes wraps item lists in square brackets, which should be
stripped. For example:
"[Sword, Shield]" should parse as ["Sword", "Shield"]
Solution:
- Enhanced parseItems() to track parenthesis depth during parsing
- Only split on commas that are OUTSIDE parentheses
- Strip wrapping square brackets before parsing
- Commas inside parentheses are now preserved as part of the item name
- Maintains backward compatibility with existing items
Implementation:
- Pre-processing: strip wrapping brackets if present
- Two-pass parsing: first collapses newlines in parentheses (existing),
then smart comma splitting (new)
- Similar approach to existing newline handling logic
Examples:
- "Sword, Shield" → ["Sword", "Shield"] (unchanged)
- "Item (tag1, tag2), Sword" → ["Item (tag1, tag2)", "Sword"] (fixed)
- "[Sword, Shield]" → ["Sword", "Shield"] (fixed)
Fixes: Items with commas split into multiple items
Fixes bug where expanding an existing storage location would close
the "Add Location" form that was currently open. This happened
because renderInventory() recreated all HTML from scratch, resetting
all inline forms to hidden state.
Solution:
- Track open form states in inventoryActions module
- Restore form visibility after each re-render
- Applies to all inline forms: add location, add items (on person,
stored, assets)
This also fixes the related issue where switching tabs would close
open forms.
Fixes: Location disappears when expanding while adding new location
- Created getLocationId() helper function to normalize location names to IDs
- Function removes special characters (apostrophes, etc.) before converting to ID
- Both rendering and action handlers now use same ID generation logic
- Fixes issue where locations with apostrophes couldn't be deleted
- Example: "Dottore's Study" now properly generates ID "Dottores-Study"
- Commented out debug logging
- Fixed extension settings not appearing in Extensions tab by appending to correct container (#extensions_settings2)
- Added Discord and Support Creator buttons directly in JavaScript
- Added persistence for committedTrackerData to maintain state across refreshes and restarts
- Updated saveChatData() to include committedTrackerData in chat metadata
- Updated loadChatData() to restore committedTrackerData from saved chat data
Added comprehensive error handling to prevent extension initialization failures:
- Added settings validation in loadSettings() to detect corrupt data
- Improved error recovery in main initialization with granular try-catch blocks
- Enhanced HTML regex import with structure validation and detailed error logging
- Added detection for conflicting old manual formatting regex scripts
- Added user-friendly toastr notifications for initialization errors and conflicts
- Each init step now has independent error handling to prevent cascade failures
This fixes issues where invalid extension_settings could prevent the extension
from loading entirely. The extension will now gracefully handle corrupt data,
warn about conflicts, and fall back to defaults when necessary.
Related to user report where extension wouldn't load with certain settings.json
configurations containing old manual formatting regexes or malformed data.
Implemented comprehensive individual item management system with toggleable view modes:
- Added item parsing utilities (parseItems/serializeItems) for comma-separated strings
- Implemented list view (full-width rows) and grid view (responsive cards)
- Added view mode toggle buttons per inventory section (onPerson, stored, assets)
- View preferences persist per-section in settings
- Replaced text-based editing with add/remove item controls
- Added inline forms for adding new items (matching existing UX patterns)
- Applied theme accent color (--rpg-highlight) to all outlines and active states
- Updated all tabs (desktop/mobile/inventory subtabs) with theme-consistent styling
Technical improvements:
- Created itemParser.js utility module for item string manipulation
- Enhanced inventory rendering with conditional list/grid HTML generation
- Added switchViewMode handler with settings persistence
- Fixed [object Object] display bug with comprehensive type checking
- All buttons and items now use transparent backgrounds with theme accent borders