This commit is contained in:
yoannchb-pro
2025-01-14 12:23:13 -05:00
parent ea57a315b7
commit 16a82fe3d8
50 changed files with 404 additions and 243 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,7 +1,7 @@
{
"manifest_version": 3,
"name": "MoodleGPT",
"version": "1.1.1",
"version": "1.1.2",
"description": "Hidden chat-gpt for your moodle quiz",
"permissions": ["storage"],
"action": {
+30 -20
View File
@@ -7,13 +7,7 @@
<title>MoodleGPT</title>
<link rel="stylesheet" href="style.css" />
<!-- Should always be at the top -->
<script src="./js/utils.js" defer></script>
<script src="./js/index.js" defer></script>
<script src="./js/modeHandler.js" defer></script>
<script src="./js/gptVersion.js" defer></script>
<script src="./js/version.js" defer></script>
<script src="./popup.js" defer></script>
<link rel="icon" type="image/png" href="../icon.png" />
<link
@@ -41,20 +35,35 @@
<p id="version"></p>
</div>
</div>
<div class="line center">
<label for="apiKey" class="textLabel">Api Key<span class="required">*</span>:</label>
<input id="apiKey" type="text" />
<!-- SETTINGS -->
<div class="settings" id="settings">
<div class="line center">
<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>
<input type="text" id="model" list="models" />
<datalist id="models"></datalist>
<i id="check-model" title="Test" style="cursor: pointer" class="fa-solid fa-play"></i>
</div>
</div>
<div class="line center">
<label for="model" class="textLabel">GPT Model<span class="required">*</span>:</label>
<input type="text" id="model" list="models" />
<datalist id="models"></datalist>
<!-- ADVANCED SETTINGS -->
<div class="settings" id="advanced-settings" style="display: none">
<div class="line center">
<label for="code" class="textLabel">Code:</label>
<input id="code" type="text" />
</div>
</div>
<div class="line center">
<label for="code" class="textLabel">Code:</label>
<input id="code" type="text" />
<!-- SWITCH SETTINGS MODE -->
<div class="line center mt">
<a id="switch-settings" href="#">Advanced settings</a>
</div>
<div class="line mt">
<div class="line center-y mt">
<i class="fa-solid fa-robot"></i>
<p>Mode:</p>
</div>
@@ -69,9 +78,9 @@
</li>
</ul>
</div>
<div class="line mt">
<div class="line mt center-y">
<i class="fa-solid fa-gear"></i>
<p>Settings:</p>
<p>Options:</p>
</div>
<div class="line center" style="gap: 2rem">
<div class="col">
@@ -122,6 +131,7 @@
</div>
<div class="line center">
<a
class="donate"
href="https://www.buymeacoffee.com/yoannchbpro"
target="_blank"
rel="noopener noreferrer"
-54
View File
@@ -1,54 +0,0 @@
'use strict';
const apiKeySelector = document.querySelector('#apiKey');
const inputModel = document.querySelector('#model');
const modelsList = document.querySelector('#models');
const imagesIntegrationLine = document.querySelector('#includeImages-line');
/**
* Check if the gpt version is at least 4 to show the option 'Include images'
*/
function checkCanIncludeImages() {
const version = inputModel.value;
if (isCurrentVersionSupportingImages(version)) {
imagesIntegrationLine.style.display = 'flex';
} else {
imagesIntegrationLine.style.display = 'none';
}
}
inputModel.addEventListener('input', checkCanIncludeImages);
// We populate the datalist of the chatgpt model
async function populateDatalistWithGptVersions() {
const apiKey = apiKeySelector.value?.trim();
if (!apiKey) return;
inputModel.innerHTML = '';
try {
const req = await fetch('https://api.openai.com/v1/models', {
headers: {
Authorization: `Bearer ${apiKey}`
}
});
const rep = await req.json();
rep.data.sort((a, b) => b.id.localeCompare(a.id)); // we sort the model to get the best chatgpt version first
const models = rep.data.filter(model => model.id.startsWith('gpt'));
for (const model of models) {
const opt = document.createElement('option');
opt.value = model.id;
opt.textContent = model.id;
modelsList.appendChild(opt);
}
checkCanIncludeImages();
} catch (err) {
console.error(err);
showMessage({ msg: 'Failed to fetch last ChatGPT versions', error: true });
}
}
inputModel.addEventListener('focus', populateDatalistWithGptVersions);
-88
View File
@@ -1,88 +0,0 @@
'use strict';
const saveBtn = document.querySelector('.save');
// inputs id
const inputsText = ['apiKey', 'code', 'model'];
const inputsCheckbox = [
'logs',
'title',
'cursor',
'typing',
'mouseover',
'infinite',
'timeout',
'history',
'includeImages'
];
// Save the configuration
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, includeImages] =
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 });
return;
}
if (code.length > 0 && code.length < 2) {
showMessage({
msg: 'The code should at least contain 2 characters',
error: true
});
return;
}
chrome.storage.sync.set({
moodleGPT: {
apiKey,
code,
model,
logs,
title,
cursor,
typing,
mouseover,
infinite,
timeout,
history,
includeImages,
mode: actualMode
}
});
showMessage({ msg: 'Configuration saved' });
});
// we load back the configuration
chrome.storage.sync.get(['moodleGPT']).then(function (storage) {
const config = storage.moodleGPT;
if (config) {
if (config.mode) {
actualMode = config.mode;
for (const mode of modes) {
if (mode.value === config.mode) {
mode.classList.remove('not-selected');
} else {
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] || ''));
}
handleModeChange();
checkCanIncludeImages();
});
-48
View File
@@ -1,48 +0,0 @@
'use strict';
const mode = document.querySelector('#mode');
const modes = mode.querySelectorAll('button');
let actualMode = 'autocomplete';
// input to don't take in consideration
const toExcludes = ['includeImages'];
// inputs id that need to be disabled for a specific mode
const disabledForThisMode = {
autocomplete: [],
clipboard: ['typing', 'mouseover'],
'question-to-answer': ['typing', 'infinite', 'mouseover']
};
/**
* Handle when a mode change to show specific input or to hide them
*/
function handleModeChange() {
const needDisable = disabledForThisMode[actualMode];
const dontNeedDisable = inputsCheckbox.filter(
input => !needDisable.includes(input) && !toExcludes.includes(input)
);
for (const id of needDisable) {
document.querySelector('#' + id).parentElement.style.display = 'none';
}
for (const id of dontNeedDisable) {
document.querySelector('#' + id).parentElement.style.display = null;
}
}
// Mode hanlder
for (const button of modes) {
button.addEventListener('click', function () {
const value = button.value;
actualMode = value;
for (const mode of modes) {
if (mode.value !== value) {
mode.classList.add('not-selected');
} else {
mode.classList.remove('not-selected');
}
}
handleModeChange();
});
}
-25
View File
@@ -1,25 +0,0 @@
'use strict';
/**
* Show message into the popup
*/
function showMessage({ msg, error, infinite }) {
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);
}
/**
* Check if the current model support images integrations
* @param {string} version
* @returns
*/
function isCurrentVersionSupportingImages(version) {
const versionNumber = version.match(/gpt-(\d+)/);
if (!versionNumber?.[1]) {
return false;
}
return Number(versionNumber[1]) >= 4;
}
-63
View File
@@ -1,63 +0,0 @@
'use strict';
const CURRENT_VERSION = '1.1.1';
const versionDisplay = document.querySelector('#version');
/**
* Get the last version from the github
* @returns
*/
async function getLastVersion() {
const req = await fetch(
'https://raw.githubusercontent.com/yoannchb-pro/MoodleGPT/main/package.json'
);
const rep = await req.json();
return rep.version;
}
/**
* Display the version or an update message
* @param {string} version
* @param {boolean} isCurrent
* @returns
*/
function setVersion(version, isCurrent = true) {
if (isCurrent) {
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;
versionDisplay.appendChild(link);
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 => {
console.error(err);
return CURRENT_VERSION;
});
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])) {
return setVersion(lastVersion, false);
} else if (parseInt(currentVersionSplitted[i]) > parseInt(lastVertionSplitted[i])) {
return setVersion(CURRENT_VERSION);
}
}
setVersion(CURRENT_VERSION);
}
notifyUpdate();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+43
View File
@@ -35,6 +35,15 @@ main {
width: 22rem;
}
.settings {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.4rem;
text-align: center;
width: 100%;
}
img {
width: 5rem;
}
@@ -56,6 +65,10 @@ a {
align-items: center;
}
.center-y {
align-items: center;
}
.line .textLabel {
width: 5rem;
text-align: left;
@@ -148,3 +161,33 @@ a {
cursor: not-allowed;
opacity: 0.75;
}
.donate {
color: white;
animation: infinite donate 5s linear;
font-weight: bold;
}
@keyframes donate {
0% {
transform: translateX(0);
}
3.57% {
transform: translateY(-9px);
}
7.14% {
transform: translateY(-9px) rotate(17deg);
}
10.78% {
transform: translateY(-9px) rotate(-17deg);
}
14% {
transform: translateY(-9px) rotate(17deg);
}
18% {
transform: translateY(-9px) rotate(-17deg);
}
22% {
transform: translateY(0) rotate(0);
}
}