diff --git a/public/script.js b/public/script.js index 506d0681f..8ad914990 100644 --- a/public/script.js +++ b/public/script.js @@ -84,6 +84,7 @@ import { } from "./scripts/poe.js"; import { debounce, delay } from "./scripts/utils.js"; +import { extension_settings, loadExtensionSettings } from "./scripts/extensions.js"; //exporting functions and vars for mods export { @@ -2396,6 +2397,7 @@ async function getSettings(type) { script.src = src; $("body").append(script); } + loadExtensionSettings(settings); } //get the character to auto-load @@ -2449,6 +2451,7 @@ async function saveSettings(type) { horde_settings: horde_settings, power_user: power_user, poe_settings: poe_settings, + extension_settings: extension_settings, ...nai_settings, ...kai_settings, ...oai_settings, diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js index 6a5b17b92..47daef84e 100644 --- a/public/scripts/extensions.js +++ b/public/scripts/extensions.js @@ -1,33 +1,65 @@ -import { callPopup } from "../script.js"; +import { callPopup, saveSettings, saveSettingsDebounced } from "../script.js"; import { isSubsetOf } from "./utils.js"; export { getContext, getApiUrl, + loadExtensionSettings, defaultRequestArgs, modules, + extension_settings, }; const extensionNames = ['caption', 'dice', 'expressions', 'floating-prompt', 'memory']; const manifests = await getManifests(extensionNames); -const extensions_urlKey = 'extensions_url'; -const extensions_autoConnectKey = 'extensions_autoconnect'; -const extensions_disabledKey = 'extensions_disabled'; + +// TODO: Delete in next release +function migrateFromLocalStorage() { + const extensions_urlKey = 'extensions_url'; + const extensions_autoConnectKey = 'extensions_autoconnect'; + const extensions_disabledKey = 'extensions_disabled'; + + const apiUrl = localStorage.getItem(extensions_urlKey); + const autoConnect = localStorage.getItem(extensions_autoConnectKey); + const extensionsDisabled = localStorage.getItem(extensions_disabledKey); + + if (apiUrl !== null) { + extension_settings.apiUrl = apiUrl; + localStorage.removeItem(extensions_urlKey); + } + + if (autoConnect !== null) { + extension_settings.autoConnect = autoConnect; + localStorage.removeItem(extensions_autoConnectKey); + } + + if (extensionsDisabled !== null) { + extension_settings.disabledExtensions = JSON.parse(extensionsDisabled); + localStorage.removeItem(extensions_disabledKey); + } +} + +const extension_settings = { + apiUrl: '', + autoConnect: '', + disabledExtensions: [], + memory: {}, + note: { + default: '', + }, + caption: {}, + expressions: {}, + dice: {}, +}; let modules = []; -let disabledExtensions = getDisabledExtensions(); let activeExtensions = new Set(); const getContext = () => window['TavernAI'].getContext(); -const getApiUrl = () => localStorage.getItem('extensions_url'); +const getApiUrl = () => extension_settings.apiUrl; const defaultUrl = "http://localhost:5100"; const defaultRequestArgs = { method: 'GET', headers: { 'Bypass-Tunnel-Reminder': 'bypass' } }; let connectedToApi = false; -function getDisabledExtensions() { - const value = localStorage.getItem(extensions_disabledKey); - return value ? JSON.parse(value) : []; -} - function onDisableExtensionClick() { const name = $(this).data('name'); disableExtension(name); @@ -38,15 +70,15 @@ function onEnableExtensionClick() { enableExtension(name); } -function enableExtension(name) { - disabledExtensions = disabledExtensions.filter(x => x !== name); - localStorage.setItem(extensions_disabledKey, JSON.stringify(disabledExtensions)); +async function enableExtension(name) { + extension_settings.disabledExtensions = extension_settings.disabledExtensions.filter(x => x !== name); + await saveSettings(); location.reload(); } -function disableExtension(name) { - disabledExtensions.push(name); - localStorage.setItem(extensions_disabledKey, JSON.stringify(disabledExtensions)); +async function disableExtension(name) { + extension_settings.disabledExtensions.push(name); + await saveSettings(); location.reload(); } @@ -77,7 +109,7 @@ async function activateExtensions() { // all required modules are active (offline extensions require none) if (isSubsetOf(modules, manifest.requires)) { try { - const isDisabled = disabledExtensions.includes(name); + const isDisabled = extension_settings.disabledExtensions.includes(name); const li = document.createElement('li'); if (!isDisabled) { @@ -104,20 +136,27 @@ async function activateExtensions() { async function connectClickHandler() { const baseUrl = $("#extensions_url").val(); - localStorage.setItem(extensions_urlKey, baseUrl); + extension_settings.apiUrl = baseUrl; + saveSettingsDebounced(); await connectToApi(baseUrl); } function autoConnectInputHandler() { const value = $(this).prop('checked'); - localStorage.setItem(extensions_autoConnectKey, value.toString()); + extension_settings.autoConnect = !!value; if (value && !connectedToApi) { $("#extensions_connect").trigger('click'); } + + saveSettingsDebounced(); } async function connectToApi(baseUrl) { + if (!baseUrl) { + return; + } + const url = new URL(baseUrl); url.pathname = '/api/modules'; @@ -220,7 +259,7 @@ function showExtensionsDetails() { } } } - else if (disabledExtensions.includes(name)) { + else if (extension_settings.disabledExtensions.includes(name)) { html += `

