jujutechnology commited on
Commit
c098be1
·
verified ·
1 Parent(s): 87188de

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +119 -88
app.py CHANGED
@@ -38,7 +38,7 @@ def should_generate_visual(user_prompt, ai_response):
38
  """Determine if a visual aid would be helpful based on the content"""
39
  # Expanded keywords to trigger new dynamic visuals
40
  k12_visual_keywords = [
41
- 'add', 'subtract', 'multiply', 'times', 'divide', 'counting', 'numbers',
42
  'fraction', 'half', 'quarter', 'third', 'parts', 'whole',
43
  'shape', 'triangle', 'circle', 'square', 'rectangle',
44
  'money', 'coins', 'dollars', 'cents', 'change',
@@ -47,20 +47,40 @@ def should_generate_visual(user_prompt, ai_response):
47
  'place value', 'tens', 'ones', 'hundreds',
48
  'pattern', 'sequence', 'skip counting',
49
  'greater than', 'less than', 'equal', 'compare',
50
- 'number line', 'array', 'grid'
51
  ]
52
 
53
  combined_text = (user_prompt + " " + ai_response).lower()
54
- return any(keyword in combined_text for keyword in k12_visual_keywords)
 
 
55
 
56
  def create_visual_manipulative(user_prompt, ai_response):
57
  """-- SMART VISUAL ROUTER --
58
  Parses the user prompt and calls the appropriate dynamic visual function."""
59
  try:
60
- user_lower = user_prompt.lower()
61
 
62
- # Priority 1: Time / Clock (e.g., "7:30", "4 o'clock")
63
- time_match = re.search(r'(\d{1,2}):(\d{2})', user_lower) or re.search(r'(\d{1,2})\s*o\'clock', user_lower)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  if time_match:
65
  groups = time_match.groups()
66
  hour = int(groups[0])
@@ -68,22 +88,15 @@ def create_visual_manipulative(user_prompt, ai_response):
68
  if 1 <= hour <= 12 and 0 <= minute <= 59:
69
  return create_clock_visual(hour, minute)
70
 
71
- # Priority 2: Fractions (e.g., "2/5", "fraction 3/8")
72
  fraction_match = re.search(r'(\d+)/(\d+)', user_lower)
73
  if fraction_match:
74
  num, den = int(fraction_match.group(1)), int(fraction_match.group(2))
75
- if 0 < num <= den and den <= 16: # Keep it visually clean
76
  return create_dynamic_fraction_circle(num, den)
77
 
78
- # Priority 3: Multiplication Arrays (e.g., "3 times 5", "4 x 6")
79
- mult_match = re.search(r'(\d+)\s*(?:x|times)\s*(\d+)', user_lower)
80
- if mult_match:
81
- rows, cols = int(mult_match.group(1)), int(mult_match.group(2))
82
- if rows <= 10 and cols <= 10: # Keep arrays reasonable
83
- return create_multiplication_array(rows, cols)
84
-
85
- # Priority 4: Addition/Subtraction Blocks
86
- if any(word in user_lower for word in ['add', 'plus', '+', 'subtract', 'minus', 'take away', '-']):
87
  numbers = re.findall(r'\d+', user_prompt)
88
  if len(numbers) >= 2:
89
  num1, num2 = int(numbers[0]), int(numbers[1])
@@ -91,16 +104,16 @@ def create_visual_manipulative(user_prompt, ai_response):
91
  if num1 <= 20 and num2 <= 20:
92
  return create_counting_blocks(num1, num2, operation)
93
 
94
- # Priority 5: Number Lines
95
- if 'number line' in user_lower:
96
  numbers = [int(n) for n in re.findall(r'\d+', user_prompt)]
97
  if numbers:
98
  start = min(numbers) - 2
99
  end = max(numbers) + 2
100
  return create_number_line(start, end, numbers, "Your Numbers on the Line")
101
 
102
- # Priority 6: Place Value
103
- if 'place value' in user_lower:
104
  numbers = re.findall(r'\d+', user_prompt)
