Keldos commited on
Commit
44970ac
1 Parent(s): e1ee750

fix & refactor: 解决拷贝代码框按钮消失的问题

Browse files

但是,现在的代码可能有点问题……
mutationList中不存在新增bot message的addNode,只能用属性变化分析,可能会导致加button次数过多?
但是bot对话内容生成时又是不断覆盖pre的……因此不能用之前的抖动.5s,否则页面框会闪烁。

这可能会导致浏览器占用一些CPU和内存……希望有人能优化一下,谢谢

assets/Kelpy-Codos.js DELETED
@@ -1,76 +0,0 @@
1
- // ==UserScript==
2
- // @name Kelpy Codos
3
- // @namespace https://github.com/Keldos-Li/Kelpy-Codos
4
- // @version 1.0.5
5
- // @author Keldos; https://keldos.me/
6
- // @description Add copy button to PRE tags before CODE tag, for Chuanhu Chat especially.
7
- // Based on Chuanhu Chat version: ac04408 (2023-3-22)
8
- // @license Apache-2.0
9
- // @grant none
10
- // ==/UserScript==
11
-
12
- (function () {
13
- 'use strict';
14
-
15
- function addCopyButton(pre) {
16
- var code = pre.querySelector('code');
17
- if (!code) {
18
- return; // 如果没有找到 <code> 元素,则不添加按钮
19
- }
20
- var firstChild = code.firstChild;
21
- if (!firstChild) {
22
- return; // 如果 <code> 元素没有子节点,则不添加按钮
23
- }
24
- var button = document.createElement('button');
25
- button.textContent = '\uD83D\uDCCE'; // 使用 📎 符号作为“复制”按钮的文本
26
- button.style.position = 'relative';
27
- button.style.float = 'right';
28
- button.style.fontSize = '1em'; // 可选:调整按钮大小
29
- button.style.background = 'none'; // 可选:去掉背景颜色
30
- button.style.border = 'none'; // 可选:去掉边框
31
- button.style.cursor = 'pointer'; // 可选:显示指针样式
32
- button.addEventListener('click', function () {
33
- var range = document.createRange();
34
- range.selectNodeContents(code);
35
- range.setStartBefore(firstChild); // 将范围设置为第一个子节点之前
36
- var selection = window.getSelection();
37
- selection.removeAllRanges();
38
- selection.addRange(range);
39
-
40
- try {
41
- var success = document.execCommand('copy');
42
- if (success) {
43
- button.textContent = '\u2714';
44
- setTimeout(function () {
45
- button.textContent = '\uD83D\uDCCE'; // 恢复按钮为“复制”
46
- }, 2000);
47
- } else {
48
- button.textContent = '\u2716';
49
- }
50
- } catch (e) {
51
- console.error(e);
52
- button.textContent = '\u2716';
53
- }
54
-
55
- selection.removeAllRanges();
56
- });
57
- code.insertBefore(button, firstChild); // 将按钮插入到第一个子元素之前
58
- }
59
-
60
- function handleNewElements(mutationsList, observer) {
61
- for (var mutation of mutationsList) {
62
- if (mutation.type === 'childList') {
63
- for (var node of mutation.addedNodes) {
64
- if (node.nodeName === 'PRE') {
65
- addCopyButton(node);
66
- }
67
- }
68
- }
69
- }
70
- }
71
-
72
- var observer = new MutationObserver(handleNewElements);
73
- observer.observe(document.documentElement, { childList: true, subtree: true });
74
-
75
- document.querySelectorAll('pre').forEach(addCopyButton);
76
- })();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/custom.css CHANGED
@@ -287,7 +287,12 @@ ol:not(.options), ul:not(.options) {
287
  .toggle-md-btn {
288
  top: 0;
289
  }
290
-
 
 
 
 
 
291
 
292
  .message-wrap>div img{
293
  border-radius: 10px !important;
 
287
  .toggle-md-btn {
288
  top: 0;
289
  }
290
+ .copy-code-btn {
291
+ position: relative;
292
+ float: right;
293
+ font-size: 1em;
294
+ cursor: pointer;
295
+ }
296
 
297
  .message-wrap>div img{
298
  border-radius: 10px !important;
assets/custom.js CHANGED
@@ -333,6 +333,41 @@ function addChuanhuButton(botElement) {
333
  botElement.insertBefore(toggleButton, copyButton);
334
  }
335
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
336
  function renderMarkdownText(message) {
337
  var mdDiv = message.querySelector('.md-message');
338
  if (mdDiv) mdDiv.classList.remove('hideM');
@@ -412,6 +447,8 @@ var mObserver = new MutationObserver(function (mutationsList) {
412
  mathjaxUpdated = false;
413
  }
414
  saveHistoryHtml();
 
 
415
  }
416
  }
417
  for (var node of mmutation.removedNodes) {
@@ -421,10 +458,13 @@ var mObserver = new MutationObserver(function (mutationsList) {
421
  mathjaxUpdated = false;
422
  }
423
  saveHistoryHtml();
 
 
424
  }
425
  }
426
  } else if (mmutation.type === 'attributes') {
427
  if (mmutation.target.nodeType === 1 && mmutation.target.classList.contains('message') && mmutation.target.getAttribute('data-testid') === 'bot') {
 
428
  if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
429
  isThrottled = true;
430
  clearTimeout(timeoutId);
 
333
  botElement.insertBefore(toggleButton, copyButton);
334
  }
335
 
336
+ function addCopyCodeButton(pre) {
337
+ var code = null;
338
+ var firstChild = null;
339
+ code = pre.querySelector('code');
340
+ if (!code) return;
341
+ firstChild = code.querySelector('div');
342
+ if (!firstChild) return;
343
+ var oldCopyButton = null;
344
+ oldCopyButton = code.querySelector('button.copy-code-btn');
345
+ // if (oldCopyButton) oldCopyButton.remove();
346
+ if (oldCopyButton) return; // 没太有用,新生成的对话中始终会被pre覆盖,导致按钮消失,这段代码不启用……
347
+ var codeButton = document.createElement('button');
348
+ codeButton.classList.add('copy-code-btn');
349
+ codeButton.textContent = '\uD83D\uDCCE';
350
+
351
+ code.insertBefore(codeButton, firstChild);
352
+ codeButton.addEventListener('click', function () {
353
+ var range = document.createRange();
354
+ range.selectNodeContents(code);
355
+ range.setStartBefore(firstChild);
356
+ navigator.clipboard
357
+ .writeText(range.toString())
358
+ .then(() => {
359
+ codeButton.textContent = '\u2714';
360
+ setTimeout(function () {
361
+ codeButton.textContent = '\uD83D\uDCCE';
362
+ }, 2000);
363
+ })
364
+ .catch(e => {
365
+ console.error(e);
366
+ codeButton.textContent = '\u2716';
367
+ });
368
+ });
369
+ }
370
+
371
  function renderMarkdownText(message) {
372
  var mdDiv = message.querySelector('.md-message');
373
  if (mdDiv) mdDiv.classList.remove('hideM');
 
447
  mathjaxUpdated = false;
448
  }
449
  saveHistoryHtml();
450
+ document.querySelectorAll('#chuanhu_chatbot>.wrap>.message-wrap .message.bot').forEach(addChuanhuButton);
451
+ document.querySelectorAll('#chuanhu_chatbot>.wrap>.message-wrap .message.bot pre').forEach(addCopyCodeButton);
452
  }
453
  }
454
  for (var node of mmutation.removedNodes) {
 
458
  mathjaxUpdated = false;
459
  }
460
  saveHistoryHtml();
461
+ document.querySelectorAll('#chuanhu_chatbot>.wrap>.message-wrap .message.bot').forEach(addChuanhuButton);
462
+ document.querySelectorAll('#chuanhu_chatbot>.wrap>.message-wrap .message.bot pre').forEach(addCopyCodeButton);
463
  }
