@zhin.js/plugin-html-renderer 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +567 -0
- package/lib/index.js.map +1 -0
- package/lib/types.d.ts +94 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +5 -0
- package/lib/types.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# @zhin.js/plugin-html-renderer
|
|
2
|
+
|
|
3
|
+
使用 [@zhinjs/satori](https://github.com/zhinjs/satori) 将 HTML/CSS 渲染为图片的 Zhin.js 插件。
|
|
4
|
+
|
|
5
|
+
## 特点
|
|
6
|
+
|
|
7
|
+
- 🚀 **基于 JSDOM** - 直接解析 HTML/CSS,无需 React
|
|
8
|
+
- 🎨 **CSS 支持** - 支持 Flexbox、渐变、阴影等常用 CSS 属性
|
|
9
|
+
- 📝 **中文支持** - 自动加载 Noto Sans SC 字体
|
|
10
|
+
- 🤖 **AI 集成** - 提供 `html.render` 和 `html.card` 工具供 AI 使用
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @zhin.js/plugin-html-renderer
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 使用
|
|
19
|
+
|
|
20
|
+
### 在配置文件中启用
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
# zhin.config.yml
|
|
24
|
+
plugins:
|
|
25
|
+
- "@zhin.js/plugin-html-renderer"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 代码中使用
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { useContext } from 'zhin.js';
|
|
32
|
+
|
|
33
|
+
// 获取渲染服务
|
|
34
|
+
const renderer = useContext('html-renderer');
|
|
35
|
+
|
|
36
|
+
// 渲染 HTML 为 PNG
|
|
37
|
+
const result = await renderer.render(`
|
|
38
|
+
<div style="
|
|
39
|
+
display: flex;
|
|
40
|
+
padding: 20px;
|
|
41
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
42
|
+
color: white;
|
|
43
|
+
border-radius: 12px;
|
|
44
|
+
">
|
|
45
|
+
Hello, World!
|
|
46
|
+
</div>
|
|
47
|
+
`, {
|
|
48
|
+
width: 400,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// result.data 是 PNG Buffer
|
|
52
|
+
// result.width / result.height 是实际尺寸
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### AI 工具
|
|
56
|
+
|
|
57
|
+
插件提供两个 AI 工具:
|
|
58
|
+
|
|
59
|
+
#### `html.render` - 渲染任意 HTML
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
用户: 帮我画一个红色的方块
|
|
63
|
+
AI: 调用 html.render 工具生成图片
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### `html.card` - 生成卡片
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
用户: 生成一张蓝色主题的通知卡片,标题是"系统消息",内容是"服务已更新"
|
|
70
|
+
AI: 调用 html.card 工具生成美观的卡片图片
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## 配置
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
# zhin.config.yml
|
|
77
|
+
htmlRenderer:
|
|
78
|
+
defaultWidth: 800 # 默认宽度
|
|
79
|
+
defaultBackgroundColor: "#ffffff" # 默认背景色
|
|
80
|
+
fontUrls: # 自定义字体 URL
|
|
81
|
+
- url: "https://example.com/font.woff2"
|
|
82
|
+
name: "CustomFont"
|
|
83
|
+
weight: 400
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## API
|
|
87
|
+
|
|
88
|
+
### `render(html, options)`
|
|
89
|
+
|
|
90
|
+
将 HTML 渲染为图片。
|
|
91
|
+
|
|
92
|
+
**参数:**
|
|
93
|
+
- `html: string` - HTML 代码
|
|
94
|
+
- `options.width?: number` - 宽度(默认 800)
|
|
95
|
+
- `options.height?: number` - 高度(自动计算)
|
|
96
|
+
- `options.format?: 'png' | 'svg'` - 输出格式(默认 png)
|
|
97
|
+
- `options.backgroundColor?: string` - 背景色(默认 #ffffff)
|
|
98
|
+
- `options.scale?: number` - 缩放比例(默认 1)
|
|
99
|
+
|
|
100
|
+
**返回:**
|
|
101
|
+
```typescript
|
|
102
|
+
interface RenderResult {
|
|
103
|
+
data: Buffer | string; // PNG Buffer 或 SVG 字符串
|
|
104
|
+
format: 'png' | 'svg';
|
|
105
|
+
width: number;
|
|
106
|
+
height: number;
|
|
107
|
+
mimeType: string;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `registerFont(font)`
|
|
112
|
+
|
|
113
|
+
注册自定义字体。
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
renderer.registerFont({
|
|
117
|
+
name: 'MyFont',
|
|
118
|
+
data: fontArrayBuffer,
|
|
119
|
+
weight: 400,
|
|
120
|
+
style: 'normal',
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 支持的 CSS 属性
|
|
125
|
+
|
|
126
|
+
参考 [@zhinjs/satori 文档](https://github.com/zhinjs/satori#支持的-css-属性)
|
|
127
|
+
|
|
128
|
+
主要支持:
|
|
129
|
+
- **布局**: `display: flex`, `position`
|
|
130
|
+
- **Flexbox**: `flex-direction`, `justify-content`, `align-items`, `gap`
|
|
131
|
+
- **尺寸**: `width`, `height`, `padding`, `margin`
|
|
132
|
+
- **边框**: `border`, `border-radius`
|
|
133
|
+
- **背景**: `background`, `background-image` (渐变)
|
|
134
|
+
- **文字**: `font-size`, `font-weight`, `color`, `text-align`
|
|
135
|
+
- **变换**: `transform`, `opacity`
|
|
136
|
+
|
|
137
|
+
## 许可证
|
|
138
|
+
|
|
139
|
+
MIT
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML 渲染器插件
|
|
3
|
+
*
|
|
4
|
+
* 使用 @zhinjs/satori 将 HTML/CSS 转换为 SVG,
|
|
5
|
+
* 使用 @resvg/resvg-js 将 SVG 转换为 PNG
|
|
6
|
+
*/
|
|
7
|
+
import type { HtmlRendererService, OutputFormat } from './types.js';
|
|
8
|
+
declare const rendererService: HtmlRendererService;
|
|
9
|
+
/**
|
|
10
|
+
* 渲染 HTML 为图片的组件
|
|
11
|
+
*/
|
|
12
|
+
declare const RenderImage: import("zhin.js").Component<{
|
|
13
|
+
children?: any;
|
|
14
|
+
html?: string;
|
|
15
|
+
width?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
format?: OutputFormat;
|
|
18
|
+
backgroundColor?: string;
|
|
19
|
+
scale?: number;
|
|
20
|
+
}>;
|
|
21
|
+
export type { HtmlRendererConfig, HtmlRendererService, RenderOptions, RenderResult, FontConfig, OutputFormat, } from './types.js';
|
|
22
|
+
export { rendererService, RenderImage };
|
|
23
|
+
declare module 'zhin.js' {
|
|
24
|
+
namespace Plugin {
|
|
25
|
+
interface Contexts {
|
|
26
|
+
'html-renderer': HtmlRendererService;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAEV,mBAAmB,EAInB,YAAY,EACb,MAAM,YAAY,CAAC;AA6cpB,QAAA,MAAM,eAAe,qBAA0C,CAAC;AAehE;;GAEG;AACH,QAAA,MAAM,WAAW;eACJ,GAAG;WACP,MAAM;YACL,MAAM;aACL,MAAM;aACN,YAAY;sBACH,MAAM;YAChB,MAAM;EA4BC,CAAC;AA+KlB,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,YAAY,GACb,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,CAAC;AAGxC,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,MAAM,CAAC;QACf,UAAU,QAAQ;YAChB,eAAe,EAAE,mBAAmB,CAAC;SACtC;KACF;CACF"}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { jsx as _jsx } from "zhin.js/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* HTML 渲染器插件
|
|
4
|
+
*
|
|
5
|
+
* 使用 @zhinjs/satori 将 HTML/CSS 转换为 SVG,
|
|
6
|
+
* 使用 @resvg/resvg-js 将 SVG 转换为 PNG
|
|
7
|
+
*/
|
|
8
|
+
import { usePlugin, defineComponent, ZhinTool } from "zhin.js";
|
|
9
|
+
import satori, { getDefaultFonts } from '@zhinjs/satori';
|
|
10
|
+
import { Resvg } from '@resvg/resvg-js';
|
|
11
|
+
import { JSDOM } from 'jsdom';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// 插件初始化
|
|
14
|
+
// ============================================================================
|
|
15
|
+
const plugin = usePlugin();
|
|
16
|
+
const { logger, provide, addComponent } = plugin;
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// 默认配置
|
|
19
|
+
// ============================================================================
|
|
20
|
+
const DEFAULT_CONFIG = {
|
|
21
|
+
defaultWidth: 800,
|
|
22
|
+
defaultFonts: [],
|
|
23
|
+
defaultBackgroundColor: '#ffffff',
|
|
24
|
+
cacheFonts: true,
|
|
25
|
+
fontUrls: [],
|
|
26
|
+
};
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// 字体管理
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// 字体缓存
|
|
31
|
+
const fontCache = new Map();
|
|
32
|
+
// 默认字体加载状态
|
|
33
|
+
let defaultFontLoaded = false;
|
|
34
|
+
/**
|
|
35
|
+
* 加载默认字体(使用 @zhinjs/satori 内置字体)
|
|
36
|
+
*/
|
|
37
|
+
async function loadDefaultFont() {
|
|
38
|
+
if (defaultFontLoaded) {
|
|
39
|
+
const cached = fontCache.get('default');
|
|
40
|
+
return cached || null;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// 使用 @zhinjs/satori 内置的字体
|
|
44
|
+
const builtinFonts = getDefaultFonts();
|
|
45
|
+
if (builtinFonts.length > 0) {
|
|
46
|
+
// 注册所有内置字体
|
|
47
|
+
for (const font of builtinFonts) {
|
|
48
|
+
const fontConfig = {
|
|
49
|
+
name: font.name,
|
|
50
|
+
data: font.data,
|
|
51
|
+
weight: font.weight,
|
|
52
|
+
style: font.style,
|
|
53
|
+
};
|
|
54
|
+
fontCache.set(`${font.name}-${font.weight}`, fontConfig);
|
|
55
|
+
logger.debug(`Builtin font registered: ${font.name} (${Math.round(font.data.byteLength / 1024)}KB)`);
|
|
56
|
+
}
|
|
57
|
+
// 设置第一个字体为默认
|
|
58
|
+
const defaultFont = builtinFonts[0];
|
|
59
|
+
const defaultFontConfig = {
|
|
60
|
+
name: defaultFont.name,
|
|
61
|
+
data: defaultFont.data,
|
|
62
|
+
weight: defaultFont.weight,
|
|
63
|
+
style: defaultFont.style,
|
|
64
|
+
};
|
|
65
|
+
fontCache.set('default', defaultFontConfig);
|
|
66
|
+
defaultFontLoaded = true;
|
|
67
|
+
logger.info(`Default fonts loaded from @zhinjs/satori: ${builtinFonts.map(f => f.name).join(', ')}`);
|
|
68
|
+
return defaultFontConfig;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
logger.warn('Failed to load builtin fonts:', error);
|
|
73
|
+
}
|
|
74
|
+
logger.warn('No fonts available');
|
|
75
|
+
defaultFontLoaded = true;
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 从 URL 加载字体
|
|
80
|
+
*/
|
|
81
|
+
async function loadFontFromUrl(url, name, weight = 400) {
|
|
82
|
+
try {
|
|
83
|
+
const cacheKey = `${name}-${weight}`;
|
|
84
|
+
if (fontCache.has(cacheKey)) {
|
|
85
|
+
return fontCache.get(cacheKey);
|
|
86
|
+
}
|
|
87
|
+
const response = await fetch(url);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`Failed to fetch font: ${response.status}`);
|
|
90
|
+
}
|
|
91
|
+
const fontData = await response.arrayBuffer();
|
|
92
|
+
const font = {
|
|
93
|
+
name,
|
|
94
|
+
data: fontData,
|
|
95
|
+
weight,
|
|
96
|
+
style: 'normal',
|
|
97
|
+
};
|
|
98
|
+
fontCache.set(cacheKey, font);
|
|
99
|
+
logger.debug(`Font loaded from URL: ${name}`);
|
|
100
|
+
return font;
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
logger.warn(`Failed to load font from ${url}:`, error);
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ============================================================================
|
|
108
|
+
// Emoji 支持
|
|
109
|
+
// ============================================================================
|
|
110
|
+
/**
|
|
111
|
+
* 将 emoji 代码点转换为 Twemoji URL
|
|
112
|
+
*/
|
|
113
|
+
function emojiToTwemojiUrl(emoji) {
|
|
114
|
+
// 获取 emoji 的代码点
|
|
115
|
+
const codePoints = [];
|
|
116
|
+
for (const char of emoji) {
|
|
117
|
+
const cp = char.codePointAt(0);
|
|
118
|
+
if (cp) {
|
|
119
|
+
// 跳过变体选择器 (FE0F)
|
|
120
|
+
if (cp !== 0xfe0f) {
|
|
121
|
+
codePoints.push(cp.toString(16));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const filename = codePoints.join('-');
|
|
126
|
+
// 使用 jsDelivr CDN 的 Twemoji
|
|
127
|
+
return `https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/svg/${filename}.svg`;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* 加载 emoji 图片
|
|
131
|
+
*/
|
|
132
|
+
async function loadEmojiImage(emoji) {
|
|
133
|
+
try {
|
|
134
|
+
const url = emojiToTwemojiUrl(emoji);
|
|
135
|
+
const response = await fetch(url);
|
|
136
|
+
if (!response.ok) {
|
|
137
|
+
logger.debug(`Failed to load emoji ${emoji}: ${response.status}`);
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
const svg = await response.text();
|
|
141
|
+
// 转换为 data URL
|
|
142
|
+
const base64 = Buffer.from(svg).toString('base64');
|
|
143
|
+
return `data:image/svg+xml;base64,${base64}`;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
logger.debug(`Failed to load emoji ${emoji}:`, error);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Emoji 缓存
|
|
151
|
+
const emojiCache = new Map();
|
|
152
|
+
/**
|
|
153
|
+
* 加载额外资源(字体和 emoji)
|
|
154
|
+
*/
|
|
155
|
+
async function loadAdditionalAsset(languageCode, segment) {
|
|
156
|
+
// 如果是 emoji
|
|
157
|
+
if (languageCode === 'emoji') {
|
|
158
|
+
// 检查缓存
|
|
159
|
+
if (emojiCache.has(segment)) {
|
|
160
|
+
return emojiCache.get(segment);
|
|
161
|
+
}
|
|
162
|
+
const result = await loadEmojiImage(segment);
|
|
163
|
+
if (result) {
|
|
164
|
+
emojiCache.set(segment, result);
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
// 其他语言代码暂不处理
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// 渲染引擎
|
|
173
|
+
// ============================================================================
|
|
174
|
+
/**
|
|
175
|
+
* 将 HTML 包装为完整的 HTML 文档
|
|
176
|
+
*/
|
|
177
|
+
function wrapHtml(html, backgroundColor = '#ffffff') {
|
|
178
|
+
// 如果已经是完整 HTML,直接返回
|
|
179
|
+
if (html.includes('<!DOCTYPE') || html.includes('<html')) {
|
|
180
|
+
return html;
|
|
181
|
+
}
|
|
182
|
+
return `<!DOCTYPE html>
|
|
183
|
+
<html>
|
|
184
|
+
<head>
|
|
185
|
+
<meta charset="UTF-8">
|
|
186
|
+
<style>
|
|
187
|
+
* {
|
|
188
|
+
margin: 0;
|
|
189
|
+
padding: 0;
|
|
190
|
+
box-sizing: border-box;
|
|
191
|
+
}
|
|
192
|
+
body {
|
|
193
|
+
display: flex;
|
|
194
|
+
flex-direction: column;
|
|
195
|
+
background-color: ${backgroundColor};
|
|
196
|
+
font-family: "Noto Sans SC", sans-serif;
|
|
197
|
+
}
|
|
198
|
+
</style>
|
|
199
|
+
</head>
|
|
200
|
+
<body>
|
|
201
|
+
${html}
|
|
202
|
+
</body>
|
|
203
|
+
</html>`;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 渲染 HTML 为 SVG(使用 @zhinjs/satori 的 JSDOM 方式)
|
|
207
|
+
*/
|
|
208
|
+
async function renderHtmlToSvg(html, width, height, fonts, backgroundColor) {
|
|
209
|
+
// 确保至少有一个字体
|
|
210
|
+
let finalFonts = fonts;
|
|
211
|
+
if (finalFonts.length === 0) {
|
|
212
|
+
const defaultFont = await loadDefaultFont();
|
|
213
|
+
if (defaultFont) {
|
|
214
|
+
finalFonts = [defaultFont];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (finalFonts.length === 0) {
|
|
218
|
+
logger.warn('No fonts available, rendering may fail for non-ASCII characters');
|
|
219
|
+
}
|
|
220
|
+
// 包装 HTML
|
|
221
|
+
const wrappedHtml = wrapHtml(html, backgroundColor);
|
|
222
|
+
// 使用 JSDOM 解析 HTML
|
|
223
|
+
const dom = new JSDOM(wrappedHtml);
|
|
224
|
+
// 使用 @zhinjs/satori 渲染
|
|
225
|
+
const satoriOptions = {
|
|
226
|
+
width,
|
|
227
|
+
fonts: finalFonts.map(f => ({
|
|
228
|
+
name: f.name,
|
|
229
|
+
data: f.data,
|
|
230
|
+
weight: f.weight,
|
|
231
|
+
style: f.style,
|
|
232
|
+
})),
|
|
233
|
+
// 支持 emoji 加载
|
|
234
|
+
loadAdditionalAsset,
|
|
235
|
+
};
|
|
236
|
+
if (height) {
|
|
237
|
+
satoriOptions.height = height;
|
|
238
|
+
}
|
|
239
|
+
const svg = await satori(dom, satoriOptions);
|
|
240
|
+
// 从 SVG 中解析实际尺寸
|
|
241
|
+
const widthMatch = svg.match(/width="(\d+)"/);
|
|
242
|
+
const heightMatch = svg.match(/height="(\d+)"/);
|
|
243
|
+
const actualWidth = widthMatch ? parseInt(widthMatch[1], 10) : width;
|
|
244
|
+
const actualHeight = heightMatch ? parseInt(heightMatch[1], 10) : height || width;
|
|
245
|
+
return { svg, width: actualWidth, height: actualHeight };
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* 将 SVG 转换为 PNG
|
|
249
|
+
*/
|
|
250
|
+
function svgToPng(svg, scale = 1) {
|
|
251
|
+
const resvg = new Resvg(svg, {
|
|
252
|
+
fitTo: scale !== 1 ? {
|
|
253
|
+
mode: 'zoom',
|
|
254
|
+
value: scale,
|
|
255
|
+
} : undefined,
|
|
256
|
+
});
|
|
257
|
+
const pngData = resvg.render();
|
|
258
|
+
return Buffer.from(pngData.asPng());
|
|
259
|
+
}
|
|
260
|
+
// ============================================================================
|
|
261
|
+
// 渲染服务
|
|
262
|
+
// ============================================================================
|
|
263
|
+
/**
|
|
264
|
+
* 创建渲染服务
|
|
265
|
+
*/
|
|
266
|
+
function createHtmlRendererService(config = {}) {
|
|
267
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
268
|
+
// 注册默认字体
|
|
269
|
+
for (const font of mergedConfig.defaultFonts) {
|
|
270
|
+
fontCache.set(`${font.name}-${font.weight || 400}`, font);
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
async render(html, options = {}) {
|
|
274
|
+
const { width = mergedConfig.defaultWidth, height, format = 'png', backgroundColor = mergedConfig.defaultBackgroundColor, fonts = [], scale = 1, } = options;
|
|
275
|
+
// 合并字体
|
|
276
|
+
const allFonts = [
|
|
277
|
+
...Array.from(fontCache.values()),
|
|
278
|
+
...fonts,
|
|
279
|
+
];
|
|
280
|
+
// 渲染为 SVG
|
|
281
|
+
const { svg, width: actualWidth, height: actualHeight } = await renderHtmlToSvg(html, width, height, allFonts, backgroundColor);
|
|
282
|
+
if (format === 'svg') {
|
|
283
|
+
return {
|
|
284
|
+
data: svg,
|
|
285
|
+
format: 'svg',
|
|
286
|
+
width: actualWidth,
|
|
287
|
+
height: actualHeight,
|
|
288
|
+
mimeType: 'image/svg+xml',
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// 转换为 PNG
|
|
292
|
+
const png = svgToPng(svg, scale);
|
|
293
|
+
return {
|
|
294
|
+
data: png,
|
|
295
|
+
format: 'png',
|
|
296
|
+
width: Math.round(actualWidth * scale),
|
|
297
|
+
height: Math.round(actualHeight * scale),
|
|
298
|
+
mimeType: 'image/png',
|
|
299
|
+
};
|
|
300
|
+
},
|
|
301
|
+
// renderJsx 保留用于向后兼容,但内部转换为 HTML
|
|
302
|
+
async renderJsx(element, options = {}) {
|
|
303
|
+
// 将 JSX 元素序列化为 HTML(简化实现)
|
|
304
|
+
const html = serializeJsxToHtml(element);
|
|
305
|
+
return this.render(html, options);
|
|
306
|
+
},
|
|
307
|
+
registerFont(font) {
|
|
308
|
+
const key = `${font.name}-${font.weight || 400}`;
|
|
309
|
+
fontCache.set(key, font);
|
|
310
|
+
logger.debug(`Font registered: ${font.name}`);
|
|
311
|
+
},
|
|
312
|
+
getFonts() {
|
|
313
|
+
return Array.from(fontCache.values());
|
|
314
|
+
},
|
|
315
|
+
clearFonts() {
|
|
316
|
+
fontCache.clear();
|
|
317
|
+
defaultFontLoaded = false;
|
|
318
|
+
logger.debug('Font cache cleared');
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* 简单的 JSX 到 HTML 序列化(用于向后兼容)
|
|
324
|
+
*/
|
|
325
|
+
function serializeJsxToHtml(element) {
|
|
326
|
+
if (typeof element === 'string' || typeof element === 'number') {
|
|
327
|
+
return String(element);
|
|
328
|
+
}
|
|
329
|
+
if (element === null || element === undefined) {
|
|
330
|
+
return '';
|
|
331
|
+
}
|
|
332
|
+
if (Array.isArray(element)) {
|
|
333
|
+
return element.map(serializeJsxToHtml).join('');
|
|
334
|
+
}
|
|
335
|
+
if (typeof element === 'object' && element.type) {
|
|
336
|
+
const { type, props = {} } = element;
|
|
337
|
+
const { children, style, ...restProps } = props;
|
|
338
|
+
// 处理样式
|
|
339
|
+
let styleStr = '';
|
|
340
|
+
if (style && typeof style === 'object') {
|
|
341
|
+
styleStr = Object.entries(style)
|
|
342
|
+
.map(([key, value]) => {
|
|
343
|
+
// 转换 camelCase 到 kebab-case
|
|
344
|
+
const cssKey = key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
345
|
+
return `${cssKey}: ${value}`;
|
|
346
|
+
})
|
|
347
|
+
.join('; ');
|
|
348
|
+
}
|
|
349
|
+
// 构建属性字符串
|
|
350
|
+
const attrs = Object.entries(restProps)
|
|
351
|
+
.filter(([key]) => key !== 'dangerouslySetInnerHTML')
|
|
352
|
+
.map(([key, value]) => `${key}="${value}"`)
|
|
353
|
+
.join(' ');
|
|
354
|
+
const styleAttr = styleStr ? ` style="${styleStr}"` : '';
|
|
355
|
+
const attrStr = attrs ? ` ${attrs}` : '';
|
|
356
|
+
// 处理 dangerouslySetInnerHTML
|
|
357
|
+
if (props.dangerouslySetInnerHTML && props.dangerouslySetInnerHTML.__html) {
|
|
358
|
+
return `<${type}${attrStr}${styleAttr}>${props.dangerouslySetInnerHTML.__html}</${type}>`;
|
|
359
|
+
}
|
|
360
|
+
// 处理子元素
|
|
361
|
+
const childrenHtml = children ? serializeJsxToHtml(children) : '';
|
|
362
|
+
// 自闭合标签
|
|
363
|
+
const selfClosingTags = ['img', 'br', 'hr', 'input', 'meta', 'link'];
|
|
364
|
+
if (selfClosingTags.includes(type) && !childrenHtml) {
|
|
365
|
+
return `<${type}${attrStr}${styleAttr} />`;
|
|
366
|
+
}
|
|
367
|
+
return `<${type}${attrStr}${styleAttr}>${childrenHtml}</${type}>`;
|
|
368
|
+
}
|
|
369
|
+
return '';
|
|
370
|
+
}
|
|
371
|
+
// ============================================================================
|
|
372
|
+
// 注册服务
|
|
373
|
+
// ============================================================================
|
|
374
|
+
// 获取配置
|
|
375
|
+
const configService = plugin.root.inject('config');
|
|
376
|
+
const appConfig = configService?.get('zhin.config.yml') || {};
|
|
377
|
+
const pluginConfig = appConfig.htmlRenderer || {};
|
|
378
|
+
// 创建服务实例
|
|
379
|
+
const rendererService = createHtmlRendererService(pluginConfig);
|
|
380
|
+
// 注册为 Context
|
|
381
|
+
provide({
|
|
382
|
+
name: 'html-renderer',
|
|
383
|
+
description: 'HTML to image rendering service using @zhinjs/satori',
|
|
384
|
+
value: rendererService,
|
|
385
|
+
});
|
|
386
|
+
logger.info('HTML Renderer service registered (using @zhinjs/satori)');
|
|
387
|
+
// ============================================================================
|
|
388
|
+
// JSX 组件
|
|
389
|
+
// ============================================================================
|
|
390
|
+
/**
|
|
391
|
+
* 渲染 HTML 为图片的组件
|
|
392
|
+
*/
|
|
393
|
+
const RenderImage = defineComponent(async function RenderImage(props) {
|
|
394
|
+
const { children, html, width, height, format = 'png', backgroundColor, scale } = props;
|
|
395
|
+
try {
|
|
396
|
+
let result;
|
|
397
|
+
if (html) {
|
|
398
|
+
result = await rendererService.render(html, { width, height, format, backgroundColor, scale });
|
|
399
|
+
}
|
|
400
|
+
else if (children) {
|
|
401
|
+
result = await rendererService.renderJsx(children, { width, height, format, backgroundColor, scale });
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
return '❌ 未提供渲染内容';
|
|
405
|
+
}
|
|
406
|
+
if (result.format === 'svg') {
|
|
407
|
+
return `[SVG 图片 ${result.width}x${result.height}]`;
|
|
408
|
+
}
|
|
409
|
+
// 转换为 base64 URL
|
|
410
|
+
const base64 = result.data.toString('base64');
|
|
411
|
+
const dataUrl = `data:${result.mimeType};base64,${base64}`;
|
|
412
|
+
return _jsx("image", { url: dataUrl });
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
logger.error('RenderImage error:', error);
|
|
416
|
+
return `❌ 渲染失败: ${error instanceof Error ? error.message : String(error)}`;
|
|
417
|
+
}
|
|
418
|
+
}, 'RenderImage');
|
|
419
|
+
addComponent(RenderImage);
|
|
420
|
+
// ============================================================================
|
|
421
|
+
// AI 工具
|
|
422
|
+
// ============================================================================
|
|
423
|
+
/**
|
|
424
|
+
* HTML 渲染工具 - 供 AI 使用
|
|
425
|
+
*/
|
|
426
|
+
const renderHtmlTool = new ZhinTool('html.render')
|
|
427
|
+
.desc('将 HTML/CSS 代码渲染为图片')
|
|
428
|
+
.tag('render', 'image', 'html')
|
|
429
|
+
.param('html', { type: 'string', description: 'HTML 代码(支持内联 CSS 样式)' }, true)
|
|
430
|
+
.param('width', { type: 'number', description: '图片宽度(像素,默认 800)' })
|
|
431
|
+
.param('height', { type: 'number', description: '图片高度(像素,自动计算)' })
|
|
432
|
+
.param('backgroundColor', { type: 'string', description: '背景颜色(默认 #ffffff)' })
|
|
433
|
+
.execute(async ({ html, width, height, backgroundColor }) => {
|
|
434
|
+
try {
|
|
435
|
+
const result = await rendererService.render(html, {
|
|
436
|
+
width: width,
|
|
437
|
+
height: height,
|
|
438
|
+
backgroundColor: backgroundColor,
|
|
439
|
+
format: 'png',
|
|
440
|
+
});
|
|
441
|
+
// 转换为 base64
|
|
442
|
+
const base64 = result.data.toString('base64');
|
|
443
|
+
return {
|
|
444
|
+
success: true,
|
|
445
|
+
width: result.width,
|
|
446
|
+
height: result.height,
|
|
447
|
+
format: result.format,
|
|
448
|
+
base64,
|
|
449
|
+
dataUrl: `data:${result.mimeType};base64,${base64}`,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
return {
|
|
454
|
+
success: false,
|
|
455
|
+
error: error instanceof Error ? error.message : String(error),
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
})
|
|
459
|
+
.action(async (message, result) => {
|
|
460
|
+
const html = result.params.html;
|
|
461
|
+
const width = result.params.width;
|
|
462
|
+
const height = result.params.height;
|
|
463
|
+
const backgroundColor = result.params.backgroundColor;
|
|
464
|
+
try {
|
|
465
|
+
const renderResult = await rendererService.render(html, {
|
|
466
|
+
width,
|
|
467
|
+
height,
|
|
468
|
+
backgroundColor,
|
|
469
|
+
format: 'png',
|
|
470
|
+
});
|
|
471
|
+
const base64 = renderResult.data.toString('base64');
|
|
472
|
+
const dataUrl = `data:${renderResult.mimeType};base64,${base64}`;
|
|
473
|
+
return _jsx("image", { url: dataUrl });
|
|
474
|
+
}
|
|
475
|
+
catch (error) {
|
|
476
|
+
return `❌ 渲染失败: ${error instanceof Error ? error.message : String(error)}`;
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
// 注册工具
|
|
480
|
+
const toolService = plugin.root.inject('tool');
|
|
481
|
+
if (toolService) {
|
|
482
|
+
toolService.add(renderHtmlTool, plugin.name);
|
|
483
|
+
logger.debug('HTML render tool registered');
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* 生成卡片图片工具 - 供 AI 使用
|
|
487
|
+
*/
|
|
488
|
+
const generateCardTool = new ZhinTool('html.card')
|
|
489
|
+
.desc('生成美观的卡片图片(自动生成 HTML)')
|
|
490
|
+
.tag('render', 'image', 'card')
|
|
491
|
+
.param('title', { type: 'string', description: '卡片标题' }, true)
|
|
492
|
+
.param('content', { type: 'string', description: '卡片内容(支持多行)' }, true)
|
|
493
|
+
.param('theme', {
|
|
494
|
+
type: 'string',
|
|
495
|
+
description: '主题颜色: blue, green, purple, orange, red(默认 blue)',
|
|
496
|
+
enum: ['blue', 'green', 'purple', 'orange', 'red']
|
|
497
|
+
})
|
|
498
|
+
.param('width', { type: 'number', description: '卡片宽度(像素,默认 400)' })
|
|
499
|
+
.execute(async ({ title, content, theme = 'blue', width = 400 }) => {
|
|
500
|
+
const themeColors = {
|
|
501
|
+
blue: { bg: '#f0f9ff', accent: '#3b82f6', text: '#1e3a5f' },
|
|
502
|
+
green: { bg: '#f0fdf4', accent: '#22c55e', text: '#14532d' },
|
|
503
|
+
purple: { bg: '#faf5ff', accent: '#a855f7', text: '#3b0764' },
|
|
504
|
+
orange: { bg: '#fff7ed', accent: '#f97316', text: '#7c2d12' },
|
|
505
|
+
red: { bg: '#fef2f2', accent: '#ef4444', text: '#7f1d1d' },
|
|
506
|
+
};
|
|
507
|
+
const colors = themeColors[theme] || themeColors.blue;
|
|
508
|
+
const html = `
|
|
509
|
+
<div style="
|
|
510
|
+
display: flex;
|
|
511
|
+
flex-direction: column;
|
|
512
|
+
padding: 24px;
|
|
513
|
+
background: linear-gradient(135deg, ${colors.bg} 0%, white 100%);
|
|
514
|
+
border-radius: 16px;
|
|
515
|
+
border-left: 4px solid ${colors.accent};
|
|
516
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
517
|
+
">
|
|
518
|
+
<div style="
|
|
519
|
+
font-size: 24px;
|
|
520
|
+
font-weight: bold;
|
|
521
|
+
color: ${colors.text};
|
|
522
|
+
margin-bottom: 12px;
|
|
523
|
+
">${title}</div>
|
|
524
|
+
<div style="
|
|
525
|
+
font-size: 16px;
|
|
526
|
+
color: #374151;
|
|
527
|
+
line-height: 1.6;
|
|
528
|
+
white-space: pre-wrap;
|
|
529
|
+
">${content}</div>
|
|
530
|
+
</div>`;
|
|
531
|
+
try {
|
|
532
|
+
const result = await rendererService.render(html, {
|
|
533
|
+
width: width,
|
|
534
|
+
format: 'png',
|
|
535
|
+
});
|
|
536
|
+
const base64 = result.data.toString('base64');
|
|
537
|
+
return {
|
|
538
|
+
success: true,
|
|
539
|
+
width: result.width,
|
|
540
|
+
height: result.height,
|
|
541
|
+
base64,
|
|
542
|
+
dataUrl: `data:${result.mimeType};base64,${base64}`,
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
return {
|
|
547
|
+
success: false,
|
|
548
|
+
error: error instanceof Error ? error.message : String(error),
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
.action(async (message, result) => {
|
|
553
|
+
const { title, content, theme, width } = result.params;
|
|
554
|
+
// 执行 execute 获取结果
|
|
555
|
+
const executeResult = await generateCardTool.toTool().execute({ title, content, theme, width }, { platform: message.$adapter, senderId: message.$sender.id });
|
|
556
|
+
if (!executeResult.success) {
|
|
557
|
+
return `❌ 生成失败: ${executeResult.error}`;
|
|
558
|
+
}
|
|
559
|
+
return _jsx("image", { url: executeResult.dataUrl });
|
|
560
|
+
});
|
|
561
|
+
// 注册卡片工具
|
|
562
|
+
if (toolService) {
|
|
563
|
+
toolService.add(generateCardTool, plugin.name);
|
|
564
|
+
logger.debug('HTML card tool registered');
|
|
565
|
+
}
|
|
566
|
+
export { rendererService, RenderImage };
|
|
567
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":"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"}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTML 渲染器类型定义
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* 输出格式
|
|
6
|
+
*/
|
|
7
|
+
export type OutputFormat = 'svg' | 'png';
|
|
8
|
+
/**
|
|
9
|
+
* 字体配置
|
|
10
|
+
*/
|
|
11
|
+
export interface FontConfig {
|
|
12
|
+
/** 字体名称 */
|
|
13
|
+
name: string;
|
|
14
|
+
/** 字体数据 (ArrayBuffer) */
|
|
15
|
+
data: ArrayBuffer;
|
|
16
|
+
/** 字体粗细 */
|
|
17
|
+
weight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
|
|
18
|
+
/** 字体样式 */
|
|
19
|
+
style?: 'normal' | 'italic';
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 渲染选项
|
|
23
|
+
*/
|
|
24
|
+
export interface RenderOptions {
|
|
25
|
+
/** 输出宽度(像素) */
|
|
26
|
+
width?: number;
|
|
27
|
+
/** 输出高度(像素,如果不指定则自动计算) */
|
|
28
|
+
height?: number;
|
|
29
|
+
/** 输出格式 */
|
|
30
|
+
format?: OutputFormat;
|
|
31
|
+
/** 背景颜色 */
|
|
32
|
+
backgroundColor?: string;
|
|
33
|
+
/** 自定义字体 */
|
|
34
|
+
fonts?: FontConfig[];
|
|
35
|
+
/** 是否启用表情符号渲染 */
|
|
36
|
+
enableEmoji?: boolean;
|
|
37
|
+
/** 缩放比例(用于高清渲染) */
|
|
38
|
+
scale?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 渲染结果
|
|
42
|
+
*/
|
|
43
|
+
export interface RenderResult {
|
|
44
|
+
/** 输出数据 */
|
|
45
|
+
data: Buffer | string;
|
|
46
|
+
/** 输出格式 */
|
|
47
|
+
format: OutputFormat;
|
|
48
|
+
/** 实际宽度 */
|
|
49
|
+
width: number;
|
|
50
|
+
/** 实际高度 */
|
|
51
|
+
height: number;
|
|
52
|
+
/** MIME 类型 */
|
|
53
|
+
mimeType: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 插件配置
|
|
57
|
+
*/
|
|
58
|
+
export interface HtmlRendererConfig {
|
|
59
|
+
/** 默认宽度 */
|
|
60
|
+
defaultWidth?: number;
|
|
61
|
+
/** 默认字体 */
|
|
62
|
+
defaultFonts?: FontConfig[];
|
|
63
|
+
/** 默认背景颜色 */
|
|
64
|
+
defaultBackgroundColor?: string;
|
|
65
|
+
/** 是否缓存字体 */
|
|
66
|
+
cacheFonts?: boolean;
|
|
67
|
+
/** 字体 URL(用于自动加载) */
|
|
68
|
+
fontUrls?: string[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 渲染服务接口
|
|
72
|
+
*/
|
|
73
|
+
export interface HtmlRendererService {
|
|
74
|
+
/** 渲染 HTML 字符串为图片 */
|
|
75
|
+
render(html: string, options?: RenderOptions): Promise<RenderResult>;
|
|
76
|
+
/** 渲染 JSX 元素为图片 */
|
|
77
|
+
renderJsx(element: JSX.Element, options?: RenderOptions): Promise<RenderResult>;
|
|
78
|
+
/** 注册字体 */
|
|
79
|
+
registerFont(font: FontConfig): void;
|
|
80
|
+
/** 获取已注册的字体 */
|
|
81
|
+
getFonts(): FontConfig[];
|
|
82
|
+
/** 清空字体缓存 */
|
|
83
|
+
clearFonts(): void;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 模板渲染上下文
|
|
87
|
+
*/
|
|
88
|
+
export interface TemplateContext {
|
|
89
|
+
/** 模板数据 */
|
|
90
|
+
data: Record<string, any>;
|
|
91
|
+
/** 渲染选项 */
|
|
92
|
+
options?: RenderOptions;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,KAAK,CAAC;AAEzC;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW;IACX,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC7D,WAAW;IACX,KAAK,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,eAAe;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0BAA0B;IAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW;IACX,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,WAAW;IACX,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY;IACZ,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,iBAAiB;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,WAAW;IACX,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,WAAW;IACX,MAAM,EAAE,YAAY,CAAC;IACrB,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW;IACX,MAAM,EAAE,MAAM,CAAC;IACf,cAAc;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW;IACX,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW;IACX,YAAY,CAAC,EAAE,UAAU,EAAE,CAAC;IAC5B,aAAa;IACb,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,aAAa;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,qBAAqB;IACrB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAErE,mBAAmB;IACnB,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAEhF,WAAW;IACX,YAAY,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAErC,eAAe;IACf,QAAQ,IAAI,UAAU,EAAE,CAAC;IAEzB,aAAa;IACb,UAAU,IAAI,IAAI,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW;IACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,WAAW;IACX,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB"}
|
package/lib/types.js
ADDED
package/lib/types.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zhin.js/plugin-html-renderer",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "HTML to image rendering plugin for Zhin.js using @zhinjs/satori",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./lib/index.js",
|
|
7
|
+
"types": "./lib/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/index.d.ts",
|
|
11
|
+
"import": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"lib",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc --build",
|
|
21
|
+
"clean": "rm -rf lib"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"zhin",
|
|
25
|
+
"bot",
|
|
26
|
+
"plugin",
|
|
27
|
+
"html",
|
|
28
|
+
"image",
|
|
29
|
+
"render",
|
|
30
|
+
"satori",
|
|
31
|
+
"svg",
|
|
32
|
+
"png"
|
|
33
|
+
],
|
|
34
|
+
"author": {
|
|
35
|
+
"name": "lc-cn",
|
|
36
|
+
"email": "admin@liucl.cn",
|
|
37
|
+
"url": "https://github.com/lc-cn"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@zhinjs/satori": "workspace:*",
|
|
42
|
+
"@resvg/resvg-js": "^2.6.2",
|
|
43
|
+
"jsdom": "^24.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/jsdom": "^21.1.6",
|
|
47
|
+
"typescript": "^5.3.3",
|
|
48
|
+
"zhin.js": "workspace:*"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"zhin.js": "workspace:*"
|
|
52
|
+
},
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/zhinjs/zhin.git",
|
|
56
|
+
"directory": "plugins/utils/html-renderer"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public",
|
|
60
|
+
"registry": "https://registry.npmjs.org"
|
|
61
|
+
}
|
|
62
|
+
}
|