Files
rpg-companion-sillytavern/AGENTS.md
T
ARIA c938916fd8 Fixes #18: Update README & AGENTS.md documentation
- README: Add Equipment System feature, Code Quality, Performance,
  Memory Management, Testing, Development Commands, and Tech Stack sections
- AGENTS.md: Update source tree with CSS modules, equipment constants,
  renderUtils, settings consolidation; add development commands,
  performance optimizations, memory management notes, validator factory;
  update file reference table

Based on PRs: #1 #3 #6 #8 #10 #12 #14 #17
2026-07-12 17:03:37 +02:00

22 KiB

RPG Companion for SillyTavern — Agent Instructions

What this is

A browser extension for SillyTavern (AI roleplay frontend). It is NOT a standalone Node.js application. All JavaScript runs in the browser context inside SillyTavern. There is no bundler — JS files are loaded directly by SillyTavern's extension loader. CSS modules are concatenated via a build script.


Quick start

  • Entry point: index.js (loaded by SillyTavern via manifest.json)
  • CSS: Modular — 28 files in src/styles/, concatenated via npm run build:css (or npm run build:css:min for production)
  • HTML templates: template.html (main panel), settings.html (Extensions tab settings drawer)
  • Runtime: Browser only. All code executes inside SillyTavern's page context.
  • jQuery: Available globally as $ / jQuery. Used extensively for DOM manipulation.
  • ES modules: All src/ files use import/export (browser ES module syntax).
  • Testing: Jest + Babel ESM support — 78 tests (npm test)

Manifest and loading

manifest.json tells SillyTavern how to load the extension:

{
  "display_name": "RPG Companion",
  "loading_order": 100,
  "js": "index.js",
  "css": "style.css"
}

SillyTavern loads index.js and style.css relative to the extension directory. The extension name used internally is third-party/rpg-companion-sillytavern.

Extension path detection

src/core/config.js auto-detects whether the extension is installed globally (public/extensions/) or per-user (data/default-user/extensions/) by inspecting import.meta.url. This determines template loading paths.


SillyTavern integration

The extension imports from SillyTavern's global scripts using relative paths that assume the extension lives at:

  • scripts/extensions/third-party/rpg-companion-sillytavern/ (global install), or
  • data/default-user/extensions/third-party/rpg-companion-sillytavern/ (user install)

Key imports in index.js:

import { getContext, renderExtensionTemplateAsync, extension_settings as st_extension_settings }
  from '../../../extensions.js';
import { eventSource, event_types, substituteParams, chat, saveSettingsDebounced, ... }
  from '../../../../script.js';
import { selected_group, getGroupMembers } from '../../../group-chats.js';
import { power_user } from '../../../power-user.js';

The deeper relative imports in src/ files (e.g., ../../../../../../script.js) follow the same pattern adjusted for directory depth.

SillyTavern globals available at runtime

  • self.SillyTavern — global namespace object
  • $ / jQuery — jQuery
  • toastr — toast notification library
  • eventSource — SillyTavern's event emitter
  • chat — current chat message array
  • chat_metadata — per-chat metadata store
  • characters — loaded character array
  • getContext() — returns current chat context

Architecture

Source tree (src/)

