Spaces:
Sleeping
Sleeping
Update main content with beautiful trauma-informed introduction
Browse files- src/streamlit_app.py +270 -424
src/streamlit_app.py
CHANGED
@@ -6,319 +6,37 @@ import random
|
|
6 |
|
7 |
# Initialize page configuration - MUST BE FIRST ST COMMAND
|
8 |
st.set_page_config(
|
9 |
-
page_title="NurtureNest",
|
10 |
page_icon="🪴",
|
11 |
layout="wide",
|
12 |
initial_sidebar_state="expanded"
|
13 |
)
|
14 |
|
15 |
-
#
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
background-color: #F5F5F0; /* Soft parchment background */
|
24 |
-
font-family: 'Inter', 'Nunito', sans-serif;
|
25 |
-
}
|
26 |
-
|
27 |
-
/* Headers */
|
28 |
-
h1, h2, h3 {
|
29 |
-
font-family: 'Nunito', 'Inter', sans-serif;
|
30 |
-
color: #2E2E2E; /* Charcoal */
|
31 |
-
font-weight: 600;
|
32 |
-
margin-bottom: 1.5rem;
|
33 |
-
line-height: 1.6;
|
34 |
-
}
|
35 |
-
|
36 |
-
/* Base text styling */
|
37 |
-
p, li, label {
|
38 |
-
font-family: 'Inter', 'Nunito', sans-serif;
|
39 |
-
color: #2E2E2E;
|
40 |
-
line-height: 1.8;
|
41 |
-
font-size: 16px;
|
42 |
-
}
|
43 |
-
|
44 |
-
/* Text areas and inputs */
|
45 |
-
.stTextArea, .stTextInput {
|
46 |
-
background-color: #FFFFFF;
|
47 |
-
border: 1px solid #E0E0E0;
|
48 |
-
padding: 1.25rem;
|
49 |
-
border-radius: 1rem;
|
50 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
51 |
-
font-size: 16px;
|
52 |
-
line-height: 1.8;
|
53 |
-
}
|
54 |
-
|
55 |
-
/* Buttons */
|
56 |
-
.stButton > button {
|
57 |
-
font-family: 'Nunito', 'Inter', sans-serif;
|
58 |
-
font-weight: 600;
|
59 |
-
padding: 0.75rem 2rem;
|
60 |
-
border-radius: 1rem;
|
61 |
-
border: none;
|
62 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
63 |
-
transition: all 0.3s ease;
|
64 |
-
}
|
65 |
-
|
66 |
-
/* Tone-specific button colors */
|
67 |
-
.warm-tone button {
|
68 |
-
background-color: #FFDD99;
|
69 |
-
color: #2E2E2E;
|
70 |
-
}
|
71 |
-
|
72 |
-
.reflective-tone button {
|
73 |
-
background-color: #B8E0D2;
|
74 |
-
color: #2E2E2E;
|
75 |
-
}
|
76 |
-
|
77 |
-
.encouraging-tone button {
|
78 |
-
background-color: #C9D8F2;
|
79 |
-
color: #2E2E2E;
|
80 |
-
}
|
81 |
-
|
82 |
-
.direct-tone button {
|
83 |
-
background-color: #D3D3D3;
|
84 |
-
color: #2E2E2E;
|
85 |
-
}
|
86 |
-
|
87 |
-
.stButton > button:hover {
|
88 |
-
transform: translateY(-1px);
|
89 |
-
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
90 |
-
}
|
91 |
-
|
92 |
-
/* Chat containers */
|
93 |
-
.user-message {
|
94 |
-
background-color: #FFFFFF;
|
95 |
-
padding: 1.5rem;
|
96 |
-
border-radius: 1rem;
|
97 |
-
margin: 1rem 0;
|
98 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
99 |
-
}
|
100 |
-
|
101 |
-
.ai-message-warm {
|
102 |
-
background-color: #FFFAE6;
|
103 |
-
padding: 1.5rem;
|
104 |
-
border-radius: 1rem;
|
105 |
-
margin: 1rem 0;
|
106 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
107 |
-
}
|
108 |
-
|
109 |
-
.ai-message-reflective {
|
110 |
-
background-color: #F0F8F5;
|
111 |
-
padding: 1.5rem;
|
112 |
-
border-radius: 1rem;
|
113 |
-
margin: 1rem 0;
|
114 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
115 |
-
}
|
116 |
-
|
117 |
-
.ai-message-encouraging {
|
118 |
-
background-color: #F5F8FC;
|
119 |
-
padding: 1.5rem;
|
120 |
-
border-radius: 1rem;
|
121 |
-
margin: 1rem 0;
|
122 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
123 |
-
}
|
124 |
-
|
125 |
-
.ai-message-direct {
|
126 |
-
background-color: #F8F8F8;
|
127 |
-
padding: 1.5rem;
|
128 |
-
border-radius: 1rem;
|
129 |
-
margin: 1rem 0;
|
130 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
131 |
-
}
|
132 |
-
|
133 |
-
/* Message text */
|
134 |
-
.message-text {
|
135 |
-
font-size: 18px;
|
136 |
-
line-height: 1.8;
|
137 |
-
color: #2E2E2E;
|
138 |
-
}
|
139 |
-
|
140 |
-
.message-meta {
|
141 |
-
font-size: 14px;
|
142 |
-
color: #666666;
|
143 |
-
margin-bottom: 0.5rem;
|
144 |
-
}
|
145 |
-
|
146 |
-
/* Sidebar styling */
|
147 |
-
.css-1d391kg, .css-1p05t8e {
|
148 |
-
background-color: #FFFFFF !important;
|
149 |
-
padding: 2rem;
|
150 |
-
font-family: 'Inter', 'Nunito', sans-serif;
|
151 |
-
}
|
152 |
-
|
153 |
-
.sidebar-text {
|
154 |
-
color: #2E2E2E;
|
155 |
-
line-height: 1.8;
|
156 |
-
font-size: 16px;
|
157 |
-
}
|
158 |
-
|
159 |
-
/* Alert styling */
|
160 |
-
.alert {
|
161 |
-
background-color: #FFF8E1;
|
162 |
-
border-left: 4px solid #FFDD99;
|
163 |
-
padding: 1.5rem;
|
164 |
-
border-radius: 1rem;
|
165 |
-
margin: 1.5rem 0;
|
166 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
167 |
-
}
|
168 |
-
|
169 |
-
/* Expander styling */
|
170 |
-
.streamlit-expanderHeader {
|
171 |
-
background-color: #FFFFFF;
|
172 |
-
border-radius: 1rem;
|
173 |
-
padding: 1rem;
|
174 |
-
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
175 |
-
}
|
176 |
-
|
177 |
-
/* Psychoeducation snippets */
|
178 |
-
.psychoed-text {
|
179 |
-
font-style: italic;
|
180 |
-
color: #4A4A4A;
|
181 |
-
padding: 1rem;
|
182 |
-
background-color: #FAFAFA;
|
183 |
-
border-radius: 0.5rem;
|
184 |
-
margin: 1rem 0;
|
185 |
-
}
|
186 |
-
</style>
|
187 |
-
""", unsafe_allow_html=True)
|
188 |
-
|
189 |
-
# Enhanced tone characteristics with trauma-informed approach
|
190 |
-
TONE_STYLES = {
|
191 |
-
"Warm ☀️": {
|
192 |
-
"icon": "✨",
|
193 |
-
"prefix": "Noticing...",
|
194 |
-
"description": "Soft, nurturing, emotionally validating. Like being gently held in a moment of overwhelm.",
|
195 |
-
"system_message": """You are a trauma-informed, parenting-focused reflection companion.
|
196 |
-
Your role is to witness and validate, never to advise or correct.
|
197 |
-
|
198 |
-
Respond in a warm, nurturing tone that:
|
199 |
-
- Honors that healing and parenting happen simultaneously
|
200 |
-
- Validates the complexity of emotional experiences
|
201 |
-
- Recognizes protective responses as survival wisdom
|
202 |
-
- Centers regulation and co-regulation
|
203 |
-
- Trusts in the parent's innate capacity to stay in relationship
|
204 |
-
|
205 |
-
Never:
|
206 |
-
- Offer parenting advice or suggestions
|
207 |
-
- Imply there is a "right" way to parent
|
208 |
-
- Focus on behavior management
|
209 |
-
- Pathologize normal responses to stress
|
210 |
-
- Suggest perfectionism as a goal
|
211 |
-
|
212 |
-
End every reflection with:
|
213 |
-
"This might be something to reflect on with your therapist."
|
214 |
-
"""
|
215 |
},
|
216 |
-
"
|
217 |
-
"
|
218 |
-
"
|
219 |
-
"description": "
|
220 |
-
"
|
221 |
-
|
222 |
-
|
223 |
-
Respond in a reflective, contemplative tone that:
|
224 |
-
- Honors that healing and parenting happen simultaneously
|
225 |
-
- Makes space for all emotions without judgment
|
226 |
-
- Recognizes protective responses as survival wisdom
|
227 |
-
- Centers regulation and co-regulation
|
228 |
-
- Trusts in the parent's innate capacity to stay in relationship
|
229 |
-
|
230 |
-
Never:
|
231 |
-
- Offer parenting advice or suggestions
|
232 |
-
- Imply there is a "right" way to parent
|
233 |
-
- Focus on behavior management
|
234 |
-
- Pathologize normal responses to stress
|
235 |
-
- Suggest perfectionism as a goal
|
236 |
-
|
237 |
-
End every reflection with:
|
238 |
-
"This might be something to reflect on with your therapist."
|
239 |
-
"""
|
240 |
-
},
|
241 |
-
"Encouraging 🌈": {
|
242 |
-
"icon": "🌟",
|
243 |
-
"prefix": "Here's what you're already doing well...",
|
244 |
-
"description": "Affirming, uplifting, belief in the caregiver's capacity. Gentle spark of hope without bypassing pain.",
|
245 |
-
"system_message": """You are a trauma-informed, parenting-focused reflection companion.
|
246 |
-
Your role is to affirm capacity and effort, never to advise or correct.
|
247 |
-
|
248 |
-
Respond in an encouraging, affirming tone that:
|
249 |
-
- Honors that healing and parenting happen simultaneously
|
250 |
-
- Validates both struggle and strength
|
251 |
-
- Recognizes protective responses as survival wisdom
|
252 |
-
- Centers regulation and co-regulation
|
253 |
-
- Trusts in the parent's innate capacity to stay in relationship
|
254 |
-
|
255 |
-
Never:
|
256 |
-
- Offer parenting advice or suggestions
|
257 |
-
- Imply there is a "right" way to parent
|
258 |
-
- Focus on behavior management
|
259 |
-
- Pathologize normal responses to stress
|
260 |
-
- Suggest perfectionism as a goal
|
261 |
-
|
262 |
-
End every reflection with:
|
263 |
-
"This might be something to reflect on with your therapist."
|
264 |
-
"""
|
265 |
},
|
266 |
-
"
|
267 |
-
"
|
268 |
-
"
|
269 |
-
"description": "
|
270 |
-
"
|
271 |
-
|
272 |
-
|
273 |
-
Respond in a direct, clear tone that:
|
274 |
-
- Honors that healing and parenting happen simultaneously
|
275 |
-
- Names patterns with care and context
|
276 |
-
- Recognizes protective responses as survival wisdom
|
277 |
-
- Centers regulation and co-regulation
|
278 |
-
- Trusts in the parent's innate capacity to stay in relationship
|
279 |
-
|
280 |
-
Never:
|
281 |
-
- Offer parenting advice or suggestions
|
282 |
-
- Imply there is a "right" way to parent
|
283 |
-
- Focus on behavior management
|
284 |
-
- Pathologize normal responses to stress
|
285 |
-
- Suggest perfectionism as a goal
|
286 |
-
|
287 |
-
End every reflection with:
|
288 |
-
"This might be something to reflect on with your therapist."
|
289 |
-
"""
|
290 |
}
|
291 |
}
|
292 |
|
293 |
-
# Enhanced daily prompts
|
294 |
-
DAILY_PROMPTS = [
|
295 |
-
"What moment from today is sitting heavy or tender in your chest?",
|
296 |
-
"Was there a moment today you wish you could redo?",
|
297 |
-
"When did you feel most connected to your child today?",
|
298 |
-
"What helped you stay grounded today?",
|
299 |
-
"What did your child need today that was hard to give?",
|
300 |
-
"Where do you notice yourself needing support or understanding?",
|
301 |
-
"What parenting moment today would you like to hold gently?"
|
302 |
-
]
|
303 |
-
|
304 |
-
# Psychoeducational insights
|
305 |
-
PSYCHOED_INSIGHTS = [
|
306 |
-
"Moments of rupture are part of secure attachment when repair is present.",
|
307 |
-
"Exhaustion shrinks your window of tolerance—that's not a failure, it's a signal.",
|
308 |
-
"Your child's big feelings are not a reflection of your parenting—they're an invitation to co-regulate.",
|
309 |
-
"Protective responses come from survival wisdom, even when they don't match the present moment.",
|
310 |
-
"The path to secure attachment includes repairs, not perfection."
|
311 |
-
]
|
312 |
-
|
313 |
-
# Compassionate reframes
|
314 |
-
REFRAMES = [
|
315 |
-
"Trying again is powerful modeling.",
|
316 |
-
"The guilt you feel signals how much you care—it's not a verdict.",
|
317 |
-
"Your awareness is already creating change, even before behavior shifts.",
|
318 |
-
"Meeting yourself with compassion creates space to meet your child differently.",
|
319 |
-
"Every repair strengthens trust, even if the pattern isn't perfect yet."
|
320 |
-
]
|
321 |
-
|
322 |
# Handle API key setup
|
323 |
try:
|
324 |
api_key = "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"
|
@@ -328,121 +46,166 @@ except Exception as e:
|
|
328 |
st.stop()
|
329 |
|
330 |
# Initialize session state
|
|
|
|
|
331 |
if 'journal_entries' not in st.session_state:
|
332 |
st.session_state.journal_entries = []
|
333 |
-
if 'current_entry' not in st.session_state:
|
334 |
-
st.session_state.current_entry = ""
|
335 |
-
if 'reflections' not in st.session_state:
|
336 |
-
st.session_state.reflections = []
|
337 |
-
if 'selected_tone' not in st.session_state:
|
338 |
-
st.session_state.selected_tone = "Warm ☀️"
|
339 |
-
if 'mood' not in st.session_state:
|
340 |
-
st.session_state.mood = None
|
341 |
-
if 'reflection_type' not in st.session_state:
|
342 |
-
st.session_state.reflection_type = "Daily Check-in"
|
343 |
|
344 |
# Sidebar content
|
345 |
st.sidebar.markdown("""
|
346 |
-
# Welcome to NurtureNest 🪴
|
347 |
|
348 |
-
A gentle space for parenting
|
349 |
|
350 |
-
|
351 |
-
-
|
352 |
-
-
|
353 |
-
-
|
354 |
-
-
|
355 |
|
356 |
-
*Created with deep respect for the
|
357 |
|
358 |
---
|
359 |
Created by [Jocelyn Skillman LMHC](http://www.jocelynskillman.com)
|
360 |
[@jocelynskillmanlmhc](https://jocelynskillmanlmhc.substack.com/)
|
361 |
""")
|
362 |
|
363 |
-
#
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
372 |
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
|
|
|
|
|
|
|
|
377 |
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
|
|
|
|
|
|
|
|
382 |
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
|
|
|
|
|
|
387 |
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
|
|
|
|
|
|
392 |
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
|
|
|
|
|
|
402 |
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
|
|
|
|
|
|
|
|
407 |
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
|
|
412 |
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
st.markdown("""
|
421 |
-
# NurtureNest
|
422 |
-
|
423 |
-
This is not a "parenting fix-it" tool. It is a pause. A breath. A quiet turning inward.
|
424 |
-
|
425 |
-
This space holds what feels messy, tangled, or painful—and trusts in your capacity to transform it. Here, guilt becomes a sign of care. Rupture becomes an opening for reconnection. Your presence, not your perfection, is what heals.
|
426 |
-
|
427 |
-
---
|
428 |
-
""")
|
429 |
|
430 |
-
#
|
431 |
-
st.markdown("### Choose your
|
432 |
-
|
433 |
-
"",
|
434 |
-
options=list(
|
435 |
-
format_func=lambda x: x,
|
436 |
-
help="Select the
|
|
|
437 |
)
|
438 |
|
439 |
-
# Display
|
440 |
-
st.markdown(f"
|
|
|
|
|
|
|
|
|
|
|
441 |
|
442 |
# Crisis support notice
|
443 |
st.markdown("""
|
444 |
<div class="alert">
|
445 |
-
<strong>⚠️ Important:</strong> This
|
446 |
</div>
|
447 |
""", unsafe_allow_html=True)
|
448 |
|
@@ -451,33 +214,52 @@ st.markdown("### Share what's on your heart...")
|
|
451 |
col1, col2 = st.columns([4, 1])
|
452 |
with col1:
|
453 |
journal_entry = st.text_area(
|
454 |
-
"",
|
455 |
height=100,
|
456 |
key="journal_input",
|
457 |
-
help="Share your
|
458 |
-
placeholder="
|
|
|
459 |
)
|
460 |
with col2:
|
461 |
-
submit = st.button("
|
462 |
-
|
463 |
-
# Initialize chat history in session state if it doesn't exist
|
464 |
-
if 'chat_history' not in st.session_state:
|
465 |
-
st.session_state.chat_history = []
|
466 |
|
467 |
-
if submit and journal_entry:
|
468 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
469 |
message = c.messages.create(
|
470 |
model="claude-3-opus-20240229",
|
471 |
max_tokens=1000,
|
472 |
-
system=
|
473 |
messages=[{"role": "user", "content": journal_entry}]
|
474 |
)
|
475 |
|
476 |
# Add the new exchange to chat history
|
477 |
st.session_state.chat_history.append({
|
478 |
"user_message": journal_entry,
|
479 |
-
"ai_response":
|
480 |
-
"
|
481 |
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M")
|
482 |
})
|
483 |
|
@@ -488,26 +270,27 @@ if submit and journal_entry: # Only process if there's text and button is click
|
|
488 |
st.error(f"Error getting reflection: {str(e)}")
|
489 |
|
490 |
# Display chat history in reverse chronological order
|
491 |
-
st.
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
<div class="message
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
<div
|
506 |
-
<
|
|
|
|
|
|
|
507 |
</div>
|
508 |
-
|
509 |
-
</div>
|
510 |
-
""", unsafe_allow_html=True)
|
511 |
|
512 |
# Save conversation button at the bottom
|
513 |
if st.session_state.chat_history:
|
@@ -520,18 +303,81 @@ if st.session_state.journal_entries:
|
|
520 |
with st.expander("📔 Your Saved Reflections", expanded=False):
|
521 |
for entry in reversed(st.session_state.journal_entries):
|
522 |
timestamp = entry.get("timestamp", "")
|
523 |
-
|
524 |
content = entry.get("user_message", entry.get("content", ""))
|
525 |
reflection = entry.get("ai_response", entry.get("reflection", ""))
|
526 |
|
527 |
st.markdown(f"""
|
528 |
-
**{timestamp}** - *{
|
529 |
|
530 |
-
*Your
|
531 |
{content}
|
532 |
|
533 |
-
*
|
534 |
{reflection}
|
535 |
|
536 |
---
|
537 |
-
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
7 |
# Initialize page configuration - MUST BE FIRST ST COMMAND
|
8 |
st.set_page_config(
|
9 |
+
page_title="NurtureNest: Inner Child Mirror",
|
10 |
page_icon="🪴",
|
11 |
layout="wide",
|
12 |
initial_sidebar_state="expanded"
|
13 |
)
|
14 |
|
15 |
+
# Age context dictionary
|
16 |
+
AGE_CONTEXT = {
|
17 |
+
"3-5": {
|
18 |
+
"voice": "Very simple, sensory-focused reassurance",
|
19 |
+
"example": "Your body was so tight. That's okay. You're safe now.",
|
20 |
+
"description": "Simple words and gentle comfort for your littlest self",
|
21 |
+
"color": "#FFF5E6",
|
22 |
+
"border": "#FFB366"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
},
|
24 |
+
"6-8": {
|
25 |
+
"voice": "Gentle relational reflection",
|
26 |
+
"example": "Sometimes big feelings come when we feel alone. I hear how hard that was.",
|
27 |
+
"description": "Warm understanding for your grade school self",
|
28 |
+
"color": "#F0F7F4",
|
29 |
+
"border": "#88C6B6"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
},
|
31 |
+
"9-12": {
|
32 |
+
"voice": "Emotionally attuned validation",
|
33 |
+
"example": "It makes sense that you felt that way. Your feelings matter.",
|
34 |
+
"description": "Respectful reflection for your older child self",
|
35 |
+
"color": "#F5F8FC",
|
36 |
+
"border": "#9FB7E3"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
38 |
}
|
39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
# Handle API key setup
|
41 |
try:
|
42 |
api_key = "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"
|
|
|
46 |
st.stop()
|
47 |
|
48 |
# Initialize session state
|
49 |
+
if 'chat_history' not in st.session_state:
|
50 |
+
st.session_state.chat_history = []
|
51 |
if 'journal_entries' not in st.session_state:
|
52 |
st.session_state.journal_entries = []
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
# Sidebar content
|
55 |
st.sidebar.markdown("""
|
56 |
+
# Welcome to NurtureNest: Inner Child Mirror 🪴
|
57 |
|
58 |
+
A gentle space for re-parenting through connection with your inner child.
|
59 |
|
60 |
+
Here you can:
|
61 |
+
- Connect with your younger self
|
62 |
+
- Process parenting moments with deep understanding
|
63 |
+
- Experience the healing power of being truly seen
|
64 |
+
- Learn to parent from a place of wholeness
|
65 |
|
66 |
+
*Created with deep respect for the healing journey of parents.*
|
67 |
|
68 |
---
|
69 |
Created by [Jocelyn Skillman LMHC](http://www.jocelynskillman.com)
|
70 |
[@jocelynskillmanlmhc](https://jocelynskillmanlmhc.substack.com/)
|
71 |
""")
|
72 |
|
73 |
+
# Main content area
|
74 |
+
st.markdown("""
|
75 |
+
# Welcome to the Inner Child Mirror
|
76 |
+
## A gentle place to be held, not fixed.
|
77 |
+
|
78 |
+
Parenting isn't just about raising our children. It's also about meeting the younger parts of ourselves—the ones who didn't always get the co-regulation, safety, or softness they needed. These parts don't disappear when we grow up. They often rise up when we're stressed, overwhelmed, or when our child's behavior echoes an old wound.
|
79 |
+
|
80 |
+
The Inner Child Mirror is a simple but powerful tool in your parenting toolkit.
|
81 |
+
|
82 |
+
### Here's how it works:
|
83 |
+
|
84 |
+
1. You write a brief note about a hard moment from today
|
85 |
+
2. You choose an age (3–12) that feels connected to how you were feeling
|
86 |
+
3. You receive a warm, developmentally attuned reflection—as if spoken to that younger you
|
87 |
+
|
88 |
+
This practice is grounded in trauma-informed care, attachment science, and the work of experts like Dr. Dan Siegel, Sarah Peyton, and Dr. Becky Kennedy. It helps you:
|
89 |
+
|
90 |
+
- Build compassion for yourself—especially after rupture moments
|
91 |
+
- Expand your capacity for repair (with your child and yourself)
|
92 |
+
- Notice and tend to your own nervous system with care
|
93 |
+
- Interrupt inherited patterns with love and attunement
|
94 |
+
|
95 |
+
You're not just "using an app"—you're doing intergenerational work in the most tender, doable way.
|
96 |
+
Not by being perfect. But by practicing presence—even with the parts of you that were once overwhelmed, scared, or unseen.
|
97 |
+
|
98 |
+
<div class="special-note">
|
99 |
+
<p><strong>This is not therapy. It's not performance. It's a ritual of repair.</strong><br>
|
100 |
+
You're not alone in this.</p>
|
101 |
+
</div>
|
102 |
+
|
103 |
+
When you're ready, choose an age. Let the mirror offer you the kind of reflection every child deserves.
|
104 |
+
|
105 |
+
---
|
106 |
+
""", unsafe_allow_html=True)
|
107 |
+
|
108 |
+
# Update CSS to include special styling for the introduction
|
109 |
+
st.markdown("""
|
110 |
+
<style>
|
111 |
+
/* Enhanced typography for introduction */
|
112 |
+
.stApp {
|
113 |
+
background-color: #F5F5F0;
|
114 |
+
font-family: 'Inter', 'Nunito', sans-serif;
|
115 |
+
}
|
116 |
|
117 |
+
h1 {
|
118 |
+
color: #2E2E2E;
|
119 |
+
font-family: 'Nunito', sans-serif;
|
120 |
+
font-size: 2.5rem;
|
121 |
+
font-weight: 700;
|
122 |
+
margin-bottom: 0.5rem;
|
123 |
+
line-height: 1.2;
|
124 |
+
}
|
125 |
|
126 |
+
h2 {
|
127 |
+
color: #666666;
|
128 |
+
font-family: 'Inter', sans-serif;
|
129 |
+
font-size: 1.5rem;
|
130 |
+
font-weight: 400;
|
131 |
+
margin-bottom: 2rem;
|
132 |
+
font-style: italic;
|
133 |
+
}
|
134 |
|
135 |
+
h3 {
|
136 |
+
color: #2E2E2E;
|
137 |
+
font-family: 'Nunito', sans-serif;
|
138 |
+
font-size: 1.75rem;
|
139 |
+
font-weight: 600;
|
140 |
+
margin: 2rem 0 1rem 0;
|
141 |
+
}
|
142 |
|
143 |
+
p {
|
144 |
+
color: #2E2E2E;
|
145 |
+
font-family: 'Inter', sans-serif;
|
146 |
+
font-size: 1.1rem;
|
147 |
+
line-height: 1.8;
|
148 |
+
margin: 1rem 0;
|
149 |
+
}
|
150 |
|
151 |
+
ul {
|
152 |
+
margin: 1.5rem 0;
|
153 |
+
padding-left: 1.5rem;
|
154 |
+
}
|
155 |
|
156 |
+
li {
|
157 |
+
color: #2E2E2E;
|
158 |
+
font-family: 'Inter', sans-serif;
|
159 |
+
font-size: 1.1rem;
|
160 |
+
line-height: 1.8;
|
161 |
+
margin: 0.5rem 0;
|
162 |
+
}
|
163 |
|
164 |
+
.special-note {
|
165 |
+
background-color: #FFF5E6;
|
166 |
+
border-left: 4px solid #FFB366;
|
167 |
+
padding: 1.5rem;
|
168 |
+
border-radius: 1rem;
|
169 |
+
margin: 2rem 0;
|
170 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
171 |
+
}
|
172 |
|
173 |
+
.special-note p {
|
174 |
+
margin: 0;
|
175 |
+
font-size: 1.2rem;
|
176 |
+
color: #2E2E2E;
|
177 |
+
}
|
178 |
|
179 |
+
hr {
|
180 |
+
margin: 2rem 0;
|
181 |
+
border: none;
|
182 |
+
border-top: 1px solid #E0E0E0;
|
183 |
+
}
|
184 |
+
</style>
|
185 |
+
""", unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
|
187 |
+
# Age selection
|
188 |
+
st.markdown("### Choose the age of your inner child")
|
189 |
+
selected_age = st.selectbox(
|
190 |
+
"Age Selection",
|
191 |
+
options=list(AGE_CONTEXT.keys()),
|
192 |
+
format_func=lambda x: f"Age {x}: {AGE_CONTEXT[x]['description']}",
|
193 |
+
help="Select the age of your inner child that needs to be heard today",
|
194 |
+
label_visibility="collapsed"
|
195 |
)
|
196 |
|
197 |
+
# Display age context description
|
198 |
+
st.markdown(f"""
|
199 |
+
<div style="padding: 1rem; background-color: {AGE_CONTEXT[selected_age]['color']}; border-left: 4px solid {AGE_CONTEXT[selected_age]['border']}; border-radius: 0.5rem; margin: 1rem 0;">
|
200 |
+
<p style="margin: 0; font-style: italic;">{AGE_CONTEXT[selected_age]['voice']}</p>
|
201 |
+
<p style="margin: 0.5rem 0 0 0; font-size: 0.9em; color: #666666;">Example: {AGE_CONTEXT[selected_age]['example']}</p>
|
202 |
+
</div>
|
203 |
+
""", unsafe_allow_html=True)
|
204 |
|
205 |
# Crisis support notice
|
206 |
st.markdown("""
|
207 |
<div class="alert">
|
208 |
+
<strong>⚠️ Important:</strong> This is not therapy. If you need support, please reach out to a mental health professional or click <a href="https://www.samhsa.gov/find-help/national-helpline" target="_blank">here</a> for resources.
|
209 |
</div>
|
210 |
""", unsafe_allow_html=True)
|
211 |
|
|
|
214 |
col1, col2 = st.columns([4, 1])
|
215 |
with col1:
|
216 |
journal_entry = st.text_area(
|
217 |
+
"Your sharing",
|
218 |
height=100,
|
219 |
key="journal_input",
|
220 |
+
help="Share your parenting moment. Your inner child will be heard with care.",
|
221 |
+
placeholder="Tell me about a moment that feels hard...",
|
222 |
+
label_visibility="collapsed"
|
223 |
)
|
224 |
with col2:
|
225 |
+
submit = st.button("Share 💝", use_container_width=True)
|
|
|
|
|
|
|
|
|
226 |
|
227 |
+
if submit and journal_entry:
|
228 |
try:
|
229 |
+
# Create age-appropriate system message
|
230 |
+
system_message = f"""You are a trauma-informed Inner Child Mirror for parents.
|
231 |
+
You speak directly to their {selected_age}-year-old self with deep emotional attunement.
|
232 |
+
|
233 |
+
Your role is to provide emotional safety and validation, never advice or correction.
|
234 |
+
Respond as if speaking to a {selected_age}-year-old, using:
|
235 |
+
- Age-appropriate language and concepts
|
236 |
+
- Simple, kind sentences (5-7 lines maximum)
|
237 |
+
- Focus on emotional and bodily experience
|
238 |
+
- Unconditional positive regard
|
239 |
+
- No fixing, explaining, or instructing
|
240 |
+
|
241 |
+
For age {selected_age}:
|
242 |
+
{AGE_CONTEXT[selected_age]['voice']}
|
243 |
+
|
244 |
+
Always end with a gentle reminder of worth, such as:
|
245 |
+
"You are still good."
|
246 |
+
"You didn't do anything wrong by feeling that way."
|
247 |
+
"You are still loved. You can try again."
|
248 |
+
"You're not alone."
|
249 |
+
"""
|
250 |
+
|
251 |
message = c.messages.create(
|
252 |
model="claude-3-opus-20240229",
|
253 |
max_tokens=1000,
|
254 |
+
system=system_message,
|
255 |
messages=[{"role": "user", "content": journal_entry}]
|
256 |
)
|
257 |
|
258 |
# Add the new exchange to chat history
|
259 |
st.session_state.chat_history.append({
|
260 |
"user_message": journal_entry,
|
261 |
+
"ai_response": message.content[0].text,
|
262 |
+
"age": selected_age,
|
263 |
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M")
|
264 |
})
|
265 |
|
|
|
270 |
st.error(f"Error getting reflection: {str(e)}")
|
271 |
|
272 |
# Display chat history in reverse chronological order
|
273 |
+
if st.session_state.chat_history:
|
274 |
+
st.markdown("### Your Mirror")
|
275 |
+
for exchange in reversed(st.session_state.chat_history):
|
276 |
+
# User message
|
277 |
+
st.markdown(f"""
|
278 |
+
<div class="user-message">
|
279 |
+
<div class="message-meta"><em>Your sharing</em></div>
|
280 |
+
<div class="message-text">{exchange["user_message"]}</div>
|
281 |
+
</div>
|
282 |
+
""", unsafe_allow_html=True)
|
283 |
+
|
284 |
+
# Mirror response with age-specific styling
|
285 |
+
age_group = exchange["age"]
|
286 |
+
st.markdown(f"""
|
287 |
+
<div style="background-color: {AGE_CONTEXT[age_group]['color']}; padding: 1.5rem; border-radius: 1rem; margin: 1rem 0; border-left: 4px solid {AGE_CONTEXT[age_group]['border']}; box-shadow: 0 2px 8px rgba(0,0,0,0.05);">
|
288 |
+
<div class="message-meta">
|
289 |
+
<em>Your {age_group}-year-old self is heard</em>
|
290 |
+
</div>
|
291 |
+
<div class="message-text">{exchange["ai_response"]}</div>
|
292 |
</div>
|
293 |
+
""", unsafe_allow_html=True)
|
|
|
|
|
294 |
|
295 |
# Save conversation button at the bottom
|
296 |
if st.session_state.chat_history:
|
|
|
303 |
with st.expander("📔 Your Saved Reflections", expanded=False):
|
304 |
for entry in reversed(st.session_state.journal_entries):
|
305 |
timestamp = entry.get("timestamp", "")
|
306 |
+
age = entry.get("age", "")
|
307 |
content = entry.get("user_message", entry.get("content", ""))
|
308 |
reflection = entry.get("ai_response", entry.get("reflection", ""))
|
309 |
|
310 |
st.markdown(f"""
|
311 |
+
**{timestamp}** - *Age {age}*
|
312 |
|
313 |
+
*Your sharing:*
|
314 |
{content}
|
315 |
|
316 |
+
*Your inner child was heard:*
|
317 |
{reflection}
|
318 |
|
319 |
---
|
320 |
+
""")
|
321 |
+
|
322 |
+
# Update CSS for styling
|
323 |
+
st.markdown("""
|
324 |
+
<style>
|
325 |
+
/* Base styling */
|
326 |
+
.stApp {
|
327 |
+
background-color: #F5F5F0;
|
328 |
+
font-family: 'Inter', 'Nunito', sans-serif;
|
329 |
+
}
|
330 |
+
|
331 |
+
/* User message styling */
|
332 |
+
.user-message {
|
333 |
+
background-color: #FFFFFF;
|
334 |
+
padding: 1.5rem;
|
335 |
+
border-radius: 1rem;
|
336 |
+
margin: 1rem 0;
|
337 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
338 |
+
}
|
339 |
+
|
340 |
+
/* Text styling */
|
341 |
+
.message-text {
|
342 |
+
font-size: 18px;
|
343 |
+
line-height: 1.8;
|
344 |
+
color: #2E2E2E;
|
345 |
+
font-family: 'Nunito', 'Inter', sans-serif;
|
346 |
+
}
|
347 |
+
|
348 |
+
.message-meta {
|
349 |
+
font-size: 14px;
|
350 |
+
color: #666666;
|
351 |
+
margin-bottom: 0.75rem;
|
352 |
+
font-family: 'Inter', sans-serif;
|
353 |
+
}
|
354 |
+
|
355 |
+
/* Button styling */
|
356 |
+
.stButton > button {
|
357 |
+
background-color: #88C6B6;
|
358 |
+
color: white;
|
359 |
+
border: none;
|
360 |
+
padding: 0.75rem 1.5rem;
|
361 |
+
border-radius: 1rem;
|
362 |
+
font-family: 'Nunito', 'Inter', sans-serif;
|
363 |
+
font-weight: 600;
|
364 |
+
transition: all 0.3s ease;
|
365 |
+
}
|
366 |
+
|
367 |
+
.stButton > button:hover {
|
368 |
+
background-color: #7AB3A3;
|
369 |
+
transform: translateY(-1px);
|
370 |
+
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
371 |
+
}
|
372 |
+
|
373 |
+
/* Alert styling */
|
374 |
+
.alert {
|
375 |
+
background-color: #FFF8E1;
|
376 |
+
border-left: 4px solid #FFB366;
|
377 |
+
padding: 1.5rem;
|
378 |
+
border-radius: 1rem;
|
379 |
+
margin: 1.5rem 0;
|
380 |
+
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
381 |
+
}
|
382 |
+
</style>
|
383 |
+
""", unsafe_allow_html=True)
|