import streamlit as st import anthropic import requests import json import base64 import plotly.graph_objects as go from typing import Dict, Any import time import random # Page Config st.set_page_config( page_title="NarrativeCraft: Immersive Story & Character Design Studio", layout="wide", initial_sidebar_state="expanded", page_icon="📚" ) # Custom CSS for professional dark theme st.markdown(""" """, unsafe_allow_html=True) # Initialize session state if 'character' not in st.session_state: st.session_state.character = None if 'story_params' not in st.session_state: st.session_state.story_params = None if 'generated_content' not in st.session_state: st.session_state.generated_content = None if 'voice_data' not in st.session_state: st.session_state.voice_data = None # Constants ARCHETYPES = { "The Hero": "Brave, determined protagonist driven by a noble cause", "The Mentor": "Wise guide with deep knowledge and experience", "The Trickster": "Clever, unpredictable character who challenges conventions", "The Sage": "Philosophical, thoughtful character seeking truth", "The Rebel": "Independent spirit fighting against the system", "The Caregiver": "Nurturing, protective character driven by compassion", "The Creator": "Innovative, artistic character driven by vision", "The Explorer": "Adventure-seeking character driven by curiosity", "The Ruler": "Leadership-focused character seeking control", "The Innocent": "Pure-hearted character maintaining optimism" } GENRES = [ "Epic Fantasy", "Science Fiction", "Dark Fantasy", "Historical Fiction", "Magical Realism", "Contemporary Drama", "Mystery/Thriller", "Romance", "Political Intrigue", "Cyberpunk", "Space Opera", "Urban Fantasy", "Gothic Horror", "Adventure", "Literary Fiction" ] WRITING_STYLES = { "Classical": "Elegant, formal prose with rich descriptions", "Modern Minimalist": "Clean, precise language with impact", "Lyrical": "Poetic, flowing prose with metaphorical depth", "Gritty Realism": "Raw, direct style with stark honesty", "Experimental": "Innovative structure and unique voice", "Journalistic": "Clear, factual style with objectivity", "Stream of Consciousness": "Free-flowing, internal narrative", "Epic": "Grand, sweeping style with historical weight" } def create_character_section(): st.markdown('
Character Design
', unsafe_allow_html=True) col1, col2 = st.columns([2, 1]) with col1: selected_archetype = st.selectbox( "Choose Character Archetype", list(ARCHETYPES.keys()) ) st.info(ARCHETYPES[selected_archetype]) age_range = st.select_slider( "Age Range", options=["Child", "Teen", "Young Adult", "Adult", "Middle-aged", "Elderly"], value="Adult" ) with col2: st.markdown('Character Preview
', unsafe_allow_html=True) st.markdown(f"**Archetype:** {selected_archetype}") st.markdown(f"**Age:** {age_range}") st.markdown('Personality Traits
', unsafe_allow_html=True) trait_col1, trait_col2, trait_col3 = st.columns(3) with trait_col1: confidence = st.slider("Confidence", 1, 10, 5, help="Character's self-assurance level") empathy = st.slider("Empathy", 1, 10, 5, help="Ability to understand others") intelligence = st.slider("Intelligence", 1, 10, 5, help="Mental capability and wit") with trait_col2: courage = st.slider("Courage", 1, 10, 5, help="Bravery in face of adversity") ambition = st.slider("Ambition", 1, 10, 5, help="Drive to achieve goals") loyalty = st.slider("Loyalty", 1, 10, 5, help="Faithfulness to causes/people") with trait_col3: humor = st.slider("Humor", 1, 10, 5, help="Sense of humor and wit") creativity = st.slider("Creativity", 1, 10, 5, help="Imaginative capability") wisdom = st.slider("Wisdom", 1, 10, 5, help="Depth of understanding") st.markdown('Voice & Speech
', unsafe_allow_html=True) voice_col1, voice_col2 = st.columns(2) with voice_col1: voice_type = st.selectbox( "Voice Quality", ["Deep", "Melodious", "Rough", "Soft", "Commanding", "Gentle", "Energetic"], help="Primary characteristic of the character's voice" ) accent = st.selectbox( "Accent", ["Standard", "Regional", "Foreign", "Cultured", "Rustic"], help="Character's accent or dialect" ) with voice_col2: speech_pattern = st.selectbox( "Speech Pattern", ["Formal", "Casual", "Educated", "Street-wise", "Poetic", "Technical", "Noble"], help="How the character typically speaks" ) speech_pacing = st.select_slider( "Speech Pace", options=["Very Slow", "Slow", "Moderate", "Quick", "Very Quick"], value="Moderate", help="Speed and rhythm of speech" ) st.markdown('Emotional Profile
', unsafe_allow_html=True) emo_col1, emo_col2 = st.columns(2) with emo_col1: primary_emotion = st.selectbox( "Primary Emotional State", ["Determined", "Serene", "Passionate", "Calculating", "Troubled", "Optimistic", "Reserved"] ) emotional_stability = st.slider("Emotional Stability", 1, 10, 5) with emo_col2: emotional_expression = st.selectbox( "Emotional Expression Style", ["Open", "Guarded", "Volatile", "Controlled", "Subtle", "Dramatic"] ) emotional_depth = st.slider("Emotional Depth", 1, 10, 5) return { "archetype": selected_archetype, "age_range": age_range, "traits": { "confidence": confidence, "empathy": empathy, "intelligence": intelligence, "courage": courage, "ambition": ambition, "loyalty": loyalty, "humor": humor, "creativity": creativity, "wisdom": wisdom }, "voice": { "type": voice_type, "accent": accent, "pattern": speech_pattern, "pacing": speech_pacing }, "emotional_profile": { "primary_emotion": primary_emotion, "stability": emotional_stability, "expression": emotional_expression, "depth": emotional_depth } } def create_voice_preview(description: str, text: str) -> Dict: """Create voice preview using ElevenLabs API""" url = 'https://api.elevenlabs.io/v1/text-to-voice/create-previews' headers = { 'xi-api-key': st.secrets["ELEVENLABS_API_KEY"], 'Content-Type': 'application/json' } payload = { 'voice_description': description, 'text': text } try: response = requests.post(url, headers=headers, json=payload) if response.status_code == 200: return response.json() else: st.error(f"Voice generation error: {response.status_code}") st.write("Error details:", response.json()) return None except Exception as e: st.error(f"Error: {str(e)}") return None def display_character_profile(character: Dict, voice_data: Dict = None): """Display character profile with voice playback""" if not character: st.warning("No character data available") return try: with st.container(): st.markdown('{character["archetype"]}
', unsafe_allow_html=True) col1, col2 = st.columns([2, 1]) with col1: if character.get("traits"): st.markdown("### Personality Profile") # Personality Traits Radar Chart traits = character['traits'] fig = go.Figure(data=go.Scatterpolar( r=[traits[key] for key in traits.keys()], theta=list(traits.keys()), fill='toself', line=dict(color='#4e8cff') )) fig.update_layout( polar=dict( radialaxis=dict(visible=True, range=[0, 10])), showlegend=False, paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', font=dict(color='white') ) st.plotly_chart(fig, use_container_width=True) with col2: st.markdown('Voice Sample
', unsafe_allow_html=True) if voice_data and 'previews' in voice_data and voice_data['previews']: st.markdown('Core Traits
', unsafe_allow_html=True) st.markdown(f"**Age Range:** {character.get('age_range', 'N/A')}") st.markdown(f"**Voice Type:** {character['voice'].get('type', 'N/A')}") st.markdown(f"**Speech Pattern:** {character['voice'].get('pattern', 'N/A')}") st.markdown('Story Configuration
', unsafe_allow_html=True) # Main Story Parameters col1, col2 = st.columns(2) with col1: genre = st.selectbox("Genre", GENRES) writing_style = st.selectbox( "Writing Style", list(WRITING_STYLES.keys()) ) st.info(WRITING_STYLES[writing_style]) tone = st.select_slider( "Story Tone", options=["Light", "Hopeful", "Neutral", "Dark", "Gritty"], value="Neutral" ) with col2: pacing = st.select_slider( "Story Pacing", options=["Slow & Thoughtful", "Balanced", "Fast & Intense"], value="Balanced" ) plot_complexity = st.slider("Plot Complexity", 1, 10, 5) dialogue_focus = st.slider("Dialogue Emphasis", 1, 10, 5) # Setting Details st.markdown('Setting & Atmosphere
', unsafe_allow_html=True) set_col1, set_col2 = st.columns(2) with set_col1: time_period = st.selectbox( "Time Period", ["Ancient", "Medieval", "Renaissance", "Industrial", "Modern", "Contemporary", "Near Future", "Far Future", "Multiple Eras"] ) setting_type = st.selectbox( "Setting Type", ["Urban", "Rural", "Wilderness", "Fantasy World", "Space", "Underground", "Ocean", "Multiple Locations"] ) with set_col2: atmosphere = st.select_slider( "Atmosphere", options=["Mysterious", "Whimsical", "Tense", "Peaceful", "Epic", "Intimate"], value="Tense" ) realism_level = st.select_slider( "Realism Level", options=["Highly Realistic", "Balanced", "Fantastical"], value="Balanced" ) # Themes and Conflicts st.markdown('Themes & Conflicts
', unsafe_allow_html=True) theme_col1, theme_col2 = st.columns(2) with theme_col1: themes = st.multiselect( "Main Themes", ["Redemption", "Power", "Love", "Justice", "Identity", "Change", "Good vs Evil", "Order vs Chaos", "Faith", "Family"], default=["Identity"] ) conflict_type = st.selectbox( "Primary Conflict", ["Person vs Person", "Person vs Nature", "Person vs Society", "Person vs Self", "Person vs Technology", "Person vs Fate"] ) with theme_col2: moral_ambiguity = st.select_slider( "Moral Ambiguity", options=["Clear Good/Evil", "Some Gray Areas", "Morally Complex"], value="Some Gray Areas" ) theme_exploration = st.select_slider( "Theme Exploration", options=["Subtle", "Balanced", "Overt"], value="Balanced" ) return { "main_params": { "genre": genre, "writing_style": writing_style, "tone": tone, "pacing": pacing, "plot_complexity": plot_complexity, "dialogue_focus": dialogue_focus }, "setting": { "time_period": time_period, "type": setting_type, "atmosphere": atmosphere, "realism": realism_level }, "themes": { "main_themes": themes, "conflict": conflict_type, "moral_ambiguity": moral_ambiguity, "exploration": theme_exploration } } def display_story_preview(story_params: Dict, generated_content: Dict = None): """Display generated story content""" if not generated_content: st.info("Generate preview to see content") return st.markdown('Prologue
', unsafe_allow_html=True) st.markdown(generated_content.get('prologue', '')) # Character Analysis if generated_content.get('character_analysis'): st.markdown('Character Analysis
', unsafe_allow_html=True) st.markdown(generated_content['character_analysis']) # Scene Preview if generated_content.get('scene_preview'): st.markdown('Scene Preview
', unsafe_allow_html=True) st.markdown(generated_content['scene_preview']) with tabs[1]: col1, col2 = st.columns(2) with col1: # Themes st.markdown('Themes & Motifs
', unsafe_allow_html=True) themes = generated_content.get('analysis', {}).get('themes', []) if themes: for theme in themes: st.markdown(f"• {theme}") else: st.info("No themes specified") with col2: # Style Analysis st.markdown('Style Analysis
', unsafe_allow_html=True) style = generated_content.get('analysis', {}).get('style', {}) if style: for key, value in style.items(): st.markdown(f"**{key}:**") st.markdown(value) else: st.info("No style analysis available") st.markdown('