customforge 0.1.0-alpha.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,130 @@
1
+ import { Canvas, FabricImage, Textbox } from 'fabric';
2
+ import type { AddImageOptions, AddTextOptions, DesignDocument } from '../core/types';
3
+ /** 二维编辑器内部初始化配置 */
4
+ interface DesignEditorOptions {
5
+ /** 画布逻辑宽度,单位为像素 */
6
+ width: number;
7
+ /** 画布逻辑高度,单位为像素 */
8
+ height: number;
9
+ }
10
+ type RenderListener = () => void;
11
+ type SelectionListener = (hasSelection: boolean) => void;
12
+ /**
13
+ * 基于 Fabric.js 的二维纹理编辑器
14
+ *
15
+ * 负责基础纹理、文字、图片、对象选择和 PNG 导出
16
+ * 显示尺寸可以响应容器变化,内部逻辑尺寸保持不变
17
+ */
18
+ export declare class DesignEditor {
19
+ /** 当前实例使用的 Fabric Canvas */
20
+ readonly canvas: Canvas;
21
+ private readonly host;
22
+ private readonly width;
23
+ private readonly height;
24
+ private readonly renderListeners;
25
+ private readonly selectionListeners;
26
+ private readonly objectIds;
27
+ private readonly usedObjectIds;
28
+ private readonly imageSources;
29
+ private readonly resizeObserver;
30
+ private objectIdSequence;
31
+ /**
32
+ * @param host 二维编辑器挂载容器
33
+ * @param options 画布逻辑尺寸
34
+ */
35
+ constructor(host: HTMLElement, options: DesignEditorOptions);
36
+ /** 供 Three.js 创建 CanvasTexture 的底层 HTML Canvas */
37
+ get textureCanvas(): HTMLCanvasElement;
38
+ /** 当前画布中可编辑对象的数量,不包含背景纹理 */
39
+ get objectCount(): number;
40
+ /**
41
+ * 订阅 Fabric Canvas 完成渲染事件
42
+ *
43
+ * @param listener 每次画布完成渲染后调用的函数
44
+ * @returns 用于取消本次订阅的函数
45
+ */
46
+ onRender(listener: RenderListener): () => void;
47
+ /**
48
+ * 订阅画布选中状态变化
49
+ *
50
+ * @param listener 接收当前是否存在选区的函数
51
+ * @returns 用于取消本次订阅的函数
52
+ */
53
+ onSelectionChange(listener: SelectionListener): () => void;
54
+ /**
55
+ * 设置铺满画布的基础纹理,不传地址时恢复默认背景色
56
+ *
57
+ * 背景纹理不参与对象选择,但会包含在实时纹理和 PNG 导出中
58
+ *
59
+ * @param url 基础纹理地址
60
+ * @throws 图片加载失败或被 CORS 策略阻止时抛出错误
61
+ */
62
+ setBackgroundTexture(url?: string): Promise<void>;
63
+ /**
64
+ * 添加并选中一个可编辑文字对象
65
+ *
66
+ * 文字位置使用画布像素坐标,原点位于对象左上角
67
+ * 对象过大时会等比缩小,越界时会自动移回画布
68
+ *
69
+ * @param options 文字内容、位置和样式
70
+ * @returns 创建的 Fabric Textbox
71
+ */
72
+ addText(options?: AddTextOptions): Textbox;
73
+ /**
74
+ * 加载、添加并选中一个图片对象
75
+ *
76
+ * 图片位置使用画布像素坐标,原点位于图片中心
77
+ * 对象过大时会等比缩小,越界时会自动移回画布
78
+ * 远程图片必须提供正确的 CORS 响应头才能安全导出 PNG
79
+ *
80
+ * @param options 图片地址、中心位置和显示宽度
81
+ * @returns 创建的 FabricImage
82
+ * @throws 图片加载失败或被 CORS 策略阻止时抛出错误
83
+ */
84
+ addImage(options: AddImageOptions): Promise<FabricImage>;
85
+ /**
86
+ * 返回与 Fabric.js 无关的当前设计快照
87
+ *
88
+ * 文档只包含可编辑对象和逻辑画布尺寸,不包含背景纹理或产品模型配置
89
+ *
90
+ * @returns 可以安全传给 JSON.stringify 的 Design JSON 文档
91
+ * @throws 画布包含不支持的对象或非字符串文字填充时抛出错误
92
+ */
93
+ saveDesign(): DesignDocument;
94
+ /**
95
+ * 校验并恢复 Design JSON,图片全部加载成功后才替换当前对象
96
+ *
97
+ * 背景纹理和当前产品保持不变,恢复后不选中任何对象
98
+ *
99
+ * @param value JSON.parse 结果或符合 DesignDocument 的对象
100
+ * @throws Schema 无效、画布尺寸不匹配或图片无法加载时抛出错误
101
+ */
102
+ loadDesign(value: unknown): Promise<void>;
103
+ /**
104
+ * 删除当前对象或多选选区
105
+ *
106
+ * @returns 是否删除了至少一个对象
107
+ */
108
+ deleteSelected(): boolean;
109
+ /**
110
+ * 将当前画布内容导出为 PNG 并触发浏览器下载
111
+ *
112
+ * @param filename 下载文件名
113
+ * @throws 画布被无 CORS 授权的远程图片污染时抛出安全错误
114
+ */
115
+ exportTexture(filename?: string): void;
116
+ /** 释放 ResizeObserver、事件监听和 Fabric Canvas */
117
+ destroy(): void;
118
+ private addAndSelect;
119
+ private registerObject;
120
+ private createObjectId;
121
+ private serializeObject;
122
+ private serializeTransform;
123
+ private createObjectFromDesign;
124
+ private loadImage;
125
+ private constrainObjectToCanvas;
126
+ private resizeDisplay;
127
+ private markRenderState;
128
+ }
129
+ export {};
130
+ //# sourceMappingURL=DesignEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DesignEditor.d.ts","sourceRoot":"","sources":["../../src/editor/DesignEditor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,EACN,WAAW,EACX,OAAO,EAER,MAAM,QAAQ,CAAA;AAEf,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,cAAc,EAGf,MAAM,eAAe,CAAA;AAOtB,mBAAmB;AACnB,UAAU,mBAAmB;IAC3B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAA;IAEb,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,KAAK,cAAc,GAAG,MAAM,IAAI,CAAA;AAChC,KAAK,iBAAiB,GAAG,CAAC,YAAY,EAAE,OAAO,KAAK,IAAI,CAAA;AAExD;;;;;GAKG;AACH,qBAAa,YAAY;IACvB,4BAA4B;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IAEvB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA4B;IAC5D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA+B;IAClE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsC;IAChE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;IAClD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqC;IAClE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAgB;IAC/C,OAAO,CAAC,gBAAgB,CAAI;IAE5B;;;OAGG;IACH,YAAY,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB,EAgD1D;IAED,kDAAkD;IAClD,IAAI,aAAa,IAAI,iBAAiB,CAErC;IAED,4BAA4B;IAC5B,IAAI,WAAW,IAAI,MAAM,CAExB;IAED;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,IAAI,CAG7C;IAED;;;;;OAKG;IACH,iBAAiB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAGzD;IAED;;;;;;;OAOG;IACG,oBAAoB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBtD;IAED;;;;;;;;OAQG;IACH,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAsB7C;IAED;;;;;;;;;;OAUG;IACG,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAuB7D;IAED;;;;;;;OAOG;IACH,UAAU,IAAI,cAAc,CAM3B;IAED;;;;;;;OAOG;IACG,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA4C9C;IAED;;;;OAIG;IACH,cAAc,IAAI,OAAO,CAWxB;IAED;;;;;OAKG;IACH,aAAa,CAAC,QAAQ,SAAuB,GAAG,IAAI,CAWnD;IAED,4CAA4C;IAC5C,OAAO,IAAI,IAAI,CAMd;IAED,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,eAAe;IAkCvB,OAAO,CAAC,kBAAkB;YAaZ,sBAAsB;IAoCpC,OAAO,CAAC,SAAS;IAOjB,OAAO,CAAC,uBAAuB;IAwB/B,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,eAAe;CA4BxB"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * 将短生命周期 Blob URL 转换为可写入 Design JSON 的 Data URL
3
+ *
4
+ * 远程 URL 和已有 Data URL 会保持不变
5
+ *
6
+ * @param src 图片来源
7
+ * @returns 可跨页面会话重新加载的图片来源
8
+ * @throws Blob URL 已失效或浏览器无法读取对应 Blob 时抛出错误
9
+ */
10
+ export declare function resolvePersistentImageSource(src: string): Promise<string>;
11
+ //# sourceMappingURL=imageSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"imageSource.d.ts","sourceRoot":"","sources":["../../src/editor/imageSource.ts"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU/E"}
@@ -0,0 +1,39 @@
1
+ /** 参与画布边界计算的轴对齐包围盒 */
2
+ export interface ObjectBounds {
3
+ /** 包围盒左边缘的画布横坐标 */
4
+ left: number;
5
+ /** 包围盒上边缘的画布纵坐标 */
6
+ top: number;
7
+ /** 包围盒宽度 */
8
+ width: number;
9
+ /** 包围盒高度 */
10
+ height: number;
11
+ }
12
+ /** 将对象包围盒移回画布所需的偏移量 */
13
+ export interface ContainmentOffset {
14
+ /** 应加到对象横坐标的偏移量 */
15
+ x: number;
16
+ /** 应加到对象纵坐标的偏移量 */
17
+ y: number;
18
+ }
19
+ /**
20
+ * 计算对象完整进入画布所需的最大等比缩放系数
21
+ *
22
+ * @param bounds 对象当前的轴对齐包围盒
23
+ * @param canvasWidth 画布逻辑宽度
24
+ * @param canvasHeight 画布逻辑高度
25
+ * @returns 不大于 1 的缩放系数,对象已经可容纳时返回 1
26
+ */
27
+ export declare function calculateContainmentScale(bounds: ObjectBounds, canvasWidth: number, canvasHeight: number): number;
28
+ /**
29
+ * 计算可容纳对象移回画布所需的平移距离
30
+ *
31
+ * 调用前应先确保包围盒不大于画布,否则无法同时满足两侧边界
32
+ *
33
+ * @param bounds 对象当前的轴对齐包围盒
34
+ * @param canvasWidth 画布逻辑宽度
35
+ * @param canvasHeight 画布逻辑高度
36
+ * @returns 应叠加到对象位置的画布坐标偏移量
37
+ */
38
+ export declare function calculateContainmentOffset(bounds: ObjectBounds, canvasWidth: number, canvasHeight: number): ContainmentOffset;
39
+ //# sourceMappingURL=objectBounds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objectBounds.d.ts","sourceRoot":"","sources":["../../src/editor/objectBounds.ts"],"names":[],"mappings":"AAAA,sBAAsB;AACtB,MAAM,WAAW,YAAY;IAC3B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAA;IAEZ,mBAAmB;IACnB,GAAG,EAAE,MAAM,CAAA;IAEX,YAAY;IACZ,KAAK,EAAE,MAAM,CAAA;IAEb,YAAY;IACZ,MAAM,EAAE,MAAM,CAAA;CACf;AAED,uBAAuB;AACvB,MAAM,WAAW,iBAAiB;IAChC,mBAAmB;IACnB,CAAC,EAAE,MAAM,CAAA;IAET,mBAAmB;IACnB,CAAC,EAAE,MAAM,CAAA;CACV;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,YAAY,EACpB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,MAAM,CAIR;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,YAAY,EACpB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,iBAAiB,CAWnB"}
@@ -0,0 +1,30 @@
1
+ import './style.css';
2
+ import { ProductCustomizer } from './customizer/ProductCustomizer';
3
+ import type { CustomizerOptions } from './core/types';
4
+ /**
5
+ * 创建产品定制器实例并完成模型、编辑器和纹理同步初始化
6
+ *
7
+ * 仅支持浏览器环境,应在 DOM 挂载容器可用后调用
8
+ * 编辑器和查看器必须使用两个不同的容器
9
+ * 不再使用返回的实例时必须调用 `destroy()` 释放资源
10
+ *
11
+ * @param options 编辑器容器、查看器容器和产品资源配置
12
+ * @returns 初始化完成的产品定制器实例
13
+ * @throws DOM 容器不存在、两个容器相同、模型加载失败或目标 Mesh 不存在时抛出错误
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const customizer = await createCustomizer({
18
+ * editor: '#editor',
19
+ * viewer: '#viewer',
20
+ * product: {
21
+ * modelUrl: 'https://example.com/product.glb',
22
+ * surfaceMesh: 'PrintArea',
23
+ * },
24
+ * })
25
+ * ```
26
+ */
27
+ export declare function createCustomizer(options: CustomizerOptions): Promise<ProductCustomizer>;
28
+ export { ProductCustomizer };
29
+ export type { AddImageOptions, AddTextOptions, CustomizerEventMap, CustomizerOptions, DesignCanvas, DesignDocument, DesignObject, DesignObjectTransform, ImageDesignObject, ProductConfiguration, TextDesignObject, } from './core/types';
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,CAAC,CAE5B;AAED,OAAO,EAAE,iBAAiB,EAAE,CAAA;AAC5B,YAAY,EACV,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,cAAc,CAAA"}