v1.1.2
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -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
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
});
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user