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: {} };