@printine/designer 0.1.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.
Files changed (199) hide show
  1. package/README.md +146 -0
  2. package/dist/DesignerRuntimeContext.d.ts +3 -0
  3. package/dist/PrintineDesigner.d.ts +9 -0
  4. package/dist/api/client.d.ts +8 -0
  5. package/dist/api/client.js +82 -0
  6. package/dist/api/common-endpoints.d.ts +12 -0
  7. package/dist/api/design-endpoints.d.ts +18 -0
  8. package/dist/api/endpoints.d.ts +33 -0
  9. package/dist/api/endpoints.js +46 -0
  10. package/dist/api/files-endpoints.d.ts +1 -0
  11. package/dist/assets/icons/aspect_ratio.d.ts +3 -0
  12. package/dist/assets/icons/delete.d.ts +3 -0
  13. package/dist/assets/icons/fit_screen.d.ts +3 -0
  14. package/dist/assets/icons/index.d.ts +11 -0
  15. package/dist/assets/icons/loading_1.d.ts +3 -0
  16. package/dist/assets/icons/loading_2.d.ts +3 -0
  17. package/dist/assets/icons/loading_3.d.ts +3 -0
  18. package/dist/assets/icons/pan.d.ts +3 -0
  19. package/dist/assets/icons/setting.d.ts +3 -0
  20. package/dist/assets/icons/text_color.d.ts +3 -0
  21. package/dist/assets/icons/text_h.d.ts +3 -0
  22. package/dist/assets/icons/text_v.d.ts +3 -0
  23. package/dist/bundled-assets.d.ts +4 -0
  24. package/dist/components/add-design.d.ts +8 -0
  25. package/dist/components/assets-dialog.d.ts +21 -0
  26. package/dist/components/canvas-context-menu.d.ts +6 -0
  27. package/dist/components/color-picker/index.d.ts +10 -0
  28. package/dist/components/context.d.ts +24 -0
  29. package/dist/components/design.d.ts +9 -0
  30. package/dist/components/fonts.d.ts +26 -0
  31. package/dist/components/image-crop.d.ts +4 -0
  32. package/dist/components/image-edit.d.ts +1 -0
  33. package/dist/components/layout/aside.d.ts +3 -0
  34. package/dist/components/layout/bottom-nav/element-menu-sheet.d.ts +11 -0
  35. package/dist/components/layout/bottom-nav/element-sheet.d.ts +11 -0
  36. package/dist/components/layout/bottom-nav/index.d.ts +1 -0
  37. package/dist/components/layout/header.d.ts +5 -0
  38. package/dist/components/loading.d.ts +3 -0
  39. package/dist/components/menus/ai.d.ts +5 -0
  40. package/dist/components/menus/background.d.ts +6 -0
  41. package/dist/components/menus/clipart.d.ts +6 -0
  42. package/dist/components/menus/files.d.ts +7 -0
  43. package/dist/components/menus/image.d.ts +8 -0
  44. package/dist/components/menus/index.d.ts +13 -0
  45. package/dist/components/menus/layer.d.ts +5 -0
  46. package/dist/components/menus/product.d.ts +3 -0
  47. package/dist/components/menus/sca.d.ts +5 -0
  48. package/dist/components/menus/shapes.d.ts +6 -0
  49. package/dist/components/menus/templates.d.ts +6 -0
  50. package/dist/components/menus/text-effects.d.ts +5 -0
  51. package/dist/components/menus/text.d.ts +6 -0
  52. package/dist/components/placement-tab.d.ts +7 -0
  53. package/dist/components/preview/3d.d.ts +7 -0
  54. package/dist/components/preview/graphic.d.ts +7 -0
  55. package/dist/components/preview/index.d.ts +18 -0
  56. package/dist/components/preview/open.d.ts +7 -0
  57. package/dist/components/preview/real-time/3d.d.ts +12 -0
  58. package/dist/components/preview/real-time/graphic.d.ts +18 -0
  59. package/dist/components/preview/real-time/index.d.ts +11 -0
  60. package/dist/components/scale-tools.d.ts +7 -0
  61. package/dist/components/semi-deisgn-config.d.ts +13 -0
  62. package/dist/components/semi-design-link.d.ts +9 -0
  63. package/dist/components/store.d.ts +1 -0
  64. package/dist/components/toolbar.d.ts +2 -0
  65. package/dist/config-D2qm60gf.js +16 -0
  66. package/dist/contracts/api-design.d.ts +176 -0
  67. package/dist/contracts/design.d.ts +86 -0
  68. package/dist/design-7zRpRtv9.js +20331 -0
  69. package/dist/engine/canvas-designer.d.ts +101 -0
  70. package/dist/engine/canvas-designer.js +313 -0
  71. package/dist/engine/constants.d.ts +27 -0
  72. package/dist/engine/constants.js +148 -0
  73. package/dist/engine/editor.d.ts +186 -0
  74. package/dist/engine/editor.js +734 -0
  75. package/dist/engine/events.d.ts +34 -0
  76. package/dist/engine/events.js +7 -0
  77. package/dist/engine/extensions/curved-text.d.ts +59 -0
  78. package/dist/engine/fabric.d.ts +4 -0
  79. package/dist/engine/fabric.js +187 -0
  80. package/dist/engine/history.d.ts +27 -0
  81. package/dist/engine/history.js +56 -0
  82. package/dist/engine/managers/event.d.ts +101 -0
  83. package/dist/engine/managers/event.js +410 -0
  84. package/dist/engine/managers/resize.d.ts +24 -0
  85. package/dist/engine/managers/resize.js +63 -0
  86. package/dist/engine/managers/snapline.d.ts +70 -0
  87. package/dist/engine/managers/snapline.js +124 -0
  88. package/dist/engine/managers/workspace.d.ts +26 -0
  89. package/dist/engine/managers/workspace.js +53 -0
  90. package/dist/engine/processors/image.d.ts +88 -0
  91. package/dist/engine/processors/image.js +473 -0
  92. package/dist/engine/processors/template.d.ts +45 -0
  93. package/dist/engine/processors/template.js +314 -0
  94. package/dist/engine/processors/text.d.ts +74 -0
  95. package/dist/engine/processors/text.js +215 -0
  96. package/dist/engine/semi-export-editor.d.ts +199 -0
  97. package/dist/engine/types.d.ts +28 -0
  98. package/dist/engine/utils/common.d.ts +25 -0
  99. package/dist/engine/utils/common.js +68 -0
  100. package/dist/engine/utils/dialog.d.ts +25 -0
  101. package/dist/engine/utils/font.d.ts +5 -0
  102. package/dist/engine/utils/font.js +39 -0
  103. package/dist/engine/utils/image.d.ts +8 -0
  104. package/dist/engine/utils/image.js +50 -0
  105. package/dist/engine/utils/loading.d.ts +17 -0
  106. package/dist/engine/utils/loading.js +71 -0
  107. package/dist/engine/utils/measure.d.ts +6 -0
  108. package/dist/engine/utils/measure.js +6 -0
  109. package/dist/engine/utils/mockup.d.ts +59 -0
  110. package/dist/engine/utils/setting.d.ts +4 -0
  111. package/dist/engine/utils/setting.js +73 -0
  112. package/dist/fabric-types.d.ts +59 -0
  113. package/dist/full-designer.js +14 -0
  114. package/dist/hooks/pagination.d.ts +19 -0
  115. package/dist/hooks/use-adobe-express.d.ts +50 -0
  116. package/dist/hooks/use-before-unload.d.ts +1 -0
  117. package/dist/hooks/use-image-drop.d.ts +18 -0
  118. package/dist/hooks/use-oss-upload.d.ts +34 -0
  119. package/dist/hooks/use-screen.d.ts +1 -0
  120. package/dist/i18n/index.d.ts +12 -0
  121. package/dist/images/design/asset_empty.png +0 -0
  122. package/dist/images/design/design/background.svg +3 -0
  123. package/dist/images/design/design/color-picker.svg +15 -0
  124. package/dist/images/design/design/computer.svg +1 -0
  125. package/dist/images/design/design/copy.svg +1 -0
  126. package/dist/images/design/design/image.svg +10 -0
  127. package/dist/images/design/design/line.svg +1 -0
  128. package/dist/images/design/design/text.svg +1 -0
  129. package/dist/images/design/design/title.svg +1 -0
  130. package/dist/images/design/empty.png +0 -0
  131. package/dist/images/design/layer_empty.png +0 -0
  132. package/dist/images/design/menu/ai.svg +1 -0
  133. package/dist/images/design/menu/back.svg +4 -0
  134. package/dist/images/design/menu/background.svg +6 -0
  135. package/dist/images/design/menu/clipart.svg +6 -0
  136. package/dist/images/design/menu/file.svg +4 -0
  137. package/dist/images/design/menu/fill.svg +1 -0
  138. package/dist/images/design/menu/image.svg +7 -0
  139. package/dist/images/design/menu/layer.svg +6 -0
  140. package/dist/images/design/menu/product.svg +3 -0
  141. package/dist/images/design/menu/sca.svg +5 -0
  142. package/dist/images/design/menu/shapes.svg +5 -0
  143. package/dist/images/design/menu/templates.svg +6 -0
  144. package/dist/images/design/menu/text.svg +6 -0
  145. package/dist/images/design/menu/uploads.svg +7 -0
  146. package/dist/images/design/spinner.svg +3 -0
  147. package/dist/images/design/spinners.svg +17 -0
  148. package/dist/images/design/transparent_pattern.svg +5 -0
  149. package/dist/index.d.ts +39 -0
  150. package/dist/index.js +127 -0
  151. package/dist/lib/analytics.d.ts +17 -0
  152. package/dist/lib/cn.d.ts +3 -0
  153. package/dist/lib/common.d.ts +16 -0
  154. package/dist/lib/config.d.ts +14 -0
  155. package/dist/lib/navigation.d.ts +2 -0
  156. package/dist/renderer/ThreeDPreview.d.ts +14 -0
  157. package/dist/renderer/sticker.js +1731 -0
  158. package/dist/renderer/three-d-preview.js +32 -0
  159. package/dist/state/designer-store.d.ts +43 -0
  160. package/dist/state/designer-store.js +85 -0
  161. package/dist/style.css +1 -0
  162. package/dist/types.d.ts +184 -0
  163. package/dist/ui/accordion.d.ts +7 -0
  164. package/dist/ui/button-group.d.ts +11 -0
  165. package/dist/ui/button.d.ts +10 -0
  166. package/dist/ui/checkbox.d.ts +4 -0
  167. package/dist/ui/command.d.ts +18 -0
  168. package/dist/ui/confirm-dialog.d.ts +15 -0
  169. package/dist/ui/context-menu.d.ts +25 -0
  170. package/dist/ui/dialog.d.ts +15 -0
  171. package/dist/ui/drawer.d.ts +15 -0
  172. package/dist/ui/editable-combobox.d.ts +10 -0
  173. package/dist/ui/hint.d.ts +8 -0
  174. package/dist/ui/image.d.ts +24 -0
  175. package/dist/ui/input-group.d.ts +16 -0
  176. package/dist/ui/input-with-addon.d.ts +17 -0
  177. package/dist/ui/input.d.ts +3 -0
  178. package/dist/ui/label.d.ts +4 -0
  179. package/dist/ui/loading-button.d.ts +15 -0
  180. package/dist/ui/popover.d.ts +9 -0
  181. package/dist/ui/scroll-area.d.ts +5 -0
  182. package/dist/ui/select.d.ts +22 -0
  183. package/dist/ui/separator.d.ts +4 -0
  184. package/dist/ui/shared-loading-button.d.ts +20 -0
  185. package/dist/ui/simple-dialog.d.ts +19 -0
  186. package/dist/ui/simple-popover.d.ts +13 -0
  187. package/dist/ui/simple-select.d.ts +20 -0
  188. package/dist/ui/skeleton.d.ts +2 -0
  189. package/dist/ui/slider.d.ts +4 -0
  190. package/dist/ui/spinner-box.d.ts +12 -0
  191. package/dist/ui/spinner.d.ts +2 -0
  192. package/dist/ui/switch.d.ts +6 -0
  193. package/dist/ui/tab/index.d.ts +15 -0
  194. package/dist/ui/textarea.d.ts +3 -0
  195. package/dist/ui/tooltip.d.ts +7 -0
  196. package/dist/workers/image-resize.worker.d.ts +1 -0
  197. package/dist/workers/mockup.worker.d.ts +5 -0
  198. package/dist/workers/render.worker.d.ts +1 -0
  199. package/package.json +201 -0
