locusing 0.1.2

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,305 @@
1
+ import {
2
+ init_render_svg,
3
+ toSVGString
4
+ } from "./chunk-3XW6GBD6.mjs";
5
+ import {
6
+ __objRest,
7
+ __spreadProps,
8
+ __spreadValues
9
+ } from "./chunk-TQQRYA5H.mjs";
10
+
11
+ // src/export/playwright-exporter.ts
12
+ init_render_svg();
13
+
14
+ // src/export/html-template.ts
15
+ var KATEX_CSS_URL = "https://cdn.jsdelivr.net/npm/katex@0.16.27/dist/katex.min.css";
16
+ function generateExportHTML(svgContent, options) {
17
+ const {
18
+ width = 1920,
19
+ height = 1080,
20
+ background = "transparent"
21
+ } = options;
22
+ return `<!DOCTYPE html>
23
+ <html>
24
+ <head>
25
+ <meta charset="UTF-8">
26
+ <meta name="viewport" content="width=${width}, height=${height}">
27
+ <link rel="stylesheet" href="${KATEX_CSS_URL}" crossorigin="anonymous">
28
+ <style>
29
+ * {
30
+ margin: 0;
31
+ padding: 0;
32
+ box-sizing: border-box;
33
+ }
34
+ html, body {
35
+ width: ${width}px;
36
+ height: ${height}px;
37
+ overflow: hidden;
38
+ }
39
+ body {
40
+ background: ${background};
41
+ display: flex;
42
+ align-items: center;
43
+ justify-content: center;
44
+ }
45
+ svg {
46
+ width: 100%;
47
+ height: 100%;
48
+ display: block;
49
+ }
50
+ /* KaTeX \u5728 SVG foreignObject \u4E2D\u7684\u6837\u5F0F\u4FEE\u6B63 */
51
+ .katex {
52
+ font-size: inherit;
53
+ line-height: 1;
54
+ }
55
+ .katex-html {
56
+ white-space: nowrap;
57
+ }
58
+ </style>
59
+ </head>
60
+ <body>
61
+ ${svgContent}
62
+ </body>
63
+ </html>`;
64
+ }
65
+ function generateExportHTMLWithInlineCSS(katexCSS, svgContent, options) {
66
+ const {
67
+ width = 1920,
68
+ height = 1080,
69
+ background = "transparent"
70
+ } = options;
71
+ return `<!DOCTYPE html>
72
+ <html>
73
+ <head>
74
+ <meta charset="UTF-8">
75
+ <meta name="viewport" content="width=${width}, height=${height}">
76
+ <style>
77
+ ${katexCSS}
78
+ </style>
79
+ <style>
80
+ * {
81
+ margin: 0;
82
+ padding: 0;
83
+ box-sizing: border-box;
84
+ }
85
+ html, body {
86
+ width: ${width}px;
87
+ height: ${height}px;
88
+ overflow: hidden;
89
+ }
90
+ body {
91
+ background: ${background};
92
+ display: flex;
93
+ align-items: center;
94
+ justify-content: center;
95
+ }
96
+ svg {
97
+ width: 100%;
98
+ height: 100%;
99
+ display: block;
100
+ }
101
+ .katex {
102
+ font-size: inherit;
103
+ line-height: 1;
104
+ }
105
+ .katex-html {
106
+ white-space: nowrap;
107
+ }
108
+ </style>
109
+ </head>
110
+ <body>
111
+ ${svgContent}
112
+ </body>
113
+ </html>`;
114
+ }
115
+
116
+ // src/export/playwright-exporter.ts
117
+ var PlaywrightExporter = class {
118
+ constructor(options = {}) {
119
+ this.browser = null;
120
+ this.page = null;
121
+ var _a, _b;
122
+ this.options = {
123
+ headless: (_a = options.headless) != null ? _a : true,
124
+ browser: (_b = options.browser) != null ? _b : "chromium"
125
+ };
126
+ }
127
+ /**
128
+ * 启动浏览器
129
+ *
130
+ * @throws 如果 Playwright 未安装
131
+ */
132
+ async launch() {
133
+ if (this.browser) {
134
+ return;
135
+ }
136
+ let playwright;
137
+ try {
138
+ playwright = await import("playwright");
139
+ } catch (e) {
140
+ throw new Error(
141
+ "Playwright is not installed. Please install it with: pnpm add -D playwright"
142
+ );
143
+ }
144
+ const browserType = playwright[this.options.browser];
145
+ if (!browserType) {
146
+ throw new Error(`Unknown browser type: ${this.options.browser}`);
147
+ }
148
+ this.browser = await browserType.launch({
149
+ headless: this.options.headless
150
+ });
151
+ this.page = await this.browser.newPage();
152
+ }
153
+ /**
154
+ * 关闭浏览器
155
+ */
156
+ async close() {
157
+ if (this.page) {
158
+ await this.page.close();
159
+ this.page = null;
160
+ }
161
+ if (this.browser) {
162
+ await this.browser.close();
163
+ this.browser = null;
164
+ }
165
+ }
166
+ /**
167
+ * 获取页面实例,确保已启动
168
+ */
169
+ getPage() {
170
+ if (!this.browser || !this.page) {
171
+ throw new Error("Browser not launched. Call launch() first.");
172
+ }
173
+ return this.page;
174
+ }
175
+ /**
176
+ * 导出 Diagram 为图片
177
+ *
178
+ * @param diagram - 要导出的 Diagram
179
+ * @param options - 导出选项
180
+ * @returns 导出结果
181
+ */
182
+ async exportDiagram(diagram, options = {}) {
183
+ const {
184
+ width = 1920,
185
+ height = 1080,
186
+ frame = true
187
+ } = options;
188
+ const svgString = toSVGString(diagram, {
189
+ width,
190
+ height,
191
+ frame,
192
+ background: "transparent"
193
+ // HTML 背景会处理
194
+ });
195
+ return this.exportSVGString(svgString, options);
196
+ }
197
+ /**
198
+ * 导出 Scene 为图片(静态帧)
199
+ *
200
+ * @param SceneClass - Scene 类
201
+ * @param options - 导出选项
202
+ * @returns 导出结果
203
+ */
204
+ async exportScene(SceneClass, options = {}) {
205
+ const _a = options, { frameTime: _frameTime = 0 } = _a, exportOptions = __objRest(_a, ["frameTime"]);
206
+ const scene = new SceneClass();
207
+ if (typeof scene.construct === "function") {
208
+ if (typeof scene.toSVGString === "function") {
209
+ const svgString = scene.toSVGString();
210
+ return this.exportSVGString(svgString, exportOptions);
211
+ }
212
+ }
213
+ throw new Error(
214
+ "Scene export requires the Scene to have a toSVGString() method. For animated scenes, use browser-based export."
215
+ );
216
+ }
217
+ /**
218
+ * 导出 SVG 字符串为图片
219
+ *
220
+ * @param svgString - SVG 字符串
221
+ * @param options - 导出选项
222
+ * @returns 导出结果
223
+ */
224
+ async exportSVGString(svgString, options = {}) {
225
+ const page = this.getPage();
226
+ const {
227
+ format = "png",
228
+ quality = 90,
229
+ deviceScaleFactor = 2,
230
+ width = 1920,
231
+ height = 1080,
232
+ background = "transparent",
233
+ waitForFonts = true,
234
+ renderDelay = 100
235
+ } = options;
236
+ await page.setViewportSize({
237
+ width,
238
+ height
239
+ });
240
+ await page.emulateMedia({ colorScheme: "light" });
241
+ const html = generateExportHTML(svgString, { width, height, background });
242
+ await page.setContent(html, {
243
+ waitUntil: "networkidle"
244
+ });
245
+ if (waitForFonts) {
246
+ await page.waitForFunction(
247
+ () => document.fonts.ready.then(() => true),
248
+ { timeout: 1e4 }
249
+ ).catch(() => {
250
+ console.warn("Font loading timeout, continuing with available fonts");
251
+ });
252
+ }
253
+ if (renderDelay > 0) {
254
+ await page.waitForTimeout(renderDelay);
255
+ }
256
+ const buffer = await page.screenshot({
257
+ type: format,
258
+ quality: format !== "png" ? quality : void 0,
259
+ fullPage: false,
260
+ omitBackground: background === "transparent",
261
+ scale: "device"
262
+ });
263
+ const actualWidth = width * deviceScaleFactor;
264
+ const actualHeight = height * deviceScaleFactor;
265
+ return {
266
+ buffer: Buffer.from(buffer),
267
+ mimeType: `image/${format}`,
268
+ width: actualWidth,
269
+ height: actualHeight
270
+ };
271
+ }
272
+ };
273
+ async function exportToPNG(diagram, outputPath, options = {}) {
274
+ const fs = await import("fs/promises");
275
+ const exporter = new PlaywrightExporter();
276
+ try {
277
+ await exporter.launch();
278
+ const result = await exporter.exportDiagram(diagram, __spreadProps(__spreadValues({}, options), {
279
+ format: "png"
280
+ }));
281
+ await fs.writeFile(outputPath, result.buffer);
282
+ } finally {
283
+ await exporter.close();
284
+ }
285
+ }
286
+ async function exportSVGToPNG(svgString, outputPath, options = {}) {
287
+ const fs = await import("fs/promises");
288
+ const exporter = new PlaywrightExporter();
289
+ try {
290
+ await exporter.launch();
291
+ const result = await exporter.exportSVGString(svgString, __spreadProps(__spreadValues({}, options), {
292
+ format: "png"
293
+ }));
294
+ await fs.writeFile(outputPath, result.buffer);
295
+ } finally {
296
+ await exporter.close();
297
+ }
298
+ }
299
+ export {
300
+ PlaywrightExporter,
301
+ exportSVGToPNG,
302
+ exportToPNG,
303
+ generateExportHTML,
304
+ generateExportHTMLWithInlineCSS
305
+ };