105
  if numbers:
106
  num = int(numbers[0])
@@ -108,10 +121,10 @@ def create_visual_manipulative(user_prompt, ai_response):
108
  return create_place_value_blocks(num)
109
 
110
  # Fallback to static, general visuals
111
- if any(word in user_lower for word in ['fraction', 'part']): return create_dynamic_fraction_circle(1, 2) # Show a default example
112
  if any(word in user_lower for word in ['shape']): return create_shape_explorer()
113
  if any(word in user_lower for word in ['money', 'coin']): return create_money_counter()
114
- if any(word in user_lower for word in ['time', 'clock']): return create_clock_visual(10, 10) # Show a default example
115
 
116
  return None # No relevant visual found
117
 
@@ -121,6 +134,74 @@ def create_visual_manipulative(user_prompt, ai_response):
121
 
122
  # --- VISUAL TOOLBOX FUNCTIONS ---
123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  def create_counting_blocks(num1, num2, operation):
125
  """(Dynamic) Create colorful counting blocks for addition/subtraction."""
126
  html = f"""
@@ -166,7 +247,7 @@ def create_clock_visual(hours, minutes):
166
  return html
167
 
168
  def create_multiplication_array(rows, cols):
169
- """(NEW & Dynamic) Generates an SVG grid of dots to show multiplication."""
170
  cell_size, gap = 25, 5
171
  svg_width = cols * (cell_size + gap)
172
  svg_height = rows * (cell_size + gap)
@@ -175,12 +256,10 @@ def create_multiplication_array(rows, cols):
175
  return html
176
 
177
  def create_number_line(start, end, points, title="Number Line"):
178
- """(NEW & Dynamic) Creates a simple number line SVG."""
179
  width = 600
180
  padding = 30
181
- # Handle the edge case where start equals end
182
- if start >= end:
183
- end = start + 1
184
  scale = (width - 2 * padding) / (end - start)
185
  def to_x(n): return padding + (n - start) * scale
186
  ticks_html = "".join([f'<g transform="translate({to_x(i)}, 50)"><line y2="10" stroke="#aaa"/><text y="30" text-anchor="middle" fill="#555">{i}</text></g>' for i in range(start, end + 1)])
@@ -191,66 +270,19 @@ def create_number_line(start, end, points, title="Number Line"):
191
  def create_place_value_blocks(number):
192
  """(FIXED & Dynamic) Create place value blocks for understanding numbers."""
193
  hundreds, tens, ones = number // 100, (number % 100) // 10, number % 10
194
-
195
- # --- Hundreds Block HTML ---
196
- h_block_html = ""
197
  if hundreds > 0:
198
  hundreds_grid = "".join(["<div style='background:#F5A6A6'></div>"] * 100)
199
- hundreds_squares = "".join([f"""
200
- <div style="width: 100px; height: 100px; background: #FF6B6B; border: 2px solid #D63031; display: grid; grid-template-columns: repeat(10, 1fr); gap: 2px; padding: 2px;">
201
- {hundreds_grid}
202
- </div>
203
- """ for _ in range(hundreds)])
204
- h_block_html = f"""
205
- <div style="text-align: center;">
206
- <h4>Hundreds: {hundreds}</h4>
207
- <div style="display: flex; gap: 5px;">{hundreds_squares}</div>
208
- </div>
209
- """
210
-
211
- # --- Tens Block HTML ---
212
- t_block_html = ""
213
  if tens > 0:
214
  tens_grid = "".join(["<div style='background:#A2E8E4'></div>"] * 10)
215
- tens_sticks = "".join([f"""
216
- <div style="width: 10px; height: 100px; background: #4ECDC4; border: 2px solid #00B894; display: grid; grid-template-rows: repeat(10, 1fr); gap: 2px; padding: 2px;">
217
- {tens_grid}
218
- </div>
219
- """ for _ in range(tens)])
220
- t_block_html = f"""
221
- <div style="text-align: center;">
222
- <h4>Tens: {tens}</h4>
223
- <div style="display: flex; gap: 5px; align-items: flex-end;">{tens_sticks}</div>
224
- </div>
225
- """
226
-
227
- # --- Ones Block HTML ---
228
- o_block_html = ""
229
  if ones > 0:
230
  ones_cubes = "".join(['<div style="width: 10px; height: 10px; background: #FFE066; border: 2px solid #FDCB6E;"></div>' for _ in range(ones)])
231
- o_block_html = f"""
232
- <div style="text-align: center;">
233
- <h4>Ones: {ones}</h4>
234
- <div style="display: flex; gap: 5px; align-items: flex-end; flex-wrap: wrap; width: 50px; justify-content: center;">{ones_cubes}</div>
235
- </div>
236
- """
237
-
238
- # --- Final Assembly ---
239
- html = f"""
240
- <div style="padding: 20px; background: linear-gradient(135deg, #dfe6e9 0%, #b2bec3 100%); border-radius: 15px; margin: 10px 0;">
241
- <h3 style="color: #333; text-align: center;">Place Value Blocks for {number}</h3>
242
- <div style="display: flex; justify-content: center; align-items: flex-end; gap: 20px; flex-wrap: wrap; padding: 20px 0; min-height: 150px;">
243
- {h_block_html}
244
- {t_block_html}
245
- {o_block_html}
246
- </div>
247
- <div style="text-align: center; margin-top: 15px; padding: 10px; background: rgba(0,0,0,0.1); border-radius: 10px;">
248
- <h4 style="color: #333; margin:0;">
249
- {hundreds} Hundreds + {tens} Tens + {ones} Ones = {number}
250
- </h4>
251
- </div>
252
- </div>
253
- """
254
  return html
255
 
256
  def create_shape_explorer():
@@ -289,18 +321,18 @@ if api_key:
289
  IMPORTANT: When explaining mathematical concepts to young learners, mention that colorful visual aids will be provided to help illustrate the concept. Use phrases like:
290
  - "Let me show you this with some colorful blocks..."
291
  - "A fun visual will help you see how this works..."
292
- - "I'll create a picture to help you understand this fraction..."
 
293
 
294
  Focus on concepts appropriate for K-12 students:
295
  - Basic counting and number recognition
296
  - Simple addition and subtraction (using manipulatives)
297
  - Multiplication as arrays or groups
 
298
  - Basic shapes and geometry
299
  - Place value with hundreds, tens, ones
300
  - Money counting and coin recognition
301
  - Time telling with analog clocks
302
- - Simple patterns and sequences
303
- - Basic measurement concepts
304
 
305
  Always use age-appropriate language and relate math to real-world examples children understand.
306
 
@@ -446,7 +478,6 @@ with st.sidebar:
446
  if st.button("🔗 Share Chat", use_container_width=True):
447
  chat_json = json.dumps(st.session_state.chats[st.session_state.active_chat_key])
448
  chat_b64 = base64.urlsafe_b64encode(chat_json.encode()).decode()
449
- # This part might need adjustment depending on how Streamlit Community Cloud handles base URLs
450
  share_url = f"https://huggingface.co/spaces/YOUR_SPACE_HERE?shared_chat={chat_b64}" # Placeholder
451
  st.code(share_url)
452
  st.info("Copy the URL above to share this specific chat! (You might need to update the base URL)")
@@ -460,7 +491,7 @@ for message in st.session_state.chats[st.session_state.active_chat_key]:
460
  st.markdown(message["content"])
461
  # If a visual was generated and saved with the message, display it
462
  if "visual_html" in message and message["visual_html"]:
463
- components.html(message["visual_html"], height=400, scrolling=True)
464
 
465
  # User input
466
  if prompt := st.chat_input("Ask a K-8 math question..."):
@@ -494,7 +525,7 @@ if prompt := st.chat_input("Ask a K-8 math question..."):
494
  if should_generate_visual(prompt, full_response):
495
  visual_html_content = create_visual_manipulative(prompt, full_response)
496
  if visual_html_content:
497
- components.html(visual_html_content, height=400, scrolling=True)
498
 
499
  # Add AI response and visual to session state
500
  st.session_state.chats[st.session_state.active_chat_key].append({
 
38
  """Determine if a visual aid would be helpful based on the content"""
39
  # Expanded keywords to trigger new dynamic visuals
40
  k12_visual_keywords = [
41
+ 'add', 'subtract', 'multiply', 'times', 'divide', 'divided by', 'counting', 'numbers',
42
  'fraction', 'half', 'quarter', 'third', 'parts', 'whole',
43
  'shape', 'triangle', 'circle', 'square', 'rectangle',
44
  'money', 'coins', 'dollars', 'cents', 'change',
 
47
  'place value', 'tens', 'ones', 'hundreds',
48
  'pattern', 'sequence', 'skip counting',
49
  'greater than', 'less than', 'equal', 'compare',
50
+ 'number line', 'array', 'grid', 'area model'
51
  ]
52
 
53
  combined_text = (user_prompt + " " + ai_response).lower()
54
+ # Also check for symbols
55
+ return any(keyword in combined_text for keyword in k12_visual_keywords) or any(op in user_prompt for op in ['*', '/'])
56
+
57
 
58
  def create_visual_manipulative(user_prompt, ai_response):
59
  """-- SMART VISUAL ROUTER --
