Skip to content

Commit 261cc07

Browse files
Merge branch 'dev'
2 parents 74a5fec + 505e94f commit 261cc07

File tree

7 files changed

+146
-63
lines changed

7 files changed

+146
-63
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@
1111
- [x] **Markdown 渲染**:支持以 Markdown 格式渲染 AI 回复,便于查看包括列表、代码块等格式化内容。
1212
- [x] **一键复制**:一键将 AI 回复复制到剪贴板,方便快速使用。
1313
- [x] **重新回答选项**:如果对 AI 回复不满意,用户可以轻松重新提问,获取新的回答。
14+
- [x] **代码复制功能**:增强代码片段复制功能。
15+
- [x] **语言切换功能**:允许用户根据偏好切换回答的语言类型。
16+
17+
1418

1519
## 即将实现的功能
1620

17-
- [ ] **代码复制功能**:增强代码片段复制功能。
1821
- [ ] **多轮对话**:支持多轮对话功能,使用户可以与 AI 进行连续的深度互动,不局限于一次性问题。
19-
- [ ] **语言切换功能**:允许用户根据偏好切换回答的语言类型。
2022
- [ ] **更多功能**:根据用户的需求和反馈,我们将在未来的版本中添加更多功能。
2123

2224
## 安装步骤

src/background.js

+23-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
11
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
2-
if (request.action === "getApiKey") {
3-
chrome.storage.sync.get('apiKey', function(data) {
4-
sendResponse({apiKey: data.apiKey});
2+
if (request.action === "getApiKeyAndLanguage") {
3+
chrome.storage.sync.get(["apiKey", "language"], function (data) {
4+
sendResponse({ apiKey: data.apiKey, language: data.language || "zh" });
55
});
6-
return true; // 保持消息通道开放,因为 sendResponse 是异步的
6+
return true; // Keep the message channel open for asynchronous response
77
}
8-
});
8+
});
9+
// Create context menu on extension installation
10+
chrome.runtime.onInstalled.addListener(() => {
11+
chrome.contextMenus.create({
12+
id: "createPopup",
13+
title: "DeepSeek AI",
14+
contexts: ["selection"],
15+
});
16+
});
17+
18+
// Handle context menu clicks
19+
chrome.contextMenus.onClicked.addListener((info, tab) => {
20+
if (info.menuItemId === "createPopup") {
21+
chrome.tabs.sendMessage(tab.id, {
22+
action: "createPopup",
23+
selectedText: info.selectionText,
24+
});
25+
}
26+
});

