Simple Grammar Fix

Ctrl+& fixes grammar using Github API

当前为 2025-05-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Simple Grammar Fix
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.0
  5. // @description Ctrl+& fixes grammar using Github API
  6. // @match *://*/*
  7. // @license WTFPL
  8. // @icon https://www.iconsdb.com/icons/preview/royal-blue/edit-12-xxl.png
  9. // @author moony
  10. // @grant GM_xmlhttpRequest
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @connect models.github.ai
  15. // ==/UserScript==
  16.  
  17. (function() {
  18. const MODEL = "openai/gpt-4.1"; // gpt-4o, o4-mini, gpt-4.1, Mistral-Large-2411 from https://github.com/marketplace?type=models
  19. const SYSTEM_PROMPT = "Fix grammar only. Return compact corrected text.";
  20.  
  21. GM_registerMenuCommand("🗝️ Set API Key", () =>
  22. GM_setValue("GITHUB_TOKEN", prompt("Enter your GitHub API key:") || GM_getValue("GITHUB_TOKEN"))
  23. );
  24.  
  25. document.addEventListener('keydown', e => {
  26. if (!(e.ctrlKey && e.key === '&')) return;
  27. e.preventDefault();
  28.  
  29. const token = GM_getValue("GITHUB_TOKEN");
  30. if (!token) return alert("API key not set. Use Tampermonkey menu → 🗝️ Set API Key");
  31.  
  32. const sel = window.getSelection();
  33. const txt = sel.toString().trim();
  34. if (!txt) return;
  35.  
  36. const el = document.activeElement;
  37. const isInput = el.tagName === 'TEXTAREA' || el.tagName === 'INPUT';
  38.  
  39. GM_xmlhttpRequest({
  40. method: "POST",
  41. url: "https://models.github.ai/inference/chat/completions",
  42. headers: {"Content-Type": "application/json", "Authorization": `Bearer ${token}`},
  43. data: JSON.stringify({
  44. messages: [{role: "system", content: SYSTEM_PROMPT}, {role: "user", content: txt}],
  45. model: MODEL, temperature: 0.7
  46. }),
  47. onload: r => {
  48. if (r.status !== 200) return console.error("Error:", r.responseText);
  49.  
  50. const fixed = JSON.parse(r.responseText).choices[0].message.content;
  51.  
  52. if (isInput) {
  53. const [start, end] = [el.selectionStart, el.selectionEnd];
  54. el.value = el.value.substring(0, start) + fixed + el.value.substring(end);
  55. el.selectionStart = start;
  56. el.selectionEnd = start + fixed.length;
  57. } else if (sel.rangeCount) {
  58. const rng = sel.getRangeAt(0);
  59. rng.deleteContents();
  60. rng.insertNode(document.createTextNode(fixed));
  61. }
  62. },
  63. onerror: e => console.error("Request failed:", e)
  64. });
  65. });
  66. })();