AlauStone commited on
Commit
17fa58a
·
verified ·
1 Parent(s): a71ebfb

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +68 -45
app.py CHANGED
@@ -374,59 +374,80 @@ inject_custom_css()
374
 
375
 
376
  def _render_action_buttons(content, msg_id):
377
- """生成消息底部的复制/分享/播报按钮"""
378
- # 转义内容用于 JavaScript
379
- escaped = content.replace("\\", "\\\\").replace("`", "\\`").replace("$", "\\$")
380
  return f'''
381
  <div style="display:flex; gap:12px; margin-top:8px; padding-top:6px; border-top:1px solid #eee;">
382
- <button onclick="copyText_{msg_id}()" style="background:none; border:none; cursor:pointer; color:#666; font-size:13px; padding:4px 8px; border-radius:4px;" onmouseover="this.style.background='#f0f0f0'" onmouseout="this.style.background='none'">📋 复制</button>
383
- <button onclick="shareText_{msg_id}()" style="background:none; border:none; cursor:pointer; color:#666; font-size:13px; padding:4px 8px; border-radius:4px;" onmouseover="this.style.background='#f0f0f0'" onmouseout="this.style.background='none'">🔗 分享</button>
384
- <button id="tts_btn_{msg_id}" onclick="speakText_{msg_id}()" style="background:none; border:none; cursor:pointer; color:#666; font-size:13px; padding:4px 8px; border-radius:4px;" onmouseover="this.style.background='#f0f0f0'" onmouseout="this.style.background='none'">🔊 播报</button>
385
  </div>
 
 
 
 
 
 
 
 
386
  <script>
387
- const msgContent_{msg_id} = `{escaped}`;
388
- let speaking_{msg_id} = false;
389
-
390
- function copyText_{msg_id}() {{
391
- navigator.clipboard.writeText(msgContent_{msg_id}).then(() => {{
392
- alert('已复制到剪贴板');
393
- }}).catch(() => {{
394
- alert('复制失败,请手动复制');
395
- }});
396
- }}
397
-
398
- function shareText_{msg_id}() {{
399
- if (navigator.share) {{
400
- navigator.share({{
401
- title: '智答AI助手',
402
- text: msgContent_{msg_id}
403
- }}).catch(() => {{}});
404
- }} else {{
405
- navigator.clipboard.writeText(msgContent_{msg_id}).then(() => {{
406
- alert('链接已复制,可粘贴分享');
407
- }});
408
  }}
409
- }}
410
-
411
- function speakText_{msg_id}() {{
412
- const btn = document.getElementById('tts_btn_{msg_id}');
413
- if (speaking_{msg_id}) {{
414
- window.speechSynthesis.cancel();
415
- speaking_{msg_id} = false;
416
- btn.textContent = '🔊 播报';
417
- }} else {{
418
- const utterance = new SpeechSynthesisUtterance(msgContent_{msg_id});
419
- utterance.lang = 'zh-CN';
420
- utterance.rate = 1.0;
421
- utterance.onend = () => {{ speaking_{msg_id} = false; btn.textContent = '🔊 播报'; }};
422
- utterance.onerror = () => {{ speaking_{msg_id} = false; btn.textContent = '🔊 播报'; }};
423
- window.speechSynthesis.speak(utterance);
424
- speaking_{msg_id} = true;
425
- btn.textContent = '⏹️ 停止';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
  }}
427
- }}
428
  </script>
429
  '''
 
430
 
431
 
432
  # 全局游客标识(session_state 在单次渲染中不变)
@@ -1775,6 +1796,7 @@ def _chat_fragment():
1775
  m["content"] + meta_html + action_btns,
1776
  unsafe_allow_html=True,
1777
  )
 
1778
  else:
1779
  st.markdown(m["content"])
1780
 
@@ -1835,6 +1857,7 @@ def _chat_fragment():
1835
  full_response + meta_html + action_btns,
1836
  unsafe_allow_html=True,
1837
  )
 
1838
  st.session_state.messages.append(
1839
  {"role": "assistant", "content": full_response, "meta": meta_info}
1840
  )
 
374
 
375
 
376
  def _render_action_buttons(content, msg_id):