Extension is disabled. Enable

`; } else { @@ -234,17 +273,24 @@ function showExtensionsDetails() { callPopup(`
${html}
`, 'text'); } -$(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'); - $("#extensions_details").on('click', showExtensionsDetails); - $(document).on('click', '.disable_extension', onDisableExtensionClick); - $(document).on('click', '.enable_extension', onEnableExtensionClick); +function loadExtensionSettings(settings) { + migrateFromLocalStorage(); + + if (settings.extension_settings) { + Object.assign(extension_settings, settings.extension_settings); + } + + $("#extensions_url").val(extension_settings.apiUrl); + $("#extensions_autoconnect").prop('checked', extension_settings.autoConnect).trigger('input'); // Activate offline extensions activateExtensions(); +} + +$(document).ready(async function () { + $("#extensions_connect").on('click', connectClickHandler); + $("#extensions_autoconnect").on('input', autoConnectInputHandler); + $("#extensions_details").on('click', showExtensionsDetails); + $(document).on('click', '.disable_extension', onDisableExtensionClick); + $(document).on('click', '.enable_extension', onEnableExtensionClick); }); \ No newline at end of file diff --git a/public/scripts/extensions/floating-prompt/index.js b/public/scripts/extensions/floating-prompt/index.js index a9c1d8f38..c14b0c959 100644 --- a/public/scripts/extensions/floating-prompt/index.js +++ b/public/scripts/extensions/floating-prompt/index.js @@ -1,46 +1,57 @@ -import { getContext } from "../../extensions.js"; +import { chat_metadata, saveSettingsDebounced } from "../../../script.js"; +import { extension_settings, getContext } from "../../extensions.js"; +import { debounce } from "../../utils.js"; export { MODULE_NAME }; +const saveChatDebounced = debounce(async () => await getContext().saveChat(), 1000); + const MODULE_NAME = '2_floating_prompt'; // <= Deliberate, for sorting lower than memory const UPDATE_INTERVAL = 1000; -let lastMessageNumber = null; -let promptInsertionInterval = 1; -let promptInsertionPosition = 1; -let promptInsertionDepth = 0; -let defaultNote = ''; +const DEFAULT_DEPTH = 4; +const DEFAULT_POSITION = 1; +const DEFAULT_INTERVAL = 1; -function onExtensionFloatingPromptInput() { - saveSettings(); +const metadata_keys = { + prompt: 'note_prompt', + interval: 'note_interval', + depth: 'note_depth', + position: 'note_position', } -function onExtensionFloatingIntervalInput() { - promptInsertionInterval = Number($(this).val()); - saveSettings(); +async function onExtensionFloatingPromptInput() { + chat_metadata[metadata_keys.prompt] = $(this).val(); + saveChatDebounced(); } -function onExtensionFloatingDepthInput() { +async function onExtensionFloatingIntervalInput() { + chat_metadata[metadata_keys.interval] = Number($(this).val()); + saveChatDebounced(); +} + +async function onExtensionFloatingDepthInput() { let value = Number($(this).val()); - if (promptInsertionDepth < 0) { + if (value < 0) { value = Math.abs(value); $(this).val(value); } - promptInsertionDepth = value; - saveSettings(); + chat_metadata[metadata_keys.depth] = value; + saveChatDebounced(); } -function onExtensionFloatingPositionInput(e) { - promptInsertionPosition = e.target.value; - saveSettings(); +async function onExtensionFloatingPositionInput(e) { + chat_metadata[metadata_keys.position] = e.target.value; + saveChatDebounced(); } function onExtensionFloatingDefaultInput() { - defaultNote = $(this).val(); - saveSettings(); + extension_settings.note.default = $(this).val(); + saveSettingsDebounced(); } +// TODO Remove in next release function getLocalStorageKeys() { const context = getContext(); @@ -65,27 +76,60 @@ function getLocalStorageKeys() { }; } -function loadSettings() { +function migrateFromLocalStorage() { const keys = getLocalStorageKeys(); - defaultNote = localStorage.getItem(keys.default) ?? ''; - const prompt = localStorage.getItem(keys.prompt) ?? defaultNote ?? ''; - const interval = localStorage.getItem(keys.interval) ?? 1; - const position = localStorage.getItem(keys.position) ?? 1; - const depth = localStorage.getItem(keys.depth) ?? 0; - $('#extension_floating_prompt').val(prompt).trigger('input'); - $('#extension_floating_interval').val(interval).trigger('input'); - $('#extension_floating_depth').val(depth).trigger('input'); - $(`input[name="extension_floating_position"][value="${position}"]`).prop('checked', true).trigger('change'); - $('#extension_floating_default').val(defaultNote); + const defaultNote = localStorage.getItem(keys.default); + const prompt = localStorage.getItem(keys.prompt); + const interval = localStorage.getItem(keys.interval); + const position = localStorage.getItem(keys.position); + const depth = localStorage.getItem(keys.depth); + + if (defaultNote !== null) { + if (typeof extension_settings.note !== 'object') { + extension_settings.note = {}; + } + + extension_settings.note.default = defaultNote; + saveSettingsDebounced(); + localStorage.removeItem(keys.default); + } + + if (chat_metadata) { + if (interval !== null) { + chat_metadata[metadata_keys.interval] = interval; + localStorage.removeItem(keys.interval); + } + + if (depth !== null) { + chat_metadata[metadata_keys.depth] = depth; + localStorage.removeItem(keys.depth); + } + + if (position !== null) { + chat_metadata[metadata_keys.position] = position; + localStorage.removeItem(keys.position); + } + + if (prompt !== null) { + chat_metadata[metadata_keys.prompt] = prompt; + localStorage.removeItem(keys.prompt); + saveChatDebounced(); + } + } } -function saveSettings() { - const keys = getLocalStorageKeys(); - localStorage.setItem(keys.prompt, $('#extension_floating_prompt').val()); - localStorage.setItem(keys.interval, $('#extension_floating_interval').val()); - localStorage.setItem(keys.depth, $('#extension_floating_depth').val()); - localStorage.setItem(keys.position, $('input:radio[name="extension_floating_position"]:checked').val()); - localStorage.setItem(keys.default, defaultNote); + +function loadSettings() { + migrateFromLocalStorage(); + chat_metadata[metadata_keys.prompt] = chat_metadata[metadata_keys.prompt] ?? extension_settings.note.default ?? ''; + chat_metadata[metadata_keys.interval] = chat_metadata[metadata_keys.interval] ?? DEFAULT_INTERVAL; + chat_metadata[metadata_keys.position] = chat_metadata[metadata_keys.position] ?? DEFAULT_POSITION; + chat_metadata[metadata_keys.depth] = chat_metadata[metadata_keys.depth] ?? DEFAULT_DEPTH; + $('#extension_floating_prompt').val(chat_metadata[metadata_keys.prompt]); + $('#extension_floating_interval').val(chat_metadata[metadata_keys.interval]); + $('#extension_floating_depth').val(chat_metadata[metadata_keys.depth]); + $(`input[name="extension_floating_position"][value="${chat_metadata[metadata_keys.position]}"]`).prop('checked', true); + $('#extension_floating_default').val(extension_settings.note.default); } async function moduleWorker() { @@ -98,24 +142,24 @@ async function moduleWorker() { loadSettings(); // take the count of messages - lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0; + let lastMessageNumber = Array.isArray(context.chat) && context.chat.length ? context.chat.filter(m => m.is_user).length : 0; // special case for new chat if (Array.isArray(context.chat) && context.chat.length === 1) { lastMessageNumber = 1; } - if (lastMessageNumber <= 0 || promptInsertionInterval <= 0) { + if (lastMessageNumber <= 0 || chat_metadata[metadata_keys.interval] <= 0) { $('#extension_floating_counter').text('No'); return; } - const messagesTillInsertion = lastMessageNumber >= promptInsertionInterval - ? (lastMessageNumber % promptInsertionInterval) - : (promptInsertionInterval - lastMessageNumber); + const messagesTillInsertion = lastMessageNumber >= chat_metadata[metadata_keys.interval] + ? (lastMessageNumber % chat_metadata[metadata_keys.interval]) + : (chat_metadata[metadata_keys.interval] - lastMessageNumber); const shouldAddPrompt = messagesTillInsertion == 0; const prompt = shouldAddPrompt ? $('#extension_floating_prompt').val() : ''; - context.setExtensionPrompt(MODULE_NAME, prompt, promptInsertionPosition, promptInsertionDepth); + context.setExtensionPrompt(MODULE_NAME, prompt, chat_metadata[metadata_keys.position], chat_metadata[metadata_keys.depth]); $('#extension_floating_counter').text(shouldAddPrompt ? 'This' : messagesTillInsertion); }