@codehz/draw-call 0.1.2 → 0.2.0

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.
@@ -0,0 +1,284 @@
1
+ /**
2
+ * 示例:使用 draw-call 的 RichText 组件
3
+ * 运行: bun examples/richtext.ts
4
+ */
5
+ import { Box, RichText, Text } from "@codehz/draw-call";
6
+ import { createNodeCanvas } from "@codehz/draw-call/node";
7
+ import { GlobalFonts } from "@napi-rs/canvas";
8
+ import { fileURLToPath } from "bun";
9
+
10
+ GlobalFonts.registerFromPath(fileURLToPath(import.meta.resolve("@fontpkg/unifont/unifont-15.0.01.ttf")), "unifont");
11
+
12
+ const canvas = createNodeCanvas({
13
+ width: 400,
14
+ height: 820,
15
+ pixelRatio: 2,
16
+ });
17
+
18
+ // 绘制背景
19
+ canvas.render(
20
+ Box({
21
+ width: "fill",
22
+ height: "fill",
23
+ background: "#f8f9fa",
24
+ padding: 20,
25
+ direction: "column",
26
+ gap: 20,
27
+ children: [
28
+ // 标题
29
+ Text({
30
+ content: "RichText 富文本演示",
31
+ font: { size: 20, weight: "bold", family: "unifont" },
32
+ color: "#333",
33
+ }),
34
+
35
+ // 基本用法
36
+ Box({
37
+ background: "#ffffff",
38
+ border: { radius: 8 },
39
+ padding: 12,
40
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
41
+ direction: "column",
42
+ gap: 8,
43
+ children: [
44
+ Text({
45
+ content: "基本用法 - 多样式文本",
46
+ font: { size: 12, family: "unifont" },
47
+ color: "#666",
48
+ }),
49
+ RichText({
50
+ spans: [
51
+ { text: "Hello, ", color: "#333", font: { size: 14, family: "unifont" } },
52
+ { text: "World", color: "#ff6b6b", font: { size: 18, weight: "bold", family: "unifont" } },
53
+ { text: "!", color: "#333", font: { size: 14, family: "unifont" } },
54
+ ],
55
+ }),
56
+ ],
57
+ }),
58
+
59
+ // 渐变色文本
60
+ Box({
61
+ background: "#ffffff",
62
+ border: { radius: 8 },
63
+ padding: 12,
64
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
65
+ direction: "column",
66
+ gap: 8,
67
+ children: [
68
+ Text({
69
+ content: "带背景色的文本",
70
+ font: { size: 12, family: "unifont" },
71
+ color: "#666",
72
+ }),
73
+ RichText({
74
+ spans: [
75
+ { text: "红色背景", background: "#ffe8e8", color: "#f5222d" },
76
+ { text: " " },
77
+ { text: "绿色背景", background: "#f6ffed", color: "#52c41a" },
78
+ { text: " " },
79
+ { text: "蓝色背景", background: "#e8f4ff", color: "#1890ff" },
80
+ ],
81
+ }),
82
+ ],
83
+ }),
84
+
85
+ // 下划线和删除线
86
+ Box({
87
+ background: "#ffffff",
88
+ border: { radius: 8 },
89
+ padding: 12,
90
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
91
+ direction: "column",
92
+ gap: 8,
93
+ children: [
94
+ Text({
95
+ content: "文本装饰 - 下划线和删除线",
96
+ font: { size: 12, family: "unifont" },
97
+ color: "#666",
98
+ }),
99
+ RichText({
100
+ spans: [
101
+ { text: "下划线文本", underline: true, color: "#333", font: { size: 14, family: "unifont" } },
102
+ { text: " " },
103
+ { text: "删除线文本", strikethrough: true, color: "#333", font: { size: 14, family: "unifont" } },
104
+ { text: " " },
105
+ {
106
+ text: "组合效果",
107
+ underline: true,
108
+ strikethrough: true,
109
+ color: "#f5222d",
110
+ font: { size: 14, family: "unifont" },
111
+ },
112
+ ],
113
+ }),
114
+ ],
115
+ }),
116
+
117
+ // 自动换行
118
+ Box({
119
+ background: "#ffffff",
120
+ border: { radius: 8 },
121
+ padding: 12,
122
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
123
+ direction: "column",
124
+ gap: 8,
125
+ children: [
126
+ Text({
127
+ content: "自动换行 - 长文本",
128
+ font: { size: 12, family: "unifont" },
129
+ color: "#666",
130
+ }),
131
+ RichText({
132
+ spans: [
133
+ {
134
+ text: "这是一个很长很长的富文本段落,它会自动换行以适应容器的宽度。",
135
+ color: "#333",
136
+ font: { size: 13, family: "unifont" },
137
+ },
138
+ ],
139
+ }),
140
+ ],
141
+ }),
142
+
143
+ // 混合样式换行
144
+ Box({
145
+ background: "#ffffff",
146
+ border: { radius: 8 },
147
+ padding: 12,
148
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
149
+ direction: "column",
150
+ gap: 8,
151
+ children: [
152
+ Text({
153
+ content: "混合样式自动换行",
154
+ font: { size: 12, family: "unifont" },
155
+ color: "#666",
156
+ }),
157
+ RichText({
158
+ spans: [
159
+ { text: "draw-call ", color: "#333", font: { size: 13, family: "unifont" } },
160
+ { text: "是一个", color: "#666", font: { size: 13, family: "unifont" } },
161
+ { text: "声明式", color: "#1890ff", font: { size: 13, weight: "bold", family: "unifont" } },
162
+ { text: " Canvas 绘图库,支持", color: "#333", font: { size: 13, family: "unifont" } },
163
+ { text: "Flexbox 布局", color: "#52c41a", font: { size: 13, family: "unifont" }, underline: true },
164
+ { text: "和", color: "#333", font: { size: 13, family: "unifont" } },
165
+ { text: "富文本渲染", color: "#f5222d", font: { size: 13, weight: "bold", family: "unifont" } },
166
+ { text: "。", color: "#333", font: { size: 13, family: "unifont" } },
167
+ ],
168
+ }),
169
+ ],
170
+ }),
171
+
172
+ // 对齐方式
173
+ Box({
174
+ background: "#ffffff",
175
+ border: { radius: 8 },
176
+ padding: 12,
177
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
178
+ direction: "column",
179
+ gap: 8,
180
+ children: [
181
+ Text({
182
+ content: "文本对齐方式",
183
+ font: { size: 12, family: "unifont" },
184
+ color: "#666",
185
+ }),
186
+ Box({
187
+ width: "fill",
188
+ background: "#f5f5f5",
189
+ border: { radius: 4 },
190
+ padding: 8,
191
+ direction: "column",
192
+ gap: 4,
193
+ children: [
194
+ Text({
195
+ content: "左对齐:",
196
+ font: { size: 11, family: "unifont" },
197
+ color: "#999",
198
+ }),
199
+ RichText({
200
+ align: "left",
201
+ spans: [{ text: "这是一段左对齐的富文本", color: "#333", font: { size: 12, family: "unifont" } }],
202
+ }),
203
+ ],
204
+ }),
205
+ Box({
206
+ width: "fill",
207
+ background: "#f5f5f5",
208
+ border: { radius: 4 },
209
+ padding: 8,
210
+ direction: "column",
211
+ gap: 4,
212
+ children: [
213
+ Text({
214
+ content: "居中对齐:",
215
+ font: { size: 11, family: "unifont" },
216
+ color: "#999",
217
+ }),
218
+ RichText({
219
+ align: "center",
220
+ width: "fill",
221
+ spans: [{ text: "这是一段居中对齐的富文本", color: "#333", font: { size: 12, family: "unifont" } }],
222
+ }),
223
+ ],
224
+ }),
225
+ Box({
226
+ width: "fill",
227
+ background: "#f5f5f5",
228
+ border: { radius: 4 },
229
+ padding: 8,
230
+ direction: "column",
231
+ gap: 4,
232
+ children: [
233
+ Text({
234
+ content: "右对齐:",
235
+ font: { size: 11, family: "unifont" },
236
+ color: "#999",
237
+ }),
238
+ RichText({
239
+ align: "right",
240
+ width: "fill",
241
+ spans: [{ text: "这是一段右对齐的富文本", color: "#333", font: { size: 12, family: "unifont" } }],
242
+ }),
243
+ ],
244
+ }),
245
+ ],
246
+ }),
247
+
248
+ // 彩色标签云
249
+ Box({
250
+ background: "#ffffff",
251
+ border: { radius: 8 },
252
+ padding: 12,
253
+ shadow: { offsetY: 2, blur: 8, color: "rgba(0,0,0,0.06)" },
254
+ direction: "column",
255
+ gap: 8,
256
+ children: [
257
+ Text({
258
+ content: "彩色标签展示",
259
+ font: { size: 12, family: "unifont" },
260
+ color: "#666",
261
+ }),
262
+ RichText({
263
+ spans: [
264
+ { text: "TypeScript", background: "#e8f4ff", color: "#1890ff", font: { size: 12, family: "unifont" } },
265
+ { text: " " },
266
+ { text: "Canvas", background: "#f6ffed", color: "#52c41a", font: { size: 12, family: "unifont" } },
267
+ { text: " " },
268
+ { text: "Flexbox", background: "#fff7e6", color: "#fa8c16", font: { size: 12, family: "unifont" } },
269
+ { text: " " },
270
+ { text: "声明式", background: "#ffe8e8", color: "#f5222d", font: { size: 12, family: "unifont" } },
271
+ { text: " " },
272
+ { text: "组件化", background: "#f0f0ff", color: "#2f54eb", font: { size: 12, family: "unifont" } },
273
+ ],
274
+ }),
275
+ ],
276
+ }),
277
+ ],
278
+ })
279
+ );
280
+
281
+ // 保存到文件
282
+ const buffer = await canvas.toBuffer("image/png");
283
+ await Bun.write("examples/richtext.png", buffer);
284
+ console.log("RichText demo saved to examples/richtext.png");
package/index.cjs CHANGED
@@ -70,6 +70,15 @@ function Image(props) {
70
70
  };
