@orion-studios/payload-studio 0.6.0-beta.44 → 0.6.0-beta.46
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/admin/client.mjs +1 -1
- package/dist/admin-app/client.mjs +1 -1
- package/dist/builder-v2/client.d.mts +18 -0
- package/dist/builder-v2/client.d.ts +18 -0
- package/dist/builder-v2/client.js +373 -24
- package/dist/builder-v2/client.mjs +373 -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 +280 -7
- package/dist/builder-v2/index.mjs +275 -6
- package/dist/builder-v2/styles.css +206 -3
- package/dist/{chunk-MJEOSRFT.mjs → chunk-2XH7X34N.mjs} +144 -144
- package/dist/index.mjs +3 -3
- package/package.json +1 -1
|
@@ -157,10 +157,127 @@ var sanitizeBuilderCss = (value) => {
|
|
|
157
157
|
}
|
|
158
158
|
return value.replace(/@import\s+[^;]+;/gi, "").replace(/expression\s*\(/gi, "").replace(/javascript\s*:/gi, "");
|
|
159
159
|
};
|
|
160
|
+
var scopeSelector = (selector, scope) => selector.split(",").map((part) => {
|
|
161
|
+
const trimmed = part.trim();
|
|
162
|
+
if (!trimmed || trimmed.startsWith(scope) || trimmed.startsWith("@")) {
|
|
163
|
+
return trimmed;
|
|
164
|
+
}
|
|
165
|
+
if (/^(html|body|:root)\b/i.test(trimmed)) {
|
|
166
|
+
return trimmed.replace(/^(html|body|:root)\b/i, scope);
|
|
167
|
+
}
|
|
168
|
+
return `${scope} ${trimmed}`;
|
|
169
|
+
}).filter(Boolean).join(", ");
|
|
170
|
+
var scopeBuilderCss = (value, scope = ".orion-builder-v2-runtime") => {
|
|
171
|
+
const css = sanitizeBuilderCss(value);
|
|
172
|
+
if (!css) {
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
175
|
+
let output = "";
|
|
176
|
+
let cursor = 0;
|
|
177
|
+
const rulePattern = /([^{}]+)\{/g;
|
|
178
|
+
let match;
|
|
179
|
+
while ((match = rulePattern.exec(css)) !== null) {
|
|
180
|
+
const selectorStart = match.index;
|
|
181
|
+
const selector = match[1];
|
|
182
|
+
output += css.slice(cursor, selectorStart);
|
|
183
|
+
const trimmedSelector = selector.trim();
|
|
184
|
+
if (trimmedSelector.startsWith("@keyframes") || trimmedSelector.startsWith("@font-face") || trimmedSelector.startsWith("@page")) {
|
|
185
|
+
output += `${selector}{`;
|
|
186
|
+
} else if (trimmedSelector.startsWith("@media") || trimmedSelector.startsWith("@supports")) {
|
|
187
|
+
output += `${selector}{`;
|
|
188
|
+
} else {
|
|
189
|
+
output += `${scopeSelector(selector, scope)} {`;
|
|
190
|
+
}
|
|
191
|
+
cursor = rulePattern.lastIndex;
|
|
192
|
+
}
|
|
193
|
+
output += css.slice(cursor);
|
|
194
|
+
return output;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// src/builder-v2/validation.ts
|
|
198
|
+
var hasMatch = (value, pattern) => pattern.test(value);
|
|
199
|
+
var validateBuilderV2Output = (input) => {
|
|
200
|
+
const html = typeof input.html === "string" ? input.html : "";
|
|
201
|
+
const css = typeof input.css === "string" ? input.css : "";
|
|
202
|
+
const issues = [];
|
|
203
|
+
if (!html.trim()) {
|
|
204
|
+
issues.push({
|
|
205
|
+
code: "empty-page",
|
|
206
|
+
message: "This page has no rendered content.",
|
|
207
|
+
path: "compiledHtml",
|
|
208
|
+
severity: "error"
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (!hasMatch(html, /<h1\b/i)) {
|
|
212
|
+
issues.push({
|
|
213
|
+
code: "missing-h1",
|
|
214
|
+
message: "Add one H1 so the published page has a clear primary heading.",
|
|
215
|
+
path: "compiledHtml",
|
|
216
|
+
severity: "warning"
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
if (hasMatch(html, /\son[a-z]+\s*=/i)) {
|
|
220
|
+
issues.push({
|
|
221
|
+
code: "inline-event-handler",
|
|
222
|
+
message: "Inline event handlers are not allowed in published builder HTML.",
|
|
223
|
+
path: "compiledHtml",
|
|
224
|
+
severity: "error"
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (hasMatch(html, /<(script|object|embed)\b/i)) {
|
|
228
|
+
issues.push({
|
|
229
|
+
code: "unsafe-element",
|
|
230
|
+
message: "Script, object, and embed tags are not allowed in builder content.",
|
|
231
|
+
path: "compiledHtml",
|
|
232
|
+
severity: "error"
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
if (hasMatch(html, /\b(?:href|src)=["']\s*javascript:/i)) {
|
|
236
|
+
issues.push({
|
|
237
|
+
code: "unsafe-url",
|
|
238
|
+
message: "Links and media cannot use javascript URLs.",
|
|
239
|
+
path: "compiledHtml",
|
|
240
|
+
severity: "error"
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
if (hasMatch(css, /@import\s/i)) {
|
|
244
|
+
issues.push({
|
|
245
|
+
code: "css-import",
|
|
246
|
+
message: "CSS imports are removed at publish time. Use the site font and theme managers instead.",
|
|
247
|
+
path: "compiledCss",
|
|
248
|
+
severity: "warning"
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
if (hasMatch(css, /position\s*:\s*fixed/i)) {
|
|
252
|
+
issues.push({
|
|
253
|
+
code: "fixed-position",
|
|
254
|
+
message: "Fixed positioning can cover site navigation or dialogs. Review this before publishing.",
|
|
255
|
+
path: "compiledCss",
|
|
256
|
+
severity: "warning"
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return issues;
|
|
260
|
+
};
|
|
261
|
+
var hasBlockingBuilderV2Issues = (issues) => issues.some((issue) => issue.severity === "error");
|
|
160
262
|
|
|
161
263
|
// src/builder-v2/editor/defaultBlocks.ts
|
|
162
264
|
var registerOrionBuilderV2Blocks = (editor) => {
|
|
163
265
|
const blocks = editor.Blocks;
|
|
266
|
+
blocks.add("orion-nav", {
|
|
267
|
+
category: "Chrome",
|
|
268
|
+
content: `
|
|
269
|
+
<header class="orion-builder-v2-nav">
|
|
270
|
+
<a class="orion-builder-v2-logo" href="/">Brand</a>
|
|
271
|
+
<nav aria-label="Primary">
|
|
272
|
+
<a href="/">Home</a>
|
|
273
|
+
<a href="/about">About</a>
|
|
274
|
+
<a href="/contact">Contact</a>
|
|
275
|
+
</nav>
|
|
276
|
+
<a class="orion-builder-v2-button" href="/contact">Start</a>
|
|
277
|
+
</header>
|
|
278
|
+
`,
|
|
279
|
+
label: "Nav"
|
|
280
|
+
});
|
|
164
281
|
blocks.add("orion-section", {
|
|
165
282
|
category: "Layout",
|
|
166
283
|
content: `
|
|
@@ -200,6 +317,89 @@ var registerOrionBuilderV2Blocks = (editor) => {
|
|
|
200
317
|
`,
|
|
201
318
|
label: "Columns"
|
|
202
319
|
});
|
|
320
|
+
blocks.add("orion-card-grid", {
|
|
321
|
+
category: "Sections",
|
|
322
|
+
content: `
|
|
323
|
+
<section class="orion-builder-v2-section">
|
|
324
|
+
<div class="orion-builder-v2-container">
|
|
325
|
+
<p class="orion-builder-v2-kicker">Featured</p>
|
|
326
|
+
<h2>Cards that explain the offer</h2>
|
|
327
|
+
<div class="orion-builder-v2-grid is-3">
|
|
328
|
+
<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>
|
|
329
|
+
<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>
|
|
330
|
+
<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>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
</section>
|
|
334
|
+
`,
|
|
335
|
+
label: "Card grid"
|
|
336
|
+
});
|
|
337
|
+
blocks.add("orion-gallery", {
|
|
338
|
+
category: "Media",
|
|
339
|
+
content: `
|
|
340
|
+
<section class="orion-builder-v2-section">
|
|
341
|
+
<div class="orion-builder-v2-container">
|
|
342
|
+
<h2>Gallery</h2>
|
|
343
|
+
<div class="orion-builder-v2-gallery">
|
|
344
|
+
<img alt="Gallery item" src="https://placehold.co/900x700" />
|
|
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
|
+
</div>
|
|
349
|
+
</div>
|
|
350
|
+
</section>
|
|
351
|
+
`,
|
|
352
|
+
label: "Gallery"
|
|
353
|
+
});
|
|
354
|
+
blocks.add("orion-testimonials", {
|
|
355
|
+
category: "Sections",
|
|
356
|
+
content: `
|
|
357
|
+
<section class="orion-builder-v2-section is-muted">
|
|
358
|
+
<div class="orion-builder-v2-container">
|
|
359
|
+
<p class="orion-builder-v2-kicker">Testimonials</p>
|
|
360
|
+
<h2>What clients say</h2>
|
|
361
|
+
<div class="orion-builder-v2-grid is-3">
|
|
362
|
+
<blockquote class="orion-builder-v2-card"><p>"A clear, warm experience from start to finish."</p><cite>Client Name</cite></blockquote>
|
|
363
|
+
<blockquote class="orion-builder-v2-card"><p>"Exactly what we needed, without friction."</p><cite>Client Name</cite></blockquote>
|
|
364
|
+
<blockquote class="orion-builder-v2-card"><p>"The site made every next step obvious."</p><cite>Client Name</cite></blockquote>
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
</section>
|
|
368
|
+
`,
|
|
369
|
+
label: "Testimonials"
|
|
370
|
+
});
|
|
371
|
+
blocks.add("orion-faq", {
|
|
372
|
+
category: "Sections",
|
|
373
|
+
content: `
|
|
374
|
+
<section class="orion-builder-v2-section">
|
|
375
|
+
<div class="orion-builder-v2-container is-narrow">
|
|
376
|
+
<p class="orion-builder-v2-kicker">FAQ</p>
|
|
377
|
+
<h2>Questions answered</h2>
|
|
378
|
+
<details open><summary>What should visitors know first?</summary><p>Give a short, useful answer that removes hesitation.</p></details>
|
|
379
|
+
<details><summary>How does the process work?</summary><p>Explain the next step clearly.</p></details>
|
|
380
|
+
<details><summary>How do we get started?</summary><p>Point people to the strongest call to action.</p></details>
|
|
381
|
+
</div>
|
|
382
|
+
</section>
|
|
383
|
+
`,
|
|
384
|
+
label: "FAQ"
|
|
385
|
+
});
|
|
386
|
+
blocks.add("orion-pricing", {
|
|
387
|
+
category: "Commerce",
|
|
388
|
+
content: `
|
|
389
|
+
<section class="orion-builder-v2-section">
|
|
390
|
+
<div class="orion-builder-v2-container">
|
|
391
|
+
<p class="orion-builder-v2-kicker">Pricing</p>
|
|
392
|
+
<h2>Simple package options</h2>
|
|
393
|
+
<div class="orion-builder-v2-grid is-3">
|
|
394
|
+
<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>
|
|
395
|
+
<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>
|
|
396
|
+
<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>
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
</section>
|
|
400
|
+
`,
|
|
401
|
+
label: "Pricing"
|
|
402
|
+
});
|
|
203
403
|
blocks.add("orion-button", {
|
|
204
404
|
category: "Basic",
|
|
205
405
|
content: '<a class="orion-builder-v2-button" href="/contact">Button</a>',
|
|
@@ -224,6 +424,22 @@ var registerOrionBuilderV2Blocks = (editor) => {
|
|
|
224
424
|
`,
|
|
225
425
|
label: "Form"
|
|
226
426
|
});
|
|
427
|
+
blocks.add("orion-footer", {
|
|
428
|
+
category: "Chrome",
|
|
429
|
+
content: `
|
|
430
|
+
<footer class="orion-builder-v2-footer">
|
|
431
|
+
<div>
|
|
432
|
+
<strong>Brand</strong>
|
|
433
|
+
<p>Short positioning line for the site footer.</p>
|
|
434
|
+
</div>
|
|
435
|
+
<nav aria-label="Footer">
|
|
436
|
+
<a href="/privacy">Privacy</a>
|
|
437
|
+
<a href="/contact">Contact</a>
|
|
438
|
+
</nav>
|
|
439
|
+
</footer>
|
|
440
|
+
`,
|
|
441
|
+
label: "Footer"
|
|
442
|
+
});
|
|
227
443
|
};
|
|
228
444
|
|
|
229
445
|
// src/builder-v2/editor/projectComponents.ts
|
|
@@ -293,7 +509,7 @@ var postToParent = (payload) => {
|
|
|
293
509
|
};
|
|
294
510
|
var buildSavePayload = (editor, status, projectData) => ({
|
|
295
511
|
builderMode: "grapes-v2",
|
|
296
|
-
compiledCss:
|
|
512
|
+
compiledCss: scopeBuilderCss(editor.getCss()),
|
|
297
513
|
compiledHtml: sanitizeBuilderHtml(editor.getHtml()),
|
|
298
514
|
projectData,
|
|
299
515
|
status
|
|
@@ -392,12 +608,52 @@ var uploadPayloadMediaAssets = async (editor, files) => {
|
|
|
392
608
|
editor.AssetManager.add(uploadedAssets);
|
|
393
609
|
}
|
|
394
610
|
};
|
|
395
|
-
function GrapesPageEditor({
|
|
611
|
+
function GrapesPageEditor({
|
|
612
|
+
adapter,
|
|
613
|
+
autosaveIntervalMs = 3e4,
|
|
614
|
+
initialData,
|
|
615
|
+
pageID
|
|
616
|
+
}) {
|
|
396
617
|
const containerRef = useRef(null);
|
|
397
618
|
const editorRef = useRef(null);
|
|
619
|
+
const autosaveTimerRef = useRef(null);
|
|
620
|
+
const saveRef = useRef(async () => void 0);
|
|
398
621
|
const [error, setError] = useState("");
|
|
622
|
+
const [historyState, setHistoryState] = useState({ canRedo: false, canUndo: false });
|
|
623
|
+
const [lastSavedAt, setLastSavedAt] = useState("");
|
|
399
624
|
const [loading, setLoading] = useState(true);
|
|
625
|
+
const [selectedDevice, setSelectedDevice] = useState("desktop");
|
|
400
626
|
const [saving, setSaving] = useState(null);
|
|
627
|
+
const [saveMessage, setSaveMessage] = useState("");
|
|
628
|
+
const [validationIssues, setValidationIssues] = useState([]);
|
|
629
|
+
const updateHistoryState = (editor) => {
|
|
630
|
+
const next = {
|
|
631
|
+
canRedo: editor.UndoManager.hasRedo(),
|
|
632
|
+
canUndo: editor.UndoManager.hasUndo()
|
|
633
|
+
};
|
|
634
|
+
setHistoryState(next);
|
|
635
|
+
postToParent({
|
|
636
|
+
...next,
|
|
637
|
+
type: "history-state"
|
|
638
|
+
});
|
|
639
|
+
};
|
|
640
|
+
const runValidation = (editor) => {
|
|
641
|
+
const issues = validateBuilderV2Output({
|
|
642
|
+
css: editor.getCss(),
|
|
643
|
+
html: editor.getHtml()
|
|
644
|
+
});
|
|
645
|
+
setValidationIssues(issues);
|
|
646
|
+
postToParent({ issues, type: "validation-state" });
|
|
647
|
+
return issues;
|
|
648
|
+
};
|
|
649
|
+
const setDevice = (device) => {
|
|
650
|
+
const editor = editorRef.current;
|
|
651
|
+
if (!editor) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
editor.setDevice(device);
|
|
655
|
+
setSelectedDevice(device);
|
|
656
|
+
};
|
|
401
657
|
useEffect(() => {
|
|
402
658
|
let active = true;
|
|
403
659
|
const init = async () => {
|
|
@@ -481,12 +737,24 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
481
737
|
void loadPayloadMediaAssets(editor);
|
|
482
738
|
editor.on("update", () => {
|
|
483
739
|
postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
740
|
+
updateHistoryState(editor);
|
|
741
|
+
runValidation(editor);
|
|
742
|
+
setSaveMessage("Unsaved changes");
|
|
743
|
+
if (autosaveTimerRef.current) {
|
|
744
|
+
window.clearTimeout(autosaveTimerRef.current);
|
|
745
|
+
}
|
|
746
|
+
autosaveTimerRef.current = window.setTimeout(() => {
|
|
747
|
+
if (editor.getDirtyCount() > 0) {
|
|
748
|
+
void saveRef.current("draft", { autosave: true });
|
|
749
|
+
}
|
|
750
|
+
}, autosaveIntervalMs);
|
|
751
|
+
});
|
|
752
|
+
editor.on("component:selected", () => {
|
|
753
|
+
setSaveMessage("Selection ready");
|
|
489
754
|
});
|
|
755
|
+
setSelectedDevice(editor.getDevice() || "desktop");
|
|
756
|
+
runValidation(editor);
|
|
757
|
+
updateHistoryState(editor);
|
|
490
758
|
setLoading(false);
|
|
491
759
|
} catch (initError) {
|
|
492
760
|
setError(initError instanceof Error ? initError.message : "Could not load the website builder.");
|
|
@@ -496,15 +764,30 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
496
764
|
void init();
|
|
497
765
|
return () => {
|
|
498
766
|
active = false;
|
|
767
|
+
if (autosaveTimerRef.current) {
|
|
768
|
+
window.clearTimeout(autosaveTimerRef.current);
|
|
769
|
+
}
|
|
499
770
|
editorRef.current?.destroy();
|
|
500
771
|
editorRef.current = null;
|
|
501
772
|
};
|
|
502
|
-
}, [adapter, initialData?.projectData, initialData?.title]);
|
|
503
|
-
const save = async (status) => {
|
|
773
|
+
}, [adapter, autosaveIntervalMs, initialData?.projectData, initialData?.title]);
|
|
774
|
+
const save = async (status, options = {}) => {
|
|
504
775
|
const editor = editorRef.current;
|
|
505
776
|
if (!editor || saving) {
|
|
506
777
|
return;
|
|
507
778
|
}
|
|
779
|
+
const issues = runValidation(editor);
|
|
780
|
+
if (status === "published" && hasBlockingBuilderV2Issues(issues)) {
|
|
781
|
+
const message = "Resolve blocking validation errors before publishing.";
|
|
782
|
+
setSaveMessage(message);
|
|
783
|
+
postToParent({
|
|
784
|
+
message,
|
|
785
|
+
ok: false,
|
|
786
|
+
status,
|
|
787
|
+
type: "save-result"
|
|
788
|
+
});
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
508
791
|
setSaving(status);
|
|
509
792
|
try {
|
|
510
793
|
const projectData = editor.getProjectData();
|
|
@@ -519,15 +802,19 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
519
802
|
builderMode: "grapes-v2",
|
|
520
803
|
builderProjectData: projectData,
|
|
521
804
|
builderPublishedProjectData: projectData,
|
|
522
|
-
|
|
805
|
+
builderLastPublishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
806
|
+
builderLastSavedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
807
|
+
builderValidationIssues: issues,
|
|
523
808
|
compiledCss: payload.compiledCss,
|
|
524
809
|
compiledHtml: payload.compiledHtml
|
|
525
810
|
} : {
|
|
526
811
|
_status: "draft",
|
|
812
|
+
builderAutosaveProjectData: options.autosave ? projectData : null,
|
|
527
813
|
builderDynamicComponents: dynamicComponents,
|
|
528
814
|
builderMode: "grapes-v2",
|
|
529
815
|
builderProjectData: projectData,
|
|
530
|
-
|
|
816
|
+
builderLastSavedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
817
|
+
builderValidationIssues: issues,
|
|
531
818
|
compiledCss: payload.compiledCss,
|
|
532
819
|
compiledHtml: payload.compiledHtml
|
|
533
820
|
}
|
|
@@ -550,14 +837,18 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
550
837
|
type: "dirty-state"
|
|
551
838
|
});
|
|
552
839
|
postToParent({
|
|
553
|
-
message: status === "published" ? "Published." : "Draft saved.",
|
|
840
|
+
message: status === "published" ? "Published." : options.autosave ? "Autosaved." : "Draft saved.",
|
|
554
841
|
ok: true,
|
|
555
842
|
status,
|
|
556
843
|
type: "save-result"
|
|
557
844
|
});
|
|
845
|
+
setLastSavedAt((/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }));
|
|
846
|
+
setSaveMessage(status === "published" ? "Published" : options.autosave ? "Autosaved" : "Draft saved");
|
|
558
847
|
} catch (saveError) {
|
|
848
|
+
const message = saveError instanceof Error ? saveError.message : "Could not save.";
|
|
849
|
+
setSaveMessage(message);
|
|
559
850
|
postToParent({
|
|
560
|
-
message
|
|
851
|
+
message,
|
|
561
852
|
ok: false,
|
|
562
853
|
status,
|
|
563
854
|
type: "save-result"
|
|
@@ -566,6 +857,9 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
566
857
|
setSaving(null);
|
|
567
858
|
}
|
|
568
859
|
};
|
|
860
|
+
useEffect(() => {
|
|
861
|
+
saveRef.current = save;
|
|
862
|
+
}, [saving]);
|
|
569
863
|
useEffect(() => {
|
|
570
864
|
const onMessage = (event) => {
|
|
571
865
|
const data = event.data;
|
|
@@ -575,19 +869,11 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
575
869
|
}
|
|
576
870
|
if (data.type === "dirty-check-request") {
|
|
577
871
|
postToParent({ dirty: editor.getDirtyCount() > 0, type: "dirty-state" });
|
|
578
|
-
|
|
579
|
-
canRedo: editor.UndoManager.hasRedo(),
|
|
580
|
-
canUndo: editor.UndoManager.hasUndo(),
|
|
581
|
-
type: "history-state"
|
|
582
|
-
});
|
|
872
|
+
updateHistoryState(editor);
|
|
583
873
|
return;
|
|
584
874
|
}
|
|
585
875
|
if (data.type === "history-check-request") {
|
|
586
|
-
|
|
587
|
-
canRedo: editor.UndoManager.hasRedo(),
|
|
588
|
-
canUndo: editor.UndoManager.hasUndo(),
|
|
589
|
-
type: "history-state"
|
|
590
|
-
});
|
|
876
|
+
updateHistoryState(editor);
|
|
591
877
|
return;
|
|
592
878
|
}
|
|
593
879
|
if (data.type === "undo") {
|
|
@@ -606,19 +892,82 @@ function GrapesPageEditor({ adapter, initialData, pageID }) {
|
|
|
606
892
|
return () => window.removeEventListener("message", onMessage);
|
|
607
893
|
}, [saving]);
|
|
608
894
|
return /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-editor", children: [
|
|
895
|
+
/* @__PURE__ */ jsxs("header", { className: "orion-builder-v2-topbar", children: [
|
|
896
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
897
|
+
/* @__PURE__ */ jsx("p", { className: "orion-builder-v2-eyebrow", children: "Website Builder V2" }),
|
|
898
|
+
/* @__PURE__ */ jsx("h1", { children: initialData?.title || "Untitled page" })
|
|
899
|
+
] }),
|
|
900
|
+
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-toolbar", "aria-label": "Builder controls", children: [
|
|
901
|
+
["desktop", "tablet", "mobile"].map((device) => /* @__PURE__ */ jsx(
|
|
902
|
+
"button",
|
|
903
|
+
{
|
|
904
|
+
"aria-pressed": selectedDevice === device,
|
|
905
|
+
className: "orion-builder-v2-tool",
|
|
906
|
+
onClick: () => setDevice(device),
|
|
907
|
+
type: "button",
|
|
908
|
+
children: device
|
|
909
|
+
},
|
|
910
|
+
device
|
|
911
|
+
)),
|
|
912
|
+
/* @__PURE__ */ jsx(
|
|
913
|
+
"button",
|
|
914
|
+
{
|
|
915
|
+
className: "orion-builder-v2-tool",
|
|
916
|
+
disabled: !historyState.canUndo,
|
|
917
|
+
onClick: () => {
|
|
918
|
+
editorRef.current?.UndoManager.undo();
|
|
919
|
+
editorRef.current && updateHistoryState(editorRef.current);
|
|
920
|
+
},
|
|
921
|
+
type: "button",
|
|
922
|
+
children: "Undo"
|
|
923
|
+
}
|
|
924
|
+
),
|
|
925
|
+
/* @__PURE__ */ jsx(
|
|
926
|
+
"button",
|
|
927
|
+
{
|
|
928
|
+
className: "orion-builder-v2-tool",
|
|
929
|
+
disabled: !historyState.canRedo,
|
|
930
|
+
onClick: () => {
|
|
931
|
+
editorRef.current?.UndoManager.redo();
|
|
932
|
+
editorRef.current && updateHistoryState(editorRef.current);
|
|
933
|
+
},
|
|
934
|
+
type: "button",
|
|
935
|
+
children: "Redo"
|
|
936
|
+
}
|
|
937
|
+
),
|
|
938
|
+
/* @__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" }),
|
|
939
|
+
/* @__PURE__ */ jsx("button", { className: "orion-builder-v2-tool is-publish", disabled: Boolean(saving), onClick: () => void save("published"), type: "button", children: saving === "published" ? "Publishing..." : "Publish" })
|
|
940
|
+
] })
|
|
941
|
+
] }),
|
|
609
942
|
/* @__PURE__ */ jsxs("aside", { className: "orion-builder-v2-sidebar", children: [
|
|
610
943
|
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
611
944
|
/* @__PURE__ */ jsx("h2", { children: "Blocks" }),
|
|
945
|
+
/* @__PURE__ */ jsx("p", { children: "Drag sections into the page. Dynamic blocks render through the project adapter." }),
|
|
612
946
|
/* @__PURE__ */ jsx("div", { id: "orion-builder-v2-blocks" })
|
|
613
947
|
] }),
|
|
614
948
|
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
615
|
-
/* @__PURE__ */ jsx("h2", { children: "
|
|
949
|
+
/* @__PURE__ */ jsx("h2", { children: "Inspector" }),
|
|
950
|
+
/* @__PURE__ */ jsx("p", { children: "Selection settings, dynamic bindings, links, and labels." }),
|
|
616
951
|
/* @__PURE__ */ jsx("div", { id: "orion-builder-v2-traits" })
|
|
952
|
+
] }),
|
|
953
|
+
/* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-panel", children: [
|
|
954
|
+
/* @__PURE__ */ jsx("h2", { children: "Validation" }),
|
|
955
|
+
/* @__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: [
|
|
956
|
+
/* @__PURE__ */ jsx("strong", { children: issue.message }),
|
|
957
|
+
/* @__PURE__ */ jsx("span", { children: issue.severity })
|
|
958
|
+
] }, `${issue.code}-${issue.path}`)) })
|
|
617
959
|
] })
|
|
618
960
|
] }),
|
|
619
961
|
/* @__PURE__ */ jsxs("main", { className: "orion-builder-v2-main", children: [
|
|
620
962
|
loading ? /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-status", children: "Loading builder..." }) : null,
|
|
621
963
|
error ? /* @__PURE__ */ jsx("div", { className: "orion-builder-v2-error", children: error }) : null,
|
|
964
|
+
saveMessage || lastSavedAt ? /* @__PURE__ */ jsxs("div", { className: "orion-builder-v2-save-status", children: [
|
|
965
|
+
/* @__PURE__ */ jsx("strong", { children: saveMessage || "Ready" }),
|
|
966
|
+
lastSavedAt ? /* @__PURE__ */ jsxs("span", { children: [
|
|
967
|
+
"Last saved ",
|
|
968
|
+
lastSavedAt
|
|
969
|
+
] }) : null
|
|
970
|
+
] }) : null,
|
|
622
971
|
/* @__PURE__ */ jsx("div", { className: "orion-builder-v2-canvas", ref: containerRef })
|
|
623
972
|
] })
|
|
624
973
|
] });
|
|
@@ -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 };
|