@sudajs/cli 0.8.2 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudajs/cli",
3
- "version": "0.8.2",
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.2.1"
35
+ "@sudajs/theme-engine": "2.3.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@tailwindcss/postcss": "^4.3.0",
@@ -48,8 +48,8 @@
48
48
  "vite": "^7.3.3",
49
49
  "vitest": "^3.2.4",
50
50
  "@suda/build-config": "0.0.0",
51
- "@suda/tsconfig": "0.0.0",
52
- "@suda/eslint-config": "0.0.0"
51
+ "@suda/eslint-config": "0.0.0",
52
+ "@suda/tsconfig": "0.0.0"
53
53
  },
54
54
  "scripts": {
55
55
  "build": "tsup",
@@ -2,56 +2,81 @@
2
2
 
3
3
  This directory is a standalone Vite + React + Tailwind Suda theme generated by `suda theme init`.
4
4
 
5
- ## Contract
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.
11
16
 
12
- ## Styling
13
-
14
- - Tailwind v4 starts in `src/styles.css` with `@import "tailwindcss";` and `@source "./**/*.{ts,tsx}";`.
15
- - Theme-local assets live in `src/assets/`; the CLI copies them to `dist/assets/` during build.
16
- - Customize `vite.config.ts`, `postcss.config.mjs`, and `src/styles.css` as needed.
17
+ ## Component strategy
17
18
 
18
- ## Commands
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.
19
25
 
20
- ```bash
21
- pnpm install
22
- pnpm lint
23
- pnpm typecheck
24
- pnpm format
25
- pnpm dev # Vite-powered local preview
26
- pnpm build # Vite build + Suda artifact finalize + AI metadata check
27
- pnpm validate
28
- suda theme check
29
- ```
26
+ ## Layout and slot rules
30
27
 
31
- ## AI metadata
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.
32
33
 
33
- Every component and layout component should declare useful `ai.instructions`. Missing component-level instructions fail `suda theme check` and block publish.
34
+ ## Styling and assets
34
35
 
35
- Use `visibleIf: ({ props }, { fields }) => boolean` for synchronous field visibility that depends on sibling props or resolved fields. Keep it pure and fast; use Puck `resolveFields` directly for heavier dynamic field changes. AI and MCP schemas expose `visibleIf` fields as dynamic visibility so agents know the editor surface can change with props.
36
+ - Tailwind v4 starts in `src/styles.css` with `@import "tailwindcss";` and `@source "./**/*.{ts,tsx}";`.
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.
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.
42
+ - Customize `vite.config.ts`, `postcss.config.mjs`, and `src/styles.css` only when the theme needs it.
36
43
 
37
44
  ## Field authoring rules
38
45
 
39
- - Prefer Suda field helpers over generic text fields: use `url` for links and routes, `image` for images, `video` for videos, `media` for mixed media, `color` for colors, `icon` for icon names, `font` for font selections, `range` for bounded numbers, `spacing` for spacing tokens, and `menu` for navigation items.
40
- - Use `select` or `radio` when authors must choose from a fixed set of values. Keep option values stable and serializable.
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.
41
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.
42
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.
43
- - 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.
44
- - Keep `fields`, `defaultProps`, and `render` in sync: every editor field should have a sensible default and render should tolerate omitted optional values.
45
- - 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.
46
- - Keep persisted props JSON-serializable. Do not store functions, React nodes, class instances, database ids for media, or environment-specific absolute filesystem paths.
47
- - Add optional field-level `ai.instructions`, `ai.required`, or `ai.exclude` when a prop needs generation guidance beyond its label and type.
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.
58
+
59
+ ## AI metadata
60
+
61
+ Every page component and layout component must declare useful `ai.instructions`. Missing component-level instructions fail `suda theme check` and block publish.
62
+
63
+ Good component instructions explain:
64
+
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.
70
+
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.
48
72
 
49
73
  ## Block slot authoring rules
50
74
 
51
- Prefer `blockSlots` when a section owns nested content that benefits from independent editor selection and configuration, such as hero actions, pricing cards, feature rows, stats, or timeline items. This is usually more editor-friendly than modeling those items as plain `array` / `object` fields because each local block can be selected and configured on its own in the visual editor. Keep ordinary section props in `fields`; `fields` is optional and does not need to list every prop.
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`.
52
76
 
53
- - Type the slot prop as Puck `Slot` and declare `blockSlots` with the same prop key. Non-slot props should stay in `fields` or nested `array` / `object` fields.
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.
54
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.
55
80
  - Do not add nested `blockSlots` or `cms` to local blocks. Block slots are one level deep in the theme authoring API.
56
81
  - Do not put the slot prop in the host component's `defaultProps`; use `defaultBlocks` on the slot instead.
57
82
  - Do not hand-write `id` in local block `defaultProps`, `defaultBlocks[].props`, or starter page nested block props. The editor/runtime owns block ids.
@@ -70,7 +95,8 @@ type HeroProps = {
70
95
  export const Hero: SudaComponentConfig<HeroProps> = {
71
96
  label: "Hero",
72
97
  ai: {
73
- instructions: "Primary page introduction with editable call-to-action blocks.",
98
+ instructions:
99
+ "Primary page introduction for landing pages. Use once near the top with one or two local action blocks.",
74
100
  },
75
101
  fields: {
76
102
  title: { type: "text", label: "Title" },
@@ -107,7 +133,7 @@ export const Hero: SudaComponentConfig<HeroProps> = {
107
133
  };
108
134
  ```
109
135
 
110
- Starter page data should use the public host component type and a standard slot array on the slot prop. Use local block `kind` values as nested `type` values.
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.
111
137
 
112
138
  ```ts
113
139
  {
@@ -127,3 +153,35 @@ Starter page data should use the public host component type and a standard slot
127
153
  },
128
154
  }
129
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.
@@ -19,9 +19,18 @@ pnpm build
19
19
  pnpm validate
20
20
  ```
21
21
 
22
- `pnpm dev` starts the Vite-powered preview. `pnpm build` produces the Suda artifact contract in `dist/`, including `dist/index.js`, `dist/runtime.client.js`, `dist/manifest.json`, and `dist/styles.css`.
22
+ `pnpm dev` starts the Vite-powered preview. `pnpm build` produces the Suda artifact contract in `dist/`, including `dist/index.js`, `dist/runtime.client.js`, `dist/manifest.json`, and `dist/styles.css`. Published artifact paths omit the local `dist/` prefix.
23
23
 
24
- Theme-local assets live in `src/assets/`. The starter hero exposes a Logo field that defaults to `src/assets/suda-logo.svg`, so you can replace it with your own brand image or choose a different image in the editor.
24
+ Theme-local assets live in top-level `assets/`. The starter hero exposes a Logo field that defaults to `assets/brand/suda-logo.svg`, so you can replace it with your own brand image or choose a different image in the editor.
25
+
26
+ Preview screenshots are required before publishing:
27
+
28
+ ```bash
29
+ suda theme capture
30
+ pnpm build
31
+ ```
32
+
33
+ By default capture writes `assets/preview/desktop.png`, `assets/preview/tablet.png`, and `assets/preview/mobile.png`.
25
34
 
26
35
  Fields can use `visibleIf: ({ props }, { fields }) => boolean` for synchronous dynamic visibility. The starter hero shows this by hiding the Logo picker when `showLogo` is false. Suda converts `visibleIf` to Puck `resolveFields` during normalization, and the generated AI/MCP section schema marks those fields as dynamically visible.
27
36
 
@@ -9,8 +9,8 @@ export const sourceManifest: ThemeSourceManifest = {
9
9
  name: "__SUDA_THEME_KEY__",
10
10
  categories: ["other"],
11
11
  minEngineVersion: "0.0.0",
12
- entry: "dist/index.js",
13
- clientEntry: "dist/runtime.client.js",
12
+ entry: "index.js",
13
+ clientEntry: "runtime.client.js",
14
14
  // Required. SudaCloud currently hosts SSR themes only; CSR/Hybrid postures
15
15
  // are on the roadmap. Authoring `renderMode` is the contract a theme uses
16
16
  // to declare which posture it expects from the host.
@@ -63,7 +63,7 @@ export const Hero: SudaComponentConfig<HeroProps> = {
63
63
  },
64
64
  defaultProps: {
65
65
  showLogo: true,
66
- logo: themeAsset("assets/suda-logo.svg"),
66
+ logo: themeAsset("assets/brand/suda-logo.svg"),
67
67
  eyebrow: "New theme",
68
68
  title: "Build with SudaCloud",
69
69
  description: "Edit this starter section in the visual editor.",
@@ -14,7 +14,7 @@ export const starterPages: ThemeStarterPage[] = [
14
14
  type: "Hero",
15
15
  props: {
16
16
  id: "Hero-1",
17
- logo: themeAsset("assets/suda-logo.svg"),
17
+ logo: themeAsset("assets/brand/suda-logo.svg"),
18
18
  eyebrow: "Starter page",
19
19
  title: "Welcome to __SUDA_THEME_KEY__",
20
20
  description: "This page was generated by suda theme init.",