From 35cffa2696d2451087f318ae296728b8b45263e7 Mon Sep 17 00:00:00 2001 From: ARIA Date: Fri, 3 Jul 2026 11:22:30 +0200 Subject: [PATCH] Added Agents.md file --- AGENTS.md | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..02a2a58 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,330 @@ +# 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 build step** — files are loaded directly by SillyTavern's extension loader. + +--- + +## Quick start + +- **Entry point**: `index.js` (loaded by SillyTavern via `manifest.json`) +- **CSS**: `style.css` (single file, ~12,300 lines) +- **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). + +--- + +## Manifest and loading + +`manifest.json` tells SillyTavern how to load the extension: + +```json +{ + "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`: +```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 +│ ├── state.js # Centralized state variables + getters/setters +│ ├── persistence.js # Save/load settings, chat data, migrations +│ ├── events.js # SillyTavern event system wrapper +│ └── i18n.js # Translation system (fetches JSON, applies data-i18n-key) +├── 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 +├── systems/ # Feature systems (organized by concern) +│ ├── generation/ # AI prompt generation, parsing, API calls +│ │ ├── promptBuilder.js # Builds tracker prompts for AI +│ │ ├── parser.js # Parses AI responses to extract tracker data +│ │ ├── 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 +│ ├── 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 +│ ├── 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 + ├── 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 + ├── sillyTavernExpressions.js # ST expression mapping + ├── thoughtBasedExpressionPortraits.js # Expression-to-portrait mapping + └── transformations.js # Data transformation utilities +``` + +### Data flow + +1. **User sends message** → `MESSAGE_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 responds** → `MESSAGE_RECEIVED` event fires +7. **Extension hooks** `onMessageReceived` → `parser.js` extracts tracker data +8. **Rendering**: `renderUserStats()`, `renderInfoBox()`, `renderThoughts()`, etc. update the UI +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. + +### 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 + +--- + +## Development commands + +```bash +# Validate translation files (check missing keys, type mismatches, empty values) +npm run validate_locale_once + +# Watch mode: re-validate on file changes +npm run validate_locale +``` + +No build, lint, test, or typecheck commands exist. The code runs directly in the browser. + +--- + +## 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: +```js +$panelContainer = $('#rpg-companion-panel') +$userStatsContainer = $('#rpg-user-stats') +``` + +### Event system + +All SillyTavern event registration goes through `src/core/events.js`: +```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` + +### 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 from `src/core/state.js`. New settings fields should be added to both the state module (runtime defaults) AND `src/core/config.js` (documentation defaults). + +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. **The `package.json` has no runtime dependencies**. The `devDependencies` (chokidar, fs-extra, glob) are only for the i18n validator. + +10. **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. + +--- + +## File reference + +| File | Purpose | +|------|---------| +| `index.js` | Main entry point. All imports, UI init, event registration, startup logic. | +| `manifest.json` | SillyTavern extension manifest (name, loading order, JS/CSS files). | +| `style.css` | All CSS (~12,300 lines). Themes, animations, responsive breakpoints. | +| `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. | +| `src/core/state.js` | Central state module. All extension-wide variables live here. | +| `src/core/persistence.js` | Settings/chat data save/load, version migrations. | +| `src/core/config.js` | Extension name, path detection, default settings documentation. | +| `src/core/events.js` | SillyTavern event system wrapper with bulk register/unregister. | +| `src/core/i18n.js` | Translation loader and DOM applier. | +| `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. | +| `src/systems/generation/parser.js` | Parses AI responses to extract tracker JSON. | +| `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/i18n/validator.js` | Node.js script for validating translation consistency. |