Skip to content

Commit 3dec087

Browse files
committed
init
1 parent ddff224 commit 3dec087

21 files changed

+433
-172
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@
2121
npm-debug.log*
2222
yarn-debug.log*
2323
yarn-error.log*
24+
25+
.idea/
26+
.idea/*

package-lock.json

+103
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
"@testing-library/jest-dom": "^5.16.5",
77
"@testing-library/react": "^13.4.0",
88
"@testing-library/user-event": "^13.5.0",
9+
"autoprefixer": "^10.4.14",
10+
"install": "^0.13.0",
11+
"postcss": "^8.4.24",
912
"react": "^18.2.0",
1013
"react-dom": "^18.2.0",
14+
"react-icons": "^4.9.0",
1115
"react-scripts": "5.0.1",
16+
"tailwindcss": "^3.3.2",
1217
"web-vitals": "^2.1.4"
1318
},
1419
"scripts": {

public/background.js

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
chrome.runtime.onInstalled.addListener(() => {
2+
chrome.contextMenus.create({
3+
id: "add-to-anki",
4+
title: "Create Anki card",
5+
contexts: ["selection"],
6+
});
7+
});
8+
9+
chrome.contextMenus.onClicked.addListener(handleContextMenu);
10+
11+
function handleContextMenu(info) {
12+
if (info.menuItemId === "add-to-anki") {
13+
const text = "请将下列描述提取成anki卡片的形式, 描述: " + info.selectionText + "\n并且以[{\"front\": \"xxx\", \"back\": \"xxx\"}]格式返回";
14+
getGPT3Result(text).then((result) => {
15+
saveAnki(result);
16+
});
17+
}
18+
}
19+
20+
async function getGPT3Result(text) {
21+
const config = await getAnkiConfig();
22+
const GPT_API_KEY = config.openAIKey;
23+
24+
const response = await fetch("https://api.openai.com/v1/chat/completions", {
25+
method: "POST",
26+
headers: {
27+
"Content-Type": "application/json",
28+
Authorization: "Bearer " + GPT_API_KEY,
29+
},
30+
body: JSON.stringify({
31+
model: "gpt-3.5-turbo",
32+
messages: [{content: text, role: "user"}]
33+
})
34+
});
35+
const data = await response.json();
36+
const ret = data.choices[0].message.content.trim();
37+
return ret;
38+
}
39+
40+
async function saveAnki(result) {
41+
const resultArray = JSON.parse(result);
42+
43+
const today = new Date();
44+
const year = today.getFullYear();
45+
const month = ('0' + (today.getMonth() + 1)).slice(-2); // Month is zero-indexed
46+
const day = ('0' + today.getDate()).slice(-2);
47+
const today_time_str = `${year}-${month}-${day}`;
48+
49+
const config = await getAnkiConfig();
50+
const ANKI_DESK_NAME = config.ankiDeskName;
51+
const ANKI_SERVER_ADDRESS = config.ankiServerAddress;
52+
53+
const requestData = {
54+
action: "addNote",
55+
version: 6,
56+
params: {
57+
note: {
58+
deckName: ANKI_DESK_NAME,
59+
modelName: "Basic",
60+
fields: {
61+
Front: "",
62+
Back: ""
63+
},
64+
options: {
65+
allowDuplicate: false,
66+
duplicateScope: "deck",
67+
duplicateScopeOptions: {
68+
deckName: "Default",
69+
checkChildren: false,
70+
checkAllModels: false
71+
}
72+
},
73+
tags: [today_time_str],
74+
}
75+
}
76+
};
77+
78+
for (let i = 0; i < resultArray.length; i++) {
79+
const currentResult = resultArray[i];
80+
requestData.params.note.fields.Front = currentResult.front;
81+
requestData.params.note.fields.Back = currentResult.back;
82+
83+
fetch(ANKI_SERVER_ADDRESS, {
84+
method: "POST",
85+
modelName: "Basic",
86+
headers: {
87+
"Content-Type": "application/json",
88+
},
89+
body: JSON.stringify(requestData),
90+
})
91+
.then((response) => {
92+
if (response.ok) {
93+
console.log("Note added to Anki!");
94+
showNotification("add anki success");
95+
} else {
96+
console.error("Failed to add note to Anki:", response);
97+
showNotification("add anki failed");
98+
}
99+
})
100+
.catch((error) => {
101+
console.error("Failed to add note to Anki:", error);
102+
});
103+
}
104+
}
105+
106+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
107+
if (message.type === 'SAVE_ANKI_CONFIG') {
108+
chrome.storage.local.set({ankiConfig: message.config});
109+
console.log("set anki config success");
110+
}
111+
});
112+
113+
function getAnkiConfig() {
114+
return new Promise((resolve, reject) => {
115+
chrome.storage.local.get("ankiConfig", (result) => {
116+
if (result.ankiConfig) {
117+
resolve(result.ankiConfig);
118+
} else {
119+
resolve({
120+
openAIKey: "your_openai_key",
121+
ankiDeskName: "your_anki_desk_name",
122+
ankiServerAddress: "your_anki_server_address",
123+
});
124+
}
125+
});
126+
});
127+
}
128+
129+
function showNotification(message) {
130+
chrome.notifications.create({
131+
type: "basic",
132+
title: "Anki Card Created",
133+
message: message,
134+
iconUrl: "unload.png",
135+
});
136+
}

public/favicon.ico

-3.78 KB
Binary file not shown.

public/index.html

+1-34
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,9 @@
22
<html lang="en">
33
<head>
44
<meta charset="utf-8" />
5-
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
6-
<meta name="viewport" content="width=device-width, initial-scale=1" />
7-
<meta name="theme-color" content="#000000" />
8-
<meta
9-
name="description"
10-
content="Web site created using create-react-app"
11-
/>
12-
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
13-
<!--
14-
manifest.json provides metadata used when your web app is installed on a
15-
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
16-
-->
17-
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
18-
<!--
19-
Notice the use of %PUBLIC_URL% in the tags above.
20-
It will be replaced with the URL of the `public` folder during the build.
21-
Only files inside the `public` folder can be referenced from the HTML.
22-
23-
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24-
work correctly both with client-side routing and a non-root public URL.
25-
Learn how to configure a non-root public URL by running `npm run build`.
26-
-->
27-
<title>React App</title>
5+
<title>Anki card with chatgpt</title>
286
</head>
297
<body>
30-
<noscript>You need to enable JavaScript to run this app.</noscript>
318
<div id="root"></div>
32-
<!--
33-
This HTML file is a template.
34-
If you open it directly in the browser, you will see an empty page.
35-
36-
You can add webfonts, meta tags, or analytics to this file.
37-
The build step will place the bundled scripts into the <body> tag.
38-
39-
To begin the development, run `npm start` or `yarn start`.
40-
To create a production bundle, use `npm run build` or `yarn build`.
41-
-->
429
</body>
4310
</html>

public/logo192.png

-5.22 KB
Binary file not shown.

public/logo512.png

-9.44 KB
Binary file not shown.

0 commit comments

Comments
 (0)