awacke1 commited on
Commit
7eb6a2c
·
verified ·
1 Parent(s): 15c6070

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -7
app.py CHANGED
@@ -17,6 +17,7 @@ from datetime import datetime
17
  from PyPDF2 import PdfReader
18
  import threading
19
  from PIL import Image
 
20
 
21
  # ==============================================================================
22
  # Configuration & Constants
@@ -148,19 +149,26 @@ def update_player_state(username, position=None):
148
  players[username]["last_action_timestamp"] = time.time()
149
  state["players"] = players
150
  write_history_file(state)
 
151
 
152
- def add_object_to_state(obj_data):
153
  state = read_history_file()
154
  state = prune_inactive_players(state)
155
  state["objects"][obj_data["obj_id"]] = obj_data
156
  write_history_file(state)
 
 
157
 
158
- def remove_object_from_state(obj_id):
159
  state = read_history_file()
160
  state = prune_inactive_players(state)
161
  if obj_id in state["objects"]:
 
162
  del state["objects"][obj_id]
163
  write_history_file(state)
 
 
 
164
 
165
  def log_action(username, action_type, data):
166
  timestamp = get_current_time_str()
@@ -184,6 +192,41 @@ def log_action(username, action_type, data):
184
  except Exception as e:
185
  print(f"Error writing to player history log {player_log_file}: {e}")
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  # ==============================================================================
188
  # User State & Session Init
189
  # ==============================================================================
@@ -206,7 +249,7 @@ def load_username():
206
 
207
  def init_session_state():
208
  defaults = {
209
- 'message_input': "",
210
  'audio_cache': {},
211
  'tts_voice': "en-US-AriaNeural",
212
  'chat_history': [],
@@ -217,7 +260,8 @@ def init_session_state():
217
  'last_message': "",
218
  'selected_object': 'None',
219
  'paste_image_base64': "",
220
- 'new_world_name': "MyWorld"
 
221
  }
222
  for k, v in defaults.items():
223
  if k not in st.session_state:
@@ -332,6 +376,7 @@ async def save_chat_entry(username, message, voice, is_markdown=False):
332
  if st.session_state.get('enable_audio', True):
333
  tts_message = message
334
  audio_file = await async_edge_tts_generate(tts_message, voice, username)
 
335
  return md_file, audio_file
336
 
337
  async def load_chat_history():
@@ -613,12 +658,14 @@ def render_sidebar():
613
  if st.session_state.selected_object != name:
614
  st.session_state.selected_object = name
615
  update_player_state(st.session_state.username)
 
616
  col_idx += 1
617
  st.markdown("---")
618
  if st.button("🚫 Clear Tool", key="clear_tool", use_container_width=True):
619
  if st.session_state.selected_object != 'None':
620
  st.session_state.selected_object = 'None'
621
  update_player_state(st.session_state.username)
 
622
 
623
  st.markdown("---")
624
  st.header("🗣️ Voice & User")
@@ -650,8 +697,9 @@ def render_main_content():
650
 
651
  with tab_world:
652
  st.header("Shared 3D World")
653
- st.caption("Click to place objects with the selected tool. Right-click to delete. State is saved in history.json.")
654
  state = read_history_file()
 
655
  html_file_path = 'index.html'
656
  try:
657
  with open(html_file_path, 'r', encoding='utf-8') as f:
