Files
ST-Bionic-Memory-Ecology/host/st-native-render.js
2026-04-08 02:09:32 +08:00

164 lines
4.2 KiB
JavaScript

import { substituteParamsExtended } from "../../../../../script.js";
import jsyaml from "../vendor/js-yaml.mjs";
function getTemplateRuntime() {
return globalThis.window?.EjsTemplate || globalThis.EjsTemplate || null;
}
function safeStringify(value) {
if (value == null) return "";
if (typeof value === "string") return value;
try {
return JSON.stringify(value, null, 2);
} catch {
return String(value);
}
}
function deepGet(target, path) {
if (!target || !path) return undefined;
const parts = String(path || "")
.split(".")
.filter(Boolean);
let current = target;
for (const part of parts) {
if (current == null) return undefined;
current = current[part];
}
return current;
}
export function getLatestMessageVarTable() {
try {
if (globalThis.window?.Mvu?.getMvuData) {
return (
globalThis.window.Mvu.getMvuData({
type: "message",
message_id: "latest",
}) || {}
);
}
} catch {
// ignore
}
try {
const getVars =
globalThis.window?.TavernHelper?.getVariables ||
globalThis.window?.Mvu?.getMvuData ||
globalThis.TavernHelper?.getVariables ||
globalThis.Mvu?.getMvuData;
if (typeof getVars === "function") {
return getVars({ type: "message", message_id: "latest" }) || {};
}
} catch {
// ignore
}
return {};
}
export async function prepareStNativeEjsEnv() {
try {
const runtime = getTemplateRuntime();
const prepare =
runtime?.prepareContext || runtime?.preparecontext || null;
if (typeof prepare !== "function") {
return null;
}
return (await prepare.call(runtime, {})) || null;
} catch {
return null;
}
}
function substituteMacrosViaST(text) {
try {
if (typeof substituteParamsExtended === "function") {
return substituteParamsExtended(text);
}
} catch {
// ignore
}
return text;
}
function resolveGetMessageVariableMacros(text, messageVars) {
return String(text || "").replace(
/\{\{\s*get_message_variable::([^}]+)\s*}}/g,
(_, rawPath) => {
const path = String(rawPath || "").trim();
if (!path) return "";
return safeStringify(deepGet(messageVars, path));
},
);
}
function resolveFormatMessageVariableMacros(text, messageVars) {
return String(text || "").replace(
/\{\{\s*format_message_variable::([^}]+)\s*}}/g,
(_, rawPath) => {
const path = String(rawPath || "").trim();
if (!path) return "";
const value = deepGet(messageVars, path);
if (value == null) return "";
if (typeof value === "string") return value;
try {
return jsyaml.dump(value, {
lineWidth: -1,
noRefs: true,
});
} catch {
return safeStringify(value);
}
},
);
}
export async function renderTemplateWithStSupport(
text,
{ env = null, messageVars = null, evaluateEjs = true } = {},
) {
const originalText = String(text ?? "");
const runtime = getTemplateRuntime();
const effectiveEnv = env || null;
const effectiveMessageVars =
messageVars && typeof messageVars === "object"
? messageVars
: getLatestMessageVarTable();
let output = originalText;
let ejsEvaluated = false;
let ejsError = null;
if (evaluateEjs && originalText.includes("<%")) {
try {
const evalTemplate =
runtime?.evalTemplate || runtime?.evaltemplate || null;
if (runtime && effectiveEnv && typeof evalTemplate === "function") {
output = await evalTemplate.call(runtime, output, effectiveEnv);
ejsEvaluated = true;
}
} catch (error) {
ejsError = error;
}
}
const afterMacroSubstitute = substituteMacrosViaST(output);
const afterMessageVariableResolve = resolveFormatMessageVariableMacros(
resolveGetMessageVariableMacros(afterMacroSubstitute, effectiveMessageVars),
effectiveMessageVars,
);
return {
text: afterMessageVariableResolve,
stNativeRuntimeAvailable: Boolean(runtime),
envPrepared: Boolean(effectiveEnv),
ejsEvaluated,
ejsError,
macroApplied: afterMacroSubstitute !== output,
messageVariableMacrosApplied:
afterMessageVariableResolve !== afterMacroSubstitute,
};
}