diff --git a/public/index.html b/public/index.html index 6dc36cac0..5f8113b4e 100644 --- a/public/index.html +++ b/public/index.html @@ -1203,14 +1203,9 @@
- - +
diff --git a/public/script.js b/public/script.js index bc06c2393..430feb012 100644 --- a/public/script.js +++ b/public/script.js @@ -2612,7 +2612,7 @@ async function getSettings(type) { loadPoeSettings(settings); // Load power user settings - loadPowerUserSettings(settings); + loadPowerUserSettings(settings, data); //Enable GUI deference settings if GUI is selected for Kobold diff --git a/public/scripts/power-user.js b/public/scripts/power-user.js index 367ab9c2e..477c11902 100644 --- a/public/scripts/power-user.js +++ b/public/scripts/power-user.js @@ -1,5 +1,5 @@ -import { saveSettingsDebounced, characters } from "../script.js"; -import { delay, debounce } from "./utils.js"; +import { saveSettingsDebounced, characters, callPopup, token } from "../script.js"; +import { delay } from "./utils.js"; export { @@ -52,19 +52,13 @@ let power_user = { blur_tint_color: `${getComputedStyle(document.documentElement).getPropertyValue('--SmartThemeBlurTintColor').trim()}`, waifuMode: false, + theme: 'Default (Dark)', }; +let themes = []; + const storage_keys = { - collapse_newlines: "TavernAI_collapse_newlines", - force_pygmalion_formatting: "TavernAI_force_pygmalion_formatting", - pin_examples: "TavernAI_pin_examples", - disable_description_formatting: "TavernAI_disable_description_formatting", - disable_scenario_formatting: "TavernAI_disable_scenario_formatting", - disable_personality_formatting: "TavernAI_disable_personality_formatting", - always_force_name2: "TavernAI_always_force_name2", - custom_chat_separator: "TavernAI_custom_chat_separator", fast_ui_mode: "TavernAI_fast_ui_mode", - multigen: "TavernAI_multigen", avatar_style: "TavernAI_avatar_style", chat_display: "TavernAI_chat_display", sheld_width: "TavernAI_sheld_width", @@ -207,6 +201,39 @@ async function applyFontScale() { chat.style.transition = chatTransition; } +async function applyTheme(name) { + const theme = themes.find(x => x.name == name); + + if (!theme) { + return; + } + + const themeProperties = [ + { key: 'main_text_color', selector: '#main-text-color-picker', type: 'main' }, + { key: 'italics_text_color', selector: '#italics-color-picker', type: 'italics' }, + { key: 'fastui_bg_color', selector: '#fastui-bg-color-picker', type: 'fastUIBG' }, + { key: 'blur_tint_color', selector: '#blur-tint-color-picker', type: 'blurTint' }, + { + key: 'blur_strength', + action: async () => { + localStorage.setItem(storage_keys.blur_strength, power_user.blur_strength); + await applyBlurStrength(); + } + } + ]; + + for (const { key, selector, type, action } of themeProperties) { + if (theme[key] !== undefined) { + power_user[key] = theme[key]; + if (selector) $(selector).attr('color', power_user[key]); + if (type) await applyThemeColor(type); + if (action) await action(); + } + } + + console.log('theme applied: ' + name); +} + applyAvatarStyle(); switchUiMode(); applyChatDisplay(); @@ -216,28 +243,16 @@ applyBlurStrength(); applyThemeColor(); switchWaifuMode() -// TODO delete in next release -function loadFromLocalStorage() { - power_user.collapse_newlines = localStorage.getItem(storage_keys.collapse_newlines) == "true"; - power_user.force_pygmalion_formatting = localStorage.getItem(storage_keys.force_pygmalion_formatting) == "true"; - power_user.pin_examples = localStorage.getItem(storage_keys.pin_examples) == "true"; - power_user.disable_description_formatting = localStorage.getItem(storage_keys.disable_description_formatting) == "true"; - power_user.disable_scenario_formatting = localStorage.getItem(storage_keys.disable_scenario_formatting) == "true"; - power_user.disable_personality_formatting = localStorage.getItem(storage_keys.disable_personality_formatting) == "true"; - power_user.always_force_name2 = localStorage.getItem(storage_keys.always_force_name2) == "true"; - power_user.custom_chat_separator = localStorage.getItem(storage_keys.custom_chat_separator); - power_user.multigen = localStorage.getItem(storage_keys.multigen) == "true"; -} - -function loadPowerUserSettings(settings) { - // Migrate legacy settings - loadFromLocalStorage(); - - // Now do it properly from settings.json +function loadPowerUserSettings(settings, data) { + // Load from settings.json if (settings.power_user !== undefined) { Object.assign(power_user, settings.power_user); } + if (data.themes !== undefined) { + themes = data.themes; + } + // These are still local storage const fastUi = localStorage.getItem(storage_keys.fast_ui_mode); const waifuMode = localStorage.getItem(storage_keys.waifuMode); @@ -266,16 +281,23 @@ function loadPowerUserSettings(settings) { $(`input[name="chat_display"][value="${power_user.chat_display}"]`).prop("checked", true); $(`input[name="sheld_width"][value="${power_user.sheld_width}"]`).prop("checked", true); $("#font_scale").val(power_user.font_scale); - $("#font_scale_counter").html(power_user.font_scale); + $("#font_scale_counter").text(power_user.font_scale); $("#blur_strength").val(power_user.blur_strength); - $("#blur_strength_counter").html(power_user.blur_strength); + $("#blur_strength_counter").text(power_user.blur_strength); $("#main-text-color-picker").attr('color', power_user.main_text_color); $("#italics-color-picker").attr('color', power_user.italics_text_color); $("#fastui-bg-color-picker").attr('color', power_user.fastui_bg_color); $("#blur-tint-color-picker").attr('color', power_user.blur_tint_color); + for (const theme of themes) { + const option = document.createElement('option'); + option.value = theme.name; + option.innerText = theme.name; + option.selected = theme.name == power_user.theme; + $("#themes").append(option); + } $(`#character_sort_order option[data-order="${power_user.sort_order}"][data-field="${power_user.sort_field}"]`).prop("selected", true); sortCharactersList(); @@ -415,6 +437,58 @@ $(document).ready(() => { saveSettingsDebounced(); }); + $("#themes").on('change', function () { + const themeSelected = $(this).find(':selected').val(); + power_user.theme = themeSelected; + applyTheme(themeSelected); + saveSettingsDebounced(); + }); + + $("#ui-preset-save-button").on('click', async function () { + const name = await callPopup('Name him, name your son:', 'input'); + + if (!name) { + return; + } + + const theme = { + name, + blur_strength: power_user.blur_strength, + main_text_color: power_user.main_text_color, + italics_text_color: power_user.italics_text_color, + fastui_bg_color: power_user.fastui_bg_color, + blur_tint_color: power_user.blur_tint_color, + }; + + const response = await fetch('/savetheme', { + method: 'POST', headers: { + 'X-CSRF-Token': token, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(theme) + }); + + if (response.ok) { + const themeIndex = themes.findIndex(x => x.name == name); + + if (themeIndex == -1) { + themes.push(theme); + const option = document.createElement('option'); + option.selected = true; + option.value = name; + option.innerText = name; + $('#themes').append(option); + } + else { + themes[themeIndex] = theme; + $(`#themes option[value="${name}"]`).attr('selected', true); + } + + power_user.theme = name; + saveSettingsDebounced(); + } + }); + $("#play_message_sound").on('input', function () { power_user.play_message_sound = !!$(this).prop('checked'); saveSettingsDebounced(); diff --git a/public/themes/Aqua Blue.json b/public/themes/Aqua Blue.json new file mode 100644 index 000000000..1cd4f5880 --- /dev/null +++ b/public/themes/Aqua Blue.json @@ -0,0 +1,8 @@ +{ + "name": "Aqua Blue", + "blur_strength": 2, + "main_text_color": "rgba(160, 190, 190, 1)", + "italics_text_color": "rgba(170, 200, 200, 1)", + "fastui_bg_color": "rgba(7, 54, 66, 0.9)", + "blur_tint_color": "rgba(0, 43, 54, 0.8)" +} \ No newline at end of file diff --git a/public/themes/Default (Dark).json b/public/themes/Default (Dark).json new file mode 100644 index 000000000..1997cd35e --- /dev/null +++ b/public/themes/Default (Dark).json @@ -0,0 +1,8 @@ +{ + "name": "Default (Dark)", + "blur_strength": 10, + "main_text_color": "rgb(220, 220, 210)", + "italics_text_color": "rgb(175, 175, 175)", + "fastui_bg_color": "rgba(0, 0, 0, 0.9)", + "blur_tint_color": "rgba(0, 0, 0, 0.5)" +} \ No newline at end of file diff --git a/public/themes/Megumin Red.json b/public/themes/Megumin Red.json new file mode 100644 index 000000000..c403a950b --- /dev/null +++ b/public/themes/Megumin Red.json @@ -0,0 +1,8 @@ +{ + "name": "Megumin Red", + "blur_strength": 10, + "main_text_color": "rgba(230, 230, 230, 1)", + "italics_text_color": "rgba(200, 200, 200, 1)", + "fastui_bg_color": "rgba(70, 5, 5, 0.9)", + "blur_tint_color": "rgba(50, 10, 10, 0.75)" +} \ No newline at end of file diff --git a/server.js b/server.js index 419fe34cd..fe0f331bd 100644 --- a/server.js +++ b/server.js @@ -117,6 +117,7 @@ const directories = { thumbnails: 'thumbnails/', thumbnailsBg: 'thumbnails/bg/', thumbnailsAvatar: 'thumbnails/avatar/', + themes: 'public/themes', }; // CSRF Protection // @@ -1037,6 +1038,7 @@ app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's cod const openai_setting_names = []; const textgenerationwebui_presets = []; const textgenerationwebui_preset_names = []; + const themes = []; const settings = fs.readFileSync('public/settings.json', 'utf8', (err, data) => { if (err) return response.sendStatus(500); @@ -1139,6 +1141,30 @@ app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's cod textgenerationwebui_preset_names.push(item.replace(/\.[^/.]+$/, '')); }); + // Theme files + const themeFiles = fs + .readdirSync(directories.themes) + .filter(x => path.parse(x).ext == '.json') + .sort(); + + themeFiles.forEach(item => { + const file = fs.readFileSync( + path.join(directories.themes, item), + 'utf-8', + (err, data) => { + if (err) return response.sendStatus(500); + return data; + } + ); + + try { + themes.push(json5.parse(file)); + } + catch { + // skip + } + }) + response.send({ settings, koboldai_settings, @@ -1150,6 +1176,7 @@ app.post('/getsettings', jsonParser, (request, response) => { //Wintermute's cod openai_setting_names, textgenerationwebui_presets, textgenerationwebui_preset_names, + themes, enable_extensions: enableExtensions, }); }); @@ -1182,6 +1209,17 @@ app.post('/deleteworldinfo', jsonParser, (request, response) => { return response.sendStatus(200); }); +app.post('/savetheme', jsonParser, (request, response) => { + if (!request.body || !request.body.name) { + return response.sendStatus(400); + } + + const filename = path.join(directories.themes, sanitize(request.body.name) + '.json'); + fs.writeFileSync(filename, JSON.stringify(request.body), 'utf8'); + + return response.sendStatus(200); +}); + function readWorldInfoFile(worldInfoName) { if (!worldInfoName) { return { entries: {} };