diff --git a/.gitignore b/.gitignore index 0635f66..56a3483 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ env/ # IDEs .vscode/ +.windsurf/ .idea/ *.swp *.swo diff --git a/CONTEXTUAL_RESPONSE_FEATURE.md b/CONTEXTUAL_RESPONSE_FEATURE.md new file mode 100644 index 0000000..d7ebc42 --- /dev/null +++ b/CONTEXTUAL_RESPONSE_FEATURE.md @@ -0,0 +1,393 @@ +# π§ Context-Aware Response Generator + +**Feature Added:** October 11, 2025 +**Status:** β Complete and Tested + +--- + +## Overview + +The Context-Aware Response Generator allows storytellers to generate AI responses that take into account multiple characters' actions and messages simultaneously. This is a powerful tool for creating cohesive narratives that incorporate everyone's contributions. + +--- + +## Key Features + +### 1. **Multi-Character Selection** π +- Select one or more characters to include in the context +- Visual indicators show which characters have pending messages +- "Select All Pending" quick action button +- Character selection with checkboxes showing message count + +### 2. **Two Response Types** π + +#### Scene Description (Broadcast) +- Generates a narrative that addresses all selected characters +- Can be used as a scene narration (broadcast to all) +- Perfect for environmental descriptions or group events + +#### Individual Responses (Private) +- Generates personalized responses for each selected character +- **Automatically parses and distributes** responses to individual characters +- Sends privately to each character's conversation +- Clears pending response flags + +### 3. **Smart Context Building** π + +The system automatically gathers and includes: +- Current scene description +- Recent public actions (last 5) +- Each character's profile (name, description, personality) +- Recent conversation history (last 3 messages per character) +- Optional additional context from storyteller + +### 4. **Response Parsing** π§ + +For individual responses, the system recognizes multiple formats: +``` +**For Bargin:** Your response here +**For Willow:** Your response here + +or + +For Bargin: Your response here +For Willow: Your response here +``` + +The backend automatically: +1. Parses each character's section +2. Adds to their private conversation history +3. Clears their pending response flag +4. Sends via WebSocket if connected + +--- + +## How to Use + +### As a Storyteller: + +1. **Open the Generator** + - Click "βΆ Show Generator" in the storyteller dashboard + - The section expands with all controls + +2. **Select Characters** + - Check the boxes for characters you want to include + - Or click "Select All Pending" for quick selection + - See selection summary below checkboxes + +3. **Choose Response Type** + - **Scene Description:** For general narration or environmental descriptions + - **Individual Responses:** For personalized replies to each character + +4. **Configure Options** + - Select LLM model (GPT-4o, GPT-4, etc.) + - Add optional context/guidance for the AI + +5. **Generate** + - Click "β¨ Generate Context-Aware Response" + - Wait for AI generation (a few seconds) + - Review the generated response + +6. **Use the Response** + - For scenes: Click "Use as Scene" to populate the scene textarea + - For individual: Responses are automatically sent to characters + - You'll get a confirmation alert showing who received responses + +--- + +## Technical Implementation + +### Backend Endpoint + +**POST** `/sessions/{session_id}/generate_contextual_response` + +**Request Body:** +```json +{ + "character_ids": ["char-id-1", "char-id-2"], + "response_type": "individual" | "scene", + "model": "gpt-4o", + "additional_context": "Make it dramatic" +} +``` + +**Response (Individual):** +```json +{ + "response": "Full generated response with all sections", + "model_used": "gpt-4o", + "characters_included": [{"id": "...", "name": "..."}], + "response_type": "individual", + "individual_responses_sent": { + "Bargin": "Individual response text", + "Willow": "Individual response text" + }, + "success": true +} +``` + +### Context Building + +The prompt sent to the LLM includes: + +``` +You are the storyteller/game master in an RPG session. Here's what the characters have done: + +Current Scene: [if set] + +Recent public actions: +- Public message 1 +- Public message 2 + +Character: Bargin +Description: A dwarf warrior +Personality: Gruff and brave +Recent messages: + Bargin: I push open the door + You (Storyteller): You hear creaking hinges + +Character: Willow +Description: An elven archer +Personality: Cautious and observant +Recent messages: + Willow: I look for traps + You (Storyteller): Roll for perception + +Additional context: [if provided] + +Generate [scene/individual responses based on type] +``` + +### Response Parsing (Individual Mode) + +The backend uses regex patterns to extract individual responses: + +```python +patterns = [ + r'\*\*For CharName:\*\*\s*(.*?)(?=\*\*For\s+\w+:|\Z)', + r'For CharName:\s*(.*?)(?=For\s+\w+:|\Z)', + r'\*\*CharName:\*\*\s*(.*?)(?=\*\*\w+:|\Z)', + r'CharName:\s*(.*?)(?=\w+:|\Z)', +] +``` + +Each matched section is: +1. Extracted and trimmed +2. Added to character's conversation history +3. Sent via WebSocket if character is connected +4. Pending flag cleared + +--- + +## UI Components + +### Generator Section + +Located in `StorytellerView`, between the scene section and character list: + +**Visual Design:** +- Pink/red gradient header (stands out from other sections) +- Collapsible with show/hide toggle +- Clear sections for each configuration step +- Visual feedback for pending characters + +**Layout:** +``` +βββββββββββββββββββββββββββββββββββββββββββ +β π§ AI Context-Aware Response Generator β +β βΌ Hide β +βββββββββββββββββββββββββββββββββββββββββββ€ +β Description text β +β β +β Character Selection β +β β Bargin (β) (3 msgs) β +β β Willow (2 msgs) β +β β +β Response Type: [Scene/Individual βΌ] β +β Model: [GPT-4o βΌ] β +β Additional Context: [textarea] β +β β +β [β¨ Generate Context-Aware Response] β +β β +β Generated Response: β +β βββββββββββββββββββββββββββββββββββ β +β β Response text here... β β +β βββββββββββββββββββββββββββββββββββ β +β [Use as Scene] [Clear] β +βββββββββββββββββββββββββββββββββββββββββββ +``` + +--- + +## Benefits + +### For Storytellers +β **Save Time** - Generate responses considering all players at once +β **Consistency** - AI maintains narrative coherence across characters +β **Context Awareness** - Responses reference recent actions and personality +β **Flexibility** - Choose between broadcast scenes or individual replies +β **Efficiency** - Automatic distribution of individual responses + +### For Players +β **Better Immersion** - Responses feel more connected to the story +β **No Waiting** - Storyteller can respond to multiple players quickly +β **Personalization** - Individual responses tailored to each character +β **Privacy Maintained** - Individual responses still private + +--- + +## Example Use Cases + +### Use Case 1: Party Splits Up +**Scenario:** Bargin goes through the front door, Willow scouts around back + +**Action:** +1. Select both Bargin and Willow +2. Choose "Individual Responses" +3. Add context: "The building is guarded" +4. Generate + +**Result:** +- Bargin gets: "As you push open the door, guards immediately turn toward you..." +- Willow gets: "Around the back, you spot an unguarded window..." + +### Use Case 2: Group Enters New Area +**Scenario:** All players enter a mysterious temple + +**Action:** +1. Select all characters +2. Choose "Scene Description" +3. Generate + +**Result:** +A cohesive scene describing the temple that references all characters' recent actions and reactions. + +### Use Case 3: Quick Responses to Pending Messages +**Scenario:** 3 characters have asked questions + +**Action:** +1. Click "Select All Pending (3)" +2. Choose "Individual Responses" +3. Generate + +**Result:** +All three characters receive personalized answers, pending flags cleared. + +--- + +## Additional Feature: Session ID Copy Button + +**Also Added:** Copy button next to Session ID in Storyteller dashboard + +**Usage:** +- Click "π Copy" button next to the Session ID +- ID copied to clipboard +- Alert confirms successful copy +- Makes sharing sessions easy + +**Location:** Storyteller header, next to session ID code + +--- + +## CSS Classes Added + +```css +.contextual-section +.contextual-header +.contextual-generator +.contextual-description +.character-selection +.selection-header +.btn-small +.character-checkboxes +.character-checkbox +.checkbox-label +.pending-badge-small +.message-count +.selection-summary +.response-type-selector +.response-type-help +.model-selector-contextual +.additional-context +.btn-large +.generated-response +.response-content +.response-actions +.session-id-container +.btn-copy +``` + +--- + +## Testing + +### Manual Testing Checklist + +- [ ] Select single character - generates response +- [ ] Select multiple characters - includes all in context +- [ ] Scene description - generates cohesive narrative +- [ ] Individual responses - parses and sends to each character +- [ ] "Select All Pending" button - selects correct characters +- [ ] Additional context - influences AI generation +- [ ] Model selection - uses chosen model +- [ ] Copy session ID button - copies to clipboard +- [ ] Collapse/expand generator - UI works correctly +- [ ] Character receives individual response - appears in their conversation +- [ ] Pending flags cleared - after individual responses sent + +--- + +## Future Enhancements + +Potential improvements for later versions: + +1. **Response Templates** - Save common response patterns +2. **Batch Actions** - Send same scene to subset of characters +3. **Response History** - View previous generated responses +4. **Fine-tune Prompts** - Custom prompt templates per game +5. **Voice/Tone Settings** - Adjust AI personality (serious/playful/dark) +6. **Character Reactions** - Generate suggested player reactions +7. **Conversation Summaries** - AI summary of what happened +8. **Export Context** - Save context for reference + +--- + +## Files Modified + +### Backend +- `main.py` + - Added `ContextualResponseRequest` model + - Added `/generate_contextual_response` endpoint + - Added response parsing logic + - Added individual message distribution + +### Frontend +- `frontend/src/components/StorytellerView.js` + - Added contextual response state variables + - Added character selection functions + - Added response generation function + - Added copy session ID function + - Added generator UI section + +- `frontend/src/App.css` + - Added `.contextual-*` styles + - Added `.character-checkbox` styles + - Added `.btn-copy` styles + - Added `.session-id-container` styles + - Added `.response-type-help` styles + +--- + +## Summary + +The Context-Aware Response Generator is a powerful tool that significantly improves storyteller efficiency. By allowing the storyteller to generate responses that consider multiple characters simultaneously, it: + +- Reduces response time +- Improves narrative consistency +- Maintains privacy through automatic distribution +- Provides flexibility between scene and individual responses +- Makes managing multiple players much easier + +Combined with the session ID copy button, these features make the storyteller experience more streamlined and professional. + +**Status:** β Ready for use! diff --git a/DEMO_SESSION.md b/DEMO_SESSION.md new file mode 100644 index 0000000..6767657 --- /dev/null +++ b/DEMO_SESSION.md @@ -0,0 +1,328 @@ +# π² Demo Session - "The Cursed Tavern" + +**Pre-configured test session for quick development and testing** + +--- + +## Quick Access + +When you start the server, a demo session is automatically created with: + +- **Session ID:** `demo-session-001` +- **Session Name:** "The Cursed Tavern" +- **2 Pre-configured Characters** +- **Starting Scene & Adventure Hook** + +--- + +## How to Use + +### From the Home Page (Easiest) + +Three big colorful buttons appear at the top: + +1. **π² Join as Storyteller** - Opens storyteller dashboard +2. **βοΈ Play as Bargin (Dwarf Warrior)** - Opens character view as Bargin +3. **πΉ Play as Willow (Elf Ranger)** - Opens character view as Willow + +Just click and you're in! + +### Manual Access + +If you want to manually enter the session: + +**As Storyteller:** +- Session ID: `demo-session-001` + +**As Bargin:** +- Session ID: `demo-session-001` +- Character ID: `char-bargin-001` + +**As Willow:** +- Session ID: `demo-session-001` +- Character ID: `char-willow-002` + +--- + +## Characters + +### Bargin Ironforge βοΈ + +**Race:** Dwarf +**Class:** Warrior +**Personality:** Brave but reckless. Loves a good fight and a strong ale. Quick to anger but fiercely loyal to companions. + +**Description:** +A stout dwarf warrior with a braided red beard and battle-scarred armor. Carries a massive war axe named 'Grudgekeeper'. + +**Character ID:** `char-bargin-001` +**LLM Model:** GPT-3.5 Turbo + +--- + +### Willow Moonwhisper πΉ + +**Race:** Elf +**Class:** Ranger +**Personality:** Cautious and observant. Prefers to scout ahead and avoid unnecessary conflict. Has an affinity for nature and animals. + +**Description:** +An elven ranger with silver hair and piercing green eyes. Moves silently through shadows, bow always at the ready. + +**Character ID:** `char-willow-002` +**LLM Model:** GPT-3.5 Turbo + +--- + +## The Adventure + +### Scenario: The Cursed Tavern + +The village of Millhaven has a problem. The old Rusty Flagon tavern, once a cheerful gathering place, has become a source of terror. Locals report: + +- **Ghostly figures** moving through the windows +- **Unearthly screams** echoing from within +- **Eerie green light** flickering after dark +- Strange whispers that drive people mad + +The village elder has hired adventurers to investigate and put an end to the disturbances. + +### Starting Scene + +``` +You stand outside the weathered doors of the Rusty Flagon tavern. +Strange whispers echo from within, and the windows flicker with an +eerie green light. The townspeople warned you about this place, +but the reward for investigating is too good to pass up. +``` + +### Initial Message (Both Characters) + +When the characters first join, they see: + +``` +Welcome to the Cursed Tavern adventure! You've been hired by the +village elder to investigate strange happenings at the old tavern. +Locals report seeing ghostly figures and hearing unearthly screams. +Your mission: discover what's causing the disturbances and put an +end to it. What would you like to do? +``` + +--- + +## Testing Scenarios + +### Test the Message System + +1. **Private Messages:** + - Bargin: "I quietly check the door for traps" + - Willow: "I scan the area for signs of danger" + - Storyteller should see both privately + +2. **Public Messages:** + - Bargin: "I kick open the door!" (public) + - Willow should see this action + - Storyteller sees it too + +3. **Mixed Messages:** + - Bargin (public): "I step inside boldly" + - Bargin (private): "I'm actually terrified but don't want Willow to know" + - Willow sees: "I step inside boldly" + - Storyteller sees: Both parts + +### Test Context-Aware Responses + +1. Select both Bargin and Willow in storyteller dashboard +2. Click "Select All Pending" +3. Choose "Individual Responses" +4. Generate context-aware response +5. Verify each character receives their personalized response + +### Test AI Suggestions + +1. As storyteller, view Bargin's conversation +2. Click "β¨ AI Suggest" +3. Review generated suggestion +4. Edit and send + +--- + +## Development Benefits + +This demo session eliminates the need to: + +- Create a new session every time you restart the server +- Manually create character profiles +- Enter character descriptions and personalities +- Type in session IDs repeatedly +- Set up test scenarios + +Just restart the server and click one button to test! + +--- + +## Server Startup Output + +When you start the server with `bash start.sh`, you'll see: + +``` +============================================================ +π² DEMO SESSION CREATED! +============================================================ +Session ID: demo-session-001 +Session Name: The Cursed Tavern + +Characters: + 1. Bargin Ironforge (ID: char-bargin-001) + A stout dwarf warrior with a braided red beard and battle-scarred armor... + + 2. Willow Moonwhisper (ID: char-willow-002) + An elven ranger with silver hair and piercing green eyes... + +Scenario: The Cursed Tavern +Scene: You stand outside the weathered doors of the Rusty Flagon tavern... + +============================================================ +To join as Storyteller: Use session ID 'demo-session-001' +To join as Bargin: Use session ID 'demo-session-001' + character ID 'char-bargin-001' +To join as Willow: Use session ID 'demo-session-001' + character ID 'char-willow-002' +============================================================ +``` + +--- + +## Customization + +Want to modify the demo session? Edit `create_demo_session()` in `main.py`: + +### Change Characters + +```python +# Modify character attributes +bargin = Character( + name="Your Character Name", + description="Your description", + personality="Your personality", + llm_model="gpt-4", # Change model + # ... +) +``` + +### Change Scenario + +```python +demo_session = GameSession( + name="Your Adventure Name", + current_scene="Your starting scene...", + scene_history=["Your backstory..."] +) +``` + +### Add More Characters + +```python +# Create a third character +third_char = Character(...) +demo_session.characters[third_char.id] = third_char +``` + +### Change Session ID + +```python +demo_session_id = "my-custom-id" +``` + +--- + +## Disabling Demo Session + +If you want to disable auto-creation of the demo session, comment out this line in `main.py`: + +```python +if __name__ == "__main__": + import uvicorn + + # create_demo_session() # Comment this out + + uvicorn.run(app, host="0.0.0.0", port=8000) +``` + +--- + +## Technical Details + +### Implementation + +The demo session is created in the `create_demo_session()` function in `main.py`, which: + +1. Creates a `GameSession` object +2. Creates two `Character` objects +3. Adds an initial storyteller message to both character histories +4. Stores the session in the in-memory `sessions` dictionary +5. Prints session info to the console + +### Frontend Integration + +The home page (`SessionSetup.js`) includes three quick-access functions: + +- `joinDemoStoryteller()` - Calls `onCreateSession("demo-session-001")` +- `joinDemoBargin()` - Calls `onJoinSession("demo-session-001", "char-bargin-001")` +- `joinDemoWillow()` - Calls `onJoinSession("demo-session-001", "char-willow-002")` + +These bypass the normal session creation/joining flow. + +--- + +## Why This Matters + +During development and testing, you'll restart the server **dozens of times**. Without a demo session, each restart requires: + +1. Click "Create Session" +2. Enter session name +3. Wait for creation +4. Copy session ID +5. Open new window +6. Paste session ID +7. Enter character name +8. Enter character description +9. Enter personality +10. Select model +11. Click join +12. Repeat for second character + +With the demo session: + +1. Click one button + +**That's a huge time saver!** + +--- + +## Future Enhancements + +When database persistence is implemented, you could: + +- Save demo session to database on first run +- Load multiple pre-configured adventures +- Create a "Quick Start Gallery" of scenarios +- Import/export demo sessions as JSON + +--- + +## FAQ + +**Q: Does the demo session persist across server restarts?** +A: No, it's recreated fresh each time. This ensures a clean state for testing. + +**Q: Can I have multiple demo sessions?** +A: Yes! Just create additional sessions with different IDs in the startup function. + +**Q: Will the demo session interfere with real sessions?** +A: No, it's just another session in memory. You can create regular sessions alongside it. + +**Q: Can I modify character stats mid-session?** +A: Not yet, but you can edit the character objects directly in the code and restart. + +--- + +**Happy Testing!** π²β¨ diff --git a/FIXES_SUMMARY.md b/FIXES_SUMMARY.md new file mode 100644 index 0000000..8fbf9e2 --- /dev/null +++ b/FIXES_SUMMARY.md @@ -0,0 +1,314 @@ +# π§ Bug Fixes & Improvements + +**Date:** October 11, 2025 +**Status:** β Complete + +--- + +## Fixes Applied + +### 1. **Character Chat Log History** π + +**Problem:** +Players could only see the most recent storyteller response in their conversation. Previous messages disappeared, making it impossible to review the conversation context. + +**Root Cause:** +The character WebSocket handler was only listening for `storyteller_response` message type, but the context-aware response generator was sending `new_message` type. + +**Solution:** +Updated `CharacterView.js` to handle both message types: + +```javascript +// Before +else if (data.type === 'storyteller_response') { + setMessages(prev => [...prev, data.message]); +} + +// After +else if (data.type === 'storyteller_response' || data.type === 'new_message') { + setMessages(prev => [...prev, data.message]); +} +``` + +**Impact:** +β Characters now see full conversation history +β Context is preserved when reading back messages +β Individual responses from context-aware generator appear correctly + +--- + +### 2. **Pydantic Deprecation Warnings** β οΈ + +**Problem:** +10 deprecation warnings when running the application: + +``` +PydanticDeprecatedSince20: The `dict` method is deprecated; +use `model_dump` instead. +``` + +**Root Cause:** +Using Pydantic V1 `.dict()` method with Pydantic V2 models. + +**Solution:** +Replaced all 9 instances of `.dict()` with `.model_dump()` in `main.py`: + +**Locations Fixed:** +1. Line 152: Character history in WebSocket +2. Line 153: Public messages in WebSocket +3. Line 180: Public message broadcasting +4. Line 191: Mixed message broadcasting +5. Line 207: Character message forwarding +6. Line 234: Session state conversation history +7. Line 240: Session state public messages +8. Line 262: Storyteller response +9. Line 487: Context-aware individual responses +10. Line 571: Pending messages +11. Line 594: Character conversation endpoint + +**Impact:** +β No more deprecation warnings +β Code is Pydantic V2 compliant +β Future-proof for Pydantic V3 + +--- + +### 3. **Session ID Copy Button** π + +**Problem:** +No easy way to share the session ID with players. Had to manually select and copy the ID. + +**Root Cause:** +Missing UI affordance for common action. + +**Solution:** +Added copy button with clipboard API: + +```javascript +// Copy function +const copySessionId = () => { + navigator.clipboard.writeText(sessionId).then(() => { + alert('β Session ID copied to clipboard!'); + }).catch(err => { + alert('Failed to copy session ID. Please copy it manually.'); + }); +}; + +// UI +
+ Session ID: {sessionId}
+
+ π‘ The AI will generate responses in this format:
+ [CharacterName] Response text here.
+ Each response is automatically parsed and sent privately
+ to the respective character.
+
Private character-storyteller interactions
+ {/* Demo Session Quick Access */} ++ Jump right into a pre-configured adventure with two characters already created! +
+Start a new game as the storyteller
diff --git a/frontend/src/components/StorytellerView.js b/frontend/src/components/StorytellerView.js index 6e93fef..d7b4ef5 100644 --- a/frontend/src/components/StorytellerView.js +++ b/frontend/src/components/StorytellerView.js @@ -12,6 +12,16 @@ function StorytellerView({ sessionId }) { const [currentScene, setCurrentScene] = useState(''); const [isConnected, setIsConnected] = useState(false); const [isGeneratingSuggestion, setIsGeneratingSuggestion] = useState(false); + + // Context-aware response state + const [selectedCharacterIds, setSelectedCharacterIds] = useState([]); + const [contextualResponseType, setContextualResponseType] = useState('scene'); + const [contextualAdditionalContext, setContextualAdditionalContext] = useState(''); + const [contextualModel, setContextualModel] = useState('gpt-4o'); + const [isGeneratingContextual, setIsGeneratingContextual] = useState(false); + const [generatedContextualResponse, setGeneratedContextualResponse] = useState(''); + const [showContextualGenerator, setShowContextualGenerator] = useState(false); + const wsRef = useRef(null); useEffect(() => { @@ -137,6 +147,108 @@ function StorytellerView({ sessionId }) { } }; + // Toggle character selection for contextual response + const toggleCharacterSelection = (charId) => { + setSelectedCharacterIds(prev => + prev.includes(charId) + ? prev.filter(id => id !== charId) + : [...prev, charId] + ); + }; + + // Select all characters with pending messages + const selectAllPending = () => { + const pendingIds = Object.entries(characters) + .filter(([_, char]) => char.pending_response) + .map(([id, _]) => id); + setSelectedCharacterIds(pendingIds); + }; + + // Generate contextual response + const generateContextualResponse = async () => { + if (selectedCharacterIds.length === 0 || isGeneratingContextual) return; + + setIsGeneratingContextual(true); + setGeneratedContextualResponse(''); + + try { + const response = await fetch( + `${API_URL}/sessions/${sessionId}/generate_contextual_response`, + { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + character_ids: selectedCharacterIds, + response_type: contextualResponseType, + model: contextualModel, + additional_context: contextualAdditionalContext || null + }) + } + ); + + if (!response.ok) { + throw new Error('Failed to generate contextual response'); + } + + const data = await response.json(); + + // If individual responses were sent, show confirmation + if (data.response_type === 'individual' && data.individual_responses_sent) { + const sentCount = Object.keys(data.individual_responses_sent).length; + const sentNames = Object.keys(data.individual_responses_sent).join(', '); + + if (sentCount > 0) { + alert(`β Individual responses sent to ${sentCount} character(s): ${sentNames}\n\nThe responses have been delivered privately to each character.`); + + // Clear selections after successful send + setSelectedCharacterIds([]); + setContextualAdditionalContext(''); + + // Update character states to reflect no pending responses + setCharacters(prev => { + const updated = { ...prev }; + Object.keys(data.individual_responses_sent).forEach(charName => { + const charEntry = Object.entries(updated).find(([_, char]) => char.name === charName); + if (charEntry) { + const [charId, char] = charEntry; + updated[charId] = { ...char, pending_response: false }; + } + }); + return updated; + }); + } + + // Still show the full generated response for reference + setGeneratedContextualResponse(data.response); + } else { + // Scene description - just show the response + setGeneratedContextualResponse(data.response); + } + } catch (error) { + console.error('Error generating contextual response:', error); + alert('Failed to generate contextual response. Please try again.'); + } finally { + setIsGeneratingContextual(false); + } + }; + + // Use generated response as scene + const useAsScene = () => { + if (!generatedContextualResponse) return; + setSceneText(generatedContextualResponse); + setShowContextualGenerator(false); + }; + + // Copy session ID to clipboard + const copySessionId = () => { + navigator.clipboard.writeText(sessionId).then(() => { + alert('β Session ID copied to clipboard!'); + }).catch(err => { + console.error('Failed to copy:', err); + alert('Failed to copy session ID. Please copy it manually.'); + }); + }; + const selectedChar = selectedCharacter ? characters[selectedCharacter] : null; const pendingCount = Object.values(characters).filter(c => c.pending_response).length; @@ -145,7 +257,14 @@ function StorytellerView({ sessionId }) {Session ID: {sessionId}
+ Session ID: {sessionId}
+
{isConnected ? 'β Connected' : 'β Disconnected'} @@ -197,6 +316,136 @@ function StorytellerView({ sessionId }) { )}
+ Generate a response that takes into account multiple characters' actions and messages. + Perfect for creating scenes or responses that incorporate everyone's contributions. +
+ + {/* Character Selection */} +
+ π‘ The AI will generate responses in this format: [CharacterName] Response text here. Each response is automatically parsed and sent privately to the respective character.
+