Commit Graph

341 Commits

Author SHA1 Message Date
Paperboy 7b723663f0 Merge pull request #1 from paperboygold/fix/preset-import-error
fix: remove unavailable preset API import
2025-10-20 12:13:31 +11:00
Lucas 'Paperboy' Rose-Winters d46b36ac0b fix: remove unavailable preset API import
- 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
2025-10-20 12:11:38 +11:00
Spicy Marinara 599356fafb Merge pull request #13 from paperboygold/feature/inventory-bugfixes
feat: inventory bugfixes, status polish, editable inventory fields
2025-10-20 02:03:06 +02:00
Lucas 'Paperboy' Rose-Winters 428d6fb40e feat(inventory): add inline editing for inventory items
- 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
2025-10-20 09:25:55 +11:00
Lucas 'Paperboy' Rose-Winters 9b6d0d41cd fix(avatars): add fuzzy name matching for character portraits
- 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')
2025-10-20 09:06:31 +11:00
Lucas 'Paperboy' Rose-Winters 5ec12cbf10 feat(ui): add centered 'Add Item' button to all storage locations
- 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
2025-10-20 08:43:49 +11:00
Lucas 'Paperboy' Rose-Winters 7da5413fdd fix(inventory): preserve empty storage locations
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
2025-10-20 08:10:41 +11:00
Lucas 'Paperboy' Rose-Winters f09c42ec6e fix(ai-context): sync manual edits to committed tracker data
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
2025-10-20 08:05:08 +11:00
Lucas 'Paperboy' Rose-Winters 7b320b8d0b style(ui): center user avatar in Status tab
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
2025-10-20 07:52:21 +11:00
Lucas 'Paperboy' Rose-Winters dc603b8b49 feat(inventory): granular item-level validation and auto-capitalization
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"
2025-10-20 07:50:43 +11:00
Lucas 'Paperboy' Rose-Winters e21e71b03a fix(inventory): ensure stored locations always initialized properly
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
2025-10-20 07:19:46 +11:00
Lucas 'Paperboy' Rose-Winters 681c2f0e47 feat(inventory): add security hardening for prototype pollution and DoS
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.
2025-10-20 07:16:54 +11:00
Lucas 'Paperboy' Rose-Winters 3a84e24c0a feat(inventory): enhance parser for AI formatting edge cases
Completely rewrote parseItems() to robustly handle diverse AI output
formats without requiring JSON mode (not all local models support it).