71
71
  }
72
72
 
73
+ //#endregion
74
+ //#region src/components/RichText.ts
75
+ function RichText(props) {
76
+ return {
77
+ type: "richtext",
78
+ ...props
79
+ };
80
+ }
81
+
73
82
  //#endregion
74
83
  //#region src/components/Stack.ts
75
84
  function Stack(props) {
@@ -135,15 +144,76 @@ function Text(props) {
135
144
  };
136
145
  }
137
146
 
147
+ //#endregion
148
+ //#region src/layout/utils/print.ts
149
+ /**
150
+ * 获取元素类型的显示名称
151
+ */
152
+ function getElementType(element) {
153
+ switch (element.type) {
154
+ case "box": return "Box";
155
+ case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
156
+ case "stack": return "Stack";
157
+ case "image": return "Image";
158
+ case "svg": return "Svg";
159
+ default: return element.type;
160
+ }
161
+ }
162
+ /**
163
+ * 递归打印布局树
164
+ */
165
+ function printLayoutToString(node, prefix = "", isLast = true, depth = 0) {
166
+ const lines = [];
167
+ const connector = isLast ? "└─ " : "├─ ";
168
+ const type = getElementType(node.element);
169
+ const { x, y, width, height } = node.layout;
170
+ const childCount = node.children.length;
171
+ lines.push(`${prefix}${connector}${type} @(${Math.round(x)},${Math.round(y)}) size:${Math.round(width)}x${Math.round(height)}`);
172
+ if (node.element.type === "text" && node.lines) {
173
+ const contentPrefix = prefix + (isLast ? " " : "│ ");
174
+ for (let i = 0; i < node.lines.length; i++) {
175
+ const lineText = node.lines[i];
176
+ const isLastLine = i === node.lines.length - 1 && childCount === 0;
177
+ lines.push(`${contentPrefix}${isLastLine ? "└─ " : "├─ "}${JSON.stringify(lineText)}`);
178
+ }
179
+ }
180
+ for (let i = 0; i < node.children.length; i++) {
181
+ const child = node.children[i];
182
+ const isChildLast = i === node.children.length - 1;
183
+ const childPrefix = prefix + (isLast ? " " : "│ ");
184
+ lines.push(...printLayoutToString(child, childPrefix, isChildLast, depth + 1));
185
+ }
186
+ return lines;
187
+ }
188
+ /**
189
+ * 打印 LayoutNode 树结构到控制台
190
+ */
191
+ function printLayout(node) {
192
+ const lines = printLayoutToString(node, "", true);
193
+ console.log(lines.join("\n"));
194
+ }
195
+ /**
196
+ * 将 LayoutNode 转换为美观的字符串
197
+ * @param node LayoutNode 根节点
198
+ * @param indent 缩进字符串,默认为两个空格
199
+ * @returns 格式化的字符串
200
+ */
201
+ function layoutToString(node, _indent = " ") {
202
+ return printLayoutToString(node, "", true).join("\n");
203
+ }
204
+
138
205
  //#endregion
