@djvlc/sandbox 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.
- package/dist/index.cjs +631 -0
- package/dist/index.d.cts +406 -0
- package/dist/index.d.ts +406 -0
- package/dist/index.js +598 -0
- package/package.json +50 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
MessageType: () => MessageType,
|
|
24
|
+
SandboxClient: () => SandboxClient,
|
|
25
|
+
SandboxHost: () => SandboxHost,
|
|
26
|
+
createMessage: () => createMessage,
|
|
27
|
+
createResponse: () => createResponse,
|
|
28
|
+
generateMessageId: () => generateMessageId,
|
|
29
|
+
isDjvlcMessage: () => isDjvlcMessage
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/protocol.ts
|
|
34
|
+
var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
|
35
|
+
MessageType2["READY"] = "ready";
|
|
36
|
+
MessageType2["INIT"] = "init";
|
|
37
|
+
MessageType2["DESTROY"] = "destroy";
|
|
38
|
+
MessageType2["LOAD_PAGE"] = "load_page";
|
|
39
|
+
MessageType2["UPDATE_SCHEMA"] = "update_schema";
|
|
40
|
+
MessageType2["REFRESH"] = "refresh";
|
|
41
|
+
MessageType2["SELECT_COMPONENT"] = "select_component";
|
|
42
|
+
MessageType2["HOVER_COMPONENT"] = "hover_component";
|
|
43
|
+
MessageType2["UPDATE_COMPONENT"] = "update_component";
|
|
44
|
+
MessageType2["DELETE_COMPONENT"] = "delete_component";
|
|
45
|
+
MessageType2["ADD_COMPONENT"] = "add_component";
|
|
46
|
+
MessageType2["MOVE_COMPONENT"] = "move_component";
|
|
47
|
+
MessageType2["SYNC_STATE"] = "sync_state";
|
|
48
|
+
MessageType2["SYNC_VARIABLES"] = "sync_variables";
|
|
49
|
+
MessageType2["COMPONENT_CLICK"] = "component_click";
|
|
50
|
+
MessageType2["COMPONENT_HOVER"] = "component_hover";
|
|
51
|
+
MessageType2["COMPONENT_CONTEXT_MENU"] = "component_context_menu";
|
|
52
|
+
MessageType2["COMPONENT_DRAG_START"] = "component_drag_start";
|
|
53
|
+
MessageType2["COMPONENT_DRAG_END"] = "component_drag_end";
|
|
54
|
+
MessageType2["ERROR"] = "error";
|
|
55
|
+
MessageType2["LOG"] = "log";
|
|
56
|
+
return MessageType2;
|
|
57
|
+
})(MessageType || {});
|
|
58
|
+
function generateMessageId() {
|
|
59
|
+
return `msg_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
60
|
+
}
|
|
61
|
+
function createMessage(type, data) {
|
|
62
|
+
return {
|
|
63
|
+
type,
|
|
64
|
+
id: generateMessageId(),
|
|
65
|
+
data,
|
|
66
|
+
timestamp: Date.now()
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function createResponse(requestId, type, success, data, error) {
|
|
70
|
+
return {
|
|
71
|
+
type,
|
|
72
|
+
id: generateMessageId(),
|
|
73
|
+
requestId,
|
|
74
|
+
success,
|
|
75
|
+
data,
|
|
76
|
+
error,
|
|
77
|
+
timestamp: Date.now()
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function isDjvlcMessage(data) {
|
|
81
|
+
if (!data || typeof data !== "object") return false;
|
|
82
|
+
const msg = data;
|
|
83
|
+
return typeof msg.type === "string" && typeof msg.id === "string" && typeof msg.timestamp === "number";
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/sandbox-host.ts
|
|
87
|
+
var SandboxHost = class {
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
90
|
+
this.isReady = false;
|
|
91
|
+
// ==================== 私有方法 ====================
|
|
92
|
+
this.handleMessage = (event) => {
|
|
93
|
+
if (event.source !== this.options.iframe.contentWindow) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (this.options.targetOrigin !== "*" && event.origin !== this.options.targetOrigin) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const message = event.data;
|
|
100
|
+
if (!isDjvlcMessage(message)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.log("debug", "Received message:", message.type, message);
|
|
104
|
+
if ("requestId" in message) {
|
|
105
|
+
const pending = this.pendingRequests.get(message.requestId);
|
|
106
|
+
if (pending) {
|
|
107
|
+
clearTimeout(pending.timer);
|
|
108
|
+
this.pendingRequests.delete(message.requestId);
|
|
109
|
+
if (message.success) {
|
|
110
|
+
pending.resolve(message.data);
|
|
111
|
+
} else {
|
|
112
|
+
pending.reject(new Error(message.error || "Unknown error"));
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
switch (message.type) {
|
|
118
|
+
case "ready" /* READY */:
|
|
119
|
+
this.isReady = true;
|
|
120
|
+
this.readyResolve();
|
|
121
|
+
this.options.onReady?.();
|
|
122
|
+
break;
|
|
123
|
+
case "component_click" /* COMPONENT_CLICK */:
|
|
124
|
+
this.options.onComponentClick?.(
|
|
125
|
+
message.data.componentId,
|
|
126
|
+
message.data
|
|
127
|
+
);
|
|
128
|
+
break;
|
|
129
|
+
case "component_hover" /* COMPONENT_HOVER */:
|
|
130
|
+
this.options.onComponentHover?.(
|
|
131
|
+
message.data.componentId,
|
|
132
|
+
message.data
|
|
133
|
+
);
|
|
134
|
+
break;
|
|
135
|
+
case "component_context_menu" /* COMPONENT_CONTEXT_MENU */:
|
|
136
|
+
this.options.onComponentContextMenu?.(
|
|
137
|
+
message.data.componentId,
|
|
138
|
+
message.data
|
|
139
|
+
);
|
|
140
|
+
break;
|
|
141
|
+
case "sync_state" /* SYNC_STATE */:
|
|
142
|
+
this.options.onSyncState?.(message.data);
|
|
143
|
+
break;
|
|
144
|
+
case "error" /* ERROR */:
|
|
145
|
+
this.options.onError?.(message.data);
|
|
146
|
+
break;
|
|
147
|
+
case "log" /* LOG */: {
|
|
148
|
+
const logData = message.data;
|
|
149
|
+
this.options.onLog?.(logData.level, logData.message, ...logData.args || []);
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
this.options = {
|
|
155
|
+
timeout: 3e4,
|
|
156
|
+
debug: false,
|
|
157
|
+
...options
|
|
158
|
+
};
|
|
159
|
+
this.readyPromise = new Promise((resolve) => {
|
|
160
|
+
this.readyResolve = resolve;
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 连接到 iframe
|
|
165
|
+
*/
|
|
166
|
+
connect() {
|
|
167
|
+
window.addEventListener("message", this.handleMessage);
|
|
168
|
+
this.log("debug", "SandboxHost connected");
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 断开连接
|
|
172
|
+
*/
|
|
173
|
+
disconnect() {
|
|
174
|
+
window.removeEventListener("message", this.handleMessage);
|
|
175
|
+
this.pendingRequests.forEach(({ timer }) => clearTimeout(timer));
|
|
176
|
+
this.pendingRequests.clear();
|
|
177
|
+
this.isReady = false;
|
|
178
|
+
this.log("debug", "SandboxHost disconnected");
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* 等待就绪
|
|
182
|
+
*/
|
|
183
|
+
async waitReady() {
|
|
184
|
+
return this.readyPromise;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 检查是否就绪
|
|
188
|
+
*/
|
|
189
|
+
get ready() {
|
|
190
|
+
return this.isReady;
|
|
191
|
+
}
|
|
192
|
+
// ==================== 页面操作 ====================
|
|
193
|
+
/**
|
|
194
|
+
* 初始化
|
|
195
|
+
*/
|
|
196
|
+
async init(data) {
|
|
197
|
+
await this.send("init" /* INIT */, data);
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* 加载页面
|
|
201
|
+
*/
|
|
202
|
+
async loadPage(data) {
|
|
203
|
+
await this.send("load_page" /* LOAD_PAGE */, data);
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 更新 Schema
|
|
207
|
+
*/
|
|
208
|
+
async updateSchema(data) {
|
|
209
|
+
await this.send("update_schema" /* UPDATE_SCHEMA */, data);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 刷新
|
|
213
|
+
*/
|
|
214
|
+
async refresh() {
|
|
215
|
+
await this.send("refresh" /* REFRESH */, {});
|
|
216
|
+
}
|
|
217
|
+
// ==================== 组件操作 ====================
|
|
218
|
+
/**
|
|
219
|
+
* 选择组件
|
|
220
|
+
*/
|
|
221
|
+
async selectComponent(componentId, multi = false) {
|
|
222
|
+
await this.send("select_component" /* SELECT_COMPONENT */, {
|
|
223
|
+
componentId,
|
|
224
|
+
multi
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* 悬停组件
|
|
229
|
+
*/
|
|
230
|
+
async hoverComponent(componentId) {
|
|
231
|
+
await this.send("hover_component" /* HOVER_COMPONENT */, {
|
|
232
|
+
componentId
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 更新组件
|
|
237
|
+
*/
|
|
238
|
+
async updateComponent(data) {
|
|
239
|
+
await this.send("update_component" /* UPDATE_COMPONENT */, data);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* 添加组件
|
|
243
|
+
*/
|
|
244
|
+
async addComponent(data) {
|
|
245
|
+
await this.send("add_component" /* ADD_COMPONENT */, data);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* 移动组件
|
|
249
|
+
*/
|
|
250
|
+
async moveComponent(data) {
|
|
251
|
+
await this.send("move_component" /* MOVE_COMPONENT */, data);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* 删除组件
|
|
255
|
+
*/
|
|
256
|
+
async deleteComponent(componentId) {
|
|
257
|
+
await this.send("delete_component" /* DELETE_COMPONENT */, {
|
|
258
|
+
componentId
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// ==================== 状态同步 ====================
|
|
262
|
+
/**
|
|
263
|
+
* 同步变量
|
|
264
|
+
*/
|
|
265
|
+
async syncVariables(variables) {
|
|
266
|
+
await this.send("sync_variables" /* SYNC_VARIABLES */, { variables });
|
|
267
|
+
}
|
|
268
|
+
send(type, data) {
|
|
269
|
+
return new Promise((resolve, reject) => {
|
|
270
|
+
const message = createMessage(type, data);
|
|
271
|
+
const timer = setTimeout(() => {
|
|
272
|
+
this.pendingRequests.delete(message.id);
|
|
273
|
+
reject(new Error(`Request timeout: ${type}`));
|
|
274
|
+
}, this.options.timeout);
|
|
275
|
+
this.pendingRequests.set(message.id, { resolve, reject, timer });
|
|
276
|
+
this.log("debug", "Sending message:", type, message);
|
|
277
|
+
this.options.iframe.contentWindow?.postMessage(message, this.options.targetOrigin);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
log(level, message, ...args) {
|
|
281
|
+
if (this.options.debug || level !== "debug") {
|
|
282
|
+
console[level](`[SandboxHost] ${message}`, ...args);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
// src/sandbox-client.ts
|
|
288
|
+
var SandboxClient = class {
|
|
289
|
+
constructor(options = {}) {
|
|
290
|
+
this.runtime = null;
|
|
291
|
+
this.selectedComponents = /* @__PURE__ */ new Set();
|
|
292
|
+
this.hoveredComponent = null;
|
|
293
|
+
// ==================== 私有方法 ====================
|
|
294
|
+
this.handleMessage = async (event) => {
|
|
295
|
+
const message = event.data;
|
|
296
|
+
if (!isDjvlcMessage(message)) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
this.log("debug", "Received message:", message.type, message);
|
|
300
|
+
try {
|
|
301
|
+
let responseData;
|
|
302
|
+
switch (message.type) {
|
|
303
|
+
case "init" /* INIT */:
|
|
304
|
+
responseData = await this.handleInit(message.data);
|
|
305
|
+
break;
|
|
306
|
+
case "load_page" /* LOAD_PAGE */:
|
|
307
|
+
responseData = await this.handleLoadPage(message.data);
|
|
308
|
+
break;
|
|
309
|
+
case "update_schema" /* UPDATE_SCHEMA */:
|
|
310
|
+
responseData = await this.handleUpdateSchema(message.data);
|
|
311
|
+
break;
|
|
312
|
+
case "refresh" /* REFRESH */:
|
|
313
|
+
responseData = await this.handleRefresh();
|
|
314
|
+
break;
|
|
315
|
+
case "select_component" /* SELECT_COMPONENT */:
|
|
316
|
+
responseData = this.handleSelectComponent(message.data);
|
|
317
|
+
break;
|
|
318
|
+
case "hover_component" /* HOVER_COMPONENT */:
|
|
319
|
+
responseData = this.handleHoverComponent(message.data);
|
|
320
|
+
break;
|
|
321
|
+
case "update_component" /* UPDATE_COMPONENT */:
|
|
322
|
+
responseData = this.handleUpdateComponent(message.data);
|
|
323
|
+
break;
|
|
324
|
+
case "add_component" /* ADD_COMPONENT */:
|
|
325
|
+
responseData = this.handleAddComponent(message.data);
|
|
326
|
+
break;
|
|
327
|
+
case "move_component" /* MOVE_COMPONENT */:
|
|
328
|
+
responseData = this.handleMoveComponent(message.data);
|
|
329
|
+
break;
|
|
330
|
+
case "delete_component" /* DELETE_COMPONENT */:
|
|
331
|
+
responseData = this.handleDeleteComponent(message.data);
|
|
332
|
+
break;
|
|
333
|
+
case "sync_variables" /* SYNC_VARIABLES */:
|
|
334
|
+
responseData = this.handleSyncVariables(message.data);
|
|
335
|
+
break;
|
|
336
|
+
default:
|
|
337
|
+
this.log("warn", "Unknown message type:", message.type);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
this.sendResponse(message.id, message.type, true, responseData);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
this.sendResponse(
|
|
343
|
+
message.id,
|
|
344
|
+
message.type,
|
|
345
|
+
false,
|
|
346
|
+
void 0,
|
|
347
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
this.options = {
|
|
352
|
+
targetOrigin: "*",
|
|
353
|
+
debug: false,
|
|
354
|
+
...options
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 设置运行时实例
|
|
359
|
+
*/
|
|
360
|
+
setRuntime(runtime) {
|
|
361
|
+
this.runtime = runtime;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* 连接到父窗口
|
|
365
|
+
*/
|
|
366
|
+
connect() {
|
|
367
|
+
window.addEventListener("message", this.handleMessage);
|
|
368
|
+
this.log("debug", "SandboxClient connected");
|
|
369
|
+
this.sendReady();
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* 断开连接
|
|
373
|
+
*/
|
|
374
|
+
disconnect() {
|
|
375
|
+
window.removeEventListener("message", this.handleMessage);
|
|
376
|
+
this.log("debug", "SandboxClient disconnected");
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* 初始化组件事件监听
|
|
380
|
+
*/
|
|
381
|
+
initComponentEventListeners() {
|
|
382
|
+
document.addEventListener("click", (e) => {
|
|
383
|
+
const target = e.target;
|
|
384
|
+
const componentEl = this.findComponentElement(target);
|
|
385
|
+
if (componentEl) {
|
|
386
|
+
e.preventDefault();
|
|
387
|
+
e.stopPropagation();
|
|
388
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
389
|
+
const componentType = componentEl.getAttribute("data-component-type");
|
|
390
|
+
if (componentId) {
|
|
391
|
+
this.sendComponentEvent("component_click" /* COMPONENT_CLICK */, {
|
|
392
|
+
componentId,
|
|
393
|
+
componentType: componentType || "unknown",
|
|
394
|
+
bounds: componentEl.getBoundingClientRect(),
|
|
395
|
+
position: { x: e.clientX, y: e.clientY }
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}, true);
|
|
400
|
+
document.addEventListener("mouseover", (e) => {
|
|
401
|
+
const target = e.target;
|
|
402
|
+
const componentEl = this.findComponentElement(target);
|
|
403
|
+
if (componentEl) {
|
|
404
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
405
|
+
const componentType = componentEl.getAttribute("data-component-type");
|
|
406
|
+
if (componentId && componentId !== this.hoveredComponent) {
|
|
407
|
+
this.hoveredComponent = componentId;
|
|
408
|
+
this.sendComponentEvent("component_hover" /* COMPONENT_HOVER */, {
|
|
409
|
+
componentId,
|
|
410
|
+
componentType: componentType || "unknown",
|
|
411
|
+
bounds: componentEl.getBoundingClientRect()
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
document.addEventListener("mouseout", (e) => {
|
|
417
|
+
const relatedTarget = e.relatedTarget;
|
|
418
|
+
if (!relatedTarget || !this.findComponentElement(relatedTarget)) {
|
|
419
|
+
if (this.hoveredComponent) {
|
|
420
|
+
this.hoveredComponent = null;
|
|
421
|
+
this.sendComponentEvent("component_hover" /* COMPONENT_HOVER */, {
|
|
422
|
+
componentId: "",
|
|
423
|
+
componentType: ""
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
document.addEventListener("contextmenu", (e) => {
|
|
429
|
+
const target = e.target;
|
|
430
|
+
const componentEl = this.findComponentElement(target);
|
|
431
|
+
if (componentEl) {
|
|
432
|
+
e.preventDefault();
|
|
433
|
+
const componentId = componentEl.getAttribute("data-component-id");
|
|
434
|
+
const componentType = componentEl.getAttribute("data-component-type");
|
|
435
|
+
if (componentId) {
|
|
436
|
+
this.sendComponentEvent("component_context_menu" /* COMPONENT_CONTEXT_MENU */, {
|
|
437
|
+
componentId,
|
|
438
|
+
componentType: componentType || "unknown",
|
|
439
|
+
bounds: componentEl.getBoundingClientRect(),
|
|
440
|
+
position: { x: e.clientX, y: e.clientY }
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* 注入编辑器样式
|
|
448
|
+
*/
|
|
449
|
+
injectEditorStyles() {
|
|
450
|
+
const style = document.createElement("style");
|
|
451
|
+
style.textContent = `
|
|
452
|
+
/* \u9009\u4E2D\u72B6\u6001 */
|
|
453
|
+
[data-component-id].djvlc-selected {
|
|
454
|
+
outline: 2px solid #1890ff !important;
|
|
455
|
+
outline-offset: 2px;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/* \u60AC\u505C\u72B6\u6001 */
|
|
459
|
+
[data-component-id].djvlc-hovered {
|
|
460
|
+
outline: 1px dashed #1890ff !important;
|
|
461
|
+
outline-offset: 1px;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/* \u7981\u7528\u4EA4\u4E92 */
|
|
465
|
+
[data-component-id] * {
|
|
466
|
+
pointer-events: none;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
[data-component-id] {
|
|
470
|
+
cursor: pointer;
|
|
471
|
+
pointer-events: auto;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/* \u62D6\u62FD\u5360\u4F4D\u7B26 */
|
|
475
|
+
.djvlc-drop-indicator {
|
|
476
|
+
position: absolute;
|
|
477
|
+
background: #1890ff;
|
|
478
|
+
opacity: 0.5;
|
|
479
|
+
pointer-events: none;
|
|
480
|
+
z-index: 10000;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
.djvlc-drop-indicator.horizontal {
|
|
484
|
+
height: 4px;
|
|
485
|
+
width: 100%;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.djvlc-drop-indicator.vertical {
|
|
489
|
+
width: 4px;
|
|
490
|
+
height: 100%;
|
|
491
|
+
}
|
|
492
|
+
`;
|
|
493
|
+
document.head.appendChild(style);
|
|
494
|
+
}
|
|
495
|
+
async handleInit(data) {
|
|
496
|
+
this.log("info", "Init with config:", data);
|
|
497
|
+
}
|
|
498
|
+
async handleLoadPage(data) {
|
|
499
|
+
this.log("info", "Load page:", data.pageUid);
|
|
500
|
+
}
|
|
501
|
+
async handleUpdateSchema(data) {
|
|
502
|
+
this.log("info", "Update schema", data.fullUpdate ? "(full)" : "(partial)");
|
|
503
|
+
}
|
|
504
|
+
async handleRefresh() {
|
|
505
|
+
this.log("info", "Refresh page");
|
|
506
|
+
window.location.reload();
|
|
507
|
+
}
|
|
508
|
+
handleSelectComponent(data) {
|
|
509
|
+
if (data.componentId) {
|
|
510
|
+
if (data.multi) {
|
|
511
|
+
if (this.selectedComponents.has(data.componentId)) {
|
|
512
|
+
this.selectedComponents.delete(data.componentId);
|
|
513
|
+
} else {
|
|
514
|
+
this.selectedComponents.add(data.componentId);
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
this.selectedComponents.clear();
|
|
518
|
+
this.selectedComponents.add(data.componentId);
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
this.selectedComponents.clear();
|
|
522
|
+
}
|
|
523
|
+
this.updateSelectionStyles();
|
|
524
|
+
this.syncState();
|
|
525
|
+
}
|
|
526
|
+
handleHoverComponent(data) {
|
|
527
|
+
document.querySelectorAll(".djvlc-hovered").forEach((el) => {
|
|
528
|
+
el.classList.remove("djvlc-hovered");
|
|
529
|
+
});
|
|
530
|
+
if (data.componentId) {
|
|
531
|
+
const el = document.querySelector(`[data-component-id="${data.componentId}"]`);
|
|
532
|
+
if (el && !this.selectedComponents.has(data.componentId)) {
|
|
533
|
+
el.classList.add("djvlc-hovered");
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
handleUpdateComponent(data) {
|
|
538
|
+
this.log("info", "Update component:", data.componentId);
|
|
539
|
+
this.runtime?.updateComponent(data.componentId, data.props || {});
|
|
540
|
+
}
|
|
541
|
+
handleAddComponent(data) {
|
|
542
|
+
this.log("info", "Add component:", data.component.type);
|
|
543
|
+
}
|
|
544
|
+
handleMoveComponent(data) {
|
|
545
|
+
this.log("info", "Move component:", data.componentId, "to", data.targetIndex);
|
|
546
|
+
}
|
|
547
|
+
handleDeleteComponent(data) {
|
|
548
|
+
this.log("info", "Delete component:", data.componentId);
|
|
549
|
+
}
|
|
550
|
+
handleSyncVariables(data) {
|
|
551
|
+
Object.entries(data.variables).forEach(([key, value]) => {
|
|
552
|
+
this.runtime?.setVariable(key, value);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
updateSelectionStyles() {
|
|
556
|
+
document.querySelectorAll(".djvlc-selected").forEach((el) => {
|
|
557
|
+
el.classList.remove("djvlc-selected");
|
|
558
|
+
});
|
|
559
|
+
this.selectedComponents.forEach((id) => {
|
|
560
|
+
const el = document.querySelector(`[data-component-id="${id}"]`);
|
|
561
|
+
if (el) {
|
|
562
|
+
el.classList.add("djvlc-selected");
|
|
563
|
+
}
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
syncState() {
|
|
567
|
+
const state = {
|
|
568
|
+
phase: this.runtime?.getState().phase || "idle",
|
|
569
|
+
page: this.runtime?.getState().page ? {
|
|
570
|
+
pageUid: this.runtime.getState().page.pageUid,
|
|
571
|
+
pageVersionId: this.runtime.getState().page.pageVersionId
|
|
572
|
+
} : void 0,
|
|
573
|
+
selectedComponents: Array.from(this.selectedComponents),
|
|
574
|
+
hoveredComponent: this.hoveredComponent
|
|
575
|
+
};
|
|
576
|
+
this.send("sync_state" /* SYNC_STATE */, state);
|
|
577
|
+
}
|
|
578
|
+
sendReady() {
|
|
579
|
+
const data = {
|
|
580
|
+
runtimeVersion: "0.1.0",
|
|
581
|
+
capabilities: [
|
|
582
|
+
"select",
|
|
583
|
+
"hover",
|
|
584
|
+
"update",
|
|
585
|
+
"add",
|
|
586
|
+
"move",
|
|
587
|
+
"delete",
|
|
588
|
+
"drag"
|
|
589
|
+
]
|
|
590
|
+
};
|
|
591
|
+
this.send("ready" /* READY */, data);
|
|
592
|
+
}
|
|
593
|
+
sendComponentEvent(type, data) {
|
|
594
|
+
this.send(type, data);
|
|
595
|
+
}
|
|
596
|
+
send(type, data) {
|
|
597
|
+
const message = createMessage(type, data);
|
|
598
|
+
this.log("debug", "Sending message:", type, message);
|
|
599
|
+
window.parent.postMessage(message, this.options.targetOrigin || "*");
|
|
600
|
+
}
|
|
601
|
+
sendResponse(requestId, type, success, data, error) {
|
|
602
|
+
const response = createResponse(requestId, type, success, data, error);
|
|
603
|
+
this.log("debug", "Sending response:", type, response);
|
|
604
|
+
window.parent.postMessage(response, this.options.targetOrigin || "*");
|
|
605
|
+
}
|
|
606
|
+
findComponentElement(target) {
|
|
607
|
+
let current = target;
|
|
608
|
+
while (current) {
|
|
609
|
+
if (current.hasAttribute("data-component-id")) {
|
|
610
|
+
return current;
|
|
611
|
+
}
|
|
612
|
+
current = current.parentElement;
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
log(level, message, ...args) {
|
|
617
|
+
if (this.options.debug || level !== "debug") {
|
|
618
|
+
console[level](`[SandboxClient] ${message}`, ...args);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
};
|
|
622
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
623
|
+
0 && (module.exports = {
|
|
624
|
+
MessageType,
|
|
625
|
+
SandboxClient,
|
|
626
|
+
SandboxHost,
|
|
627
|
+
createMessage,
|
|
628
|
+
createResponse,
|
|
629
|
+
generateMessageId,
|
|
630
|
+
isDjvlcMessage
|
|
631
|
+
});
|