464
  }
465
  } else if (mmutation.type === 'attributes') {
466
  if (mmutation.target.nodeType === 1 && mmutation.target.classList.contains('message') && mmutation.target.getAttribute('data-testid') === 'bot') {
467
+ document.querySelectorAll('#chuanhu_chatbot>.wrap>.message-wrap .message.bot pre').forEach(addCopyCodeButton); // 目前写的是有点问题的,会导致加button次数过多,但是bot对话内容生成时又是不断覆盖pre的……
468
  if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
469
  isThrottled = true;
470
  clearTimeout(timeoutId);
modules/overwrites.py CHANGED
@@ -77,16 +77,14 @@ def postprocess_chat_messages(
77
  raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
78
 
79
  with open("./assets/custom.js", "r", encoding="utf-8") as f, \
80
- open("./assets/Kelpy-Codos.js", "r", encoding="utf-8") as f2, \
81
- open("./assets/external-scripts.js", "r", encoding="utf-8") as f3:
82
  customJS = f.read()
83
- kelpyCodos = f2.read()
84
- externalScripts = f3.read()
85
 
86
 
87
  def reload_javascript():
88
  print("Reloading javascript...")
89
- js = f'<script>{customJS}</script><script>{kelpyCodos}</script><script async>{externalScripts}</script>'
90
  def template_response(*args, **kwargs):
91
  res = GradioTemplateResponseOriginal(*args, **kwargs)
92
  res.body = res.body.replace(b'</html>', f'{js}</html>'.encode("utf8"))
 
77
  raise ValueError(f"Invalid message for Chatbot component: {chat_message}")
78
 
79
  with open("./assets/custom.js", "r", encoding="utf-8") as f, \
80
+ open("./assets/external-scripts.js", "r", encoding="utf-8") as f1:
 
81
  customJS = f.read()
82
+ externalScripts = f1.read()
 
83
 
84
 
85
  def reload_javascript():
86
  print("Reloading javascript...")
87
+ js = f'<script>{customJS}</script><script async>{externalScripts}</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"))