@@ -0,0 +1,199 @@
1
+ import type { Size } from '../contracts/design';
2
+ import fabric from './fabric';
3
+ import ImageProcessor from './processors/image';
4
+ import TemplateProcessor from './processors/template';
5
+ import TextProcessor from './processors/text';
6
+ import { ResizeManager } from './managers/resize';
7
+ import type { Mockup } from '../contracts/api-design';
8
+ import { WorkspaceManager } from './managers/workspace';
9
+ export default class Editor {
10
+ canvas: fabric.Canvas;
11
+ imageProcessor: ImageProcessor;
12
+ textProcessor: TextProcessor;
13
+ templateProcessor: TemplateProcessor;
14
+ resizeManager: ResizeManager;
15
+ workspaceManager: WorkspaceManager;
16
+ private overlayBorderHandler?;
17
+ private imageCache;
18
+ private overlayCache;
19
+ private overlayScale;
20
+ constructor(container: HTMLCanvasElement, size: Size, target: HTMLDivElement, overlayScale?: number);
21
+ /**
22
+ * 统一拦截图片创建:优先从会话缓存复用已加载的 HTMLImageElement,避免视图切换或导出时重复请求远程图片
23
+ */
24
+ private setupImageCacheInterceptor;
25
+ /**
26
+ * 渲染 overlayImage(SVG)
27
+ * @param mockup 模板信息
28
+ * @param changeView 是否切换视图,如果为true,则需要重新设置画布和背景图
29
+
30
+ */
31
+ renderOverlayImage(mockup: Mockup, changeView?: boolean): Promise<void>;
32
+ /**
33
+ * 获取 overlay 图:优先使用会话级缓存,避免重复网络请求
34
+ * 会返回深拷贝,确保不同视图间互不影响
35
+ */
36
+ private getOverlayImage;
37
+ /**
38
+ * 绘制可设计区域边框,优先使用 clipPath 的真实形状
39
+ */
40
+ private setupOverlayBorders;
41
+ /**
42
+ * 渲染背景图(Bitmap)png、webp 等位图
43
+ * @param mockup 模板信息
44
+ * @param color 背景颜色
45
+ * @param changeView 是否切换视图,如果为true,则需要重新设置画布和背景图
46
+ */
47
+ /**
48
+ * 设置背景遮罩
49
+ */
50
+ setBackgroundMask(maskImageUrl: string): Promise<void>;
51
+ /**
52
+ * 设置背景颜色、透明图案
53
+ */
54
+ setBackground(color: string): Promise<void>;
55
+ /**
56
+ * 获取当前选中的对象
57
+ */
58
+ get activeObject(): fabric.Object | null;
59
+ /**
60
+ * 获取对象经过画布转换后的实际尺寸
61
+ */
62
+ getObjectActualSize(obj: fabric.Object): {
63
+ left: number;
64
+ top: number;
65
+ width: number;
66
+ height: number;
67
+ centerX: number;
68
+ centerY: number;
69
+ };
70
+ /**
71
+ * 添加对象
72
+ *
73
+ */
74
+ addObject(obj: fabric.Object, active?: boolean): void;
75
+ /**
76
+ * 恢复对象
77
+ */
78
+ restoreObject(obj: any): Promise<fabric.Object[]>;
79
+ /**
80
+ * 半定制文本更新:如果存在 originalWidth / originalHeight,则按固定框重新计算 fitScale;
81
+ * 否则只更新文本内容/字体等基础属性。
82
+ */
83
+ updateText(rawObject: Record<string, any>, config: Record<string, any>): Promise<Record<string, any> | null>;
84
+ /**
85
+ * 半定制图片替换:使用 cover 模式重新计算缩放,保持原始占位框中心不变。
86
+ */
87
+ updateImage(rawObject: Record<string, any>, config: Record<string, any>): Promise<Record<string, any> | null>;
88
+ /**
89
+ * 清理图片 blob URL(只清理不在缓存中的临时 blob)
90
+ * @param obj 对象
91
+ */
92
+ revokeImgBlobLink(obj: fabric.Object): void;
93
+ /**
94
+ * 删除对象,默认是当前选中的对象
95
+ * @param {String} id 对象id 可空
96
+ */
97
+ deleteObject: (id?: string) => void;
98
+ /**
99
+ * 修改对象的属性
100
+ * @param {String} id 对象id
101
+ * @param {Record<string, any>} obj 对象属性集合
102
+ */
103
+ updateObject(id: string, obj: Record<string, any>): void;
104
+ /**
105
+ * 对象缩放
106
+ */
107
+ updateObjectScale(obj: fabric.Object, scale: number): void;
108
+ /**
109
+ * 修改画布上的对象的选中状态和事件状态
110
+ * @param status
111
+ */
112
+ updateObjectsEvents(status: boolean): void;
113
+ /**
114
+ * 获取当前可选择的对象,不包括背景图、裁剪区域
115
+ */
116
+ getLayerList(): fabric.Object[];
117
+ /**
118
+ * 设置当前选中的对象
119
+ */
120
+ setActiveObject(id: string): void;
121
+ /**
122
+ * 同时选中多个对象
123
+ */
124
+ setActiveObjects(objects: fabric.Object[]): void;
125
+ /**
126
+ * 以画布中心点缩放画布
127
+ */
128
+ zoomToCenter(zoom: number): void;
129
+ /**
130
+ * 获取当前画布上渲染的对象
131
+ */
132
+ getRenderedObjects(): Record<string, any>;
133
+ /**
134
+ * 清空当前画布
135
+ */
136
+ clearCanvas(): void;
137
+ /**
138
+ * 将图片对象的 clipPath 重新关联到画布上的对象
139
+ */
140
+ relinkClipPaths(): void;
141
+ /**
142
+ * 切换视图,重新渲染画布
143
+ */
144
+ switchView(mockup: Mockup, color: string, state: Record<string, any> | undefined): Promise<void>;
145
+ /**
146
+ * 半定制固定文本在修改内容/字体后,按 originalWidth/originalHeight 重新计算自适应缩放。
147
+ * 仅供半定制 Pixi 链路使用,不影响通用编辑器。
148
+ */
149
+ /**
150
+ * 根据 workspace 尺寸变化,按比例缩放所有对象
151
+ */
152
+ private _scaleObjectsToWorkspace;
153
+ /**
154
+ * 计算对象相对于画布的全局变换矩阵(包括父组的影响)
155
+ */
156
+ private getGlobalTransformMatrix;
157
+ /**
158
+ * 判断对象是否位于 activeSelection 中
159
+ */
160
+ private isInActiveSelection;
161
+ /**
162
+ * 注册图片到缓存(会话级缓存,整个编辑期间保持)
163
+ * @param src 图片源地址(originSrc)
164
+ * @param img fabric.Image 对象
165
+ * @param blobUrl 可选的 blob URL(用于撤销/重做)
166
+ */
167
+ registerImageCache(src: string, img: fabric.Image, blobUrl?: string): void;
168
+ /**
169
+ * 清除整个图片缓存(在关闭编辑器时调用)
170
+ */
171
+ clearImageCache(): void;
172
+ /**
173
+ * 销毁编辑器,释放所有资源
174
+ */
175
+ destroy(): void;
176
+ /**
177
+ * 按需生成图片 dataURL(不缓存,减少内存占用)
178
+ */
179
+ private getCachedDataURL;
180
+ /**
181
+ * 从缓存克隆图片,避免重复下载
182
+ */
183
+ private cloneImageFromCache;
184
+ /**
185
+ * 导出 clipPath 区域的 SVG(按 overlay 原始尺寸)
186
+ * @param {boolean} embedImages 是否将图片转为 dataURL 内嵌
187
+ * @param {String?} color 背景颜色
188
+ * @returns {Promise<string[]>} SVG 字符串
189
+ */
190
+ export(embedImages?: boolean, color?: string, options?: {
191
+ minOutputSize?: number;
192
+ preferImageBitmap?: boolean;
193
+ objectCloneMultiplierCap?: number;
194
+ minObjectCloneMultiplier?: number;
195
+ outputMultiplierCap?: number;
196
+ quality?: number;
197
+ returnBlobUrl?: boolean;
198
+ }): Promise<string | ImageBitmap>;
199
+ }
@@ -0,0 +1,28 @@
1
+ export interface Size {
2
+ width: number;
3
+ height: number;
4
+ }
5
+ export interface TransformBox extends Size {
6
+ scaleX?: number;
7
+ scaleY?: number;
8
+ left: number;
9
+ top: number;
10
+ }
11
+ export interface RenderableBox extends TransformBox {
12
+ right: number;
13
+ bottom: number;
14
+ center?: {
15
+ x: number;
16
+ y: number;
17
+ };
18
+ angle?: number;
19
+ centerX?: number;
20
+ centerY?: number;
21
+ originalWidth?: number;
22
+ originalHeight?: number;
23
+ }
24
+ export interface PrintArea {
25
+ widthInInch: number;
26
+ heightInInch: number;
27
+ }
28
+ export type DesignerElementType = 'product' | 'shapes' | 'images' | 'text' | 'textEffects' | 'layer' | 'templates' | 'clipart' | 'ai' | 'background' | 'sca' | 'files';
@@ -0,0 +1,25 @@
1
+ import type { fabric } from 'fabric';
2
+ import '../../fabric-types';
3
+ import type { Size } from '../types';
4
+ export declare const getCanvasSize: (container: Size, ratio: number) => Size;
5
+ export declare const cloneObject: (obj: fabric.Object, includeProps?: string[]) => Promise<fabric.Object>;
6
+ export declare const urlToBlob: (url: string) => Promise<Blob>;
7
+ export declare const urlToDataURL: (url: string) => Promise<string>;
8
+ export declare const blobToDataURL: (blob: Blob) => Promise<string>;
9
+ export declare const blobUrlToFile: (blobUrl: string) => Promise<File>;
10
+ export declare const resizeBase64Image: (base64: string, width: number, height: number) => Promise<string>;
11
+ export declare const generateThumbImage: (svg: string, width?: number, height?: number, options?: {
12
+ bgColor?: string;
13
+ pad?: number;
14
+ }) => Promise<Blob | null>;
15
+ export declare const dataUrlToFile: (dataUrl: string, filename?: string) => File;
16
+ export declare function directionOffsetToXY(direction: number, offset: number, maxOffset?: number): {
17
+ offsetX: number;
18
+ offsetY: number;
19
+ };
20
+ export declare function xyToDirectionOffset(offsetX: number, offsetY: number, maxOffset?: number): {
21
+ direction: number;
22
+ offset: number;
23
+ };
24
+ export declare const toVerticalStack: (raw: string) => string;
25
+ export declare const toHorizontalRaw: (stacked: string) => string;
@@ -0,0 +1,68 @@
1
+ import { v4 as m } from "uuid";
2
+ const L = (t, e) => {
3
+ const n = { width: t.width, height: t.height };
4
+ return n.width = t.width > 600 ? 600 : t.width, n.height = n.width / e, n;
5
+ }, O = (t, e = []) => new Promise((n) => {
6
+ t.clone((a) => n(a), e);
7
+ }), w = async (t) => (await fetch(t, { mode: "cors" })).blob(), R = async (t) => S(await w(t)), S = async (t) => new Promise((e) => {
8
+ const n = new FileReader();
9
+ n.onload = () => e(n.result), n.readAsDataURL(t);
10
+ }), T = async (t) => {
11
+ const e = await w(t);
12
+ return new File([e], `${m()}.${e.type.split("/")[1]}`, { type: e.type });
13
+ }, $ = async (t, e, n) => {
14
+ const a = t.replace(/^data:image\/svg\+xml;base64,/, ""), i = atob(a), o = new DOMParser().parseFromString(i, "image/svg+xml").querySelector("svg");
15
+ if (!o) throw new Error("Invalid SVG");
16
+ if (o.setAttribute("width", e.toString()), o.setAttribute("height", n.toString()), !o.hasAttribute("viewBox")) {
17
+ const s = o.getAttribute("width"), c = o.getAttribute("height");
18
+ s && c && o.setAttribute("viewBox", `0 0 ${s} ${c}`);
19
+ }
20
+ return `data:image/svg+xml;base64,${btoa(new XMLSerializer().serializeToString(o))}`;
21
+ }, B = async (t, e = 200, n = 200, a = { pad: 10 }) => {
22
+ const i = e - (a.pad || 0) * 2, r = n - (a.pad || 0) * 2, h = await w(t), o = URL.createObjectURL(h), s = await new Promise((p, M) => {
23
+ const d = new Image();
24
+ d.onload = () => p(d), d.onerror = M, d.src = o;
25
+ }), c = s.naturalWidth || s.width, l = s.naturalHeight || s.height, b = Math.min(i / c, r / l), f = Math.round(c * b), v = Math.round(l * b), y = (a.pad || 0) + Math.round((i - f) / 2), x = (a.pad || 0) + Math.round((r - v) / 2), g = document.createElement("canvas");
26
+ g.width = e, g.height = n;
27
+ const u = g.getContext("2d");
28
+ return u.imageSmoothingEnabled = !0, u.imageSmoothingQuality = "high", u.drawImage(s, 0, 0, c, l, y, x, f, v), URL.revokeObjectURL(o), new Promise((p) => g.toBlob(p, "image/png", 1));
29
+ }, U = (t) => {
30
+ if (!t) return "";
31
+ const e = t.split("/")[1] ?? "";
32
+ return e ? e.split("+")[0].split(";")[0].replace(/[^a-z0-9]/gi, "").toLowerCase() : "";
33
+ }, D = (t, e) => {
34
+ const [n, a] = t.split(","), r = n.match(/data:(.*);base64/)?.[1] ?? "application/octet-stream", h = atob(a), o = new Uint8Array(h.length);
35
+ for (let l = 0; l < h.length; l += 1) o[l] = h.charCodeAt(l);
36
+ const s = U(r), c = e ? e.includes(".") || !s ? e : `${e}.${s}` : s ? `${m()}.${s}` : m();
37
+ return new File([o], c, { type: r });
38
+ };
39
+ function z(t, e, n = 10) {
40
+ const a = t * Math.PI / 180, i = e / 100 * n;
41
+ return { offsetX: i * Math.cos(a), offsetY: i * Math.sin(a) };
42
+ }
43
+ function I(t, e, n = 10) {
44
+ const i = Math.sqrt(t ** 2 + e ** 2) / n * 100;
45
+ let r = Math.atan2(e, t) * (180 / Math.PI);
46
+ return r < 0 && (r += 360), r > 180 && (r -= 360), { direction: r, offset: Math.min(100, Math.max(0, i)) };
47
+ }
48
+ const P = (t) => t.split(`
49
+ `).map((n) => n.split("").join(`
50
+ `)).join(`
51
+
52
+ `), F = (t) => t.replace(/\n\u200B\n/g, "\0").replace(/\n/g, "").replace(/\u0000/g, `
53
+ `).replace(/\u200B/g, "");
54
+ export {
55
+ S as blobToDataURL,
56
+ T as blobUrlToFile,
57
+ O as cloneObject,
58
+ D as dataUrlToFile,
59
+ z as directionOffsetToXY,
60
+ B as generateThumbImage,
61
+ L as getCanvasSize,
62
+ $ as resizeBase64Image,
63
+ F as toHorizontalRaw,
64
+ P as toVerticalStack,
65
+ w as urlToBlob,
66
+ R as urlToDataURL,
67
+ I as xyToDirectionOffset
68
+ };
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+ type ControlProps = {
3
+ openProp?: string;
4
+ onOpenChangeProp?: string;
5
+ };
6
+ type OpenOptions = {
7
+ onClose?: () => void;
8
+ };
9
+ /**
10
+ * 任意弹窗的命令式打开(可并发多实例)
11
+ * - Component: 任意 React 组件
12
+ * - props: 初始 props(除受控开关外)
13
+ * - control: 受控属性名映射(默认 open/onOpenChange;你可传 show/onChange)
14
+ * 返回:句柄(close、update、wait)
15
+ */
16
+ export declare function openAnyDialog<P extends Record<string, any>>(Component: React.ComponentType<P>, props: Partial<P>, control?: ControlProps, options?: OpenOptions): {
17
+ close: (result?: unknown) => void;
18
+ update: (patch: Partial<P>) => void;
19
+ wait: Promise<unknown>;
20
+ };
21
+ /**
22
+ * Promise 版:关闭时 resolve 返回值
23
+ */
24
+ export declare function showDialog<P extends Record<string, any>, R = unknown>(Component: React.ComponentType<P>, props: Partial<P>, control?: ControlProps, options?: OpenOptions): Promise<R | undefined>;
25
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { Font, FontFile } from '../../contracts/design';
2
+ export declare const fontCache: Set<string>;
3
+ /** Loads a Printine font file once and registers it with the current browser document. */
4
+ export declare function loadFontface(font: FontFile, maxRetry?: number): Promise<void>;
5
+ export declare function loadFontFile(font: Font): Promise<void>;
@@ -0,0 +1,39 @@
1
+ const y = /* @__PURE__ */ new Set(), l = /* @__PURE__ */ new Map();
2
+ async function w(t, n = 3) {
3
+ const a = t.family, i = t.url;
4
+ if (!a || !i) throw new Error("Font family and URL are required");
5
+ const e = `${a}_${t.weight}_${t.style}`;
6
+ if (y.has(e)) return;
7
+ const s = l.get(e);
8
+ if (s) return s;
9
+ const c = (async () => {
10
+ let r;
11
+ for (let d = 0; d < n; d += 1)
12
+ try {
13
+ const o = new FontFace(a, `url(${i})`, {
14
+ weight: String(t.weight),
15
+ style: t.style,
16
+ display: "swap"
17
+ });
18
+ document.fonts.add(await o.load()), y.add(e);
19
+ return;
20
+ } catch (o) {
21
+ r = o;
22
+ }
23
+ throw r instanceof Error ? r : new Error(`Unable to load font: ${a}`);
24
+ })();
25
+ l.set(e, c);
26
+ try {
27
+ await c;
28
+ } finally {
29
+ l.delete(e);
30
+ }
31
+ }
32
+ async function f(t) {
33
+ await Promise.all(t.files.map((n) => w({ ...n, family: t.family })));
34
+ }
35
+ export {
36
+ y as fontCache,
37
+ f as loadFontFile,
38
+ w as loadFontface
39
+ };
@@ -0,0 +1,8 @@
1
+ import { fabric } from 'fabric';
2
+ import '../../fabric-types';
3
+ export declare const loadImage: (url: string) => Promise<fabric.Image>;
4
+ export declare const loadSvgImage: (url: string, options?: Record<string, any>) => Promise<fabric.Group>;
5
+ export declare const loadOverlaySvgImage: (url: string) => Promise<{
6
+ image: fabric.Group;
7
+ clipPath?: fabric.Object;
8
+ }>;
@@ -0,0 +1,50 @@
1
+ import { fabric as l } from "fabric";
2
+ import { cloneObject as s } from "./common.js";
3
+ const f = (i) => new Promise((o, n) => {
4
+ l.Image.fromURL(
5
+ i,
6
+ (e) => {
7
+ e ? o(e) : n(new Error("Failed to load image"));
8
+ },
9
+ { crossOrigin: "anonymous" }
10
+ );
11
+ }), c = (i, o = {}) => new Promise((n, e) => {
12
+ l.loadSVGFromURL(
13
+ i,
14
+ (r, m) => {
15
+ if (!r?.length) {
16
+ e();
17
+ return;
18
+ }
19
+ r.forEach((a) => a.set({ ...o })), n(l.util.groupSVGElements(r, m));
20
+ },
21
+ void 0,
22
+ { crossOrigin: "anonymous" }
23
+ );
24
+ }), u = (i) => {
25
+ let o;
26
+ return new Promise((n, e) => {
27
+ l.loadSVGFromURL(
28
+ i,
29
+ async (r, m) => {
30
+ if (!r?.length) {
31
+ e();
32
+ return;
33
+ }
34
+ r.filter((t) => t.id !== "bg").forEach((t) => t.set({ objectCaching: !1, dirty: !0 }));
35
+ const a = r.find((t) => t.id === "bg");
36
+ a && (o = await s(a), a.set({ fill: "transparent" })), n({
37
+ image: l.util.groupSVGElements(r, m),
38
+ clipPath: o
39
+ });
40
+ },
41
+ void 0,
42
+ { crossOrigin: "anonymous" }
43
+ );
44
+ });
45
+ };
46
+ export {
47
+ f as loadImage,
48
+ u as loadOverlaySvgImage,
49
+ c as loadSvgImage
50
+ };
@@ -0,0 +1,17 @@
1
+ import { fabric } from 'fabric';
2
+ import type { TransformBox } from '../types';
3
+ export interface LoadingOptions {
4
+ spinnerUrl?: string;
5
+ }
6
+ /** Temporary Fabric loading overlay used while image and text assets are processed. */
7
+ export declare class Loading {
8
+ private readonly canvas;
9
+ private readonly dimension;
10
+ private status;
11
+ private loadingBox;
12
+ private readonly spinnerUrl;
13
+ constructor(canvas: fabric.Canvas, dimension: TransformBox, options?: LoadingOptions);
14
+ private spin;
15
+ addLoadingGroup(): Promise<void>;
16
+ removeLoadingGroup(): void;
17
+ }
@@ -0,0 +1,71 @@
1
+ import { fabric as a } from "fabric";
2
+ import { designerEventBus as d } from "../events.js";
3
+ import { d as c } from "../../config-D2qm60gf.js";
4
+ let o;
5
+ class u {
6
+ constructor(i, e, s = {}) {
7
+ this.canvas = i, this.dimension = e, this.status = null, this.loadingBox = null, this.spinnerUrl = s.spinnerUrl ?? c("/images/design/spinner.svg");
8
+ }
9
+ spin(i) {
10
+ i.rotate(0).animate(
11
+ { angle: 360 },
12
+ {
13
+ duration: 1500,
14
+ onComplete: () => {
15
+ this.status !== "remove" && this.spin(i);
16
+ },
17
+ onChange: () => {
18
+ this.status !== "remove" && this.canvas.renderAll();
19
+ },
20
+ easing: (e, s, t, n) => t * e / n + s
21
+ }
22
+ );
23
+ }
24
+ async addLoadingGroup() {
25
+ d.emit("design:objectLoading"), o || (o = await (await fetch(this.spinnerUrl)).text());
26
+ const i = new a.Rect({
27
+ width: this.dimension.width,
28
+ height: this.dimension.height,
29
+ scaleX: this.dimension.scaleX,
30
+ scaleY: this.dimension.scaleY,
31
+ stroke: "#3540a5",
32
+ strokeWidth: 1,
33
+ selectable: !1,
34
+ evented: !1,
35
+ fill: "transparent",
36
+ left: 0,
37
+ top: 0
38
+ });
39
+ a.loadSVGFromString(o, (e, s) => {
40
+ if (this.status === "remove") return;
41
+ const t = Math.min(this.dimension.width, this.dimension.height), n = Math.min(s.width, s.height), r = n < t ? 1 : t / n;
42
+ e.forEach((h) => {
43
+ h.fill === "currentColor" && h.set("fill", "#3540a5");
44
+ });
45
+ const l = new a.Group(e, {
46
+ left: this.dimension.width * (this.dimension.scaleX || 1) / 2,
47
+ top: this.dimension.height * (this.dimension.scaleY || 1) / 2,
48
+ scaleX: r,
49
+ scaleY: r,
50
+ selectable: !1,
51
+ evented: !1,
52
+ originX: "center",
53
+ originY: "center"
54
+ });
55
+ this.loadingBox = new a.Group([i, l], {
56
+ id: "loading",
57
+ left: this.dimension.left,
58
+ top: this.dimension.top,
59
+ selectable: !1,
60
+ evented: !1,
61
+ excludeHistory: !0
62
+ }), this.canvas.add(this.loadingBox), this.spin(l), this.canvas.renderAll();
63
+ });
64
+ }
65
+ removeLoadingGroup() {
66
+ this.status = "remove", this.loadingBox && (this.canvas.remove(this.loadingBox), this.canvas.renderAll());
67
+ }
68
+ }
69
+ export {
70
+ u as Loading
71
+ };
@@ -0,0 +1,6 @@
1
+ /** Physical print-area helpers shared by the editor engine. */
2
+ export declare const DEFAULT_DPI = 300;
3
+ /** Converts inches to canvas pixels (or back when `reverse` is set), matching the existing editor math. */
4
+ export declare const inchToPixel: (value: number, reverse?: boolean, dpi?: number) => number;
5
+ /** Mirrors the host helper: only the path segment decides whether a remote asset is an SVG. */
6
+ export declare const isSvg: (src: string) => boolean;
@@ -0,0 +1,6 @@
1
+ const o = 300, c = (s, n = !1, t = 300) => n ? s / t : s * t, i = (s) => s.split("?")[0].endsWith(".svg");
2
+ export {
3
+ o as DEFAULT_DPI,
4
+ c as inchToPixel,
5
+ i as isSvg
6
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 从 SVG URL 提取尺寸和 clipPath 信息(用于 mockup 生成)
3
+ * @param svgUrl SVG 文件 URL
4
+ * @returns Promise<{ width: number; height: number; clipPathData?: string }>
5
+ */
6
+ export declare const loadSvgInfo: (svgUrl: string) => Promise<{
7
+ width: number;
8
+ height: number;
9
+ clipPathData?: string;
10
+ }>;
11
+ /**
12
+ * Mockup 生成结果
13
+ */
14
+ export interface MockupResult {
15
+ dataBlob?: Blob;
16
+ resizeBlob?: Blob;
17
+ imageInfo: {
18
+ originalWidth: number;
19
+ originalHeight: number;
20
+ scaledWidth: number;
21
+ scaledHeight: number;
22
+ left: number;
23
+ top: number;
24
+ scale: number;
25
+ };
26
+ canvasWidth: number;
27
+ canvasHeight: number;
28
+ }
29
+ export interface ImageBitmapInfo {
30
+ width: number;
31
+ height: number;
32
+ arrayBuffer: ArrayBuffer;
33
+ type: string;
34
+ }
35
+ export declare const resizeImage: (_id: string, imageSource: ImageBitmapInfo, size: {
36
+ width: number;
37
+ height: number;
38
+ }, _productName?: string) => Promise<Blob>;
39
+ /**
40
+ * 根据模型底板 svg 和上传的图片生成对应的设计面图片
41
+ * 使用 Web Worker + OffscreenCanvas,不阻塞主线程
42
+ *
43
+ * ⭐ SVG 会自动在主线程加载为 ImageBitmap,解决 Worker 中的兼容性问题
44
+ *
45
+ * @param imageSource 图片文件 arraybuffer
46
+ * @param plateSource 底板 SVG URL 或预加载的 plate 对象(自动在主线程处理 SVG)
47
+ * @returns Promise<MockupResult> 返回合并后的 PNG dataURL 和图片位置信息
48
+ */
49
+ export declare const generateMockup: (imageSource: ImageBitmapInfo, plateSource: string | {
50
+ width: number;
51
+ height: number;
52
+ clipPathData?: string;
53
+ }) => Promise<MockupResult>;
54
+ /**
55
+ * 清理 Worker 资源
56
+ */
57
+ export declare const terminateAllWorkers: () => void;
58
+ export declare const terminateMockupWorker: () => void;
59
+ export declare const terminateResizeWorker: () => void;
@@ -0,0 +1,4 @@
1
+ import { fabric } from 'fabric';
2
+ export declare const setDefaultControls: (size?: number) => void;
3
+ /** Create the image-only resize controls used by the editor's image processor. */
4
+ export declare const getImageControls: () => Record<string, fabric.Control>;