139
206
  exports.Box = Box;
140
207
  exports.Image = Image;
208
+ exports.RichText = RichText;
141
209
  exports.Stack = Stack;
142
210
  exports.Svg = Svg;
143
211
  exports.Text = Text;
144
212
  exports.computeLayout = require_render.computeLayout;
145
213
  exports.createCanvas = createCanvas;
146
214
  exports.createCanvasMeasureContext = require_render.createCanvasMeasureContext;
215
+ exports.layoutToString = layoutToString;
147
216
  exports.linearGradient = require_render.linearGradient;
217
+ exports.printLayout = printLayout;
148
218
  exports.radialGradient = require_render.radialGradient;
149
219
  exports.svg = svg;
package/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AlignItems, B as Color, C as SvgProps, D as SvgTransformProps, E as SvgTextChild, F as LayoutConstraints, G as RadialGradientDescriptor, H as FontProps, I as LayoutNode, J as Spacing, K as Shadow, L as LayoutProps, M as ContainerLayoutProps, N as FlexDirection, O as TextElement, P as JustifyContent, R as Border, S as SvgPolylineChild, T as SvgStyleProps, U as GradientDescriptor, V as ColorStop, W as LinearGradientDescriptor, X as linearGradient, Y as StrokeProps, Z as radialGradient, _ as SvgEllipseChild, a as BoxElement, b as SvgPathChild, c as ImageElement, d as StackElement, f as StackProps, g as SvgElement, h as SvgCircleChild, i as createCanvas, j as AlignSelf, k as TextProps, l as ImageProps, m as SvgChild, n as DrawCallCanvas, o as BoxProps, p as SvgAlign, q as Size, r as LayoutSize, s as Element, t as CanvasOptions, u as StackAlign, v as SvgGroupChild, w as SvgRectChild, x as SvgPolygonChild, y as SvgLineChild, z as Bounds } from "./canvas.cjs";
1
+ import { $ as linearGradient, A as SvgTransformProps, B as LayoutProps, C as SvgPathChild, D as SvgRectChild, E as SvgProps, F as ContainerLayoutProps, G as FontProps, H as Bounds, I as FlexDirection, J as RadialGradientDescriptor, K as GradientDescriptor, L as JustifyContent, M as TextProps, N as AlignItems, O as SvgStyleProps, P as AlignSelf, Q as StrokeProps, R as LayoutConstraints, S as SvgLineChild, T as SvgPolylineChild, U as Color, V as Border, W as ColorStop, X as Size, Y as Shadow, Z as Spacing, _ as SvgChild, a as BoxElement, b as SvgEllipseChild, c as ImageElement, d as RichTextProps, et as radialGradient, f as RichTextSpan, g as SvgAlign, h as StackProps, i as createCanvas, j as TextElement, k as SvgTextChild, l as ImageProps, m as StackElement, n as DrawCallCanvas, o as BoxProps, p as StackAlign, q as LinearGradientDescriptor, r as LayoutSize, s as Element, t as CanvasOptions, u as RichTextElement, v as SvgCircleChild, w as SvgPolygonChild, x as SvgGroupChild, y as SvgElement, z as LayoutNode } from "./canvas.cjs";
2
2
 
