mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Add async template renderer
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, substituteParams, renderTemplate, animation_duration } from '../script.js';
|
||||
import { callPopup, eventSource, event_types, saveSettings, saveSettingsDebounced, getRequestHeaders, animation_duration } from '../script.js';
|
||||
import { hideLoader, showLoader } from './loader.js';
|
||||
import { renderTemplate, renderTemplateAsync } from '../script.js';
|
||||
import { isSubsetOf, setValueByPath } from './utils.js';
|
||||
export {
|
||||
getContext,
|
||||
@ -50,17 +51,31 @@ export function saveMetadataDebounced() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an ability for extensions to render HTML templates.
|
||||
* Provides an ability for extensions to render HTML templates synchronously.
|
||||
* Templates sanitation and localization is forced.
|
||||
* @param {string} extensionName Extension name
|
||||
* @param {string} templateId Template ID
|
||||
* @param {object} templateData Additional data to pass to the template
|
||||
* @returns {string} Rendered HTML
|
||||
*
|
||||
* @deprecated Use renderExtensionTemplateAsync instead.
|
||||
*/
|
||||
export function renderExtensionTemplate(extensionName, templateId, templateData = {}, sanitize = true, localize = true) {
|
||||
return renderTemplate(`scripts/extensions/${extensionName}/${templateId}.html`, templateData, sanitize, localize, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an ability for extensions to render HTML templates asynchronously.
|
||||
* Templates sanitation and localization is forced.
|
||||
* @param {string} extensionName Extension name
|
||||
* @param {string} templateId Template ID
|
||||
* @param {object} templateData Additional data to pass to the template
|
||||
* @returns {Promise<string>} Rendered HTML
|
||||
*/
|
||||
export function renderExtensionTemplateAsync(extensionName, templateId, templateData = {}, sanitize = true, localize = true) {
|
||||
return renderTemplateAsync(`scripts/extensions/${extensionName}/${templateId}.html`, templateData, sanitize, localize, true);
|
||||
}
|
||||
|
||||
// Disables parallel updates
|
||||
class ModuleWorkerWrapper {
|
||||
constructor(callback) {
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
printCharactersDebounced,
|
||||
setCharacterId,
|
||||
setEditedMessageId,
|
||||
renderTemplate,
|
||||
chat,
|
||||
getFirstDisplayedMessageId,
|
||||
showMoreMessages,
|
||||
@ -23,6 +22,7 @@ import {
|
||||
ANIMATION_DURATION_DEFAULT,
|
||||
setActiveGroup,
|
||||
setActiveCharacter,
|
||||
renderTemplateAsync,
|
||||
} from '../script.js';
|
||||
import { isMobile, initMovingUI, favsToHotswap } from './RossAscends-mods.js';
|
||||
import {
|
||||
@ -1363,8 +1363,8 @@ export function registerDebugFunction(functionId, name, description, func) {
|
||||
debug_functions.push({ functionId, name, description, func });
|
||||
}
|
||||
|
||||
function showDebugMenu() {
|
||||
const template = renderTemplate('debug', { functions: debug_functions });
|
||||
async function showDebugMenu() {
|
||||
const template = await renderTemplateAsync('debug', { functions: debug_functions });
|
||||
callPopup(template, 'text', '', { wide: true, large: true });
|
||||
}
|
||||
|
||||
|
130
public/scripts/templates.js
Normal file
130
public/scripts/templates.js
Normal file
@ -0,0 +1,130 @@
|
||||
import { applyLocale } from './i18n.js';
|
||||
|
||||
/**
|
||||
* @type {Map<string, function>}
|
||||
* @description Cache for Handlebars templates.
|
||||
*/
|
||||
const TEMPLATE_CACHE = new Map();
|
||||
|
||||
/**
|
||||
* Loads a URL content using XMLHttpRequest synchronously.
|
||||
* @param {string} url URL to load synchronously
|
||||
* @returns {string} Response text
|
||||
*/
|
||||
function getUrlSync(url) {
|
||||
console.debug('Loading URL synchronously', url);
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', url, false); // `false` makes the request synchronous
|
||||
request.send();
|
||||
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
return request.responseText;
|
||||
}
|
||||
|
||||
throw new Error(`Error loading ${url}: ${request.status} ${request.statusText}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a URL content using XMLHttpRequest asynchronously.
|
||||
* @param {string} url URL to load asynchronously
|
||||
* @returns {Promise<string>} Response text
|
||||
*/
|
||||
function getUrlAsync(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
request.onload = () => {
|
||||
if (request.status >= 200 && request.status < 300) {
|
||||
resolve(request.responseText);
|
||||
} else {
|
||||
reject(new Error(`Error loading ${url}: ${request.status} ${request.statusText}`));
|
||||
}
|
||||
};
|
||||
request.onerror = () => {
|
||||
reject(new Error(`Error loading ${url}: ${request.status} ${request.statusText}`));
|
||||
};
|
||||
request.send();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a Handlebars template asynchronously.
|
||||
* @param {string} templateId ID of the template to render
|
||||
* @param {Record<string, any>} templateData The data to pass to the template
|
||||
* @param {boolean} sanitize Should the template be sanitized with DOMPurify
|
||||
* @param {boolean} localize Should the template be localized
|
||||
* @param {boolean} fullPath Should the template ID be treated as a full path or a relative path
|
||||
* @returns {Promise<string>} Rendered template
|
||||
*/
|
||||
export async function renderTemplateAsync(templateId, templateData = {}, sanitize = true, localize = true, fullPath = false) {
|
||||
async function fetchTemplateAsync(pathToTemplate) {
|
||||
let template = TEMPLATE_CACHE.get(pathToTemplate);
|
||||
if (!template) {
|
||||
const templateContent = await getUrlAsync(pathToTemplate);
|
||||
template = Handlebars.compile(templateContent);
|
||||
TEMPLATE_CACHE.set(pathToTemplate, template);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
try {
|
||||
const pathToTemplate = fullPath ? templateId : `/scripts/templates/${templateId}.html`;
|
||||
const template = await fetchTemplateAsync(pathToTemplate);
|
||||
let result = template(templateData);
|
||||
|
||||
if (sanitize) {
|
||||
result = DOMPurify.sanitize(result);
|
||||
}
|
||||
|
||||
if (localize) {
|
||||
result = applyLocale(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
console.error('Error rendering template', templateId, templateData, err);
|
||||
toastr.error('Check the DevTools console for more information.', 'Error rendering template');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a Handlebars template synchronously.
|
||||
* @param {string} templateId ID of the template to render
|
||||
* @param {Record<string, any>} templateData The data to pass to the template
|
||||
* @param {boolean} sanitize Should the template be sanitized with DOMPurify
|
||||
* @param {boolean} localize Should the template be localized
|
||||
* @param {boolean} fullPath Should the template ID be treated as a full path or a relative path
|
||||
* @returns {string} Rendered template
|
||||
*
|
||||
* @deprecated Use renderTemplateAsync instead.
|
||||
*/
|
||||
export function renderTemplate(templateId, templateData = {}, sanitize = true, localize = true, fullPath = false) {
|
||||
function fetchTemplateSync(pathToTemplate) {
|
||||
let template = TEMPLATE_CACHE.get(pathToTemplate);
|
||||
if (!template) {
|
||||
const templateContent = getUrlSync(pathToTemplate);
|
||||
template = Handlebars.compile(templateContent);
|
||||
TEMPLATE_CACHE.set(pathToTemplate, template);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
|
||||
try {
|
||||
const pathToTemplate = fullPath ? templateId : `/scripts/templates/${templateId}.html`;
|
||||
const template = fetchTemplateSync(pathToTemplate);
|
||||
let result = template(templateData);
|
||||
|
||||
if (sanitize) {
|
||||
result = DOMPurify.sanitize(result);
|
||||
}
|
||||
|
||||
if (localize) {
|
||||
result = applyLocale(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
console.error('Error rendering template', templateId, templateData, err);
|
||||
toastr.error('Check the DevTools console for more information.', 'Error rendering template');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user