diff --git a/public/script.js b/public/script.js
index 7efe1d231..bb4a13020 100644
--- a/public/script.js
+++ b/public/script.js
@@ -3283,27 +3283,9 @@ function read_bg_load(input) {
processData: false,
success: function (html) {
setBackground(html);
- if (bg1_toggle == true) {
- // this is a repeat of the background setting function for when user uploads a new BG image
- bg1_toggle = false; // should make the Bg setting a modular function to be called in both cases
- var number_bg = 2;
- var target_opacity = 1.0;
- } else {
- bg1_toggle = true;
- var number_bg = 1;
- var target_opacity = 0.0;
- }
- $("#bg2").transition({
- opacity: target_opacity,
- duration: 1300, //animation_rm_duration,
- easing: "linear",
- complete: function () {
- $("#options").css("display", "none");
- },
- });
- $("#bg" + number_bg).css(
+ $("#bg1").css(
"background-image",
- "url(" + e.target.result + ")"
+ `url(${e.target.result})`
);
$("#form_bg_download").after(
`
@@ -3890,40 +3872,35 @@ $(document).ready(function () {
$("#form_upload_avatar").trigger("reset");
});
- $(document).on("click", ".bg_example", function () {
+ $(document).on("click", ".bg_example", async function () {
//when user clicks on a BG thumbnail...
- var this_bgfile = $(this).attr("bgfile"); // this_bgfile = whatever they clicked
+ const this_bgfile = $(this).attr("bgfile"); // this_bgfile = whatever they clicked
+
+ const customBg = window.getComputedStyle(document.getElementById('bg_custom')).backgroundImage;
+
+ // custom background is set. Do not override the layer below
+ if (customBg !== 'none') {
+ return;
+ }
// if clicked on upload button
if (!this_bgfile) {
return;
}
- if (bg1_toggle == true) {
- //if bg1 is toggled true (initially set as true in first JS vars)
- bg1_toggle = false; // then toggle it false
- var number_bg = 2; // sets a variable for bg2
- var target_opacity = 1.0; // target opacity is 100%
- } else {
- //if bg1 is FALSE
- bg1_toggle = true; // make it true
- var number_bg = 1; // set variable to bg1..
- var target_opacity = 0.0; // set target opacity to 0
- }
- $("#bg2").stop(); // first, stop whatever BG transition was happening before
- $("#bg2").transition({
- // start a new BG transition routine
- opacity: target_opacity, // set opacity to previously set variable
- duration: 1300, //animation_rm_duration,
- easing: "linear",
- complete: function () {
- },
+ const backgroundUrl = `backgrounds/${this_bgfile}`;
+
+ // fetching to browser memory to reduce flicker
+ fetch(backgroundUrl).then(() => {
+ $("#bg1").css(
+ "background-image",
+ `url("${backgroundUrl}")`
+ );
+ setBackground(this_bgfile);
+ }).catch(() => {
+ console.log('Background could not be set: ' + backgroundUrl);
});
- $("#bg" + number_bg).css(
- "background-image",
- 'url("backgrounds/' + this_bgfile + '")'
- );
- setBackground(this_bgfile);
+
});
$(document).on("click", ".bg_example_cross", function (e) {
e.stopPropagation();
diff --git a/public/scripts/extensions.js b/public/scripts/extensions.js
index 0596e843c..df4b7022f 100644
--- a/public/scripts/extensions.js
+++ b/public/scripts/extensions.js
@@ -9,7 +9,7 @@ export {
extension_settings,
};
-const extensionNames = ['caption', 'dice', 'expressions', 'floating-prompt', 'memory'];
+const extensionNames = ['caption', 'dice', 'expressions', 'floating-prompt', 'memory', 'backgrounds'];
const manifests = await getManifests(extensionNames);
const defaultUrl = "http://localhost:5100";
diff --git a/public/scripts/extensions/backgrounds/index.js b/public/scripts/extensions/backgrounds/index.js
new file mode 100644
index 000000000..4ae8d389a
--- /dev/null
+++ b/public/scripts/extensions/backgrounds/index.js
@@ -0,0 +1,140 @@
+import { getContext } from "../../extensions.js";
+export { MODULE_NAME };
+
+const MODULE_NAME = 'backgrounds';
+const METADATA_KEY = 'custom_background';
+const UPDATE_INTERVAL = 1000;
+
+async function moduleWorker() {
+ if (hasCustomBackground()) {
+ $('#unlock_background').show();
+ $('#lock_background').hide();
+ setCustomBackground();
+ }
+ else {
+ $('#unlock_background').hide();
+ $('#lock_background').show();
+ unsetCustomBackground();
+ }
+}
+
+function onLockBackgroundClick() {
+ const bgImage = window.getComputedStyle(document.getElementById('bg1')).backgroundImage;
+
+ // Extract the URL from the CSS string
+ const urlRegex = /url\((['"])?(.*?)\1\)/;
+ const matches = bgImage.match(urlRegex);
+ const url = matches[2];
+
+ // Remove the protocol and host, leaving the relative URL
+ const relativeUrl = new URL(url).pathname;
+ const relativeBgImage = `url("${relativeUrl}")`
+
+ saveBackgroundMetadata(relativeBgImage);
+ setCustomBackground();
+ $('#unlock_background').show();
+ $('#lock_background').hide();
+}
+
+function onUnlockBackgroundClick() {
+ removeBackgroundMetadata();
+ unsetCustomBackground();
+ $('#unlock_background').hide();
+ $('#lock_background').show();
+}
+
+function hasCustomBackground() {
+ const context = getContext();
+ return !!context.chatMetadata[METADATA_KEY];
+}
+
+function saveBackgroundMetadata(file) {
+ const context = getContext();
+ context.chatMetadata[METADATA_KEY] = file;
+ context.saveChat();
+}
+
+function removeBackgroundMetadata() {
+ const context = getContext();
+ delete context.chatMetadata[METADATA_KEY];
+ context.saveChat();
+}
+
+function setCustomBackground() {
+ const context = getContext();
+ const file = context.chatMetadata[METADATA_KEY];
+
+ // bg already set
+ if (document.getElementById("bg_custom").style.backgroundImage == file) {
+ return;
+ }
+
+ $("#bg_custom").css("background-image", file);
+ $("#custom_bg_preview").css("background-image", file);
+}
+
+function animateBgSet(selector, value) {
+ $(selector).animate({
+ opacity: 0
+ }, 500,
+ function () {
+ $(this).css('background-image', value).animate({
+ opacity: 1
+ }, 500);
+ });
+}
+
+function unsetCustomBackground() {
+ animateBgSet("#bg_custom", 'none');
+ $("#custom_bg_preview").css("background-image", 'none');
+}
+
+function onSelectBackgroundClick() {
+ const bgfile = $(this).attr("bgfile");
+
+ if (hasCustomBackground()) {
+ saveBackgroundMetadata(`url("backgrounds/${bgfile}")`);
+ setCustomBackground();
+ }
+}
+
+$(document).ready(function () {
+ function addSettings() {
+ const html = `
+
+
+
+
+
+
+
+
+ Press "Lock" to assign a currently selected background to a character or group chat.
+ Any background image selected while lock is engaged will be saved automatically.
+
+
+
Preview
+
+
+
+
+
+ `;
+ $('#extensions_settings').append(html);
+ $('#lock_background').on('click', onLockBackgroundClick);
+ $('#unlock_background').on('click', onUnlockBackgroundClick);
+ $(document).on("click", ".bg_example", onSelectBackgroundClick);
+ }
+
+ addSettings();
+ setInterval(moduleWorker, UPDATE_INTERVAL);
+});
\ No newline at end of file
diff --git a/public/scripts/extensions/backgrounds/manifest.json b/public/scripts/extensions/backgrounds/manifest.json
new file mode 100644
index 000000000..a95e547eb
--- /dev/null
+++ b/public/scripts/extensions/backgrounds/manifest.json
@@ -0,0 +1,8 @@
+{
+ "display_name": "Character Backgrounds",
+ "loading_order": 7,
+ "requires": [],
+ "optional": [],
+ "js": "index.js",
+ "css": "style.css"
+}
\ No newline at end of file
diff --git a/public/scripts/extensions/backgrounds/style.css b/public/scripts/extensions/backgrounds/style.css
new file mode 100644
index 000000000..6a4bde1af
--- /dev/null
+++ b/public/scripts/extensions/backgrounds/style.css
@@ -0,0 +1,45 @@
+#custom_bg_preview {
+ width: 160px;
+ height: 90px;
+ background-color: var(--grey30a);
+ background-repeat: no-repeat;
+ background-attachment: fixed;
+ background-size: cover;
+ border-radius: 20px;
+ border: 1px solid var(--black50a);
+ box-shadow: 0 0 7px var(--black50a);
+ margin: 5px;
+}
+
+#custom_bg_preview::before {
+ content: 'No Background';
+ color: white;
+ position: relative;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+#custom_bg_preview:not([style*="background-image: none"])::before {
+ display: none;
+}
+
+.background_controls .menu_button {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ column-gap: 10px;
+}
+
+.background_controls {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ column-gap: 10px;
+}
+
+.background_controls small {
+ flex-grow: 1;
+}
\ No newline at end of file
diff --git a/public/style.css b/public/style.css
index 4eb0630f6..4769b24fa 100644
--- a/public/style.css
+++ b/public/style.css
@@ -157,26 +157,23 @@ code {
line-height: var(--mainFontSize);
}
-#bg1 {
- background: url(backgrounds/tavern1.jpg);
+#bg1, #bg_custom {
background-repeat: no-repeat;
background-attachment: fixed;
background-size: cover;
position: absolute;
width: 100%;
height: 100%;
+ transition: background-image 0.5s ease-in-out;
+}
+
+#bg1 {
+ background-image: url(backgrounds/tavern1.jpg);
z-index: 0;
}
-#bg2 {
- background: url(backgrounds/tavern1.jpg);
- background-repeat: no-repeat;
- background-attachment: fixed;
- background-size: cover;
- opacity: 0.0;
- position: absolute;
- width: 100%;
- height: 100%;
+#bg_custom {
+ background-image: none;
z-index: 1;
}
@@ -3260,7 +3257,7 @@ toolcool-color-picker {
}
#bg1,
- #bg2 {
+ #bg_custom {
position: fixed;
}
@@ -3420,7 +3417,7 @@ body.no-blur #send_form.no-connection {
}
body.no-blur #bg1,
-body.no-blur #bg2 {
+body.no-blur #bg_custom {
filter: unset;
}