60
  Parses the user prompt and calls the appropriate dynamic visual function."""
61
  try:
62
+ user_lower = user_prompt.lower().replace(' ', '') # Remove spaces for easier parsing
63
 
64
+ # Priority 1: Division (e.g., "42 divided by 6", "21 / 3")
65
+ div_match = re.search(r'(\d+)dividedby(\d+)', user_lower) or re.search(r'(\d+)/(\d+)', user_lower)
66
+ if div_match and "fraction" not in user_lower: # Avoid confusion with fractions
67
+ dividend, divisor = int(div_match.group(1)), int(div_match.group(2))
68
+ if dividend <= 50 and divisor > 0: # Keep visuals manageable
69
+ return create_division_groups_visual(dividend, divisor)
70
+
71
+ # Priority 2: Multiplication (e.g., "3 times 5", "15 * 19")
72
+ mult_match = re.search(r'(\d+)(?:x|times|\*)(\d+)', user_lower)
73
+ if mult_match:
74
+ num1, num2 = int(mult_match.group(1)), int(mult_match.group(2))
75
+ # Use dot array for small numbers
76
+ if num1 <= 10 and num2 <= 10:
77
+ return create_multiplication_array(num1, num2)
78
+ # Use area model for larger numbers (up to 99x99)
79
+ elif 10 < num1 < 100 and 10 < num2 < 100:
80
+ return create_multiplication_area_model(num1, num2)
81
+
82
+ # Priority 3: Time / Clock (e.g., "7:30", "4 o'clock")
83
+ time_match = re.search(r'(\d{1,2}):(\d{2})', user_lower) or re.search(r'(\d{1,2})o\'clock', user_lower)
84
  if time_match:
85
  groups = time_match.groups()
86
  hour = int(groups[0])
 
88
  if 1 <= hour <= 12 and 0 <= minute <= 59:
89
  return create_clock_visual(hour, minute)
90
 
91
+ # Priority 4: Fractions (e.g., "2/5", "fraction 3/8")
92
  fraction_match = re.search(r'(\d+)/(\d+)', user_lower)
93
  if fraction_match:
94
  num, den = int(fraction_match.group(1)), int(fraction_match.group(2))
95
+ if 0 < num <= den and den <= 16:
96
  return create_dynamic_fraction_circle(num, den)
97
 
98
+ # Priority 5: Addition/Subtraction Blocks
99
+ if any(word in user_lower for word in ['add', 'plus', '+', 'subtract', 'minus', 'takeaway', '-']):
 
 
 
 
 
 
 
100
  numbers = re.findall(r'\d+', user_prompt)
101
  if len(numbers) >= 2:
102
  num1, num2 = int(numbers[0]), int(numbers[1])
 
104
  if num1 <= 20 and num2 <= 20:
105
  return create_counting_blocks(num1, num2, operation)
106
 
107
+ # Priority 6: Number Lines
108
+ if 'numberline' in user_lower:
109
  numbers = [int(n) for n in re.findall(r'\d+', user_prompt)]
110
  if numbers:
111
  start = min(numbers) - 2
112
  end = max(numbers) + 2
113
  return create_number_line(start, end, numbers, "Your Numbers on the Line")
114
 
115
+ # Priority 7: Place Value
116
+ if 'placevalue' in user_lower:
117
  numbers = re.findall(r'\d+', user_prompt)
118
  if numbers:
119
  num = int(numbers[0])
 
121
  return create_place_value_blocks(num)
122
 
123
  # Fallback to static, general visuals
124
+ if any(word in user_lower for word in ['fraction', 'part']): return create_dynamic_fraction_circle(1, 2)
125
  if any(word in user_lower for word in ['shape']): return create_shape_explorer()
126
  if any(word in user_lower for word in ['money', 'coin']): return create_money_counter()
127
+ if any(word in user_lower for word in ['time', 'clock']): return create_clock_visual(10, 10)
128
 
129
  return None # No relevant visual found
130
 
 
134
 
135
  # --- VISUAL TOOLBOX FUNCTIONS ---
136
 
137
+ def create_multiplication_area_model(num1, num2):
138
+ """(NEW & Dynamic) Creates an area model for 2-digit by 2-digit multiplication."""
139
+ n1_tens, n1_ones = num1 // 10, num1 % 10
140
+ n2_tens, n2_ones = num2 // 10, num2 % 10
141
+
142
+ p1 = n1_tens * n2_tens * 100 # E.g., 10 * 10
143
+ p2 = n1_tens * n2_ones * 10 # E.g., 10 * 9
144
+ p3 = n1_ones * n2_tens * 10 # E.g., 5 * 10
145
+ p4 = n1_ones * n2_ones # E.g., 5 * 9
146
+
147
+ total = p1 + p2 + p3 + p4
148
+
149
+ html = f"""
150
+ <div style="font-family: sans-serif; padding: 20px; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); border-radius: 15px; margin: 10px 0;">
151
+ <h3 style="text-align: center; color: #333;">Area Model for {num1} × {num2}</h3>
152
+ <div style="display: grid; grid-template-columns: auto 1fr 1fr; grid-template-rows: auto 1fr 1fr; gap: 5px; max-width: 400px; margin: 20px auto; color: #333;">
153
+ <!-- Corner --> <div/>
154
+ <div style="text-align: center; font-weight: bold; padding: 5px;">{n2_tens*10}</div>
155
+ <div style="text-align: center; font-weight: bold; padding: 5px;">{n2_ones}</div>
156
+
157
+ <div style="text-align: center; font-weight: bold; padding: 5px;">{n1_tens*10}</div>
158
+ <div style="background: #FFADAD; padding: 20px; text-align: center; border-radius: 8px;"><b>{n1_tens*10}×{n2_tens*10}</b><br/>{p1}</div>
159
+ <div style="background: #FFD6A5; padding: 20px; text-align: center; border-radius: 8px;"><b>{n1_tens*10}×{n2_ones}</b><br/>{p2}</div>
160
+
161
+ <div style="text-align: center; font-weight: bold; padding: 5px;">{n1_ones}</div>
162
+ <div style="background: #FDFFB6; padding: 20px; text-align: center; border-radius: 8px;"><b>{n1_ones}×{n2_tens*10}</b><br/>{p3}</div>
163
+ <div style="background: #CAFFBF; padding: 20px; text-align: center; border-radius: 8px;"><b>{n1_ones}×{n2_ones}</b><br/>{p4}</div>
164
+ </div>
165
+ <div style="text-align: center; margin-top: 15px; font-size: 1.1em;">
166
+ <b>Add the parts together:</b> {p1} + {p2} + {p3} + {p4} = <b>{total}</b>
167
+ </div>
168
+ </div>
169
+ """
170
+ return html
171
+
172
+ def create_division_groups_visual(dividend, divisor):
173
+ """(NEW & Dynamic) Creates a visual for division by grouping."""
174
+ if divisor == 0: return ""
175
+ quotient = dividend // divisor
176
+
177
+ groups_html = ""
178
+ dot_colors = ["#FF6B6B", "#4ECDC4", "#FFD93D", "#95E1D3", "#A0C4FF", "#FDBF6F"]
179
+
180
+ for i in range(divisor):
181
+ dots_in_group = "".join([f'<div style="width: 15px; height: 15px; background: {dot_colors[i % len(dot_colors)]}; border-radius: 50%;"></div>' for _ in range(quotient)])
182
+ groups_html += f"""
183
+ <div style="border: 2px dashed {dot_colors[i % len(dot_colors)]}; border-radius: 10px; padding: 10px; text-align: center;">
184
+ <b style="color: #333;">Group {i+1}</b>
185
+ <div style="display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; justify-content: center;">
186
+ {dots_in_group}
187
+ </div>
188
+ </div>
189
+ """
190
+
191
+ html = f"""
192
+ <div style="padding: 20px; background: #f0f2f6; border-radius: 15px; margin: 10px 0;">
193
+ <h3 style="text-align: center; color: #333;">Dividing {dividend} into {divisor} Groups</h3>
194
+ <p style="text-align: center; color: #555;">We are sharing {dividend} items equally among {divisor} groups.</p>
195
+ <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px; margin-top: 20px;">
196
+ {groups_html}
197
+ </div>
198
+ <h4 style="text-align: center; margin-top: 25px; color: #333;">
199
+ Each group gets <b>{quotient}</b> items. So, {dividend} ÷ {divisor} = {quotient}.
200
+ </h4>
201
+ </div>
202
+ """
203
+ return html
204
+
205
  def create_counting_blocks(num1, num2, operation):
206
  """(Dynamic) Create colorful counting blocks for addition/subtraction."""
207
  html = f"""
 
