This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Loner Assistant v2.0 is a comprehensive web-based companion tool for the Loner solo RPG system. It enables players to manage campaigns, characters, story content, and game mechanics entirely in a browser using client-side architecture with IndexedDB for persistent storage.
- What is Loner? A minimalist, tag-based solo RPG with emergent storytelling. Rules are rules-light and narrative-focused (see
loner-en.md). - Architecture: Vanilla JavaScript (no frameworks), Dexie.js for database, pure HTML/CSS for UI
- Status: Phase 4 - Advanced features (roll tables, supplements, Get Inspired flavors) are implemented; Phases 1-3 (foundation, sessions, content management) are complete
- User: Roberto, the creator. He has strong debugging skills, appreciates clean modular architecture, and favors simpler solutions over complex ones
- Frontend: Vanilla HTML, CSS, JavaScript (no frameworks)
- Database: Dexie.js 3.x (IndexedDB wrapper) - provides structured data storage with version migrations
- Libraries:
- Dexie.js for database management
- Quill.js (rich text editor, optional for future enhancements)
- No build process - application runs directly in the browser
loner-assistant/
├── index.html # Single-page HTML shell
├── css/style.css # All application styling
├── js/
│ ├── main.js # App initialization, state management, coordination
│ ├── database.js # Dexie.js setup and all CRUD operations
│ ├── oracle.js # Dice rolling (d6), oracle interpretation, twist counter
│ ├── campaigns.js # Campaign CRUD and display
│ ├── characters.js # Character sheet creation, editing, display
│ ├── sessions.js # Session management within campaigns
│ ├── npcs.js # NPC CRUD and relationship management
│ ├── locations.js # Location CRUD and tracking
│ ├── threads.js # Narrative thread (ongoing storyline) management
│ ├── events.js # Event log and timeline display
│ ├── editor.js # Rich text note editor with table auto-insertion
│ ├── table-manager.js # UI for table selection and rolling
│ ├── tables.js # Roll table execution and history
│ └── ui.js # Shared UI utilities (modals, panels, etc.)
├── data/
│ ├── table-registry.js # Central registry of all available supplements
│ └── tables/
│ ├── core-loner.js # Core Loner tables (Adventure Maker, Twist, Actions, etc.)
│ ├── core-inspired.js # Core "Get Inspired" flavor (random word generation)
│ └── supplements/ # Future supplement tables
│ └── sample-encounters.js
└── lib/
└── dexie.min.js # Database library (minified)
The database is defined in database.js with version 3 (migrations handled automatically). Key collections:
- campaigns - Adventure/story collections
- sessions - Individual play sessions within campaigns
- characters - Player characters (protagonist)
- npcs - Non-player characters
- locations - Story locations
- events - Event log entries (timestamped actions)
- threads - Narrative threads (ongoing storylines with status)
- rollTables - User-defined and supplement tables
- supplements - Table collections/supplements
- rollHistory - All oracle rolls (chance/risk dice results)
- tableRolls - Results from rolling supplemental tables
- userPreferences - User settings (e.g., Get Inspired flavor selection)
- customTables - User-created custom tables
- App initialization and DOMContentLoaded setup
- Global state management (currentCampaignId, currentSessionId, currentCharacterId)
- State persistence to localStorage (saveAppState/loadAppState)
- Theme switching (dark/light mode)
- Module coordination
Key Functions:
setCurrentCampaign(campaignId, sessionId)- Switch active campaignsetCurrentCharacter(characterId)- Set active charactergetState()- Return current app state
All database operations go through this module. It abstracts Dexie.js completely, exposing simple async functions for CRUD.
Architecture Pattern:
- Database schema defined once at top
- Separate functions for each entity type (campaigns, characters, etc.)
- All functions are async
- No complex queries - keep it simple
Critical Functions (examples):
createCampaign(name, description)- New campaigngetSession(sessionId)- Fetch sessionsaveTwistCounter(sessionId, count)- Persist twist counter stateaddRollHistory(sessionId, result)- Log oracle roll
Handles all Loner core mechanics:
- d6 rolling (
rollD6()) - Oracle consultation (Chance die vs Risk die)
- Result interpretation ("Yes", "No", "Yes, but...", "Yes, and...", "No, but...", "No, and...")
- Twist counter logic (reaches 3 → trigger twist, reset to 0)
- Advantage/Disadvantage modifiers
Key Functions:
rollOracle()- Main oracle roll with current modifiersinterpretOracleRoll(chanceDice, riskDice)- Convert dice to result textupdateTwistCounter()- Visual display of twist counter
Module per content type. Each handles:
- UI rendering (lists, forms, detail views)
- Event listeners (create, edit, delete, view)
- Calling database functions
- Modal management
Pattern: Functions are named to match UI actions (createCampaignForm(), showCharacterDetail(), etc.)
Handles rolling supplemental tables (Adventure Maker, Get Inspired, custom tables):
table-manager.js- UI for selecting supplements and tables, two-step selection interfacetables.js- Execute rolls and save results to tableRolls collection
Two-Step Interface: User first selects a supplement, then selects a specific table within that supplement (scales well as content expands).
Rich text editor with auto-insertion capabilities:
- Integrates with Quill.js for rich text
- Auto-insert oracle results, table rolls, and character references
- Save notes to database
- Open
index.htmlin a modern browser (no build step needed) - IndexedDB storage persists locally in browser
- Check browser console for debug logs
When modifying the database schema:
- Increment version in
database.js(db.version(N)) - Add migration logic if needed
- Dexie handles schema changes automatically on version bump
- Test with fresh IndexedDB (clear storage in DevTools if needed)
- Create new file in
data/tables/(e.g.,space-opera.js) - Follow the format of
core-loner.jsandcore-inspired.js - Register supplement in
data/table-registry.js - Enable via UI (userPreferences collection)
- Use browser DevTools IndexedDB inspector to verify data
- Console logs throughout for debugging (prefixed with emojis: 🎲, 💾, ⚡, etc.)
- Test localStorage state persistence (can view in DevTools Storage tab)
Each JS module should:
- Have a clear comment block at the top explaining its purpose
- Define only functions related to its concern
- Use async/await for database calls
- Use descriptive function names that match UI actions
- All Dexie calls are async - use
awaitor.then() - Always handle null returns (item might not exist)
- Database errors typically mean schema mismatch or logic error
- Avoid querying all elements; use data attributes (
data-*) - Use event delegation where practical
- Update state in database BEFORE updating UI (not the other way around)
- Functions that display UI:
show*(),render*(),display*() - Functions that modify data:
create*(),update*(),delete*(),save*() - Variables for DOM elements:
*Elem,*El, or just descriptive names
The app tracks three pieces of state globally:
currentCampaignId- Active campaigncurrentSessionId- Current session within that campaigncurrentCharacterId- Currently viewed character
This is persisted to localStorage and restored on app load. When a user switches campaigns, both campaignId and sessionId are updated together.
- Stored per-session in
sessions.twistCounter - Increments when oracle roll produces doubles (equal dice)
- At 3, triggers a twist event (random 2d6 roll on Twist Table)
- Resets to 0 after twist
- Visual indicator shows current count and proximity to twist
Users select which "flavor" (theme) for Get Inspired tables:
- Stored in
userPreferencescollection with key 'getInspiredFlavor' - Multiple flavors can exist for future supplements
- Currently only "core-inspired" flavor exists
Tables are loaded from registry, not hardcoded:
- Registry (
table-registry.js) is single source of truth for available supplements - Each supplement can define multiple tables
- UI dynamically builds dropdown of available tables based on selected supplement
- This allows adding new supplements without modifying UI code
Both characters and NPCs use descriptive "tags" instead of numeric stats (per Loner design):
- Tags are stored as comma-separated strings
- When displaying, split on comma and show as badges
- No validation of tag content - pure narrative
- Add to Dexie schema in
database.js(e.g.,factions: '++id, campaignId, name, status') - Increment database version
- Add CRUD functions in
database.js - Create
js/factions.jswith UI functions - Add navigation button in
index.html - Wire up in
main.jsfor view management
- Update relevant table file in
data/tables/(e.g.,core-loner.js) - No database changes needed unless storing custom user tables
- Reload to pick up new tables
- Inspect database via DevTools → Application → IndexedDB
- Verify data structure matches schema
- If schema is wrong, check version in
database.jsand increment if needed - Clear IndexedDB in DevTools and refresh to trigger fresh initialization
The application follows Roberto's phased development plan:
- Phase 1 (Complete): Foundation - campaigns, characters, oracle, scenes, conflicts
- Phase 2 (Complete): Sessions, note editor with auto-insertion
- Phase 3 (Complete): Content management (NPCs, locations, threads, events)
- Phase 4 (Current): Advanced - roll tables, supplements, Get Inspired flavors, extensible table system
- Phase 5 (Planned): Polish - keyboard shortcuts, mobile UX, drag-and-drop, tutorial
Current focus is refinement within Phase 4 features and user experience improvements.
- Empty State Pattern - When a list is empty, show helpful empty state UI (not just a blank page)
- Two-Step Selection - For UI with many items (like tables), use two-step selection (e.g., supplement first, then table)
- Auto-Insert from Tools - Game tools (oracle, tables) can auto-insert results into active note
- Session Statefulness - Loading a session populates all displays with that session's data
- Immutable State Updates - Always save to database before updating UI
- Check
SESSION_HANDOFF.mdfor very recent context about Roberto's development approach and learnings - Roberto prefers clear architectural decisions with reasoning, not abstract theory
- Test database changes thoroughly - schema bugs can corrupt data
- Use browser DevTools console heavily; check for any red errors or warnings
- Mobile responsiveness is desired but desktop-first focus
- Loner rulebook (
loner-en.md) is authoritative for game mechanics; refer to it when implementing rules