ChatGPT 页面翻译按钮

🍓 让 ChatGPT 一键翻译您正在阅读的网页

当前为 2023-04-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name chatgpt-page-translate-button
  3. // @description 🍓 let ChatGPT translate the web page you are reading in one click
  4. // @author mefengl
  5. // @version 0.1.10
  6. // @namespace https://github.com/mefengl
  7. // @require https://cdn.jsdelivr.net/npm/@mozilla/readability@0.4.3/Readability.min.js
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=openai.com
  9. // @license MIT
  10. // @match *://*/*
  11. // @grant GM_setValue
  12. // @grant GM_addValueChangeListener
  13.  
  14. // @name:en ChatGPT Page Translate Button
  15. // @description:en 🍓 let ChatGPT translate the web page you are reading in one click
  16. // @name:zh-CN ChatGPT 页面翻译按钮
  17. // @description:zh-CN 🍓 让 ChatGPT 一键翻译您正在阅读的网页
  18. // @name:es Botón de traducción de página de ChatGPT
  19. // @description:es 🍓 permite que ChatGPT traduzca la página web que estás leyendo con un solo clic
  20. // @name:hi ChatGPT पृष्ठ अनुवाद बटन
  21. // @description:hi 🍓 ChatGPT को वेबपेज जो आप पढ़ रहे हैं को एक क्लिक में अनुवाद करने दें
  22. // @name:ar زر ترجمة الصفحة لـ ChatGPT
  23. // @description:ar 🍓 دع ChatGPT يترجم صفحة الويب التي تقرأها بنقرة واحدة
  24. // @name:pt Botão de tradução de página do ChatGPT
  25. // @description:pt 🍓 permita que o ChatGPT traduza a página da web que você está lendo com um clique
  26. // @name:ru Кнопка перевода страницы ChatGPT
  27. // @description:ru 🍓 позволяет ChatGPT переводить веб-страницу, которую вы читаете, одним щелчком мыши
  28. // @name:ja ChatGPTページ翻訳ボタン
  29. // @description:ja 🍓 ChatGPTで読んでいるWebページをワンクリックで翻訳
  30. // @name:de ChatGPT-Seitenübersetzungs-Button
  31. // @description:de 🍓 Lassen Sie ChatGPT die Webseite, die Sie gerade lesen, mit einem Klick übersetzen
  32. // @name:fr Bouton de traduction de page ChatGPT
  33. // @description:fr 🍓 laissez ChatGPT traduire la page Web que vous lisez en un seul clic
  34. // ==/UserScript==
  35. (() => {
  36. var __async = (__this, __arguments, generator) => {
  37. return new Promise((resolve, reject) => {
  38. var fulfilled = (value) => {
  39. try {
  40. step(generator.next(value));
  41. } catch (e) {
  42. reject(e);
  43. }
  44. };
  45. var rejected = (value) => {
  46. try {
  47. step(generator.throw(value));
  48. } catch (e) {
  49. reject(e);
  50. }
  51. };
  52. var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
  53. step((generator = generator.apply(__this, __arguments)).next());
  54. });
  55. };
  56.  
  57. // ../../packages/chatkit/dist/index.mjs
  58. function getTextarea() {
  59. const form = document.querySelector("form");
  60. if (!form)
  61. return;
  62. const textareas = form.querySelectorAll("textarea");
  63. const result = textareas[0];
  64. return result;
  65. }
  66. function getSubmitButton() {
  67. const textarea = getTextarea();
  68. if (!textarea)
  69. return;
  70. return textarea.nextElementSibling;
  71. }
  72. function getRegenerateButton() {
  73. const form = document.querySelector("form");
  74. if (!form)
  75. return;
  76. const buttons = form.querySelectorAll("button");
  77. const result = Array.from(buttons).find((button) => {
  78. var _a;
  79. return (_a = button.textContent) == null ? void 0 : _a.trim().toLowerCase().includes("regenerate");
  80. });
  81. return result;
  82. }
  83. function getStopGeneratingButton() {
  84. const form = document.querySelector("form");
  85. if (!form)
  86. return;
  87. const buttons = form.querySelectorAll("button");
  88. const result = Array.from(buttons).find((button) => {
  89. var _a;
  90. return (_a = button.textContent) == null ? void 0 : _a.trim().toLowerCase().includes("stop generating");
  91. });
  92. return result;
  93. }
  94. function getLastResponseElement() {
  95. const responseElements = document.querySelectorAll(".group.w-full");
  96. return responseElements[responseElements.length - 1];
  97. }
  98. function getLastResponse() {
  99. const lastResponseElement = getLastResponseElement();
  100. if (!lastResponseElement)
  101. return;
  102. const lastResponse = lastResponseElement.textContent;
  103. return lastResponse;
  104. }
  105. function getTextareaValue() {
  106. var _a;
  107. return ((_a = getTextarea()) == null ? void 0 : _a.value) || "";
  108. }
  109. function setTextarea(message) {
  110. const textarea = getTextarea();
  111. if (!textarea)
  112. return;
  113. textarea.value = message;
  114. textarea.dispatchEvent(new Event("input"));
  115. }
  116. function send(message) {
  117. setTextarea(message);
  118. const textarea = getTextarea();
  119. if (!textarea)
  120. return;
  121. textarea.dispatchEvent(new KeyboardEvent("keydown", { key: "Enter", bubbles: true }));
  122. }
  123. function regenerate() {
  124. const regenerateButton = getRegenerateButton();
  125. if (!regenerateButton)
  126. return;
  127. regenerateButton.click();
  128. }
  129. function onSend(callback) {
  130. const textarea = getTextarea();
  131. if (!textarea)
  132. return;
  133. textarea.addEventListener("keydown", function(event) {
  134. if (event.key === "Enter" && !event.shiftKey) {
  135. callback();
  136. }
  137. });
  138. const sendButton = getSubmitButton();
  139. if (!sendButton)
  140. return;
  141. sendButton.addEventListener("mousedown", callback);
  142. }
  143. function isGenerating() {
  144. var _a, _b;
  145. return ((_b = (_a = getSubmitButton()) == null ? void 0 : _a.firstElementChild) == null ? void 0 : _b.childElementCount) === 3;
  146. }
  147. function waitForIdle() {
  148. return new Promise((resolve) => {
  149. const interval = setInterval(() => {
  150. if (!isGenerating()) {
  151. clearInterval(interval);
  152. resolve();
  153. }
  154. }, 1e3);
  155. });
  156. }
  157. function setListener(key = "prompt_texts") {
  158. let last_trigger_time = +/* @__PURE__ */ new Date();
  159. if (location.href.includes("chat.openai")) {
  160. GM_addValueChangeListener(key, (name, old_value, new_value) => __async(this, null, function* () {
  161. if (+/* @__PURE__ */ new Date() - last_trigger_time < 500) {
  162. return;
  163. }
  164. last_trigger_time = +/* @__PURE__ */ new Date();
  165. setTimeout(() => __async(this, null, function* () {
  166. const prompt_texts = new_value;
  167. if (prompt_texts.length > 0) {
  168. let firstTime = true;
  169. while (prompt_texts.length > 0) {
  170. if (!firstTime) {
  171. yield new Promise((resolve) => setTimeout(resolve, 2e3));
  172. }
  173. if (!firstTime && chatgpt.isGenerating()) {
  174. continue;
  175. }
  176. firstTime = false;
  177. const prompt_text = prompt_texts.shift() || "";
  178. chatgpt.send(prompt_text);
  179. }
  180. }
  181. }), 0);
  182. GM_setValue(key, []);
  183. }));
  184. }
  185. }
  186. function getConversation() {
  187. var _a, _b;
  188. return (_b = (_a = document.querySelector('div[class^="react-scroll-to-bottom"]')) == null ? void 0 : _a.firstChild) == null ? void 0 : _b.firstChild;
  189. }
  190. function getModelSelectButton() {
  191. const conversation = getConversation();
  192. if (!conversation)
  193. return;
  194. return Array.from(conversation.querySelectorAll("button")).find((button) => {
  195. var _a;
  196. return (_a = button.textContent) == null ? void 0 : _a.trim().toLowerCase().includes("model");
  197. });
  198. }
  199. function isConversationStarted() {
  200. return !getModelSelectButton();
  201. }
  202. function setPureConversation() {
  203. const conversation = getConversation();
  204. if (!conversation)
  205. return;
  206. const firstChild = conversation.firstChild;
  207. if (!firstChild)
  208. return;
  209. const newDiv = document.createElement("div");
  210. conversation.insertBefore(newDiv, firstChild.nextSibling);
  211. }
  212. function isHorizontalConversation() {
  213. const conversation = getConversation();
  214. if (!conversation)
  215. return true;
  216. if (!isConversationStarted())
  217. return true;
  218. return conversation.classList.contains("grid");
  219. }
  220. function setHorizontalConversation() {
  221. if (isHorizontalConversation())
  222. return;
  223. setPureConversation();
  224. const conversation = getConversation();
  225. if (!conversation)
  226. return;
  227. conversation.classList.remove("flex", "flex-col", "items-center");
  228. conversation.classList.add("grid", "grid-cols-2", "place-items-center");
  229. }
  230. var chatgpt = {
  231. getTextarea,
  232. getSubmitButton,
  233. getRegenerateButton,
  234. getStopGeneratingButton,
  235. getLastResponseElement,
  236. getLastResponse,
  237. getTextareaValue,
  238. setTextarea,
  239. send,
  240. regenerate,
  241. onSend,
  242. isGenerating,
  243. waitForIdle,
  244. setListener,
  245. getConversation,
  246. getModelSelectButton,
  247. isConversationStarted,
  248. setPureConversation,
  249. isHorizontalConversation,
  250. setHorizontalConversation
  251. };
  252. var chatgpt_default = chatgpt;
  253.  
  254. // src/createButton/index.ts
  255. function createButton(callback) {
  256. if (window.location.href.includes("chat.openai")) {
  257. return;
  258. }
  259. const hideRight = document.title.match(/[\u4e00-\u9fa5]/) ? "-130px" : "-120px";
  260. const button = document.createElement("button");
  261. button.innerHTML = "\u7F51\u9875\u7FFB\u8BD1";
  262. button.style.position = "fixed";
  263. button.style.width = "140px";
  264. button.style.top = "120px";
  265. button.style.right = hideRight;
  266. button.style.zIndex = "999999";
  267. button.style.backgroundColor = "#4285f4";
  268. button.style.color = "#fff";
  269. button.style.opacity = "0.8";
  270. button.style.border = "none";
  271. button.style.borderRadius = "4px";
  272. button.style.padding = "10px 16px";
  273. button.style.fontSize = "18px";
  274. button.style.cursor = "pointer";
  275. button.style.transition = "right 0.3s";
  276. document.body.appendChild(button);
  277. button.addEventListener("mouseenter", () => {
  278. button.style.right = "-10px";
  279. });
  280. button.addEventListener("mouseleave", () => {
  281. button.style.right = hideRight;
  282. });
  283. document.addEventListener("fullscreenchange", () => {
  284. if (document.fullscreenElement) {
  285. button.style.display = "none";
  286. } else {
  287. button.style.display = "block";
  288. }
  289. });
  290. button.addEventListener("click", callback);
  291. }
  292. var createButton_default = createButton;
  293.  
  294. // src/SimpleArticleSegmentation/index.ts
  295. var MIN_PARAGRAPH_LENGTH = 1600;
  296. var MAX_PARAGRAPH_LENGTH = 1800;
  297. var SimpleArticleSegmentation = class {
  298. constructor(text) {
  299. this.text = text;
  300. }
  301. segment() {
  302. const paragraphs = [];
  303. const sentences = this.text.split(new RegExp("(?<=[.!?])\\s+"));
  304. let paragraph = "";
  305. for (const sentence of sentences) {
  306. if (paragraph.length + sentence.length + 1 <= MAX_PARAGRAPH_LENGTH) {
  307. paragraph += (paragraph.length > 0 ? " " : "") + sentence;
  308. } else {
  309. if (paragraph.length >= MIN_PARAGRAPH_LENGTH) {
  310. paragraphs.push(paragraph);
  311. paragraph = sentence;
  312. } else {
  313. paragraph += " " + sentence;
  314. }
  315. }
  316. }
  317. if (paragraph.length >= MIN_PARAGRAPH_LENGTH) {
  318. paragraphs.push(paragraph);
  319. }
  320. return paragraphs;
  321. }
  322. };
  323. var SimpleArticleSegmentation_default = SimpleArticleSegmentation;
  324.  
  325. // src/getParagraphs/index.ts
  326. function getParagraphs() {
  327. try {
  328. let docClone = document.cloneNode(true);
  329. let article = new Readability(docClone).parse();
  330. if (article && article.textContent) {
  331. const segmenter = new SimpleArticleSegmentation_default(article.textContent);
  332. const paragraphs = segmenter.segment();
  333. for (let i = 0; i < paragraphs.length; i++) {
  334. paragraphs[i] = paragraphs[i].trim();
  335. }
  336. return paragraphs;
  337. } else {
  338. console.warn("Readability.js could not extract any text content from this page.");
  339. return [];
  340. }
  341. } catch (error) {
  342. console.error("An error occurred while using Readability.js:", error);
  343. return [];
  344. }
  345. }
  346. var getParagraphs_default = getParagraphs;
  347.  
  348. // src/index.ts
  349. function initialize() {
  350. return __async(this, null, function* () {
  351. yield new Promise((resolve) => window.addEventListener("load", resolve));
  352. yield new Promise((resolve) => setTimeout(resolve, 1e3));
  353. });
  354. }
  355. function main() {
  356. return __async(this, null, function* () {
  357. yield initialize();
  358. const key = "prompt_texts";
  359. chatgpt_default.setListener(key);
  360. const translateWeb = () => __async(this, null, function* () {
  361. const paragraphs = getParagraphs_default();
  362. const prompt_texts = paragraphs.map((paragraph) => {
  363. return `${paragraph}
  364.  
  365. translate above paragraph to Chinese with compact and intuitive format (use Markdown syntax to optimize the display format):`;
  366. });
  367. GM_setValue(key, prompt_texts);
  368. });
  369. createButton_default(translateWeb);
  370. });
  371. }
  372. (function() {
  373. main();
  374. })();
  375. })();
  376.