3
3
  //#region src/components/Box.d.ts
4
4
  declare function Box(props: BoxProps): BoxElement;
@@ -6,6 +6,9 @@ declare function Box(props: BoxProps): BoxElement;
6
6
  //#region src/components/Image.d.ts
7
7
  declare function Image(props: ImageProps): ImageElement;
8
8
  //#endregion
9
+ //#region src/components/RichText.d.ts
10
+ declare function RichText(props: RichTextProps): RichTextElement;
11
+ //#endregion
9
12
  //#region src/components/Stack.d.ts
10
13
  declare function Stack(props: StackProps): StackElement;
11
14
  //#endregion
@@ -27,16 +30,32 @@ declare const svg: {
27
30
  declare function Text(props: TextProps): TextElement;
28
31
  //#endregion
29
32
  //#region src/layout/utils/measure.d.ts
33
+ interface MeasureTextResult {
34
+ width: number;
35
+ height: number;
36
+ offset: number;
37
+ ascent: number;
38
+ descent: number;
39
+ }
30
40
  interface MeasureContext {
31
- measureText(text: string, font: FontProps): {
32
- width: number;
33
- height: number;
34
- offset: number;
35
- };
41
+ measureText(text: string, font: FontProps): MeasureTextResult;
36
42
  }
37
43
  declare function createCanvasMeasureContext(ctx: CanvasRenderingContext2D): MeasureContext;
38
44
  //#endregion
39
45
  //#region src/layout/engine.d.ts
40
46
  declare function computeLayout(element: Element, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
41
47
  //#endregion
42
- export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LayoutSize, type LinearGradientDescriptor, type MeasureContext, type RadialGradientDescriptor, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, computeLayout, createCanvas, createCanvasMeasureContext, linearGradient, radialGradient, svg };
48
+ //#region src/layout/utils/print.d.ts
49
+ /**
50
+ * 打印 LayoutNode 树结构到控制台
51
+ */
52
+ declare function printLayout(node: LayoutNode): void;
53
+ /**
54
+ * 将 LayoutNode 转换为美观的字符串
55
+ * @param node LayoutNode 根节点
56
+ * @param indent 缩进字符串,默认为两个空格
57
+ * @returns 格式化的字符串
58
+ */
59
+ declare function layoutToString(node: LayoutNode, _indent?: string): string;
60
+ //#endregion
61
+ export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LayoutSize, type LinearGradientDescriptor, type MeasureContext, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
package/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { A as AlignItems, B as Color, C as SvgProps, D as SvgTransformProps, E as SvgTextChild, F as LayoutConstraints, G as RadialGradientDescriptor, H as FontProps, I as LayoutNode, J as Spacing, K as Shadow, L as LayoutProps, M as ContainerLayoutProps, N as FlexDirection, O as TextElement, P as JustifyContent, R as Border, S as SvgPolylineChild, T as SvgStyleProps, U as GradientDescriptor, V as ColorStop, W as LinearGradientDescriptor, X as linearGradient, Y as StrokeProps, Z as radialGradient, _ as SvgEllipseChild, a as BoxElement, b as SvgPathChild, c as ImageElement, d as StackElement, f as StackProps, g as SvgElement, h as SvgCircleChild, i as createCanvas, j as AlignSelf, k as TextProps, l as ImageProps, m as SvgChild, n as DrawCallCanvas, o as BoxProps, p as SvgAlign, q as Size, r as LayoutSize, s as Element, t as CanvasOptions, u as StackAlign, v as SvgGroupChild, w as SvgRectChild, x as SvgPolygonChild, y as SvgLineChild, z as Bounds } from "./canvas.mjs";
1
+ import { $ as linearGradient, A as SvgTransformProps, B as LayoutProps, C as SvgPathChild, D as SvgRectChild, E as SvgProps, F as ContainerLayoutProps, G as FontProps, H as Bounds, I as FlexDirection, J as RadialGradientDescriptor, K as GradientDescriptor, L as JustifyContent, M as TextProps, N as AlignItems, O as SvgStyleProps, P as AlignSelf, Q as StrokeProps, R as LayoutConstraints, S as SvgLineChild, T as SvgPolylineChild, U as Color, V as Border, W as ColorStop, X as Size, Y as Shadow, Z as Spacing, _ as SvgChild, a as BoxElement, b as SvgEllipseChild, c as ImageElement, d as RichTextProps, et as radialGradient, f as RichTextSpan, g as SvgAlign, h as StackProps, i as createCanvas, j as TextElement, k as SvgTextChild, l as ImageProps, m as StackElement, n as DrawCallCanvas, o as BoxProps, p as StackAlign, q as LinearGradientDescriptor, r as LayoutSize, s as Element, t as CanvasOptions, u as RichTextElement, v as SvgCircleChild, w as SvgPolygonChild, x as SvgGroupChild, y as SvgElement, z as LayoutNode } from "./canvas.mjs";
2
2
 
3
3
  //#region src/components/Box.d.ts
4
4
  declare function Box(props: BoxProps): BoxElement;
@@ -6,6 +6,9 @@ declare function Box(props: BoxProps): BoxElement;
6
6
  //#region src/components/Image.d.ts
7
7
  declare function Image(props: ImageProps): ImageElement;
8
8
  //#endregion
9
+ //#region src/components/RichText.d.ts
10
+ declare function RichText(props: RichTextProps): RichTextElement;
11
+ //#endregion
9
12
  //#region src/components/Stack.d.ts
10
13
  declare function Stack(props: StackProps): StackElement;
11
14
  //#endregion
@@ -27,16 +30,32 @@ declare const svg: {
27
30
  declare function Text(props: TextProps): TextElement;
28
31
  //#endregion
29
32
  //#region src/layout/utils/measure.d.ts
33
+ interface MeasureTextResult {
34
+ width: number;
35
+ height: number;
36
+ offset: number;
37
+ ascent: number;
38
+ descent: number;
39
+ }
30
40
  interface MeasureContext {
31
- measureText(text: string, font: FontProps): {
32
- width: number;
33
- height: number;
34
- offset: number;
35
- };
41
+ measureText(text: string, font: FontProps): MeasureTextResult;
36
42
  }
37
43
  declare function createCanvasMeasureContext(ctx: CanvasRenderingContext2D): MeasureContext;
38
44
  //#endregion
39
45
  //#region src/layout/engine.d.ts
40
46
  declare function computeLayout(element: Element, ctx: MeasureContext, constraints: LayoutConstraints, x?: number, y?: number): LayoutNode;
41
47
  //#endregion
42
- export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LayoutSize, type LinearGradientDescriptor, type MeasureContext, type RadialGradientDescriptor, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, computeLayout, createCanvas, createCanvasMeasureContext, linearGradient, radialGradient, svg };
48
+ //#region src/layout/utils/print.d.ts
49
+ /**
50
+ * 打印 LayoutNode 树结构到控制台
51
+ */
52
+ declare function printLayout(node: LayoutNode): void;
53
+ /**
54
+ * 将 LayoutNode 转换为美观的字符串
55
+ * @param node LayoutNode 根节点
56
+ * @param indent 缩进字符串,默认为两个空格
57
+ * @returns 格式化的字符串
58
+ */
59
+ declare function layoutToString(node: LayoutNode, _indent?: string): string;
60
+ //#endregion
61
+ export { type AlignItems, type AlignSelf, type Border, type Bounds, Box, type BoxElement, type BoxProps, type CanvasOptions, type Color, type ColorStop, type ContainerLayoutProps, type DrawCallCanvas, type Element, type FlexDirection, type FontProps, type GradientDescriptor, Image, type JustifyContent, type LayoutNode, type LayoutProps, type LayoutSize, type LinearGradientDescriptor, type MeasureContext, type RadialGradientDescriptor, RichText, type RichTextElement, type RichTextProps, type RichTextSpan, type Shadow, type Size, type Spacing, Stack, type StackAlign, type StackElement, type StackProps, type StrokeProps, Svg, type SvgAlign, type SvgChild, type SvgCircleChild, type SvgElement, type SvgEllipseChild, type SvgGroupChild, type SvgLineChild, type SvgPathChild, type SvgPolygonChild, type SvgPolylineChild, type SvgProps, type SvgRectChild, type SvgStyleProps, type SvgTextChild, type SvgTransformProps, Text, type TextElement, type TextProps, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
package/index.mjs CHANGED
@@ -70,6 +70,15 @@ function Image(props) {
70
70
  };
71
71
  }
72
72
 
73
+ //#endregion
74
+ //#region src/components/RichText.ts
75
+ function RichText(props) {
76
+ return {
77
+ type: "richtext",
78
+ ...props
79
+ };
80
+ }
81
+
73
82
  //#endregion
74
83
  //#region src/components/Stack.ts
75
84
  function Stack(props) {
@@ -136,4 +145,62 @@ function Text(props) {
136
145
  }
137
146
 
138
147
  //#endregion
139
- export { Box, Image, Stack, Svg, Text, computeLayout, createCanvas, createCanvasMeasureContext, linearGradient, radialGradient, svg };
148
+ //#region src/layout/utils/print.ts
149
+ /**
150
+ * 获取元素类型的显示名称
151
+ */
152
+ function getElementType(element) {
153
+ switch (element.type) {
154
+ case "box": return "Box";
155
+ case "text": return `Text "${element.content.slice(0, 20)}${element.content.length > 20 ? "..." : ""}"`;
156
+ case "stack": return "Stack";
157
+ case "image": return "Image";
158
+ case "svg": return "Svg";
159
+ default: return element.type;
160
+ }
161
+ }
162
+ /**
163
+ * 递归打印布局树
164
+ */
165
+ function printLayoutToString(node, prefix = "", isLast = true, depth = 0) {
166
+ const lines = [];
167
+ const connector = isLast ? "└─ " : "├─ ";
168
+ const type = getElementType(node.element);
169
+ const { x, y, width, height } = node.layout;
170
+ const childCount = node.children.length;
171
+ lines.push(`${prefix}${connector}${type} @(${Math.round(x)},${Math.round(y)}) size:${Math.round(width)}x${Math.round(height)}`);
172
+ if (node.element.type === "text" && node.lines) {
173
+ const contentPrefix = prefix + (isLast ? " " : "│ ");
174
+ for (let i = 0; i < node.lines.length; i++) {
175
+ const lineText = node.lines[i];
176
+ const isLastLine = i === node.lines.length - 1 && childCount === 0;
177
+ lines.push(`${contentPrefix}${isLastLine ? "└─ " : "├─ "}${JSON.stringify(lineText)}`);
178
+ }
179
+ }
180
+ for (let i = 0; i < node.children.length; i++) {
181
+ const child = node.children[i];
182
+ const isChildLast = i === node.children.length - 1;
183
+ const childPrefix = prefix + (isLast ? " " : "│ ");
184
+ lines.push(...printLayoutToString(child, childPrefix, isChildLast, depth + 1));
185
+ }
186
+ return lines;
187
+ }
188
+ /**
189
+ * 打印 LayoutNode 树结构到控制台
190
+ */
191
+ function printLayout(node) {
192
+ const lines = printLayoutToString(node, "", true);
193
+ console.log(lines.join("\n"));
194
+ }
195
+ /**
196
+ * 将 LayoutNode 转换为美观的字符串
197
+ * @param node LayoutNode 根节点
198
+ * @param indent 缩进字符串,默认为两个空格
199
+ * @returns 格式化的字符串
200
+ */
201
+ function layoutToString(node, _indent = " ") {
202
+ return printLayoutToString(node, "", true).join("\n");
203
+ }
204
+
205
+ //#endregion
206
+ export { Box, Image, RichText, Stack, Svg, Text, computeLayout, createCanvas, createCanvasMeasureContext, layoutToString, linearGradient, printLayout, radialGradient, svg };
package/node.cjs CHANGED
@@ -8,7 +8,7 @@ let _napi_rs_canvas = require("@napi-rs/canvas");
8
8
  * 此函数需要 @napi-rs/canvas 作为依赖
9
9
  * 安装: bun add @napi-rs/canvas
10
10
  */
11
- function createCanvas(options) {
11
+ function createNodeCanvas(options) {
12
12
  const { width, height, pixelRatio = 1 } = options;
13
13
  const canvas = (0, _napi_rs_canvas.createCanvas)(width * pixelRatio, height * pixelRatio);
14
14
  const ctx = canvas.getContext("2d");
@@ -45,4 +45,4 @@ function createCanvas(options) {
45
45
  }
46
46
 
47
47
  //#endregion
48
- exports.createCanvas = createCanvas;
48
+ exports.createNodeCanvas = createNodeCanvas;
package/node.d.cts CHANGED
@@ -7,6 +7,6 @@ import { n as DrawCallCanvas, t as CanvasOptions } from "./canvas.cjs";
7
7
  * 此函数需要 @napi-rs/canvas 作为依赖
8
8
  * 安装: bun add @napi-rs/canvas
9
9
  */
10
- declare function createCanvas(options: Omit<CanvasOptions, "canvas">): DrawCallCanvas;
10
+ declare function createNodeCanvas(options: Omit<CanvasOptions, "canvas">): DrawCallCanvas;
11
11
  //#endregion
12
- export { createCanvas };
12
+ export { createNodeCanvas };
package/node.d.mts CHANGED
@@ -7,6 +7,6 @@ import { n as DrawCallCanvas, t as CanvasOptions } from "./canvas.mjs";
7
7
  * 此函数需要 @napi-rs/canvas 作为依赖
8
8
  * 安装: bun add @napi-rs/canvas
9
9
  */
10
- declare function createCanvas(options: Omit<CanvasOptions, "canvas">): DrawCallCanvas;
10
+ declare function createNodeCanvas(options: Omit<CanvasOptions, "canvas">): DrawCallCanvas;
11
11
  //#endregion
12
- export { createCanvas };
12
+ export { createNodeCanvas };
package/node.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as computeLayout, r as createCanvasMeasureContext, t as renderNode } from "./render.mjs";
2
- import { createCanvas as createCanvas$1 } from "@napi-rs/canvas";
2
+ import { createCanvas } from "@napi-rs/canvas";
3
3
 
4
4
  //#region src/node.ts
5
5
  /**
@@ -8,9 +8,9 @@ import { createCanvas as createCanvas$1 } from "@napi-rs/canvas";
8
8
  * 此函数需要 @napi-rs/canvas 作为依赖
9
9
  * 安装: bun add @napi-rs/canvas
10
10
  */
11
- function createCanvas(options) {
11
+ function createNodeCanvas(options) {
12
12
  const { width, height, pixelRatio = 1 } = options;
13
- const canvas = createCanvas$1(width * pixelRatio, height * pixelRatio);
13
+ const canvas = createCanvas(width * pixelRatio, height * pixelRatio);
14
14
  const ctx = canvas.getContext("2d");
15
15
  if (pixelRatio !== 1) ctx.scale(pixelRatio, pixelRatio);
16
16
  const measureCtx = createCanvasMeasureContext(ctx);
@@ -45,4 +45,4 @@ function createCanvas(options) {
45
45
  }
46
46
 
47
47
  //#endregion
48
- export { createCanvas };
48
+ export { createNodeCanvas };
package/package.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "optionalDependencies": {
26
26
  "@napi-rs/canvas": "^0.1.88"
27
27
  },
28
- "version": "0.1.2",
28
+ "version": "0.2.0",
29
29
  "main": "./index.cjs",
30
30
  "types": "./index.d.cts",
31
31
  "exports": {