New capabilities:
1. Multiple bracket types: [], {}, [[]]
2. Wrapping quotes: "...", '...'
3. Newline-based lists: "Sword\nShield" → ["Sword", "Shield"]
4. Markdown stripping: **bold**, *italic*, `code`, ~~strike~~
5. List markers: "- Sword", "1. Item", "• Item"
6. Graceful unmatched parentheses (warns but doesn't crash)
7. Per-item quote stripping: ["Sword", "Shield"]

Implementation:
- 6-step processing pipeline with clear documentation
- Helper function cleanSingleItem() for per-item cleanup
- Preserves commas inside parentheses (existing feature)
- Console warnings for malformed input (unmatched parens)

Examples now supported:
- Standard: "Sword, Shield" ✓
- Newlines: "Sword\nShield\nPotion" ✓
- Bulleted: "- Sword\n- Shield" ✓
- Numbered: "1. Sword\n2. Shield" ✓
- Markdown: "**Sword** (equipped)" → "Sword (equipped)" ✓
- Complex: "Potato (Cursed, Sexy, Etc), **Shield**" ✓

This future-proofs the parser against varied AI model behaviors.
2025-10-20 07:14:18 +11:00
Lucas 'Paperboy' Rose-Winters 6ba513c530 fix(inventory): handle commas inside parentheses and strip brackets
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
2025-10-20 07:08:46 +11:00
Lucas 'Paperboy' Rose-Winters 0991c30fc9 fix(inventory): preserve form state across re-renders
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
2025-10-20 07:06:04 +11:00
Spicy_Marinara 6d105482c3 Fix storage location deletion bug with special characters
- 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
2025-10-19 20:47:12 +02:00
Spicy_Marinara dcbc788aa2 Fix preset existence check to use array includes
- openai_setting_names from /api/settings/get is an array, not an object
- Use .includes() instead of 'in' operator to check for preset
- Prevents duplicate preset imports on every page refresh
- Preset is now only imported once on first extension load
2025-10-19 20:33:22 +02:00
Spicy_Marinara 4f6d2deeb0 Fix preset duplicate import on every refresh
- Check if preset exists using /api/settings/get instead of HEAD request
- Look for preset in openai_setting_names to determine if it's already imported
- Comment out 'already exists' log to reduce console noise
- Only import preset if it's truly missing from the settings
2025-10-19 20:11:55 +02:00
Spicy_Marinara 029860359f Fix preset import API endpoint
- Use correct /api/presets/save endpoint instead of /api/presets/save-openai
- Add getRequestHeaders() import and use it in the fetch call
- Include apiId: 'openai' in the request body
- Fixes 'Forbidden' error when importing preset
2025-10-19 20:07:14 +02:00
Spicy_Marinara f5418841cb Add preset switching feature and clean up console logs
- Add 'Use separate preset for tracker generation' setting
- Implement automatic preset switching using /preset slash command
- Import getCurrentPresetName() from SillyTavern's regex engine
- Automatically import 'RPG Companion Trackers' preset on first load
- Comment out non-essential console.log statements
- Keep initialization, error, and migration logs for debugging
2025-10-19 20:05:17 +02:00
Spicy_Marinara 4a3170c661 Fix extension settings menu and persist committedTrackerData
- 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
2025-10-18 13:21:41 +02:00
Spicy Marinara 66085d494e Merge pull request #12 from paperboygold/feature/defensive-initialization
fix(init): add defensive error handling for edge cases
2025-10-18 12:47:00 +02:00
Lucas 'Paperboy' Rose-Winters d9b784d745 fix(init): add defensive error handling for edge cases
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.
2025-10-18 16:01:10 +11:00
Spicy Marinara d7c1db4fb1 Revise acknowledgments and improve tips section
Updated acknowledgments and tips for clarity.
2025-10-17 12:29:32 +02:00
Spicy Marinara 5380ae1f21 Merge pull request #11 from paperboygold/feature/inventory-system
feat: inventory system
2025-10-17 12:28:31 +02:00
Paperboy 1e84c363da Merge branch 'main' into feature/inventory-system 2025-10-17 18:12:26 +11:00
Lucas 'Paperboy' Rose-Winters 0f2e19fc91 fix(ui): adjust thought bubble sizing for desktop and mobile
- Desktop: reduce max-width by 30% (350px → 245px) to fit within circles
- Mobile: increase content font size (clamp(9px,1.2vw,11px) → clamp(12px,3.5vw,16px))
- Mobile: increase thought icon size by 20% (2.25rem → 2.7rem)
- Mobile: adjust icon font size proportionally (1.85vw → 2.2vw)
2025-10-17 17:52:58 +11:00
Lucas 'Paperboy' Rose-Winters 5342ea01ee fix(inventory): handle parenthetical descriptions with newlines in item parser
Updated parseItems() to intelligently collapse newlines within parentheses:
- Tracks parentheses depth to handle nested parens
- Replaces newlines with spaces only when inside parentheses
- Preserves newlines outside parentheses
- Prevents double spaces after newline replacement

Example fix:
- Input: 'Books (various magical tomes\n\nhistorical texts)\n\nAlchemy Ingredients'
- Before: ['Books (various magical tomes', 'historical texts)', 'Alchemy Ingredients']
- After: ['Books (various magical tomes historical texts)', 'Alchemy Ingredients']
2025-10-17 17:42:27 +11:00
Lucas 'Paperboy' Rose-Winters de43e84cd0 style(inventory): improve view toggle and storage name contrast
- Changed storage location names to black for better visibility on colored background
- Updated view toggle buttons to have dark background with proper border outline
- Removed wrapper background from view toggle, individual buttons now standalone
2025-10-17 17:33:58 +11:00
Lucas 'Paperboy' Rose-Winters 73050a085b feat(inventory): add list/grid view modes with individual item management
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
2025-10-17 17:30:57 +11:00
Lucas 'Paperboy' Rose-Winters 26acee3a70 fix(mobile): prevent Info tab from rendering over other tabs
Changed selector from .rpg-mobile-tab-content[data-tab-content="info"]
to include .active class, preventing the info tab from always being
visible regardless of which tab is selected.

The display: flex !important was overriding the base tab switching
logic (.rpg-mobile-tab-content { display: none; }), causing the info
tab to render on top of all other tabs.
2025-10-17 16:59:45 +11:00
Lucas 'Paperboy' Rose-Winters b3ca2960d8 fix(mobile): center avatar without breaking mood layout
Changed selector from .rpg-stats-left > div to :first-child to only
target the avatar wrapper, preventing the mood emoji from stretching
into the attributes column.

The previous fix made all direct children of .rpg-stats-left span the
full width, which incorrectly affected the mood div. Now only the
first child (avatar wrapper) spans both columns and is centered.
2025-10-17 16:47:26 +11:00
Lucas 'Paperboy' Rose-Winters 0608bc6280 fix(ui): make info box compact to give stats section more space
Changed .rpg-info-section from flex: 1 to flex: 0 0 auto so it only
takes the vertical space needed by its content (calendar/weather/
location widgets) rather than expanding equally with other sections.

Previously, all .rpg-section elements had flex: 1, causing them to
equally share vertical space. This forced the info box to expand
beyond its content needs, creating excessive bottom padding.

With this change:
- Info box is now compact (no wasted space)
- Stats section expands to fill more vertical space
- Overall layout is more balanced with stats getting priority
2025-10-17 16:38:42 +11:00
Lucas 'Paperboy' Rose-Winters 97dc87062f feat(inventory): replace prompt dialogs with inline editing
Replaced all prompt() and confirm() dialogs with contenteditable fields
and inline UI components for a better user experience.

Changes:
- Made inventory fields (On Person, Stored items, Assets) contenteditable
  with blur-to-save functionality
- Replaced "Add Location" prompt with inline form (hidden by default)
- Replaced "Remove Location" confirm with inline confirmation UI
- Added CSS styling for inline editing states (hover, focus, empty)
- Added CSS for inline forms, buttons, and confirmation UI
- Fixed bug where inventory sub-tabs were unclickable due to
  incorrect container ID in toggleLocationCollapse() and
  switchInventoryTab() functions

All inline edits now save automatically on blur, matching the UX
pattern used elsewhere in the extension (mood/conditions fields).
2025-10-17 16:27:59 +11:00
Lucas 'Paperboy' Rose-Winters f560bb543b feat(ui): add tab navigation system for desktop and mobile
Desktop (2 tabs):
- Status tab: User Stats + Info Box + Character Thoughts
- Inventory tab: Inventory system (dedicated space)

Mobile (3 tabs):
- Stats tab: User Stats only
- Info tab: Info Box + Character Thoughts
- Inventory tab: Inventory only

Features:
- Created desktop.js module for desktop tab management
- Updated mobile.js to use 3-tab structure (more breathing room on small screens)
- Added CSS styling for desktop tabs (hover states, active indicators)
- Implemented viewport transition handlers (desktop ↔ mobile)
- Tabs replace dividers (cleaner visual separation)
- Character thoughts can now expand to fill vertical space

This resolves the cramped 4-section panel issue by organizing content into logical tabs on both desktop and mobile.
2025-10-17 16:18:47 +11:00
Lucas 'Paperboy' Rose-Winters abd3ade30e fix(inventory): refactor renderInventory() to match other render functions
Root cause: renderInventory() signature mismatch - was expecting parameters
but called without any, resulting in empty/broken rendering.

Changes:
- Rename renderInventory(inventory, options) → generateInventoryHTML() (internal)
- Create new renderInventory() with no parameters (like renderUserStats, etc.)
- New function gets container from state, data from settings, updates DOM directly
- Import getInventoryRenderOptions() to get current tab/collapse state
- Keep updateInventoryDisplay() for use by inventoryActions module

Now matches the pattern used by all other render modules, fixing the bug where
inventory section appeared empty in the panel.
2025-10-17 15:46:13 +11:00
Lucas 'Paperboy' Rose-Winters 2566d97dfb feat(inventory): add mobile tab support for inventory section
- Include inventory in mobile tab system alongside user stats
- Add inventory to Stats tab (grouped with user stats)
- Update setupMobileTabs() to detect and organize inventory section
- Update removeMobileTabs() to restore inventory in correct order
- Handle inventory show/hide in mobile tab transitions

Inventory now works seamlessly on mobile viewports with proper tab organization.
2025-10-17 15:38:11 +11:00
Lucas 'Paperboy' Rose-Winters 1f948cd5d8 feat(inventory): wire up v2 system to main panel with full interactivity
- Add inventory section to template.html between Thoughts and bottom controls
- Wire up renderInventory() to all event handlers (message received, character changed, swipes)
- Initialize inventory container reference and event listeners in index.js
- Add showInventory toggle checkbox to settings with visibility control
- Update layout.js to handle inventory section and divider visibility
- Add renderInventory parameter to updateRPGData for separate mode support
- Update state.js and config.js with inventory container and showInventory setting

Inventory is now fully integrated as a visible, interactive panel section that persists across all user interactions.
2025-10-17 15:36:15 +11:00
Lucas 'Paperboy' Rose-Winters 60e4a6c2cc feat(inventory): add interaction handlers for v2 system
Create comprehensive inventory interaction module:

**NEW: inventoryActions.js (286 lines)**
- editOnPerson() - Edit items currently carried/worn
- editStoredLocation() - Edit items at specific storage location
- editAssets() - Edit vehicles, property, major possessions
- addStorageLocation() - Create new storage location with validation
- removeStorageLocation() - Delete location with confirmation
- toggleLocationCollapse() - Collapsible sections with persistent state
- switchInventoryTab() - Handle sub-tab switching
- initInventoryEventListeners() - Event delegation for dynamic content
- updateLastGeneratedDataInventory() - Keep AI context synced

**Interaction Patterns:**
- Uses native prompt() and confirm() for user input
- Event delegation: .on() for dynamic elements
- Full persistence: saveSettings() + saveChatData() + updateMessageSwipeData()
- Auto re-render after every mutation
- Maintains UI state (activeSubTab, collapsedLocations)

**State Management:**
- Tracks collapsed locations across sessions
- Persists in extensionSettings.collapsedInventoryLocations
- Current tab state maintained in module scope

**Data Flow:**
User Action → Handler → Update inventory → Update lastGeneratedData
→ Persist (settings + chat + swipe) → Re-render UI

Part of Epic 7.6: Inventory interactions
Next: Wire into main panel and initialize event listeners
2025-10-17 15:28:59 +11:00
Lucas 'Paperboy' Rose-Winters 790cf995b4 feat(inventory): add v2 UI rendering with tabbed interface
Create complete inventory UI rendering system:

**NEW: inventory.js rendering module (252 lines):**
- renderInventorySubTabs() - Navigation tabs (On Person, Stored, Assets)
- renderOnPersonView() - Display items currently carried/worn
- renderStoredView() - Collapsible storage locations with edit/remove actions
- renderAssetsView() - Vehicles, property, and major possessions
- renderInventory() - Main rendering function with v1/v2 compatibility
- updateInventoryDisplay() - DOM update helper
- Includes HTML escaping for XSS prevention

**Tab System:**
- Three sub-tabs: On Person, Stored, Assets
- Smooth tab switching with active state highlighting
- Each view shows relevant inventory section

**On Person View:**
- Shows items currently carried/worn
- Edit button to modify items
- Clean text display

**Stored View:**
- Collapsible location sections
- Each location shows: name, items, edit/remove buttons
- "Add Location" button for new storage spots
- Empty state message when no locations exist
- Toggle icons (chevron-down/chevron-right)

**Assets View:**
- Display of vehicles, property, equipment
- Edit button to modify assets
- Helpful hint text explaining asset categories

**Removed old inventory UI from userStats.js:**
- Deleted rpg-inventory-box HTML (5 lines)
- Deleted inventory editing event listener (13 lines)
- Inventory now has dedicated tab instead of inline display
- Maintains backward compatibility for data tracking

**NEW: Inventory CSS styles (242 lines):**
- .rpg-inventory-container - Main layout
- .rpg-inventory-subtabs - Tab navigation styling
- .rpg-inventory-section - Content area styling
- .rpg-storage-location - Collapsible location cards
- Button styles (edit, add, remove) with hover states
- Mobile responsive breakpoints for touch-friendly UI
- Theme-aware using SillyTavern CSS variables
- Font size: 0.9rem for readability

**Design Features:**
- Clean, modern card-based layout
- Smooth transitions and hover effects
- Collapsible sections to reduce clutter
- Consistent with existing RPG Companion theme system
- Mobile-first responsive design
- Touch-friendly button sizes on mobile

Changes:
- NEW: src/systems/rendering/inventory.js (252 lines)
- MODIFIED: src/systems/rendering/userStats.js (-18 lines, removed old UI)
- MODIFIED: style.css (+242 lines, inventory styles)

Part of inventory system v2 implementation
Dependencies: v2 types, migration, parsing, generation
2025-10-17 15:14:02 +11:00
Lucas 'Paperboy' Rose-Winters b00bae905f feat(inventory): add v2 parsing and generation support
Add full AI integration for inventory v2 format:

**Parsing (NEW: inventoryParser.js, 125 lines):**
- extractInventoryData() - Parse multi-line v2 format from AI responses
  - Extracts "On Person: ..." section
  - Extracts multiple "Stored - [Location]: ..." sections
  - Extracts "Assets: ..." section
  - Returns InventoryV2 object
- extractLegacyInventory() - Fallback parser for old v1 format
- extractInventory() - Main function that tries v2 first, falls back to v1

**Parsing Integration (parser.js):**
- Import extractInventory() from inventoryParser
- Replace old single-line regex with v2-aware extraction
- Use feature flag to switch between v1/v2 parsing modes
- Maintains backward compatibility with FEATURE_FLAGS.useNewInventory

**Generation (promptBuilder.js, 60 lines changed):**
- NEW: buildInventorySummary() - Converts v2 object to multi-line text
  - Formats "On Person: ..." line
  - Formats multiple "Stored - [Location]: ..." lines
  - Formats "Assets: ..." line
  - Handles legacy v1 string format for backward compat
- Update generateTrackerInstructions() with v2 format spec:
  - Shows AI how to format inventory in multi-line v2 structure
  - Includes note about multiple storage locations
  - Falls back to v1 format when feature flag disabled
- Update generateContextualSummary() to use buildInventorySummary()
  - Converts v2 inventory to readable context for separate mode

**Format Examples:**

AI Output (v2 format):
```
On Person: Sword (equipped), 3x Health Potions, Leather Armor
Stored - Home: Spare clothes, Tools, 50 gold coins
Stored - Bank: Family heirloom, Important documents
Assets: Motorcycle (garage), Downtown apartment (owned)
```

Parsed Result:
```js
{
  version: 2,
  onPerson: "Sword (equipped), 3x Health Potions, Leather Armor",
  stored: {
    "Home": "Spare clothes, Tools, 50 gold coins",
    "Bank": "Family heirloom, Important documents"
  },
  assets: "Motorcycle (garage), Downtown apartment (owned)"
}
```

Changes:
- NEW: src/systems/generation/inventoryParser.js (125 lines)
- MODIFIED: src/systems/generation/parser.js (+14 lines)
- MODIFIED: src/systems/generation/promptBuilder.js (+60 lines)

Part of inventory system v2 implementation
Dependencies: v2 types, migration utility, persistence integration
2025-10-17 15:10:31 +11:00
Lucas 'Paperboy' Rose-Winters 39ec36f105 feat(inventory): add automatic v1→v2 migration on settings/chat load
Integrate inventory migration into persistence layer:
- Call migrateInventory() automatically when loading settings
- Call migrateInventory() automatically when loading chat data
- Save migrated data back to persistence immediately
- Update default reset inventory to v2 format
- Add console logging for migration status

Migration behavior:
- Only runs when FEATURE_FLAGS.useNewInventory is true
- Automatically detects v1 string format and converts to v2
- Handles null/undefined/empty/malformed data gracefully
- Logs migration source (v1, null, default) to console
- Persists migrated inventory immediately after conversion
- Migration runs once per load - subsequent loads see v2

Migration scenarios:
1. Old save with v1 string → migrates to v2.onPerson, saves
2. No save data → uses v2 defaults from state.js
3. Already v2 → no migration, no save
4. Chat data with v1 → migrates to v2, updates chat metadata

Changes:
- MODIFIED: src/core/persistence.js (+29 lines)
  - Updated loadSettings() with migration hook
  - Updated loadChatData() with migration hook
  - Changed default reset inventory from 'None' to v2 object

Part of inventory system v2 implementation
Dependencies: inventory types and migration utility
2025-10-17 15:06:53 +11:00
Lucas 'Paperboy' Rose-Winters 110bdb3b64 feat(inventory): implement v2 data structure and JSDoc types (Epic 7.1)
Create structured inventory system with categorized storage:
- Add JSDoc type definitions (InventoryV2, InventoryV1, MigrationResult)
- Implement migration utility for v1 → v2 conversion
- Update state.js with v2 inventory structure and feature flag
- Update config.js to match new inventory defaults

Changes:
- NEW: src/types/inventory.js (30 lines) - Type definitions
- NEW: src/utils/migration.js (84 lines) - Migration logic
- MODIFIED: src/core/state.js - v2 inventory + FEATURE_FLAGS
- MODIFIED: src/core/config.js - v2 inventory defaults

Inventory v2 structure:
{
  version: 2,
  onPerson: "Sword, 3x Potions",     // Plaintext string
  stored: {
    "Home": "Clothes, Tools",         // Location → items
    "Bank": "Gold, Documents"
  },
  assets: "Motorcycle, House"         // Plaintext string
}

Migration handles all edge cases:
- v1 string → v2.onPerson
- null/undefined → v2 defaults
- "None" → v2 defaults
- Already v2 → no changes

Context overhead: +40 chars (~5% increase) for organized multi-location storage

Part of Epic 7: New Inventory System
Implements: docs/IMPLEMENTATION_PLAN.md Epic 7.1
2025-10-17 14:58:07 +11:00
Lucas 'Paperboy' Rose-Winters 658b911d12 style: integrate upstream font-size and layout improvements
Folds in changes from commits 6d97992, cc53c69, 6987c0c:
- Update stat label/value font sizes (clamp 0.9-1vw → 0.5-0.7vw)
- Update inventory items font size (clamp 0.4-0.6vw → 0.7-0.6vw)
- Center dice save button with auto margins
- Add debug console.log for committedTrackerData
- Comment out verbose console logs for plot progression and persona avatar
- Clarify prompt instruction to remove brackets
2025-10-17 14:35:55 +11:00
Lucas 'Paperboy' Rose-Winters 0764bc63a1 feat(integration): extract SillyTavern event handlers to dedicated module
- Create src/systems/integration/sillytavern.js with all event handlers
- Move commitTrackerData() (deferred from Epic 1)
- Move sendPlotProgression() to plotProgression.js
- Move updateGenerationModeUI() to layout.js
- Add registerAllEvents() and unregisterAllEvents() to events.js
- Centralize event registration in index.js initialization

This completes Epic 6: Integration Layer Extraction
~340 lines extracted from index.js
index.js reduced from ~783 lines to 423 lines
2025-10-17 14:20:58 +11:00
Lucas 'Paperboy' Rose-Winters 175ff9560c refactor(features): extract HTML cleaning regex setup to standalone module
Extract ensureHtmlCleaningRegex into src/systems/features/htmlCleaning.js.
This module automatically imports the HTML cleaning regex script that
strips HTML tags from outgoing prompts to prevent formatting issues.

Passes SillyTavern imports (st_extension_settings, saveSettingsDebounced)
as parameters to avoid deep module import path issues.

- Create htmlCleaning.js with regex import logic
- Update index.js to import and use the new module with parameters
- Maintain backward compatibility with existing functionality
2025-10-17 13:48:49 +11:00
Lucas 'Paperboy' Rose-Winters 3473f2ac44 refactor(features): extract classic stats controls to standalone module
Extract setupClassicStatsButtons into src/systems/features/classicStats.js.
This module handles the delegated event listeners for classic RPG stat
+/- buttons (STR, DEX, CON, INT, WIS, CHA).

- Create classicStats.js with event delegation for stat buttons
- Update index.js to import and use the new module
- Maintain backward compatibility with existing functionality
2025-10-17 13:45:36 +11:00
Lucas 'Paperboy' Rose-Winters ba50fc5bdc refactor(features): extract plot progression UI to standalone module
Extract plot progression button setup from index.js into dedicated
feature module at src/systems/features/plotProgression.js.

Due to ES6 module import path limitations in deeply nested modules,
the generation logic (sendPlotProgression) remains in index.js where
it can properly import from SillyTavern's script.js. The UI setup
function accepts the generation function as a callback parameter.

Creates plotProgression.js with 62 lines of UI setup code.
Index.js increases from 800 to 869 lines (+69 for generation logic).
2025-10-17 13:39:24 +11:00
Lucas 'Paperboy' Rose-Winters f4dfd368e1 refactor(features): extract dice system to standalone module
Extract dice rolling functionality from modals.js into dedicated
feature module at src/systems/features/dice.js. This includes:
- rollDice() - core rolling logic with animation
- executeRollCommand() - dice notation parser
- updateDiceDisplay() - sidebar display updates
- clearDiceRoll() - clear last roll
- addDiceQuickReply() - quick reply integration

Also fixes ES6 module binding issue with pendingDiceRoll by adding
getPendingDiceRoll() getter function in state.js to ensure correct
value retrieval across module boundaries.

Reduces modals.js from 568 to 499 lines (-69 lines).
Creates dice.js with 113 lines of focused dice functionality.
2025-10-17 13:25:38 +11:00
Lucas 'Paperboy' Rose-Winters bb952aecec fix(ui): correct dice roller 'Save Roll' button positioning
The Save Roll button was appearing at the bottom left of the container
instead of being centered below the result.

Added flexbox layout to .rpg-dice-result container:
- display: flex
- flex-direction: column
- align-items: center

This ensures the button is properly centered and positioned below
the dice result value and details.
2025-10-17 13:05:41 +11:00