Keldos commited on
Commit
2a53137
·
1 Parent(s): 50dc299

feat: 使用mathjax渲染页面中的数学公式

Browse files
ChuanhuChatbot.py CHANGED
@@ -100,6 +100,7 @@ with gr.Blocks(css=customCSS, theme=small_and_beautiful_theme) as demo:
100
  )
101
  single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False)
102
  use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False)
 
103
  language_select_dropdown = gr.Dropdown(
104
  label=i18n("选择回复语言(针对搜索&索引功能)"),
105
  choices=REPLY_LANGUAGES,
 
100
  )
101
  single_turn_checkbox = gr.Checkbox(label=i18n("单轮对话"), value=False)
102
  use_websearch_checkbox = gr.Checkbox(label=i18n("使用在线搜索"), value=False)
103
+ render_latex_checkbox = gr.Checkbox(label="渲染LaTeX公式", elem_id="render_latex_checkbox")
104
  language_select_dropdown = gr.Dropdown(
105
  label=i18n("选择回复语言(针对搜索&索引功能)"),
106
  choices=REPLY_LANGUAGES,
assets/custom.js CHANGED
@@ -14,6 +14,9 @@ var userInfoDiv = null;
14
  var appTitleDiv = null;
15
  var chatbot = null;
16
  var apSwitch = null;
 
 
 
17
 
18
  var ga = document.getElementsByTagName("gradio-app");
19
  var targetNode = ga[0];
@@ -22,13 +25,14 @@ var isInIframe = (window.self !== window.top);
22
  // gradio 页面加载好了么??? 我能动你的元素了么??
23
  function gradioLoaded(mutations) {
24
  for (var i = 0; i < mutations.length; i++) {
25
- if (mutations[i].addedNodes.length) {
26
  gradioContainer = document.querySelector(".gradio-container");
27
  user_input_tb = document.getElementById('user_input_tb');
28
  userInfoDiv = document.getElementById("user_info");
29
  appTitleDiv = document.getElementById("app_title");
30
  chatbot = document.querySelector('#chuanhu_chatbot');
31
  apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
 
32
 
33
  if (gradioContainer && apSwitch) { // gradioCainter 加载出来了没?
34
  adjustDarkMode();
@@ -40,7 +44,11 @@ function gradioLoaded(mutations) {
40
  setTimeout(showOrHideUserInfo(), 2000);
41
  }
42
  if (chatbot) { // chatbot 加载出来了没?
43
- setChatbotHeight()
 
 
 
 
44
  }
45
  }
46
  }
@@ -140,12 +148,12 @@ function showOrHideUserInfo() {
140
  appTitleDiv.ontouchend = function () {
141
  setTimeout(function () {
142
  toggleUserInfoVisibility(true);
143
- }, 3000);
144
  };
145
  userInfoDiv.ontouchend = function () {
146
  setTimeout(function () {
147
  toggleUserInfoVisibility(true);
148
- }, 3000);
149
  };
150
  sendBtn.ontouchend = function () {
151
  setTimeout(function () {
@@ -209,6 +217,97 @@ function setChatbotHeight() {
209
  }
210
  }
211
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  // 监视页面内部 DOM 变动
213
  var observer = new MutationObserver(function (mutations) {
214
  gradioLoaded(mutations);
 
14
  var appTitleDiv = null;
15
  var chatbot = null;
16
  var apSwitch = null;
17
+ var messageBotDivs = null;
18
+ var renderLatex = null;
19
+ var shouldRenderLatex = false;
20
 
21
  var ga = document.getElementsByTagName("gradio-app");
22
  var targetNode = ga[0];
 
25
  // gradio 页面加载好了么??? 我能动你的元素了么??
26
  function gradioLoaded(mutations) {
27
  for (var i = 0; i < mutations.length; i++) {
28
+ if (mutations[i].addedNodes.length) {
29
  gradioContainer = document.querySelector(".gradio-container");
30
  user_input_tb = document.getElementById('user_input_tb');
31
  userInfoDiv = document.getElementById("user_info");
32
  appTitleDiv = document.getElementById("app_title");
33
  chatbot = document.querySelector('#chuanhu_chatbot');
34
  apSwitch = document.querySelector('.apSwitch input[type="checkbox"]');
35
+ renderLatex = document.querySelector("#render_latex_checkbox > label > input");
36
 
37
  if (gradioContainer && apSwitch) { // gradioCainter 加载出来了没?
38
  adjustDarkMode();
 
44
  setTimeout(showOrHideUserInfo(), 2000);
45
  }
46
  if (chatbot) { // chatbot 加载出来了没?
47
+ setChatbotHeight();
48
+ }
49
+ if (renderLatex) { // renderLatex 加载出来了没?
50
+ shouldRenderLatex = renderLatex.checked;
51
+ updateMathJax();
52
  }
53
  }
54
  }
 
148
  appTitleDiv.ontouchend = function () {
149
  setTimeout(function () {
150
  toggleUserInfoVisibility(true);
151
+ }, 3000);
152
  };
153
  userInfoDiv.ontouchend = function () {
154
  setTimeout(function () {
155
  toggleUserInfoVisibility(true);
156
+ }, 3000);
157
  };
158
  sendBtn.ontouchend = function () {
159
  setTimeout(function () {
 
217
  }
218
  }
219
 
220
+ var rendertime = 0; // for debugging
221
+ var mathjaxUpdated = false;
222
+
223
+ function renderMathJax() {
224
+ messageBotDivs = document.querySelectorAll('.message.bot');
225
+ for (var i = 0; i < messageBotDivs.length; i++) {
226
+ var mathJaxSpan = messageBotDivs[i].querySelector('.MathJax_Preview');
227
+ if (!mathJaxSpan && shouldRenderLatex && !mathjaxUpdated) {
228
+ MathJax.Hub.Queue(["Typeset", MathJax.Hub, messageBotDivs[i]]);
229
+ rendertime +=1; // for debugging
230
+ console.log("renderingMathJax", i)
231
+ }
232
+ }
233
+ mathjaxUpdated = true;
234
+ console.log("MathJaxTriedToRender!!!")
235
+ }
236
+
237
+ function removeMathjax() {
238
+ // var jax = MathJax.Hub.getAllJax();
239
+ // for (var i = 0; i < jax.length; i++) {
240
+ // // MathJax.typesetClear(jax[i]);
241
+ // jax[i].Text(newmath)
242
+ // jax[i].Reprocess()
243
+ // }
244
+ // 我真的不会了啊啊啊,mathjax并没有提供转换为原先文本的办法。
245
+ mathjaxUpdated = true;
246
+ console.log("MathJax removed!");
247
+ }
248
+
249
+ function updateMathJax() {
250
+ renderLatex.addEventListener("change", function() {
251
+ shouldRenderLatex = renderLatex.checked;
252
+ console.log(shouldRenderLatex)
253
+ if (!mathjaxUpdated) {
254
+ if (shouldRenderLatex) {
255
+ renderMathJax();
256
+ } else {
257
+ console.log("取消渲染!")
258
+ removeMathjax();
259
+ }
260
+ } else {
261
+ if (!shouldRenderLatex) {
262
+ mathjaxUpdated = false; // reset
263
+ }
264
+ }
265
+ });
266
+ if (shouldRenderLatex && !mathjaxUpdated) {
267
+ renderMathJax();
268
+ }
269
+ mathjaxUpdated = false;
270
+ }
271
+
272
+ let timeoutId;
273
+ let isThrottled = false;
274
+ // 监听所有元素中message的变化,用来查找需要渲染的mathjax
275
+ var mObserver = new MutationObserver(function (mutationsList, observer) {
276
+ if (shouldRenderLatex) {
277
+ for (var mutation of mutationsList) {
278
+ if (mutation.type === 'childList') {
279
+ for (var node of mutation.addedNodes) {
280
+ if (node.nodeType === 1 && node.classList.contains('message') && node.classList.contains('bot')) {
281
+ // console.log("added");
282
+ renderMathJax();
283
+ mathjaxUpdated = false;
284
+ }
285
+ }
286
+ for (var node of mutation.removedNodes) {
287
+ if (node.nodeType === 1 && node.classList.contains('message') && node.classList.contains('bot')) {
288
+ // console.log("removed");
289
+ renderMathJax();
290
+ mathjaxUpdated = false;
291
+ }
292
+ }
293
+ } else if (mutation.type === 'attributes') {
294
+ if (mutation.target.nodeType === 1 && mutation.target.classList.contains('message') && mutation.target.classList.contains('bot')) {
295
+ if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
296
+ isThrottled = true;
297
+ clearTimeout(timeoutId);
298
+ timeoutId = setTimeout(() => {
299
+ isThrottled = false;
300
+ // console.log("changed");
301
+ renderMathJax();
302
+ mathjaxUpdated = false;
303
+ }, 500);
304
+ }
305
+ }
306
+ }
307
+ }
308
+ });
309
+ mObserver.observe(targetNode, { attributes: true, childList: true, subtree: true });
310
+
311
  // 监视页面内部 DOM 变动
312
  var observer = new MutationObserver(function (mutations) {
313
  gradioLoaded(mutations);
assets/load-mathjax.js ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // window.MathJax = {
2
+ // tex: {
3
+ // inlineMath: [['$', '$'], ['\\(', '\\)']]
4
+ // },
5
+ // svg: {
6
+ // fontCache: 'global'
7
+ // }
8
+ // };
9
+
10
+ (function () {
11
+ var script = document.createElement('script');
12
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-MML-AM_CHTML";
13
+ script.async = true;
14
+ document.head.appendChild(script);
15
+
16
+ var config = document.createElement("script");
17
+ config.type = "text/x-mathjax-config";
18
+ config.text = "MathJax.Hub.Config({tex2jax: {inlineMath: [['$','$'], ['\\\\(','\\\\)']],displayMath: [['$$','$$'], ['\\\\[','\\\\]']]}});";
19
+ document.head.appendChild(config);
20
+ })();
modules/overwrites.py CHANGED
@@ -76,13 +76,15 @@ def postprocess_chat_messages(
76
  else:
77
  raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
78
 
79
- with open("./assets/custom.js", "r", encoding="utf-8") as f, open("./assets/Kelpy-Codos.js", "r", encoding="utf-8") as f2:
80
  customJS = f.read()
81
  kelpyCodos = f2.read()
 
 
82
 
83
  def reload_javascript():
84
  print("Reloading javascript...")
85
- js = f'<script>{customJS}</script><script>{kelpyCodos}</script>'
86
  def template_response(*args, **kwargs):
87
  res = GradioTemplateResponseOriginal(*args, **kwargs)
88
  res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
 
76
  else:
77
  raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
78
 
79
+ with open("./assets/custom.js", "r", encoding="utf-8") as f, open("./assets/Kelpy-Codos.js", "r", encoding="utf-8") as f2, open("./assets/load-mathjax.js", "r", encoding="utf-8") as f3:
80
  customJS = f.read()
81
  kelpyCodos = f2.read()
82
+ loadMathjax = f3.read()
83
+
84
 
85
  def reload_javascript():
86
  print("Reloading javascript...")
87
+ js = f'<script>{customJS}</script><script>{kelpyCodos}</script><script async>{loadMathjax}</script>'
88
  def template_response(*args, **kwargs):
89
  res = GradioTemplateResponseOriginal(*args, **kwargs)
90
  res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))