介绍

image-20241018141541714

因为日常需要做一些记录还有其他吧啦吧啦的事情,并且很多时候需要参考AI给出的方案,但是官方又没有给方法单一的去复制对应的公式。

所以自己写了一个

Github: 复制 ChatGPT 中的 Katex 公式为 Markdown LaTeX 格式

期间有过很多迭代,比如刚开始是下面这样的,右上角会有一个复制按钮。

screenshot

但是用着用着发现不太好用,因为这个页面是一个相对定位(relative)的容器,所以在偏移定位的时候会导致这个聊天框里面出现滑动块,还是挺幽默的。

经过更新:

image-20241018142040697

只是点击就可以复制对应的公式的markdown,举例上述图中的公式:

1
$Z = (X - \text{mean}) / \text{standard deviation}$

如果需要的话,源码可以去Github自取,同时项目保持更新,因为我自个也在用。

原代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// ==UserScript==
// @name Copy ChatGPT KaTeX Formula to Markdown
// @namespace http://github.com/PluginsKers
// @version 1.4.1
// @description Add a copy interaction to KaTeX formulas to copy them as Markdown LaTeX format, with enhanced user interaction and dynamic color scheme support
// @author PluginsKers
// @match https://chat.openai.com/*
// @match https://chatgpt.com/*
// @license MIT
// @grant none
// @downloadURL https://update.greasyfork.org/scripts/501013/Copy%20ChatGPT%20KaTeX%20Formula%20to%20Markdown.user.js
// @updateURL https://update.greasyfork.org/scripts/501013/Copy%20ChatGPT%20KaTeX%20Formula%20to%20Markdown.meta.js
// ==/UserScript==

(function() {
'use strict';

function debug(message) {
console.log(`[KaTeX Copy] ${message}`);
}

const lightSchemeStyles = `
.katex-wrapper:hover:not(.katex-copy-highlight) {
background-color: #e0e0e0;
}
.katex-copy-highlight {
background-color: #ffff99 !important;
transition: background-color 0.2s ease;
}
`;

const darkSchemeStyles = `
.katex-wrapper:hover:not(.katex-copy-highlight) {
background-color: #444;
}
.katex-copy-highlight {
background-color: #888 !important;
transition: background-color 0.2s ease;
}
`;

const style = document.createElement('style');
document.head.appendChild(style);

function updateStyles(colorScheme) {
if (colorScheme === 'dark') {
style.textContent = darkSchemeStyles;
} else {
style.textContent = lightSchemeStyles;
}
}

// Initial style update based on current color scheme
const initialColorScheme = document.documentElement.style.getPropertyValue('color-scheme') || 'light';
updateStyles(initialColorScheme);

// Observer to monitor changes in color-scheme attribute
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'style') {
const newColorScheme = document.documentElement.style.getPropertyValue('color-scheme') || 'light';
updateStyles(newColorScheme);
}
});
});

observer.observe(document.documentElement, { attributes: true });

// Function to add copy interaction to a single KaTeX element
function addCopyInteraction(katexElement) {
if (katexElement.classList.contains('copy-interaction-added')) return;

const wrapper = document.createElement('span');
wrapper.classList.add('katex-wrapper');

katexElement.parentNode.insertBefore(wrapper, katexElement);
wrapper.appendChild(katexElement);

wrapper.addEventListener('click', async () => {
const annotation = katexElement.querySelector('.katex-mathml annotation');
if (!annotation) {
alert('Error: Could not find LaTeX source');
return;
}
const latexSource = annotation.textContent;
const markdownLatex = `$${latexSource}$`;
await navigator.clipboard.writeText(markdownLatex);

// Highlight the background briefly
wrapper.classList.add('katex-copy-highlight');
setTimeout(() => {
wrapper.classList.remove('katex-copy-highlight');
}, 100); // Shorten the highlight duration
});

katexElement.classList.add('copy-interaction-added');
}

// Function to add copy interactions to all KaTeX elements
function addCopyInteractions() {
document.querySelectorAll('div.markdown .katex').forEach(addCopyInteraction);
}

// Function to wait for page stability
function waitForPageStability(callback, duration = 1000) {
let timer;
const observer = new MutationObserver(() => {
clearTimeout(timer);
timer = setTimeout(() => {
observer.disconnect();
callback();
}, duration);
});
observer.observe(document.body, { childList: true, subtree: true });
timer = setTimeout(() => {
observer.disconnect();
callback();
}, duration);
}

// Function to monitor for new elements
function monitorNewElements() {
const observer = new MutationObserver(() => {
if (!document.querySelector('.result-streaming')) {
addCopyInteractions();
}
});
observer.observe(document.body, { childList: true, subtree: true });
}

// Initial execution after page stability
waitForPageStability(() => {
addCopyInteractions();
monitorNewElements();
});
})();