zhenxun_bot/zhenxun/ui/__init__.py

227 lines
7.2 KiB
Python
Raw Normal View History

from pathlib import Path
from typing import Any
from zhenxun.services.renderer.protocols import Renderable
from . import builders
from .builders.core.layout import LayoutBuilder
from .models.core.base import RenderableComponent
from .models.core.markdown import MarkdownData
from .models.core.template import TemplateComponent
def template(path: str | Path, data: dict[str, Any]) -> TemplateComponent:
"""
创建一个基于独立模板文件的UI组件
适用于不希望遵循标准主题结构而是直接渲染单个HTML文件的场景
参数:
path: 指向HTML模板文件的绝对或相对路径
data: 传递给模板的上下文数据字典
返回:
TemplateComponent: 一个可被 `render()` 函数处理的组件实例
"""
if isinstance(path, str):
path = Path(path)
return TemplateComponent(template_path=path, data=data)
def markdown(content: str, style: str | Path | None = "default") -> MarkdownData:
"""
创建一个基于Markdown内容的UI组件
参数:
content: 要渲染的Markdown字符串
style: (可选) Markdown的样式名称 'github-light'或一个指向
自定义CSS文件的路径
返回:
MarkdownData: 一个可被 `render()` 函数处理的组件实例
"""
builder = builders.MarkdownBuilder().text(content)
component = builder.build()
if isinstance(style, Path):
component.css_path = str(style.absolute())
else:
component.style_name = style
return component
def vstack(children: list[RenderableComponent], **layout_options) -> "LayoutBuilder":
"""
创建一个垂直布局组件
便捷函数用于将多个组件垂直堆叠
参数:
children: 一个包含 `RenderableComponent` 实例的列表
**layout_options: 传递给布局模板的额外选项 `padding`, `gap`
返回:
LayoutBuilder: 一个配置好的垂直布局构建器
"""
builder = LayoutBuilder.column(**layout_options)
for child in children:
builder.add_item(child)
return builder
def hstack(children: list[RenderableComponent], **layout_options) -> "LayoutBuilder":
"""
创建一个水平布局组件
便捷函数用于将多个组件水平排列
参数:
children: 一个包含 `RenderableComponent` 实例的列表
**layout_options: 传递给布局模板的额外选项 `padding`, `gap`
返回:
LayoutBuilder: 一个配置好的水平布局构建器
"""
builder = LayoutBuilder.row(**layout_options)
for child in children:
builder.add_item(child)
return builder
async def render(
component_or_path: Renderable | str | Path,
data: dict | None = None,
*,
use_cache: bool = False,
**kwargs,
) -> bytes:
"""
统一的UI渲染入口
这是第三方开发者最常用的函数用于将任何可渲染对象转换为图片
用法:
1. 渲染一个已构建的UI组件: `render(my_builder.build())`
2. 直接渲染一个模板文件: `render("path/to/template", data={...})`
参数:
component_or_path: 一个 `Renderable` 实例或一个指向模板文件的
`str` `Path` 对象
data: (可选) `component_or_path` 是路径时必须提供此数据字典
use_cache: (可选) 是否为此渲染启用文件缓存默认为 `False`
**kwargs: 传递给底层截图引擎的额外参数例如 `viewport`
返回:
bytes: 渲染后的PNG图片字节数据
"""
from zhenxun.services import renderer_service
component: Renderable
if isinstance(component_or_path, str | Path):
if data is None:
raise ValueError("使用模板路径渲染时必须提供 'data' 参数。")
component = TemplateComponent(template_path=component_or_path, data=data)
else:
component = component_or_path
return await renderer_service.render(component, use_cache=use_cache, **kwargs)
async def render_template(
path: str | Path, data: dict, use_cache: bool = False, **kwargs
) -> bytes:
"""
渲染一个独立的Jinja2模板文件
这是一个便捷函数封装了 render() 函数的调用提供更简洁的模板渲染接口
参数:
path: 模板文件路径相对于主题模板目录
data: 传递给模板的数据字典
use_cache: (可选) 是否启用渲染缓存默认为 False
**kwargs: 传递给渲染服务的额外参数
返回:
bytes: 渲染后的图片数据
异常:
RenderingError: 渲染失败时抛出
"""
return await render(path, data, use_cache=use_cache, **kwargs)
async def render_markdown(
md: str, style: str | Path | None = "default", use_cache: bool = False, **kwargs
) -> bytes:
"""
将Markdown字符串渲染为图片
这是一个便捷函数封装了 render() 函数的调用专门用于渲染Markdown内容
参数:
md: 要渲染的Markdown内容字符串
style: (可选) 样式名称或自定义CSS文件路径默认为 "default"
use_cache: (可选) 是否启用渲染缓存默认为 False
**kwargs: 传递给渲染服务的额外参数
返回:
bytes: 渲染后的图片数据
异常:
RenderingError: 渲染失败时抛出
"""
builder = builders.MarkdownBuilder().text(md)
component = builder.build()
if isinstance(style, Path):
component.css_path = str(style.absolute())
else:
component.style_name = style
return await render(component, use_cache=use_cache, **kwargs)
from zhenxun.services.renderer.protocols import RenderResult
async def render_full_result(
component: Renderable, use_cache: bool = False, **kwargs
) -> RenderResult:
"""
渲染组件并返回包含图片和HTML的完整结果对象
主要用于调试或需要同时访问图片和其源HTML的场景
参数:
component: 一个 `Renderable` 实例
use_cache: (可选) 是否为此渲染启用文件缓存默认为 `False`
**kwargs: 传递给底层截图引擎的额外参数
返回:
RenderResult: 一个包含 `image_bytes` `html_content` 的Pydantic模型
"""
from zhenxun.services import renderer_service
from zhenxun.services.renderer.service import RenderContext
if not renderer_service._initialized:
await renderer_service.initialize()
assert renderer_service._theme_manager is not None, "ThemeManager 未初始化"
assert renderer_service._screenshot_engine is not None, "ScreenshotEngine 未初始化"
context = RenderContext(
renderer=renderer_service,
theme_manager=renderer_service._theme_manager,
screenshot_engine=renderer_service._screenshot_engine,
component=component,
use_cache=use_cache,
render_options=kwargs,
)
return await renderer_service._render_component(context)
__all__ = [
"builders",
"hstack",
"markdown",
"render",
"render_full_result",
"render_markdown",
"render_template",
"template",
"vstack",
]