src/
├── core/                    # Core infrastructure
│   ├── config.js            # Extension name, folder path, default settings (single source of truth)
│   ├── state.js             # Centralized state variables + getters/setters (imports defaults from config.js)
│   ├── persistence.js       # Save/load settings, chat data, migrations
│   ├── events.js            # SillyTavern event system wrapper
│   ├── i18n.js              # Translation system (fetches JSON, applies data-i18n-key)
│   └── settingsPanel.js     # Extracted settings panel UI (from index.js refactor)
├── i18n/                    # Translation JSON files
│   ├── en.json              # English (reference, 513 lines)
│   ├── fr.json              # French
│   ├── ru.json              # Russian
│   ├── zh-cn.json           # Simplified Chinese
│   ├── zh-tw.json           # Traditional Chinese
│   └── validator.js         # Node.js script to validate translation consistency
├── styles/                  # CSS modules (28 files, concatenated via build script)
│   ├── _01_variables.css    # CSS custom properties
│   ├── _02_panel.css        # Main panel layout
│   ├── _03_components.css   # Buttons, inputs, common components
│   ├── _04_user-stats.css   # User stats panel
│   ├── _05_info-box.css     # Info box dashboard
│   ├── _06_thoughts.css     # Character thoughts
│   ├── _07_settings.css     # Settings modal
│   ├── _08_themes.css       # Theme definitions (extended custom properties)
│   ├── _09_dice-modal.css   # Dice roller modal
│   ├── _10_tracker-editor.css # Tracker configuration editor
│   ├── _11_settings-modal.css # Settings modal styles
│   ├── _12_thought-bubbles.css # Floating thought bubbles
│   ├── _13_mobile.css       # Mobile responsive styles
│   ├── _14_inventory.css    # Inventory views
│   ├── _15_inventory-views.css # Inventory detail views
│   ├── _16_desktop-tabs.css # Desktop tab navigation
│   ├── _17_quests.css       # Quest tracking
│   ├── _18_equipment.css    # Equipment grid
│   ├── _19_encounters.css   # Combat encounters
│   ├── _20_plot-buttons.css # Plot progression buttons
│   ├── _21_music-player.css # Spotify music player
│   ├── _22_weather.css      # Weather effects
│   ├── _23_holiday-promo.css # Holiday promotions
│   ├── _24_feature-toggles.css # Feature toggle switches
│   ├── _25_import-dialog.css # Import/export dialog
│   ├── _26_fab-widgets.css  # Mobile FAB widgets
│   ├── _27_strip-widgets.css # Collapsed panel strip widgets
│   └── _28_equipment-modal.css # Equipment edit modal
├── systems/                 # Feature systems (organized by concern)
│   ├── generation/          # AI prompt generation, parsing, API calls
│   │   ├── promptBuilder.js # Builds tracker prompts for AI (refactored with focused helpers)
│   │   ├── parser.js        # Parses AI responses (~50 pre-compiled regex patterns)
│   │   ├── apiClient.js     # Makes API calls for separate/external generation
│   │   ├── injector.js      # Injects tracker prompts into generation context
│   │   ├── lockManager.js   # Manages locked tracker fields
│   │   ├── suppression.js   # Controls when to skip tracker injection
│   │   ├── encounterPrompts.js  # Combat encounter prompt building
│   │   ├── inventoryParser.js   # Parses inventory data from AI responses
│   │   └── jsonPromptHelpers.js # JSON formatting helpers for prompts
│   ├── rendering/           # UI rendering functions
│   │   ├── userStats.js     # Renders user stats panel
│   │   ├── infoBox.js       # Renders info box (date, weather, location, events)
│   │   ├── thoughts.js      # Renders character thoughts + chat bubbles
│   │   ├── inventory.js     # Renders inventory section
│   │   ├── equipment.js     # Renders equipment section
│   │   ├── quests.js        # Renders quests section
│   │   ├── musicPlayer.js   # Renders Spotify music player
│   │   └── renderUtils.js   # Rendering utilities (updateIfChanged cache, rAF batching)
│   ├── ui/                  # UI components and helpers
│   │   ├── theme.js         # Theme system (default, sci-fi, fantasy, cyberpunk, custom)
│   │   ├── modals.js        # Settings modal, dice modal, deprecation modal
│   │   ├── layout.js        # Panel positioning, collapse/expand, section visibility
│   │   ├── mobile.js        # Mobile FAB button, mobile tabs, keyboard handling
│   │   ├── desktop.js       # Desktop strip widgets
│   │   ├── trackerEditor.js # Tracker configuration editor
│   │   ├── promptsEditor.js # Custom prompt editor
│   │   ├── checkpointUI.js  # Chapter checkpoint buttons
│   │   ├── encounterUI.js   # Combat encounter modal
│   │   ├── snowflakes.js    # Snowflake animation effect
│   │   ├── weatherEffects.js # Dynamic weather visual effects
│   │   ├── alternatePresentCharacters.js  # Below-chat character panel
│   │   └── settingsListeners.js  # Settings event listeners (extracted from index.js)
│   ├── equipment/           # Equipment system
│   │   └── constants.js     # Equipment slot definitions, type limits, stat config
│   ├── integration/         # SillyTavern event integration
│   │   ├── sillytavern.js   # Event handlers: message sent/received, swipe, etc.
│   │   └── thoughtBasedExpressions.js  # Integrates with ST Character Expressions
│   ├── interaction/         # User interaction handlers
│   │   ├── inventoryActions.js  # Inventory click/edit handlers
│   │   ├── equipmentActions.js  # Equipment click/edit handlers
│   │   └── inventoryEdit.js     # Inventory inline editing
│   └── features/            # Feature modules
│       ├── plotProgression.js  # Plot progression buttons
│       ├── classicStats.js     # D&D-style attribute buttons
│       ├── dice.js             # Dice roller
│       ├── htmlCleaning.js     # Regex-based HTML tag cleaning
│       ├── jsonCleaning.js     # Regex-based JSON cleanup in messages
│       ├── musicPlayer.js      # Spotify URL parsing and embedding
│       ├── chapterCheckpoint.js # Save/restore tracker state checkpoints
│       ├── avatarGenerator.js  # Auto-generate character avatars
│       └── encounterState.js   # Combat encounter state management
├── types/                   # JSDoc type definitions
│   └── inventory.js         # InventoryV2 type definition
└── utils/                   # Utility functions
    ├── avatars.js           # Avatar URL handling
    ├── imageUrls.js         # Image URL utilities
    ├── itemParser.js        # Item string parsing
    ├── jsonMigration.js     # v2 to v3 JSON format migration
    ├── jsonRepair.js        # JSON repair for malformed AI responses (14 pre-compiled regex)
    ├── migration.js         # Settings migration helpers
    ├── presentCharacters.js # Present character data helpers
    ├── responseExtractor.js # Extract tracker data from AI responses
    ├── security.js          # Input validation and sanitization (validator factory)
    ├── sillyTavernExpressions.js  # ST expression mapping
    ├── thoughtBasedExpressionPortraits.js  # Expression-to-portrait mapping
    └── transformations.js   # Data transformation utilities

