@orion-studios/payload-studio 0.6.0-beta.43 → 0.6.0-beta.44

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,628 @@
1
+ 'use client';
2
+ "use client";
3
+ import {
4
+ MAX_DIRECT_UPLOAD_BYTES,
5
+ optimizeImageForUpload
6
+ } from "../chunk-ROTPP5CU.mjs";
7
+
8
+ // src/builder-v2/editor/GrapesPageEditor.tsx
9
+ import { useEffect, useRef, useState } from "react";
10
+
11
+ // src/builder-v2/projectData.ts
12
+ var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
13
+ var createEmptyBuilderV2ProjectData = (title = "Untitled Page") => ({
14
+ assets: [],
15
+ pages: [
16
+ {
17
+ component: {
18
+ components: [
19
+ {
20
+ attributes: {
21
+ class: "orion-builder-v2-section"
22
+ },
23
+ components: [
24
+ {
25
+ content: title,
26
+ tagName: "h1",
27
+ type: "text"
28
+ },
29
+ {
30
+ content: "Start building this page by dragging blocks from the panel.",
31
+ tagName: "p",
32
+ type: "text"
33
+ }
34
+ ],
35
+ tagName: "section"
36
+ }
37
+ ],
38
+ type: "wrapper"
39
+ },
40
+ id: "page",
41
+ name: title
42
+ }
43
+ ],
44
+ styles: []
45
+ });
46
+ var normalizeBuilderV2ProjectData = (value, fallbackTitle = "Untitled Page") => {
47
+ if (!isRecord(value)) {
48
+ return createEmptyBuilderV2ProjectData(fallbackTitle);
49
+ }
50
+ return value;
51
+ };
52
+
53
+ // src/builder-v2/runtime/placeholders.ts
54
+ var placeholderPattern = /<(?<tag>[a-zA-Z][a-zA-Z0-9-]*)\b(?<attrs>[^>]*)data-orion-component=["'](?<component>[^"']+)["'](?<rest>[^>]*)>(?<content>.*?)<\/\k<tag>>/gis;
55
+ var attrPattern = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)=(?:"([^"]*)"|'([^']*)')/g;
56
+ var decodeHtmlAttribute = (value) => value.replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">");
57
+ var parseAttributes = (value) => {
58
+ const props = {};
59
+ let match;
60
+ while ((match = attrPattern.exec(value)) !== null) {
61
+ const name = match[1];
62
+ const rawValue = match[2] ?? match[3] ?? "";
63
+ if (!name.startsWith("data-")) {
64
+ continue;
65
+ }
66
+ if (name === "data-orion-component" || name === "data-orion-id") {
67
+ continue;
68
+ }
69
+ const propName = name.replace(/^data-orion-/, "").replace(/^data-/, "").replace(/-([a-z])/g, (_, char) => char.toUpperCase());
70
+ props[propName] = decodeHtmlAttribute(rawValue);
71
+ }
72
+ return props;
73
+ };
74
+ var extractAttribute = (attrs, name) => {
75
+ const pattern = new RegExp(`${name}=["']([^"']+)["']`, "i");
76
+ const match = attrs.match(pattern);
77
+ return match ? decodeHtmlAttribute(match[1]) : null;
78
+ };
79
+ var parseBuilderV2DynamicComponents = (html) => {
80
+ const instances = [];
81
+ let match;
82
+ while ((match = placeholderPattern.exec(html)) !== null) {
83
+ const attrs = `${match.groups?.attrs ?? ""} ${match.groups?.rest ?? ""}`;
84
+ const component = match.groups?.component ?? "";
85
+ if (!component) {
86
+ continue;
87
+ }
88
+ instances.push({
89
+ component,
90
+ id: extractAttribute(attrs, "data-orion-id") || `component-${instances.length + 1}`,
91
+ props: parseAttributes(attrs)
92
+ });
93
+ }
94
+ return instances;
95
+ };
96
+
97
+ // src/builder-v2/sanitize.ts
98
+ import sanitizeHtml from "sanitize-html";
99
+ var allowedTags = sanitizeHtml.defaults.allowedTags.concat([
100
+ "article",
101
+ "aside",
102
+ "button",
103
+ "figure",
104
+ "figcaption",
105
+ "footer",
106
+ "header",
107
+ "main",
108
+ "nav",
109
+ "section",
110
+ "source",
111
+ "video"
112
+ ]);
113
+ var allowedAttributes = {
114
+ ...sanitizeHtml.defaults.allowedAttributes,
115
+ "*": [
116
+ "aria-*",
117
+ "class",
118
+ "data-*",
119
+ "id",
120
+ "role",
121
+ "style",
122
+ "title"
123
+ ],
124
+ a: ["aria-*", "class", "data-*", "href", "id", "name", "rel", "style", "target", "title"],
125
+ button: ["aria-*", "class", "data-*", "disabled", "id", "style", "title", "type"],
126
+ iframe: ["allow", "allowfullscreen", "class", "data-*", "height", "loading", "src", "style", "title", "width"],
127
+ img: ["alt", "class", "data-*", "height", "id", "loading", "sizes", "src", "srcset", "style", "title", "width"],
128
+ source: ["media", "src", "srcset", "type"],
129
+ video: ["autoplay", "class", "controls", "height", "loop", "muted", "playsinline", "poster", "preload", "src", "style", "width"]
130
+ };
131
+ var allowedIframeHosts = [
132
+ "calendar.google.com",
133
+ "calendly.com",
134
+ "player.vimeo.com",
135
+ "www.youtube.com",
136
+ "youtube.com"
137
+ ];
138
+ var sanitizeBuilderHtml = (value) => {
139
+ if (typeof value !== "string" || value.trim().length === 0) {
140
+ return "";
141
+ }
142
+ return sanitizeHtml(value, {
143
+ allowedAttributes,
144
+ allowedIframeHostnames: allowedIframeHosts,
145
+ allowedSchemes: ["http", "https", "mailto", "tel"],
146
+ allowedSchemesByTag: {
147
+ img: ["http", "https", "data"]
148
+ },
149
+ allowedTags,
150
+ allowProtocolRelative: false,
151
+ parseStyleAttributes: false
152
+ });
153
+ };
154
+ var sanitizeBuilderCss = (value) => {
155
+ if (typeof value !== "string" || value.trim().length === 0) {
156
+ return "";
157
+ }
158
+ return value.replace(/@import\s+[^;]+;/gi, "").replace(/expression\s*\(/gi, "").replace(/javascript\s*:/gi, "");
159
+ };
160
+
161
+ // src/builder-v2/editor/defaultBlocks.ts
162
+ var registerOrionBuilderV2Blocks = (editor) => {
163
+ const blocks = editor.Blocks;
164
+ blocks.add("orion-section", {
165
+ category: "Layout",
166
+ content: `
167
+ <section class="orion-builder-v2-section">
168
+ <div class="orion-builder-v2-container">
169
+ <h2>Section Heading</h2>
170
+ <p>Add supporting content here.</p>
171
+ </div>
172
+ </section>
173
+ `,
174
+ label: "Section"
175
+ });
176
+ blocks.add("orion-hero", {
177
+ category: "Sections",
178
+ content: `
179
+ <section class="orion-builder-v2-hero">
180
+ <div class="orion-builder-v2-container">
181
+ <p class="orion-builder-v2-kicker">Optional kicker</p>
182
+ <h1>Build a stronger website</h1>
183
+ <p>Use this area for a clear value proposition and call to action.</p>
184
+ <a class="orion-builder-v2-button" href="/contact">Get started</a>
185
+ </div>
186
+ </section>
187
+ `,
188
+ label: "Hero"
189
+ });
190
+ blocks.add("orion-columns", {
191
+ category: "Layout",
192
+ content: `
193
+ <section class="orion-builder-v2-section">
194
+ <div class="orion-builder-v2-container orion-builder-v2-grid is-3">
195
+ <article class="orion-builder-v2-card"><h3>Column One</h3><p>Add content.</p></article>
196
+ <article class="orion-builder-v2-card"><h3>Column Two</h3><p>Add content.</p></article>
197
+ <article class="orion-builder-v2-card"><h3>Column Three</h3><p>Add content.</p></article>
198
+ </div>
199
+ </section>
200
+ `,
201
+ label: "Columns"
202
+ });
203
+ blocks.add("orion-button", {
204
+ category: "Basic",
205
+ content: '<a class="orion-builder-v2-button" href="/contact">Button</a>',
206
+ label: "Button"
207
+ });
208
+ blocks.add("orion-image", {
209
+ category: "Basic",
210
+ content: '<img alt="Image" class="orion-builder-v2-image" src="https://placehold.co/1200x700" />',
211
+ label: "Image"
212
+ });
213
+ blocks.add("orion-form-embed", {
214
+ category: "Dynamic",
215
+ content: `
216
+ <div
217
+ class="orion-builder-v2-dynamic-placeholder"
218
+ data-orion-component="formEmbed"
219
+ data-orion-form-slug="contact"
220
+ >
221
+ <strong>Form Embed</strong>
222
+ <span>contact</span>
223
+ </div>
224
+ `,
225
+ label: "Form"
226
+ });
227
+ };
228
+
229
+ // src/builder-v2/editor/projectComponents.ts
230
+ var normalizeDefinition = (type, value) => {
231
+ if (!value || typeof value === "function") {
232
+ return null;
233
+ }
234
+ return {
235
+ ...value,
236
+ type: value.type || type
237
+ };
238
+ };
239
+ var registerProjectDynamicComponents = (editor, adapter) => {
240
+ const components = adapter?.components || {};
241
+ Object.keys(components).forEach((type) => {
242
+ const definition = normalizeDefinition(type, components[type]);
243
+ if (!definition) {
244
+ return;
245
+ }
246
+ editor.DomComponents.addType(`orion-${type}`, {
247
+ model: {
248
+ defaults: {
249
+ attributes: {
250
+ "data-orion-component": type,
251
+ "data-orion-id": `${type}-${Date.now()}`
252
+ },
253
+ components: definition.editorPreview?.({}) || `<div class="orion-builder-v2-dynamic-placeholder"><strong>${definition.label}</strong></div>`,
254
+ droppable: false,
255
+ tagName: "div",
256
+ traits: [
257
+ {
258
+ label: "Component",
259
+ name: "data-orion-component",
260
+ type: "text"
261
+ },
262
+ ...(definition.traits || []).map((trait) => ({
263
+ label: trait.label,
264
+ name: `data-orion-${trait.name.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`)}`,
265
+ options: trait.options,
266
+ placeholder: trait.placeholder,
267
+ type: trait.type
268
+ }))
269
+ ]
270
+ }
271
+ }
272
+ });
273
+ editor.Blocks.add(`orion-dynamic-${type}`, {
274
+ category: "Project",
275
+ content: {
276
+ type: `orion-${type}`
277
+ },
278
+ label: definition.label
279
+ });
280
+ });
281
+ };
282
+
283
+ // src/builder-v2/editor/GrapesPageEditor.tsx
284
+ import { jsx, jsxs } from "react/jsx-runtime";
285
+ var postToParent = (payload) => {
286
+ window.parent?.postMessage(
287
+ {
288
+ source: "payload-visual-builder-child",
289
+ ...payload
290
+ },
291
+ "*"
292
+ );
293
+ };
294
+ var buildSavePayload = (editor, status, projectData) => ({
295
+ builderMode: "grapes-v2",
296
+ compiledCss: sanitizeBuilderCss(editor.getCss()),
297
+ compiledHtml: sanitizeBuilderHtml(editor.getHtml()),
298
+ projectData,
299
+ status
300
+ });
301
+ var parsePayloadErrorMessage = async (response, fallback) => {
302
+ try {
303
+ const json = await response.json();
304
+ return json.errors?.[0]?.message || json.message || fallback;
305
+ } catch {
306
+ const raw = await response.text();
307
+ return raw.trim() || fallback;
308
+ }
309
+ };
310
+ var getRelationID = (value) => {
311
+ if (typeof value === "number" || typeof value === "string") {
312
+ return value;
313
+ }
314
+ if (!value || typeof value !== "object") {
315
+ return null;
316
+ }
317
+ const id = value.id;
318
+ return typeof id === "number" || typeof id === "string" ? id : null;
319
+ };
320
+ var mediaDocToAsset = (doc) => {
321
+ const id = getRelationID(doc);
322
+ const filename = typeof doc.filename === "string" ? doc.filename : "";
323
+ const src = typeof doc.url === "string" && doc.url.length > 0 ? doc.url : filename ? `/api/media/file/${encodeURIComponent(filename)}` : "";
324
+ if (id === null || !src) {
325
+ return null;
326
+ }
327
+ return {
328
+ alt: typeof doc.alt === "string" ? doc.alt : "",
329
+ id,
330
+ name: filename || String(id),
331
+ src,
332
+ type: "image"
333
+ };
334
+ };
335
+ var extractUploadedMedia = (value) => {
336
+ const candidate = value && typeof value === "object" && "doc" in value ? value.doc : value;
337
+ if (!candidate || typeof candidate !== "object") {
338
+ return null;
339
+ }
340
+ const id = getRelationID(candidate);
341
+ if (id === null) {
342
+ return null;
343
+ }
344
+ const typed = candidate;
345
+ return {
346
+ alt: typeof typed.alt === "string" ? typed.alt : "",
347
+ filename: typeof typed.filename === "string" ? typed.filename : "",
348
+ id,
349
+ url: typeof typed.url === "string" ? typed.url : ""
350
+ };
351
+ };
352
+ var loadPayloadMediaAssets = async (editor) => {
353
+ const response = await fetch(`/api/media?depth=1&limit=100&sort=-updatedAt&_=${Date.now()}`, {
354
+ cache: "no-store",
355
+ credentials: "include"
356
+ });
357
+ if (!response.ok) {
358
+ return;
359
+ }
360
+ const json = await response.json();
361
+ const assets = (Array.isArray(json.docs) ? json.docs : []).map((doc) => mediaDocToAsset(doc)).filter((asset) => asset !== null);
362
+ editor.AssetManager.add(assets);
363
+ };
364
+ var uploadPayloadMediaAssets = async (editor, files) => {
365
+ const fileArray = Array.from(files);
366
+ const uploadedAssets = [];
367
+ for (const file of fileArray) {
368
+ const optimizedFile = await optimizeImageForUpload(file);
369
+ if (optimizedFile.size > MAX_DIRECT_UPLOAD_BYTES) {
370
+ throw new Error("Image is too large. Use an image under 4MB or lower-resolution export.");
371
+ }
372
+ const fallbackAlt = file.name.replace(/\.[^/.]+$/, "").trim();
373
+ const formData = new FormData();
374
+ formData.set("_payload", JSON.stringify({ alt: fallbackAlt || "Uploaded image" }));
375
+ formData.set("alt", fallbackAlt || "Uploaded image");
376
+ formData.set("file", optimizedFile);
377
+ const response = await fetch("/api/media", {
378
+ body: formData,
379
+ credentials: "include",
380
+ method: "POST"
381
+ });
382
+ if (!response.ok) {
383
+ throw new Error(await parsePayloadErrorMessage(response, "Could not upload image."));
384
+ }
385
+ const uploaded = extractUploadedMedia(await response.json());
386
+ const asset = uploaded ? mediaDocToAsset(uploaded) : null;
387
+ if (asset) {
388
+ uploadedAssets.push(asset);
389
+ }
390
+ }
391
+ if (uploadedAssets.length > 0) {
392
+ editor.AssetManager.add(uploadedAssets);
393
+ }
394
+ };
395
+ function GrapesPageEditor({ adapter, initialData, pageID }) {
396
+ const containerRef = useRef(null);
397
+ const editorRef = useRef(null);
398
+ const [error, setError] = useState("");
399
+ const [loading, setLoading] = useState(true);
400
+ const [saving, setSaving] = useState(null);
401
+ useEffect(() => {
402
+ let active = true;
403
+ const init = async () => {
404
+ if (!containerRef.current || editorRef.current) {
405
+ return;
406
+ }
407
+ try {
408
+ const grapesjs = (await import("grapesjs")).default;
409
+ if (!active || !containerRef.current) {
410
+ return;
411
+ }
412
+ const projectData = normalizeBuilderV2ProjectData(initialData?.projectData, initialData?.title);
413
+ const editor = grapesjs.init({
414
+ assetManager: {
415
+ uploadFile: async (event) => {
416
+ const target = event.target;
417
+ const dataTransfer = "dataTransfer" in event ? event.dataTransfer : null;
418
+ const files = dataTransfer?.files || target?.files;
419
+ if (!files || files.length === 0) {
420
+ return void 0;
421
+ }
422
+ await uploadPayloadMediaAssets(editorRef.current || editor, files).catch((uploadError) => {
423
+ setError(uploadError instanceof Error ? uploadError.message : "Could not upload image.");
424
+ });
425
+ }
426
+ },
427
+ blockManager: {
428
+ appendTo: "#orion-builder-v2-blocks"
429
+ },
430
+ container: containerRef.current,
431
+ deviceManager: {
432
+ devices: [
433
+ { id: "desktop", name: "Desktop", width: "" },
434
+ { id: "tablet", name: "Tablet", width: "768px" },
435
+ { id: "mobile", name: "Mobile", width: "390px" }
436
+ ]
437
+ },
438
+ fromElement: false,
439
+ height: "100%",
440
+ panels: {
441
+ defaults: []
442
+ },
443
+ projectData,
444
+ selectorManager: {
445
+ componentFirst: true
446
+ },
447
+ storageManager: false,
448
+ styleManager: {
449
+ sectors: [
450
+ {
451
+ name: "Layout",
452
+ open: true,
453
+ properties: ["display", "position", "top", "right", "bottom", "left", "width", "height", "min-height", "margin", "padding"]
454
+ },
455
+ {
456
+ name: "Typography",
457
+ open: true,
458
+ properties: ["font-family", "font-size", "font-weight", "line-height", "letter-spacing", "color", "text-align"]
459
+ },
460
+ {
461
+ name: "Decoration",
462
+ open: true,
463
+ properties: ["background-color", "background", "border", "border-radius", "box-shadow", "opacity"]
464
+ },
465
+ {
466
+ name: "Flex",
467
+ open: false,
468
+ properties: ["flex-direction", "justify-content", "align-items", "gap", "flex-wrap"]
469
+ }
470
+ ]
471
+ },
472
+ traitManager: {
473
+ appendTo: "#orion-builder-v2-traits"
474
+ },
475
+ width: "auto"
476
+ });
477
+ editorRef.current = editor;
478
+ registerOrionBuilderV2Blocks(editor);
479
+ registerProjectDynamicComponents(editor, adapter);
480
+ editor.loadProjectData(projectData);
481
+ void loadPayloadMediaAssets(editor);
482
+ editor.on("update", () => {
483
+ postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
484
+ postToParent({
485
+ canRedo: editor.UndoManager.hasRedo(),
486
+ canUndo: editor.UndoManager.hasUndo(),
487
+ type: "history-state"
488
+ });
489
+ });
490
+ setLoading(false);
491
+ } catch (initError) {
492
+ setError(initError instanceof Error ? initError.message : "Could not load the website builder.");
493
+ setLoading(false);
494
+ }
495
+ };
496
+ void init();
497
+ return () => {
498
+ active = false;
499
+ editorRef.current?.destroy();
500
+ editorRef.current = null;
501
+ };
502
+ }, [adapter, initialData?.projectData, initialData?.title]);
503
+ const save = async (status) => {
504
+ const editor = editorRef.current;
505
+ if (!editor || saving) {
506
+ return;
507
+ }
508
+ setSaving(status);
509
+ try {
510
+ const projectData = editor.getProjectData();
511
+ const payload = buildSavePayload(editor, status, projectData);
512
+ const dynamicComponents = parseBuilderV2DynamicComponents(String(payload.compiledHtml || ""));
513
+ const endpoint = status === "draft" ? `/api/pages/${pageID}?draft=true` : `/api/pages/${pageID}`;
514
+ const response = await fetch(endpoint, {
515
+ body: JSON.stringify(
516
+ status === "published" ? {
517
+ _status: "published",
518
+ builderDynamicComponents: dynamicComponents,
519
+ builderMode: "grapes-v2",
520
+ builderProjectData: projectData,
521
+ builderPublishedProjectData: projectData,
522
+ builderValidationIssues: [],
523
+ compiledCss: payload.compiledCss,
524
+ compiledHtml: payload.compiledHtml
525
+ } : {
526
+ _status: "draft",
527
+ builderDynamicComponents: dynamicComponents,
528
+ builderMode: "grapes-v2",
529
+ builderProjectData: projectData,
530
+ builderValidationIssues: [],
531
+ compiledCss: payload.compiledCss,
532
+ compiledHtml: payload.compiledHtml
533
+ }
534
+ ),
535
+ credentials: "include",
536
+ headers: {
537
+ "Content-Type": "application/json"
538
+ },
539
+ method: "PATCH"
540
+ });
541
+ if (!response.ok) {
542
+ if (response.status === 401 || response.status === 403) {
543
+ postToParent({ type: "session-expired" });
544
+ }
545
+ throw new Error(await parsePayloadErrorMessage(response, "Could not save this page."));
546
+ }
547
+ editor.clearDirtyCount();
548
+ postToParent({
549
+ dirty: false,
550
+ type: "dirty-state"
551
+ });
552
+ postToParent({
553
+ message: status === "published" ? "Published." : "Draft saved.",
554
+ ok: true,
555
+ status,
556
+ type: "save-result"
557
+ });
558
+ } catch (saveError) {
559
+ postToParent({
560
+ message: saveError instanceof Error ? saveError.message : "Could not save.",
561
+ ok: false,
562
+ status,
563
+ type: "save-result"
564
+ });
565
+ } finally {
566
+ setSaving(null);
567
+ }
568
+ };
569
+ useEffect(() => {
570
+ const onMessage = (event) => {
571
+ const data = event.data;
572
+ const editor = editorRef.current;
573
+ if (!data || data.source !== "payload-visual-builder-parent" || !editor) {
574
+ return;
575
+ }
576
+ if (data.type === "dirty-check-request") {
577
+ postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
578
+ postToParent({
579
+ canRedo: editor.UndoManager.hasRedo(),
580
+ canUndo: editor.UndoManager.hasUndo(),
581
+ type: "history-state"
582
+ });
583
+ return;
584
+ }
585
+ if (data.type === "history-check-request") {
586
+ postToParent({
587
+ canRedo: editor.UndoManager.hasRedo(),
588
+ canUndo: editor.UndoManager.hasUndo(),
589
+ type: "history-state"
590
+ });
591
+ return;
592
+ }
593
+ if (data.type === "undo") {
594
+ editor.UndoManager.undo();
595
+ return;
596
+ }
597
+ if (data.type === "redo") {
598
+ editor.UndoManager.redo();
599
+ return;
600
+ }
601
+ if (data.type === "save" && (data.status === "draft" || data.status === "published")) {
602
+ void save(data.status);
603
+ }
604
+ };
605
+ window.addEventListener("message", onMessage);
606
+ return () => window.removeEventListener("message", onMessage);
607
+ }, [saving]);
608
+ return /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-editor", children: [
609
+ /* @__PURE__ */ jsxs("aside", { className: "orion-builder-v2-sidebar", children: [
610
+ /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
611
+ /* @__PURE__ */ jsx("h2", { children: "Blocks" }),
612
+ /* @__PURE__ */ jsx("div", { id: "orion-builder-v2-blocks" })
613
+ ] }),
614
+ /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
615
+ /* @__PURE__ */ jsx("h2", { children: "Settings" }),
616
+ /* @__PURE__ */ jsx("div", { id: "orion-builder-v2-traits" })
617
+ ] })
618
+ ] }),
619
+ /* @__PURE__ */ jsxs("main", { className: "orion-builder-v2-main", children: [
620
+ loading ? /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-status", children: "Loading builder..." }) : null,
621
+ error ? /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-error", children: error }) : null,
622
+ /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-canvas", ref: containerRef })
623
+ ] })
624
+ ] });
625
+ }
626
+ export {
627
+ GrapesPageEditor
628
+ };