jostlebot commited on
Commit
8ffac10
·
1 Parent(s): 79e86b2

Add custom artwork icons for all spaces

Browse files
app.py CHANGED
@@ -1,9 +1,23 @@
1
  """Practice Fields Hub — Curated Therapeutic Practice Spaces"""
2
 
3
  import streamlit as st
 
 
4
 
5
  st.set_page_config(page_title="Practice Fields", page_icon="🌱", layout="wide")
6
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  st.markdown("""
8
  <style>
9
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
@@ -13,7 +27,6 @@ st.markdown("""
13
  font-family: 'Inter', sans-serif;
14
  }
15
 
16
- /* Hide Streamlit branding */
17
  #MainMenu {visibility: hidden;}
18
  footer {visibility: hidden;}
19
 
@@ -42,7 +55,6 @@ st.markdown("""
42
  font-size: 1.15rem;
43
  font-weight: 300;
44
  font-style: italic;
45
- letter-spacing: 0.01em;
46
  }
47
 
48
  .clinician-badge {
@@ -72,7 +84,7 @@ st.markdown("""
72
  .field-card {
73
  background: linear-gradient(145deg, rgba(255,255,255,0.95) 0%, rgba(252,255,253,0.95) 100%);
74
  border-radius: 20px;
75
- padding: 1.75rem;
76
  margin: 0.6rem 0;
77
  border: 1px solid rgba(180, 210, 195, 0.25);
78
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
@@ -86,26 +98,26 @@ st.markdown("""
86
  transform: translateY(-4px);
87
  box-shadow: 0 12px 32px rgba(100, 160, 140, 0.15);
88
  border-color: rgba(140, 190, 170, 0.4);
89
- background: linear-gradient(145deg, rgba(255,255,255,1) 0%, rgba(248,255,250,1) 100%);
90
  }
91
 
92
  .field-icon {
93
- font-size: 2.5rem;
 
 
94
  margin-bottom: 0.75rem;
95
- filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1));
96
  }
97
 
98
  .field-title {
99
  color: #3a5555;
100
- font-size: 1.25rem;
101
  font-weight: 500;
102
- margin-bottom: 0.5rem;
103
- letter-spacing: -0.01em;
104
  }
105
 
106
  .field-desc {
107
  color: #6a8585;
108
- font-size: 0.95rem;
109
  line-height: 1.5;
110
  font-weight: 300;
111
  }
@@ -143,7 +155,6 @@ st.markdown("""
143
  background: rgba(255,255,255,0.6);
144
  padding: 0.75rem 1.5rem;
145
  border-radius: 30px;
146
- margin-top: 0.5rem;
147
  }
148
 
149
  .crisis-item {
@@ -152,16 +163,6 @@ st.markdown("""
152
  font-weight: 500;
153
  }
154
 
155
- .onboarding-container {
156
- max-width: 500px;
157
- margin: 2rem auto;
158
- padding: 2.5rem;
159
- background: linear-gradient(145deg, rgba(255,255,255,0.95) 0%, rgba(250,255,252,0.95) 100%);
160
- border-radius: 24px;
161
- border: 1px solid rgba(180, 210, 195, 0.25);
162
- box-shadow: 0 8px 32px rgba(100, 160, 140, 0.1);
163
- }
164
-
165
  .welcome-text {
166
  color: #4a6a6a;
167
  font-size: 1.1rem;
@@ -176,21 +177,12 @@ st.markdown("""
176
  border: 1px solid rgba(180, 210, 195, 0.4) !important;
177
  border-radius: 12px !important;
178
  padding: 0.75rem 1rem !important;
179
- font-size: 1rem !important;
180
- transition: all 0.2s ease !important;
181
- }
182
-
183
- .stTextInput > div > div > input:focus {
184
- border-color: rgba(140, 190, 170, 0.6) !important;
185
- box-shadow: 0 0 0 3px rgba(140, 190, 170, 0.1) !important;
186
  }
187
 
188
  .stTextArea > div > div > textarea {
189
  background-color: rgba(255,255,255,0.9) !important;
190
  border: 1px solid rgba(180, 210, 195, 0.4) !important;
191
  border-radius: 12px !important;
192
- padding: 0.75rem 1rem !important;
193
- font-size: 1rem !important;
194
  }
195
 
196
  .stButton > button {
@@ -200,15 +192,12 @@ st.markdown("""
200
  color: white !important;
201
  font-weight: 500 !important;
202
  padding: 0.75rem 2rem !important;
203
- font-size: 1rem !important;
204
- transition: all 0.3s ease !important;
205
  box-shadow: 0 4px 12px rgba(100, 160, 140, 0.25) !important;
206
  }
207
 
208
  .stButton > button:hover {
209
  background: linear-gradient(135deg, #6aaa98 0%, #5a9a88 100%) !important;
210
  transform: translateY(-2px) !important;
211
- box-shadow: 0 6px 20px rgba(100, 160, 140, 0.35) !important;
212
  }
213
 
214
  .divider {
@@ -226,14 +215,30 @@ if "clinician_orientation" not in st.session_state:
226
  if "onboarded" not in st.session_state:
227
  st.session_state.onboarded = False
228
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  SPACES = [
230
- {"icon": "📖", "title": "ShadowBox Library", "desc": "A resonant library for hard thoughts. Psychoeducation without synthetic intimacy.", "url": "https://huggingface.co/spaces/jostlebot/shadowbox.library"},
231
- {"icon": "💌", "title": "Tend & Send", "desc": "Craft messages with care. Practice tending to yourself before sending.", "url": "https://huggingface.co/spaces/jostlebot/TendSend.PrototypeBot"},
232
- {"icon": "🤖", "title": "Build a Bot", "desc": "AI literacy — understand how LLMs work, spot synthetic intimacy.", "url": "https://huggingface.co/spaces/jostlebot/BuildABot.2"},
233
- {"icon": "🩺", "title": "Diagnosis Explorer", "desc": "Understand diagnostic categories with nuance and care.", "url": "https://huggingface.co/spaces/jostlebot/DiagnosisExplorer"},
234
- {"icon": "🪞", "title": "GSPT", "desc": "Generating Safer Passages of Text. Warm, boundaried reflections.", "url": "https://huggingface.co/spaces/jostlebot/GSPT"},
235
- {"icon": "💚", "title": "Learn NVC", "desc": "Practice Nonviolent Communication. Transform judgments into needs.", "url": "https://huggingface.co/spaces/jostlebot/LearnNVC"},
236
- {"icon": "💬", "title": "Difficult Conversations", "desc": "Rehearse hard conversations before having them for real.", "url": "https://huggingface.co/spaces/jostlebot/PracticeDifficultConversations"},
237
  ]
238
 
239
  if not st.session_state.onboarded:
@@ -297,7 +302,7 @@ else:
297
  with col1 if i % 2 == 0 else col2:
298
  st.markdown(f"""
299
  <a href="{s["url"]}" target="_blank" class="field-card">
300
- <div class="field-icon">{s["icon"]}</div>
301
  <div class="field-title">{s["title"]}</div>
302
  <div class="field-desc">{s["desc"]}</div>
303
  </a>
 
1
  """Practice Fields Hub — Curated Therapeutic Practice Spaces"""
2
 
3
  import streamlit as st
4
+ import base64
5
+ import os
6
 
7
  st.set_page_config(page_title="Practice Fields", page_icon="🌱", layout="wide")
8
 
9
+ def get_image_base64(image_path):
10
+ """Convert image to base64 for embedding in HTML."""
11
+ try:
12
+ with open(image_path, "rb") as f:
13
+ return base64.b64encode(f.read()).decode()
14
+ except:
15
+ return None
16
+
17
+ # Get base path for images
18
+ BASE_PATH = os.path.dirname(os.path.abspath(__file__))
19
+ IMG_PATH = os.path.join(BASE_PATH, "images")
20
+
21
  st.markdown("""
22
  <style>
23
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');
 
27
  font-family: 'Inter', sans-serif;
28
  }
29
 
 
30
  #MainMenu {visibility: hidden;}
31
  footer {visibility: hidden;}
32
 
 
55
  font-size: 1.15rem;
56
  font-weight: 300;
57
  font-style: italic;
 
58
  }
59
 
60
  .clinician-badge {
 
84
  .field-card {
85
  background: linear-gradient(145deg, rgba(255,255,255,0.95) 0%, rgba(252,255,253,0.95) 100%);
86
  border-radius: 20px;
87
+ padding: 1.5rem;
88
  margin: 0.6rem 0;
89
  border: 1px solid rgba(180, 210, 195, 0.25);
90
  transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
 
98
  transform: translateY(-4px);
99
  box-shadow: 0 12px 32px rgba(100, 160, 140, 0.15);
100
  border-color: rgba(140, 190, 170, 0.4);
 
101
  }
102
 
103
  .field-icon {
104
+ width: 80px;
105
+ height: 80px;
106
+ object-fit: contain;
107
  margin-bottom: 0.75rem;
108
+ border-radius: 12px;
109
  }
110
 
111
  .field-title {
112
  color: #3a5555;
113
+ font-size: 1.2rem;
114
  font-weight: 500;
115
+ margin-bottom: 0.4rem;
 
116
  }
117
 
118
  .field-desc {
119
  color: #6a8585;
120
+ font-size: 0.9rem;
121
  line-height: 1.5;
122
  font-weight: 300;
123
  }
 
155
  background: rgba(255,255,255,0.6);
156
  padding: 0.75rem 1.5rem;
157
  border-radius: 30px;
 
158
  }
159
 
160
  .crisis-item {
 
163
  font-weight: 500;
164
  }
165
 
 
 
 
 
 
 
 
 
 
 
166
  .welcome-text {
167
  color: #4a6a6a;
168
  font-size: 1.1rem;
 
177
  border: 1px solid rgba(180, 210, 195, 0.4) !important;
178
  border-radius: 12px !important;
179
  padding: 0.75rem 1rem !important;
 
 
 
 
 
 
 
180
  }
181
 
182
  .stTextArea > div > div > textarea {
183
  background-color: rgba(255,255,255,0.9) !important;
184
  border: 1px solid rgba(180, 210, 195, 0.4) !important;
185
  border-radius: 12px !important;
 
 
186
  }
187
 
188
  .stButton > button {
 
192
  color: white !important;
193
  font-weight: 500 !important;
194
  padding: 0.75rem 2rem !important;
 
 
195
  box-shadow: 0 4px 12px rgba(100, 160, 140, 0.25) !important;
196
  }
197
 
198
  .stButton > button:hover {
199
  background: linear-gradient(135deg, #6aaa98 0%, #5a9a88 100%) !important;
200
  transform: translateY(-2px) !important;
 
201
  }
202
 
203
  .divider {
 
215
  if "onboarded" not in st.session_state:
216
  st.session_state.onboarded = False
217
 
218
+ # Load images
219
+ IMAGES = {
220
+ "shadowbox": get_image_base64(os.path.join(IMG_PATH, "shadowbox.webp")),
221
+ "tendsend": get_image_base64(os.path.join(IMG_PATH, "tendsend.webp")),
222
+ "buildabot": get_image_base64(os.path.join(IMG_PATH, "buildabot.webp")),
223
+ "diagnosis": get_image_base64(os.path.join(IMG_PATH, "diagnosis.webp")),
224
+ "gspt": get_image_base64(os.path.join(IMG_PATH, "gspt.webp")),
225
+ "learnnvc": get_image_base64(os.path.join(IMG_PATH, "learnnvc.webp")),
226
+ "difficultconversations": get_image_base64(os.path.join(IMG_PATH, "difficultconversations.webp")),
227
+ }
228
+
229
+ def img_tag(key):
230
+ if IMAGES.get(key):
231
+ return f'<img src="data:image/webp;base64,{IMAGES[key]}" class="field-icon" />'
232
+ return ""
233
+
234
  SPACES = [
235
+ {"key": "shadowbox", "title": "ShadowBox Library", "desc": "A resonant library for hard thoughts. Psychoeducation without synthetic intimacy.", "url": "https://huggingface.co/spaces/jostlebot/shadowbox.library"},
236
+ {"key": "tendsend", "title": "Tend & Send", "desc": "Craft messages with care. Practice tending to yourself before sending.", "url": "https://huggingface.co/spaces/jostlebot/TendSend.PrototypeBot"},
237
+ {"key": "buildabot", "title": "Build a Bot", "desc": "AI literacy — understand how LLMs work, spot synthetic intimacy.", "url": "https://huggingface.co/spaces/jostlebot/BuildABot.2"},
238
+ {"key": "diagnosis", "title": "Diagnosis Explorer", "desc": "Understand diagnostic categories with nuance and care.", "url": "https://huggingface.co/spaces/jostlebot/DiagnosisExplorer"},
239
+ {"key": "gspt", "title": "GSPT", "desc": "Generating Safer Passages of Text. Warm, boundaried reflections.", "url": "https://huggingface.co/spaces/jostlebot/GSPT"},
240
+ {"key": "learnnvc", "title": "Learn NVC", "desc": "Practice Nonviolent Communication. Transform judgments into needs.", "url": "https://huggingface.co/spaces/jostlebot/LearnNVC"},
241
+ {"key": "difficultconversations", "title": "Difficult Conversations", "desc": "Rehearse hard conversations before having them for real.", "url": "https://huggingface.co/spaces/jostlebot/PracticeDifficultConversations"},
242
  ]
243
 
244
  if not st.session_state.onboarded:
 
302
  with col1 if i % 2 == 0 else col2:
303
  st.markdown(f"""
304
  <a href="{s["url"]}" target="_blank" class="field-card">
305
+ {img_tag(s["key"])}
306
  <div class="field-title">{s["title"]}</div>
307
  <div class="field-desc">{s["desc"]}</div>
308
  </a>
images/buildabot.webp ADDED
images/diagnosis.webp ADDED
images/difficultconversations.webp ADDED
images/gspt.webp ADDED
images/learnnvc.webp ADDED
images/shadowbox.webp ADDED
images/tendsend.webp ADDED