@orion-studios/payload-studio 0.6.0-beta.45 → 0.6.0-beta.47
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/builder-v2/client.d.mts +5 -0
- package/dist/builder-v2/client.d.ts +5 -0
- package/dist/builder-v2/client.js +382 -24
- package/dist/builder-v2/client.mjs +382 -24
- package/dist/builder-v2/index.d.mts +81 -1
- package/dist/builder-v2/index.d.ts +81 -1
- package/dist/builder-v2/index.js +281 -7
- package/dist/builder-v2/index.mjs +276 -6
- package/dist/builder-v2/styles.css +625 -10
- package/package.json +1 -1
|
@@ -132,6 +132,7 @@ var allowedIframeHosts = [
|
|
|
132
132
|
"calendar.google.com",
|
|
133
133
|
"calendly.com",
|
|
134
134
|
"player.vimeo.com",
|
|
135
|
+
"www.google.com",
|
|
135
136
|
"www.youtube.com",
|
|
136
137
|
"youtube.com"
|
|
137
138
|
];
|
|
@@ -157,10 +158,127 @@ var sanitizeBuilderCss = (value) => {
|
|
|
157
158
|
}
|
|
158
159
|
return value.replace(/@import\s+[^;]+;/gi, "").replace(/expression\s*\(/gi, "").replace(/javascript\s*:/gi, "");
|
|
159
160
|
};
|
|
161
|
+
var scopeSelector = (selector, scope) => selector.split(",").map((part) => {
|
|
162
|
+
const trimmed = part.trim();
|
|
163
|
+
if (!trimmed || trimmed.startsWith(scope) || trimmed.startsWith("@")) {
|
|
164
|
+
return trimmed;
|
|
165
|
+
}
|
|
166
|
+
if (/^(html|body|:root)\b/i.test(trimmed)) {
|
|
167
|
+
return trimmed.replace(/^(html|body|:root)\b/i, scope);
|
|
168
|
+
}
|
|
169
|
+
return `${scope} ${trimmed}`;
|
|
170
|
+
}).filter(Boolean).join(", ");
|
|
171
|
+
var scopeBuilderCss = (value, scope = ".orion-builder-v2-runtime") => {
|
|
172
|
+
const css = sanitizeBuilderCss(value);
|
|
173
|
+
if (!css) {
|
|
174
|
+
return "";
|
|
175
|
+
}
|
|
176
|
+
let output = "";
|
|
177
|
+
let cursor = 0;
|
|
178
|
+
const rulePattern = /([^{}]+)\{/g;
|
|
179
|
+
let match;
|
|
180
|
+
while ((match = rulePattern.exec(css)) !== null) {
|
|
181
|
+
const selectorStart = match.index;
|
|
182
|
+
const selector = match[1];
|
|
183
|
+
output += css.slice(cursor, selectorStart);
|
|
184
|
+
const trimmedSelector = selector.trim();
|
|
185
|
+
if (trimmedSelector.startsWith("@keyframes") || trimmedSelector.startsWith("@font-face") || trimmedSelector.startsWith("@page")) {
|
|
186
|
+
output += `${selector}{`;
|
|
187
|
+
} else if (trimmedSelector.startsWith("@media") || trimmedSelector.startsWith("@supports")) {
|
|
188
|
+
output += `${selector}{`;
|
|
189
|
+
} else {
|
|
190
|
+
output += `${scopeSelector(selector, scope)} {`;
|
|
191
|
+
}
|
|
192
|
+
cursor = rulePattern.lastIndex;
|
|
193
|
+
}
|
|
194
|
+
output += css.slice(cursor);
|
|
195
|
+
return output;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// src/builder-v2/validation.ts
|
|
199
|
+
var hasMatch = (value, pattern) => pattern.test(value);
|
|
200
|
+
var validateBuilderV2Output = (input) => {
|
|
201
|
+
const html = typeof input.html === "string" ? input.html : "";
|
|
202
|
+
const css = typeof input.css === "string" ? input.css : "";
|
|
203
|
+
const issues = [];
|
|
204
|
+
if (!html.trim()) {
|
|
205
|
+
issues.push({
|
|
206
|
+
code: "empty-page",
|
|
207
|
+
message: "This page has no rendered content.",
|
|
208
|
+
path: "compiledHtml",
|
|
209
|
+
severity: "error"
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (!hasMatch(html, /<h1\b/i)) {
|
|
213
|
+
issues.push({
|
|
214
|
+
code: "missing-h1",
|
|
215
|
+
message: "Add one H1 so the published page has a clear primary heading.",
|
|
216
|
+
path: "compiledHtml",
|
|
217
|
+
severity: "warning"
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (hasMatch(html, /\son[a-z]+\s*=/i)) {
|
|
221
|
+
issues.push({
|
|
222
|
+
code: "inline-event-handler",
|
|
223
|
+
message: "Inline event handlers are not allowed in published builder HTML.",
|
|
224
|
+
path: "compiledHtml",
|
|
225
|
+
severity: "error"
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
if (hasMatch(html, /<(script|object|embed)\b/i)) {
|
|
229
|
+
issues.push({
|
|
230
|
+
code: "unsafe-element",
|
|
231
|
+
message: "Script, object, and embed tags are not allowed in builder content.",
|
|
232
|
+
path: "compiledHtml",
|
|
233
|
+
severity: "error"
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (hasMatch(html, /\b(?:href|src)=["']\s*javascript:/i)) {
|
|
237
|
+
issues.push({
|
|
238
|
+
code: "unsafe-url",
|
|
239
|
+
message: "Links and media cannot use javascript URLs.",
|
|
240
|
+
path: "compiledHtml",
|
|
241
|
+
severity: "error"
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (hasMatch(css, /@import\s/i)) {
|
|
245
|
+
issues.push({
|
|
246
|
+
code: "css-import",
|
|
247
|
+
message: "CSS imports are removed at publish time. Use the site font and theme managers instead.",
|
|
248
|
+
path: "compiledCss",
|
|
249
|
+
severity: "warning"
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
if (hasMatch(css, /position\s*:\s*fixed/i)) {
|
|
253
|
+
issues.push({
|
|
254
|
+
code: "fixed-position",
|
|
255
|
+
message: "Fixed positioning can cover site navigation or dialogs. Review this before publishing.",
|
|
256
|
+
path: "compiledCss",
|
|
257
|
+
severity: "warning"
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
return issues;
|
|
261
|
+
};
|
|
262
|
+
var hasBlockingBuilderV2Issues = (issues) => issues.some((issue) => issue.severity === "error");
|
|
160
263
|
|
|
161
264
|
// src/builder-v2/editor/defaultBlocks.ts
|
|
162
265
|
var registerOrionBuilderV2Blocks = (editor) => {
|
|
163
266
|
const blocks = editor.Blocks;
|
|
267
|
+
blocks.add("orion-nav", {
|
|
268
|
+
category: "Chrome",
|
|
269
|
+
content: `
|
|
270
|
+
<header class="orion-builder-v2-nav">
|
|
271
|
+
<a class="orion-builder-v2-logo" href="/">Brand</a>
|
|
272
|
+
<nav aria-label="Primary">
|
|
273
|
+
<a href="/">Home</a>
|
|
274
|
+
<a href="/about">About</a>
|
|
275
|
+
<a href="/contact">Contact</a>
|
|
276
|
+
</nav>
|
|
277
|
+
<a class="orion-builder-v2-button" href="/contact">Start</a>
|
|
278
|
+
</header>
|
|
279
|
+
`,
|
|
280
|
+
label: "Nav"
|
|
281
|
+
});
|
|
164
282
|
blocks.add("orion-section", {
|
|
165
283
|
category: "Layout",
|
|
166
284
|
content: `
|
|
@@ -200,6 +318,89 @@ var registerOrionBuilderV2Blocks = (editor) => {
|
|
|
200
318
|
`,
|
|
201
319
|
label: "Columns"
|
|
202
320
|
});
|
|
321
|
+
blocks.add("orion-card-grid", {
|
|
322
|
+
category: "Sections",
|
|
323
|
+
content: `
|
|
324
|
+
<section class="orion-builder-v2-section">
|
|
325
|
+
<div class="orion-builder-v2-container">
|
|
326
|
+
<p class="orion-builder-v2-kicker">Featured</p>
|
|
327
|
+
<h2>Cards that explain the offer</h2>
|
|
328
|
+
<div class="orion-builder-v2-grid is-3">
|
|
329
|
+
<article class="orion-builder-v2-card"><h3>Service One</h3><p>Describe the outcome clients care about.</p><a href="/contact">Learn more</a></article>
|
|
330
|
+
<article class="orion-builder-v2-card"><h3>Service Two</h3><p>Keep the card concise and scannable.</p><a href="/contact">Learn more</a></article>
|
|
331
|
+
<article class="orion-builder-v2-card"><h3>Service Three</h3><p>Use the same structure for visual rhythm.</p><a href="/contact">Learn more</a></article>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</section>
|
|
335
|
+
`,
|
|
336
|
+
label: "Card grid"
|
|
337
|
+
});
|
|
338
|
+
blocks.add("orion-gallery", {
|
|
339
|
+
category: "Media",
|
|
340
|
+
content: `
|
|
341
|
+
<section class="orion-builder-v2-section">
|
|
342
|
+
<div class="orion-builder-v2-container">
|
|
343
|
+
<h2>Gallery</h2>
|
|
344
|
+
<div class="orion-builder-v2-gallery">
|
|
345
|
+
<img alt="Gallery item" src="https://placehold.co/900x700" />
|
|
346
|
+
<img alt="Gallery item" src="https://placehold.co/900x700" />
|
|
347
|
+
<img alt="Gallery item" src="https://placehold.co/900x700" />
|
|
348
|
+
<img alt="Gallery item" src="https://placehold.co/900x700" />
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
</section>
|
|
352
|
+
`,
|
|
353
|
+
label: "Gallery"
|
|
354
|
+
});
|
|
355
|
+
blocks.add("orion-testimonials", {
|
|
356
|
+
category: "Sections",
|
|
357
|
+
content: `
|
|
358
|
+
<section class="orion-builder-v2-section is-muted">
|
|
359
|
+
<div class="orion-builder-v2-container">
|
|
360
|
+
<p class="orion-builder-v2-kicker">Testimonials</p>
|
|
361
|
+
<h2>What clients say</h2>
|
|
362
|
+
<div class="orion-builder-v2-grid is-3">
|
|
363
|
+
<blockquote class="orion-builder-v2-card"><p>"A clear, warm experience from start to finish."</p><cite>Client Name</cite></blockquote>
|
|
364
|
+
<blockquote class="orion-builder-v2-card"><p>"Exactly what we needed, without friction."</p><cite>Client Name</cite></blockquote>
|
|
365
|
+
<blockquote class="orion-builder-v2-card"><p>"The site made every next step obvious."</p><cite>Client Name</cite></blockquote>
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
</section>
|
|
369
|
+
`,
|
|
370
|
+
label: "Testimonials"
|
|
371
|
+
});
|
|
372
|
+
blocks.add("orion-faq", {
|
|
373
|
+
category: "Sections",
|
|
374
|
+
content: `
|
|
375
|
+
<section class="orion-builder-v2-section">
|
|
376
|
+
<div class="orion-builder-v2-container is-narrow">
|
|
377
|
+
<p class="orion-builder-v2-kicker">FAQ</p>
|
|
378
|
+
<h2>Questions answered</h2>
|
|
379
|
+
<details open><summary>What should visitors know first?</summary><p>Give a short, useful answer that removes hesitation.</p></details>
|
|
380
|
+
<details><summary>How does the process work?</summary><p>Explain the next step clearly.</p></details>
|
|
381
|
+
<details><summary>How do we get started?</summary><p>Point people to the strongest call to action.</p></details>
|
|
382
|
+
</div>
|
|
383
|
+
</section>
|
|
384
|
+
`,
|
|
385
|
+
label: "FAQ"
|
|
386
|
+
});
|
|
387
|
+
blocks.add("orion-pricing", {
|
|
388
|
+
category: "Commerce",
|
|
389
|
+
content: `
|
|
390
|
+
<section class="orion-builder-v2-section">
|
|
391
|
+
<div class="orion-builder-v2-container">
|
|
392
|
+
<p class="orion-builder-v2-kicker">Pricing</p>
|
|
393
|
+
<h2>Simple package options</h2>
|
|
394
|
+
<div class="orion-builder-v2-grid is-3">
|
|
395
|
+
<article class="orion-builder-v2-card"><h3>Starter</h3><p class="orion-builder-v2-price">$500</p><p>Best for focused launches.</p><a class="orion-builder-v2-button" href="/contact">Choose Starter</a></article>
|
|
396
|
+
<article class="orion-builder-v2-card is-featured"><h3>Growth</h3><p class="orion-builder-v2-price">$1,500</p><p>Best for expanding teams.</p><a class="orion-builder-v2-button" href="/contact">Choose Growth</a></article>
|
|
397
|
+
<article class="orion-builder-v2-card"><h3>Custom</h3><p class="orion-builder-v2-price">Quote</p><p>Best for complex builds.</p><a class="orion-builder-v2-button" href="/contact">Talk with us</a></article>
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
</section>
|
|
401
|
+
`,
|
|
402
|
+
label: "Pricing"
|
|
403
|
+
});
|
|
203
404
|
blocks.add("orion-button", {
|
|
204
405
|
category: "Basic",
|
|
205
406
|
content: '<a class="orion-builder-v2-button" href="/contact">Button</a>',
|
|
@@ -224,6 +425,22 @@ var registerOrionBuilderV2Blocks = (editor) => {
|
|
|
224
425
|
`,
|
|
225
426
|
label: "Form"
|
|
226
427
|
});
|
|
428
|
+
blocks.add("orion-footer", {
|
|
429
|
+
category: "Chrome",
|
|
430
|
+
content: `
|
|
431
|
+
<footer class="orion-builder-v2-footer">
|
|
432
|
+
<div>
|
|
433
|
+
<strong>Brand</strong>
|
|
434
|
+
<p>Short positioning line for the site footer.</p>
|
|
435
|
+
</div>
|
|
436
|
+
<nav aria-label="Footer">
|
|
437
|
+
<a href="/privacy">Privacy</a>
|
|
438
|
+
<a href="/contact">Contact</a>
|
|
439
|
+
</nav>
|
|
440
|
+
</footer>
|
|
441
|
+
`,
|
|
442
|
+
label: "Footer"
|
|
443
|
+
});
|
|
227
444
|
};
|
|
228
445
|
|
|
229
446
|
// src/builder-v2/editor/projectComponents.ts
|
|
@@ -293,7 +510,7 @@ var postToParent = (payload) => {
|
|
|
293
510
|
};
|
|
294
511
|
var buildSavePayload = (editor, status, projectData) => ({
|
|
295
512
|
builderMode: "grapes-v2",
|
|
296
|
-
compiledCss:
|
|
513
|
+
compiledCss: scopeBuilderCss(editor.getCss()),
|
|
297
514
|
compiledHtml: sanitizeBuilderHtml(editor.getHtml()),
|
|
298
515
|
projectData,
|
|
299
516
|
status
|
|
@@ -392,12 +609,53 @@ var uploadPayloadMediaAssets = async (editor, files) => {
|
|
|
392
609
|
editor.AssetManager.add(uploadedAssets);
|
|
393
610
|
}
|
|
394
611
|
};
|
|
395
|
-
function GrapesPageEditor({
|
|
612
|
+
function GrapesPageEditor({
|
|
613
|
+
adapter,
|
|
614
|
+
autosaveIntervalMs = 3e4,
|
|
615
|
+
initialData,
|
|
616
|
+
pageID
|
|
617
|
+
}) {
|
|
396
618
|
const containerRef = useRef(null);
|
|
397
619
|
const editorRef = useRef(null);
|
|
620
|
+
const autosaveTimerRef = useRef(null);
|
|
621
|
+
const saveRef = useRef(async () => void 0);
|
|
398
622
|
const [error, setError] = useState("");
|
|
623
|
+
const [historyState, setHistoryState] = useState({ canRedo: false, canUndo: false });
|
|
624
|
+
const [lastSavedAt, setLastSavedAt] = useState("");
|
|
399
625
|
const [loading, setLoading] = useState(true);
|
|
626
|
+
const [selectedDevice, setSelectedDevice] = useState("desktop");
|
|
400
627
|
const [saving, setSaving] = useState(null);
|
|
628
|
+
const [saveMessage, setSaveMessage] = useState("");
|
|
629
|
+
const [validationIssues, setValidationIssues] = useState([]);
|
|
630
|
+
const pageTree = initialData?.meta?.pageTree || [];
|
|
631
|
+
const updateHistoryState = (editor) => {
|
|
632
|
+
const next = {
|
|
633
|
+
canRedo: editor.UndoManager.hasRedo(),
|
|
634
|
+
canUndo: editor.UndoManager.hasUndo()
|
|
635
|
+
};
|
|
636
|
+
setHistoryState(next);
|
|
637
|
+
postToParent({
|
|
638
|
+
...next,
|
|
639
|
+
type: "history-state"
|
|
640
|
+
});
|
|
641
|
+
};
|
|
642
|
+
const runValidation = (editor) => {
|
|
643
|
+
const issues = validateBuilderV2Output({
|
|
644
|
+
css: editor.getCss(),
|
|
645
|
+
html: editor.getHtml()
|
|
646
|
+
});
|
|
647
|
+
setValidationIssues(issues);
|
|
648
|
+
postToParent({ issues, type: "validation-state" });
|
|
649
|
+
return issues;
|
|
650
|
+
};
|
|
651
|
+
const setDevice = (device) => {
|
|
652
|
+
const editor = editorRef.current;
|
|
653
|
+
if (!editor) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
editor.setDevice(device);
|
|
657
|
+
setSelectedDevice(device);
|
|
658
|
+
};
|
|
401
659
|
useEffect(() => {
|
|
402
660
|
let active = true;
|
|
403
661
|
const init = async () => {
|
|
@@ -481,12 +739,24 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
481
739
|
void loadPayloadMediaAssets(editor);
|
|
482
740
|
editor.on("update", () => {
|
|
483
741
|
postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
742
|
+
updateHistoryState(editor);
|
|
743
|
+
runValidation(editor);
|
|
744
|
+
setSaveMessage("Unsaved changes");
|
|
745
|
+
if (autosaveTimerRef.current) {
|
|
746
|
+
window.clearTimeout(autosaveTimerRef.current);
|
|
747
|
+
}
|
|
748
|
+
autosaveTimerRef.current = window.setTimeout(() => {
|
|
749
|
+
if (editor.getDirtyCount() > 0) {
|
|
750
|
+
void saveRef.current("draft", { autosave: true });
|
|
751
|
+
}
|
|
752
|
+
}, autosaveIntervalMs);
|
|
489
753
|
});
|
|
754
|
+
editor.on("component:selected", () => {
|
|
755
|
+
setSaveMessage("Selection ready");
|
|
756
|
+
});
|
|
757
|
+
setSelectedDevice(editor.getDevice() || "desktop");
|
|
758
|
+
runValidation(editor);
|
|
759
|
+
updateHistoryState(editor);
|
|
490
760
|
setLoading(false);
|
|
491
761
|
} catch (initError) {
|
|
492
762
|
setError(initError instanceof Error ? initError.message : "Could not load the website builder.");
|
|
@@ -496,15 +766,30 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
496
766
|
void init();
|
|
497
767
|
return () => {
|
|
498
768
|
active = false;
|
|
769
|
+
if (autosaveTimerRef.current) {
|
|
770
|
+
window.clearTimeout(autosaveTimerRef.current);
|
|
771
|
+
}
|
|
499
772
|
editorRef.current?.destroy();
|
|
500
773
|
editorRef.current = null;
|
|
501
774
|
};
|
|
502
|
-
}, [adapter, initialData?.projectData, initialData?.title]);
|
|
503
|
-
const save = async (status) => {
|
|
775
|
+
}, [adapter, autosaveIntervalMs, initialData?.projectData, initialData?.title]);
|
|
776
|
+
const save = async (status, options = {}) => {
|
|
504
777
|
const editor = editorRef.current;
|
|
505
778
|
if (!editor || saving) {
|
|
506
779
|
return;
|
|
507
780
|
}
|
|
781
|
+
const issues = runValidation(editor);
|
|
782
|
+
if (status === "published" && hasBlockingBuilderV2Issues(issues)) {
|
|
783
|
+
const message = "Resolve blocking validation errors before publishing.";
|
|
784
|
+
setSaveMessage(message);
|
|
785
|
+
postToParent({
|
|
786
|
+
message,
|
|
787
|
+
ok: false,
|
|
788
|
+
status,
|
|
789
|
+
type: "save-result"
|
|
790
|
+
});
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
508
793
|
setSaving(status);
|
|
509
794
|
try {
|
|
510
795
|
const projectData = editor.getProjectData();
|
|
@@ -519,15 +804,19 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
519
804
|
builderMode: "grapes-v2",
|
|
520
805
|
builderProjectData: projectData,
|
|
521
806
|
builderPublishedProjectData: projectData,
|
|
522
|
-
|
|
807
|
+
builderLastPublishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
808
|
+
builderLastSavedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
809
|
+
builderValidationIssues: issues,
|
|
523
810
|
compiledCss: payload.compiledCss,
|
|
524
811
|
compiledHtml: payload.compiledHtml
|
|
525
812
|
} : {
|
|
526
813
|
_status: "draft",
|
|
814
|
+
builderAutosaveProjectData: options.autosave ? projectData : null,
|
|
527
815
|
builderDynamicComponents: dynamicComponents,
|
|
528
816
|
builderMode: "grapes-v2",
|
|
529
817
|
builderProjectData: projectData,
|
|
530
|
-
|
|
818
|
+
builderLastSavedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
819
|
+
builderValidationIssues: issues,
|
|
531
820
|
compiledCss: payload.compiledCss,
|
|
532
821
|
compiledHtml: payload.compiledHtml
|
|
533
822
|
}
|
|
@@ -550,14 +839,18 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
550
839
|
type: "dirty-state"
|
|
551
840
|
});
|
|
552
841
|
postToParent({
|
|
553
|
-
message: status === "published" ? "Published." : "Draft saved.",
|
|
842
|
+
message: status === "published" ? "Published." : options.autosave ? "Autosaved." : "Draft saved.",
|
|
554
843
|
ok: true,
|
|
555
844
|
status,
|
|
556
845
|
type: "save-result"
|
|
557
846
|
});
|
|
847
|
+
setLastSavedAt((/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }));
|
|
848
|
+
setSaveMessage(status === "published" ? "Published" : options.autosave ? "Autosaved" : "Draft saved");
|
|
558
849
|
} catch (saveError) {
|
|
850
|
+
const message = saveError instanceof Error ? saveError.message : "Could not save.";
|
|
851
|
+
setSaveMessage(message);
|
|
559
852
|
postToParent({
|
|
560
|
-
message
|
|
853
|
+
message,
|
|
561
854
|
ok: false,
|
|
562
855
|
status,
|
|
563
856
|
type: "save-result"
|
|
@@ -566,6 +859,9 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
566
859
|
setSaving(null);
|
|
567
860
|
}
|
|
568
861
|
};
|
|
862
|
+
useEffect(() => {
|
|
863
|
+
saveRef.current = save;
|
|
864
|
+
}, [saving]);
|
|
569
865
|
useEffect(() => {
|
|
570
866
|
const onMessage = (event) => {
|
|
571
867
|
const data = event.data;
|
|
@@ -575,19 +871,11 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
575
871
|
}
|
|
576
872
|
if (data.type === "dirty-check-request") {
|
|
577
873
|
postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
|
|
578
|
-
|
|
579
|
-
canRedo: editor.UndoManager.hasRedo(),
|
|
580
|
-
canUndo: editor.UndoManager.hasUndo(),
|
|
581
|
-
type: "history-state"
|
|
582
|
-
});
|
|
874
|
+
updateHistoryState(editor);
|
|
583
875
|
return;
|
|
584
876
|
}
|
|
585
877
|
if (data.type === "history-check-request") {
|
|
586
|
-
|
|
587
|
-
canRedo: editor.UndoManager.hasRedo(),
|
|
588
|
-
canUndo: editor.UndoManager.hasUndo(),
|
|
589
|
-
type: "history-state"
|
|
590
|
-
});
|
|
878
|
+
updateHistoryState(editor);
|
|
591
879
|
return;
|
|
592
880
|
}
|
|
593
881
|
if (data.type === "undo") {
|
|
@@ -606,19 +894,89 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
606
894
|
return () => window.removeEventListener("message", onMessage);
|
|
607
895
|
}, [saving]);
|
|
608
896
|
return /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-editor", children: [
|
|
897
|
+
/* @__PURE__ */ jsxs("header", { className: "orion-builder-v2-topbar", children: [
|
|
898
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
899
|
+
/* @__PURE__ */ jsx("p", { className: "orion-builder-v2-eyebrow", children: "Website Builder V2" }),
|
|
900
|
+
/* @__PURE__ */ jsx("h1", { children: initialData?.title || "Untitled page" })
|
|
901
|
+
] }),
|
|
902
|
+
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-toolbar", "aria-label": "Builder controls", children: [
|
|
903
|
+
["desktop", "tablet", "mobile"].map((device) => /* @__PURE__ */ jsx(
|
|
904
|
+
"button",
|
|
905
|
+
{
|
|
906
|
+
"aria-pressed": selectedDevice === device,
|
|
907
|
+
className: "orion-builder-v2-tool",
|
|
908
|
+
onClick: () => setDevice(device),
|
|
909
|
+
type: "button",
|
|
910
|
+
children: device
|
|
911
|
+
},
|
|
912
|
+
device
|
|
913
|
+
)),
|
|
914
|
+
/* @__PURE__ */ jsx(
|
|
915
|
+
"button",
|
|
916
|
+
{
|
|
917
|
+
className: "orion-builder-v2-tool",
|
|
918
|
+
disabled: !historyState.canUndo,
|
|
919
|
+
onClick: () => {
|
|
920
|
+
editorRef.current?.UndoManager.undo();
|
|
921
|
+
editorRef.current && updateHistoryState(editorRef.current);
|
|
922
|
+
},
|
|
923
|
+
type: "button",
|
|
924
|
+
children: "Undo"
|
|
925
|
+
}
|
|
926
|
+
),
|
|
927
|
+
/* @__PURE__ */ jsx(
|
|
928
|
+
"button",
|
|
929
|
+
{
|
|
930
|
+
className: "orion-builder-v2-tool",
|
|
931
|
+
disabled: !historyState.canRedo,
|
|
932
|
+
onClick: () => {
|
|
933
|
+
editorRef.current?.UndoManager.redo();
|
|
934
|
+
editorRef.current && updateHistoryState(editorRef.current);
|
|
935
|
+
},
|
|
936
|
+
type: "button",
|
|
937
|
+
children: "Redo"
|
|
938
|
+
}
|
|
939
|
+
),
|
|
940
|
+
/* @__PURE__ */ jsx("button", { className: "orion-builder-v2-tool is-primary", disabled: Boolean(saving), onClick: () => void save("draft"), type: "button", children: saving === "draft" ? "Saving..." : "Save draft" }),
|
|
941
|
+
/* @__PURE__ */ jsx("button", { className: "orion-builder-v2-tool is-publish", disabled: Boolean(saving), onClick: () => void save("published"), type: "button", children: saving === "published" ? "Publishing..." : "Publish" })
|
|
942
|
+
] })
|
|
943
|
+
] }),
|
|
609
944
|
/* @__PURE__ */ jsxs("aside", { className: "orion-builder-v2-sidebar", children: [
|
|
945
|
+
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
946
|
+
/* @__PURE__ */ jsx("h2", { children: "Pages" }),
|
|
947
|
+
/* @__PURE__ */ jsx("div", { className: "orion-builder-v2-page-tree", children: pageTree.length === 0 ? /* @__PURE__ */ jsx("p", { children: "No pages loaded." }) : pageTree.map((page) => /* @__PURE__ */ jsxs("a", { className: "orion-builder-v2-page-link", href: `#page-${page.id}`, children: [
|
|
948
|
+
/* @__PURE__ */ jsx("span", { children: page.title }),
|
|
949
|
+
/* @__PURE__ */ jsx("small", { children: page.path })
|
|
950
|
+
] }, page.id)) })
|
|
951
|
+
] }),
|
|
610
952
|
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
611
953
|
/* @__PURE__ */ jsx("h2", { children: "Blocks" }),
|
|
954
|
+
/* @__PURE__ */ jsx("p", { children: "Drag sections into the page. Dynamic blocks render through the project adapter." }),
|
|
612
955
|
/* @__PURE__ */ jsx("div", { id: "orion-builder-v2-blocks" })
|
|
613
956
|
] }),
|
|
614
957
|
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
615
|
-
/* @__PURE__ */ jsx("h2", { children: "
|
|
958
|
+
/* @__PURE__ */ jsx("h2", { children: "Inspector" }),
|
|
959
|
+
/* @__PURE__ */ jsx("p", { children: "Selection settings, dynamic bindings, links, and labels." }),
|
|
616
960
|
/* @__PURE__ */ jsx("div", { id: "orion-builder-v2-traits" })
|
|
961
|
+
] }),
|
|
962
|
+
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
963
|
+
/* @__PURE__ */ jsx("h2", { children: "Validation" }),
|
|
964
|
+
/* @__PURE__ */ jsx("div", { className: "orion-builder-v2-validation-list", children: validationIssues.length === 0 ? /* @__PURE__ */ jsx("p", { children: "No issues found." }) : validationIssues.map((issue) => /* @__PURE__ */ jsxs("div", { className: `orion-builder-v2-validation is-${issue.severity}`, children: [
|
|
965
|
+
/* @__PURE__ */ jsx("strong", { children: issue.message }),
|
|
966
|
+
/* @__PURE__ */ jsx("span", { children: issue.severity })
|
|
967
|
+
] }, `${issue.code}-${issue.path}`)) })
|
|
617
968
|
] })
|
|
618
969
|
] }),
|
|
619
970
|
/* @__PURE__ */ jsxs("main", { className: "orion-builder-v2-main", children: [
|
|
620
971
|
loading ? /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-status", children: "Loading builder..." }) : null,
|
|
621
972
|
error ? /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-error", children: error }) : null,
|
|
973
|
+
saveMessage || lastSavedAt ? /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-save-status", children: [
|
|
974
|
+
/* @__PURE__ */ jsx("strong", { children: saveMessage || "Ready" }),
|
|
975
|
+
lastSavedAt ? /* @__PURE__ */ jsxs("span", { children: [
|
|
976
|
+
"Last saved ",
|
|
977
|
+
lastSavedAt
|
|
978
|
+
] }) : null
|
|
979
|
+
] }) : null,
|
|
622
980
|
/* @__PURE__ */ jsx("div", { className: "orion-builder-v2-canvas", ref: containerRef })
|
|
623
981
|
] })
|
|
624
982
|
] });
|
|
@@ -15,18 +15,76 @@ type BuilderV2ValidationIssue = {
|
|
|
15
15
|
path: string;
|
|
16
16
|
severity: 'error' | 'warning';
|
|
17
17
|
};
|
|
18
|
+
type BuilderV2Role = 'admin' | 'client' | 'developer' | 'editor';
|
|
19
|
+
type BuilderV2PermissionSet = {
|
|
20
|
+
canEditCustomCode?: boolean;
|
|
21
|
+
canManageGlobalStyles?: boolean;
|
|
22
|
+
canManageReusableSections?: boolean;
|
|
23
|
+
canPublish?: boolean;
|
|
24
|
+
canUseEmbeds?: boolean;
|
|
25
|
+
role?: BuilderV2Role;
|
|
26
|
+
};
|
|
18
27
|
type BuilderV2CompiledOutput = {
|
|
19
28
|
css: string;
|
|
20
29
|
dynamicComponents: BuilderV2DynamicComponentInstance[];
|
|
21
30
|
html: string;
|
|
31
|
+
validationIssues?: BuilderV2ValidationIssue[];
|
|
22
32
|
};
|
|
23
33
|
type BuilderV2ProjectData = Record<string, unknown>;
|
|
34
|
+
type BuilderV2ThemeTokens = {
|
|
35
|
+
buttons?: Record<string, unknown>;
|
|
36
|
+
colors?: Record<string, string>;
|
|
37
|
+
fonts?: Record<string, string>;
|
|
38
|
+
radii?: Record<string, string>;
|
|
39
|
+
shadows?: Record<string, string>;
|
|
40
|
+
spacing?: Record<string, string>;
|
|
41
|
+
typography?: Record<string, unknown>;
|
|
42
|
+
};
|
|
43
|
+
type BuilderV2PageTreeNode = {
|
|
44
|
+
children?: BuilderV2PageTreeNode[];
|
|
45
|
+
id: number | string;
|
|
46
|
+
parentId?: number | string | null;
|
|
47
|
+
path: string;
|
|
48
|
+
slug: string;
|
|
49
|
+
status?: 'draft' | 'published';
|
|
50
|
+
title: string;
|
|
51
|
+
};
|
|
52
|
+
type BuilderV2VersionSnapshot = {
|
|
53
|
+
compiledCss?: string;
|
|
54
|
+
compiledHtml?: string;
|
|
55
|
+
createdAt: string;
|
|
56
|
+
id: string;
|
|
57
|
+
label: string;
|
|
58
|
+
projectData?: BuilderV2ProjectData | null;
|
|
59
|
+
published?: boolean;
|
|
60
|
+
};
|
|
61
|
+
type BuilderV2ReusableSection = {
|
|
62
|
+
category?: string;
|
|
63
|
+
id: string;
|
|
64
|
+
name: string;
|
|
65
|
+
projectData: BuilderV2ProjectData;
|
|
66
|
+
thumbnail?: string;
|
|
67
|
+
};
|
|
68
|
+
type BuilderV2EditorMeta = {
|
|
69
|
+
pageTree?: BuilderV2PageTreeNode[];
|
|
70
|
+
permissions?: BuilderV2PermissionSet;
|
|
71
|
+
themeTokens?: BuilderV2ThemeTokens;
|
|
72
|
+
};
|
|
24
73
|
type BuilderV2PageData = {
|
|
74
|
+
builderAutosaveProjectData?: BuilderV2ProjectData | null;
|
|
25
75
|
builderDynamicComponents?: BuilderV2DynamicComponentInstance[];
|
|
76
|
+
builderLastPublishedAt?: string;
|
|
77
|
+
builderLastSavedAt?: string;
|
|
78
|
+
builderLockedAreas?: string[];
|
|
26
79
|
builderMode?: string;
|
|
27
80
|
builderProjectData?: BuilderV2ProjectData | null;
|
|
28
81
|
builderPublishedProjectData?: BuilderV2ProjectData | null;
|
|
82
|
+
builderPublishedSnapshot?: BuilderV2VersionSnapshot | null;
|
|
83
|
+
builderReusableSections?: BuilderV2ReusableSection[];
|
|
84
|
+
builderSeo?: Record<string, unknown> | null;
|
|
85
|
+
builderThemeTokens?: BuilderV2ThemeTokens | null;
|
|
29
86
|
builderValidationIssues?: BuilderV2ValidationIssue[];
|
|
87
|
+
builderVersions?: BuilderV2VersionSnapshot[];
|
|
30
88
|
compiledCss?: string;
|
|
31
89
|
compiledHtml?: string;
|
|
32
90
|
id?: number | string;
|
|
@@ -70,11 +128,13 @@ type BuilderV2TraitDefinition = {
|
|
|
70
128
|
type BuilderV2ProjectAdapter = {
|
|
71
129
|
components?: Record<string, BuilderV2ProjectComponentDefinition | BuilderV2RuntimeComponent>;
|
|
72
130
|
dataProviders?: Record<string, unknown>;
|
|
131
|
+
defaultThemeTokens?: BuilderV2ThemeTokens;
|
|
73
132
|
id: string;
|
|
74
133
|
label?: string;
|
|
75
134
|
themeTokens?: Record<string, unknown>;
|
|
76
135
|
};
|
|
77
136
|
type BuilderV2EditorInitialData = {
|
|
137
|
+
meta?: BuilderV2EditorMeta;
|
|
78
138
|
projectData?: BuilderV2ProjectData | null;
|
|
79
139
|
title?: string;
|
|
80
140
|
};
|
|
@@ -145,6 +205,19 @@ declare const createBuilderV2PageService: ({ collectionSlug, payload, user, }: C
|
|
|
145
205
|
declare const createEmptyBuilderV2ProjectData: (title?: string) => BuilderV2ProjectData;
|
|
146
206
|
declare const normalizeBuilderV2ProjectData: (value: unknown, fallbackTitle?: string) => BuilderV2ProjectData;
|
|
147
207
|
|
|
208
|
+
type PageLike = {
|
|
209
|
+
id: number | string;
|
|
210
|
+
parent?: number | string | {
|
|
211
|
+
id?: number | string;
|
|
212
|
+
} | null;
|
|
213
|
+
parentId?: number | string | null;
|
|
214
|
+
path?: string | null;
|
|
215
|
+
slug?: string | null;
|
|
216
|
+
title?: string | null;
|
|
217
|
+
_status?: 'draft' | 'published';
|
|
218
|
+
};
|
|
219
|
+
declare const buildBuilderV2PageTree: (pages: PageLike[]) => BuilderV2PageTreeNode[];
|
|
220
|
+
|
|
148
221
|
declare function BuilderPageRuntime({ adapter, className, page }: BuilderV2RuntimeProps): react_jsx_runtime.JSX.Element;
|
|
149
222
|
|
|
150
223
|
declare const parseBuilderV2DynamicComponents: (html: string) => BuilderV2DynamicComponentInstance[];
|
|
@@ -152,5 +225,12 @@ declare const splitBuilderV2HtmlIntoChunks: (html: string) => BuilderV2Renderabl
|
|
|
152
225
|
|
|
153
226
|
declare const sanitizeBuilderHtml: (value: unknown) => string;
|
|
154
227
|
declare const sanitizeBuilderCss: (value: unknown) => string;
|
|
228
|
+
declare const scopeBuilderCss: (value: unknown, scope?: string) => string;
|
|
229
|
+
|
|
230
|
+
declare const validateBuilderV2Output: (input: {
|
|
231
|
+
css?: unknown;
|
|
232
|
+
html?: unknown;
|
|
233
|
+
}) => BuilderV2ValidationIssue[];
|
|
234
|
+
declare const hasBlockingBuilderV2Issues: (issues: BuilderV2ValidationIssue[]) => boolean;
|
|
155
235
|
|
|
156
|
-
export { BuilderPageRuntime, type BuilderV2Asset, type BuilderV2CompiledOutput, type BuilderV2DynamicComponentInstance, type BuilderV2EditorInitialData, type BuilderV2FieldOptions, type BuilderV2Mode, type BuilderV2PageData, type BuilderV2PagePayloadDoc, type BuilderV2ProjectAdapter, type BuilderV2ProjectComponentDefinition, type BuilderV2ProjectData, type BuilderV2RenderResult, type BuilderV2RenderableChunk, type BuilderV2RuntimeComponent, type BuilderV2RuntimeComponentProps, type BuilderV2RuntimeProps, type BuilderV2TraitDefinition, type BuilderV2ValidationIssue, type CreateBuilderV2PageServiceArgs, type SaveBuilderV2PageInput, appendBuilderV2PageFields, compileBuilderV2Output, createBuilderV2PageFields, createBuilderV2PageService, createEmptyBuilderV2ProjectData, normalizeBuilderV2ProjectData, parseBuilderV2DynamicComponents, sanitizeBuilderCss, sanitizeBuilderHtml, splitBuilderV2HtmlIntoChunks, toBuilderV2EditorInitialData };
|
|
236
|
+
export { BuilderPageRuntime, type BuilderV2Asset, type BuilderV2CompiledOutput, type BuilderV2DynamicComponentInstance, type BuilderV2EditorInitialData, type BuilderV2FieldOptions, type BuilderV2Mode, type BuilderV2PageData, type BuilderV2PagePayloadDoc, type BuilderV2PageTreeNode, type BuilderV2PermissionSet, type BuilderV2ProjectAdapter, type BuilderV2ProjectComponentDefinition, type BuilderV2ProjectData, type BuilderV2RenderResult, type BuilderV2RenderableChunk, type BuilderV2RuntimeComponent, type BuilderV2RuntimeComponentProps, type BuilderV2RuntimeProps, type BuilderV2ThemeTokens, type BuilderV2TraitDefinition, type BuilderV2ValidationIssue, type BuilderV2VersionSnapshot, type CreateBuilderV2PageServiceArgs, type SaveBuilderV2PageInput, appendBuilderV2PageFields, buildBuilderV2PageTree, compileBuilderV2Output, createBuilderV2PageFields, createBuilderV2PageService, createEmptyBuilderV2ProjectData, hasBlockingBuilderV2Issues, normalizeBuilderV2ProjectData, parseBuilderV2DynamicComponents, sanitizeBuilderCss, sanitizeBuilderHtml, scopeBuilderCss, splitBuilderV2HtmlIntoChunks, toBuilderV2EditorInitialData, validateBuilderV2Output };
|