Data flow

  1. User sends messageMESSAGE_SENT event fires
  2. Extension hooks onMessageSent in sillytavern.js
  3. Together mode: Tracker prompt is injected into the generation context via injector.js
  4. Separate mode: After main response, apiClient.js makes a separate API call
  5. External API mode: Uses user-configured external API endpoint
  6. AI respondsMESSAGE_RECEIVED event fires
  7. Extension hooks onMessageReceivedparser.js extracts tracker data
  8. Rendering: renderUserStats(), renderInfoBox(), renderThoughts(), etc. update the UI (batched via rAF)
  9. Persistence: Tracker data is saved to chat_metadata per chat, and per-swipe

State management

All extension state lives in src/core/state.js as module-level let variables with getter/setter functions. Key state:

  • extensionSettings — persisted settings object (merged with defaults on load)
  • lastGeneratedData — tracker data from the last AI generation
  • committedTrackerData — tracker data committed after a message
  • lastActionWasSwipe — flag to handle swipe-specific logic
  • isGenerating — flag to prevent re-entrant generation
  • pendingDiceRoll — dice roll result to inject into next generation
  • DOM element caches: $panelContainer, $userStatsContainer, etc.

Settings consolidation: config.js is the single source of truth for default settings. state.js imports defaults from config.js (~300 lines eliminated from duplication).

Persistence

src/core/persistence.js handles:

  • Extension settings: saved via SillyTavern's saveSettingsDebounced()
  • Chat-specific data: saved in chat_metadata.rpg_companion per chat
  • Per-swipe data: stored on each chat message object
  • Settings versioning: automatic migration via CURRENT_SETTINGS_VERSION (currently 7)
  • Deferred saves: batches chat data saves to avoid excessive writes

Memory management

  • clearDomCache(): Invalidates jQuery object cache on panel rebuild
  • Event tracking: All on()/once() handlers tracked for cleanup via unregisterAllEvents()
  • clearDebugLogs(): Called on CHAT_CHANGED to prevent unbounded debug log growth

