Spaces:
Sleeping
Sleeping
Keldos commited on
Commit ·
5314ca6
1
Parent(s): 3fc8c4b
feat: 一键复制语言模型输出到剪贴板
Browse files- assets/Kelpy-Codos.js +3 -3
- assets/custom.css +26 -1
- assets/custom.js +57 -16
- modules/utils.py +2 -0
assets/Kelpy-Codos.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
| 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
|
| 7 |
-
// Based on Chuanhu
|
| 8 |
-
// @license
|
| 9 |
// @grant none
|
| 10 |
// ==/UserScript==
|
| 11 |
|
|
|
|
| 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 |
|
assets/custom.css
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
}
|
| 5 |
|
| 6 |
.message p { margin-bottom: 0.6rem !important;}
|
| 7 |
-
.message p:last-
|
| 8 |
|
| 9 |
#app_title {
|
| 10 |
font-weight: var(--prose-header-text-weight);
|
|
@@ -247,6 +247,31 @@ ol:not(.options), ul:not(.options) {
|
|
| 247 |
width: auto !important;
|
| 248 |
border-bottom-right-radius: 0 !important;
|
| 249 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
|
| 251 |
.message-wrap>div img{
|
| 252 |
border-radius: 10px !important;
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
.message p { margin-bottom: 0.6rem !important;}
|
| 7 |
+
.message p:last-of-type { margin-bottom: 0 !important; }
|
| 8 |
|
| 9 |
#app_title {
|
| 10 |
font-weight: var(--prose-header-text-weight);
|
|
|
|
| 247 |
width: auto !important;
|
| 248 |
border-bottom-right-radius: 0 !important;
|
| 249 |
}
|
| 250 |
+
.raw-message {
|
| 251 |
+
display: none;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
.copy-bot-btn {
|
| 255 |
+
border-radius: 5px;
|
| 256 |
+
/* background-color: #E6E6E6 !important; */
|
| 257 |
+
color: rgba(120, 120, 120, 0.64) !important;
|
| 258 |
+
padding: 4px !important;
|
| 259 |
+
position: absolute;
|
| 260 |
+
right: -22px;
|
| 261 |
+
bottom: 0;
|
| 262 |
+
cursor: pointer !important;
|
| 263 |
+
transition: color .2s ease, background-color .2s ease;
|
| 264 |
+
}
|
| 265 |
+
.copy-bot-btn:hover {
|
| 266 |
+
background-color: rgba(167, 167, 167, 0.25) !important;
|
| 267 |
+
color: unset !important;
|
| 268 |
+
}
|
| 269 |
+
.copy-bot-btn:active {
|
| 270 |
+
background-color: rgba(167, 167, 167, 0.5) !important;
|
| 271 |
+
}
|
| 272 |
+
.copy-bot-btn:focus {
|
| 273 |
+
outline: none;
|
| 274 |
+
}
|
| 275 |
|
| 276 |
.message-wrap>div img{
|
| 277 |
border-radius: 10px !important;
|
assets/custom.js
CHANGED
|
@@ -272,6 +272,36 @@ function setChatbotHeight() {
|
|
| 272 |
}
|
| 273 |
}
|
| 274 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 275 |
var rendertime = 0; // for debugging
|
| 276 |
var mathjaxUpdated = false;
|
| 277 |
|
|
@@ -326,12 +356,13 @@ function updateMathJax() {
|
|
| 326 |
|
| 327 |
let timeoutId;
|
| 328 |
let isThrottled = false;
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
|
|
|
| 335 |
if (shouldRenderLatex) {
|
| 336 |
renderMathJax();
|
| 337 |
mathjaxUpdated = false;
|
|
@@ -339,8 +370,8 @@ var mObserver = new MutationObserver(function (mutationsList, observer) {
|
|
| 339 |
saveHistoryHtml();
|
| 340 |
}
|
| 341 |
}
|
| 342 |
-
for (var node of
|
| 343 |
-
if (node.nodeType === 1 && node.classList.contains('message') && node.
|
| 344 |
if (shouldRenderLatex) {
|
| 345 |
renderMathJax();
|
| 346 |
mathjaxUpdated = false;
|
|
@@ -348,25 +379,25 @@ var mObserver = new MutationObserver(function (mutationsList, observer) {
|
|
| 348 |
saveHistoryHtml();
|
| 349 |
}
|
| 350 |
}
|
| 351 |
-
} else if (
|
| 352 |
-
if (
|
| 353 |
if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
|
| 354 |
isThrottled = true;
|
| 355 |
clearTimeout(timeoutId);
|
| 356 |
timeoutId = setTimeout(() => {
|
| 357 |
isThrottled = false;
|
| 358 |
if (shouldRenderLatex) {
|
| 359 |
-
// console.log("changed");
|
| 360 |
renderMathJax();
|
| 361 |
mathjaxUpdated = false;
|
| 362 |
}
|
|
|
|
| 363 |
saveHistoryHtml();
|
| 364 |
}, 500);
|
| 365 |
}
|
| 366 |
}
|
| 367 |
}
|
| 368 |
});
|
| 369 |
-
mObserver.observe(
|
| 370 |
|
| 371 |
var loadhistorytime = 0; // for debugging
|
| 372 |
function saveHistoryHtml() {
|
|
@@ -387,10 +418,20 @@ function loadHistoryHtml() {
|
|
| 387 |
return; // logged in, do nothing
|
| 388 |
}
|
| 389 |
if (!historyLoaded) {
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
historyLoaded = true;
|
| 395 |
console.log("History Loaded");
|
| 396 |
loadhistorytime += 1; // for debugging
|
|
|
|
| 272 |
}
|
| 273 |
}
|
| 274 |
|
| 275 |
+
const copyIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg></span>';
|
| 276 |
+
const copiedIcon = '<span><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" height=".8em" width=".8em" xmlns="http://www.w3.org/2000/svg"><polyline points="20 6 9 17 4 12"></polyline></svg></span>';
|
| 277 |
+
function addCopyBotButton(botElement) {
|
| 278 |
+
var copyButton = null;
|
| 279 |
+
var rawMessage = null;
|
| 280 |
+
rawMessage = botElement.querySelector('.raw-message');
|
| 281 |
+
copyButton = botElement.querySelector('button.copy-bot-btn');
|
| 282 |
+
if (!rawMessage) return;
|
| 283 |
+
if (copyButton) {
|
| 284 |
+
copyButton.remove();
|
| 285 |
+
}
|
| 286 |
+
var button = document.createElement('button');
|
| 287 |
+
button.classList.add('copy-bot-btn');
|
| 288 |
+
button.setAttribute('aria-label', 'Copy');
|
| 289 |
+
button.innerHTML = copyIcon;
|
| 290 |
+
button.addEventListener('click', () => {
|
| 291 |
+
const textToCopy = rawMessage.innerText;
|
| 292 |
+
navigator.clipboard
|
| 293 |
+
.writeText(textToCopy)
|
| 294 |
+
.catch(() => {
|
| 295 |
+
console.error("copy failed");
|
| 296 |
+
});
|
| 297 |
+
button.innerHTML = copiedIcon;
|
| 298 |
+
setTimeout(() => {
|
| 299 |
+
button.innerHTML = copyIcon;
|
| 300 |
+
}, 1500);
|
| 301 |
+
});
|
| 302 |
+
botElement.appendChild(button);
|
| 303 |
+
};
|
| 304 |
+
|
| 305 |
var rendertime = 0; // for debugging
|
| 306 |
var mathjaxUpdated = false;
|
| 307 |
|
|
|
|
| 356 |
|
| 357 |
let timeoutId;
|
| 358 |
let isThrottled = false;
|
| 359 |
+
var mmutation
|
| 360 |
+
// 监听所有元素中 bot message 的变化,用来查找需要渲染的mathjax, 并为 bot 消息添加复制按钮。
|
| 361 |
+
var mObserver = new MutationObserver(function (mutationsList) {
|
| 362 |
+
for (mmutation of mutationsList) {
|
| 363 |
+
if (mmutation.type === 'childList') {
|
| 364 |
+
for (var node of mmutation.addedNodes) {
|
| 365 |
+
if (node.nodeType === 1 && node.classList.contains('message') && node.getAttribute('data-testid') === 'bot') {
|
| 366 |
if (shouldRenderLatex) {
|
| 367 |
renderMathJax();
|
| 368 |
mathjaxUpdated = false;
|
|
|
|
| 370 |
saveHistoryHtml();
|
| 371 |
}
|
| 372 |
}
|
| 373 |
+
for (var node of mmutation.removedNodes) {
|
| 374 |
+
if (node.nodeType === 1 && node.classList.contains('message') && node.getAttribute('data-testid') === 'bot') {
|
| 375 |
if (shouldRenderLatex) {
|
| 376 |
renderMathJax();
|
| 377 |
mathjaxUpdated = false;
|
|
|
|
| 379 |
saveHistoryHtml();
|
| 380 |
}
|
| 381 |
}
|
| 382 |
+
} else if (mmutation.type === 'attributes') {
|
| 383 |
+
if (mmutation.target.nodeType === 1 && mmutation.target.classList.contains('message') && mmutation.target.getAttribute('data-testid') === 'bot') {
|
| 384 |
if (isThrottled) break; // 为了防止重复不断疯狂渲染,加上等待_(:з」∠)_
|
| 385 |
isThrottled = true;
|
| 386 |
clearTimeout(timeoutId);
|
| 387 |
timeoutId = setTimeout(() => {
|
| 388 |
isThrottled = false;
|
| 389 |
if (shouldRenderLatex) {
|
|
|
|
| 390 |
renderMathJax();
|
| 391 |
mathjaxUpdated = false;
|
| 392 |
}
|
| 393 |
+
document.querySelectorAll('#chuanhu_chatbot>.wrap>.message-wrap .message.bot').forEach(addCopyBotButton);
|
| 394 |
saveHistoryHtml();
|
| 395 |
}, 500);
|
| 396 |
}
|
| 397 |
}
|
| 398 |
}
|
| 399 |
});
|
| 400 |
+
mObserver.observe(document.documentElement, { attributes: true, childList: true, subtree: true });
|
| 401 |
|
| 402 |
var loadhistorytime = 0; // for debugging
|
| 403 |
function saveHistoryHtml() {
|
|
|
|
| 418 |
return; // logged in, do nothing
|
| 419 |
}
|
| 420 |
if (!historyLoaded) {
|
| 421 |
+
var tempDiv = document.createElement('div');
|
| 422 |
+
tempDiv.innerHTML = historyHtml;
|
| 423 |
+
var buttons = tempDiv.querySelectorAll('button.copy-bot-btn');
|
| 424 |
+
for (var i = 0; i < buttons.length; i++) {
|
| 425 |
+
buttons[i].parentNode.removeChild(buttons[i]);
|
| 426 |
+
}
|
| 427 |
+
var fakeHistory = document.createElement('div');
|
| 428 |
+
fakeHistory.classList.add('history-message');
|
| 429 |
+
fakeHistory.innerHTML = tempDiv.innerHTML;
|
| 430 |
+
chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
|
| 431 |
+
// var fakeHistory = document.createElement('div');
|
| 432 |
+
// fakeHistory.classList.add('history-message');
|
| 433 |
+
// fakeHistory.innerHTML = historyHtml;
|
| 434 |
+
// chatbotWrap.insertBefore(fakeHistory, chatbotWrap.firstChild);
|
| 435 |
historyLoaded = true;
|
| 436 |
console.log("History Loaded");
|
| 437 |
loadhistorytime += 1; // for debugging
|
modules/utils.py
CHANGED
|
@@ -183,6 +183,7 @@ def convert_mdtext(md_text):
|
|
| 183 |
non_code_parts = code_block_pattern.split(md_text)[::2]
|
| 184 |
|
| 185 |
result = []
|
|
|
|
| 186 |
for non_code, code in zip(non_code_parts, code_blocks + [""]):
|
| 187 |
if non_code.strip():
|
| 188 |
non_code = normalize_markdown(non_code)
|
|
@@ -194,6 +195,7 @@ def convert_mdtext(md_text):
|
|
| 194 |
code = markdown_to_html_with_syntax_highlight(code)
|
| 195 |
result.append(code)
|
| 196 |
result = "".join(result)
|
|
|
|
| 197 |
result += ALREADY_CONVERTED_MARK
|
| 198 |
return result
|
| 199 |
|
|
|
|
| 183 |
non_code_parts = code_block_pattern.split(md_text)[::2]
|
| 184 |
|
| 185 |
result = []
|
| 186 |
+
raw = f'<span class="raw-message">{html.escape(md_text)}</span>'
|
| 187 |
for non_code, code in zip(non_code_parts, code_blocks + [""]):
|
| 188 |
if non_code.strip():
|
| 189 |
non_code = normalize_markdown(non_code)
|
|
|
|
| 195 |
code = markdown_to_html_with_syntax_highlight(code)
|
| 196 |
result.append(code)
|
| 197 |
result = "".join(result)
|
| 198 |
+
result += raw
|
| 199 |
result += ALREADY_CONVERTED_MARK
|
| 200 |
return result
|
| 201 |
|