@sudajs/cli 0.10.4 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +712 -0
- package/dist/index.js +199 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/templates/theme/AGENTS.md +358 -21
- package/templates/theme/package.json +2 -2
- package/templates/theme/src/index.tsx +3 -2
- package/templates/theme/src/layout.tsx +26 -8
- package/templates/theme/src/locales/en.json +10 -1
- package/templates/theme/src/manifest.ts +49 -1
- package/templates/theme/src/sections.tsx +73 -2
- package/templates/theme/src/styles.css +20 -20
- package/templates/theme/src/templates.ts +151 -6
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { SudaComponentConfig, SudaLucideIconName } from "@sudajs/theme-engine";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getPostResource,
|
|
4
|
+
resolveAsset,
|
|
5
|
+
SudaLucideIcon,
|
|
6
|
+
type ThemePostResourceQuery,
|
|
7
|
+
type ThemeRenderMetadata,
|
|
8
|
+
} from "@sudajs/theme-engine/runtime";
|
|
3
9
|
|
|
4
10
|
import { t } from "./i18n.js";
|
|
5
11
|
import { themeAsset } from "./theme-asset.js";
|
|
@@ -32,6 +38,12 @@ type CallToActionProps = {
|
|
|
32
38
|
buttonLabel?: string;
|
|
33
39
|
buttonHref?: string;
|
|
34
40
|
};
|
|
41
|
+
type FeaturedPostsProps = {
|
|
42
|
+
id?: string;
|
|
43
|
+
title?: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
postList?: ThemePostResourceQuery;
|
|
46
|
+
} & PuckExtras;
|
|
35
47
|
|
|
36
48
|
export const Hero: SudaComponentConfig<HeroProps> = {
|
|
37
49
|
label: t("sections.hero.label"),
|
|
@@ -209,4 +221,63 @@ export const CallToAction: SudaComponentConfig<CallToActionProps> = {
|
|
|
209
221
|
),
|
|
210
222
|
};
|
|
211
223
|
|
|
212
|
-
export const
|
|
224
|
+
export const FeaturedPosts: SudaComponentConfig<FeaturedPostsProps> = {
|
|
225
|
+
label: t("sections.featuredPosts.label"),
|
|
226
|
+
ai: {
|
|
227
|
+
instructions:
|
|
228
|
+
"Reusable post resource section for ordinary pages such as home, services, cases, or insights teasers. " +
|
|
229
|
+
"Use it when a page should show dynamic CMS posts such as latest articles, featured posts, or posts from a specific tag. " +
|
|
230
|
+
"Set the posts field prop; do not generate static article cards.",
|
|
231
|
+
},
|
|
232
|
+
fields: {
|
|
233
|
+
title: { type: "text", label: t("sections.featuredPosts.fields.title") },
|
|
234
|
+
description: { type: "textarea", label: t("sections.featuredPosts.fields.description") },
|
|
235
|
+
postList: { type: "posts", label: t("sections.featuredPosts.fields.postList") },
|
|
236
|
+
},
|
|
237
|
+
defaultProps: {
|
|
238
|
+
title: "Featured insights",
|
|
239
|
+
description: "Highlight recent or strategically important posts from the CMS.",
|
|
240
|
+
postList: { strategy: "featured", limit: 3 },
|
|
241
|
+
},
|
|
242
|
+
render: ({ id, title, description, puck }) => {
|
|
243
|
+
const resource = getPostResource(puck?.metadata, {
|
|
244
|
+
componentId: id,
|
|
245
|
+
fieldKey: "postList",
|
|
246
|
+
});
|
|
247
|
+
const posts = resource.posts;
|
|
248
|
+
return (
|
|
249
|
+
<section className="__SUDA_THEME_KEY__-section">
|
|
250
|
+
<h2>{title}</h2>
|
|
251
|
+
<p>{description}</p>
|
|
252
|
+
<div className="__SUDA_THEME_KEY__-grid" style={{ "--__SUDA_THEME_KEY__-columns": 3 }}>
|
|
253
|
+
{posts.length > 0 ? (
|
|
254
|
+
posts.map((post) => (
|
|
255
|
+
<article className="__SUDA_THEME_KEY__-card" key={post.id}>
|
|
256
|
+
<p className="__SUDA_THEME_KEY__-eyebrow">
|
|
257
|
+
{post.tags[0]?.name ?? "Post"}
|
|
258
|
+
</p>
|
|
259
|
+
<h3>{post.title}</h3>
|
|
260
|
+
<p>{post.excerpt}</p>
|
|
261
|
+
<a href={post.url}>{post.title}</a>
|
|
262
|
+
</article>
|
|
263
|
+
))
|
|
264
|
+
) : (
|
|
265
|
+
<article className="__SUDA_THEME_KEY__-card">
|
|
266
|
+
<p className="__SUDA_THEME_KEY__-eyebrow">CMS</p>
|
|
267
|
+
<h3>No posts yet</h3>
|
|
268
|
+
<p>Create and publish posts in SudaCloud to populate this section.</p>
|
|
269
|
+
</article>
|
|
270
|
+
)}
|
|
271
|
+
</div>
|
|
272
|
+
</section>
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export const SECTION_COMPONENTS = {
|
|
278
|
+
Hero,
|
|
279
|
+
FeatureGrid,
|
|
280
|
+
FeaturedPosts,
|
|
281
|
+
Testimonial,
|
|
282
|
+
CallToAction,
|
|
283
|
+
};
|
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
|
|
4
4
|
.__SUDA_THEME_KEY__-root {
|
|
5
5
|
font-family: Inter, ui-sans-serif, system-ui, sans-serif;
|
|
6
|
-
color: #111827;
|
|
7
|
-
background: #ffffff;
|
|
6
|
+
color: var(--suda-color-foreground, #111827);
|
|
7
|
+
background: var(--suda-color-background, #ffffff);
|
|
8
8
|
}
|
|
9
9
|
.__SUDA_THEME_KEY__-header,
|
|
10
10
|
.__SUDA_THEME_KEY__-footer {
|
|
11
11
|
padding: 20px clamp(20px, 5vw, 64px);
|
|
12
|
-
border-bottom: 1px solid #e5e7eb;
|
|
12
|
+
border-bottom: 1px solid var(--suda-color-muted, #e5e7eb);
|
|
13
13
|
}
|
|
14
14
|
.__SUDA_THEME_KEY__-footer {
|
|
15
15
|
display: flex;
|
|
16
16
|
flex-wrap: wrap;
|
|
17
17
|
align-items: center;
|
|
18
18
|
gap: 12px 20px;
|
|
19
|
-
border-top: 1px solid #e5e7eb;
|
|
19
|
+
border-top: 1px solid var(--suda-color-muted, #e5e7eb);
|
|
20
20
|
border-bottom: 0;
|
|
21
|
-
color: #6b7280;
|
|
21
|
+
color: var(--suda-color-muted-foreground, #6b7280);
|
|
22
22
|
}
|
|
23
23
|
.__SUDA_THEME_KEY__-footer a {
|
|
24
24
|
display: inline-flex;
|
|
@@ -35,19 +35,19 @@
|
|
|
35
35
|
padding: 64px clamp(20px, 5vw, 64px);
|
|
36
36
|
}
|
|
37
37
|
.__SUDA_THEME_KEY__-hero {
|
|
38
|
-
background: #f8fafc;
|
|
38
|
+
background: var(--suda-color-accent, #f8fafc);
|
|
39
39
|
}
|
|
40
40
|
.__SUDA_THEME_KEY__-hero-logo {
|
|
41
41
|
width: 64px;
|
|
42
42
|
height: 64px;
|
|
43
|
-
border-radius: 14px;
|
|
43
|
+
border-radius: var(--suda-radius-card, 14px);
|
|
44
44
|
margin-bottom: 24px;
|
|
45
45
|
}
|
|
46
46
|
.__SUDA_THEME_KEY__-eyebrow {
|
|
47
47
|
text-transform: uppercase;
|
|
48
48
|
letter-spacing: 0.08em;
|
|
49
49
|
font-size: 12px;
|
|
50
|
-
color: #2563eb;
|
|
50
|
+
color: var(--suda-color-primary, #2563eb);
|
|
51
51
|
font-weight: 700;
|
|
52
52
|
}
|
|
53
53
|
.__SUDA_THEME_KEY__-section h1 {
|
|
@@ -65,16 +65,16 @@
|
|
|
65
65
|
.__SUDA_THEME_KEY__-section p {
|
|
66
66
|
max-width: 680px;
|
|
67
67
|
line-height: 1.7;
|
|
68
|
-
color: #4b5563;
|
|
68
|
+
color: var(--suda-color-muted-foreground, #4b5563);
|
|
69
69
|
}
|
|
70
70
|
.__SUDA_THEME_KEY__-button {
|
|
71
71
|
display: inline-flex;
|
|
72
72
|
align-items: center;
|
|
73
73
|
min-height: 42px;
|
|
74
74
|
padding: 0 18px;
|
|
75
|
-
border-radius: 8px;
|
|
76
|
-
background: #111827;
|
|
77
|
-
color: #ffffff;
|
|
75
|
+
border-radius: var(--suda-radius-button, 8px);
|
|
76
|
+
background: var(--suda-color-primary, #111827);
|
|
77
|
+
color: var(--suda-color-primary-foreground, #ffffff);
|
|
78
78
|
text-decoration: none;
|
|
79
79
|
font-weight: 700;
|
|
80
80
|
}
|
|
@@ -85,14 +85,14 @@
|
|
|
85
85
|
margin-top: 28px;
|
|
86
86
|
}
|
|
87
87
|
.__SUDA_THEME_KEY__-card {
|
|
88
|
-
border: 1px solid #e5e7eb;
|
|
89
|
-
border-radius: 8px;
|
|
88
|
+
border: 1px solid var(--suda-color-muted, #e5e7eb);
|
|
89
|
+
border-radius: var(--suda-radius-card, 8px);
|
|
90
90
|
padding: 20px;
|
|
91
91
|
}
|
|
92
92
|
.__SUDA_THEME_KEY__-card-icon {
|
|
93
93
|
width: 24px;
|
|
94
94
|
height: 24px;
|
|
95
|
-
color: #2563eb;
|
|
95
|
+
color: var(--suda-color-primary, #2563eb);
|
|
96
96
|
}
|
|
97
97
|
.__SUDA_THEME_KEY__-quote blockquote {
|
|
98
98
|
max-width: 780px;
|
|
@@ -101,15 +101,15 @@
|
|
|
101
101
|
margin: 0 0 16px;
|
|
102
102
|
}
|
|
103
103
|
.__SUDA_THEME_KEY__-cta {
|
|
104
|
-
background: #111827;
|
|
105
|
-
color: #ffffff;
|
|
104
|
+
background: var(--suda-color-foreground, #111827);
|
|
105
|
+
color: var(--suda-color-background, #ffffff);
|
|
106
106
|
}
|
|
107
107
|
.__SUDA_THEME_KEY__-cta p {
|
|
108
|
-
color: #d1d5db;
|
|
108
|
+
color: var(--suda-color-muted, #d1d5db);
|
|
109
109
|
}
|
|
110
110
|
.__SUDA_THEME_KEY__-cta .__SUDA_THEME_KEY__-button {
|
|
111
|
-
background: #ffffff;
|
|
112
|
-
color: #111827;
|
|
111
|
+
background: var(--suda-color-background, #ffffff);
|
|
112
|
+
color: var(--suda-color-foreground, #111827);
|
|
113
113
|
}
|
|
114
114
|
@media (max-width: 760px) {
|
|
115
115
|
.__SUDA_THEME_KEY__-grid {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { ThemeStarterPage } from "@sudajs/theme-engine";
|
|
1
|
+
import type { ThemeCmsTemplates, ThemeStarterPage } from "@sudajs/theme-engine";
|
|
2
2
|
|
|
3
3
|
import { themeAsset } from "./theme-asset.js";
|
|
4
4
|
|
|
5
5
|
export const starterPages: ThemeStarterPage[] = [
|
|
6
6
|
{
|
|
7
|
-
slug: "
|
|
7
|
+
slug: "index",
|
|
8
8
|
title: "Home",
|
|
9
9
|
isHome: true,
|
|
10
10
|
data: {
|
|
@@ -35,6 +35,15 @@ export const starterPages: ThemeStarterPage[] = [
|
|
|
35
35
|
],
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
type: "FeaturedPosts",
|
|
40
|
+
props: {
|
|
41
|
+
id: "FeaturedPosts-1",
|
|
42
|
+
title: "Featured posts",
|
|
43
|
+
description: "Use CMS posts to keep the homepage fresh without manual card editing.",
|
|
44
|
+
postList: { strategy: "featured", limit: 3 },
|
|
45
|
+
},
|
|
46
|
+
},
|
|
38
47
|
{
|
|
39
48
|
type: "CallToAction",
|
|
40
49
|
props: {
|
|
@@ -42,14 +51,71 @@ export const starterPages: ThemeStarterPage[] = [
|
|
|
42
51
|
title: "Launch your first page",
|
|
43
52
|
description: "Customize this starter template or let an agent generate a new draft.",
|
|
44
53
|
buttonLabel: "Get in touch",
|
|
45
|
-
buttonHref: "/contact",
|
|
54
|
+
buttonHref: "/contact-us",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
slug: "services",
|
|
62
|
+
title: "Services",
|
|
63
|
+
data: {
|
|
64
|
+
root: { props: {} },
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "Hero",
|
|
68
|
+
props: {
|
|
69
|
+
id: "Hero-Services",
|
|
70
|
+
eyebrow: "Services",
|
|
71
|
+
title: "Show what you offer",
|
|
72
|
+
description: "Use this page to explain core services, packages, or capabilities.",
|
|
73
|
+
primaryLabel: "Contact us",
|
|
74
|
+
primaryHref: "/contact-us",
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
type: "FeatureGrid",
|
|
79
|
+
props: {
|
|
80
|
+
id: "FeatureGrid-Services",
|
|
81
|
+
title: "Service highlights",
|
|
82
|
+
description: "Replace these cards with the most important ways you help customers.",
|
|
46
83
|
},
|
|
47
84
|
},
|
|
48
85
|
],
|
|
49
86
|
},
|
|
50
87
|
},
|
|
51
88
|
{
|
|
52
|
-
slug: "
|
|
89
|
+
slug: "team",
|
|
90
|
+
title: "Team",
|
|
91
|
+
data: {
|
|
92
|
+
root: { props: {} },
|
|
93
|
+
content: [
|
|
94
|
+
{
|
|
95
|
+
type: "Hero",
|
|
96
|
+
props: {
|
|
97
|
+
id: "Hero-Team",
|
|
98
|
+
eyebrow: "Team",
|
|
99
|
+
title: "Introduce the people behind the work",
|
|
100
|
+
description: "Share experience, roles, and the human story behind the project.",
|
|
101
|
+
primaryLabel: "Work with us",
|
|
102
|
+
primaryHref: "/contact-us",
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
type: "Testimonial",
|
|
107
|
+
props: {
|
|
108
|
+
id: "Testimonial-Team",
|
|
109
|
+
quote: "Use this block for a founder note, client quote, or team philosophy.",
|
|
110
|
+
author: "Team lead",
|
|
111
|
+
role: "Founder",
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
slug: "about-us",
|
|
53
119
|
title: "About",
|
|
54
120
|
data: {
|
|
55
121
|
root: { props: {} },
|
|
@@ -62,7 +128,7 @@ export const starterPages: ThemeStarterPage[] = [
|
|
|
62
128
|
title: "A clean starting point for your story",
|
|
63
129
|
description: "Use this page to introduce the project, audience, and promise.",
|
|
64
130
|
primaryLabel: "Contact us",
|
|
65
|
-
primaryHref: "/contact",
|
|
131
|
+
primaryHref: "/contact-us",
|
|
66
132
|
},
|
|
67
133
|
},
|
|
68
134
|
{
|
|
@@ -78,7 +144,7 @@ export const starterPages: ThemeStarterPage[] = [
|
|
|
78
144
|
},
|
|
79
145
|
},
|
|
80
146
|
{
|
|
81
|
-
slug: "contact",
|
|
147
|
+
slug: "contact-us",
|
|
82
148
|
title: "Contact",
|
|
83
149
|
data: {
|
|
84
150
|
root: { props: {} },
|
|
@@ -108,3 +174,82 @@ export const starterPages: ThemeStarterPage[] = [
|
|
|
108
174
|
},
|
|
109
175
|
},
|
|
110
176
|
];
|
|
177
|
+
|
|
178
|
+
export const cmsTemplates: ThemeCmsTemplates = {
|
|
179
|
+
posts: {
|
|
180
|
+
title: "Posts",
|
|
181
|
+
data: {
|
|
182
|
+
root: { props: {} },
|
|
183
|
+
content: [
|
|
184
|
+
{
|
|
185
|
+
type: "Hero",
|
|
186
|
+
props: {
|
|
187
|
+
id: "Hero-Posts",
|
|
188
|
+
eyebrow: "Articles",
|
|
189
|
+
title: "Latest updates",
|
|
190
|
+
description: "Browse news, articles, and practical insights.",
|
|
191
|
+
primaryLabel: "View tags",
|
|
192
|
+
primaryHref: "/tags",
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
],
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
post: {
|
|
199
|
+
title: "Post",
|
|
200
|
+
data: {
|
|
201
|
+
root: { props: {} },
|
|
202
|
+
content: [
|
|
203
|
+
{
|
|
204
|
+
type: "Hero",
|
|
205
|
+
props: {
|
|
206
|
+
id: "Hero-Post",
|
|
207
|
+
eyebrow: "Article",
|
|
208
|
+
title: "Article detail",
|
|
209
|
+
description: "This template renders a single published post.",
|
|
210
|
+
primaryLabel: "Back to posts",
|
|
211
|
+
primaryHref: "/posts",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
tags: {
|
|
218
|
+
title: "Tags",
|
|
219
|
+
data: {
|
|
220
|
+
root: { props: {} },
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "Hero",
|
|
224
|
+
props: {
|
|
225
|
+
id: "Hero-Tags",
|
|
226
|
+
eyebrow: "Topics",
|
|
227
|
+
title: "Browse by topic",
|
|
228
|
+
description: "Explore tags used across published posts.",
|
|
229
|
+
primaryLabel: "View posts",
|
|
230
|
+
primaryHref: "/posts",
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
tag: {
|
|
237
|
+
title: "Tag",
|
|
238
|
+
data: {
|
|
239
|
+
root: { props: {} },
|
|
240
|
+
content: [
|
|
241
|
+
{
|
|
242
|
+
type: "Hero",
|
|
243
|
+
props: {
|
|
244
|
+
id: "Hero-Tag",
|
|
245
|
+
eyebrow: "Topic",
|
|
246
|
+
title: "Topic posts",
|
|
247
|
+
description: "This template renders posts filtered by the current tag.",
|
|
248
|
+
primaryLabel: "All posts",
|
|
249
|
+
primaryHref: "/posts",
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
};
|