diff --git a/astrbot/core/utils/t2i/network_strategy.py b/astrbot/core/utils/t2i/network_strategy.py index 5f9385614f..4fec121953 100644 --- a/astrbot/core/utils/t2i/network_strategy.py +++ b/astrbot/core/utils/t2i/network_strategy.py @@ -1,7 +1,7 @@ import asyncio -import base64 import logging import random +import re from functools import lru_cache from pathlib import Path @@ -15,6 +15,11 @@ from . import RenderStrategy ASTRBOT_T2I_DEFAULT_ENDPOINT = "https://t2i.soulter.top/text2img" +SHIKI_RUNTIME_SCRIPT_ID = "astrbot-t2i-shiki-runtime" +SHIKI_RUNTIME_TEMPLATE_PATTERN = re.compile(r"\{\{\s*shiki_runtime\s*\|\s*safe\s*\}\}") +JINJA_SYNTAX_PATTERN = re.compile(r"\{[{%#]") +JINJA_RAW_OPEN_PATTERN = re.compile(r"{%-?\s*raw\s*-?%}") +JINJA_RAW_CLOSE_PATTERN = re.compile(r"{%-?\s*endraw\s*-?%}") logger = logging.getLogger("astrbot") @@ -41,7 +46,49 @@ def get_shiki_runtime() -> str: ) return "" - return runtime.replace(" bool: + raw_open_index = -1 + for match in JINJA_RAW_OPEN_PATTERN.finditer(tmpl_str, 0, index): + raw_open_index = match.start() + + raw_close_index = -1 + for match in JINJA_RAW_CLOSE_PATTERN.finditer(tmpl_str, 0, index): + raw_close_index = match.start() + + return raw_open_index > raw_close_index + + +def _wrap_runtime_for_jinja(tmpl_str: str, script: str, index: int) -> str: + if not JINJA_SYNTAX_PATTERN.search(script) or _is_inside_jinja_raw_block( + tmpl_str, + index, + ): + return script + + return f"{{% raw %}}{script}{{% endraw %}}" + + +def inject_shiki_runtime(tmpl_str: str) -> str: + if SHIKI_RUNTIME_SCRIPT_ID in tmpl_str or SHIKI_RUNTIME_TEMPLATE_PATTERN.search( + tmpl_str, + ): + return tmpl_str + + runtime = get_shiki_runtime() + if not runtime: + return tmpl_str + + script = f'' + head_close = re.search(r"", tmpl_str, flags=re.IGNORECASE) + if head_close: + script = _wrap_runtime_for_jinja(tmpl_str, script, head_close.start()) + return f"{tmpl_str[: head_close.start()]} {script}\n{tmpl_str[head_close.start() :]}" + + script = _wrap_runtime_for_jinja(tmpl_str, script, 0) + return f"{script}\n{tmpl_str}" class NetworkRenderStrategy(RenderStrategy): @@ -101,11 +148,17 @@ async def render_custom_template( options: dict | None = None, ) -> str: """使用自定义文转图模板""" - default_options = {"full_page": True, "type": "jpeg", "quality": 40} + default_options = { + "full_page": True, + "type": "jpeg", + "quality": 40, + } if options: default_options |= options - tmpl_data = {"shiki_runtime": get_shiki_runtime()} | tmpl_data + if SHIKI_RUNTIME_TEMPLATE_PATTERN.search(tmpl_str): + tmpl_data = {"shiki_runtime": get_shiki_runtime()} | tmpl_data + tmpl_str = inject_shiki_runtime(tmpl_str) post_data = { "tmpl": tmpl_str, "json": return_url, @@ -158,9 +211,11 @@ async def render( if not template_name: template_name = "base" tmpl_str = await self.get_template(name=template_name) - text_base64 = base64.b64encode(text.encode("utf-8")).decode("ascii") return await self.render_custom_template( tmpl_str, - {"text_base64": text_base64, "version": f"v{VERSION}"}, + { + "text": text, + "version": f"v{VERSION}", + }, return_url, ) diff --git a/astrbot/core/utils/t2i/template/astrbot_powershell.html b/astrbot/core/utils/t2i/template/astrbot_powershell.html index 746d06fe80..3bfa014c0c 100644 --- a/astrbot/core/utils/t2i/template/astrbot_powershell.html +++ b/astrbot/core/utils/t2i/template/astrbot_powershell.html @@ -174,14 +174,14 @@
- + diff --git a/astrbot/core/utils/t2i/template/astrbot_vitepress.html b/astrbot/core/utils/t2i/template/astrbot_vitepress.html index 381da3f98c..4e02636968 100644 --- a/astrbot/core/utils/t2i/template/astrbot_vitepress.html +++ b/astrbot/core/utils/t2i/template/astrbot_vitepress.html @@ -415,14 +415,14 @@

AstrBot Docs

- + + diff --git a/dashboard/src/components/shared/T2ITemplateEditor.vue b/dashboard/src/components/shared/T2ITemplateEditor.vue index eac49c7601..485032dbc1 100644 --- a/dashboard/src/components/shared/T2ITemplateEditor.vue +++ b/dashboard/src/components/shared/T2ITemplateEditor.vue @@ -298,17 +298,82 @@ const syncPreviewVersion = async () => { const previewData = computed(() => ({ text: tm('t2iTemplateEditor.previewText') || '这是一个示例文本,用于预览模板效果。\n\n这里可以包含多行文本,支持换行和各种格式。', - version: previewVersion.value + version: previewVersion.value })) +const injectShikiRuntime = (content) => { + if (content.includes('astrbot-t2i-shiki-runtime')) { + return content + } + + const runtimeScript = getShikiRuntimeScript() + const headClose = content.search(/<\/head\s*>/i) + if (headClose >= 0) { + return `${content.slice(0, headClose)} ${runtimeScript}\n${content.slice(headClose)}` + } + + return `${runtimeScript}\n${content}` +} + +const getShikiRuntimeScript = () => '