247
  return html
248
 
249
  def create_multiplication_array(rows, cols):
250
+ """(Dynamic) Generates an SVG grid of dots to show small multiplication."""
251
  cell_size, gap = 25, 5
252
  svg_width = cols * (cell_size + gap)
253
  svg_height = rows * (cell_size + gap)
 
256
  return html
257
 
258
  def create_number_line(start, end, points, title="Number Line"):
259
+ """(Dynamic) Creates a simple number line SVG."""
260
  width = 600
261
  padding = 30
262
+ if start >= end: end = start + 1
 
 
263
  scale = (width - 2 * padding) / (end - start)
264
  def to_x(n): return padding + (n - start) * scale
265
  ticks_html = "".join([f'<g transform="translate({to_x(i)}, 50)"><line y2="10" stroke="#aaa"/><text y="30" text-anchor="middle" fill="#555">{i}</text></g>' for i in range(start, end + 1)])
 
270
  def create_place_value_blocks(number):
271
  """(FIXED & Dynamic) Create place value blocks for understanding numbers."""
272
  hundreds, tens, ones = number // 100, (number % 100) // 10, number % 10
273
+ h_block_html, t_block_html, o_block_html = "", "", ""
 
 
274
  if hundreds > 0:
275
  hundreds_grid = "".join(["<div style='background:#F5A6A6'></div>"] * 100)
