@orion-studios/payload-studio 0.3.0-beta.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/LICENSE +21 -0
- package/README.md +47 -0
- package/dist/OrionBlocksFieldImpl-QX5GTMQZ.mjs +904 -0
- package/dist/admin/client.d.mts +110 -0
- package/dist/admin/client.d.ts +110 -0
- package/dist/admin/client.js +2853 -0
- package/dist/admin/client.mjs +1672 -0
- package/dist/admin/index.d.mts +2 -0
- package/dist/admin/index.d.ts +2 -0
- package/dist/admin/index.js +324 -0
- package/dist/admin/index.mjs +13 -0
- package/dist/admin.css +327 -0
- package/dist/blocks/index.d.mts +3 -0
- package/dist/blocks/index.d.ts +3 -0
- package/dist/blocks/index.js +844 -0
- package/dist/blocks/index.mjs +35 -0
- package/dist/chunk-6BWS3CLP.mjs +16 -0
- package/dist/chunk-BVN5HKTM.mjs +299 -0
- package/dist/chunk-L62FYT57.mjs +829 -0
- package/dist/chunk-PGLMIFRY.mjs +209 -0
- package/dist/chunk-Q76U4Z53.mjs +249 -0
- package/dist/chunk-WLXZDMK3.mjs +154 -0
- package/dist/chunk-ZLLNO5FM.mjs +229 -0
- package/dist/index-B-5K41Km.d.mts +71 -0
- package/dist/index-B-5K41Km.d.ts +71 -0
- package/dist/index-BgQSKQKb.d.mts +149 -0
- package/dist/index-BgQSKQKb.d.ts +149 -0
- package/dist/index-CITGmLG_.d.mts +81 -0
- package/dist/index-CITGmLG_.d.ts +81 -0
- package/dist/index-DhH3ERbg.d.ts +39 -0
- package/dist/index-DtQ0tph5.d.mts +39 -0
- package/dist/index-FA2Ep5rj.d.mts +129 -0
- package/dist/index-FA2Ep5rj.d.ts +129 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1735 -0
- package/dist/index.mjs +23 -0
- package/dist/nextjs/index.d.mts +2 -0
- package/dist/nextjs/index.d.ts +2 -0
- package/dist/nextjs/index.js +244 -0
- package/dist/nextjs/index.mjs +15 -0
- package/dist/studio/index.d.mts +1 -0
- package/dist/studio/index.d.ts +1 -0
- package/dist/studio/index.js +169 -0
- package/dist/studio/index.mjs +17 -0
- package/dist/studio-pages/builder.css +260 -0
- package/dist/studio-pages/client.d.mts +23 -0
- package/dist/studio-pages/client.d.ts +23 -0
- package/dist/studio-pages/client.js +1938 -0
- package/dist/studio-pages/client.mjs +1909 -0
- package/dist/studio-pages/index.d.mts +2 -0
- package/dist/studio-pages/index.d.ts +2 -0
- package/dist/studio-pages/index.js +264 -0
- package/dist/studio-pages/index.mjs +19 -0
- package/package.json +87 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// src/admin/components/BlockPicker.tsx
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
function BlockPicker({
|
|
7
|
+
blocks,
|
|
8
|
+
onSelect
|
|
9
|
+
}) {
|
|
10
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
11
|
+
const filtered = blocks.filter(
|
|
12
|
+
(b) => b.label.toLowerCase().includes(searchQuery.toLowerCase()) || b.slug.toLowerCase().includes(searchQuery.toLowerCase()) || b.description && b.description.toLowerCase().includes(searchQuery.toLowerCase())
|
|
13
|
+
);
|
|
14
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
|
|
15
|
+
blocks.length > 4 && /* @__PURE__ */ jsx(
|
|
16
|
+
"input",
|
|
17
|
+
{
|
|
18
|
+
type: "text",
|
|
19
|
+
placeholder: "Search sections...",
|
|
20
|
+
value: searchQuery,
|
|
21
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
22
|
+
style: {
|
|
23
|
+
padding: "10px 14px",
|
|
24
|
+
border: "1px solid var(--admin-input-border)",
|
|
25
|
+
borderRadius: "var(--admin-radius-md)",
|
|
26
|
+
background: "var(--admin-input-bg)",
|
|
27
|
+
color: "var(--admin-text)",
|
|
28
|
+
fontSize: 14,
|
|
29
|
+
outline: "none",
|
|
30
|
+
width: "100%",
|
|
31
|
+
boxSizing: "border-box"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
/* @__PURE__ */ jsx(
|
|
36
|
+
"div",
|
|
37
|
+
{
|
|
38
|
+
style: {
|
|
39
|
+
display: "grid",
|
|
40
|
+
gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
|
|
41
|
+
gap: 12
|
|
42
|
+
},
|
|
43
|
+
children: filtered.map((block) => /* @__PURE__ */ jsxs(
|
|
44
|
+
"button",
|
|
45
|
+
{
|
|
46
|
+
type: "button",
|
|
47
|
+
onClick: () => onSelect(block.slug),
|
|
48
|
+
style: {
|
|
49
|
+
display: "flex",
|
|
50
|
+
flexDirection: "column",
|
|
51
|
+
alignItems: "center",
|
|
52
|
+
gap: 10,
|
|
53
|
+
padding: 16,
|
|
54
|
+
background: "var(--admin-card-bg)",
|
|
55
|
+
border: "1px solid var(--admin-card-border)",
|
|
56
|
+
borderRadius: "var(--admin-radius-lg)",
|
|
57
|
+
cursor: "pointer",
|
|
58
|
+
transition: "all 0.2s ease",
|
|
59
|
+
textAlign: "center"
|
|
60
|
+
},
|
|
61
|
+
onMouseEnter: (e) => {
|
|
62
|
+
e.currentTarget.style.borderColor = "var(--admin-accent)";
|
|
63
|
+
e.currentTarget.style.boxShadow = "var(--admin-card-shadow-hover)";
|
|
64
|
+
e.currentTarget.style.transform = "translateY(-2px)";
|
|
65
|
+
},
|
|
66
|
+
onMouseLeave: (e) => {
|
|
67
|
+
e.currentTarget.style.borderColor = "var(--admin-card-border)";
|
|
68
|
+
e.currentTarget.style.boxShadow = "none";
|
|
69
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
70
|
+
},
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
"div",
|
|
74
|
+
{
|
|
75
|
+
style: {
|
|
76
|
+
width: 48,
|
|
77
|
+
height: 48,
|
|
78
|
+
borderRadius: "var(--admin-radius-md)",
|
|
79
|
+
background: "var(--admin-accent-subtle)",
|
|
80
|
+
color: "var(--admin-accent)",
|
|
81
|
+
display: "flex",
|
|
82
|
+
alignItems: "center",
|
|
83
|
+
justifyContent: "center",
|
|
84
|
+
fontSize: 24,
|
|
85
|
+
overflow: "hidden"
|
|
86
|
+
},
|
|
87
|
+
children: block.imageURL ? /* @__PURE__ */ jsx(
|
|
88
|
+
"img",
|
|
89
|
+
{
|
|
90
|
+
src: block.imageURL,
|
|
91
|
+
alt: block.label,
|
|
92
|
+
style: { width: "100%", height: "100%", objectFit: "cover" }
|
|
93
|
+
}
|
|
94
|
+
) : block.icon ? block.icon : /* @__PURE__ */ jsx(BlockDefaultIcon, {})
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
/* @__PURE__ */ jsx(
|
|
98
|
+
"span",
|
|
99
|
+
{
|
|
100
|
+
style: {
|
|
101
|
+
fontSize: 13,
|
|
102
|
+
fontWeight: 600,
|
|
103
|
+
color: "var(--admin-text)",
|
|
104
|
+
lineHeight: 1.3
|
|
105
|
+
},
|
|
106
|
+
children: block.label
|
|
107
|
+
}
|
|
108
|
+
),
|
|
109
|
+
block.description && /* @__PURE__ */ jsx(
|
|
110
|
+
"span",
|
|
111
|
+
{
|
|
112
|
+
style: {
|
|
113
|
+
fontSize: 11,
|
|
114
|
+
color: "var(--admin-text-muted)",
|
|
115
|
+
lineHeight: 1.4
|
|
116
|
+
},
|
|
117
|
+
children: block.description
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
block.slug
|
|
123
|
+
))
|
|
124
|
+
}
|
|
125
|
+
),
|
|
126
|
+
filtered.length === 0 && /* @__PURE__ */ jsx("p", { style: { textAlign: "center", color: "var(--admin-text-muted)", fontSize: 14, padding: 20 }, children: "No sections match your search." })
|
|
127
|
+
] });
|
|
128
|
+
}
|
|
129
|
+
function BlockDefaultIcon() {
|
|
130
|
+
return /* @__PURE__ */ jsxs("svg", { width: 24, height: 24, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
131
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "7", height: "7" }),
|
|
132
|
+
/* @__PURE__ */ jsx("rect", { x: "14", y: "3", width: "7", height: "7" }),
|
|
133
|
+
/* @__PURE__ */ jsx("rect", { x: "14", y: "14", width: "7", height: "7" }),
|
|
134
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "14", width: "7", height: "7" })
|
|
135
|
+
] });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/admin/components/SectionTabs.tsx
|
|
139
|
+
import { useState as useState2 } from "react";
|
|
140
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
141
|
+
function SectionTabs({
|
|
142
|
+
tabs,
|
|
143
|
+
defaultTab = 0
|
|
144
|
+
}) {
|
|
145
|
+
const [activeTab, setActiveTab] = useState2(defaultTab);
|
|
146
|
+
if (tabs.length === 0) return null;
|
|
147
|
+
return /* @__PURE__ */ jsxs2(
|
|
148
|
+
"div",
|
|
149
|
+
{
|
|
150
|
+
style: {
|
|
151
|
+
border: "1px solid var(--admin-border)",
|
|
152
|
+
borderRadius: "var(--admin-radius-lg)",
|
|
153
|
+
overflow: "hidden",
|
|
154
|
+
background: "var(--admin-card-bg)"
|
|
155
|
+
},
|
|
156
|
+
children: [
|
|
157
|
+
/* @__PURE__ */ jsx2(
|
|
158
|
+
"div",
|
|
159
|
+
{
|
|
160
|
+
style: {
|
|
161
|
+
display: "flex",
|
|
162
|
+
borderBottom: "2px solid var(--admin-border)",
|
|
163
|
+
background: "var(--admin-surface)",
|
|
164
|
+
overflowX: "auto",
|
|
165
|
+
scrollbarWidth: "none"
|
|
166
|
+
},
|
|
167
|
+
children: tabs.map((tab, index) => {
|
|
168
|
+
const isActive = index === activeTab;
|
|
169
|
+
return /* @__PURE__ */ jsxs2(
|
|
170
|
+
"button",
|
|
171
|
+
{
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: () => setActiveTab(index),
|
|
174
|
+
style: {
|
|
175
|
+
display: "flex",
|
|
176
|
+
alignItems: "center",
|
|
177
|
+
gap: 6,
|
|
178
|
+
padding: "12px 18px",
|
|
179
|
+
border: "none",
|
|
180
|
+
borderBottom: isActive ? "2px solid var(--admin-accent)" : "2px solid transparent",
|
|
181
|
+
marginBottom: -2,
|
|
182
|
+
background: "transparent",
|
|
183
|
+
color: isActive ? "var(--admin-accent)" : "var(--admin-text-muted)",
|
|
184
|
+
fontWeight: isActive ? 600 : 500,
|
|
185
|
+
fontSize: 13,
|
|
186
|
+
cursor: "pointer",
|
|
187
|
+
transition: "all 0.15s ease",
|
|
188
|
+
whiteSpace: "nowrap"
|
|
189
|
+
},
|
|
190
|
+
children: [
|
|
191
|
+
tab.icon && /* @__PURE__ */ jsx2("span", { style: { display: "flex", opacity: isActive ? 1 : 0.6 }, children: tab.icon }),
|
|
192
|
+
tab.label
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
index
|
|
196
|
+
);
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
),
|
|
200
|
+
/* @__PURE__ */ jsx2("div", { style: { padding: 20 }, children: tabs[activeTab]?.content })
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export {
|
|
207
|
+
BlockPicker,
|
|
208
|
+
SectionTabs
|
|
209
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-6BWS3CLP.mjs";
|
|
4
|
+
|
|
5
|
+
// src/studio-pages/index.ts
|
|
6
|
+
var studio_pages_exports = {};
|
|
7
|
+
__export(studio_pages_exports, {
|
|
8
|
+
createDefaultStudioDocument: () => createDefaultStudioDocument,
|
|
9
|
+
layoutToStudioDocument: () => layoutToStudioDocument,
|
|
10
|
+
pageInspectorPanels: () => pageInspectorPanels,
|
|
11
|
+
pageNodeTypes: () => pageNodeTypes,
|
|
12
|
+
pagePaletteGroups: () => pagePaletteGroups,
|
|
13
|
+
pageStudioModuleManifest: () => pageStudioModuleManifest,
|
|
14
|
+
studioDocumentToLayout: () => studioDocumentToLayout
|
|
15
|
+
});
|
|
16
|
+
var defaultNodeData = {
|
|
17
|
+
bookingEmbed: {
|
|
18
|
+
buttonHref: "/contact",
|
|
19
|
+
buttonLabel: "Book Consultation",
|
|
20
|
+
description: "Let visitors book a consultation.",
|
|
21
|
+
title: "Book a Time"
|
|
22
|
+
},
|
|
23
|
+
cta: {
|
|
24
|
+
backgroundColor: "#1f684f",
|
|
25
|
+
buttonHref: "/contact",
|
|
26
|
+
buttonLabel: "Contact Us",
|
|
27
|
+
description: "Optional supporting copy.",
|
|
28
|
+
headline: "Ready to get started?",
|
|
29
|
+
style: "light"
|
|
30
|
+
},
|
|
31
|
+
faq: {
|
|
32
|
+
items: [{ answer: "Answer goes here.", question: "Frequently asked question?" }],
|
|
33
|
+
title: "Frequently Asked Questions"
|
|
34
|
+
},
|
|
35
|
+
featureGrid: {
|
|
36
|
+
items: [
|
|
37
|
+
{ description: "Explain this point.", icon: "01", title: "Feature One" },
|
|
38
|
+
{ description: "Explain this point.", icon: "02", title: "Feature Two" },
|
|
39
|
+
{ description: "Explain this point.", icon: "03", title: "Feature Three" }
|
|
40
|
+
],
|
|
41
|
+
title: "Section Title",
|
|
42
|
+
variant: "cards"
|
|
43
|
+
},
|
|
44
|
+
formEmbed: {
|
|
45
|
+
description: "Collect lead details from visitors.",
|
|
46
|
+
formType: "quote",
|
|
47
|
+
title: "Request a Quote"
|
|
48
|
+
},
|
|
49
|
+
hero: {
|
|
50
|
+
backgroundColor: "#124a37",
|
|
51
|
+
headline: "New Hero Section",
|
|
52
|
+
kicker: "Optional kicker",
|
|
53
|
+
primaryHref: "/contact",
|
|
54
|
+
primaryLabel: "Primary Action",
|
|
55
|
+
secondaryHref: "/services",
|
|
56
|
+
secondaryLabel: "Secondary Action",
|
|
57
|
+
subheadline: "Describe your offer clearly for website visitors.",
|
|
58
|
+
variant: "default"
|
|
59
|
+
},
|
|
60
|
+
media: {
|
|
61
|
+
caption: "Add a caption",
|
|
62
|
+
size: "default"
|
|
63
|
+
},
|
|
64
|
+
richText: {
|
|
65
|
+
content: {
|
|
66
|
+
root: {
|
|
67
|
+
children: [
|
|
68
|
+
{
|
|
69
|
+
children: [
|
|
70
|
+
{
|
|
71
|
+
detail: 0,
|
|
72
|
+
format: 0,
|
|
73
|
+
mode: "normal",
|
|
74
|
+
style: "",
|
|
75
|
+
text: "Write your content here.",
|
|
76
|
+
type: "text",
|
|
77
|
+
version: 1
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
direction: "ltr",
|
|
81
|
+
format: "",
|
|
82
|
+
indent: 0,
|
|
83
|
+
type: "paragraph",
|
|
84
|
+
version: 1
|
|
85
|
+
}
|
|
86
|
+
],
|
|
87
|
+
direction: "ltr",
|
|
88
|
+
format: "",
|
|
89
|
+
indent: 0,
|
|
90
|
+
type: "root",
|
|
91
|
+
version: 1
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
title: "Section Heading",
|
|
95
|
+
width: "normal"
|
|
96
|
+
},
|
|
97
|
+
testimonials: {
|
|
98
|
+
items: [{ location: "City, ST", name: "Customer Name", quote: "Customer feedback goes here." }],
|
|
99
|
+
title: "What Customers Say"
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
var nodeTypeLabels = {
|
|
103
|
+
bookingEmbed: "Booking Embed",
|
|
104
|
+
cta: "Call To Action",
|
|
105
|
+
faq: "FAQ",
|
|
106
|
+
featureGrid: "Feature Grid",
|
|
107
|
+
formEmbed: "Form Embed",
|
|
108
|
+
hero: "Hero",
|
|
109
|
+
media: "Media",
|
|
110
|
+
richText: "Rich Text",
|
|
111
|
+
testimonials: "Testimonials"
|
|
112
|
+
};
|
|
113
|
+
var pageNodeTypes = Object.keys(defaultNodeData).map((type) => ({
|
|
114
|
+
type,
|
|
115
|
+
displayName: nodeTypeLabels[type] || type,
|
|
116
|
+
description: `Page node for ${nodeTypeLabels[type] || type}`,
|
|
117
|
+
getDefaultData: () => structuredClone(defaultNodeData[type])
|
|
118
|
+
}));
|
|
119
|
+
var validatePageDocument = (document) => {
|
|
120
|
+
const issues = [];
|
|
121
|
+
if (!document.title || document.title.trim().length === 0) {
|
|
122
|
+
issues.push({
|
|
123
|
+
code: "pages.title.required",
|
|
124
|
+
message: "Page title is required before publishing.",
|
|
125
|
+
path: "title",
|
|
126
|
+
severity: "error"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
if (document.nodes.length === 0) {
|
|
130
|
+
issues.push({
|
|
131
|
+
code: "pages.nodes.required",
|
|
132
|
+
message: "At least one section is required.",
|
|
133
|
+
path: "nodes",
|
|
134
|
+
severity: "error"
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
document.nodes.forEach((node, index) => {
|
|
138
|
+
if (node.type === "hero" && typeof node.data.headline !== "string") {
|
|
139
|
+
issues.push({
|
|
140
|
+
code: "pages.hero.headline",
|
|
141
|
+
message: "Hero section requires a headline.",
|
|
142
|
+
path: `nodes.${index}.data.headline`,
|
|
143
|
+
severity: "error"
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
return issues;
|
|
148
|
+
};
|
|
149
|
+
var pagePaletteGroups = [
|
|
150
|
+
{
|
|
151
|
+
id: "page-core",
|
|
152
|
+
label: "Core Sections",
|
|
153
|
+
items: [
|
|
154
|
+
{ nodeType: "hero", title: "Hero", description: "Top-of-page headline and CTA" },
|
|
155
|
+
{ nodeType: "featureGrid", title: "Feature Grid", description: "Service or value cards" },
|
|
156
|
+
{ nodeType: "richText", title: "Rich Text", description: "Long-form content area" },
|
|
157
|
+
{ nodeType: "media", title: "Media", description: "Image/video section" },
|
|
158
|
+
{ nodeType: "testimonials", title: "Testimonials", description: "Social proof quotes" },
|
|
159
|
+
{ nodeType: "faq", title: "FAQ", description: "Question-and-answer section" },
|
|
160
|
+
{ nodeType: "cta", title: "Call To Action", description: "Conversion strip with button" },
|
|
161
|
+
{ nodeType: "formEmbed", title: "Form Embed", description: "Lead capture form" },
|
|
162
|
+
{ nodeType: "bookingEmbed", title: "Booking Embed", description: "Scheduling panel" }
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
];
|
|
166
|
+
var pageInspectorPanels = Object.keys(defaultNodeData).map((nodeType) => ({
|
|
167
|
+
id: `${nodeType}-panel`,
|
|
168
|
+
label: `${nodeTypeLabels[nodeType] || nodeType} Settings`,
|
|
169
|
+
nodeType,
|
|
170
|
+
fields: [
|
|
171
|
+
{
|
|
172
|
+
key: "title",
|
|
173
|
+
label: "Title",
|
|
174
|
+
type: "text"
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}));
|
|
178
|
+
var pageStudioModuleManifest = {
|
|
179
|
+
id: "studio-pages",
|
|
180
|
+
version: "0.1.0-beta.0",
|
|
181
|
+
displayName: "Pages Studio Module",
|
|
182
|
+
nodeTypes: pageNodeTypes,
|
|
183
|
+
paletteGroups: pagePaletteGroups,
|
|
184
|
+
inspectorPanels: pageInspectorPanels,
|
|
185
|
+
validators: [validatePageDocument],
|
|
186
|
+
migrations: [],
|
|
187
|
+
compiler: {
|
|
188
|
+
compileNode: (node) => ({
|
|
189
|
+
id: node.id,
|
|
190
|
+
blockType: node.type,
|
|
191
|
+
...node.data
|
|
192
|
+
})
|
|
193
|
+
},
|
|
194
|
+
permissions: [
|
|
195
|
+
{ action: "read", role: "admin" },
|
|
196
|
+
{ action: "read", role: "editor" },
|
|
197
|
+
{ action: "read", role: "client" },
|
|
198
|
+
{ action: "update", role: "admin" },
|
|
199
|
+
{ action: "update", role: "editor" },
|
|
200
|
+
{ action: "update", role: "client" },
|
|
201
|
+
{ action: "publish", role: "admin" },
|
|
202
|
+
{ action: "publish", role: "editor" }
|
|
203
|
+
]
|
|
204
|
+
};
|
|
205
|
+
var ensureNodeID = (inputID, index) => {
|
|
206
|
+
if (typeof inputID === "string" && inputID.length > 0) {
|
|
207
|
+
return inputID;
|
|
208
|
+
}
|
|
209
|
+
return `node-${index + 1}`;
|
|
210
|
+
};
|
|
211
|
+
var layoutToStudioDocument = (layout, title) => {
|
|
212
|
+
const nodes = layout.filter((block) => typeof block.blockType === "string").map((block, index) => {
|
|
213
|
+
const blockType = String(block.blockType);
|
|
214
|
+
const { id, blockType: _, ...data } = block;
|
|
215
|
+
return {
|
|
216
|
+
id: ensureNodeID(id, index),
|
|
217
|
+
type: blockType,
|
|
218
|
+
data
|
|
219
|
+
};
|
|
220
|
+
});
|
|
221
|
+
return {
|
|
222
|
+
schemaVersion: 1,
|
|
223
|
+
title,
|
|
224
|
+
nodes,
|
|
225
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
var studioDocumentToLayout = (document) => document.nodes.map((node) => ({
|
|
229
|
+
id: node.id,
|
|
230
|
+
blockType: node.type,
|
|
231
|
+
...node.data
|
|
232
|
+
}));
|
|
233
|
+
var createDefaultStudioDocument = (title) => ({
|
|
234
|
+
schemaVersion: 1,
|
|
235
|
+
title,
|
|
236
|
+
nodes: [],
|
|
237
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
export {
|
|
241
|
+
pageNodeTypes,
|
|
242
|
+
pagePaletteGroups,
|
|
243
|
+
pageInspectorPanels,
|
|
244
|
+
pageStudioModuleManifest,
|
|
245
|
+
layoutToStudioDocument,
|
|
246
|
+
studioDocumentToLayout,
|
|
247
|
+
createDefaultStudioDocument,
|
|
248
|
+
studio_pages_exports
|
|
249
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./chunk-6BWS3CLP.mjs";
|
|
4
|
+
|
|
5
|
+
// src/studio/index.ts
|
|
6
|
+
var studio_exports = {};
|
|
7
|
+
__export(studio_exports, {
|
|
8
|
+
assertStudioDocumentV1: () => assertStudioDocumentV1,
|
|
9
|
+
compileStudioDocument: () => compileStudioDocument,
|
|
10
|
+
createEmptyStudioDocument: () => createEmptyStudioDocument,
|
|
11
|
+
createStudioRegistry: () => createStudioRegistry,
|
|
12
|
+
migrateStudioDocument: () => migrateStudioDocument,
|
|
13
|
+
validateStudioDocument: () => validateStudioDocument
|
|
14
|
+
});
|
|
15
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
16
|
+
var makeIssue = (message, path, code = "studio.invalid") => ({
|
|
17
|
+
code,
|
|
18
|
+
message,
|
|
19
|
+
path,
|
|
20
|
+
severity: "error"
|
|
21
|
+
});
|
|
22
|
+
var createEmptyStudioDocument = (title) => ({
|
|
23
|
+
schemaVersion: 1,
|
|
24
|
+
title,
|
|
25
|
+
nodes: [],
|
|
26
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27
|
+
});
|
|
28
|
+
function assertStudioDocumentV1(input) {
|
|
29
|
+
if (!isRecord(input)) {
|
|
30
|
+
throw new Error("Studio document must be an object");
|
|
31
|
+
}
|
|
32
|
+
if (input.schemaVersion !== 1) {
|
|
33
|
+
throw new Error("Unsupported studio schemaVersion");
|
|
34
|
+
}
|
|
35
|
+
if (!Array.isArray(input.nodes)) {
|
|
36
|
+
throw new Error("Studio document nodes must be an array");
|
|
37
|
+
}
|
|
38
|
+
const nodes = input.nodes.map((node, index) => {
|
|
39
|
+
if (!isRecord(node)) {
|
|
40
|
+
throw new Error(`Node at index ${index} must be an object`);
|
|
41
|
+
}
|
|
42
|
+
if (typeof node.id !== "string" || node.id.length === 0) {
|
|
43
|
+
throw new Error(`Node at index ${index} has invalid id`);
|
|
44
|
+
}
|
|
45
|
+
if (typeof node.type !== "string" || node.type.length === 0) {
|
|
46
|
+
throw new Error(`Node at index ${index} has invalid type`);
|
|
47
|
+
}
|
|
48
|
+
if (!isRecord(node.data)) {
|
|
49
|
+
throw new Error(`Node at index ${index} has invalid data`);
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
id: node.id,
|
|
53
|
+
type: node.type,
|
|
54
|
+
data: node.data
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
schemaVersion: 1,
|
|
59
|
+
title: typeof input.title === "string" ? input.title : void 0,
|
|
60
|
+
nodes,
|
|
61
|
+
updatedAt: typeof input.updatedAt === "string" ? input.updatedAt : void 0
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createStudioRegistry(modules) {
|
|
65
|
+
const moduleByID = new Map(modules.map((mod) => [mod.id, mod]));
|
|
66
|
+
const nodeTypes = modules.flatMap((mod) => mod.nodeTypes);
|
|
67
|
+
const nodeTypeByName = new Map(nodeTypes.map((definition) => [definition.type, definition]));
|
|
68
|
+
return {
|
|
69
|
+
getModuleByID: (id) => moduleByID.get(id),
|
|
70
|
+
getNodeTypeByName: (type) => nodeTypeByName.get(type),
|
|
71
|
+
listInspectorPanels: () => modules.flatMap((mod) => mod.inspectorPanels),
|
|
72
|
+
listModules: () => [...modules],
|
|
73
|
+
listPaletteGroups: () => modules.flatMap((mod) => mod.paletteGroups),
|
|
74
|
+
listNodeTypes: () => [...nodeTypes]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function validateStudioDocument(document, modules) {
|
|
78
|
+
const issues = [];
|
|
79
|
+
if (document.schemaVersion !== 1) {
|
|
80
|
+
issues.push(makeIssue("Unsupported schema version", "schemaVersion", "studio.schemaVersion"));
|
|
81
|
+
}
|
|
82
|
+
if (!Array.isArray(document.nodes)) {
|
|
83
|
+
issues.push(makeIssue("Nodes must be an array", "nodes", "studio.nodes"));
|
|
84
|
+
return issues;
|
|
85
|
+
}
|
|
86
|
+
const registry = createStudioRegistry(modules);
|
|
87
|
+
const nodeIDs = /* @__PURE__ */ new Set();
|
|
88
|
+
document.nodes.forEach((node, index) => {
|
|
89
|
+
if (!node.id) {
|
|
90
|
+
issues.push(makeIssue("Node id is required", `nodes.${index}.id`, "studio.node.id"));
|
|
91
|
+
}
|
|
92
|
+
if (nodeIDs.has(node.id)) {
|
|
93
|
+
issues.push(makeIssue("Node id must be unique", `nodes.${index}.id`, "studio.node.id.duplicate"));
|
|
94
|
+
}
|
|
95
|
+
nodeIDs.add(node.id);
|
|
96
|
+
if (!registry.getNodeTypeByName(node.type)) {
|
|
97
|
+
issues.push(makeIssue("Unsupported node type", `nodes.${index}.type`, "studio.node.type"));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
for (const module of modules) {
|
|
101
|
+
for (const validate of module.validators) {
|
|
102
|
+
issues.push(...validate(document));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return issues;
|
|
106
|
+
}
|
|
107
|
+
function compileStudioDocument(document, modules) {
|
|
108
|
+
const issues = validateStudioDocument(document, modules);
|
|
109
|
+
const compilerEntries = modules.filter((mod) => typeof mod.compiler?.compileNode === "function").map((mod) => mod.compiler?.compileNode);
|
|
110
|
+
const layout = document.nodes.map((node) => {
|
|
111
|
+
for (const compileNode of compilerEntries) {
|
|
112
|
+
if (!compileNode) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const compiled = compileNode(node);
|
|
116
|
+
if (compiled) {
|
|
117
|
+
return compiled;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
id: node.id,
|
|
122
|
+
blockType: node.type,
|
|
123
|
+
...node.data
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
issues,
|
|
128
|
+
layout
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function migrateStudioDocument(value, migrations) {
|
|
132
|
+
const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);
|
|
133
|
+
if (isRecord(value) && value.schemaVersion === 1) {
|
|
134
|
+
return assertStudioDocumentV1(value);
|
|
135
|
+
}
|
|
136
|
+
let current = value;
|
|
137
|
+
for (const migration of sorted) {
|
|
138
|
+
if (!isRecord(current) || current.schemaVersion !== migration.fromVersion) {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
current = migration.migrate(current);
|
|
142
|
+
}
|
|
143
|
+
return assertStudioDocumentV1(current);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export {
|
|
147
|
+
createEmptyStudioDocument,
|
|
148
|
+
assertStudioDocumentV1,
|
|
149
|
+
createStudioRegistry,
|
|
150
|
+
validateStudioDocument,
|
|
151
|
+
compileStudioDocument,
|
|
152
|
+
migrateStudioDocument,
|
|
153
|
+
studio_exports
|
|
154
|
+
};
|