@sudajs/cli 0.11.0 → 0.12.1
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 +157 -2
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/templates/theme/AGENTS.md +73 -4
- package/templates/theme/package.json +2 -2
- package/templates/theme/src/layout.tsx +26 -8
- package/templates/theme/src/locales/en.json +2 -1
- package/templates/theme/src/manifest.ts +49 -1
- package/templates/theme/src/sections.tsx +4 -1
- package/templates/theme/src/styles.css +20 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sudajs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"suda": "./bin/suda.js"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
28
|
-
"@puckeditor/core": "^0.
|
|
28
|
+
"@puckeditor/core": "^0.22.0",
|
|
29
29
|
"commander": "^12.1.0",
|
|
30
30
|
"esbuild": "^0.25.12",
|
|
31
31
|
"lucide-react": "^1.17.0",
|
|
@@ -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": "
|
|
37
|
+
"@sudajs/theme-engine": "4.0.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@tailwindcss/postcss": "^4.3.0",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"typescript": "^5.7.2",
|
|
50
50
|
"vite": "^7.3.3",
|
|
51
51
|
"vitest": "^3.2.4",
|
|
52
|
-
"@suda/eslint-config": "0.0.0",
|
|
53
52
|
"@suda/build-config": "0.0.0",
|
|
53
|
+
"@suda/eslint-config": "0.0.0",
|
|
54
54
|
"@suda/tsconfig": "0.0.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|
|
@@ -320,10 +320,18 @@ defaultProps: {
|
|
|
320
320
|
},
|
|
321
321
|
```
|
|
322
322
|
|
|
323
|
-
Supported strategies are `latest`, `featured`, `by_tag`, and `manual`.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
323
|
+
Supported strategies are `latest`, `featured`, `by_tag`, and `manual`. Manual
|
|
324
|
+
queries store only `items` and must not include `limit`. Runtime post resources
|
|
325
|
+
are keyed as `${props.id}.${fieldKey}` with both parts URL-encoded by the
|
|
326
|
+
platform. In the section render function, call
|
|
327
|
+
`getPostResource(puck?.metadata, { componentId: props.id, fieldKey: "postList" })`
|
|
328
|
+
with the exact posts field key. Never import Prisma, call platform APIs, or
|
|
329
|
+
query the database from theme code.
|
|
330
|
+
|
|
331
|
+
Do not add `resourceQuery: { type: "posts" }` or
|
|
332
|
+
`resource_query: { type: "posts" }`. Do not assume the field must be named
|
|
333
|
+
`query`. A section may have only one top-level posts field; do not nest posts
|
|
334
|
+
fields inside `object`, `array`, native slots, or `blockSlots`.
|
|
327
335
|
|
|
328
336
|
Starter pages must include one home/index page with `isHome: true`. Recommended
|
|
329
337
|
starter pages include `index`, `about-us`, `contact-us`, `services`, and `team`.
|
|
@@ -406,6 +414,67 @@ export const Hero: SudaComponentConfig<HeroProps> = {
|
|
|
406
414
|
- If a page section needs controlled nested content, use Suda `blockSlots`, not a hand-written Puck slot field. `blockSlots` lets the theme define exactly which local block kinds are allowed inside that section.
|
|
407
415
|
- Do not use legacy DropZone or `zones` patterns.
|
|
408
416
|
|
|
417
|
+
## Design system rules
|
|
418
|
+
|
|
419
|
+
Build every theme from one theme-level design system. Do not let each section,
|
|
420
|
+
CMS template, block slot, or starter page invent its own colors, type scale,
|
|
421
|
+
radius, shadows, or spacing.
|
|
422
|
+
|
|
423
|
+
- Define the editable theme system once in `src/manifest.ts` as
|
|
424
|
+
`sourceManifest.designSystem`. Include `version`, `defaultPresetId`, and
|
|
425
|
+
named `presets` with complete token sets.
|
|
426
|
+
- Use stable token names from the engine contract. At minimum, provide the
|
|
427
|
+
color tokens the theme renders (`background`, `foreground`, `primary`,
|
|
428
|
+
`primaryForeground`, `accent`, `accentForeground`, `muted`,
|
|
429
|
+
`mutedForeground`) and the radius tokens the CSS consumes (`card`, `button`,
|
|
430
|
+
`input`). Do not invent platform-facing token keys without checking the
|
|
431
|
+
generated types first.
|
|
432
|
+
- Expose the design system at the layout root only:
|
|
433
|
+
`designSystem: designSystemField(t("common.fields.designSystem"),
|
|
434
|
+
sourceManifest.designSystem)`, with
|
|
435
|
+
`createThemeDesignDefault(sourceManifest.designSystem)` in `ROOT_DEFAULTS`.
|
|
436
|
+
- In the root render, resolve the value once with
|
|
437
|
+
`resolveThemeDesignTokens(sourceManifest.designSystem, props.designSystem)`
|
|
438
|
+
and apply `createThemeDesignCssVariables(tokens)` to the theme root element.
|
|
439
|
+
Components should consume CSS variables; they should not resolve design
|
|
440
|
+
presets themselves.
|
|
441
|
+
- In `src/styles.css`, map Suda variables to theme-local or Tailwind variables
|
|
442
|
+
once, for example `--color-primary: var(--suda-color-primary, #...)` and
|
|
443
|
+
`--radius-card: var(--suda-radius-card, 24px)`. Use those variables across
|
|
444
|
+
sections, cards, CMS layouts, post lists, forms, and local blocks.
|
|
445
|
+
- Keep typography and spacing scales centralized in `src/styles.css`. Define
|
|
446
|
+
reusable classes or variables for containers, section padding, headings,
|
|
447
|
+
eyebrow text, body copy, cards, buttons, inputs, and media frames. Reuse those
|
|
448
|
+
classes instead of writing new one-off Tailwind values in every section.
|
|
449
|
+
- Section props may expose semantic choices such as `tone`, `variant`,
|
|
450
|
+
`columns`, `mediaPosition`, `showImage`, or `spacing`. Map those choices to
|
|
451
|
+
the existing design tokens and shared CSS classes.
|
|
452
|
+
- Do not add raw `color`, `font`, `spacing`, `radius`, or `shadow` fields to a
|
|
453
|
+
section just because the CSS has a value. Expose a design token field only
|
|
454
|
+
when the user should intentionally customize that value per component
|
|
455
|
+
instance. Otherwise, keep the value in the theme CSS.
|
|
456
|
+
- Use `color`, `font`, and `spacing` field types only for real editor-facing
|
|
457
|
+
design controls. Use `select`/`radio` for named variants that are already
|
|
458
|
+
part of the theme design system.
|
|
459
|
+
- Local blocks inside `blockSlots` inherit the host section's design system.
|
|
460
|
+
Local block fields should edit content or semantic variants, not define their
|
|
461
|
+
own palette, type scale, radius, shadows, or spacing.
|
|
462
|
+
- CMS main sections (`MainPosts`, `MainPost`, `MainTags`, `MainTagPosts`) and
|
|
463
|
+
ordinary post-resource sections must use the same containers, heading scale,
|
|
464
|
+
card style, media ratio, pagination style, and empty/loading states as the
|
|
465
|
+
rest of the theme.
|
|
466
|
+
- Starter pages must demonstrate the same design system across different page
|
|
467
|
+
types. Vary content, order, media, and semantic variants; do not hardcode
|
|
468
|
+
unrelated colors, spacing, rounded corners, or shadows in starter page data to
|
|
469
|
+
make pages look different.
|
|
470
|
+
- When adding a new section, first choose the existing container, heading,
|
|
471
|
+
button, card, form, and media patterns it should reuse. Add a new CSS utility
|
|
472
|
+
or token only when multiple sections will use it or the theme needs a new
|
|
473
|
+
named pattern.
|
|
474
|
+
- Keep preview screenshots, default props, starter pages, CMS templates, and
|
|
475
|
+
AI examples aligned with the same token presets. The default preset should
|
|
476
|
+
look publishable without manual editor tweaks.
|
|
477
|
+
|
|
409
478
|
## Styling and assets
|
|
410
479
|
|
|
411
480
|
- Tailwind v4 starts in `src/styles.css` with `@import "tailwindcss";` and `@source "./**/*.{ts,tsx}";`.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"@eslint/js": "^9.39.4",
|
|
21
21
|
"@tailwindcss/postcss": "^4.3.0",
|
|
22
|
-
"@puckeditor/core": "^0.
|
|
22
|
+
"@puckeditor/core": "^0.22.0",
|
|
23
23
|
"@sudajs/cli": "*",
|
|
24
24
|
"@sudajs/theme-engine": "*",
|
|
25
25
|
"@types/node": "^24.13.2",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"vite": "^8.1.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
-
"@puckeditor/core": "^0.
|
|
45
|
+
"@puckeditor/core": "^0.22.0",
|
|
46
46
|
"@sudajs/theme-engine": "*",
|
|
47
47
|
"lucide-react": "^1.17.0",
|
|
48
48
|
"react": "^19.2.7",
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import type { SudaComponentConfig, SudaRootConfig } from "@sudajs/theme-engine";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createThemeDesignCssVariables,
|
|
4
|
+
createThemeDesignDefault,
|
|
5
|
+
designSystemField,
|
|
6
|
+
getIcpRecord,
|
|
7
|
+
getPageSlot,
|
|
8
|
+
getWhiteLabel,
|
|
9
|
+
resolveThemeDesignTokens,
|
|
10
|
+
type ThemeDesignValue,
|
|
11
|
+
} from "@sudajs/theme-engine/runtime";
|
|
3
12
|
import type { ReactElement, ReactNode } from "react";
|
|
4
13
|
|
|
5
14
|
import { t } from "./i18n.js";
|
|
15
|
+
import { sourceManifest } from "./manifest.js";
|
|
6
16
|
|
|
7
17
|
type PuckExtras = { puck?: { metadata?: Record<string, unknown> } };
|
|
8
|
-
type RootProps = { children?: ReactNode; title?: string };
|
|
18
|
+
type RootProps = { children?: ReactNode; title?: string; designSystem?: ThemeDesignValue };
|
|
9
19
|
type HeaderProps = { siteName?: string };
|
|
10
20
|
type PageOutletProps = PuckExtras;
|
|
11
21
|
type FooterProps = { text?: string } & PuckExtras;
|
|
@@ -13,13 +23,21 @@ type FooterProps = { text?: string } & PuckExtras;
|
|
|
13
23
|
export const rootConfig: SudaRootConfig<RootProps> = {
|
|
14
24
|
fields: {
|
|
15
25
|
title: { type: "text", label: t("layout.root.fields.title") },
|
|
26
|
+
designSystem: designSystemField(t("layout.root.fields.designSystem"), sourceManifest.designSystem),
|
|
27
|
+
},
|
|
28
|
+
defaultProps: {
|
|
29
|
+
title: "__SUDA_THEME_KEY__",
|
|
30
|
+
designSystem: createThemeDesignDefault(sourceManifest.designSystem),
|
|
31
|
+
},
|
|
32
|
+
render: ({ children, designSystem, title }) => {
|
|
33
|
+
const tokens = resolveThemeDesignTokens(sourceManifest.designSystem, designSystem);
|
|
34
|
+
const style = createThemeDesignCssVariables(tokens);
|
|
35
|
+
return (
|
|
36
|
+
<div className="__SUDA_THEME_KEY__-root" data-site-name={title} style={style}>
|
|
37
|
+
{children}
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
16
40
|
},
|
|
17
|
-
defaultProps: { title: "__SUDA_THEME_KEY__" },
|
|
18
|
-
render: ({ children, title }) => (
|
|
19
|
-
<div className="__SUDA_THEME_KEY__-root" data-site-name={title}>
|
|
20
|
-
{children}
|
|
21
|
-
</div>
|
|
22
|
-
),
|
|
23
41
|
};
|
|
24
42
|
|
|
25
43
|
export const Header: SudaComponentConfig<HeaderProps> = {
|
|
@@ -8,7 +8,55 @@ export const sourceManifest: ThemeSourceManifest = {
|
|
|
8
8
|
key: "__SUDA_THEME_KEY__",
|
|
9
9
|
name: "__SUDA_THEME_KEY__",
|
|
10
10
|
categories: ["other"],
|
|
11
|
-
minEngineVersion: "
|
|
11
|
+
minEngineVersion: "4.0.0",
|
|
12
|
+
designSystem: {
|
|
13
|
+
version: 1,
|
|
14
|
+
defaultPresetId: "default",
|
|
15
|
+
presets: [
|
|
16
|
+
{
|
|
17
|
+
id: "default",
|
|
18
|
+
label: "Default",
|
|
19
|
+
tokens: {
|
|
20
|
+
colors: {
|
|
21
|
+
background: "#ffffff",
|
|
22
|
+
foreground: "#111827",
|
|
23
|
+
primary: "#111827",
|
|
24
|
+
primaryForeground: "#ffffff",
|
|
25
|
+
accent: "#f8fafc",
|
|
26
|
+
accentForeground: "#111827",
|
|
27
|
+
muted: "#f4f5f7",
|
|
28
|
+
mutedForeground: "#4b5563",
|
|
29
|
+
},
|
|
30
|
+
radius: {
|
|
31
|
+
card: "8px",
|
|
32
|
+
button: "8px",
|
|
33
|
+
input: "8px",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "ocean",
|
|
39
|
+
label: "Ocean",
|
|
40
|
+
tokens: {
|
|
41
|
+
colors: {
|
|
42
|
+
background: "#f8fbff",
|
|
43
|
+
foreground: "#0e1b36",
|
|
44
|
+
primary: "#2563eb",
|
|
45
|
+
primaryForeground: "#ffffff",
|
|
46
|
+
accent: "#dbeafe",
|
|
47
|
+
accentForeground: "#0e1b36",
|
|
48
|
+
muted: "#eff6ff",
|
|
49
|
+
mutedForeground: "#42526e",
|
|
50
|
+
},
|
|
51
|
+
radius: {
|
|
52
|
+
card: "14px",
|
|
53
|
+
button: "9999px",
|
|
54
|
+
input: "10px",
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
12
60
|
entry: "index.js",
|
|
13
61
|
clientEntry: "runtime.client.js",
|
|
14
62
|
// Required. SudaCloud currently hosts SSR themes only; CSR/Hybrid postures
|
|
@@ -240,7 +240,10 @@ export const FeaturedPosts: SudaComponentConfig<FeaturedPostsProps> = {
|
|
|
240
240
|
postList: { strategy: "featured", limit: 3 },
|
|
241
241
|
},
|
|
242
242
|
render: ({ id, title, description, puck }) => {
|
|
243
|
-
const resource = getPostResource(puck?.metadata,
|
|
243
|
+
const resource = getPostResource(puck?.metadata, {
|
|
244
|
+
componentId: id,
|
|
245
|
+
fieldKey: "postList",
|
|
246
|
+
});
|
|
244
247
|
const posts = resource.posts;
|
|
245
248
|
return (
|
|
246
249
|
<section className="__SUDA_THEME_KEY__-section">
|
|
@@ -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 {
|