UI Theme import/export

This commit is contained in:
Cohee 2024-02-10 23:12:16 +02:00
parent 87668f5962
commit cbea5bf996
2 changed files with 80 additions and 4 deletions

View File

@ -3114,8 +3114,17 @@
<div id="user-settings-block-content" class="flex-container spaceEvenly">
<div name="UserSettingsFirstColumn" id="UI-Theme-Block" class="flex-container flexFlowColumn wide100p">
<div id="UI-presets-block" class="flex-container flexFlowColumn">
<h4>
<span data-i18n="UI Theme Preset">Theme Preset</span>
<h4 class="title_restorable">
<span data-i18n="UI Theme">UI Theme</span>
<div class="flex-container">
<div id="ui_preset_import_button" class="menu_button menu_button_icon margin0" title="Import a theme file" data-i18n="[title]Import a theme file">
<i class="fa-solid fa-file-import"></i>
</div>
<div id="ui_preset_export_button" class="menu_button menu_button_icon margin0" title="Export a theme file" data-i18n="[title]Export a theme file">
<i class="fa-solid fa-file-export"></i>
</div>
</div>
<input type="file" id="ui_preset_import_file" accept=".json" hidden>
</h4>
<div class="flex-container flexnowrap alignitemscenter">
<select id="themes" class="margin0">

View File

@ -38,7 +38,7 @@ import { tags } from './tags.js';
import { tokenizers } from './tokenizers.js';
import { BIAS_CACHE } from './logit-bias.js';
import { countOccurrences, debounce, delay, isOdd, resetScrollHeight, shuffle, sortMoments, stringToRange, timestampToMoment } from './utils.js';
import { countOccurrences, debounce, delay, download, getFileText, isOdd, resetScrollHeight, shuffle, sortMoments, stringToRange, timestampToMoment } from './utils.js';
export {
loadPowerUserSettings,
@ -1981,10 +1981,51 @@ async function updateTheme() {
toastr.success('Theme saved.');
}
/**
* Exports the current theme to a file.
*/
async function exportTheme() {
const themeFile = await saveTheme(power_user.theme);
const fileName = `${themeFile.name}.json`;
download(JSON.stringify(themeFile, null, 4), fileName, 'application/json');
}
/**
* Imports a theme from a file.
* @param {File} file File to import.
* @returns {Promise<void>} A promise that resolves when the theme is imported.
*/
async function importTheme(file) {
if (!file) {
return;
}
const fileText = await getFileText(file);
const parsed = JSON.parse(fileText);
if (!parsed.name) {
throw new Error('Missing name');
}
if (themes.some(t => t.name === parsed.name)) {
throw new Error('Theme with that name already exists');
}
themes.push(parsed);
await applyTheme(parsed.name);
await saveTheme(parsed.name);
const option = document.createElement('option');
option.selected = true;
option.value = parsed.name;
option.innerText = parsed.name;
$('#themes').append(option);
saveSettingsDebounced();
}
/**
* Saves the current theme to the server.
* @param {string|undefined} name Theme name. If undefined, a popup will be shown to enter a name.
* @returns {Promise<void>} A promise that resolves when the theme is saved.
* @returns {Promise<object>} A promise that resolves when the theme is saved.
*/
async function saveTheme(name = undefined) {
if (typeof name !== 'string') {
@ -2056,6 +2097,8 @@ async function saveTheme(name = undefined) {
power_user.theme = name;
saveSettingsDebounced();
}
return theme;
}
async function saveMovingUI() {
@ -3278,6 +3321,30 @@ $(document).ready(() => {
reloadCurrentChat();
});
$('#ui_preset_import_button').on('click', function () {
$('#ui_preset_import_file').trigger('click');
});
$('#ui_preset_import_file').on('change', async function() {
const inputElement = this instanceof HTMLInputElement && this;
try {
const file = inputElement?.files?.[0];
await importTheme(file);
} catch (error) {
console.error('Error importing UI theme', error);
toastr.error(String(error), 'Failed to import UI theme');
} finally {
if (inputElement) {
inputElement.value = null;
}
}
});
$('#ui_preset_export_button').on('click', async function () {
await exportTheme();
});
$(document).on('click', '#debug_table [data-debug-function]', function () {
const functionId = $(this).data('debug-function');
const functionRecord = debug_functions.find(f => f.functionId === functionId);