src/content/api.js

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { md } from "./markdown";
22
import { getAllowAutoScroll } from "./scrollControl";
3+
34
export async function getAIResponse(
45
text,
56
responseElement,
@@ -12,8 +13,9 @@ export async function getAIResponse(
1213
let allowAutoScroll = true;
1314
iconContainer.style.display = "none";
1415
iconContainer.dataset.ready = "false";
15-
const { apiKey } = await new Promise((resolve) => {
16-
chrome.runtime.sendMessage({ action: "getApiKey" }, resolve);
16+
17+
const { apiKey, language } = await new Promise((resolve) => {
18+
chrome.runtime.sendMessage({ action: "getApiKeyAndLanguage" }, resolve);
1719
});
1820

1921
if (!apiKey) {
@@ -34,8 +36,11 @@ export async function getAIResponse(
3436
messages: [
3537
{
3638
role: "system",
37-
content:
38-
"你是一个有帮助的AI助手,无论用户选择哪种语言,你都能够以用户选择的语言提供文本回答。",
39+
content: `You are a helpful AI assistant. ${
40+
language === "auto"
41+
? "Please detect the language of the user's input and respond in the same language."
42+
: `The user's preferred language is ${language}. Regardless of the input language, you must respond in ${language} from now on.`
43+
} Always prioritize clear and effective communication.`,
3944
},
4045
{ role: "user", content: text },
4146
],
@@ -90,7 +95,7 @@ export async function getAIResponse(
9095
iconContainer.dataset.ready = "true";
9196
} catch (error) {
9297
console.error("Fetch error:", error);
93-
responseElement.innerHTML = "请求失败,请稍后重试。";
98+
responseElement.innerHTML = "Request failed. Please try again later.";
9499
}
95100
}
96101

src/content/content.js

+51-37
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,35 @@
1-
import { createIcon, createSvgIcon } from "./icon";
2-
import { createPopup, stylePopup, styleResponseContainer } from "./popup";
3-
1+
import { createIcon } from "./icon";
2+
import { createPopup } from "./popup";
43
import "perfect-scrollbar/css/perfect-scrollbar.css";
54

65
let aiResponseContainer;
76
let isCreatingPopup = false;
7+
let canCreateIcon = true;
88

99
const link = document.createElement("link");
1010
link.rel = "stylesheet";
1111
link.href = chrome.runtime.getURL("style.css");
1212
document.head.appendChild(link);
1313

14+
// Function to handle popup creation
15+
function handlePopupCreation(selectedText, rect) {
16+
if (isCreatingPopup) return;
17+
isCreatingPopup = true;
18+
canCreateIcon = false;
19+
20+
// Create popup
21+
createPopup(selectedText, rect);
22+
23+
// Reset flags after a short delay
24+
setTimeout(() => {
25+
isCreatingPopup = false;
26+
canCreateIcon = true;
27+
}, 100);
28+
}
29+
30+
// Handle mouseup event to create icon
1431
document.addEventListener("mouseup", function (event) {
15-
if (isCreatingPopup) return; // 如果正在创建 popup,不执行后续操作
32+
if (isCreatingPopup || !canCreateIcon) return;
1633

1734
const selectedText = window.getSelection().toString().trim();
1835
if (selectedText && event.button === 0) {
@@ -22,57 +39,55 @@ document.addEventListener("mouseup", function (event) {
2239

2340
document.body.appendChild(icon);
2441

42+
// Handle icon click event
2543
icon.addEventListener("click", function iconClickHandler(e) {
2644
e.stopPropagation();
2745
e.preventDefault();
2846

29-
if (isCreatingPopup) return; // 防止重复创建
30-
isCreatingPopup = true;
31-
32-
// 立即移除事件监听器,防止多次触发
33-
icon.removeEventListener("click", iconClickHandler);
47+
// Clear selection
48+
window.getSelection().removeAllRanges();
3449

35-
createPopup(selectedText, rect);
36-
37-
// 使用 setTimeout 来确保 popup 创建后再移除图标和选择
38-
setTimeout(() => {
50+
// Remove icon and create popup
51+
requestAnimationFrame(() => {
3952
if (document.body.contains(icon)) {
4053
document.body.removeChild(icon);
4154
}
42-
window.getSelection().removeAllRanges();
43-
isCreatingPopup = false; // 重置标志
44-
}, 100);
55+
handlePopupCreation(selectedText, rect);
56+
});
4557
});
4658

47-
document.addEventListener("mousedown", function documentClickHandler(e) {
48-
if (!isCreatingPopup && !icon.contains(e.target)) {
59+
// Handle document mousedown event
60+
const documentClickHandler = function (e) {
61+
if (e.button === 2 && !icon.contains(e.target)) {
62+
// Right-click, only remove icon
63+
document.body.removeChild(icon);
64+
e.preventDefault();
65+
} else if (e.button === 0 && !icon.contains(e.target)) {
66+
// Left-click, remove icon and selection
4967
document.body.removeChild(icon);
5068
window.getSelection().removeAllRanges();
5169
}
52-
// 移除事件监听器
5370
document.removeEventListener("mousedown", documentClickHandler);
54-
});
71+
canCreateIcon = true;
72+
};
73+
74+
document.addEventListener("mousedown", documentClickHandler);
5575
}
5676
});
5777

58-
function handleDocumentClick(event, icon, range) {
59-
if (isCreatingPopup) return; // 如果正在创建 popup,不执行后续操作
60-
61-
const selection = window.getSelection();
62-
const clickedIcon = icon.contains(event.target);
63-
const clickedInsideSelection = isClickInsideSelection(event, range);
64-
65-
if (!clickedIcon && !clickedInsideSelection) {
66-
document.body.removeChild(icon);
67-
selection.removeAllRanges();
68-
} else if (event.button === 2 && clickedInsideSelection) {
69-
document.body.removeChild(icon);
70-
} else if (event.button === 0 && clickedInsideSelection) {
71-
document.body.removeChild(icon);
72-
selection.removeAllRanges();
78+
// Listen for messages from the background script
79+
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
80+
if (request.action === "createPopup") {
81+
const selection = window.getSelection();
82+
if (selection.rangeCount > 0) {
83+
const range = selection.getRangeAt(0);
84+
const rect = range.getBoundingClientRect();
85+
handlePopupCreation(request.selectedText, rect);
86+
}
7387
}
74-
}
88+
});
7589

90+
// Helper function to check if click is inside selection
7691
function isClickInsideSelection(event, range) {
7792
const rect = range.getBoundingClientRect();
7893
return (
@@ -82,4 +97,3 @@ function isClickInsideSelection(event, range) {
8297
event.clientY <= rect.bottom
8398
);
8499
}
85-

src/manifest.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
"manifest_version": 3,
33
"name": "DeepSeek AI",
44
"description": "DeepSeek AI助手旨在为用户提供即时、智能的网页内容分析与回答服务。通过集成先进的人工智能大模型,用户可以轻松选择网页上的任何文本内容,并立即获得深度、准确的AI回答。",
5-
"version": "1.2.1",
6-
"permissions": ["storage"],
5+
"version": "1.3.0",
6+
"permissions": ["storage", "contextMenus"],
77
"content_scripts": [
88
{
99
"matches": ["<all_urls>"],

src/popup/popup.html

+36-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@
1414
width: 300px;
1515
color: #1d1d1f;
1616
}
17+
.language-switch {
18+
display: flex;
19+
justify-content: center;
20+
align-items: center;
21+
margin-bottom: 16px;
22+
}
23+
.language-switch label {
24+
margin-right: 10px;
25+
font-size: 14px;
26+
}
27+
select {
28+
padding: 4px 8px;
29+
font-size: 14px;
30+
border: 1px solid #d2d2d7;
31+
border-radius: 8px;
32+
background-color: #f2f2f7;
33+
}
1734
.header {
1835
display: flex;
1936
align-items: center;
@@ -50,7 +67,6 @@
5067
width: 100%;
5168
position: relative;
5269
text-align: center;
53-
5470
}
5571
input {
5672
width: 60%;
@@ -59,13 +75,13 @@
5975
font-size: 12px;
6076
border: 1px solid #d2d2d7;
6177
border-radius: 12px;
62-
background-color: #F2F2F7;
78+
background-color: #f2f2f7;
6379
transition: all 0.3s ease;
6480
margin-bottom: 12px;
6581
}
6682

6783
input::placeholder {
68-
color: #A9A9A9;
84+
color: #a9a9a9;
6985
}
7086
input:focus {
7187
outline: none;
@@ -140,6 +156,23 @@
140156
>
141157
前往获取 API Key
142158
</a>
159+
<div class="language-switch">
160+
<label for="language">首选语言:</label>
161+
<select id="language">
162+
<option value="auto">自动检测</option>
163+
<option value="zh">中文</option>
164+
<option value="en">English</option>
165+
<option value="es">Español</option>
166+
<option value="fr">Français</option>
167+
<option value="de">Deutsch</option>
168+
<option value="ja">日本語</option>
169+
<option value="ko">한국어</option>
170+
<option value="ru">Русский</option>
171+
<option value="ar">العربية</option>
172+
<option value="hi">हिन्दी</option>
173+
<!-- Add more languages as needed -->
174+
</select>
175+
</div>
143176
<button id="saveButton">保存</button>
144177
</div>
145178
</div>

src/popup/popup.js

+20-9
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ document.addEventListener("DOMContentLoaded", function () {
44
const errorMessage = document.getElementById("errorMessage");
55
const toggleButton = document.getElementById("toggleVisibility");
66
const iconSwitch = document.getElementById("iconSwitch");
7-
// 加载已保存的API密钥
8-
chrome.storage.sync.get("apiKey", function (data) {
7+
const languageSelect = document.getElementById("language");
8+
9+
// Load saved API key and language preference
10+
chrome.storage.sync.get(["apiKey", "language"], function (data) {
911
if (data.apiKey) {
1012
apiKeyInput.value = data.apiKey;
1113
}
14+
if (data.language) {
15+
languageSelect.value = data.language;
16+
}
1217
});
18+
1319
toggleButton.addEventListener("click", function () {
1420
if (apiKeyInput.type === "password") {
1521
apiKeyInput.type = "text";
@@ -19,6 +25,7 @@ document.addEventListener("DOMContentLoaded", function () {
1925
iconSwitch.src = "../icons/show.svg";
2026
}
2127
});
28+
2229
function validateApiKey(apiKey, callback) {
2330
fetch("https://api.deepseek.com/chat/completions", {
2431
method: "POST",
@@ -47,9 +54,10 @@ document.addEventListener("DOMContentLoaded", function () {
4754
});
4855
}
4956

50-
// 保存API密钥
57+
// Save API key and language preference
5158
saveButton.addEventListener("click", function () {
5259
const apiKey = apiKeyInput.value.trim();
60+
const language = languageSelect.value;
5361

5462
if (apiKey === "") {
5563
errorMessage.textContent = "api-key不能为空!";
@@ -58,12 +66,15 @@ document.addEventListener("DOMContentLoaded", function () {
5866

5967
validateApiKey(apiKey, function (isValid) {
6068
if (isValid) {
61-
chrome.storage.sync.set({ apiKey: apiKey }, function () {
62-
errorMessage.textContent = "api-key已保存!";
63-
setTimeout(function () {
64-
errorMessage.textContent = "";
65-
}, 2000);
66-
});
69+
chrome.storage.sync.set(
70+
{ apiKey: apiKey, language: language },
71+
function () {
72+
errorMessage.textContent = "设置已保存!";
73+
setTimeout(function () {
74+
errorMessage.textContent = "";
75+
}, 2000);
76+
}
77+
);
6778
} else {
6879
errorMessage.textContent = "api-key无效!";
6980
}

0 commit comments

Comments
 (0)