Merge branch 'staging' into webpack

f918b146e73692c7d06546fb4c870710cf8c02ef

Cohee <18619528+Cohee1207@users.noreply.github.com>

6 files changed, +192 -57Ignore whitespace
public/script.js+3 -2
@@ -13,7 +13,7 @@ import {
13 default as libs,13 default as libs,
14} from './lib.js';14} from './lib.js';
1515
16import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter, addSafariPatch } from './scripts/RossAscends-mods.js';16import { humanizedDateTime, favsToHotswap, getMessageTimeStamp, dragElement, isMobile, initRossMods, shouldSendOnEnter } from './scripts/RossAscends-mods.js';
17import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js';17import { userStatsHandler, statMesProcess, initStats } from './scripts/stats.js';
18import {18import {
19 generateKoboldWithStreaming,19 generateKoboldWithStreaming,
@@ -263,6 +263,7 @@ import { initSystemPrompts } from './scripts/sysprompt.js';
263import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js';263import { registerExtensionSlashCommands as initExtensionSlashCommands } from './scripts/extensions-slashcommands.js';
264import { ToolManager } from './scripts/tool-calling.js';264import { ToolManager } from './scripts/tool-calling.js';
265import { addShowdownPatch } from './scripts/util/showdown-patch.js';265import { addShowdownPatch } from './scripts/util/showdown-patch.js';
266import { applyBrowserFixes } from './scripts/browser-fixes.js';
266267
267//exporting functions and vars for mods268//exporting functions and vars for mods
268export {269export {
@@ -932,7 +933,7 @@ async function firstLoadInit() {
932 initLibraryShims();933 initLibraryShims();
933 addShowdownPatch(showdown);934 addShowdownPatch(showdown);
934 reloadMarkdownProcessor();935 reloadMarkdownProcessor();
935 addSafariPatch();936 applyBrowserFixes();
936 await getClientVersion();937 await getClientVersion();
937 await readSecretState();938 await readSecretState();
938 await initLocales();939 await initLocales();
public/scripts/RossAscends-mods.js+13 -23
@@ -122,7 +122,7 @@ export function humanizeGenTime(total_gen_time) {
122 */122 */
123var parsedUA = null;123var parsedUA = null;
124124
125function getParsedUA() {125export function getParsedUA() {
126 if (!parsedUA) {126 if (!parsedUA) {
127 try {127 try {
128 parsedUA = Bowser.parse(navigator.userAgent);128 parsedUA = Bowser.parse(navigator.userAgent);
@@ -718,18 +718,6 @@ export const autoFitSendTextAreaDebounced = debounce(autoFitSendTextArea, deboun
718718
719// ---------------------------------------------------719// ---------------------------------------------------
720720
721export function addSafariPatch() {
722 const userAgent = getParsedUA();
723 console.debug('User Agent', userAgent);
724 const isMobileSafari = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
725 const isDesktopSafari = userAgent?.browser?.name === 'Safari' && userAgent?.platform?.type === 'desktop';
726 const isIOS = userAgent?.os?.name === 'iOS';
727
728 if (isIOS || isMobileSafari || isDesktopSafari) {
729 document.body.classList.add('safari');
730 }
731}
732
733export function initRossMods() {721export function initRossMods() {
734 // initial status check722 // initial status check
735 checkStatusDebounced();723 checkStatusDebounced();
@@ -742,16 +730,6 @@ export function initRossMods() {
742 RA_autoconnect();730 RA_autoconnect();
743 }731 }
744732
745 if (isMobile()) {
746 const fixFunkyPositioning = () => {
747 console.debug('[Mobile] Device viewport change detected.');
748 document.documentElement.style.position = 'fixed';
749 requestAnimationFrame(() => document.documentElement.style.position = '');
750 };
751 window.addEventListener('resize', fixFunkyPositioning);
752 window.addEventListener('orientationchange', fixFunkyPositioning);
753 }
754
755 $('#main_api').change(function () {733 $('#main_api').change(function () {
756 var PrevAPI = main_api;734 var PrevAPI = main_api;
757 setTimeout(() => RA_autoconnect(PrevAPI), 100);735 setTimeout(() => RA_autoconnect(PrevAPI), 100);
@@ -934,6 +912,12 @@ export function initRossMods() {
934 if (!$(e.target).closest('#sheld').length) {912 if (!$(e.target).closest('#sheld').length) {
935 return;913 return;
936 }914 }
915 if ($('#curEditTextarea').length) {
916 // Don't swipe while in text edit mode
917 // the ios selection gestures get picked up
918 // as swipe gestures
919 return;
920 }
937 var SwipeButR = $('.swipe_right:last');921 var SwipeButR = $('.swipe_right:last');
938 var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');922 var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
939 if (SwipeTargetMesClassParent !== null) {923 if (SwipeTargetMesClassParent !== null) {
@@ -952,6 +936,12 @@ export function initRossMods() {
952 if (!$(e.target).closest('#sheld').length) {936 if (!$(e.target).closest('#sheld').length) {
953 return;937 return;
954 }938 }
939 if ($('#curEditTextarea').length) {
940 // Don't swipe while in text edit mode
941 // the ios selection gestures get picked up
942 // as swipe gestures
943 return;
944 }
955 var SwipeButL = $('.swipe_left:last');945 var SwipeButL = $('.swipe_left:last');
956 var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');946 var SwipeTargetMesClassParent = $(e.target).closest('.last_mes');
957 if (SwipeTargetMesClassParent !== null) {947 if (SwipeTargetMesClassParent !== null) {
public/scripts/browser-fixes.js+77 -0
@@ -0,0 +1,77 @@
1import { getParsedUA, isMobile } from './RossAscends-mods.js';
2
3const isFirefox = () => /firefox/i.test(navigator.userAgent);
4
5function sanitizeInlineQuotationOnCopy() {
6 // STRG+C, STRG+V on firefox leads to duplicate double quotes when inline quotation elements are copied.
7 // To work around this, take the selection and transform <q> to <span> before calling toString().
8 document.addEventListener('copy', function (event) {
9 const selection = window.getSelection();
10 if (!selection.anchorNode?.parentElement.closest('.mes_text')) {
11 return;
12 }
13
14 const range = selection.getRangeAt(0).cloneContents();
15 const tempDOM = document.createDocumentFragment();
16
17 function processNode(node) {
18 if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'q') {
19 // Transform <q> to <span>, preserve children
20 const span = document.createElement('span');
21
22 [...node.childNodes].forEach(child => {
23 const processedChild = processNode(child);
24 span.appendChild(processedChild);
25 });
26
27 return span;
28 } else {
29 // Nested structures containing <q> elements are unlikely
30 return node.cloneNode(true);
31 }
32 }
33
34 [...range.childNodes].forEach(child => {
35 const processedChild = processNode(child);
36 tempDOM.appendChild(processedChild);
37 });
38
39 const newRange = document.createRange();
40 newRange.selectNodeContents(tempDOM);
41
42 event.preventDefault();
43 event.clipboardData.setData('text/plain', newRange.toString());
44 });
45}
46
47function addSafariPatch() {
48 const userAgent = getParsedUA();
49 console.debug('User Agent', userAgent);
50 const isMobileSafari = /iPad|iPhone|iPod/.test(navigator.platform) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
51 const isDesktopSafari = userAgent?.browser?.name === 'Safari' && userAgent?.platform?.type === 'desktop';
52 const isIOS = userAgent?.os?.name === 'iOS';
53
54 if (isIOS || isMobileSafari || isDesktopSafari) {
55 document.body.classList.add('safari');
56 }
57}
58
59function applyBrowserFixes() {
60 if (isFirefox()) {
61 sanitizeInlineQuotationOnCopy();
62 }
63
64 if (isMobile()) {
65 const fixFunkyPositioning = () => {
66 console.debug('[Mobile] Device viewport change detected.');
67 document.documentElement.style.position = 'fixed';
68 requestAnimationFrame(() => document.documentElement.style.position = '');
69 };
70 window.addEventListener('resize', fixFunkyPositioning);
71 window.addEventListener('orientationchange', fixFunkyPositioning);
72 }
73
74 addSafariPatch();
75}
76
77export { isFirefox, applyBrowserFixes };
public/scripts/extensions/stable-diffusion/index.js+57 -32
@@ -19,7 +19,7 @@ import {
19} from '../../../script.js';19} from '../../../script.js';
20import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';20import { getApiUrl, getContext, extension_settings, doExtrasFetch, modules, renderExtensionTemplateAsync, writeExtensionField } from '../../extensions.js';
21import { selected_group } from '../../group-chats.js';21import { selected_group } from '../../group-chats.js';
22import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean } from '../../utils.js';22import { stringFormat, initScrollHeight, resetScrollHeight, getCharaFilename, saveBase64AsFile, getBase64Async, delay, isTrueBoolean, debounce, isFalseBoolean, deepMerge } from '../../utils.js';
23import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js';23import { getMessageTimeStamp, humanizedDateTime } from '../../RossAscends-mods.js';
24import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';24import { SECRET_KEYS, secret_state, writeSecret } from '../../secrets.js';
25import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js';25import { getNovelUnlimitedImageGeneration, getNovelAnlas, loadNovelSubscriptionData } from '../../nai-settings.js';
@@ -220,6 +220,7 @@ const defaultSettings = {
220 // Automatic1111/Horde exclusives220 // Automatic1111/Horde exclusives
221 restore_faces: false,221 restore_faces: false,
222 enable_hr: false,222 enable_hr: false,
223 adetailer_face: false,
223224
224 // Horde settings225 // Horde settings
225 horde: false,226 horde: false,
@@ -436,6 +437,7 @@ async function loadSettings() {
436 $('#sd_horde_sanitize').prop('checked', extension_settings.sd.horde_sanitize);437 $('#sd_horde_sanitize').prop('checked', extension_settings.sd.horde_sanitize);
437 $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces);438 $('#sd_restore_faces').prop('checked', extension_settings.sd.restore_faces);
438 $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr);439 $('#sd_enable_hr').prop('checked', extension_settings.sd.enable_hr);
440 $('#sd_adetailer_face').prop('checked', extension_settings.sd.adetailer_face);
439 $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode);441 $('#sd_refine_mode').prop('checked', extension_settings.sd.refine_mode);
440 $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning);442 $('#sd_multimodal_captioning').prop('checked', extension_settings.sd.multimodal_captioning);
441 $('#sd_auto_url').val(extension_settings.sd.auto_url);443 $('#sd_auto_url').val(extension_settings.sd.auto_url);
@@ -854,6 +856,11 @@ function onSamplerChange() {
854 saveSettingsDebounced();856 saveSettingsDebounced();
855}857}
856858
859function onADetailerFaceChange() {
860 extension_settings.sd.adetailer_face = !!$('#sd_adetailer_face').prop('checked');
861 saveSettingsDebounced();
862}
863
857const resolutionOptions = {864const resolutionOptions = {
858 sd_res_512x512: { width: 512, height: 512, name: '512x512 (1:1, icons, profile pictures)' },865 sd_res_512x512: { width: 512, height: 512, name: '512x512 (1:1, icons, profile pictures)' },
859 sd_res_600x600: { width: 600, height: 600, name: '600x600 (1:1, icons, profile pictures)' },866 sd_res_600x600: { width: 600, height: 600, name: '600x600 (1:1, icons, profile pictures)' },
@@ -2921,41 +2928,58 @@ async function generateHordeImage(prompt, negativePrompt, signal) {
2921 */2928 */
2922async function generateAutoImage(prompt, negativePrompt, signal) {2929async function generateAutoImage(prompt, negativePrompt, signal) {
2923 const isValidVae = extension_settings.sd.vae && !['N/A', placeholderVae].includes(extension_settings.sd.vae);2930 const isValidVae = extension_settings.sd.vae && !['N/A', placeholderVae].includes(extension_settings.sd.vae);
2931 let payload = {
2932 ...getSdRequestBody(),
2933 prompt: prompt,
2934 negative_prompt: negativePrompt,
2935 sampler_name: extension_settings.sd.sampler,
2936 scheduler: extension_settings.sd.scheduler,
2937 steps: extension_settings.sd.steps,
2938 cfg_scale: extension_settings.sd.scale,
2939 width: extension_settings.sd.width,
2940 height: extension_settings.sd.height,
2941 restore_faces: !!extension_settings.sd.restore_faces,
2942 enable_hr: !!extension_settings.sd.enable_hr,
2943 hr_upscaler: extension_settings.sd.hr_upscaler,
2944 hr_scale: extension_settings.sd.hr_scale,
2945 denoising_strength: extension_settings.sd.denoising_strength,
2946 hr_second_pass_steps: extension_settings.sd.hr_second_pass_steps,
2947 seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
2948 override_settings: {
2949 CLIP_stop_at_last_layers: extension_settings.sd.clip_skip,
2950 sd_vae: isValidVae ? extension_settings.sd.vae : undefined,
2951 },
2952 override_settings_restore_afterwards: true,
2953 clip_skip: extension_settings.sd.clip_skip, // For SD.Next
2954 save_images: true,
2955 send_images: true,
2956 do_not_save_grid: false,
2957 do_not_save_samples: false,
2958 };
2959
2960 // Conditionally add the ADetailer if adetailer_face is enabled
2961 if (extension_settings.sd.adetailer_face) {
2962 payload = deepMerge(payload, {
2963 alwayson_scripts: {
2964 ADetailer: {
2965 args: [
2966 true, // ad_enable
2967 true, // skip_img2img
2968 {
2969 'ad_model': 'face_yolov8n.pt',
2970 },
2971 ],
2972 },
2973 },
2974 });
2975 }
2976
2977 // Make the fetch call with the payload
2924 const result = await fetch('/api/sd/generate', {2978 const result = await fetch('/api/sd/generate', {
2925 method: 'POST',2979 method: 'POST',
2926 headers: getRequestHeaders(),2980 headers: getRequestHeaders(),
2927 signal: signal,2981 signal: signal,
2928 body: JSON.stringify({2982 body: JSON.stringify(payload),
2929 ...getSdRequestBody(),
2930 prompt: prompt,
2931 negative_prompt: negativePrompt,
2932 sampler_name: extension_settings.sd.sampler,
2933 scheduler: extension_settings.sd.scheduler,
2934 steps: extension_settings.sd.steps,
2935 cfg_scale: extension_settings.sd.scale,
2936 width: extension_settings.sd.width,
2937 height: extension_settings.sd.height,
2938 restore_faces: !!extension_settings.sd.restore_faces,
2939 enable_hr: !!extension_settings.sd.enable_hr,
2940 hr_upscaler: extension_settings.sd.hr_upscaler,
2941 hr_scale: extension_settings.sd.hr_scale,
2942 denoising_strength: extension_settings.sd.denoising_strength,
2943 hr_second_pass_steps: extension_settings.sd.hr_second_pass_steps,
2944 seed: extension_settings.sd.seed >= 0 ? extension_settings.sd.seed : undefined,
2945 // For AUTO1111
2946 override_settings: {
2947 CLIP_stop_at_last_layers: extension_settings.sd.clip_skip,
2948 sd_vae: isValidVae ? extension_settings.sd.vae : undefined,
2949 },
2950 override_settings_restore_afterwards: true,
2951 // For SD.Next
2952 clip_skip: extension_settings.sd.clip_skip,
2953 // Ensure generated img is saved to disk
2954 save_images: true,
2955 send_images: true,
2956 do_not_save_grid: false,
2957 do_not_save_samples: false,
2958 }),
2959 });2983 });
29602984
2961 if (result.ok) {2985 if (result.ok) {
@@ -4187,6 +4211,7 @@ jQuery(async () => {
4187 $('#sd_horde_sanitize').on('input', onHordeSanitizeInput);4211 $('#sd_horde_sanitize').on('input', onHordeSanitizeInput);
4188 $('#sd_restore_faces').on('input', onRestoreFacesInput);4212 $('#sd_restore_faces').on('input', onRestoreFacesInput);
4189 $('#sd_enable_hr').on('input', onHighResFixInput);4213 $('#sd_enable_hr').on('input', onHighResFixInput);
4214 $('#sd_adetailer_face').on('change', onADetailerFaceChange);
4190 $('#sd_refine_mode').on('input', onRefineModeInput);4215 $('#sd_refine_mode').on('input', onRefineModeInput);
4191 $('#sd_character_prompt').on('input', onCharacterPromptInput);4216 $('#sd_character_prompt').on('input', onCharacterPromptInput);
4192 $('#sd_character_negative_prompt').on('input', onCharacterNegativePromptInput);4217 $('#sd_character_negative_prompt').on('input', onCharacterNegativePromptInput);
public/scripts/extensions/stable-diffusion/settings.html+10 -0
@@ -356,6 +356,16 @@
356 </label>356 </label>
357 </div>357 </div>
358358
359 <div class="flex-container marginTopBot5" data-sd-source="auto,vlad">
360 <label for="sd_adetailer_face" class="flex1 checkbox_label" data-i18n="[title]sd_adetailer_face" title="Use ADetailer with face model during the generation. The ADetailer extension must be installed on the backend.">
361 <input id="sd_adetailer_face" type="checkbox" />
362 <small data-i18n="Use ADetailer (Face)">Use ADetailer (Face)</small>
363 </label>
364 <div class="flex1">
365 <!-- I will be useful later! -->
366 </div>
367 </div>
368
359 <div class="flex-container marginTopBot5" data-sd-source="novel">369 <div class="flex-container marginTopBot5" data-sd-source="novel">
360 <label class="flex1 checkbox_label" data-i18n="[title]SMEA versions of samplers are modified to perform better at high resolution." title="SMEA versions of samplers are modified to perform better at high resolution.">370 <label class="flex1 checkbox_label" data-i18n="[title]SMEA versions of samplers are modified to perform better at high resolution." title="SMEA versions of samplers are modified to perform better at high resolution.">
361 <input id="sd_novel_sm" type="checkbox" />371 <input id="sd_novel_sm" type="checkbox" />
public/scripts/utils.js+32 -0
@@ -30,6 +30,38 @@ export const navigation_option = {
30 previous: -1000,30 previous: -1000,
31};31};
3232
33/**
34 * Determines if a value is an object.
35 * @param {any} item The item to check.
36 * @returns {boolean} True if the item is an object, false otherwise.
37 */
38function isObject(item) {
39 return (item && typeof item === 'object' && !Array.isArray(item));
40}
41
42/**
43 * Merges properties of two objects. If the property is an object, it will be merged recursively.
44 * @param {object} target The target object
45 * @param {object} source The source object
46 * @returns {object} Merged object
47 */
48export function deepMerge(target, source) {
49 let output = Object.assign({}, target);
50 if (isObject(target) && isObject(source)) {
51 Object.keys(source).forEach(key => {
52 if (isObject(source[key])) {
53 if (!(key in target))
54 Object.assign(output, { [key]: source[key] });
55 else
56 output[key] = deepMerge(target[key], source[key]);
57 } else {
58 Object.assign(output, { [key]: source[key] });
59 }
60 });
61 }
62 return output;
63}
64
33export function escapeHtml(str) {65export function escapeHtml(str) {
34 return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');66 return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
35}67}