niimbot-canvas-sdk 1.0.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,444 @@
|
|
|
1
|
+
var d = Object.defineProperty;
|
|
2
|
+
var g = (n, t, e) => t in n ? d(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
|
|
3
|
+
var i = (n, t, e) => (g(n, typeof t != "symbol" ? t + "" : t, e), e);
|
|
4
|
+
import u from "penpal";
|
|
5
|
+
import l from "eventemitter3";
|
|
6
|
+
import { customAlphabet as m } from "nanoid";
|
|
7
|
+
class h extends l {
|
|
8
|
+
on(t, e, s) {
|
|
9
|
+
return super.on(t, e, s);
|
|
10
|
+
}
|
|
11
|
+
once(t, e, s) {
|
|
12
|
+
return super.once(t, e, s);
|
|
13
|
+
}
|
|
14
|
+
off(t, e, s) {
|
|
15
|
+
return super.off(t, e, s);
|
|
16
|
+
}
|
|
17
|
+
emit(t, ...e) {
|
|
18
|
+
return super.emit(t, ...e);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class b {
|
|
22
|
+
constructor() {
|
|
23
|
+
i(this, "queue", []);
|
|
24
|
+
i(this, "maxSize", 100);
|
|
25
|
+
}
|
|
26
|
+
enqueue(t, e = 3) {
|
|
27
|
+
this.queue.length >= this.maxSize && this.queue.shift();
|
|
28
|
+
const s = {
|
|
29
|
+
...t,
|
|
30
|
+
retryCount: 0,
|
|
31
|
+
maxRetries: e
|
|
32
|
+
};
|
|
33
|
+
return this.queue.push(s), t.messageId;
|
|
34
|
+
}
|
|
35
|
+
dequeue() {
|
|
36
|
+
return this.queue.shift();
|
|
37
|
+
}
|
|
38
|
+
hasMessages() {
|
|
39
|
+
return this.queue.length > 0;
|
|
40
|
+
}
|
|
41
|
+
getMessageById(t) {
|
|
42
|
+
return this.queue.find((e) => e.messageId === t);
|
|
43
|
+
}
|
|
44
|
+
removeMessageById(t) {
|
|
45
|
+
const e = this.queue.findIndex((s) => s.messageId === t);
|
|
46
|
+
e !== -1 && this.queue.splice(e, 1);
|
|
47
|
+
}
|
|
48
|
+
clear() {
|
|
49
|
+
this.queue = [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
class A {
|
|
53
|
+
constructor(t, e = !1) {
|
|
54
|
+
this.context = t, this.debug = e;
|
|
55
|
+
}
|
|
56
|
+
formatMessage(t, ...e) {
|
|
57
|
+
return [`[${(/* @__PURE__ */ new Date()).toISOString()}] [${this.context}] [${t}]`, ...e];
|
|
58
|
+
}
|
|
59
|
+
info(...t) {
|
|
60
|
+
console.info(...this.formatMessage("INFO", ...t));
|
|
61
|
+
}
|
|
62
|
+
debug(...t) {
|
|
63
|
+
this.debug && console.debug(...this.formatMessage("DEBUG", ...t));
|
|
64
|
+
}
|
|
65
|
+
warn(...t) {
|
|
66
|
+
console.warn(...this.formatMessage("WARN", ...t));
|
|
67
|
+
}
|
|
68
|
+
error(...t) {
|
|
69
|
+
console.error(...this.formatMessage("ERROR", ...t));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const I = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", f = m(I, 12);
|
|
73
|
+
class p extends h {
|
|
74
|
+
constructor(e, s = {}) {
|
|
75
|
+
super();
|
|
76
|
+
i(this, "connection", null);
|
|
77
|
+
i(this, "iframe");
|
|
78
|
+
i(this, "options");
|
|
79
|
+
i(this, "messageQueue");
|
|
80
|
+
i(this, "logger");
|
|
81
|
+
i(this, "isConnected", !1);
|
|
82
|
+
i(this, "reconnectAttempts", 0);
|
|
83
|
+
i(this, "maxReconnectAttempts", 3);
|
|
84
|
+
this.iframe = e, this.options = {
|
|
85
|
+
timeout: 3e4,
|
|
86
|
+
debug: !1,
|
|
87
|
+
retryCount: 3,
|
|
88
|
+
retryDelay: 1e3,
|
|
89
|
+
childOrigin: "*",
|
|
90
|
+
...s
|
|
91
|
+
}, this.messageQueue = new b(), this.logger = new A("PenpalBridge", this.options.debug);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* 建立连接
|
|
95
|
+
*/
|
|
96
|
+
async connect(e = {}) {
|
|
97
|
+
try {
|
|
98
|
+
this.logger.info("正在建立连接..."), this.connection = u.connectToChild({
|
|
99
|
+
iframe: this.iframe,
|
|
100
|
+
methods: {
|
|
101
|
+
// 宿主提供给画板的方法
|
|
102
|
+
ping: () => "pong",
|
|
103
|
+
emitEvent: (s, a) => {
|
|
104
|
+
this.emit(s, a);
|
|
105
|
+
},
|
|
106
|
+
// 接收来自画板的请求
|
|
107
|
+
...e
|
|
108
|
+
},
|
|
109
|
+
timeout: this.options.timeout,
|
|
110
|
+
childOrigin: this.options.childOrigin
|
|
111
|
+
}), await this.connection.promise, this.isConnected = !0, this.reconnectAttempts = 0, await this.processQueuedMessages(), this.logger.info("连接建立成功"), this.emit("connected");
|
|
112
|
+
} catch (s) {
|
|
113
|
+
throw this.logger.error("连接失败:", s), await this.handleReconnect(), s;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 发送消息到画板
|
|
118
|
+
*/
|
|
119
|
+
async send(e, s) {
|
|
120
|
+
var c;
|
|
121
|
+
const a = {
|
|
122
|
+
action: e,
|
|
123
|
+
params: s,
|
|
124
|
+
timestamp: Date.now(),
|
|
125
|
+
messageId: f()
|
|
126
|
+
};
|
|
127
|
+
if (!this.isConnected)
|
|
128
|
+
throw this.messageQueue.enqueue(a), new Error("Connection not ready, message queued");
|
|
129
|
+
try {
|
|
130
|
+
if (this.logger.debug("发送消息:", a), !((c = this.connection) != null && c.child))
|
|
131
|
+
throw new Error("连接未就绪");
|
|
132
|
+
const o = await this.connection.child.handleMessage(a);
|
|
133
|
+
return this.logger.debug("收到响应:", o), this.validateResponse(o);
|
|
134
|
+
} catch (o) {
|
|
135
|
+
throw this.logger.error("发送消息失败:", o), this.messageQueue.enqueue(a), o;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 处理队列中的消息
|
|
140
|
+
*/
|
|
141
|
+
async processQueuedMessages() {
|
|
142
|
+
for (; this.messageQueue.hasMessages(); ) {
|
|
143
|
+
const e = this.messageQueue.dequeue();
|
|
144
|
+
if (e)
|
|
145
|
+
try {
|
|
146
|
+
await this.send(e.action, e.params);
|
|
147
|
+
} catch (s) {
|
|
148
|
+
this.logger.warn("队列消息发送失败:", s), this.messageQueue.enqueue(e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 重连机制
|
|
154
|
+
*/
|
|
155
|
+
async handleReconnect() {
|
|
156
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
157
|
+
this.logger.error("重连次数达到上限"), this.emit("connection-lost");
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
this.reconnectAttempts++, this.logger.info(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`), await new Promise(
|
|
161
|
+
(e) => setTimeout(e, this.options.retryDelay * this.reconnectAttempts)
|
|
162
|
+
);
|
|
163
|
+
try {
|
|
164
|
+
await this.connect();
|
|
165
|
+
} catch {
|
|
166
|
+
await this.handleReconnect();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 验证响应格式
|
|
171
|
+
*/
|
|
172
|
+
validateResponse(e) {
|
|
173
|
+
if (!e || e.success === void 0)
|
|
174
|
+
throw new Error("Invalid response format");
|
|
175
|
+
if (!e.success) {
|
|
176
|
+
const s = e.error || { code: "UNKNOWN", message: "Unknown error" };
|
|
177
|
+
throw new Error(`${s.code}: ${s.message}`);
|
|
178
|
+
}
|
|
179
|
+
return e.data;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 断开连接
|
|
183
|
+
*/
|
|
184
|
+
disconnect() {
|
|
185
|
+
this.connection && (this.connection.destroy(), this.isConnected = !1, this.emit("disconnected"), this.logger.info("连接已断开"));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 获取连接状态
|
|
189
|
+
*/
|
|
190
|
+
getConnectionStatus() {
|
|
191
|
+
return this.isConnected;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
class y {
|
|
195
|
+
constructor(t) {
|
|
196
|
+
this.bridge = t;
|
|
197
|
+
}
|
|
198
|
+
async initialize(t) {
|
|
199
|
+
return this.bridge.send("canvas.initialize", t);
|
|
200
|
+
}
|
|
201
|
+
async setData(t) {
|
|
202
|
+
return this.bridge.send("canvas.setData", t);
|
|
203
|
+
}
|
|
204
|
+
async getData() {
|
|
205
|
+
return this.bridge.send("canvas.getData");
|
|
206
|
+
}
|
|
207
|
+
async clear() {
|
|
208
|
+
return this.bridge.send("canvas.clear");
|
|
209
|
+
}
|
|
210
|
+
async undo() {
|
|
211
|
+
return this.bridge.send("canvas.undo");
|
|
212
|
+
}
|
|
213
|
+
async redo() {
|
|
214
|
+
return this.bridge.send("canvas.redo");
|
|
215
|
+
}
|
|
216
|
+
async export(t) {
|
|
217
|
+
return this.bridge.send("canvas.export", { format: t });
|
|
218
|
+
}
|
|
219
|
+
async save() {
|
|
220
|
+
return this.bridge.send("canvas.save");
|
|
221
|
+
}
|
|
222
|
+
async invoke(t, ...e) {
|
|
223
|
+
return this.bridge.send("canvas.invoke", { method: t, args: e });
|
|
224
|
+
}
|
|
225
|
+
async setZoom(t) {
|
|
226
|
+
return this.bridge.send("canvas.setZoom", { zoom: t });
|
|
227
|
+
}
|
|
228
|
+
async getSnapshot() {
|
|
229
|
+
return this.bridge.send("canvas.getSnapshot");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
class v {
|
|
233
|
+
constructor(t) {
|
|
234
|
+
this.bridge = t;
|
|
235
|
+
}
|
|
236
|
+
async setAbilities(t) {
|
|
237
|
+
return this.bridge.send("abilities.set", t);
|
|
238
|
+
}
|
|
239
|
+
async getAbilities() {
|
|
240
|
+
return this.bridge.send("abilities.get");
|
|
241
|
+
}
|
|
242
|
+
async enableAbility(t) {
|
|
243
|
+
return this.bridge.send("abilities.enable", { name: t });
|
|
244
|
+
}
|
|
245
|
+
async disableAbility(t) {
|
|
246
|
+
return this.bridge.send("abilities.disable", { name: t });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
class E {
|
|
250
|
+
constructor(t) {
|
|
251
|
+
this.bridge = t;
|
|
252
|
+
}
|
|
253
|
+
async setTheme(t) {
|
|
254
|
+
return this.bridge.send("theme.set", { theme: t });
|
|
255
|
+
}
|
|
256
|
+
async getCurrentTheme() {
|
|
257
|
+
return this.bridge.send("theme.get");
|
|
258
|
+
}
|
|
259
|
+
async updateToken(t) {
|
|
260
|
+
return this.bridge.send("theme.updateToken", t);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
class P {
|
|
264
|
+
constructor(t) {
|
|
265
|
+
this.bridge = t;
|
|
266
|
+
}
|
|
267
|
+
async setLocale(t, e) {
|
|
268
|
+
return this.bridge.send("i18n.setLocale", { locale: t, messages: e });
|
|
269
|
+
}
|
|
270
|
+
async getCurrentLocale() {
|
|
271
|
+
return this.bridge.send("i18n.getLocale");
|
|
272
|
+
}
|
|
273
|
+
async updateMessages(t) {
|
|
274
|
+
return this.bridge.send("i18n.updateMessages", t);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
class w {
|
|
278
|
+
constructor(t) {
|
|
279
|
+
this.bridge = t;
|
|
280
|
+
}
|
|
281
|
+
async validateToken() {
|
|
282
|
+
return this.bridge.send("auth.validateToken");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const r = {
|
|
286
|
+
READY: "ready",
|
|
287
|
+
CHANGE: "change",
|
|
288
|
+
SAVE: "save",
|
|
289
|
+
ERROR: "error",
|
|
290
|
+
SELECTION_CHANGED: "selection-changed",
|
|
291
|
+
CONNECTED: "connected",
|
|
292
|
+
DISCONNECTED: "disconnected",
|
|
293
|
+
// 画板生命周期事件
|
|
294
|
+
LOADED: "loaded",
|
|
295
|
+
BEFORE_UNLOAD: "before-unload",
|
|
296
|
+
// 画板操作事件
|
|
297
|
+
OBJECT_ADDED: "object-added",
|
|
298
|
+
OBJECT_MODIFIED: "object-modified",
|
|
299
|
+
OBJECT_REMOVED: "object-removed",
|
|
300
|
+
// 用户交互事件
|
|
301
|
+
MOUSE_DOWN: "mouse-down",
|
|
302
|
+
MOUSE_UP: "mouse-up",
|
|
303
|
+
MOUSE_MOVE: "mouse-move",
|
|
304
|
+
KEY_DOWN: "key-down",
|
|
305
|
+
KEY_UP: "key-up"
|
|
306
|
+
};
|
|
307
|
+
class C extends h {
|
|
308
|
+
constructor(e, s) {
|
|
309
|
+
super();
|
|
310
|
+
i(this, "bridge");
|
|
311
|
+
i(this, "canvasAPI");
|
|
312
|
+
i(this, "abilityAPI");
|
|
313
|
+
i(this, "themeAPI");
|
|
314
|
+
i(this, "i18nAPI");
|
|
315
|
+
i(this, "authAPI");
|
|
316
|
+
i(this, "config");
|
|
317
|
+
i(this, "iframe");
|
|
318
|
+
i(this, "isInitialized", !1);
|
|
319
|
+
this.validateConfig(s), this.config = s, this.iframe = e, this.bridge = new p(e, {
|
|
320
|
+
debug: s.debug || !1,
|
|
321
|
+
timeout: 3e4,
|
|
322
|
+
childOrigin: this.extractOrigin(e.src)
|
|
323
|
+
}), this.canvasAPI = new y(this.bridge), this.abilityAPI = new v(this.bridge), this.themeAPI = new E(this.bridge), this.i18nAPI = new P(this.bridge), this.authAPI = new w(this.bridge), this.setupEventHandlers();
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 初始化SDK
|
|
327
|
+
*/
|
|
328
|
+
async init() {
|
|
329
|
+
if (!this.isInitialized)
|
|
330
|
+
try {
|
|
331
|
+
await this.bridge.connect({
|
|
332
|
+
getConfig: () => this.config,
|
|
333
|
+
log: (e, s) => {
|
|
334
|
+
console.log(`[Canvas ${e.toUpperCase()}]`, s);
|
|
335
|
+
}
|
|
336
|
+
}), await this.canvasAPI.initialize(this.config), this.config.abilities && await this.abilityAPI.setAbilities(this.config.abilities), this.config.theme && await this.themeAPI.setTheme(this.config.theme), this.config.locale && await this.i18nAPI.setLocale(this.config.locale, this.config.messages), this.isInitialized = !0, this.emit(r.READY);
|
|
337
|
+
} catch (e) {
|
|
338
|
+
throw this.emit(r.ERROR, e), e;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
// Canvas 操作方法
|
|
342
|
+
get canvas() {
|
|
343
|
+
return {
|
|
344
|
+
setData: (e) => this.canvasAPI.setData(e),
|
|
345
|
+
getData: () => this.canvasAPI.getData(),
|
|
346
|
+
clear: () => this.canvasAPI.clear(),
|
|
347
|
+
undo: () => this.canvasAPI.undo(),
|
|
348
|
+
redo: () => this.canvasAPI.redo(),
|
|
349
|
+
export: (e = "png") => this.canvasAPI.export(e),
|
|
350
|
+
save: () => this.canvasAPI.save(),
|
|
351
|
+
setZoom: (e) => this.canvasAPI.setZoom(e),
|
|
352
|
+
getSnapshot: () => this.canvasAPI.getSnapshot(),
|
|
353
|
+
invoke: (e, ...s) => this.canvasAPI.invoke(e, ...s)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
// 能力管理
|
|
357
|
+
get abilities() {
|
|
358
|
+
return {
|
|
359
|
+
set: (e) => this.abilityAPI.setAbilities(e),
|
|
360
|
+
get: () => this.abilityAPI.getAbilities(),
|
|
361
|
+
enable: (e) => this.abilityAPI.enableAbility(e),
|
|
362
|
+
disable: (e) => this.abilityAPI.disableAbility(e),
|
|
363
|
+
updateConfig: (e, s) => this.abilityAPI.updateAbilityConfig(e, s)
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
// 主题管理
|
|
367
|
+
get theme() {
|
|
368
|
+
return {
|
|
369
|
+
set: (e) => this.themeAPI.setTheme(e),
|
|
370
|
+
get: () => this.themeAPI.getCurrentTheme(),
|
|
371
|
+
updateToken: (e) => this.themeAPI.updateToken(e),
|
|
372
|
+
reset: () => this.themeAPI.resetTheme()
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
// 国际化管理
|
|
376
|
+
get i18n() {
|
|
377
|
+
return {
|
|
378
|
+
setLocale: (e, s) => this.i18nAPI.setLocale(e, s),
|
|
379
|
+
getLocale: () => this.i18nAPI.getCurrentLocale(),
|
|
380
|
+
updateMessages: (e) => this.i18nAPI.updateMessages(e),
|
|
381
|
+
getMessages: () => this.i18nAPI.getMessages(),
|
|
382
|
+
getTranslation: (e) => this.i18nAPI.getTranslation(e)
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
// 鉴权管理
|
|
386
|
+
get auth() {
|
|
387
|
+
return {
|
|
388
|
+
validateToken: () => this.authAPI.validateToken(),
|
|
389
|
+
refreshToken: (e) => this.authAPI.refreshToken(e),
|
|
390
|
+
getTokenInfo: () => this.authAPI.getTokenInfo()
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
// 工具方法
|
|
394
|
+
get utils() {
|
|
395
|
+
return {
|
|
396
|
+
getConnectionStatus: () => this.bridge.getConnectionStatus(),
|
|
397
|
+
disconnect: () => this.bridge.disconnect(),
|
|
398
|
+
reconnect: async () => {
|
|
399
|
+
this.bridge.disconnect(), this.isInitialized = !1, await this.init();
|
|
400
|
+
},
|
|
401
|
+
isInitialized: () => this.isInitialized
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
setupEventHandlers() {
|
|
405
|
+
this.bridge.on("canvas-change", (e) => {
|
|
406
|
+
this.emit(r.CHANGE, e);
|
|
407
|
+
}), this.bridge.on("canvas-save", (e) => {
|
|
408
|
+
this.emit(r.SAVE, e);
|
|
409
|
+
}), this.bridge.on("canvas-error", (e) => {
|
|
410
|
+
this.emit(r.ERROR, e);
|
|
411
|
+
}), this.bridge.on("canvas-selection-changed", (e) => {
|
|
412
|
+
this.emit(r.SELECTION_CHANGED, e);
|
|
413
|
+
}), this.bridge.on("connected", () => {
|
|
414
|
+
this.emit(r.CONNECTED);
|
|
415
|
+
}), this.bridge.on("disconnected", () => {
|
|
416
|
+
this.emit(r.DISCONNECTED);
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
validateConfig(e) {
|
|
420
|
+
if (!e.token)
|
|
421
|
+
throw new Error("Token is required");
|
|
422
|
+
if (e.abilities) {
|
|
423
|
+
const s = ["text", "image", "barcode", "multiLayout", "shape", "qrCode"];
|
|
424
|
+
Object.keys(e.abilities).forEach((a) => {
|
|
425
|
+
s.includes(a) || console.warn(`Unknown ability: ${a}`);
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
extractOrigin(e) {
|
|
430
|
+
try {
|
|
431
|
+
return new URL(e).origin;
|
|
432
|
+
} catch {
|
|
433
|
+
return "*";
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function R(n, t) {
|
|
438
|
+
return new C(n, t);
|
|
439
|
+
}
|
|
440
|
+
export {
|
|
441
|
+
r as CANVAS_EVENTS,
|
|
442
|
+
C as NiimbotCanvasSDK,
|
|
443
|
+
R as createCanvas
|
|
444
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(n,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("penpal"),require("eventemitter3"),require("nanoid")):typeof define=="function"&&define.amd?define(["exports","penpal","eventemitter3","nanoid"],a):(n=typeof globalThis<"u"?globalThis:n||self,a(n.NiimbotCanvasSDK={},n.Penpal,n.EventEmitter3,n.nanoid))})(this,function(n,a,h,m){"use strict";var T=Object.defineProperty;var D=(n,a,h)=>a in n?T(n,a,{enumerable:!0,configurable:!0,writable:!0,value:h}):n[a]=h;var i=(n,a,h)=>(D(n,typeof a!="symbol"?a+"":a,h),h);class u extends h{on(t,e,s){return super.on(t,e,s)}once(t,e,s){return super.once(t,e,s)}off(t,e,s){return super.off(t,e,s)}emit(t,...e){return super.emit(t,...e)}}class b{constructor(){i(this,"queue",[]);i(this,"maxSize",100)}enqueue(t,e=3){this.queue.length>=this.maxSize&&this.queue.shift();const s={...t,retryCount:0,maxRetries:e};return this.queue.push(s),t.messageId}dequeue(){return this.queue.shift()}hasMessages(){return this.queue.length>0}getMessageById(t){return this.queue.find(e=>e.messageId===t)}removeMessageById(t){const e=this.queue.findIndex(s=>s.messageId===t);e!==-1&&this.queue.splice(e,1)}clear(){this.queue=[]}}class A{constructor(t,e=!1){this.context=t,this.debug=e}formatMessage(t,...e){return[`[${new Date().toISOString()}] [${this.context}] [${t}]`,...e]}info(...t){console.info(...this.formatMessage("INFO",...t))}debug(...t){this.debug&&console.debug(...this.formatMessage("DEBUG",...t))}warn(...t){console.warn(...this.formatMessage("WARN",...t))}error(...t){console.error(...this.formatMessage("ERROR",...t))}}const f="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",p=m.customAlphabet(f,12);class I extends u{constructor(e,s={}){super();i(this,"connection",null);i(this,"iframe");i(this,"options");i(this,"messageQueue");i(this,"logger");i(this,"isConnected",!1);i(this,"reconnectAttempts",0);i(this,"maxReconnectAttempts",3);this.iframe=e,this.options={timeout:3e4,debug:!1,retryCount:3,retryDelay:1e3,childOrigin:"*",...s},this.messageQueue=new b,this.logger=new A("PenpalBridge",this.options.debug)}async connect(e={}){try{this.logger.info("正在建立连接..."),this.connection=a.connectToChild({iframe:this.iframe,methods:{ping:()=>"pong",emitEvent:(s,c)=>{this.emit(s,c)},...e},timeout:this.options.timeout,childOrigin:this.options.childOrigin}),await this.connection.promise,this.isConnected=!0,this.reconnectAttempts=0,await this.processQueuedMessages(),this.logger.info("连接建立成功"),this.emit("connected")}catch(s){throw this.logger.error("连接失败:",s),await this.handleReconnect(),s}}async send(e,s){var l;const c={action:e,params:s,timestamp:Date.now(),messageId:p()};if(!this.isConnected)throw this.messageQueue.enqueue(c),new Error("Connection not ready, message queued");try{if(this.logger.debug("发送消息:",c),!((l=this.connection)!=null&&l.child))throw new Error("连接未就绪");const d=await this.connection.child.handleMessage(c);return this.logger.debug("收到响应:",d),this.validateResponse(d)}catch(d){throw this.logger.error("发送消息失败:",d),this.messageQueue.enqueue(c),d}}async processQueuedMessages(){for(;this.messageQueue.hasMessages();){const e=this.messageQueue.dequeue();if(e)try{await this.send(e.action,e.params)}catch(s){this.logger.warn("队列消息发送失败:",s),this.messageQueue.enqueue(e)}}}async handleReconnect(){if(this.reconnectAttempts>=this.maxReconnectAttempts){this.logger.error("重连次数达到上限"),this.emit("connection-lost");return}this.reconnectAttempts++,this.logger.info(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})`),await new Promise(e=>setTimeout(e,this.options.retryDelay*this.reconnectAttempts));try{await this.connect()}catch{await this.handleReconnect()}}validateResponse(e){if(!e||e.success===void 0)throw new Error("Invalid response format");if(!e.success){const s=e.error||{code:"UNKNOWN",message:"Unknown error"};throw new Error(`${s.code}: ${s.message}`)}return e.data}disconnect(){this.connection&&(this.connection.destroy(),this.isConnected=!1,this.emit("disconnected"),this.logger.info("连接已断开"))}getConnectionStatus(){return this.isConnected}}class v{constructor(t){this.bridge=t}async initialize(t){return this.bridge.send("canvas.initialize",t)}async setData(t){return this.bridge.send("canvas.setData",t)}async getData(){return this.bridge.send("canvas.getData")}async clear(){return this.bridge.send("canvas.clear")}async undo(){return this.bridge.send("canvas.undo")}async redo(){return this.bridge.send("canvas.redo")}async export(t){return this.bridge.send("canvas.export",{format:t})}async save(){return this.bridge.send("canvas.save")}async invoke(t,...e){return this.bridge.send("canvas.invoke",{method:t,args:e})}async setZoom(t){return this.bridge.send("canvas.setZoom",{zoom:t})}async getSnapshot(){return this.bridge.send("canvas.getSnapshot")}}class y{constructor(t){this.bridge=t}async setAbilities(t){return this.bridge.send("abilities.set",t)}async getAbilities(){return this.bridge.send("abilities.get")}async enableAbility(t){return this.bridge.send("abilities.enable",{name:t})}async disableAbility(t){return this.bridge.send("abilities.disable",{name:t})}}class E{constructor(t){this.bridge=t}async setTheme(t){return this.bridge.send("theme.set",{theme:t})}async getCurrentTheme(){return this.bridge.send("theme.get")}async updateToken(t){return this.bridge.send("theme.updateToken",t)}}class P{constructor(t){this.bridge=t}async setLocale(t,e){return this.bridge.send("i18n.setLocale",{locale:t,messages:e})}async getCurrentLocale(){return this.bridge.send("i18n.getLocale")}async updateMessages(t){return this.bridge.send("i18n.updateMessages",t)}}class w{constructor(t){this.bridge=t}async validateToken(){return this.bridge.send("auth.validateToken")}}const o={READY:"ready",CHANGE:"change",SAVE:"save",ERROR:"error",SELECTION_CHANGED:"selection-changed",CONNECTED:"connected",DISCONNECTED:"disconnected",LOADED:"loaded",BEFORE_UNLOAD:"before-unload",OBJECT_ADDED:"object-added",OBJECT_MODIFIED:"object-modified",OBJECT_REMOVED:"object-removed",MOUSE_DOWN:"mouse-down",MOUSE_UP:"mouse-up",MOUSE_MOVE:"mouse-move",KEY_DOWN:"key-down",KEY_UP:"key-up"};class g extends u{constructor(e,s){super();i(this,"bridge");i(this,"canvasAPI");i(this,"abilityAPI");i(this,"themeAPI");i(this,"i18nAPI");i(this,"authAPI");i(this,"config");i(this,"iframe");i(this,"isInitialized",!1);this.validateConfig(s),this.config=s,this.iframe=e,this.bridge=new I(e,{debug:s.debug||!1,timeout:3e4,childOrigin:this.extractOrigin(e.src)}),this.canvasAPI=new v(this.bridge),this.abilityAPI=new y(this.bridge),this.themeAPI=new E(this.bridge),this.i18nAPI=new P(this.bridge),this.authAPI=new w(this.bridge),this.setupEventHandlers()}async init(){if(!this.isInitialized)try{await this.bridge.connect({getConfig:()=>this.config,log:(e,s)=>{console.log(`[Canvas ${e.toUpperCase()}]`,s)}}),await this.canvasAPI.initialize(this.config),this.config.abilities&&await this.abilityAPI.setAbilities(this.config.abilities),this.config.theme&&await this.themeAPI.setTheme(this.config.theme),this.config.locale&&await this.i18nAPI.setLocale(this.config.locale,this.config.messages),this.isInitialized=!0,this.emit(o.READY)}catch(e){throw this.emit(o.ERROR,e),e}}get canvas(){return{setData:e=>this.canvasAPI.setData(e),getData:()=>this.canvasAPI.getData(),clear:()=>this.canvasAPI.clear(),undo:()=>this.canvasAPI.undo(),redo:()=>this.canvasAPI.redo(),export:(e="png")=>this.canvasAPI.export(e),save:()=>this.canvasAPI.save(),setZoom:e=>this.canvasAPI.setZoom(e),getSnapshot:()=>this.canvasAPI.getSnapshot(),invoke:(e,...s)=>this.canvasAPI.invoke(e,...s)}}get abilities(){return{set:e=>this.abilityAPI.setAbilities(e),get:()=>this.abilityAPI.getAbilities(),enable:e=>this.abilityAPI.enableAbility(e),disable:e=>this.abilityAPI.disableAbility(e),updateConfig:(e,s)=>this.abilityAPI.updateAbilityConfig(e,s)}}get theme(){return{set:e=>this.themeAPI.setTheme(e),get:()=>this.themeAPI.getCurrentTheme(),updateToken:e=>this.themeAPI.updateToken(e),reset:()=>this.themeAPI.resetTheme()}}get i18n(){return{setLocale:(e,s)=>this.i18nAPI.setLocale(e,s),getLocale:()=>this.i18nAPI.getCurrentLocale(),updateMessages:e=>this.i18nAPI.updateMessages(e),getMessages:()=>this.i18nAPI.getMessages(),getTranslation:e=>this.i18nAPI.getTranslation(e)}}get auth(){return{validateToken:()=>this.authAPI.validateToken(),refreshToken:e=>this.authAPI.refreshToken(e),getTokenInfo:()=>this.authAPI.getTokenInfo()}}get utils(){return{getConnectionStatus:()=>this.bridge.getConnectionStatus(),disconnect:()=>this.bridge.disconnect(),reconnect:async()=>{this.bridge.disconnect(),this.isInitialized=!1,await this.init()},isInitialized:()=>this.isInitialized}}setupEventHandlers(){this.bridge.on("canvas-change",e=>{this.emit(o.CHANGE,e)}),this.bridge.on("canvas-save",e=>{this.emit(o.SAVE,e)}),this.bridge.on("canvas-error",e=>{this.emit(o.ERROR,e)}),this.bridge.on("canvas-selection-changed",e=>{this.emit(o.SELECTION_CHANGED,e)}),this.bridge.on("connected",()=>{this.emit(o.CONNECTED)}),this.bridge.on("disconnected",()=>{this.emit(o.DISCONNECTED)})}validateConfig(e){if(!e.token)throw new Error("Token is required");if(e.abilities){const s=["text","image","barcode","multiLayout","shape","qrCode"];Object.keys(e.abilities).forEach(c=>{s.includes(c)||console.warn(`Unknown ability: ${c}`)})}}extractOrigin(e){try{return new URL(e).origin}catch{return"*"}}}function C(r,t){return new g(r,t)}n.CANVAS_EVENTS=o,n.NiimbotCanvasSDK=g,n.createCanvas=C,Object.defineProperty(n,Symbol.toStringTag,{value:"Module"})});
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "niimbot-canvas-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Niimbot Canvas iframe communication SDK",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/niimbot-canvas-sdk.umd.js",
|
|
7
|
+
"module": "dist/niimbot-canvas-sdk.es.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/niimbot-canvas-sdk.es.js",
|
|
12
|
+
"require": "./dist/niimbot-canvas-sdk.umd.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./vue": {
|
|
16
|
+
"import": "./dist/vue.es.js",
|
|
17
|
+
"require": "./dist/vue.umd.js",
|
|
18
|
+
"types": "./dist/vue.d.ts"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"dev": "vite",
|
|
27
|
+
"build": "vite build",
|
|
28
|
+
"preview": "vite preview",
|
|
29
|
+
"test": "vitest",
|
|
30
|
+
"lint": "eslint src --ext .ts,.vue",
|
|
31
|
+
"prepare": "npm run build",
|
|
32
|
+
"release": "npm run build && npm publish --registry=http://your-private-npm.com"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"eventemitter3": "^5.0.1",
|
|
36
|
+
"nanoid": "^5.0.3",
|
|
37
|
+
"penpal": "^6.0.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
42
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
43
|
+
"@vitejs/plugin-vue": "^4.0.0",
|
|
44
|
+
"@vue/test-utils": "^2.0.0",
|
|
45
|
+
"eslint": "^8.45.0",
|
|
46
|
+
"eslint-plugin-vue": "^9.0.0",
|
|
47
|
+
"typescript": "^5.0.0",
|
|
48
|
+
"vite": "^4.0.0",
|
|
49
|
+
"vitest": "^0.34.0",
|
|
50
|
+
"vue": "^3.3.0",
|
|
51
|
+
"vue-tsc": "^1.8.0"
|
|
52
|
+
},
|
|
53
|
+
"keywords": [
|
|
54
|
+
"canvas",
|
|
55
|
+
"iframe",
|
|
56
|
+
"sdk",
|
|
57
|
+
"niimbot",
|
|
58
|
+
"design",
|
|
59
|
+
"editor"
|
|
60
|
+
],
|
|
61
|
+
"author": "Niimbot Team",
|
|
62
|
+
"license": "MIT"
|
|
63
|
+
}
|