Files
SillyTavern/public/scripts/extensions.js
2023-03-18 13:58:44 +02:00

169 lines
5.4 KiB
JavaScript

import { isSubsetOf } from "./utils.js";
export {
getContext,
getApiUrl,
defaultRequestArgs,
};
const extensionNames = ['caption', 'dice', 'expressions', 'floating-prompt', 'memory'];
const manifests = await getManifests(extensionNames);
const extensions_urlKey = 'extensions_url';
const extensions_autoConnectKey = 'extensions_autoconnect';
let modules = [];
let activeExtensions = new Set();
const getContext = () => window['TavernAI'].getContext();
const getApiUrl = () => localStorage.getItem('extensions_url');
const defaultUrl = "http://localhost:5100";
const defaultRequestArgs = { method: 'GET', headers: { 'Bypass-Tunnel-Reminder': 'bypass' } };
let connectedToApi = false;
async function getManifests(names) {
const obj = {};
for (const name of names) {
const response = await fetch(`/scripts/extensions/${name}/manifest.json`);
if (response.ok) {
const json = await response.json();
obj[name] = json;
}
}
return obj;
}
async function activateExtensions() {
const extensions = Object.entries(manifests).sort((a, b) => a[1].loading_order - b[1].loading_order);
for (let entry of extensions) {
const name = entry[0];
const manifest = entry[1];
if (activeExtensions.has(name)) {
continue;
}
// all required modules are active (offline extensions require none)
if (isSubsetOf(modules, manifest.requires)) {
try {
await addExtensionScript(name, manifest);
await addExtensionStyle(name, manifest);
activeExtensions.add(name);
$('#extensions_list').append(`<li id="${name}">${manifest.display_name}</li>`);
}
catch (error) {
console.error(`Could not activate extension: ${name}`);
console.error(error);
}
}
}
}
async function connectClickHandler() {
const baseUrl = $("#extensions_url").val();
localStorage.setItem(extensions_urlKey, baseUrl);
await connectToApi(baseUrl);
}
function autoConnectInputHandler() {
const value = $(this).prop('checked');
localStorage.setItem(extensions_autoConnectKey, value.toString());
if (value && !connectedToApi) {
$("#extensions_connect").trigger('click');
}
}
async function connectToApi(baseUrl) {
const url = new URL(baseUrl);
url.pathname = '/api/modules';
try {
const getExtensionsResult = await fetch(url, defaultRequestArgs);
if (getExtensionsResult.ok) {
const data = await getExtensionsResult.json();
modules = data.modules;
activateExtensions();
}
updateStatus(getExtensionsResult.ok);
}
catch {
updateStatus(false);
}
}
function updateStatus(success) {
connectedToApi = success;
const _text = success ? 'Connected to API' : 'Could not connect to API';
const _class = success ? 'success' : 'failure';
$('#extensions_status').text(_text);
$('#extensions_status').attr('class', _class);
}
function addExtensionStyle(name, manifest) {
if (manifest.css) {
return new Promise((resolve, reject) => {
const url = `/scripts/extensions/${name}/${manifest.css}`;
if ($(`link[id="${name}"]`).length === 0) {
const link = document.createElement('link');
link.id = name;
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
link.onload = function () {
resolve();
}
link.onerror = function (e) {
reject(e);
}
document.head.appendChild(link);
}
});
}
return Promise.resolve();
}
function addExtensionScript(name, manifest) {
if (manifest.js) {
return new Promise((resolve, reject) => {
const url = `/scripts/extensions/${name}/${manifest.js}`;
let ready = false;
if ($(`script[id="${name}"]`).length === 0) {
const script = document.createElement('script');
script.id = name;
script.type = 'module';
script.src = url;
script.async = true;
script.onerror = function (err) {
reject(err, script);
};
script.onload = script.onreadystatechange = function () {
// console.log(this.readyState); // uncomment this line to see which ready states are called.
if (!ready && (!this.readyState || this.readyState == 'complete')) {
ready = true;
resolve();
}
};
document.body.appendChild(script);
}
});
}
return Promise.resolve();
}
$(document).ready(async function () {
const url = localStorage.getItem(extensions_urlKey) ?? defaultUrl;
const autoConnect = localStorage.getItem(extensions_autoConnectKey) == 'true';
$("#extensions_url").val(url);
$("#extensions_connect").on('click', connectClickHandler);
$("#extensions_autoconnect").on('input', autoConnectInputHandler);
$("#extensions_autoconnect").prop('checked', autoConnect).trigger('input');
// Activate offline extensions
activateExtensions();
});