@sudajs/cli 0.13.1 → 0.13.2
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/package.json +2 -2
- package/templates/theme/AGENTS.md +161 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sudajs/cli",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"suda": "./bin/suda.js"
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"react": "^19.2.7",
|
|
35
35
|
"react-dom": "^19.2.7",
|
|
36
36
|
"zod": "^3.24.1",
|
|
37
|
-
"@sudajs/theme-engine": "5.1.
|
|
37
|
+
"@sudajs/theme-engine": "5.1.2"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@tailwindcss/postcss": "^4.3.0",
|
|
@@ -71,6 +71,28 @@ one pass:
|
|
|
71
71
|
8. Run `pnpm typecheck`, `pnpm lint`, `pnpm build`, and `pnpm validate` before
|
|
72
72
|
handoff.
|
|
73
73
|
|
|
74
|
+
Rendering fallback rule:
|
|
75
|
+
|
|
76
|
+
- Defaults belong in `defaultProps`, starter page data, or CMS template data.
|
|
77
|
+
During render, read values directly from props. Do not add fallback display
|
|
78
|
+
values with `||`, `??`, or ternaries unless the fallback is truly required for
|
|
79
|
+
runtime safety.
|
|
80
|
+
- This is wrong because render invents content that is not in props:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
<div>{props.title || "title"}</div>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- This is correct because render only displays the authored prop value:
|
|
87
|
+
|
|
88
|
+
```tsx
|
|
89
|
+
<div>{props.title}</div>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- Conditional rendering is fine when absence intentionally removes optional UI,
|
|
93
|
+
such as hiding an optional image, button, or eyebrow. Do not use conditional
|
|
94
|
+
rendering to substitute placeholder copy, labels, links, or menu items.
|
|
95
|
+
|
|
74
96
|
Configure ordinary fields like this:
|
|
75
97
|
|
|
76
98
|
```ts
|
|
@@ -143,6 +165,145 @@ Field source of truth:
|
|
|
143
165
|
`range`, `spacing`, `media`, `image`, `video`, and `posts`. These are valid
|
|
144
166
|
in `fields` and are normalized by `@sudajs/theme-engine`.
|
|
145
167
|
|
|
168
|
+
## Navigation and footer menus
|
|
169
|
+
|
|
170
|
+
Navigation and footer menus must be typed arrays. Do not use the legacy menu
|
|
171
|
+
field type, multiline text areas, or newline-delimited strings to model menus.
|
|
172
|
+
Every link destination must use a `url` field type, never a plain `text` field.
|
|
173
|
+
The `fields`, TypeScript props, and `defaultProps` data structures must match
|
|
174
|
+
exactly.
|
|
175
|
+
|
|
176
|
+
Navigation supports at most two levels:
|
|
177
|
+
|
|
178
|
+
- `navItems[]`
|
|
179
|
+
- `navItems[].submenu[]`
|
|
180
|
+
|
|
181
|
+
Submenu items may contain only text and link fields. Do not add another nested
|
|
182
|
+
submenu field inside `navItems[].submenu[]`; the field shape must make third
|
|
183
|
+
levels impossible. An empty `submenu` array means the item is an ordinary
|
|
184
|
+
top-level link.
|
|
185
|
+
|
|
186
|
+
The simplest Navigation field shape is:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
type NavSubItem = {
|
|
190
|
+
label: string;
|
|
191
|
+
href: string;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
type NavItem = {
|
|
195
|
+
label: string;
|
|
196
|
+
href: string;
|
|
197
|
+
submenu: NavSubItem[];
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const navItemsField = {
|
|
201
|
+
type: "array",
|
|
202
|
+
label: t("common.fields.navigationItems"),
|
|
203
|
+
getItemSummary: (item: NavItem) => item.label || "Navigation item",
|
|
204
|
+
defaultItemProps: {
|
|
205
|
+
label: "Home",
|
|
206
|
+
href: "/",
|
|
207
|
+
submenu: [],
|
|
208
|
+
},
|
|
209
|
+
arrayFields: {
|
|
210
|
+
label: {
|
|
211
|
+
type: "text",
|
|
212
|
+
label: t("common.fields.label"),
|
|
213
|
+
},
|
|
214
|
+
href: {
|
|
215
|
+
type: "url",
|
|
216
|
+
label: t("common.fields.link"),
|
|
217
|
+
},
|
|
218
|
+
submenu: {
|
|
219
|
+
type: "array",
|
|
220
|
+
label: t("common.fields.submenu"),
|
|
221
|
+
getItemSummary: (item: NavSubItem) => item.label || "Submenu item",
|
|
222
|
+
arrayFields: {
|
|
223
|
+
label: {
|
|
224
|
+
type: "text",
|
|
225
|
+
label: t("common.fields.label"),
|
|
226
|
+
},
|
|
227
|
+
href: {
|
|
228
|
+
type: "url",
|
|
229
|
+
label: t("common.fields.link"),
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const defaultNavItems: NavItem[] = [
|
|
237
|
+
{
|
|
238
|
+
label: "Home",
|
|
239
|
+
href: "/",
|
|
240
|
+
submenu: [],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
label: "Company",
|
|
244
|
+
href: "/company",
|
|
245
|
+
submenu: [
|
|
246
|
+
{
|
|
247
|
+
label: "About",
|
|
248
|
+
href: "/about",
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
];
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Footer grouped menus use two array levels:
|
|
256
|
+
|
|
257
|
+
- `columns[]`
|
|
258
|
+
- `columns[].links[]`
|
|
259
|
+
|
|
260
|
+
Use this prop shape and mirror it exactly in the field config and defaults:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
type FooterColumn = {
|
|
264
|
+
title: string;
|
|
265
|
+
links: {
|
|
266
|
+
text: string;
|
|
267
|
+
url: string;
|
|
268
|
+
}[];
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const footerColumnsField = {
|
|
272
|
+
type: "array",
|
|
273
|
+
label: t("common.fields.columns"),
|
|
274
|
+
getItemSummary: (item: FooterColumn) => item.title || "Footer column",
|
|
275
|
+
defaultItemProps: {
|
|
276
|
+
title: "Company",
|
|
277
|
+
links: [{ text: "About", url: "/about" }],
|
|
278
|
+
},
|
|
279
|
+
arrayFields: {
|
|
280
|
+
title: {
|
|
281
|
+
type: "text",
|
|
282
|
+
label: t("common.fields.title"),
|
|
283
|
+
},
|
|
284
|
+
links: {
|
|
285
|
+
type: "array",
|
|
286
|
+
label: t("common.fields.links"),
|
|
287
|
+
getItemSummary: (item: FooterColumn["links"][number]) => item.text || "Footer link",
|
|
288
|
+
defaultItemProps: {
|
|
289
|
+
text: "About",
|
|
290
|
+
url: "/about",
|
|
291
|
+
},
|
|
292
|
+
arrayFields: {
|
|
293
|
+
text: {
|
|
294
|
+
type: "text",
|
|
295
|
+
label: t("common.fields.label"),
|
|
296
|
+
},
|
|
297
|
+
url: {
|
|
298
|
+
type: "url",
|
|
299
|
+
label: t("common.fields.link"),
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
};
|
|
305
|
+
```
|
|
306
|
+
|
|
146
307
|
Use these field patterns:
|
|
147
308
|
|
|
148
309
|
```ts
|
|
@@ -236,8 +397,6 @@ defaultProps: {
|
|
|
236
397
|
download: "",
|
|
237
398
|
icon: "sparkles",
|
|
238
399
|
accentColor: "#2563eb",
|
|
239
|
-
font: "system",
|
|
240
|
-
links: [{ label: "Home", url: "/" }],
|
|
241
400
|
spacing: "md",
|
|
242
401
|
postList: { strategy: "featured", limit: 3 },
|
|
243
402
|
badge: { label: "New", tone: "primary" },
|