Performance optimizations

  • Rendering: requestAnimationFrame batching in rerenderRpgState(), updateIfChanged() cache comparison in renderUtils.js, CSS content-visibility: auto on inventory/equipment containers
  • Parser: ~64 regex patterns pre-compiled as module-level constants, format detection caching with lastDetectedFormat (3-hit threshold)
  • CSS: Minification via --minify flag, extended CSS custom properties, theme refactoring (379 → ~220 lines)

Development commands

# Build CSS from modular sources
npm run build:css              # Concatenate CSS modules into style.css
npm run build:css:min          # Minified production build (~50% smaller)

# Testing
npm test                       # Run Jest test suite (78 tests)

# Translation validation
npm run validate_locale        # Watch mode: re-validate on file changes
npm run validate_locale_once   # Single validation run

i18n system

How it works

  1. Translation files are flat JSON in src/i18n/{locale}.json
  2. Keys use dot notation: "template.settingsModal.display.showUserStats"
  3. HTML elements use data-i18n-key, data-i18n-title, or data-i18n-aria-label attributes
  4. src/core/i18n.js fetches translations via fetch() from the extension path
  5. applyTranslations() walks the DOM and updates textContent, title, and aria-label

Adding translations

  1. Add keys to src/i18n/en.json (reference locale)
  2. Add corresponding keys to all other locale files
  3. Add data-i18n-key="your.key.here" to HTML elements in template.html or settings.html
  4. For dynamically generated text, use i18n.getTranslation('your.key.here') in JS

Validator

src/i18n/validator.js is a Node.js script (uses require(), not ES modules). It:

  • Compares all locales against the reference locale for missing/extra keys
  • Checks for type mismatches
  • Checks for empty strings and null values
  • Scans .html/.js/.jsx files for unlocalized text (text in tags without data-i18n-key)

Key conventions

jQuery usage

DOM manipulation uses jQuery throughout. Element caches are stored as jQuery objects with $ prefix:

$panelContainer = $('#rpg-companion-panel')
$userStatsContainer = $('#rpg-user-stats')

Event system

All SillyTavern event registration goes through src/core/events.js:

registerAllEvents({
    [event_types.MESSAGE_SENT]: onMessageSent,
    [event_types.CHAT_CHANGED]: [onCharacterChanged, updatePersonaAvatar],
});

CSS naming

All CSS classes use rpg- prefix to avoid conflicts with SillyTavern and other extensions:

  • .rpg-panel, .rpg-section, .rpg-stat-bar, .rpg-btn-primary, etc.
  • CSS custom properties: --rpg-bg, --rpg-accent, --rpg-text, --rpg-highlight, --rpg-border, --rpg-shadow

CSS build pipeline

CSS modules in src/styles/ are concatenated in numeric order by scripts/build-css.js. Modules follow the naming convention _NN_name.css where NN is the load order. After modifying any CSS module, run npm run build:css to regenerate style.css.

Validator factory

Input sanitization uses createStringValidator() from src/utils/security.js — a factory pattern that unifies sanitizeLocationName/sanitizeItemName with configurable rules (max length, allowed characters, etc.).

Panel structure

The main panel (template.html) has these sections in order:

  1. Collapse toggle button
  2. Strip widget container (shown when panel is collapsed)
  3. Game container with header
  4. Dice roll display
  5. Content box with sections: User Stats → Info Box → Thoughts → Inventory → Equipment → Quests → Music Player
  6. Feature toggles row (HTML, Dialogue Coloring, Deception, Omniscience, CYOA, Spotify, Weather, Narrator, Auto Avatars)
  7. Manual update button
  8. Settings and Edit Trackers buttons

Generation modes

Three modes control how tracker data is generated:

  • together: Tracker prompt injected into main generation. Single API call, faster, but tracker JSON mixed in response.
  • separate: Two API calls — one for roleplay, one for tracker data. Cleaner roleplay, slower.
  • external: Uses user-configured external API (OpenAI-compatible endpoint) for tracker generation.