377
+ """生成消息底部的复制/分享/播报按钮(纯HTML部分)"""
 
 
378
  return f'''
379
  <div style="display:flex; gap:12px; margin-top:8px; padding-top:6px; border-top:1px solid #eee;">
380
+ <button id="copy_btn_{msg_id}" style="background:none; border:none; cursor:pointer; color:#666; font-size:13px; padding:4px 8px; border-radius:4px;">📋 复制</button>
381
+ <button id="share_btn_{msg_id}" style="background:none; border:none; cursor:pointer; color:#666; font-size:13px; padding:4px 8px; border-radius:4px;">🔗 分享</button>
382
+ <button id="tts_btn_{msg_id}" style="background:none; border:none; cursor:pointer; color:#666; font-size:13px; padding:4px 8px; border-radius:4px;">🔊 播报</button>
383
  </div>
384
+ '''
385
+
386
+
387
+ def _inject_action_js(content, msg_id):
388
+ """注入按钮的 JavaScript(需要用 components.html 执行)"""
389
+ # 转义内容用于 JavaScript
390
+ escaped = content.replace("\\", "\\\\").replace("`", "\\`").replace("$", "\\$").replace("</script>", "<\\/script>")
391
+ js_code = f'''
392
  <script>
393
+ (function() {{
394
+ const msgContent = `{escaped}`;
395
+ let speaking = false;
396
+
397
+ const copyBtn = window.parent.document.getElementById('copy_btn_{msg_id}');
398
+ const shareBtn = window.parent.document.getElementById('share_btn_{msg_id}');
399
+ const ttsBtn = window.parent.document.getElementById('tts_btn_{msg_id}');
400
+
401
+ if (copyBtn) {{
402
+ copyBtn.onclick = function() {{
403
+ navigator.clipboard.writeText(msgContent).then(() => {{
404
+ copyBtn.textContent = '✅ 已复制';
405
+ setTimeout(() => {{ copyBtn.textContent = '📋 复制'; }}, 1500);
406
+ }}).catch(() => {{ alert('复制失败'); }});
407
+ }};
408
+ copyBtn.onmouseover = function() {{ this.style.background='#f0f0f0'; }};
409
+ copyBtn.onmouseout = function() {{ this.style.background='none'; }};
 
 
 
 
410
  }}
411
+
412
+ if (shareBtn) {{
413
+ shareBtn.onclick = function() {{
414
+ if (navigator.share) {{
415
+ navigator.share({{ title: '智答AI助手', text: msgContent }}).catch(() => {{}});
416
+ }} else {{
417
+ navigator.clipboard.writeText(msgContent).then(() => {{
418
+ shareBtn.textContent = ' 已复制';
419
+ setTimeout(() => {{ shareBtn.textContent = '🔗 分享'; }}, 1500);
420
+ }});
421
+ }}
422
+ }};
423
+ shareBtn.onmouseover = function() {{ this.style.background='#f0f0f0'; }};
424
+ shareBtn.onmouseout = function() {{ this.style.background='none'; }};
425
+ }}
426
+
427
+ if (ttsBtn) {{
428
+ ttsBtn.onclick = function() {{
429
+ if (speaking) {{
430
+ window.speechSynthesis.cancel();
431
+ speaking = false;
432
+ ttsBtn.textContent = '🔊 播报';
433
+ }} else {{
434
+ const utterance = new SpeechSynthesisUtterance(msgContent);
435
+ utterance.lang = 'zh-CN';
436
+ utterance.rate = 1.0;
437
+ utterance.onend = () => {{ speaking = false; ttsBtn.textContent = '🔊 播报'; }};
438
+ utterance.onerror = () => {{ speaking = false; ttsBtn.textContent = '🔊 播报'; }};
439
+ window.speechSynthesis.speak(utterance);
440
+ speaking = true;
441
+ ttsBtn.textContent = '⏹️ 停止';
442
+ }}
443
+ }};
444
+ ttsBtn.onmouseover = function() {{ this.style.background='#f0f0f0'; }};
445
+ ttsBtn.onmouseout = function() {{ this.style.background='none'; }};
446
  }}
447
+ }})();
448
  </script>
449
  '''
450
+ components.html(js_code, height=0)
451
 
452
 
453
  # 全局游客标识(session_state 在单次渲染中不变)
 
1796
  m["content"] + meta_html + action_btns,
1797
  unsafe_allow_html=True,
1798
  )
1799
+ _inject_action_js(m["content"], f"hist_{idx}")
1800
  else:
1801
  st.markdown(m["content"])
1802
 
 
1857
  full_response + meta_html + action_btns,
1858
  unsafe_allow_html=True,
1859
  )
1860
+ _inject_action_js(full_response, f"new_{msg_idx}")
1861
  st.session_state.messages.append(
1862
  {"role": "assistant", "content": full_response, "meta": meta_info}
1863
  )