@sudajs/cli 0.9.0 → 0.9.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 +2 -0
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/templates/theme/AGENTS.md +88 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sudajs/cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"suda": "./bin/suda.js"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"react": "^19.2.7",
|
|
33
33
|
"react-dom": "^19.2.7",
|
|
34
34
|
"zod": "^3.24.1",
|
|
35
|
-
"@sudajs/theme-engine": "2.3.
|
|
35
|
+
"@sudajs/theme-engine": "2.3.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@tailwindcss/postcss": "^4.3.0",
|
|
@@ -2,57 +2,81 @@
|
|
|
2
2
|
|
|
3
3
|
This directory is a standalone Vite + React + Tailwind Suda theme generated by `suda theme init`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Your job in a theme project is to create a complete, publishable theme with theme-specific components, realistic starter pages, scoped styling, assets, and AI metadata. Do not build pages by assembling generic engine components; those are reserved for end users who need supplemental editing blocks after the theme is installed.
|
|
6
|
+
|
|
7
|
+
## Theme contract
|
|
6
8
|
|
|
7
9
|
- Keep `renderMode: "ssr"` in `src/manifest.ts`.
|
|
8
10
|
- Keep source entries at `src/index.tsx`, `src/runtime.client.ts`, and `src/styles.css`.
|
|
11
|
+
- Export a complete `ThemeModule` from `src/index.tsx`: `manifest`, `pageConfig`, `layoutConfig`, `defaultLayout`, and `starterPages`.
|
|
12
|
+
- Keep `pageConfig` focused on page content sections. Keep `layoutConfig` focused on shared site chrome such as root, header, page outlet, and footer.
|
|
9
13
|
- Do not edit generated files under `dist/`; run `pnpm build` to regenerate them.
|
|
10
14
|
- React, React DOM, Puck, and `@sudajs/theme-engine` are host-provided peers. Do not bundle private copies into the theme runtime.
|
|
15
|
+
- Keep persisted props JSON-serializable. Do not store functions, React nodes, class instances, database ids for media, or environment-specific absolute filesystem paths.
|
|
16
|
+
|
|
17
|
+
## Component strategy
|
|
18
|
+
|
|
19
|
+
- Create theme-specific components and sections first. Every page component should express this theme's brand, industry, visual rhythm, and content model.
|
|
20
|
+
- Do not register `createEngineComponents()`, `createBaseBlocks()`, or `createContainers()` in `pageConfig.components` when creating a theme. Generic engine components are fallback editing tools for users, not the foundation of a theme template.
|
|
21
|
+
- Prefer purpose-built sections such as hero, services, product highlights, feature comparisons, gallery, process, pricing, testimonial, FAQ, article list, contact, and CTA when the theme needs those patterns.
|
|
22
|
+
- Keep section APIs semantic. Expose props that describe content and editor decisions, not implementation details.
|
|
23
|
+
- Keep `fields`, `defaultProps`, and `render` in sync. Every editor field should have a sensible default, and render functions should tolerate omitted optional values.
|
|
24
|
+
- Put editor controls in props when they change rendering, for example `showLogo`, `mediaPosition`, `tone`, or `columns`; pair dependent controls with `visibleIf` rather than hiding logic only in JSX.
|
|
25
|
+
|
|
26
|
+
## Layout and slot rules
|
|
27
|
+
|
|
28
|
+
- Page-level sections and components must not declare native Puck `slot` fields. Do not write `fields: { content: { type: "slot" } }` in page components.
|
|
29
|
+
- Native Puck slots are reserved for Suda-provided layout/container primitives and theme layout components. The normal template pattern is the one in `src/layout.tsx`: `rootConfig` renders `children`, `PageOutlet` renders `getPageSlot(puck?.metadata)`, and `Header` / `Footer` surround that outlet in `defaultLayout`.
|
|
30
|
+
- Do not add arbitrary nested page composition through native slots. It makes AI-generated pages unpredictable and hard to validate.
|
|
31
|
+
- 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.
|
|
32
|
+
- Do not use legacy DropZone or `zones` patterns.
|
|
11
33
|
|
|
12
|
-
## Styling
|
|
34
|
+
## Styling and assets
|
|
13
35
|
|
|
14
36
|
- Tailwind v4 starts in `src/styles.css` with `@import "tailwindcss";` and `@source "./**/*.{ts,tsx}";`.
|
|
15
|
-
- Theme-local assets live in top-level `assets/`; the CLI copies them to `dist/assets/` during build.
|
|
37
|
+
- Theme-local assets live in top-level `assets/`; the CLI copies them to `dist/assets/` during build.
|
|
38
|
+
- Use `themeAsset("assets/...")` for theme-bundled assets in default props and starter pages. Do not import images from React code.
|
|
39
|
+
- Use `resolveAsset(puck?.metadata, value)` from `@sudajs/theme-engine/runtime` before rendering user-selected media fields.
|
|
40
|
+
- Scope theme CSS with the generated theme key classes. Avoid global resets that could affect the host editor or other themes.
|
|
16
41
|
- Preview screenshots are required at `assets/preview/desktop.png`, `assets/preview/tablet.png`, and `assets/preview/mobile.png`; run `suda theme capture` to generate them.
|
|
17
|
-
- Customize `vite.config.ts`, `postcss.config.mjs`, and `src/styles.css`
|
|
42
|
+
- Customize `vite.config.ts`, `postcss.config.mjs`, and `src/styles.css` only when the theme needs it.
|
|
18
43
|
|
|
19
|
-
##
|
|
44
|
+
## Field authoring rules
|
|
20
45
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
46
|
+
Use the most specific field type available. A generic `text` field may pass typecheck, but it is wrong when a semantic Suda field exists.
|
|
47
|
+
|
|
48
|
+
- Always use `url` for links and routes, including anchors, `mailto:` links, and external URLs. Do not use `text` for href-like props.
|
|
49
|
+
- Use `image` for images, `video` for videos, and `media` only when the same prop intentionally accepts mixed media.
|
|
50
|
+
- Use `color` for color values, `icon` for icon names, `font` for font selections, `range` for bounded numbers, `spacing` for spacing tokens, and `menu` for navigation items.
|
|
51
|
+
- Use `select` or `radio` for fixed choices. Keep option values stable, short, and serializable.
|
|
52
|
+
- Use `array` for repeatable items and `object` for grouped settings. Keep nested field names aligned with the prop shape and `defaultProps`.
|
|
53
|
+
- Use `textarea` for multi-sentence copy. Use `text` only for short labels, headings, slugs, names, or plain strings that are not URLs, media, colors, icons, fonts, or menus.
|
|
54
|
+
- For card grids, feature lists, pricing tables, logo walls, gallery grids, and any multi-column component, expose a `columns` prop with a `range` or `select` field and a sensible default.
|
|
55
|
+
- Use `visibleIf: ({ props }, { fields }) => boolean` for synchronous field visibility that depends on sibling props or resolved fields. Keep it pure and fast.
|
|
56
|
+
- Use Puck `resolveFields` only for heavier dynamic field changes that cannot be expressed with `visibleIf`.
|
|
57
|
+
- Add field-level `ai.instructions`, `ai.required`, `ai.exclude`, `ai.stream`, or `ai.bind` when a prop needs generation guidance beyond its label and type. Use `ai.stream: false` for atomic values such as URLs that should not stream partially.
|
|
31
58
|
|
|
32
59
|
## AI metadata
|
|
33
60
|
|
|
34
|
-
Every component and layout component
|
|
61
|
+
Every page component and layout component must declare useful `ai.instructions`. Missing component-level instructions fail `suda theme check` and block publish.
|
|
35
62
|
|
|
36
|
-
|
|
63
|
+
Good component instructions explain:
|
|
37
64
|
|
|
38
|
-
|
|
65
|
+
- Purpose: what content or business role the component serves.
|
|
66
|
+
- Use when / avoid when: how AI should choose this component instead of a similar one.
|
|
67
|
+
- Placement: where it normally belongs in a page.
|
|
68
|
+
- Frequency: whether it should appear once, multiple times, or only near another section.
|
|
69
|
+
- Composition: required neighboring content or local blocks, when relevant.
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
- Use `select` or `radio` when authors must choose from a fixed set of values. Keep option values stable and serializable.
|
|
42
|
-
- Use `array` for repeatable items and `object` for grouped settings. Keep nested field names aligned with the prop shape and `defaultProps`.
|
|
43
|
-
- For card grids, feature lists, pricing tables, logo walls, gallery grids, and any multi-column component, expose a `columns` prop with a `range` or `select` field and a sensible default.
|
|
44
|
-
- Use `textarea` for multi-sentence copy and `text` only for short labels, headings, slugs, or plain strings that are not URLs/media/color/icon/font values.
|
|
45
|
-
- Keep `fields`, `defaultProps`, and `render` in sync: every editor field should have a sensible default and render should tolerate omitted optional values.
|
|
46
|
-
- Put editor-only controls in props when they change rendering, for example `showLogo`; pair dependent controls with `visibleIf` rather than hiding logic only in JSX.
|
|
47
|
-
- Keep persisted props JSON-serializable. Do not store functions, React nodes, class instances, database ids for media, or environment-specific absolute filesystem paths.
|
|
48
|
-
- Add optional field-level `ai.instructions`, `ai.required`, or `ai.exclude` when a prop needs generation guidance beyond its label and type.
|
|
71
|
+
Use `ai.exclude: true` only for structural or editor-only components, and still provide instructions explaining why AI must not generate them. `PageOutlet` is the standard example.
|
|
49
72
|
|
|
50
73
|
## Block slot authoring rules
|
|
51
74
|
|
|
52
|
-
|
|
75
|
+
Use `blockSlots` when a section owns controlled local content that benefits from independent editor selection and configuration, such as hero actions, pricing cards, feature rows, stats, timeline items, or contact methods. Keep ordinary section props in `fields`.
|
|
53
76
|
|
|
54
|
-
- Type the slot prop as Puck `Slot` and declare `blockSlots` with the same prop key.
|
|
77
|
+
- Type the slot prop as Puck `Slot` and declare `blockSlots` with the same prop key. This is the Suda block-slot API; do not create a native `fields.<key>.type = "slot"` yourself.
|
|
55
78
|
- Define local block kinds under `blockSlots.<slotName>.blocks`. Local blocks support normal Suda fields, `label`, `defaultProps`, `render`, `metadata`, `inline`, `permissions`, and optional `ai`.
|
|
79
|
+
- Keep local blocks specific to the owning section. They are not reusable global page components.
|
|
56
80
|
- Do not add nested `blockSlots` or `cms` to local blocks. Block slots are one level deep in the theme authoring API.
|
|
57
81
|
- Do not put the slot prop in the host component's `defaultProps`; use `defaultBlocks` on the slot instead.
|
|
58
82
|
- Do not hand-write `id` in local block `defaultProps`, `defaultBlocks[].props`, or starter page nested block props. The editor/runtime owns block ids.
|
|
@@ -71,7 +95,8 @@ type HeroProps = {
|
|
|
71
95
|
export const Hero: SudaComponentConfig<HeroProps> = {
|
|
72
96
|
label: "Hero",
|
|
73
97
|
ai: {
|
|
74
|
-
instructions:
|
|
98
|
+
instructions:
|
|
99
|
+
"Primary page introduction for landing pages. Use once near the top with one or two local action blocks.",
|
|
75
100
|
},
|
|
76
101
|
fields: {
|
|
77
102
|
title: { type: "text", label: "Title" },
|
|
@@ -108,7 +133,7 @@ export const Hero: SudaComponentConfig<HeroProps> = {
|
|
|
108
133
|
};
|
|
109
134
|
```
|
|
110
135
|
|
|
111
|
-
Starter page data should use the public host component type and a standard
|
|
136
|
+
Starter page data should use the public host component type and a standard array on the block-slot prop. Use local block `kind` values as nested `type` values.
|
|
112
137
|
|
|
113
138
|
```ts
|
|
114
139
|
{
|
|
@@ -128,3 +153,35 @@ Starter page data should use the public host component type and a standard slot
|
|
|
128
153
|
},
|
|
129
154
|
}
|
|
130
155
|
```
|
|
156
|
+
|
|
157
|
+
## Starter pages
|
|
158
|
+
|
|
159
|
+
- Provide realistic starter pages in `src/templates.ts`; do not ship placeholder-only pages.
|
|
160
|
+
- Every starter page is Puck `Data` with `root: { props: {} }` and `content: [...]`.
|
|
161
|
+
- Each top-level component instance in starter page `content` must include a stable `props.id`.
|
|
162
|
+
- Use only components registered in `pageConfig.components` for top-level page content.
|
|
163
|
+
- Match starter page content to the theme's intended audience and category. The home page should show the theme's best composition, not just every component in order.
|
|
164
|
+
- Use `themeAsset("assets/...")` for bundled starter media.
|
|
165
|
+
|
|
166
|
+
## CMS-aware themes
|
|
167
|
+
|
|
168
|
+
- If the theme supports posts or tags, declare `content.views` on the exported `ThemeModule`.
|
|
169
|
+
- Add one starter page with matching `cmsView` for every declared CMS view.
|
|
170
|
+
- Declare component-level `cms` capabilities only on components that can render that CMS source.
|
|
171
|
+
- Use `cmsBinding` in starter page props when a component should receive CMS data at render time.
|
|
172
|
+
- Keep static fallback props for CMS components so the page still renders meaningfully when CMS payloads are absent.
|
|
173
|
+
|
|
174
|
+
## Commands
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pnpm install
|
|
178
|
+
pnpm lint
|
|
179
|
+
pnpm typecheck
|
|
180
|
+
pnpm format
|
|
181
|
+
pnpm dev # Vite-powered local preview
|
|
182
|
+
pnpm build # Vite build + Suda artifact finalize + AI metadata check
|
|
183
|
+
pnpm validate
|
|
184
|
+
suda theme check
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Before publishing or handing off a finished theme, run `pnpm typecheck`, `pnpm lint`, `pnpm build`, and `pnpm validate`. Use `suda theme check` when iterating on AI metadata.
|