Skip to content

Commit 7952737

Browse files
committed
fix: 修复聊天面板缺失文件
1 parent a3072ee commit 7952737

File tree

2 files changed

+263
-0
lines changed

2 files changed

+263
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ llms/paraphrase-multilingual-MiniLM-L12-v2_quint8_avx2.onnx
2323
# 测试文件
2424
*.js.map
2525
*.js
26+
!src/**/*.js
2627
.vscode-test
2728

2829

src/resources/chatPanelScript.js

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
// src/resources/webviewScript.js
2+
3+
// 初始化 Mermaid 和代码高亮
4+
function initializeLibraries() {
5+
mermaid.initialize({ startOnLoad: false, theme: 'dark' });
6+
hljs.configure({ ignoreUnescapedHTML: true });
7+
}
8+
9+
// 设置编辑按钮功能
10+
function setupEditButtons() {
11+
document.querySelectorAll('.edit-btn').forEach(btn => {
12+
btn.onclick = (event) => {
13+
const userDiv = event.target.closest('.user');
14+
const contentDiv = userDiv.querySelector('.user-content');
15+
userDiv.innerHTML = `
16+
<textarea class="edit-textarea" style="width:100%; min-height:100px; resize:vertical; margin-bottom:8px; padding:8px; box-sizing:border-box;">${contentDiv.textContent}</textarea>
17+
<div class="edit-buttons" style="display:flex; gap:8px; justify-content:flex-end;">
18+
<button class="edit-send" style="padding:6px 12px;">发送</button>
19+
<button class="edit-cancel" style="padding:6px 12px;">取消</button>
20+
</div>`;
21+
22+
const editSend = userDiv.querySelector('.edit-send');
23+
const editCancel = userDiv.querySelector('.edit-cancel');
24+
25+
editSend.onclick = () => {
26+
const newText = userDiv.querySelector('textarea').value;
27+
vscode.postMessage({ command: 'editMessage', index: parseInt(userDiv.dataset.index), text: newText });
28+
userDiv.innerHTML = `<button class="edit-btn">✎</button><div class="user-content">${newText}</div>`;
29+
setupEditButtons();
30+
};
31+
32+
editCancel.onclick = () => {
33+
userDiv.innerHTML = `<button class="edit-btn">✎</button><div class="user-content">${contentDiv.textContent}</div>`;
34+
setupEditButtons();
35+
};
36+
};
37+
});
38+
}
39+
40+
// 设置复制按钮功能(事件委托)
41+
function setupCopyButtonDelegation() {
42+
chat.addEventListener('click', (event) => {
43+
const copyBtn = event.target.closest('.copy-btn');
44+
if (!copyBtn) {return; }
45+
46+
const preElement = copyBtn.closest('pre');
47+
if (!preElement) {return; }
48+
49+
const code = preElement.querySelector('code').textContent;
50+
navigator.clipboard.writeText(code)
51+
.then(() => {
52+
copyBtn.textContent = 'Copied!';
53+
setTimeout(() => copyBtn.textContent = 'Copy', 2000);
54+
})
55+
.catch(err => console.error('Copy failed:', err));
56+
});
57+
}
58+
59+
// 渲染数学公式
60+
function fnRenderDisplayMath(webviewDiv) {
61+
const strRawHtml = webviewDiv.innerHTML;
62+
const rgxDisplayMath = /\$\$([\s\S]+?)\$\$/g;
63+
const strReplacedHtml = strRawHtml.replace(rgxDisplayMath, (strMatch, strInnerTex) => {
64+
try {
65+
const strTex = strInnerTex.replace(/^\s+|\s+$/g, '');
66+
return katex.renderToString(strTex, {
67+
displayMode: true,
68+
throwOnError: false
69+
});
70+
} catch (err) {
71+
console.error('KaTeX render error:', err);
72+
return strMatch;
73+
}
74+
});
75+
webviewDiv.innerHTML = strReplacedHtml;
76+
}
77+
78+
// 渲染 Mermaid 图表
79+
async function renderMermaid(webviewDiv) {
80+
const codeBlocks = webviewDiv.querySelectorAll('pre code.language-mermaid');
81+
for (const codeBlock of codeBlocks) {
82+
const parentPre = codeBlock.closest('pre');
83+
const mermaidCode = codeBlock.textContent;
84+
try {
85+
const { svg } = await mermaid.render('mermaid-diagram-' + Date.now(), mermaidCode);
86+
const mermaidDiv = document.createElement('div');
87+
mermaidDiv.className = 'mermaid';
88+
mermaidDiv.innerHTML = svg;
89+
90+
const rawDiv = document.createElement('div');
91+
rawDiv.className = 'mermaid-raw';
92+
rawDiv.innerHTML = `<pre><code class="language-mermaid">${mermaidCode}</code></pre>`;
93+
94+
const container = document.createElement('div');
95+
container.className = 'mermaid-container';
96+
container.appendChild(mermaidDiv);
97+
container.appendChild(rawDiv);
98+
99+
parentPre.replaceWith(container);
100+
} catch (err) {
101+
console.error('Mermaid render error:', err);
102+
const errorDiv = document.createElement('div');
103+
errorDiv.className = 'mermaid-error';
104+
errorDiv.textContent = 'Failed to render Mermaid diagram';
105+
parentPre.replaceWith(errorDiv);
106+
}
107+
}
108+
}
109+
110+
// 切换 Mermaid 显示模式
111+
function toggleMermaidDisplay() {
112+
const containers = document.querySelectorAll('.mermaid-container');
113+
if (mermaidToggle.checked) {
114+
containers.forEach(container => container.classList.add('mermaid-rendered'));
115+
} else {
116+
containers.forEach(container => container.classList.remove('mermaid-rendered'));
117+
}
118+
}
119+
120+
// 确保复制按钮存在
121+
function ensureCopyButtons() {
122+
document.querySelectorAll('.model pre').forEach(pre => {
123+
if (!pre.querySelector('.copy-btn')) {
124+
const button = document.createElement('button');
125+
button.className = 'copy-btn';
126+
button.textContent = 'Copy';
127+
pre.appendChild(button);
128+
}
129+
});
130+
}
131+
132+
// 渲染消息
133+
async function renderMessage(role, content, index) {
134+
const lastChild = chat.lastElementChild;
135+
let targetDiv;
136+
137+
if (lastChild && lastChild.classList.contains(role)) {
138+
targetDiv = lastChild;
139+
targetDiv.dataset.markdownContent += content;
140+
} else {
141+
targetDiv = document.createElement('div');
142+
targetDiv.className = role;
143+
targetDiv.dataset.markdownContent = content;
144+
chat.appendChild(targetDiv);
145+
}
146+
147+
if (role === 'model') {
148+
targetDiv.innerHTML = marked.parse(targetDiv.dataset.markdownContent, {
149+
breaks: false,
150+
mangle: false,
151+
headerIds: false,
152+
highlight: (code, lang) => hljs.highlight(hljs.getLanguage(lang) ? lang : 'plaintext', code).value
153+
});
154+
155+
fnRenderDisplayMath(targetDiv);
156+
await renderMermaid(targetDiv);
157+
158+
renderMathInElement(targetDiv, {
159+
delimiters: [
160+
{ left: '$$', right: '$$', display: true },
161+
{ left: '$', right: '$', display: false },
162+
{ left: '\\[', right: '\\]', display: true },
163+
{ left: '\\(', right: '\\)', display: false }
164+
],
165+
throwOnError: false
166+
});
167+
ensureCopyButtons();
168+
hljs.highlightAll();
169+
} else {
170+
targetDiv.innerHTML = `<button class="edit-btn">✎</button><div class="user-content">${targetDiv.dataset.markdownContent}</div>`;
171+
targetDiv.dataset.index = index;
172+
setupEditButtons();
173+
}
174+
chat.scrollTop = chat.scrollHeight;
175+
}
176+
177+
// 消息处理
178+
function setupMessageHandlers() {
179+
window.addEventListener('message', async (event) => {
180+
const data = event.data;
181+
182+
if (data.role && data.content) {
183+
await renderMessage(data.role, data.content, data.index);
184+
return;
185+
}
186+
187+
if (!data.command) { return; }
188+
189+
switch (data.command) {
190+
case 'disableSendButton':
191+
sendButton.disabled = true;
192+
break;
193+
case 'enableSendButton':
194+
sendButton.disabled = false;
195+
break;
196+
case 'showStopButton':
197+
stopButton.style.display = 'inline-block';
198+
break;
199+
case 'hideStopButton':
200+
stopButton.style.display = 'none';
201+
break;
202+
case 'clearAfterIndex':
203+
const clearIndex = data.index;
204+
document.querySelectorAll('.user').forEach(userDiv => {
205+
if (parseInt(userDiv.dataset.index) >= clearIndex) {
206+
const modelDiv = userDiv.nextElementSibling;
207+
if (modelDiv?.classList.contains('model')) { modelDiv.remove(); }
208+
userDiv.remove();
209+
}
210+
});
211+
break;
212+
}
213+
});
214+
215+
mermaidToggle.addEventListener('change', toggleMermaidDisplay);
216+
}
217+
218+
// 输入处理
219+
function setupInputHandlers() {
220+
sendButton.addEventListener('click', sendMessage);
221+
newSessionButton.addEventListener('click', startNewSession);
222+
stopButton.addEventListener('click', stopOperation);
223+
input.addEventListener('keydown', handleKeyDown);
224+
}
225+
226+
function sendMessage() {
227+
const text = input.value.trim();
228+
if (text) {
229+
vscode.postMessage({ command: 'sendMessage', text, webSearch: webSearchCheckbox.checked });
230+
input.value = '';
231+
}
232+
}
233+
234+
function startNewSession() {
235+
vscode.postMessage({ command: 'newSession' });
236+
chat.innerHTML = '';
237+
sendButton.disabled = false;
238+
stopButton.style.display = 'none';
239+
}
240+
241+
function stopOperation() {
242+
vscode.postMessage({ command: 'stop' });
243+
}
244+
245+
function handleKeyDown(e) {
246+
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
247+
sendMessage();
248+
}
249+
}
250+
251+
// 主初始化函数
252+
function initializeWebview() {
253+
// 初始化库和事件监听
254+
initializeLibraries();
255+
setupMessageHandlers();
256+
setupInputHandlers();
257+
setupEditButtons();
258+
setupCopyButtonDelegation();
259+
}
260+
261+
initializeWebview();
262+

0 commit comments

Comments
 (0)