AlauStone commited on
Commit
a71ebfb
·
verified ·
1 Parent(s): 6e76c27

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -9
app.py CHANGED
@@ -372,6 +372,63 @@ def inject_custom_css():
372
 
373
  inject_custom_css()
374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  # 全局游客标识(session_state 在单次渲染中不变)
376
  IS_GUEST = not bool(st.session_state.get("current_user"))
377
 
@@ -1709,11 +1766,13 @@ def _chat_fragment():
1709
  q = st.session_state.pop("_quick_question")
1710
 
1711
  with chat_box:
1712
- for m in st.session_state.messages:
1713
  with st.chat_message(m["role"]):
1714
- if m["role"] == "assistant" and m.get("meta"):
 
 
1715
  st.markdown(
1716
- m["content"] + f'\n\n<span style="color:#999;font-size:12px;">{m["meta"]}</span>',
1717
  unsafe_allow_html=True,
1718
  )
1719
  else:
@@ -1768,12 +1827,14 @@ def _chat_fragment():
1768
  llm_answer(q, relevant_docs, _model_name, _web_on, kb_mode=_kb_mode, source_files=source_files)
1769
  )
1770
  meta_info = st.session_state.get("last_meta", "")
1771
- # meta 拼进 response_container,不创建独立DOM元素
1772
- if meta_info:
1773
- response_container.markdown(
1774
- full_response + f'\n\n<span style="color:#999;font-size:12px;">{meta_info}</span>',
1775
- unsafe_allow_html=True,
1776
- )
 
 
1777
  st.session_state.messages.append(
1778
  {"role": "assistant", "content": full_response, "meta": meta_info}
1779
  )
 
372
 
373
  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 在单次渲染中不变)
433
  IS_GUEST = not bool(st.session_state.get("current_user"))
434
 
 
1766
  q = st.session_state.pop("_quick_question")
1767
 
1768
  with chat_box:
1769
+ for idx, m in enumerate(st.session_state.messages):
1770
  with st.chat_message(m["role"]):
1771
+ if m["role"] == "assistant":
1772
+ meta_html = f'\n\n<span style="color:#999;font-size:12px;">{m["meta"]}</span>' if m.get("meta") else ""
1773
+ action_btns = _render_action_buttons(m["content"], f"hist_{idx}")
1774
  st.markdown(
1775
+ m["content"] + meta_html + action_btns,
1776
  unsafe_allow_html=True,
1777
  )
1778
  else:
 
1827
  llm_answer(q, relevant_docs, _model_name, _web_on, kb_mode=_kb_mode, source_files=source_files)
1828
  )
1829
  meta_info = st.session_state.get("last_meta", "")
1830
+ # 生成工具按钮(使用当前消息数作为ID)
1831
+ msg_idx = len(st.session_state.messages)
1832
+ action_btns = _render_action_buttons(full_response, f"new_{msg_idx}")
1833
+ meta_html = f'\n\n<span style="color:#999;font-size:12px;">{meta_info}</span>' if meta_info else ""
1834
+ response_container.markdown(
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
  )