Spaces:
Sleeping
Sleeping
Update homepage.py (#3)
Browse files- Update homepage.py (c9f159aaa5e0af52756239bdc5c6b8f8cf3f0179)
Co-authored-by: Shashank Verma <ShashankV1@users.noreply.huggingface.co>
- homepage.py +81 -265
homepage.py
CHANGED
|
@@ -1,280 +1,96 @@
|
|
| 1 |
import streamlit as st
|
| 2 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
from datetime import datetime
|
| 4 |
|
| 5 |
-
# ---
|
| 6 |
-
# Streamlit automatically discovers pages in a 'pages' subdirectory.
|
| 7 |
-
# This homepage.py serves as the default 'Home' page.
|
| 8 |
-
# Navigation is handled by Streamlit's sidebar, automatically generated from pages/*.py files.
|
| 9 |
-
|
| 10 |
-
# --- Optional: Drawing canvas library check ---
|
| 11 |
-
try:
|
| 12 |
-
from streamlit_drawable_canvas import st_canvas
|
| 13 |
-
DRAWING_AVAILABLE = True
|
| 14 |
-
except ImportError:
|
| 15 |
-
DRAWING_AVAILABLE = False
|
| 16 |
-
|
| 17 |
-
st.set_page_config(
|
| 18 |
-
page_title="HappyTrails",
|
| 19 |
-
page_icon="🐾",
|
| 20 |
-
layout="wide",
|
| 21 |
-
initial_sidebar_state="expanded"
|
| 22 |
-
)
|
| 23 |
-
|
| 24 |
-
# --- THEME DEFINITIONS (IMPORTANT: COPY THIS BLOCK TO ALL FILES IN THE 'PAGES' FOLDER) ---
|
| 25 |
-
# These themes apply global styles and define character-based hero images.
|
| 26 |
THEMES = {
|
| 27 |
"boy": {
|
| 28 |
-
"primary_background": "#D0E4FF",
|
| 29 |
-
"secondary_background": "#
|
| 30 |
-
"
|
| 31 |
-
"accent_color": "#E53935", # Red for buttons/accents
|
| 32 |
-
"hero_image": "https://placehold.co/400x300/e53935/ffffff?text=Spiderman+Theme",
|
| 33 |
-
"welcome_message": "Ready for an adventure, {name}!" # Updated to use {name}
|
| 34 |
-
},
|
| 35 |
-
"girl": {
|
| 36 |
-
"primary_background": "#FFEBFB", # Light pink/purple for Gwen Stacy
|
| 37 |
-
"secondary_background": "#F5C7F7", # Slightly darker pink
|
| 38 |
-
"text_color": "#7B1FA2",
|
| 39 |
-
"accent_color": "#EC407A",
|
| 40 |
-
"hero_image": "https://placehold.co/400x300/EC407A/ffffff?text=Gwen+Stacy+Theme",
|
| 41 |
-
"welcome_message": "Hello, {name}! Let's make today amazing!" # Updated to use {name}
|
| 42 |
-
},
|
| 43 |
-
"neutral": { # Default for "Other" or if profile not set
|
| 44 |
-
"primary_background": "#E0FFE0",
|
| 45 |
-
"secondary_background": "#C9F0C9",
|
| 46 |
-
"text_color": "#33691E",
|
| 47 |
-
"accent_color": "#8BC34A",
|
| 48 |
-
"hero_image": "https://placehold.co/400x300/8BC34A/ffffff?text=HappyTrails+Theme",
|
| 49 |
-
"welcome_message": "Welcome to HappyTrails, {name}!" # Updated to use {name}
|
| 50 |
}
|
| 51 |
}
|
| 52 |
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
<circle cx="30" cy="18" r="8"/> <!-- Head -->
|
| 57 |
-
<circle cx="30" cy="42" r="18"/> <!-- Body -->
|
| 58 |
-
<path d="M30,26 L10,5 L5,15 L15,25" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> <!-- Top-left leg -->
|
| 59 |
-
<path d="M30,26 L50,5 L55,15 L45,25" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> <!-- Top-right leg -->
|
| 60 |
-
<path d="M30,40 L10,50 L5,55 L15,45" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> <!-- Mid-left leg -->
|
| 61 |
-
<path d="M30,40 L50,50 L55,55 L45,45" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> <!-- Mid-right leg -->
|
| 62 |
-
</svg>
|
| 63 |
-
"""
|
| 64 |
-
|
| 65 |
-
GWEN_STACY_ICON_SVG = """
|
| 66 |
-
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 60 60" style="vertical-align:middle; margin-right:10px; fill:currentColor;">
|
| 67 |
-
<!-- Hood shape -->
|
| 68 |
-
<path d="M30 5 L5 25 V55 H55 V25 L30 5 Z" fill="none" stroke="currentColor" stroke-width="2"/>
|
| 69 |
-
<!-- Eye shapes - stylized like her mask -->
|
| 70 |
-
<path d="M18 25 C15 20 10 25 18 35 C26 25 21 20 18 25 Z" fill="currentColor"/> <!-- Left Eye -->
|
| 71 |
-
<path d="M42 25 C45 20 50 25 42 35 C34 25 39 20 42 25 Z" fill="currentColor"/> <!-- Right Eye -->
|
| 72 |
-
</svg>
|
| 73 |
-
"""
|
| 74 |
-
|
| 75 |
-
# Function to apply the selected theme via custom CSS (IMPORTANT: COPY THIS BLOCK TO ALL FILES IN THE 'PAGES' FOLDER)
|
| 76 |
-
def apply_theme():
|
| 77 |
-
selected_gender = st.session_state.user_profile.get("gender", "neutral") if "user_profile" in st.session_state else "neutral"
|
| 78 |
-
theme = THEMES.get(selected_gender, THEMES["neutral"])
|
| 79 |
-
|
| 80 |
-
# Define CSS variables for easier use
|
| 81 |
-
st.markdown(f"""
|
| 82 |
<style>
|
| 83 |
-
:root {{
|
| 84 |
-
--primary-background: {theme["primary_background"]};
|
| 85 |
-
--secondary-background: {theme["secondary_background"]};
|
| 86 |
-
--text-color: {theme["text_color"]};
|
| 87 |
-
--accent-color: {theme["accent_color"]};
|
| 88 |
-
}}
|
| 89 |
-
|
| 90 |
-
/* Apply theme colors using CSS variables */
|
| 91 |
.stApp {{
|
| 92 |
-
background-color:
|
| 93 |
-
color: var(--text-color); /* Fallback for general text */
|
| 94 |
-
}}
|
| 95 |
-
/* Target common Streamlit components for text color */
|
| 96 |
-
/* Increased specificity for text color to override Streamlit defaults */
|
| 97 |
-
div, p, span, li, ul, ol, strong, em,
|
| 98 |
-
.st-emotion-cache-nahz7x, /* General text in st.write, st.markdown */
|
| 99 |
-
.st-emotion-cache-1f8796m, /* Text inside info/warning/success boxes */
|
| 100 |
-
.st-emotion-cache-1m6a05l, /* Streamlit headers/titles */
|
| 101 |
-
.st-emotion-cache-jny0p5, /* Text input and text area labels */
|
| 102 |
-
.st-emotion-cache-vk3360, /* Radio/checkbox labels */
|
| 103 |
-
.st-emotion-cache-1k1q0z8 /* Selectbox labels */
|
| 104 |
-
{{
|
| 105 |
-
color: var(--text-color) !important; /* Use !important to ensure override */
|
| 106 |
}}
|
| 107 |
-
|
| 108 |
-
.st-emotion-cache-1jm6gvd {{ /* Targeted for the main content area background */
|
| 109 |
-
background-color: var(--secondary-background);
|
| 110 |
-
}}
|
| 111 |
-
/* Headings color */
|
| 112 |
-
h1, h2, h3, h4, h5, h6 {{
|
| 113 |
-
color: var(--text-color) !important; /* Use !important for headings */
|
| 114 |
-
}}
|
| 115 |
-
/* Button styling */
|
| 116 |
.stButton>button {{
|
| 117 |
-
background-color:
|
| 118 |
-
color: white
|
| 119 |
-
border-radius: 8px;
|
| 120 |
-
border: none;
|
| 121 |
-
padding: 10px 20px;
|
| 122 |
-
font-weight: bold;
|
| 123 |
-
box-shadow: 2px 2px 5px rgba(0,0,0,0.2);
|
| 124 |
-
}}
|
| 125 |
-
.stButton>button:hover {{
|
| 126 |
-
background-color: #FF7043; /* A slightly different accent on hover */
|
| 127 |
-
transform: translateY(-2px);
|
| 128 |
-
box-shadow: 3px 3px 6px rgba(0,0,0,0.3);
|
| 129 |
-
}}
|
| 130 |
-
/* Label colors for various input widgets */
|
| 131 |
-
.stRadio > label, .stSelectbox > label, .stSlider > label, .stTextInput > label, .stTextArea > label, .stFileUploader > label {{
|
| 132 |
-
color: var(--text-color) !important;
|
| 133 |
-
}}
|
| 134 |
-
/* Markdown text color - important for general text */
|
| 135 |
-
.stMarkdown {{
|
| 136 |
-
color: var(--text-color) !important;
|
| 137 |
-
}}
|
| 138 |
-
.stRadio div[role="radiogroup"] {{
|
| 139 |
-
background-color: var(--secondary-background);
|
| 140 |
-
padding: 10px;
|
| 141 |
-
border-radius: 8px;
|
| 142 |
-
}}
|
| 143 |
-
.stSelectbox div[data-baseweb="select"] {{
|
| 144 |
-
background-color: var(--secondary-background);
|
| 145 |
-
border-radius: 8px;
|
| 146 |
-
}}
|
| 147 |
-
|
| 148 |
-
/* Specific styles for info/warning/success/error boxes to ensure text visibility */
|
| 149 |
-
/* Targeting by class of the main alert container and the icon/text spans within */
|
| 150 |
-
.st-emotion-cache-1f8796m[data-baseweb="alert"] {{ /* general alert box container */
|
| 151 |
-
color: var(--text-color) !important; /* Text color inside alert boxes */
|
| 152 |
-
border-left-width: 5px;
|
| 153 |
-
border-left-style: solid;
|
| 154 |
-
border-radius: 8px;
|
| 155 |
-
padding: 1rem;
|
| 156 |
-
margin-bottom: 1rem;
|
| 157 |
-
}}
|
| 158 |
-
.st-emotion-cache-1f8796m[data-baseweb="alert"][kind="info"] {{
|
| 159 |
-
background-color: rgba(0, 123, 255, 0.1);
|
| 160 |
-
border-left-color: #007bff;
|
| 161 |
-
}}
|
| 162 |
-
.st-emotion-cache-1f8796m[data-baseweb="alert"][kind="success"] {{
|
| 163 |
-
background-color: rgba(40, 167, 69, 0.1);
|
| 164 |
-
border-left-color: #28a745;
|
| 165 |
-
}}
|
| 166 |
-
.st-emotion-cache-1f8796m[data-baseweb="alert"][kind="warning"] {{
|
| 167 |
-
background-color: rgba(255, 193, 7, 0.1);
|
| 168 |
-
border-left-color: #ffc107;
|
| 169 |
-
}}
|
| 170 |
-
.st-emotion-cache-1f8796m[data-baseweb="alert"][kind="error"] {{
|
| 171 |
-
background-color: rgba(220, 53, 69, 0.1);
|
| 172 |
-
border-left-color: #dc3545;
|
| 173 |
-
}}
|
| 174 |
-
/* For Streamlit's chat messages to ensure readability */
|
| 175 |
-
.stChatMessage {{
|
| 176 |
-
background-color: var(--secondary-background) !important; /* Chat bubble background */
|
| 177 |
-
color: var(--text-color) !important; /* Chat text color */
|
| 178 |
-
border-radius: 12px;
|
| 179 |
-
padding: 10px 15px;
|
| 180 |
-
margin-bottom: 8px;
|
| 181 |
-
}}
|
| 182 |
-
.stChatMessage.st-user {{ /* User's message */
|
| 183 |
-
background-color: rgba(var(--accent-color-rgb), 0.1) !important; /* Lighter accent for user */
|
| 184 |
-
color: var(--text-color) !important;
|
| 185 |
-
}}
|
| 186 |
-
.stChatMessage.st-assistant {{ /* Assistant's message */
|
| 187 |
-
background-color: rgba(var(--secondary-background-rgb), 0.8) !important; /* Secondary background for assistant */
|
| 188 |
-
color: var(--text-color) !important;
|
| 189 |
-
}}
|
| 190 |
-
|
| 191 |
-
/* Ensuring SVGs fill with current text color */
|
| 192 |
-
svg {{
|
| 193 |
-
fill: currentColor;
|
| 194 |
}}
|
| 195 |
</style>
|
| 196 |
-
""",
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
# --- Session State Initialization (Global and accessible by all pages) ---
|
| 200 |
-
# This block ensures that essential session state variables are initialized once.
|
| 201 |
-
if 'user_profile' not in st.session_state:
|
| 202 |
-
st.session_state.user_profile = {"name": "Friend", "age": None, "gender": "neutral", "profile_set": False}
|
| 203 |
-
if 'conversation' not in st.session_state:
|
| 204 |
-
st.session_state.conversation = []
|
| 205 |
-
if 'mood_data' not in st.session_state:
|
| 206 |
-
st.session_state.mood_data = []
|
| 207 |
-
if 'drawings' not in st.session_state: # For Drawing Canvas (list to store image data)
|
| 208 |
-
st.session_state.drawings = []
|
| 209 |
-
if 'canvas_key' not in st.session_state: # For Drawing Canvas (to force clear)
|
| 210 |
-
st.session_state.canvas_key = 0
|
| 211 |
-
|
| 212 |
-
# Game-specific session states (for games_page.py)
|
| 213 |
-
if "board" not in st.session_state: # For Tic-Tac-Toe
|
| 214 |
-
st.session_state.board = [" " for _ in range(9)]
|
| 215 |
-
st.session_state.player = "X"
|
| 216 |
-
st.session_state.game_over = False
|
| 217 |
-
st.session_state.message = "Your turn (X)!"
|
| 218 |
-
if "game_state" not in st.session_state: # For managing game resets (Tic-Tac-Toe, WYR)
|
| 219 |
-
st.session_state.game_state = None
|
| 220 |
-
if "current_wyr_question" not in st.session_state: # For Would You Rather?
|
| 221 |
-
st.session_state.current_wyr_question = None
|
| 222 |
-
if "wyr_choice_made" not in st.session_state: # For Would You Rather?
|
| 223 |
-
st.session_state.wyr_choice_made = False
|
| 224 |
-
if "current_adventure" not in st.session_state: # For Adventure Jar
|
| 225 |
-
st.session_state.current_adventure = None
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
# --- Apply Theme Globally ---
|
| 229 |
-
# This function is called at the very top of the script to ensure CSS is applied
|
| 230 |
-
# before any other content is rendered, providing a consistent theme.
|
| 231 |
-
apply_theme()
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
# --- Main Page Content ---
|
| 235 |
-
# This content runs only when homepage.py is the active page (i.e., when launched or 'Home' is clicked in sidebar)
|
| 236 |
-
st.title(f"👋 Welcome to HappyTrails!")
|
| 237 |
-
|
| 238 |
-
# --- User Profile Setup Section ---
|
| 239 |
-
if not st.session_state.user_profile["profile_set"]:
|
| 240 |
-
st.subheader("First, tell HappyTrails a little about yourself!")
|
| 241 |
-
with st.form("user_profile_form", clear_on_submit=False):
|
| 242 |
-
name = st.text_input("What's your name?", max_chars=20, key="profile_name_input")
|
| 243 |
-
age = st.number_input("How old are you?", min_value=1, max_value=120, step=1, key="profile_age_input")
|
| 244 |
-
gender_option = st.radio(
|
| 245 |
-
"Are you a boy or a girl, or would you prefer not to say?",
|
| 246 |
-
["Boy", "Girl", "Other / Prefer not to say"],
|
| 247 |
-
key="profile_gender_radio"
|
| 248 |
-
)
|
| 249 |
-
submit_profile = st.form_submit_button("Start My HappyTrails Adventure!")
|
| 250 |
-
|
| 251 |
-
if submit_profile:
|
| 252 |
-
if name.strip():
|
| 253 |
-
st.session_state.user_profile["name"] = name.strip()
|
| 254 |
-
st.session_state.user_profile["age"] = age
|
| 255 |
-
st.session_state.user_profile["gender"] = gender_option.lower().replace(" / prefer not to say", "") # Clean up string
|
| 256 |
-
st.session_state.user_profile["profile_set"] = True
|
| 257 |
-
st.success(f"Hello, {st.session_state.user_profile['name']}! Get ready for fun!")
|
| 258 |
-
st.rerun() # Rerun to apply theme and show personalized content
|
| 259 |
-
else:
|
| 260 |
-
st.warning("Please tell us your name!")
|
| 261 |
-
else:
|
| 262 |
-
# --- Personalized Welcome After Profile Setup ---
|
| 263 |
-
current_theme = THEMES.get(st.session_state.user_profile["gender"], THEMES["neutral"])
|
| 264 |
-
|
| 265 |
-
# Use f-string formatting to insert the user's name directly
|
| 266 |
-
welcome_message_formatted = current_theme['welcome_message'].format(name=st.session_state.user_profile['name'])
|
| 267 |
-
st.header(f"{welcome_message_formatted} 🤗")
|
| 268 |
-
|
| 269 |
-
st.markdown("""
|
| 270 |
-
#### Here’s how to get started and have fun with your digital friend:
|
| 271 |
-
|
| 272 |
-
HappyTrails is your friend for every feeling! By playing games, chatting, drawing, and exploring adventures, HappyTrails helps you:
|
| 273 |
-
- **Understand Emotions:** Track your feelings in the journal.
|
| 274 |
-
- **Boost Creativity:** Express yourself through drawing and imagination.
|
| 275 |
-
- **Develop Skills:** Play fun games that make you think and learn.
|
| 276 |
-
- **Feel Heard & Safe:** Always have a friend ready to listen.
|
| 277 |
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
+
from streamlit_drawable_canvas import st_canvas
|
| 3 |
+
from PIL import Image
|
| 4 |
+
import numpy as np
|
| 5 |
+
import io
|
| 6 |
+
import base64
|
| 7 |
from datetime import datetime
|
| 8 |
|
| 9 |
+
# ---------------------- Initialize Theme Colors (You can adjust this) ----------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
THEMES = {
|
| 11 |
"boy": {
|
| 12 |
+
"primary_background": "#D0E4FF",
|
| 13 |
+
"secondary_background": "#A7C7E7",
|
| 14 |
+
"button_color": "#4A90E2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
}
|
| 16 |
}
|
| 17 |
|
| 18 |
+
theme = THEMES.get("boy", THEMES["boy"])
|
| 19 |
+
st.markdown(
|
| 20 |
+
f"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
.stApp {{
|
| 23 |
+
background-color: {theme['primary_background']};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
.stButton>button {{
|
| 26 |
+
background-color: {theme['button_color']};
|
| 27 |
+
color: white;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}}
|
| 29 |
</style>
|
| 30 |
+
""",
|
| 31 |
+
unsafe_allow_html=True
|
| 32 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
+
# ---------------------- Initialize Session State ----------------------
|
| 35 |
+
if "saved_drawings" not in st.session_state:
|
| 36 |
+
st.session_state.saved_drawings = []
|
| 37 |
+
|
| 38 |
+
# ---------------------- Main Page Function ----------------------
|
| 39 |
+
def draw_page():
|
| 40 |
+
st.title("🎨 HappyTrails: Draw Your Mood!")
|
| 41 |
+
|
| 42 |
+
# Canvas settings with unique keys
|
| 43 |
+
stroke_width = st.sidebar.slider("Brush Size", 1, 25, 5, key="brush_size_slider")
|
| 44 |
+
stroke_color = st.sidebar.color_picker("Stroke Color", "#000000", key="stroke_color_picker")
|
| 45 |
+
bg_color = st.sidebar.color_picker("Canvas Background", "#FFFFFF", key="bg_color_picker")
|
| 46 |
+
drawing_mode = st.sidebar.selectbox("Drawing Tool", ("freedraw", "line", "rect", "circle", "transform"), key="drawing_tool_select")
|
| 47 |
+
realtime_update = st.sidebar.checkbox("Update in Real Time", True, key="realtime_update_checkbox")
|
| 48 |
+
|
| 49 |
+
# Load canvas with fallback
|
| 50 |
+
initial_canvas_data = {"version": "5.3.0", "objects": []}
|
| 51 |
+
|
| 52 |
+
canvas_result = st_canvas(
|
| 53 |
+
fill_color="rgba(0, 0, 0, 0)",
|
| 54 |
+
stroke_width=stroke_width,
|
| 55 |
+
stroke_color=stroke_color,
|
| 56 |
+
background_color=bg_color,
|
| 57 |
+
background_image=None,
|
| 58 |
+
update_streamlit=realtime_update,
|
| 59 |
+
height=400,
|
| 60 |
+
width=600,
|
| 61 |
+
drawing_mode=drawing_mode,
|
| 62 |
+
key="canvas",
|
| 63 |
+
initial_drawing=initial_canvas_data
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
# Save drawing if user clicks
|
| 67 |
+
if st.button("💾 Save Drawing"):
|
| 68 |
+
if canvas_result.image_data is not None and canvas_result.image_data.sum() > 0:
|
| 69 |
+
drawing_data = {
|
| 70 |
+
"image": canvas_result.image_data,
|
| 71 |
+
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
| 72 |
+
"bg_color": bg_color,
|
| 73 |
+
"stroke_color": stroke_color
|
| 74 |
+
}
|
| 75 |
+
st.session_state.saved_drawings.append(drawing_data)
|
| 76 |
+
st.success("Drawing saved!")
|
| 77 |
+
else:
|
| 78 |
+
st.warning("Canvas is empty. Draw something before saving.")
|
| 79 |
+
|
| 80 |
+
# Show saved drawings
|
| 81 |
+
if st.session_state.saved_drawings:
|
| 82 |
+
st.subheader("🖼️ Your Saved Drawings")
|
| 83 |
+
for i, saved in enumerate(st.session_state.saved_drawings):
|
| 84 |
+
st.markdown(f"*Drawing {i + 1} - {saved['timestamp']}*")
|
| 85 |
+
st.image(saved["image"])
|
| 86 |
+
|
| 87 |
+
# Clear saved drawings with confirmation
|
| 88 |
+
with st.expander("⚠️ Clear All Drawings", expanded=False):
|
| 89 |
+
confirm_clear = st.checkbox("Yes, delete all my drawings", key="clear_confirm_checkbox")
|
| 90 |
+
if confirm_clear and st.button("🗑️ Clear All My Saved Drawings"):
|
| 91 |
+
st.session_state.saved_drawings = []
|
| 92 |
+
st.success("All drawings deleted.")
|
| 93 |
+
|
| 94 |
+
# ---------------------- Run the page ----------------------
|
| 95 |
+
if _name_ == "_main_":
|
| 96 |
+
draw_page()
|