276
+ hundreds_squares = "".join([f'<div style="width: 100px; height: 100px; background: #FF6B6B; border: 2px solid #D63031; display: grid; grid-template-columns: repeat(10, 1fr); gap: 2px; padding: 2px;">{hundreds_grid}</div>' for _ in range(hundreds)])
277
+ h_block_html = f'<div style="text-align: center;"><h4>Hundreds: {hundreds}</h4><div style="display: flex; gap: 5px;">{hundreds_squares}</div></div>'
 
 
 
 
 
 
 
 
 
 
 
 
278
  if tens > 0:
279
  tens_grid = "".join(["<div style='background:#A2E8E4'></div>"] * 10)
280
+ tens_sticks = "".join([f'<div style="width: 10px; height: 100px; background: #4ECDC4; border: 2px solid #00B894; display: grid; grid-template-rows: repeat(10, 1fr); gap: 2px; padding: 2px;">{tens_grid}</div>' for _ in range(tens)])
281
+ t_block_html = f'<div style="text-align: center;"><h4>Tens: {tens}</h4><div style="display: flex; gap: 5px; align-items: flex-end;">{tens_sticks}</div></div>'
 
 
 
 
 
 
 
 
 
 
 
 
282
  if ones > 0:
283
  ones_cubes = "".join(['<div style="width: 10px; height: 10px; background: #FFE066; border: 2px solid #FDCB6E;"></div>' for _ in range(ones)])