@@ -666,6 +714,7 @@ def render_main_content():
666
  </script>"""
667
  html_content_with_state = html_template.replace('</head>', js_injection_script + '\n</head>', 1)
668
  components.html(html_content_with_state, height=700, scrolling=False)
 
669
  except FileNotFoundError:
670
  st.error(f"CRITICAL ERROR: Could not find '{html_file_path}'.")
671
  except Exception as e:
@@ -682,7 +731,7 @@ def render_main_content():
682
  else:
683
  st.caption("No chat messages yet.")
684
 
685
- message_value = st.text_input("Your Message:", key="message_input", label_visibility="collapsed")
686
  send_button_clicked = st.button("Send Chat 💬", key="send_chat_button")
687
  should_autosend = st.session_state.get('autosend', False) and message_value
688
 
@@ -693,7 +742,7 @@ def render_main_content():
693
  voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
694
  asyncio.run(save_chat_entry(st.session_state.username, message_to_send, voice))
695
  update_player_state(st.session_state.username)
696
- st.session_state.message_input = ""
697
  st.rerun()
698
  elif send_button_clicked:
699
  st.toast("Message empty or same as last.")
@@ -705,6 +754,7 @@ def render_main_content():
705
  if st.button("Clear World State", key="clear_world_state"):
706
  state = {"objects": {}, "players": {}}
707
  write_history_file(state)
 
708
  st.success("World state cleared!")
709
  st.rerun()
710
 
 
17
  from PyPDF2 import PdfReader
18
  import threading
19
  from PIL import Image
20
+ from streamlit_javascript import st_javascript
21
 
22
  # ==============================================================================
23
  # Configuration & Constants
 
149
  players[username]["last_action_timestamp"] = time.time()
150
  state["players"] = players
151
  write_history_file(state)
152
+ return state
153
 
154
+ def add_object_to_state(obj_data, username):
155
  state = read_history_file()
156
  state = prune_inactive_players(state)
157
  state["objects"][obj_data["obj_id"]] = obj_data
158
  write_history_file(state)
159
+ log_action(username, "place", obj_data)
160
+ return state
161
 
162
+ def remove_object_from_state(obj_id, username):
163
  state = read_history_file()
164
  state = prune_inactive_players(state)
165
  if obj_id in state["objects"]:
166
+ obj_data = state["objects"][obj_id]
167
  del state["objects"][obj_id]
168
  write_history_file(state)
169
+ log_action(username, "delete", {"obj_id": obj_id})
170
+ return state, obj_data
171
+ return state, None
172
 
173
  def log_action(username, action_type, data):
174
  timestamp = get_current_time_str()
 
192
  except Exception as e:
193
  print(f"Error writing to player history log {player_log_file}: {e}")
194
 
195
+ # ==============================================================================
196
+ # JavaScript Message Handling
197
+ # ==============================================================================
198
+
199
+ def handle_js_messages():
200
+ message = st_javascript("""
201
+ window.addEventListener('message', (event) => {
202
+ return JSON.stringify(event.data);
203
+ }, {once: true});
204
+ return null;
205
+ """)
206
+ if message:
207
+ try:
208
+ data = json.loads(message)
209
+ action = data.get("type")
210
+ payload = data.get("payload", {})
211
+ username = payload.get("username", st.session_state.username)
212
+ if action == "place_object":
213
+ state = add_object_to_state(payload["object_data"], username)
214
+ st.session_state.world_state = state
215
+ st.rerun()
216
+ elif action == "delete_object":
217
+ state, obj_data = remove_object_from_state(payload["obj_id"], username)
218
+ st.session_state.world_state = state
219
+ st.rerun()
220
+ elif action == "move_player":
221
+ state = update_player_state(username, payload["position"])
222
+ log_action(username, "move", payload["position"])
223
+ st.session_state.world_state = state
224
+ st.rerun()
225
+ except json.JSONDecodeError:
226
+ print(f"Invalid JS message: {message}")
227
+ except Exception as e:
228
+ print(f"Error handling JS message: {e}")
229
+
230
  # ==============================================================================
231
  # User State & Session Init
232
  # ==============================================================================
 
249
 
250
  def init_session_state():
251
  defaults = {
252
+ 'message_counter': 0,
253
  'audio_cache': {},
254
  'tts_voice': "en-US-AriaNeural",
255
  'chat_history': [],
 
260
  'last_message': "",
261
  'selected_object': 'None',
262
  'paste_image_base64': "",
263
+ 'new_world_name': "MyWorld",
264
+ 'world_state': {"objects": {}, "players": {}}
265
  }
266
  for k, v in defaults.items():
267
  if k not in st.session_state:
 
376
  if st.session_state.get('enable_audio', True):
377
  tts_message = message
378
  audio_file = await async_edge_tts_generate(tts_message, voice, username)
379
+ log_action(username, "chat", {"message": message})
380
  return md_file, audio_file
381
 
382
  async def load_chat_history():
 
658
  if st.session_state.selected_object != name:
659
  st.session_state.selected_object = name
660
  update_player_state(st.session_state.username)
661
+ st.rerun()
662
  col_idx += 1
663
  st.markdown("---")
664
  if st.button("🚫 Clear Tool", key="clear_tool", use_container_width=True):
665
  if st.session_state.selected_object != 'None':
666
  st.session_state.selected_object = 'None'
667
  update_player_state(st.session_state.username)
668
+ st.rerun()
669
 
670
  st.markdown("---")
671
  st.header("🗣️ Voice & User")
 
697
 
698
  with tab_world:
699
  st.header("Shared 3D World")
700
+ st.caption("Click to place objects with the selected tool, or click to move player. Right-click to delete. State is saved in history.json.")
701
  state = read_history_file()
702
+ st.session_state.world_state = state
703
  html_file_path = 'index.html'
704
  try:
705
  with open(html_file_path, 'r', encoding='utf-8') as f:
 
714
  </script>"""
715
  html_content_with_state = html_template.replace('</head>', js_injection_script + '\n</head>', 1)
716
  components.html(html_content_with_state, height=700, scrolling=False)
717
+ handle_js_messages()
718
  except FileNotFoundError:
719
  st.error(f"CRITICAL ERROR: Could not find '{html_file_path}'.")
720
  except Exception as e:
 
731
  else:
732
  st.caption("No chat messages yet.")
733
 
734
+ message_value = st.text_input("Your Message:", key=f"message_input_{st.session_state.get('message_counter', 0)}", label_visibility="collapsed")
735
  send_button_clicked = st.button("Send Chat 💬", key="send_chat_button")
736
  should_autosend = st.session_state.get('autosend', False) and message_value
737
 
 
742
  voice = FUN_USERNAMES.get(st.session_state.username, "en-US-AriaNeural")
743
  asyncio.run(save_chat_entry(st.session_state.username, message_to_send, voice))
744
  update_player_state(st.session_state.username)
745
+ st.session_state.message_counter = st.session_state.get('message_counter', 0) + 1
746
  st.rerun()
747
  elif send_button_clicked:
748
  st.toast("Message empty or same as last.")
 
754
  if st.button("Clear World State", key="clear_world_state"):
755
  state = {"objects": {}, "players": {}}
756
  write_history_file(state)
757
+ st.session_state.world_state = state
758
  st.success("World state cleared!")
759
  st.rerun()
760