├── docs ├── anki.png ├── config.png └── select.png ├── public ├── unload.png ├── index.html ├── manifest.json └── background.js ├── tailwind.config.js ├── src ├── index.js ├── index.css └── app.js ├── .gitignore ├── README.md ├── README-en.md └── package.json /docs/anki.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mggger/chatgpt-anki-chrome-extension/HEAD/docs/anki.png -------------------------------------------------------------------------------- /docs/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mggger/chatgpt-anki-chrome-extension/HEAD/docs/config.png -------------------------------------------------------------------------------- /docs/select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mggger/chatgpt-anki-chrome-extension/HEAD/docs/select.png -------------------------------------------------------------------------------- /public/unload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mggger/chatgpt-anki-chrome-extension/HEAD/public/unload.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 |
22 |
23 | 3. 当遇到感兴趣知识点的时候,选中文本, 通过chatgpt总结成anki卡片
24 |
25 |
26 |
27 | 4. 完成后,可以在anki里面开始背诵了
28 |
29 | 
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 3,
3 | "name": "Anki card creator",
4 | "version": "1.0",
5 | "description": "Create Anki cards from selected text",
6 | "permissions": ["tabs", "contextMenus", "storage", "notifications", "activeTab"],
7 | "background": {
8 | "service_worker": "background.js"
9 | },
10 | "options_ui": {
11 | "page": "index.html",
12 | "open_in_tab": true
13 | },
14 | "icons": {
15 | "48": "unload.png"
16 | },
17 | "action": {
18 | "default_popup": "index.html",
19 | "default_title": "anki extractor",
20 | "default_icon": {
21 | "16": "unload.png",
22 | "32": "unload.png",
23 | "48": "unload.png",
24 | "128": "unload.png"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/README-en.md:
--------------------------------------------------------------------------------
1 | # Anki Card Creator
2 |
3 | Create Anki cards using ChatGPT
4 |
5 | This project originates from:
6 |
7 | 1. When surfing online, collect valuable fragmented information and use ChatGPT to organize it into Anki cards.
8 | 2. Studying for exams/certifications or memorizing standard answers for interviews.
9 |
10 | ## How to Use
11 |
12 | 1. Install the Chrome extension through the zip package: [Download Link](https://github.com/mggger/chatgpt-anki-chrome-extension/files/11913090/v0.1.zip)
13 |
14 | 2. Set up the relevant configurations and save them.
15 |
16 | 
17 |
18 | 3. When you come across points of interest, select the text and use ChatGPT to summarize it into an Anki card.
19 |
20 | 
21 |
22 | 4. Once done, you can start revising it in Anki.
23 |
24 | 
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "chatgpt-docs-anki",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "autoprefixer": "^10.4.14",
10 | "install": "^0.13.0",
11 | "postcss": "^8.4.24",
12 | "react": "^18.2.0",
13 | "react-dom": "^18.2.0",
14 | "react-icons": "^4.9.0",
15 | "react-scripts": "5.0.1",
16 | "tailwindcss": "^3.3.2",
17 | "web-vitals": "^2.1.4"
18 | },
19 | "scripts": {
20 | "start": "react-scripts start",
21 | "build": "react-scripts build",
22 | "test": "react-scripts test",
23 | "eject": "react-scripts eject"
24 | },
25 | "eslintConfig": {
26 | "extends": [
27 | "react-app",
28 | "react-app/jest"
29 | ]
30 | },
31 | "browserslist": {
32 | "production": [
33 | ">0.2%",
34 | "not dead",
35 | "not op_mini all"
36 | ],
37 | "development": [
38 | "last 1 chrome version",
39 | "last 1 firefox version",
40 | "last 1 safari version"
41 | ]
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/public/background.js:
--------------------------------------------------------------------------------
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 | }
137 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | /*global chrome*/
2 | import React, {useState, useEffect} from 'react';
3 | import {FaEye, FaEyeSlash} from 'react-icons/fa';
4 |
5 |
6 | const AnkiPage = () => {
7 | const [openAIKey, setOpenAIKey] = useState('');
8 | const [showOpenAIKey, setShowOpenAIKey] = useState(false);
9 | const [ankiServerAddress, setAnkiServerAddress] = useState('');
10 | const [ankiDeskName, setAnkiDeskName] = useState('');
11 | const [reload, setReload] = useState(false); // New state variable
12 |
13 |
14 | const handleOpenAIKeyChange = (e) => {
15 | setOpenAIKey(e.target.value);
16 | };
17 |
18 | const toggleShowOpenAIKey = () => {
19 | setShowOpenAIKey((prevState) => !prevState);
20 | };
21 |
22 | const handleAnkiServerAddressChange = (e) => {
23 | setAnkiServerAddress(e.target.value);
24 | };
25 |
26 | const handleAnkiDeskNameChange = (e) => {
27 | setAnkiDeskName(e.target.value);
28 | };
29 |
30 | useEffect(() => {
31 | const config = JSON.parse(localStorage.getItem("ankiConfig"));
32 | if (config) {
33 | const {openAIKey, ankiServerAddress, ankiDeskName} = config;
34 | setOpenAIKey(openAIKey);
35 | setAnkiServerAddress(ankiServerAddress);
36 | setAnkiDeskName(ankiDeskName);
37 | }
38 | }, [reload]);
39 |
40 | const handleSave = () => {
41 | const config = {
42 | openAIKey: openAIKey,
43 | ankiServerAddress: ankiServerAddress,
44 | ankiDeskName: ankiDeskName
45 | };
46 |
47 | localStorage.setItem("ankiConfig", JSON.stringify(config));
48 | setReload(!reload); // Toggle the reload state variable to trigger useEffect
49 |
50 | if (chrome && chrome.runtime && chrome.runtime.sendMessage) {
51 | chrome.runtime.sendMessage({type: 'SAVE_ANKI_CONFIG', config: config});
52 | }
53 |
54 | alert("save success");
55 | };
56 |
57 | return (
58 |