Guided generation compatibility

The skipInjectionsForGuided setting controls tracker injection behavior during guided generations:

  • 'none' — always inject (default)
  • 'impersonation' — skip only for impersonation-style guided generations
  • 'guided' — skip for any guided/instruct or quiet_prompt generation

Important gotchas

  1. No bundler: All imports use relative paths that depend on the extension's directory structure within SillyTavern. Changing file locations requires updating import paths.

  2. Relative import depth matters: Files in src/ use deeper relative paths (../../../../../../script.js) than index.js (../../../../script.js). When moving files between directories, adjust accordingly.

  3. Settings merge: On load, saved settings are deep-merged with defaults. config.js is the single source of truth for default settings — state.js imports from it. New settings fields should be added to config.js.

  4. Chat metadata: Per-chat tracker data lives in chat_metadata.rpg_companion. Per-swipe data lives on individual chat message objects in the swipe_store.

  5. i18n fetch path: Translations are fetched at /scripts/extensions/third-party/rpg-companion-sillytavern/src/i18n/{lang}.json. The hardcoded path in i18n.js must match the extension's actual URL path.

  6. Extension enable/disable: The extension can be toggled from SillyTavern's Extensions tab. When disabled, all UI elements are removed from the DOM. When re-enabled, initUI() rebuilds everything.

  7. Mobile vs desktop: Viewport breakpoint is 1000px. Below: FAB button + mobile tabs. Above: fixed panel + desktop tabs.

  8. External API key: Stored in localStorage as rpg_companion_external_api_key, NOT in extension settings (for security).

  9. Dev dependencies: package.json has devDependencies (Jest, Babel, chokidar, fs-extra, glob, execa) for testing and i18n validation. No runtime dependencies — the extension runs entirely in the browser.

  10. CSS rebuild required: After modifying any file in src/styles/, run npm run build:css to regenerate style.css. SillyTavern loads style.css, not the individual modules.

  11. Deprecation status: The README states this extension is deprecated in favor of Marinara Engine. The code shows a deprecation modal that displays on first load.

  12. ESM testing: Jest uses Babel for ESM transformation (babel.config.json + jest.config.cjs). All test files use ES module syntax.


File reference

File Purpose
index.js Main entry point. Imports, UI init, event registration, startup logic (~456 lines after refactor).
manifest.json SillyTavern extension manifest (name, loading order, JS/CSS files).
style.css Built CSS output (concatenated from src/styles/ modules). Do not edit directly.
template.html Main panel HTML template. Rendered by SillyTavern's renderExtensionTemplateAsync.
settings.html Extensions tab settings drawer. Minimal — most settings are in the panel modal.
scripts/build-css.js CSS build script. Concatenates modules, supports --minify flag.
src/core/state.js Central state module. Imports defaults from config.js. All extension-wide variables.
src/core/persistence.js Settings/chat data save/load, version migrations.
src/core/config.js Extension name, path detection, default settings (single source of truth).
src/core/events.js SillyTavern event system wrapper with bulk register/unregister.
src/core/i18n.js Translation loader and DOM applier.
src/core/settingsPanel.js Extracted settings panel UI (from index.js refactor, PR #6).
src/systems/integration/sillytavern.js All SillyTavern event handlers (message lifecycle, swipes, chat changes).
src/systems/generation/promptBuilder.js Builds AI prompts for tracker generation (refactored with focused helpers).
src/systems/generation/parser.js Parses AI responses to extract tracker JSON (~50 pre-compiled regex patterns).
src/systems/generation/apiClient.js API client for separate/external generation modes.
src/systems/generation/injector.js Injects tracker prompts into SillyTavern's generation context.
src/systems/rendering/renderUtils.js Rendering utilities: updateIfChanged() cache, rAF batching.
src/systems/equipment/constants.js Equipment slot definitions, type limits, stat configuration.
src/utils/security.js Input validation, sanitization, validator factory (createStringValidator()).
src/utils/jsonRepair.js JSON repair for malformed AI responses (14 pre-compiled regex patterns).
src/i18n/validator.js Node.js script for validating translation consistency.