Textbox, question to answser mode and clipboard mode is not formatted anymore
This commit is contained in:
@@ -2,7 +2,6 @@ import Config from "../types/config";
|
||||
import titleIndications from "../utils/title-indications";
|
||||
import reply from "./reply";
|
||||
|
||||
let injectionFunction: (this: HTMLElement, ev: MouseEvent) => any = null;
|
||||
const pressedKeys: string[] = [];
|
||||
const listeners: {
|
||||
element: HTMLElement;
|
||||
@@ -53,7 +52,13 @@ function setUpMoodleGpt(config: Config) {
|
||||
|
||||
if (config.cursor) hiddenButton.style.cursor = "pointer";
|
||||
|
||||
injectionFunction = reply.bind(null, config, hiddenButton, form, query);
|
||||
const injectionFunction = reply.bind(
|
||||
null,
|
||||
config,
|
||||
hiddenButton,
|
||||
form,
|
||||
query
|
||||
);
|
||||
listeners.push({ element: hiddenButton, fn: injectionFunction });
|
||||
hiddenButton.addEventListener("click", injectionFunction);
|
||||
}
|
||||
@@ -61,4 +66,12 @@ function setUpMoodleGpt(config: Config) {
|
||||
if (config.title) titleIndications("Injected");
|
||||
}
|
||||
|
||||
export { injectionFunction, codeListener };
|
||||
function removeListener(element: HTMLElement) {
|
||||
const index = listeners.findIndex((listener) => listener.element === element);
|
||||
if (index !== -1) {
|
||||
const listener = listeners.splice(index, 1)[0];
|
||||
listener.element.removeEventListener("click", listener.fn);
|
||||
}
|
||||
}
|
||||
|
||||
export { codeListener, removeListener };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Config from "../types/config";
|
||||
import GPTAnswer from "../types/gptAnswer";
|
||||
import normalizeText from "../utils/normalize-text";
|
||||
|
||||
/**
|
||||
@@ -10,7 +11,7 @@ import normalizeText from "../utils/normalize-text";
|
||||
async function getChatGPTResponse(
|
||||
config: Config,
|
||||
question: string
|
||||
): Promise<string> {
|
||||
): Promise<GPTAnswer> {
|
||||
const controller = new AbortController();
|
||||
const timeoutControler = setTimeout(() => controller.abort(), 15000);
|
||||
const req = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
@@ -32,7 +33,10 @@ async function getChatGPTResponse(
|
||||
clearTimeout(timeoutControler);
|
||||
const rep = await req.json();
|
||||
const response = rep.choices[0].message.content;
|
||||
return normalizeText(response);
|
||||
return {
|
||||
response,
|
||||
normalizedResponse: normalizeText(response),
|
||||
};
|
||||
}
|
||||
|
||||
export default getChatGPTResponse;
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import Config from "../../types/config";
|
||||
import GPTAnswer from "../../types/gptAnswer";
|
||||
import titleIndications from "../../utils/title-indications";
|
||||
|
||||
/**
|
||||
* Copy the response in the clipboard if we can automaticaly fill the question
|
||||
* @param config
|
||||
* @param inputList
|
||||
* @param response
|
||||
* @param force Force the copy to clipboard
|
||||
* @returns
|
||||
* @param gptAnswer
|
||||
*/
|
||||
function handleClipboard(config: Config, response: string) {
|
||||
function handleClipboard(config: Config, gptAnswer: GPTAnswer) {
|
||||
if (config.title) titleIndications("Copied to clipboard");
|
||||
navigator.clipboard.writeText(response);
|
||||
navigator.clipboard.writeText(gptAnswer.response);
|
||||
}
|
||||
|
||||
export default handleClipboard;
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import Config from "../../types/config";
|
||||
import GPTAnswer from "../../types/gptAnswer";
|
||||
|
||||
/**
|
||||
* Hanlde contenteditable elements
|
||||
* @param config
|
||||
* @param inputList
|
||||
* @param gptAnswer
|
||||
* @returns
|
||||
*/
|
||||
function handleContentEditable(
|
||||
config: Config,
|
||||
inputList: NodeListOf<HTMLElement>,
|
||||
response: string
|
||||
gptAnswer: GPTAnswer
|
||||
): boolean {
|
||||
const input = inputList[0];
|
||||
|
||||
@@ -16,10 +24,10 @@ function handleContentEditable(
|
||||
if (config.typing) {
|
||||
let index = 0;
|
||||
input.addEventListener("keydown", function (event: KeyboardEvent) {
|
||||
if (event.key === "Backspace") index = response.length + 1;
|
||||
if (index > response.length) return;
|
||||
if (event.key === "Backspace") index = gptAnswer.response.length + 1;
|
||||
if (index > gptAnswer.response.length) return;
|
||||
event.preventDefault();
|
||||
input.textContent = response.slice(0, ++index);
|
||||
input.textContent = gptAnswer.response.slice(0, ++index);
|
||||
|
||||
/* Put the cursor at the end of the typed text */
|
||||
input.focus();
|
||||
@@ -31,7 +39,7 @@ function handleContentEditable(
|
||||
selection.addRange(range);
|
||||
});
|
||||
} else {
|
||||
input.textContent = response;
|
||||
input.textContent = gptAnswer.response;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
import Config from "../../types/config";
|
||||
import GPTAnswer from "../../types/gptAnswer";
|
||||
|
||||
/**
|
||||
* Handle number input
|
||||
* @param config
|
||||
* @param inputList
|
||||
* @param response
|
||||
* @param gptAnswer
|
||||
* @returns
|
||||
*/
|
||||
function handleNumber(
|
||||
config: Config,
|
||||
inputList: NodeListOf<HTMLElement>,
|
||||
response: string
|
||||
gptAnswer: GPTAnswer
|
||||
): boolean {
|
||||
const input = inputList[0] as HTMLInputElement | HTMLTextAreaElement;
|
||||
|
||||
if (inputList.length !== 1 || input.type !== "number") return false;
|
||||
|
||||
const number = response.match(/\d+([,\.]\d+)?/gi)?.[0]?.replace(",", ".");
|
||||
const number = gptAnswer.normalizedResponse
|
||||
.match(/\d+([,\.]\d+)?/gi)?.[0]
|
||||
?.replace(",", ".");
|
||||
|
||||
if (!number) return false;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Config from "../../types/config";
|
||||
import GPTAnswer from "../../types/gptAnswer";
|
||||
import Logs from "../../utils/logs";
|
||||
import normalizeText from "../../utils/normalize-text";
|
||||
|
||||
@@ -6,12 +7,12 @@ import normalizeText from "../../utils/normalize-text";
|
||||
* Handle checkbox and input elements
|
||||
* @param config
|
||||
* @param inputList
|
||||
* @param response
|
||||
* @param gptAnswer
|
||||
*/
|
||||
function handleRadioAndCheckbox(
|
||||
config: Config,
|
||||
inputList: NodeListOf<HTMLElement>,
|
||||
response: string
|
||||
gptAnswer: GPTAnswer
|
||||
): boolean {
|
||||
const input = inputList?.[0] as HTMLInputElement;
|
||||
|
||||
@@ -20,7 +21,7 @@ function handleRadioAndCheckbox(
|
||||
|
||||
for (const input of inputList as NodeListOf<HTMLInputElement>) {
|
||||
const content = normalizeText(input.parentNode.textContent);
|
||||
const valide = response.includes(content);
|
||||
const valide = gptAnswer.normalizedResponse.includes(content);
|
||||
if (config.logs) Logs.responseTry(content, valide);
|
||||
if (valide) {
|
||||
if (config.mouseover) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import Config from "../../types/config";
|
||||
import GPTAnswer from "../../types/gptAnswer";
|
||||
import Logs from "../../utils/logs";
|
||||
import normalizeText from "../../utils/normalize-text";
|
||||
|
||||
@@ -6,19 +7,19 @@ import normalizeText from "../../utils/normalize-text";
|
||||
* Handle select elements (and put in order select)
|
||||
* @param config
|
||||
* @param inputList
|
||||
* @param response
|
||||
* @param gptAnswer
|
||||
* @returns
|
||||
*/
|
||||
function handleSelect(
|
||||
config: Config,
|
||||
inputList: NodeListOf<HTMLElement>,
|
||||
response: string
|
||||
gptAnswer: GPTAnswer
|
||||
): boolean {
|
||||
if (inputList.length === 0 || inputList[0].tagName !== "SELECT") return false;
|
||||
|
||||
let correct = response.split("\n");
|
||||
let correct = gptAnswer.normalizedResponse.split("\n");
|
||||
if (correct.length === 1 && correct.length !== inputList.length)
|
||||
correct = response.split(",");
|
||||
correct = gptAnswer.normalizedResponse.split(",");
|
||||
|
||||
if (config.logs) Logs.array(correct);
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import Config from "../../types/config";
|
||||
import GPTAnswer from "../../types/gptAnswer";
|
||||
|
||||
/**
|
||||
* Handle textbox
|
||||
* @param config
|
||||
* @param inputList
|
||||
* @param response
|
||||
* @param gptAnswer
|
||||
* @returns
|
||||
*/
|
||||
function handleTextbox(
|
||||
config: Config,
|
||||
inputList: NodeListOf<HTMLElement>,
|
||||
response: string
|
||||
gptAnswer: GPTAnswer
|
||||
): boolean {
|
||||
const input = inputList[0] as HTMLInputElement | HTMLTextAreaElement;
|
||||
|
||||
@@ -23,13 +24,13 @@ function handleTextbox(
|
||||
if (config.typing) {
|
||||
let index = 0;
|
||||
input.addEventListener("keydown", function (event: KeyboardEvent) {
|
||||
if (event.key === "Backspace") index = response.length + 1;
|
||||
if (index > response.length) return;
|
||||
if (event.key === "Backspace") index = gptAnswer.response.length + 1;
|
||||
if (index > gptAnswer.response.length) return;
|
||||
event.preventDefault();
|
||||
input.value = response.slice(0, ++index);
|
||||
input.value = gptAnswer.response.slice(0, ++index);
|
||||
});
|
||||
} else {
|
||||
input.value = response;
|
||||
input.value = gptAnswer.response;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
+25
-16
@@ -8,7 +8,7 @@ import handleTextbox from "./questions/textbox";
|
||||
import handleClipboard from "./questions/clipboard";
|
||||
import handleNumber from "./questions/number";
|
||||
import handleContentEditable from "./questions/contenteditable";
|
||||
import { injectionFunction } from "./code-listener";
|
||||
import { removeListener } from "./code-listener";
|
||||
|
||||
/**
|
||||
* Reply to the question
|
||||
@@ -29,41 +29,51 @@ async function reply(
|
||||
const question = createQuestion(config, form);
|
||||
const inputList: NodeListOf<HTMLElement> = form.querySelectorAll(query);
|
||||
|
||||
const response = await getChatGPTResponse(config, question).catch(
|
||||
const gptAnswer = await getChatGPTResponse(config, question).catch(
|
||||
(error) => ({
|
||||
error,
|
||||
})
|
||||
);
|
||||
|
||||
const haveError = typeof response === "object" && "error" in response;
|
||||
const haveError = typeof gptAnswer === "object" && "error" in gptAnswer;
|
||||
|
||||
if (config.cursor)
|
||||
hiddenButton.style.cursor =
|
||||
config.infinite || haveError ? "pointer" : "initial";
|
||||
|
||||
if (haveError) {
|
||||
console.error(response.error);
|
||||
console.error(gptAnswer.error);
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.logs) {
|
||||
Logs.question(question);
|
||||
Logs.response(response);
|
||||
Logs.response("Original: " + gptAnswer.response);
|
||||
Logs.response("Normalized: " + gptAnswer.normalizedResponse);
|
||||
}
|
||||
|
||||
/* Handle clipboard mode */
|
||||
if (config.mode === "clipboard") {
|
||||
return handleClipboard(config, response);
|
||||
if (!config.infinite) removeListener(hiddenButton);
|
||||
return handleClipboard(config, gptAnswer);
|
||||
}
|
||||
|
||||
/* Handle question to answer mode */
|
||||
if (config.mode === "question-to-answer") {
|
||||
removeListener(hiddenButton);
|
||||
|
||||
const questionBackup = form.textContent;
|
||||
const questionContainer = form.querySelector(".qtext");
|
||||
questionContainer.textContent = response;
|
||||
const questionContainer = form.querySelector<HTMLElement>(".qtext");
|
||||
|
||||
questionContainer.textContent = gptAnswer.response;
|
||||
questionContainer.style.whiteSpace = "pre-wrap";
|
||||
|
||||
questionContainer.addEventListener("click", function () {
|
||||
questionContainer.textContent =
|
||||
questionContainer.textContent === questionBackup
|
||||
? response
|
||||
: questionBackup;
|
||||
const isNotResponse = questionContainer.textContent === questionBackup;
|
||||
questionContainer.style.whiteSpace = isNotResponse ? "pre-wrap" : null;
|
||||
questionContainer.textContent = isNotResponse
|
||||
? gptAnswer.response
|
||||
: questionBackup;
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -77,15 +87,14 @@ async function reply(
|
||||
];
|
||||
|
||||
for (const handler of handlers) {
|
||||
if (handler(config, inputList, response)) return;
|
||||
if (handler(config, inputList, gptAnswer)) return;
|
||||
}
|
||||
|
||||
/* In the case we can't auto complete the question */
|
||||
handleClipboard(config, response);
|
||||
handleClipboard(config, gptAnswer);
|
||||
|
||||
/* Better then set once on the event because if there is an error the user can click an other time on the question */
|
||||
if (!config.infinite)
|
||||
hiddenButton.removeEventListener("click", injectionFunction);
|
||||
if (!config.infinite) removeListener(hiddenButton);
|
||||
}
|
||||
|
||||
export default reply;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
type Config = {
|
||||
apiKey: string;
|
||||
code: string;
|
||||
model?: string;
|
||||
infinite?: boolean;
|
||||
typing?: boolean;
|
||||
mouseover?: boolean;
|
||||
cursor?: boolean;
|
||||
logs?: boolean;
|
||||
title?: boolean;
|
||||
timeout?: boolean;
|
||||
mode?: "autocomplete" | "question-to-answer" | "clipboard";
|
||||
};
|
||||
|
||||
export default Config;
|
||||
type Config = {
|
||||
apiKey: string;
|
||||
code: string;
|
||||
model?: string;
|
||||
infinite?: boolean;
|
||||
typing?: boolean;
|
||||
mouseover?: boolean;
|
||||
cursor?: boolean;
|
||||
logs?: boolean;
|
||||
title?: boolean;
|
||||
timeout?: boolean;
|
||||
mode?: "autocomplete" | "question-to-answer" | "clipboard";
|
||||
};
|
||||
|
||||
export default Config;
|
||||
@@ -0,0 +1,6 @@
|
||||
type GPTAnswer = {
|
||||
response: string;
|
||||
normalizedResponse: string;
|
||||
};
|
||||
|
||||
export default GPTAnswer;
|
||||
Reference in New Issue
Block a user