prettier formatage
This commit is contained in:
+11
-11
@@ -1,18 +1,18 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier'
|
||||
],
|
||||
ignorePatterns: ["node_modules/"],
|
||||
ignorePatterns: ['node_modules/'],
|
||||
overrides: [
|
||||
{
|
||||
files: ["extension/popup/*.js", "src/**/*.ts"],
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
files: ['extension/popup/*.js', 'src/**/*.ts'],
|
||||
rules: {}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none",
|
||||
"arrowParens": "avoid",
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
# TODO
|
||||
|
||||
- Historic for questions (implemented but need testing)
|
||||
- Contributing.md
|
||||
- Contributing.md / Fixe readme.md 'MoodleGPT don't complete my quiz ?'
|
||||
- Better prompt (Fixe put in order question, Fixe calculation question)
|
||||
- Support math equation from image stocked in the `data-mathml` attribute
|
||||
- Better assets
|
||||
|
||||
+418
-1
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@@ -41,15 +41,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="line center">
|
||||
<label for="apiKey" class="textLabel"
|
||||
>Api Key<span class="required">*</span>:</label
|
||||
>
|
||||
<label for="apiKey" class="textLabel">Api Key<span class="required">*</span>:</label>
|
||||
<input id="apiKey" type="text" />
|
||||
</div>
|
||||
<div class="line center">
|
||||
<label for="model" class="textLabel"
|
||||
>GPT Model<span class="required">*</span>:</label
|
||||
>
|
||||
<label for="model" class="textLabel">GPT Model<span class="required">*</span>:</label>
|
||||
<input id="model" type="text" />
|
||||
<i
|
||||
id="reloadModel"
|
||||
@@ -73,9 +69,7 @@
|
||||
<button value="clipboard" class="not-selected">clipboard</button>
|
||||
</li>
|
||||
<li>
|
||||
<button value="question-to-answer" class="not-selected">
|
||||
question to answer
|
||||
</button>
|
||||
<button value="question-to-answer" class="not-selected">question to answer</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Get the last ChatGPT version
|
||||
*/
|
||||
function getLastChatGPTVersion() {
|
||||
const apiKeySelector = document.querySelector("#apiKey");
|
||||
const reloadModel = document.querySelector("#reloadModel");
|
||||
const apiKeySelector = document.querySelector('#apiKey');
|
||||
const reloadModel = document.querySelector('#reloadModel');
|
||||
|
||||
let apiKey = apiKeySelector.value;
|
||||
|
||||
// If the api key is set we enable the button to get the last chatgpt version
|
||||
function checkFiledApiKey() {
|
||||
if (apiKey) {
|
||||
reloadModel.removeAttribute("disabled");
|
||||
reloadModel.setAttribute("title", "Get last ChatGPT version");
|
||||
reloadModel.removeAttribute('disabled');
|
||||
reloadModel.setAttribute('title', 'Get last ChatGPT version');
|
||||
return;
|
||||
}
|
||||
|
||||
reloadModel.setAttribute("disabled", true);
|
||||
reloadModel.setAttribute("title", "Provide an api key first");
|
||||
reloadModel.setAttribute('disabled', true);
|
||||
reloadModel.setAttribute('title', 'Provide an api key first');
|
||||
}
|
||||
checkFiledApiKey();
|
||||
|
||||
// Check if the api key is set
|
||||
apiKeySelector.addEventListener("input", function () {
|
||||
apiKeySelector.addEventListener('input', function () {
|
||||
apiKey = apiKeySelector.value.trim();
|
||||
checkFiledApiKey();
|
||||
});
|
||||
|
||||
// Event listener to handle a click on the relaod icon button
|
||||
reloadModel.addEventListener("click", async function () {
|
||||
reloadModel.addEventListener('click', async function () {
|
||||
if (!apiKey) return;
|
||||
|
||||
try {
|
||||
const req = await fetch("https://api.openai.com/v1/models", {
|
||||
const req = await fetch('https://api.openai.com/v1/models', {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
Authorization: `Bearer ${apiKey}`
|
||||
}
|
||||
});
|
||||
const rep = await req.json();
|
||||
const model = rep.data.find((model) => model.id.startsWith("gpt"));
|
||||
document.querySelector("#model").value = model.id;
|
||||
const model = rep.data.find(model => model.id.startsWith('gpt'));
|
||||
document.querySelector('#model').value = model.id;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
showMessage({ msg: "Failed to fetch last ChatGPT version", error: true });
|
||||
showMessage({ msg: 'Failed to fetch last ChatGPT version', error: true });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+31
-34
@@ -1,38 +1,39 @@
|
||||
const saveBtn = document.querySelector(".save");
|
||||
const saveBtn = document.querySelector('.save');
|
||||
|
||||
/* inputs id */
|
||||
const inputsText = ["apiKey", "code", "model"];
|
||||
const inputsText = ['apiKey', 'code', 'model'];
|
||||
const inputsCheckbox = [
|
||||
"logs",
|
||||
"title",
|
||||
"cursor",
|
||||
"typing",
|
||||
"mouseover",
|
||||
"infinite",
|
||||
"timeout",
|
||||
"history",
|
||||
'logs',
|
||||
'title',
|
||||
'cursor',
|
||||
'typing',
|
||||
'mouseover',
|
||||
'infinite',
|
||||
'timeout',
|
||||
'history'
|
||||
];
|
||||
|
||||
/* Save the configuration */
|
||||
saveBtn.addEventListener("click", function () {
|
||||
const [apiKey, code, model] = inputsText.map((selector) =>
|
||||
document.querySelector("#" + selector).value.trim()
|
||||
saveBtn.addEventListener('click', function () {
|
||||
const [apiKey, code, model] = inputsText.map(selector =>
|
||||
document.querySelector('#' + selector).value.trim()
|
||||
);
|
||||
const [logs, title, cursor, typing, mouseover, infinite, timeout, history] = inputsCheckbox.map(
|
||||
selector => {
|
||||
const element = document.querySelector('#' + selector);
|
||||
return element.checked && element.parentElement.style.display !== 'none';
|
||||
}
|
||||
);
|
||||
const [logs, title, cursor, typing, mouseover, infinite, timeout, history] =
|
||||
inputsCheckbox.map((selector) => {
|
||||
const element = document.querySelector("#" + selector);
|
||||
return element.checked && element.parentElement.style.display !== "none";
|
||||
});
|
||||
|
||||
if (!apiKey || !model) {
|
||||
showMessage({ msg: "Please complete all the form", error: true });
|
||||
showMessage({ msg: 'Please complete all the form', error: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (code.length > 0 && code.length < 3) {
|
||||
showMessage({
|
||||
msg: "The code should at least contain 3 characters",
|
||||
error: true,
|
||||
msg: 'The code should at least contain 3 characters',
|
||||
error: true
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -50,15 +51,15 @@ saveBtn.addEventListener("click", function () {
|
||||
infinite,
|
||||
timeout,
|
||||
history,
|
||||
mode: actualMode,
|
||||
},
|
||||
mode: actualMode
|
||||
}
|
||||
});
|
||||
|
||||
showMessage({ msg: "Configuration saved" });
|
||||
showMessage({ msg: 'Configuration saved' });
|
||||
});
|
||||
|
||||
/* we load back the configuration */
|
||||
chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
|
||||
chrome.storage.sync.get(['moodleGPT']).then(function (storage) {
|
||||
const config = storage.moodleGPT;
|
||||
|
||||
if (config) {
|
||||
@@ -66,21 +67,17 @@ chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
|
||||
actualMode = config.mode;
|
||||
for (const mode of modes) {
|
||||
if (mode.value === config.mode) {
|
||||
mode.classList.remove("not-selected");
|
||||
mode.classList.remove('not-selected');
|
||||
} else {
|
||||
mode.classList.add("not-selected");
|
||||
mode.classList.add('not-selected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inputsText.forEach((key) =>
|
||||
config[key]
|
||||
? (document.querySelector("#" + key).value = config[key])
|
||||
: null
|
||||
);
|
||||
inputsCheckbox.forEach(
|
||||
(key) => (document.querySelector("#" + key).checked = config[key] || "")
|
||||
inputsText.forEach(key =>
|
||||
config[key] ? (document.querySelector('#' + key).value = config[key]) : null
|
||||
);
|
||||
inputsCheckbox.forEach(key => (document.querySelector('#' + key).checked = config[key] || ''));
|
||||
}
|
||||
|
||||
handleModeChange();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
const mode = document.querySelector("#mode");
|
||||
const modes = mode.querySelectorAll("button");
|
||||
const mode = document.querySelector('#mode');
|
||||
const modes = mode.querySelectorAll('button');
|
||||
|
||||
let actualMode = "autocomplete";
|
||||
let actualMode = 'autocomplete';
|
||||
|
||||
/* inputs id that need to be disabled for a specific mode */
|
||||
const disabledForThisMode = {
|
||||
autocomplete: [],
|
||||
clipboard: ["typing", "mouseover"],
|
||||
"question-to-answer": ["typing", "infinite", "mouseover"],
|
||||
clipboard: ['typing', 'mouseover'],
|
||||
'question-to-answer': ['typing', 'infinite', 'mouseover']
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -17,27 +17,25 @@ const disabledForThisMode = {
|
||||
*/
|
||||
function handleModeChange() {
|
||||
const needDisable = disabledForThisMode[actualMode];
|
||||
const dontNeedDisable = inputsCheckbox.filter(
|
||||
(input) => !needDisable.includes(input)
|
||||
);
|
||||
const dontNeedDisable = inputsCheckbox.filter(input => !needDisable.includes(input));
|
||||
for (const id of needDisable) {
|
||||
document.querySelector("#" + id).parentElement.style.display = "none";
|
||||
document.querySelector('#' + id).parentElement.style.display = 'none';
|
||||
}
|
||||
for (const id of dontNeedDisable) {
|
||||
document.querySelector("#" + id).parentElement.style.display = null;
|
||||
document.querySelector('#' + id).parentElement.style.display = null;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mode handler */
|
||||
for (const button of modes) {
|
||||
button.addEventListener("click", function () {
|
||||
button.addEventListener('click', function () {
|
||||
const value = button.value;
|
||||
actualMode = value;
|
||||
for (const mode of modes) {
|
||||
if (mode.value !== value) {
|
||||
mode.classList.add("not-selected");
|
||||
mode.classList.add('not-selected');
|
||||
} else {
|
||||
mode.classList.remove("not-selected");
|
||||
mode.classList.remove('not-selected');
|
||||
}
|
||||
}
|
||||
handleModeChange();
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Show message into the popup
|
||||
*/
|
||||
function showMessage({ msg, error, infinite }) {
|
||||
const message = document.querySelector("#message");
|
||||
message.style.color = error ? "red" : "limegreen";
|
||||
const message = document.querySelector('#message');
|
||||
message.style.color = error ? 'red' : 'limegreen';
|
||||
message.textContent = msg;
|
||||
message.style.display = "block";
|
||||
if (!infinite) setTimeout(() => (message.style.display = "none"), 5000);
|
||||
message.style.display = 'block';
|
||||
if (!infinite) setTimeout(() => (message.style.display = 'none'), 5000);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
'use strict';
|
||||
|
||||
const CURRENT_VERSION = "1.0.4";
|
||||
const versionDisplay = document.querySelector("#version");
|
||||
const CURRENT_VERSION = '1.0.4';
|
||||
const versionDisplay = document.querySelector('#version');
|
||||
|
||||
/**
|
||||
* Get the last version from the github
|
||||
@@ -9,7 +9,7 @@ const versionDisplay = document.querySelector("#version");
|
||||
*/
|
||||
async function getLastVersion() {
|
||||
const req = await fetch(
|
||||
"https://raw.githubusercontent.com/yoannchb-pro/MoodleGPT/main/package.json"
|
||||
'https://raw.githubusercontent.com/yoannchb-pro/MoodleGPT/main/package.json'
|
||||
);
|
||||
const rep = await req.json();
|
||||
return rep.version;
|
||||
@@ -23,34 +23,31 @@ async function getLastVersion() {
|
||||
*/
|
||||
function setVersion(version, isCurrent = true) {
|
||||
if (isCurrent) {
|
||||
versionDisplay.textContent = "v" + version;
|
||||
versionDisplay.textContent = 'v' + version;
|
||||
return;
|
||||
}
|
||||
|
||||
const link = document.createElement("a");
|
||||
link.href = "https://github.com/yoannchb-pro/MoodleGPT";
|
||||
link.rel = "noopener noreferrer";
|
||||
link.target = "_blank";
|
||||
link.textContent = "v" + version;
|
||||
const link = document.createElement('a');
|
||||
link.href = 'https://github.com/yoannchb-pro/MoodleGPT';
|
||||
link.rel = 'noopener noreferrer';
|
||||
link.target = '_blank';
|
||||
link.textContent = 'v' + version;
|
||||
versionDisplay.appendChild(link);
|
||||
versionDisplay.appendChild(document.createTextNode(" is now available !"));
|
||||
versionDisplay.appendChild(document.createTextNode(' is now available !'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the extension neeed an update or not
|
||||
*/
|
||||
async function notifyUpdate() {
|
||||
const lastVersion = await getLastVersion().catch((err) => {
|
||||
const lastVersion = await getLastVersion().catch(err => {
|
||||
console.error(err);
|
||||
return CURRENT_VERSION;
|
||||
});
|
||||
|
||||
const lastVertionSplitted = lastVersion.split(".");
|
||||
const currentVersionSplitted = CURRENT_VERSION.split(".");
|
||||
const minVersionLength = Math.min(
|
||||
lastVertionSplitted.length,
|
||||
currentVersionSplitted.length
|
||||
);
|
||||
const lastVertionSplitted = lastVersion.split('.');
|
||||
const currentVersionSplitted = CURRENT_VERSION.split('.');
|
||||
const minVersionLength = Math.min(lastVertionSplitted.length, currentVersionSplitted.length);
|
||||
|
||||
for (let i = 0; i < minVersionLength; ++i) {
|
||||
if (parseInt(lastVertionSplitted[i]) > parseInt(currentVersionSplitted[i]))
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Segeo UI", sans-serif;
|
||||
font-family: 'Segeo UI', sans-serif;
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@ a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.line input[type="text"],
|
||||
.line input[type="password"] {
|
||||
.line input[type='text'],
|
||||
.line input[type='password'] {
|
||||
flex: 1 1;
|
||||
border: thin solid var(--color);
|
||||
padding: 0.3rem 0.5rem;
|
||||
@@ -77,7 +77,7 @@ a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.line input[type="checkbox"] {
|
||||
.line input[type='checkbox'] {
|
||||
accent-color: var(--btn-color);
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -1,16 +1,16 @@
|
||||
const ts = require("rollup-plugin-ts");
|
||||
const terser = require("@rollup/plugin-terser");
|
||||
const ts = require('rollup-plugin-ts');
|
||||
const terser = require('@rollup/plugin-terser');
|
||||
|
||||
const config = require("./tsconfig.json");
|
||||
const config = require('./tsconfig.json');
|
||||
|
||||
module.exports = {
|
||||
input: "./src/index.ts",
|
||||
input: './src/index.ts',
|
||||
output: [
|
||||
{
|
||||
file: "./extension/MoodleGPT.js",
|
||||
format: "umd",
|
||||
sourcemap: true,
|
||||
},
|
||||
file: './extension/MoodleGPT.js',
|
||||
format: 'umd',
|
||||
sourcemap: true
|
||||
}
|
||||
],
|
||||
plugins: [ts(config), terser()],
|
||||
plugins: [ts(config), terser()]
|
||||
};
|
||||
|
||||
+20
-20
@@ -1,6 +1,6 @@
|
||||
import type Config from "@typing/config";
|
||||
import titleIndications from "@utils/title-indications";
|
||||
import reply from "./reply";
|
||||
import type Config from '@typing/config';
|
||||
import titleIndications from '@utils/title-indications';
|
||||
import reply from './reply';
|
||||
|
||||
type Listener = {
|
||||
element: HTMLElement;
|
||||
@@ -15,10 +15,10 @@ const listeners: Listener[] = [];
|
||||
* @param config
|
||||
*/
|
||||
function codeListener(config: Config) {
|
||||
document.body.addEventListener("keydown", function (event) {
|
||||
document.body.addEventListener('keydown', function (event) {
|
||||
pressedKeys.push(event.key);
|
||||
if (pressedKeys.length > config.code!.length) pressedKeys.shift();
|
||||
if (pressedKeys.join("") === config.code) {
|
||||
if (pressedKeys.join('') === config.code) {
|
||||
pressedKeys.length = 0;
|
||||
setUpMoodleGpt(config);
|
||||
}
|
||||
@@ -30,10 +30,10 @@ function codeListener(config: Config) {
|
||||
* @param element
|
||||
*/
|
||||
function removeListener(element: HTMLElement) {
|
||||
const index = listeners.findIndex((listener) => listener.element === element);
|
||||
const index = listeners.findIndex(listener => listener.element === element);
|
||||
if (index !== -1) {
|
||||
const listener = listeners.splice(index, 1)[0];
|
||||
listener.element.removeEventListener("click", listener.fn);
|
||||
listener.element.removeEventListener('click', listener.fn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,42 +46,42 @@ function setUpMoodleGpt(config: Config) {
|
||||
// Removing events if there are already declared
|
||||
if (listeners.length > 0) {
|
||||
for (const listener of listeners) {
|
||||
if (config.cursor) listener.element.style.cursor = "initial";
|
||||
listener.element.removeEventListener("click", listener.fn);
|
||||
if (config.cursor) listener.element.style.cursor = 'initial';
|
||||
listener.element.removeEventListener('click', listener.fn);
|
||||
}
|
||||
if (config.title) titleIndications("Removed");
|
||||
if (config.title) titleIndications('Removed');
|
||||
listeners.length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Query to find inputs and forms
|
||||
const inputTypeQuery = ["checkbox", "radio", "text", "number"]
|
||||
.map((e) => `input[type="${e}"]`)
|
||||
.join(",");
|
||||
const inputQuery = inputTypeQuery + ", textarea, select, [contenteditable]";
|
||||
const forms = document.querySelectorAll(".formulation");
|
||||
const inputTypeQuery = ['checkbox', 'radio', 'text', 'number']
|
||||
.map(e => `input[type="${e}"]`)
|
||||
.join(',');
|
||||
const inputQuery = inputTypeQuery + ', textarea, select, [contenteditable]';
|
||||
const forms = document.querySelectorAll('.formulation');
|
||||
|
||||
// For each form we inject a function on the queqtion
|
||||
for (const form of forms) {
|
||||
const questionElement: HTMLElement | null = form.querySelector(".qtext");
|
||||
const questionElement: HTMLElement | null = form.querySelector('.qtext');
|
||||
|
||||
if (questionElement === null) continue;
|
||||
|
||||
if (config.cursor) questionElement.style.cursor = "pointer";
|
||||
if (config.cursor) questionElement.style.cursor = 'pointer';
|
||||
|
||||
const injectionFunction = reply.bind(null, {
|
||||
config,
|
||||
questionElement,
|
||||
form: form as HTMLElement,
|
||||
inputQuery,
|
||||
removeListener: () => removeListener(questionElement),
|
||||
removeListener: () => removeListener(questionElement)
|
||||
});
|
||||
|
||||
listeners.push({ element: questionElement, fn: injectionFunction });
|
||||
questionElement.addEventListener("click", injectionFunction);
|
||||
questionElement.addEventListener('click', injectionFunction);
|
||||
}
|
||||
|
||||
if (config.title) titleIndications("Injected");
|
||||
if (config.title) titleIndications('Injected');
|
||||
}
|
||||
|
||||
export { codeListener, removeListener, setUpMoodleGpt };
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import normalizeText from "@utils/normalize-text";
|
||||
import htmlTableToString from "@utils/html-table-to-string";
|
||||
import normalizeText from '@utils/normalize-text';
|
||||
import htmlTableToString from '@utils/html-table-to-string';
|
||||
|
||||
/**
|
||||
* Normalize the question as text and add sub informations
|
||||
@@ -12,19 +12,15 @@ function createAndNormalizeQuestion(questionContainer: HTMLElement) {
|
||||
|
||||
// We remove unnecessary information
|
||||
const accesshideElements: NodeListOf<HTMLElement> =
|
||||
questionContainer.querySelectorAll(".accesshide");
|
||||
questionContainer.querySelectorAll('.accesshide');
|
||||
for (const useless of accesshideElements) {
|
||||
question = question.replace(useless.innerText, "");
|
||||
question = question.replace(useless.innerText, '');
|
||||
}
|
||||
|
||||
// Make tables more readable for chat-gpt
|
||||
const tables: NodeListOf<HTMLTableElement> =
|
||||
questionContainer.querySelectorAll(".qtext table");
|
||||
const tables: NodeListOf<HTMLTableElement> = questionContainer.querySelectorAll('.qtext table');
|
||||
for (const table of tables) {
|
||||
question = question.replace(
|
||||
table.innerText,
|
||||
"\n" + htmlTableToString(table) + "\n"
|
||||
);
|
||||
question = question.replace(table.innerText, '\n' + htmlTableToString(table) + '\n');
|
||||
}
|
||||
|
||||
return normalizeText(question, false);
|
||||
|
||||
+16
-19
@@ -1,6 +1,6 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import normalizeText from "@utils/normalize-text";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import normalizeText from '@utils/normalize-text';
|
||||
|
||||
type History = {
|
||||
url: string | null;
|
||||
@@ -9,9 +9,9 @@ type History = {
|
||||
};
|
||||
|
||||
enum ROLE {
|
||||
SYSTEM = "system",
|
||||
USER = "user",
|
||||
ASSISTANT = "assistant",
|
||||
SYSTEM = 'system',
|
||||
USER = 'user',
|
||||
ASSISTANT = 'assistant'
|
||||
}
|
||||
|
||||
const INSTRUCTION: string = `
|
||||
@@ -32,9 +32,9 @@ const history: History = {
|
||||
url: null,
|
||||
system: {
|
||||
role: ROLE.SYSTEM,
|
||||
content: INSTRUCTION,
|
||||
content: INSTRUCTION
|
||||
},
|
||||
history: [],
|
||||
history: []
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -43,10 +43,7 @@ const history: History = {
|
||||
* @param question
|
||||
* @returns
|
||||
*/
|
||||
async function getChatGPTResponse(
|
||||
config: Config,
|
||||
question: string
|
||||
): Promise<GPTAnswer> {
|
||||
async function getChatGPTResponse(config: Config, question: string): Promise<GPTAnswer> {
|
||||
const URL = location.hostname + location.pathname;
|
||||
|
||||
// We reset the history when we enter a new moodle quiz or when it's desactivate
|
||||
@@ -60,11 +57,11 @@ async function getChatGPTResponse(
|
||||
|
||||
const message = { role: ROLE.USER, content: question };
|
||||
|
||||
const req = await fetch("https://api.openai.com/v1/chat/completions", {
|
||||
method: "POST",
|
||||
const req = await fetch('https://api.openai.com/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${config.apiKey}`,
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${config.apiKey}`
|
||||
},
|
||||
signal: config.timeout ? controller.signal : null,
|
||||
body: JSON.stringify({
|
||||
@@ -73,8 +70,8 @@ async function getChatGPTResponse(
|
||||
temperature: 0.8,
|
||||
top_p: 1.0,
|
||||
presence_penalty: 1.0,
|
||||
stop: null,
|
||||
}),
|
||||
stop: null
|
||||
})
|
||||
});
|
||||
|
||||
clearTimeout(timeoutControler);
|
||||
@@ -91,7 +88,7 @@ async function getChatGPTResponse(
|
||||
return {
|
||||
question,
|
||||
response,
|
||||
normalizedResponse: normalizeText(response),
|
||||
normalizedResponse: normalizeText(response)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import type Config from "@typing/config";
|
||||
import handleClipboard from "@core/questions/clipboard";
|
||||
import handleContentEditable from "@core/questions/contenteditable";
|
||||
import handleNumber from "@core/questions/number";
|
||||
import handleRadio from "@core/questions/radio";
|
||||
import handleCheckbox from "@core/questions/checkbox";
|
||||
import handleSelect from "@core/questions/select";
|
||||
import handleTextbox from "@core/questions/textbox";
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import type Config from '@typing/config';
|
||||
import handleClipboard from '@core/questions/clipboard';
|
||||
import handleContentEditable from '@core/questions/contenteditable';
|
||||
import handleNumber from '@core/questions/number';
|
||||
import handleRadio from '@core/questions/radio';
|
||||
import handleCheckbox from '@core/questions/checkbox';
|
||||
import handleSelect from '@core/questions/select';
|
||||
import handleTextbox from '@core/questions/textbox';
|
||||
|
||||
type Props = {
|
||||
config: Config;
|
||||
@@ -31,7 +31,7 @@ function autoCompleteMode(props: Props) {
|
||||
handleNumber,
|
||||
handleSelect,
|
||||
handleRadio,
|
||||
handleCheckbox,
|
||||
handleCheckbox
|
||||
];
|
||||
|
||||
for (const handler of handlers) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import handleClipboard from "@core/questions/clipboard";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import handleClipboard from '@core/questions/clipboard';
|
||||
|
||||
type Props = {
|
||||
config: Config;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
|
||||
type Props = {
|
||||
questionElement: HTMLElement;
|
||||
@@ -21,15 +21,13 @@ function questionToAnswerMode(props: Props) {
|
||||
questionElement.textContent = props.gptAnswer.response;
|
||||
|
||||
// Format the content
|
||||
questionElement.style.whiteSpace = "pre-wrap";
|
||||
questionElement.style.whiteSpace = 'pre-wrap';
|
||||
|
||||
// To go back to the question / answer
|
||||
let contentIsResponse = true;
|
||||
questionElement.addEventListener("click", function () {
|
||||
questionElement.style.whiteSpace = contentIsResponse ? "" : "pre-warp";
|
||||
questionElement.textContent = contentIsResponse
|
||||
? questionBackup
|
||||
: props.gptAnswer.response;
|
||||
questionElement.addEventListener('click', function () {
|
||||
questionElement.style.whiteSpace = contentIsResponse ? '' : 'pre-warp';
|
||||
questionElement.textContent = contentIsResponse ? questionBackup : props.gptAnswer.response;
|
||||
|
||||
contentIsResponse = !contentIsResponse;
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import Logs from "@utils/logs";
|
||||
import normalizeText from "@utils/normalize-text";
|
||||
import { pickBestReponse } from "@utils/pick-best-response";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import Logs from '@utils/logs';
|
||||
import normalizeText from '@utils/normalize-text';
|
||||
import { pickBestReponse } from '@utils/pick-best-response';
|
||||
|
||||
/**
|
||||
* Handle input checkbox elements
|
||||
@@ -18,18 +18,18 @@ function handleCheckbox(
|
||||
const firstInput = inputList?.[0] as HTMLInputElement;
|
||||
|
||||
// Handle the case the input is not a checkbox
|
||||
if (!firstInput || firstInput.type !== "checkbox") {
|
||||
if (!firstInput || firstInput.type !== 'checkbox') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const corrects = gptAnswer.normalizedResponse.split("\n");
|
||||
const corrects = gptAnswer.normalizedResponse.split('\n');
|
||||
|
||||
const possibleAnswers = Array.from(inputList)
|
||||
.map((inp) => ({
|
||||
.map(inp => ({
|
||||
element: inp,
|
||||
value: normalizeText(inp?.parentElement?.textContent ?? ""),
|
||||
value: normalizeText(inp?.parentElement?.textContent ?? '')
|
||||
}))
|
||||
.filter((obj) => obj.value !== "");
|
||||
.filter(obj => obj.value !== '');
|
||||
|
||||
for (const correct of corrects) {
|
||||
const bestAnswer = pickBestReponse(correct, possibleAnswers);
|
||||
@@ -40,13 +40,9 @@ function handleCheckbox(
|
||||
|
||||
const correctInput = bestAnswer.element as HTMLInputElement;
|
||||
if (config.mouseover) {
|
||||
correctInput.addEventListener(
|
||||
"mouseover",
|
||||
() => (correctInput.checked = true),
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
correctInput.addEventListener('mouseover', () => (correctInput.checked = true), {
|
||||
once: true
|
||||
});
|
||||
} else {
|
||||
correctInput.checked = true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import titleIndications from "@utils/title-indications";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import titleIndications from '@utils/title-indications';
|
||||
|
||||
/**
|
||||
* Copy the response in the clipboard if we can automaticaly fill the question
|
||||
@@ -8,7 +8,7 @@ import titleIndications from "@utils/title-indications";
|
||||
* @param gptAnswer
|
||||
*/
|
||||
function handleClipboard(config: Config, gptAnswer: GPTAnswer) {
|
||||
if (config.title) titleIndications("Copied to clipboard");
|
||||
if (config.title) titleIndications('Copied to clipboard');
|
||||
navigator.clipboard.writeText(gptAnswer.response);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
|
||||
/**
|
||||
* Hanlde contenteditable elements
|
||||
@@ -17,15 +17,15 @@ function handleContentEditable(
|
||||
|
||||
if (
|
||||
inputList.length !== 1 || // for now we don't handle many input for editable textcontent
|
||||
input.getAttribute("contenteditable") !== "true"
|
||||
input.getAttribute('contenteditable') !== 'true'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.typing) {
|
||||
let index = 0;
|
||||
input.addEventListener("keydown", function (event: KeyboardEvent) {
|
||||
if (event.key === "Backspace") index = gptAnswer.response.length + 1;
|
||||
input.addEventListener('keydown', function (event: KeyboardEvent) {
|
||||
if (event.key === 'Backspace') index = gptAnswer.response.length + 1;
|
||||
if (index > gptAnswer.response.length) return;
|
||||
event.preventDefault();
|
||||
input.textContent = gptAnswer.response.slice(0, ++index);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
|
||||
/**
|
||||
* Handle number input
|
||||
@@ -17,24 +17,22 @@ function handleNumber(
|
||||
|
||||
if (
|
||||
inputList.length !== 1 || // for now we don't handle many input number
|
||||
input.type !== "number"
|
||||
input.type !== 'number'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const number = gptAnswer.normalizedResponse
|
||||
.match(/\d+([,.]\d+)?/gi)?.[0]
|
||||
?.replace(",", ".");
|
||||
const number = gptAnswer.normalizedResponse.match(/\d+([,.]\d+)?/gi)?.[0]?.replace(',', '.');
|
||||
|
||||
if (number === undefined) return false;
|
||||
|
||||
if (config.typing) {
|
||||
let index = 0;
|
||||
input.addEventListener("keydown", function (event: Event) {
|
||||
input.addEventListener('keydown', function (event: Event) {
|
||||
event.preventDefault();
|
||||
if ((<KeyboardEvent>event).key === "Backspace") index = number.length + 1;
|
||||
if ((<KeyboardEvent>event).key === 'Backspace') index = number.length + 1;
|
||||
if (index > number.length) return;
|
||||
if (number.slice(index, index + 1) === ".") ++index;
|
||||
if (number.slice(index, index + 1) === '.') ++index;
|
||||
input.value = number.slice(0, ++index);
|
||||
});
|
||||
} else {
|
||||
|
||||
+13
-20
@@ -1,8 +1,8 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import Logs from "@utils/logs";
|
||||
import normalizeText from "@utils/normalize-text";
|
||||
import { pickBestReponse } from "@utils/pick-best-response";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import Logs from '@utils/logs';
|
||||
import normalizeText from '@utils/normalize-text';
|
||||
import { pickBestReponse } from '@utils/pick-best-response';
|
||||
|
||||
/**
|
||||
* Handle input radio elements
|
||||
@@ -18,21 +18,18 @@ function handleRadio(
|
||||
const firstInput = inputList?.[0] as HTMLInputElement;
|
||||
|
||||
// Handle the case the input is not a radio
|
||||
if (!firstInput || firstInput.type !== "radio") {
|
||||
if (!firstInput || firstInput.type !== 'radio') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const possibleAnswers = Array.from(inputList)
|
||||
.map((inp) => ({
|
||||
.map(inp => ({
|
||||
element: inp,
|
||||
value: normalizeText(inp?.parentElement?.textContent ?? ""),
|
||||
value: normalizeText(inp?.parentElement?.textContent ?? '')
|
||||
}))
|
||||
.filter((obj) => obj.value !== "");
|
||||
.filter(obj => obj.value !== '');
|
||||
|
||||
const bestAnswer = pickBestReponse(
|
||||
gptAnswer.normalizedResponse,
|
||||
possibleAnswers
|
||||
);
|
||||
const bestAnswer = pickBestReponse(gptAnswer.normalizedResponse, possibleAnswers);
|
||||
|
||||
if (config.logs && bestAnswer.value) {
|
||||
Logs.bestAnswer(bestAnswer.value, bestAnswer.similarity);
|
||||
@@ -40,13 +37,9 @@ function handleRadio(
|
||||
|
||||
const correctInput = bestAnswer.element as HTMLInputElement;
|
||||
if (config.mouseover) {
|
||||
correctInput.addEventListener(
|
||||
"mouseover",
|
||||
() => (correctInput.checked = true),
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
correctInput.addEventListener('mouseover', () => (correctInput.checked = true), {
|
||||
once: true
|
||||
});
|
||||
} else {
|
||||
correctInput.checked = true;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import Logs from "@utils/logs";
|
||||
import normalizeText from "@utils/normalize-text";
|
||||
import { pickBestReponse } from "@utils/pick-best-response";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
import Logs from '@utils/logs';
|
||||
import normalizeText from '@utils/normalize-text';
|
||||
import { pickBestReponse } from '@utils/pick-best-response';
|
||||
|
||||
/**
|
||||
* Handle select elements (and put in order select)
|
||||
@@ -16,23 +16,23 @@ function handleSelect(
|
||||
inputList: NodeListOf<HTMLElement>,
|
||||
gptAnswer: GPTAnswer
|
||||
): boolean {
|
||||
if (inputList.length === 0 || inputList[0].tagName !== "SELECT") return false;
|
||||
if (inputList.length === 0 || inputList[0].tagName !== 'SELECT') return false;
|
||||
|
||||
const corrects = gptAnswer.normalizedResponse.split("\n");
|
||||
const corrects = gptAnswer.normalizedResponse.split('\n');
|
||||
|
||||
if (config.logs) Logs.array(corrects);
|
||||
|
||||
for (let i = 0; i < inputList.length; ++i) {
|
||||
if (!corrects[i]) break;
|
||||
|
||||
const options = inputList[i].querySelectorAll("option");
|
||||
const options = inputList[i].querySelectorAll('option');
|
||||
|
||||
const possibleAnswers = Array.from(options)
|
||||
.map((opt) => ({
|
||||
.map(opt => ({
|
||||
element: opt,
|
||||
value: normalizeText(opt.textContent ?? ""),
|
||||
value: normalizeText(opt.textContent ?? '')
|
||||
}))
|
||||
.filter((obj) => obj.value !== "");
|
||||
.filter(obj => obj.value !== '');
|
||||
|
||||
const bestAnswer = pickBestReponse(corrects[i], possibleAnswers);
|
||||
|
||||
@@ -41,18 +41,14 @@ function handleSelect(
|
||||
}
|
||||
|
||||
const correctOption = bestAnswer.element as HTMLOptionElement;
|
||||
const currentSelect = correctOption.closest("select");
|
||||
const currentSelect = correctOption.closest('select');
|
||||
|
||||
if (currentSelect === null) continue;
|
||||
|
||||
if (config.mouseover) {
|
||||
currentSelect.addEventListener(
|
||||
"click",
|
||||
() => (correctOption.selected = true),
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
currentSelect.addEventListener('click', () => (correctOption.selected = true), {
|
||||
once: true
|
||||
});
|
||||
} else {
|
||||
correctOption.selected = true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type Config from "@typing/config";
|
||||
import type GPTAnswer from "@typing/gptAnswer";
|
||||
import type Config from '@typing/config';
|
||||
import type GPTAnswer from '@typing/gptAnswer';
|
||||
|
||||
/**
|
||||
* Handle textbox
|
||||
@@ -17,16 +17,16 @@ function handleTextbox(
|
||||
|
||||
if (
|
||||
inputList.length !== 1 || // for now we don't handle many input text
|
||||
(input.tagName !== "TEXTAREA" && input.type !== "text")
|
||||
(input.tagName !== 'TEXTAREA' && input.type !== 'text')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (config.typing) {
|
||||
let index = 0;
|
||||
input.addEventListener("keydown", function (event: Event) {
|
||||
input.addEventListener('keydown', function (event: Event) {
|
||||
event.preventDefault();
|
||||
if ((<KeyboardEvent>event).key === "Backspace") {
|
||||
if ((<KeyboardEvent>event).key === 'Backspace') {
|
||||
index = gptAnswer.response.length + 1;
|
||||
}
|
||||
if (index > gptAnswer.response.length) return;
|
||||
|
||||
+20
-25
@@ -1,10 +1,10 @@
|
||||
import type Config from "@typing/config";
|
||||
import Logs from "@utils/logs";
|
||||
import getChatGPTResponse from "./get-response";
|
||||
import createAndNormalizeQuestion from "./create-question";
|
||||
import clipboardMode from "./modes/clipboard";
|
||||
import questionToAnswerMode from "./modes/question-to-answer";
|
||||
import autoCompleteMode from "./modes/autocomplete";
|
||||
import type Config from '@typing/config';
|
||||
import Logs from '@utils/logs';
|
||||
import getChatGPTResponse from './get-response';
|
||||
import createAndNormalizeQuestion from './create-question';
|
||||
import clipboardMode from './modes/clipboard';
|
||||
import questionToAnswerMode from './modes/question-to-answer';
|
||||
import autoCompleteMode from './modes/autocomplete';
|
||||
|
||||
type Props = {
|
||||
config: Config;
|
||||
@@ -20,24 +20,19 @@ type Props = {
|
||||
* @returns
|
||||
*/
|
||||
async function reply(props: Props): Promise<void> {
|
||||
if (props.config.cursor) props.questionElement.style.cursor = "wait";
|
||||
if (props.config.cursor) props.questionElement.style.cursor = 'wait';
|
||||
|
||||
const question = createAndNormalizeQuestion(props.form);
|
||||
const inputList: NodeListOf<HTMLElement> = props.form.querySelectorAll(
|
||||
props.inputQuery
|
||||
);
|
||||
const inputList: NodeListOf<HTMLElement> = props.form.querySelectorAll(props.inputQuery);
|
||||
|
||||
const gptAnswer = await getChatGPTResponse(props.config, question).catch(
|
||||
(error) => ({
|
||||
error,
|
||||
})
|
||||
);
|
||||
const gptAnswer = await getChatGPTResponse(props.config, question).catch(error => ({
|
||||
error
|
||||
}));
|
||||
|
||||
const haveError = typeof gptAnswer === "object" && "error" in gptAnswer;
|
||||
const haveError = typeof gptAnswer === 'object' && 'error' in gptAnswer;
|
||||
|
||||
if (props.config.cursor) {
|
||||
props.questionElement.style.cursor =
|
||||
props.config.infinite || haveError ? "pointer" : "initial";
|
||||
props.questionElement.style.cursor = props.config.infinite || haveError ? 'pointer' : 'initial';
|
||||
}
|
||||
|
||||
if (haveError) {
|
||||
@@ -51,28 +46,28 @@ async function reply(props: Props): Promise<void> {
|
||||
}
|
||||
|
||||
switch (props.config.mode) {
|
||||
case "clipboard":
|
||||
case 'clipboard':
|
||||
clipboardMode({
|
||||
config: props.config,
|
||||
questionElement: props.questionElement,
|
||||
gptAnswer,
|
||||
removeListener: props.removeListener,
|
||||
removeListener: props.removeListener
|
||||
});
|
||||
break;
|
||||
case "question-to-answer":
|
||||
case 'question-to-answer':
|
||||
questionToAnswerMode({
|
||||
gptAnswer,
|
||||
questionElement: props.questionElement,
|
||||
removeListener: props.removeListener,
|
||||
removeListener: props.removeListener
|
||||
});
|
||||
break;
|
||||
case "autocomplete":
|
||||
case 'autocomplete':
|
||||
autoCompleteMode({
|
||||
config: props.config,
|
||||
gptAnswer,
|
||||
inputList,
|
||||
questionElement: props.questionElement,
|
||||
removeListener: props.removeListener,
|
||||
removeListener: props.removeListener
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,10 +1,10 @@
|
||||
import type Config from "@typing/config";
|
||||
import { codeListener, setUpMoodleGpt } from "./core/code-listener";
|
||||
import type Config from '@typing/config';
|
||||
import { codeListener, setUpMoodleGpt } from './core/code-listener';
|
||||
|
||||
chrome.storage.sync.get(["moodleGPT"]).then(function (storage) {
|
||||
chrome.storage.sync.get(['moodleGPT']).then(function (storage) {
|
||||
const config: Config = storage.moodleGPT;
|
||||
|
||||
if (!config) throw new Error("Please configure MoodleGPT into the extension");
|
||||
if (!config) throw new Error('Please configure MoodleGPT into the extension');
|
||||
|
||||
if (config.code) {
|
||||
codeListener(config);
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ type Config = {
|
||||
title?: boolean;
|
||||
timeout?: boolean;
|
||||
history?: boolean;
|
||||
mode?: "autocomplete" | "question-to-answer" | "clipboard";
|
||||
mode?: 'autocomplete' | 'question-to-answer' | 'clipboard';
|
||||
};
|
||||
|
||||
export default Config;
|
||||
|
||||
@@ -5,37 +5,32 @@
|
||||
*/
|
||||
function htmlTableToString(table: HTMLTableElement) {
|
||||
const tab: string[][] = [];
|
||||
const lines = Array.from(table.querySelectorAll("tr"));
|
||||
const lines = Array.from(table.querySelectorAll('tr'));
|
||||
const maxColumnsLength: number[] = [];
|
||||
lines.map((line) => {
|
||||
const cells = Array.from(line.querySelectorAll("td, th"));
|
||||
lines.map(line => {
|
||||
const cells = Array.from(line.querySelectorAll('td, th'));
|
||||
const cellsContent = cells.map((cell, index) => {
|
||||
const content = cell.textContent?.trim();
|
||||
maxColumnsLength[index] = Math.max(
|
||||
maxColumnsLength[index] || 0,
|
||||
content?.length || 0
|
||||
);
|
||||
return content ?? "";
|
||||
maxColumnsLength[index] = Math.max(maxColumnsLength[index] || 0, content?.length || 0);
|
||||
return content ?? '';
|
||||
});
|
||||
tab.push(cellsContent);
|
||||
});
|
||||
|
||||
const lineSeparationSize =
|
||||
maxColumnsLength.reduce((a, b) => a + b) + tab[0].length * 3 + 1;
|
||||
const lineSeparation =
|
||||
"\n" + Array(lineSeparationSize).fill("-").join("") + "\n";
|
||||
const lineSeparationSize = maxColumnsLength.reduce((a, b) => a + b) + tab[0].length * 3 + 1;
|
||||
const lineSeparation = '\n' + Array(lineSeparationSize).fill('-').join('') + '\n';
|
||||
|
||||
const mappedTab = tab.map((line) => {
|
||||
const mappedTab = tab.map(line => {
|
||||
const mappedLine = line.map((content, index) =>
|
||||
content.padEnd(
|
||||
maxColumnsLength[index],
|
||||
"\u00A0" // For no matching with \s
|
||||
'\u00A0' // For no matching with \s
|
||||
)
|
||||
);
|
||||
return "| " + mappedLine.join(" | ") + " |";
|
||||
return '| ' + mappedLine.join(' | ') + ' |';
|
||||
});
|
||||
const head = mappedTab.shift();
|
||||
return head + lineSeparation + mappedTab.join("\n");
|
||||
return head + lineSeparation + mappedTab.join('\n');
|
||||
}
|
||||
|
||||
export default htmlTableToString;
|
||||
|
||||
+9
-9
@@ -1,28 +1,28 @@
|
||||
import GPTAnswer from "@typing/gptAnswer";
|
||||
import { toPourcentage } from "./pick-best-response";
|
||||
import GPTAnswer from '@typing/gptAnswer';
|
||||
import { toPourcentage } from './pick-best-response';
|
||||
|
||||
class Logs {
|
||||
static question(text: string) {
|
||||
const css = "color: cyan";
|
||||
console.log("%c[QUESTION]: %s", css, text);
|
||||
const css = 'color: cyan';
|
||||
console.log('%c[QUESTION]: %s', css, text);
|
||||
}
|
||||
|
||||
static bestAnswer(answer: string, similarity: number) {
|
||||
const css = "color: green";
|
||||
const css = 'color: green';
|
||||
console.log(
|
||||
"%c[BEST ANSWER]: %s",
|
||||
'%c[BEST ANSWER]: %s',
|
||||
css,
|
||||
`"${answer}" with a similarity of ${toPourcentage(similarity)}`
|
||||
);
|
||||
}
|
||||
|
||||
static array(arr: unknown[]) {
|
||||
console.log("[CORRECTS] ", arr);
|
||||
console.log('[CORRECTS] ', arr);
|
||||
}
|
||||
|
||||
static response(gptAnswer: GPTAnswer) {
|
||||
console.log("Original:\n" + gptAnswer.response);
|
||||
console.log("Normalized:\n" + gptAnswer.normalizedResponse);
|
||||
console.log('Original:\n' + gptAnswer.response);
|
||||
console.log('Normalized:\n' + gptAnswer.normalizedResponse);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ function normalizeText(text: string, toLowerCase: boolean = true) {
|
||||
if (toLowerCase) text = text.toLowerCase();
|
||||
|
||||
const normalizedText = text
|
||||
.replace(/\n+/gi, "\n") //remove duplicate new lines
|
||||
.replace(/(\n\s*\n)+/g, "\n") //remove useless white space from textcontent
|
||||
.replace(/[ \t]+/gi, " ") //replace multiples space or tabs by a space
|
||||
.replace(/\n+/gi, '\n') //remove duplicate new lines
|
||||
.replace(/(\n\s*\n)+/g, '\n') //remove useless white space from textcontent
|
||||
.replace(/[ \t]+/gi, ' ') //replace multiples space or tabs by a space
|
||||
.trim()
|
||||
// We remove the following content because sometimes ChatGPT will reply: "answer d"
|
||||
.replace(/^[a-z\d]\.\s/gi, "") //a. text, b. text, c. text, 1. text, 2. text, 3.text
|
||||
.replace(/\n[a-z\d]\.\s/gi, "\n"); //same but with new line
|
||||
.replace(/^[a-z\d]\.\s/gi, '') //a. text, b. text, c. text, 1. text, 2. text, 3.text
|
||||
.replace(/\n[a-z\d]\.\s/gi, '\n'); //same but with new line
|
||||
|
||||
return normalizedText;
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ function levenshteinDistance(str1: string, str2: string) {
|
||||
if (str2.length === 0) return str1.length;
|
||||
|
||||
const matrix: number[][] = [];
|
||||
const str1WithoutSpaces = str1.replace(/\s+/, "");
|
||||
const str2WithoutSpaces = str2.replace(/\s+/, "");
|
||||
const str1WithoutSpaces = str1.replace(/\s+/, '');
|
||||
const str2WithoutSpaces = str2.replace(/\s+/, '');
|
||||
|
||||
for (let i = 0; i <= str1WithoutSpaces.length; ++i) {
|
||||
matrix.push([i]);
|
||||
@@ -33,8 +33,7 @@ function levenshteinDistance(str1: string, str2: string) {
|
||||
: Math.min(
|
||||
matrix[i - 1][j] + 1,
|
||||
matrix[i][j - 1] + 1,
|
||||
matrix[i - 1][j - 1] +
|
||||
(str1WithoutSpaces[i - 1] === str2WithoutSpaces[j - 1] ? 0 : 1)
|
||||
matrix[i - 1][j - 1] + (str1WithoutSpaces[i - 1] === str2WithoutSpaces[j - 1] ? 0 : 1)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -67,7 +66,7 @@ export function pickBestReponse(
|
||||
let bestResponse: BestResponse = {
|
||||
element: null,
|
||||
similarity: 0,
|
||||
value: null,
|
||||
value: null
|
||||
};
|
||||
for (const obj of arr) {
|
||||
const similarity = sentenceSimilarity(obj.value, answer);
|
||||
@@ -100,7 +99,7 @@ export function pickResponsesWithSimilarityGreaterThan(
|
||||
responses.push({
|
||||
similarity,
|
||||
value: obj.value,
|
||||
element: obj.element,
|
||||
element: obj.element
|
||||
});
|
||||
}
|
||||
return responses.sort((a, b) => a.similarity - b.similarity);
|
||||
@@ -111,5 +110,5 @@ export function pickResponsesWithSimilarityGreaterThan(
|
||||
* @param similarity
|
||||
*/
|
||||
export function toPourcentage(similarity: number): string {
|
||||
return Math.round(similarity * 100 * 100) / 100 + "%";
|
||||
return Math.round(similarity * 100 * 100) / 100 + '%';
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Segeo UI";
|
||||
font-family: 'Segeo UI';
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
@@ -17,23 +17,20 @@
|
||||
<div class="inp">
|
||||
<input type="checkbox" />
|
||||
<label
|
||||
>a. Systems Administrator: Managing and maintaining computer systems
|
||||
and networks.</label
|
||||
>a. Systems Administrator: Managing and maintaining computer systems and
|
||||
networks.</label
|
||||
>
|
||||
</div>
|
||||
<div class="inp">
|
||||
<input type="checkbox" />
|
||||
<label
|
||||
>b. Software Developer: Designing, coding, testing, and maintaining
|
||||
software applications.</label
|
||||
>b. Software Developer: Designing, coding, testing, and maintaining software
|
||||
applications.</label
|
||||
>
|
||||
</div>
|
||||
<div class="inp">
|
||||
<input type="checkbox" />
|
||||
<label>
|
||||
c. Professional Chef: Creating delicious meals in a restaurant
|
||||
kitchen.
|
||||
</label>
|
||||
<label> c. Professional Chef: Creating delicious meals in a restaurant kitchen. </label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -276,8 +273,8 @@
|
||||
<section class="formulation">
|
||||
<div class="qtext">
|
||||
<p>
|
||||
Gives a "reverseWorld" function in javascript which takes as a
|
||||
parameter a word and flips it in the opposite direction
|
||||
Gives a "reverseWorld" function in javascript which takes as a parameter a word and flips
|
||||
it in the opposite direction
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
/* Reset real moodle inputs to try in real env */
|
||||
for (const option of document.querySelectorAll("option")) {
|
||||
for (const option of document.querySelectorAll('option')) {
|
||||
option.selected = false;
|
||||
option.disabled = false;
|
||||
option.closest("select").disabled = false;
|
||||
option.closest('select').disabled = false;
|
||||
}
|
||||
|
||||
for (const input of document.querySelectorAll(
|
||||
'input[type="radio"], input[type="checkbox"]'
|
||||
)) {
|
||||
for (const input of document.querySelectorAll('input[type="radio"], input[type="checkbox"]')) {
|
||||
input.checked = false;
|
||||
input.disabled = false;
|
||||
}
|
||||
|
||||
for (const icon of document.querySelectorAll(".text-danger, .text-success")) {
|
||||
for (const icon of document.querySelectorAll('.text-danger, .text-success')) {
|
||||
icon.remove();
|
||||
}
|
||||
|
||||
for (const feedback of document.querySelectorAll(".specificfeedback")) {
|
||||
for (const feedback of document.querySelectorAll('.specificfeedback')) {
|
||||
feedback.remove();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user