284
+ o_block_html = f'<div style="text-align: center;"><h4>Ones: {ones}</h4><div style="display: flex; gap: 5px; align-items: flex-end; flex-wrap: wrap; width: 50px; justify-content: center;">{ones_cubes}</div></div>'
285
+ html = f"""<div style="padding: 20px; background: linear-gradient(135deg, #dfe6e9 0%, #b2bec3 100%); border-radius: 15px; margin: 10px 0;"><h3 style="color: #333; text-align: center;">Place Value Blocks for {number}</h3><div style="display: flex; justify-content: center; align-items: flex-end; gap: 20px; flex-wrap: wrap; padding: 20px 0; min-height: 150px;">{h_block_html}{t_block_html}{o_block_html}</div><div style="text-align: center; margin-top: 15px; padding: 10px; background: rgba(0,0,0,0.1); border-radius: 10px;"><h4 style="color: #333; margin:0;">{hundreds} Hundreds + {tens} Tens + {ones} Ones = {number}</h4></div></div>"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
286
  return html
287
 
288
  def create_shape_explorer():
 
321
  IMPORTANT: When explaining mathematical concepts to young learners, mention that colorful visual aids will be provided to help illustrate the concept. Use phrases like:
322
  - "Let me show you this with some colorful blocks..."
323
  - "A fun visual will help you see how this works..."
324
+ - "Let's use an area model to understand this multiplication problem..."
325
+ - "I'll create a picture showing how we can divide these into groups..."
326
 
327
  Focus on concepts appropriate for K-12 students:
328
  - Basic counting and number recognition
329
  - Simple addition and subtraction (using manipulatives)
330
  - Multiplication as arrays or groups
331
+ - Division as sharing into equal groups
332
  - Basic shapes and geometry
333
  - Place value with hundreds, tens, ones
334
  - Money counting and coin recognition
335
  - Time telling with analog clocks
 
 
336
 
337
  Always use age-appropriate language and relate math to real-world examples children understand.
338
 
 
478
  if st.button("🔗 Share Chat", use_container_width=True):
479
  chat_json = json.dumps(st.session_state.chats[st.session_state.active_chat_key])
480
  chat_b64 = base64.urlsafe_b64encode(chat_json.encode()).decode()
 
481
  share_url = f"https://huggingface.co/spaces/YOUR_SPACE_HERE?shared_chat={chat_b64}" # Placeholder
482
  st.code(share_url)
483
  st.info("Copy the URL above to share this specific chat! (You might need to update the base URL)")
 
491
  st.markdown(message["content"])
492
  # If a visual was generated and saved with the message, display it
493
  if "visual_html" in message and message["visual_html"]:
494
+ components.html(message["visual_html"], height=450, scrolling=True)
495
 
496
  # User input
497
  if prompt := st.chat_input("Ask a K-8 math question..."):
 
525
  if should_generate_visual(prompt, full_response):
526
  visual_html_content = create_visual_manipulative(prompt, full_response)
527
  if visual_html_content:
528
+ components.html(visual_html_content, height=450, scrolling=True)
529
 
530
  # Add AI response and visual to session state
531
  st.session_state.chats[st.session_state.active_chat_key].append({