uniweb 0.1.7 → 0.2.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/README.md +291 -59
- package/package.json +3 -3
- package/src/index.js +281 -149
package/README.md
CHANGED
|
@@ -1,24 +1,116 @@
|
|
|
1
1
|
# uniweb
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Create a well-structured Vite + React site with content/code separation, file-based routing, localization, and structured content—out of the box. The architecture scales to dynamic content management and collaborative visual editing when you need it.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
|
-
Create a new Uniweb project
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
npx uniweb@latest create my-project --template workspace
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
This scaffolds a Vite project with everything wired—routing, state, and build pipeline ready. You get a site and a Foundation in a monorepo, pre-configured to work together.
|
|
7
|
+
Create a new Uniweb project:
|
|
14
8
|
|
|
15
9
|
```bash
|
|
10
|
+
npx uniweb@latest create my-project
|
|
16
11
|
cd my-project
|
|
17
12
|
pnpm install
|
|
18
13
|
pnpm dev
|
|
19
14
|
```
|
|
20
15
|
|
|
21
|
-
|
|
16
|
+
You get a workspace with two packages:
|
|
17
|
+
|
|
18
|
+
- **`site/`** — Content, pages, entry point
|
|
19
|
+
- **`foundation/`** — Your React components
|
|
20
|
+
|
|
21
|
+
Content authors work in markdown. Component authors work in React. Neither can break the other's work, and component updates flow to every site using them.
|
|
22
|
+
|
|
23
|
+
## What You Get
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
my-project/
|
|
27
|
+
├── site/ # Content + configuration
|
|
28
|
+
│ ├── pages/ # File-based routing
|
|
29
|
+
│ │ └── home/
|
|
30
|
+
│ │ ├── page.yml # Page metadata
|
|
31
|
+
│ │ └── 1-hero.md # Section content
|
|
32
|
+
│ ├── locales/ # i18n (mirrors pages/)
|
|
33
|
+
│ │ └── es/
|
|
34
|
+
│ └── public/ # Static assets
|
|
35
|
+
│
|
|
36
|
+
└── foundation/ # Your components
|
|
37
|
+
└── src/
|
|
38
|
+
└── components/
|
|
39
|
+
└── Hero/
|
|
40
|
+
└── index.jsx
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Pages are folders.** Create `pages/about/` with a markdown file inside → visit `/about`. That's the whole routing model.
|
|
44
|
+
|
|
45
|
+
### Content as Markdown
|
|
46
|
+
|
|
47
|
+
```markdown
|
|
48
|
+
---
|
|
49
|
+
component: Hero
|
|
50
|
+
theme: dark
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Welcome
|
|
54
|
+
|
|
55
|
+
Build something great.
|
|
56
|
+
|
|
57
|
+
[Get Started](#)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Frontmatter specifies the component and configuration. The body contains the actual content—headings, paragraphs, links, images—which gets semantically parsed into structured data your component receives.
|
|
61
|
+
|
|
62
|
+
### Beyond Markdown
|
|
63
|
+
|
|
64
|
+
For content that doesn't fit markdown patterns—products, team members, events—use JSON blocks with schema tags:
|
|
65
|
+
|
|
66
|
+
````markdown
|
|
67
|
+
```json #team-member
|
|
68
|
+
{
|
|
69
|
+
"name": "Sarah Chen",
|
|
70
|
+
"role": "Lead Architect"
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
````
|
|
74
|
+
|
|
75
|
+
Components receive validated, localized data. Natural content stays in markdown; structured data goes in JSON blocks.
|
|
76
|
+
|
|
77
|
+
### Components as React
|
|
78
|
+
|
|
79
|
+
```jsx
|
|
80
|
+
export function Hero({ content, params }) {
|
|
81
|
+
const { title } = content.main.header;
|
|
82
|
+
const { paragraphs, links } = content.main.body;
|
|
83
|
+
const { theme = 'light' } = params;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<section className={`py-20 text-center ${theme === 'dark' ? 'bg-gray-900 text-white' : ''}`}>
|
|
87
|
+
<h1 className="text-4xl font-bold">{title}</h1>
|
|
88
|
+
<p className="text-xl text-gray-600">{paragraphs[0]}</p>
|
|
89
|
+
{links[0] && (
|
|
90
|
+
<a href={links[0].url} className="mt-8 px-6 py-3 bg-blue-600 text-white rounded inline-block">
|
|
91
|
+
{links[0].text}
|
|
92
|
+
</a>
|
|
93
|
+
)}
|
|
94
|
+
</section>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Standard React. Standard Tailwind. The `{ content, params }` interface is only for *exposed* components—the ones content creators select in markdown frontmatter. Internal components (the majority of your codebase) use regular React props.
|
|
100
|
+
|
|
101
|
+
No framework to learn. Foundations are purpose-built component systems designed for a specific domain (marketing, documentation, learning, etc.). Sites are Vite apps that load content from markdown files. The CLI handles the wiring.
|
|
102
|
+
|
|
103
|
+
## The Bigger Picture
|
|
104
|
+
|
|
105
|
+
The structure you start with scales without rewrites:
|
|
106
|
+
|
|
107
|
+
1. **Single project** — One site, one component library. Most projects stay here.
|
|
108
|
+
2. **Multi-site** — One foundation powers multiple sites. Release it once, updates propagate automatically.
|
|
109
|
+
3. **Full platform** — [uniweb.app](https://uniweb.app) adds visual editing, live content management, and team collaboration. Your foundation plugs in and its components become native to the editor.
|
|
110
|
+
|
|
111
|
+
Start with local markdown files deployed anywhere. Connect to [uniweb.app](https://uniweb.app) when you're ready for dynamic content and team collaboration.
|
|
112
|
+
|
|
113
|
+
---
|
|
22
114
|
|
|
23
115
|
## Installation
|
|
24
116
|
|
|
@@ -42,9 +134,9 @@ uniweb create [project-name] [options]
|
|
|
42
134
|
|
|
43
135
|
**Options:**
|
|
44
136
|
|
|
45
|
-
| Option | Description
|
|
46
|
-
| ------------------- |
|
|
47
|
-
| `--template <type>` | Project template: `
|
|
137
|
+
| Option | Description |
|
|
138
|
+
| ------------------- | ------------------------------------- |
|
|
139
|
+
| `--template <type>` | Project template: `single` or `multi` |
|
|
48
140
|
|
|
49
141
|
**Examples:**
|
|
50
142
|
|
|
@@ -52,17 +144,14 @@ uniweb create [project-name] [options]
|
|
|
52
144
|
# Interactive prompts
|
|
53
145
|
uniweb create
|
|
54
146
|
|
|
55
|
-
# Create with specific name
|
|
147
|
+
# Create with specific name (defaults to single template)
|
|
56
148
|
uniweb create my-project
|
|
57
149
|
|
|
58
|
-
#
|
|
59
|
-
uniweb create my-
|
|
60
|
-
|
|
61
|
-
# Standalone foundation
|
|
62
|
-
uniweb create my-foundation --template foundation
|
|
150
|
+
# Single project with site + foundation
|
|
151
|
+
uniweb create my-project --template single
|
|
63
152
|
|
|
64
|
-
#
|
|
65
|
-
uniweb create my-
|
|
153
|
+
# Multi-site/foundation monorepo
|
|
154
|
+
uniweb create my-workspace --template multi
|
|
66
155
|
```
|
|
67
156
|
|
|
68
157
|
### `build`
|
|
@@ -75,9 +164,10 @@ uniweb build [options]
|
|
|
75
164
|
|
|
76
165
|
**Options:**
|
|
77
166
|
|
|
78
|
-
| Option
|
|
79
|
-
|
|
|
80
|
-
| `--target <type>`
|
|
167
|
+
| Option | Description |
|
|
168
|
+
| ------------------- | --------------------------------------------------------------------- |
|
|
169
|
+
| `--target <type>` | Build target: `foundation` or `site` (auto-detected if not specified) |
|
|
170
|
+
| `--platform <name>` | Deployment platform (e.g., `vercel`) for platform-specific output |
|
|
81
171
|
|
|
82
172
|
**Examples:**
|
|
83
173
|
|
|
@@ -90,56 +180,112 @@ uniweb build --target foundation
|
|
|
90
180
|
|
|
91
181
|
# Explicitly build as site
|
|
92
182
|
uniweb build --target site
|
|
183
|
+
|
|
184
|
+
# Build for Vercel deployment
|
|
185
|
+
uniweb build --platform vercel
|
|
93
186
|
```
|
|
94
187
|
|
|
95
188
|
## Project Templates
|
|
96
189
|
|
|
97
|
-
###
|
|
190
|
+
### Single (Default)
|
|
98
191
|
|
|
99
|
-
A
|
|
192
|
+
A minimal workspace with a site and foundation as sibling packages. This is the recommended starting point.
|
|
100
193
|
|
|
101
194
|
```
|
|
102
|
-
my-
|
|
103
|
-
├── package.json
|
|
104
|
-
├── pnpm-workspace.yaml
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
195
|
+
my-project/
|
|
196
|
+
├── package.json # Workspace root (includes workspaces field for npm)
|
|
197
|
+
├── pnpm-workspace.yaml # Pre-configured for evolution (see below)
|
|
198
|
+
│
|
|
199
|
+
├── site/ # Site package (content + entry)
|
|
200
|
+
│ ├── package.json
|
|
201
|
+
│ ├── vite.config.js
|
|
202
|
+
│ ├── index.html
|
|
203
|
+
│ ├── site.yml
|
|
204
|
+
│ ├── src/
|
|
205
|
+
│ │ └── main.jsx # Thin entry point
|
|
206
|
+
│ ├── pages/ # Content structure
|
|
207
|
+
│ │ └── home/
|
|
208
|
+
│ │ ├── page.yml
|
|
209
|
+
│ │ └── 1-hero.md
|
|
210
|
+
│ ├── locales/ # i18n (mirrors pages/)
|
|
211
|
+
│ │ └── es/
|
|
212
|
+
│ │ └── home/
|
|
213
|
+
│ └── public/ # Static assets
|
|
214
|
+
│
|
|
215
|
+
└── foundation/ # Foundation package (components)
|
|
216
|
+
├── package.json
|
|
217
|
+
├── vite.config.js
|
|
218
|
+
├── src/
|
|
219
|
+
│ ├── index.js # Component registry
|
|
220
|
+
│ ├── styles.css # Tailwind
|
|
221
|
+
│ ├── meta.js # Foundation metadata
|
|
222
|
+
│ └── components/
|
|
223
|
+
│ └── Hero/
|
|
224
|
+
│ ├── index.jsx
|
|
225
|
+
│ └── meta.js
|
|
226
|
+
└── ...
|
|
108
227
|
```
|
|
109
228
|
|
|
110
|
-
|
|
229
|
+
**Key characteristics:**
|
|
111
230
|
|
|
112
|
-
|
|
231
|
+
- **Convention-compliant** — Each package has its own `src/`
|
|
232
|
+
- **Clear dep boundaries** — Component libs → `foundation/package.json`, runtime → `site/package.json`
|
|
233
|
+
- **Zero extraction** — `foundation/` is already a complete, publishable package
|
|
234
|
+
- **Scales naturally** — Rename to `sites/marketing/` and `foundations/marketing/` when needed
|
|
235
|
+
|
|
236
|
+
### Multi
|
|
237
|
+
|
|
238
|
+
A monorepo for multi-site or multi-foundation development.
|
|
113
239
|
|
|
114
240
|
```
|
|
115
|
-
my-
|
|
116
|
-
├── package.json
|
|
117
|
-
├──
|
|
118
|
-
|
|
119
|
-
├──
|
|
120
|
-
│
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
241
|
+
my-workspace/
|
|
242
|
+
├── package.json # Workspace root (includes workspaces field for npm)
|
|
243
|
+
├── pnpm-workspace.yaml # Same config as site template
|
|
244
|
+
│
|
|
245
|
+
├── sites/
|
|
246
|
+
│ ├── marketing/ # Main marketing site
|
|
247
|
+
│ │ ├── package.json
|
|
248
|
+
│ │ ├── site.yml
|
|
249
|
+
│ │ ├── src/
|
|
250
|
+
│ │ ├── pages/
|
|
251
|
+
│ │ └── ...
|
|
252
|
+
│ └── docs/ # Documentation site
|
|
253
|
+
│
|
|
254
|
+
└── foundations/
|
|
255
|
+
├── marketing/ # Marketing foundation
|
|
256
|
+
│ ├── package.json
|
|
257
|
+
│ ├── src/components/
|
|
258
|
+
│ └── ...
|
|
259
|
+
└── documentation/ # Documentation foundation
|
|
125
260
|
```
|
|
126
261
|
|
|
127
|
-
|
|
262
|
+
Use this when you need:
|
|
128
263
|
|
|
129
|
-
|
|
264
|
+
- Multiple sites sharing foundations
|
|
265
|
+
- Multiple foundations for different purposes
|
|
266
|
+
- A testing site for foundation development
|
|
130
267
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
268
|
+
## Dependency Management
|
|
269
|
+
|
|
270
|
+
Each package manages its own dependencies:
|
|
271
|
+
|
|
272
|
+
**`site/package.json`:**
|
|
273
|
+
|
|
274
|
+
- `@uniweb/runtime`
|
|
275
|
+
- `@my-project/foundation` (workspace link)
|
|
276
|
+
- Vite, Tailwind (dev)
|
|
277
|
+
|
|
278
|
+
**`foundation/package.json`:**
|
|
279
|
+
|
|
280
|
+
- Component libraries (carousel, icons, etc.)
|
|
281
|
+
- React as peer dependency
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
# Add component dependency
|
|
285
|
+
cd foundation && pnpm add embla-carousel
|
|
286
|
+
|
|
287
|
+
# Site references foundation via workspace
|
|
288
|
+
# No path gymnastics needed
|
|
143
289
|
```
|
|
144
290
|
|
|
145
291
|
## Foundation Build Process
|
|
@@ -165,10 +311,96 @@ dist/
|
|
|
165
311
|
└── [preset].webp
|
|
166
312
|
```
|
|
167
313
|
|
|
314
|
+
## Workspace Configuration
|
|
315
|
+
|
|
316
|
+
Both templates use the same unified workspace configuration:
|
|
317
|
+
|
|
318
|
+
```yaml
|
|
319
|
+
# pnpm-workspace.yaml
|
|
320
|
+
packages:
|
|
321
|
+
- "site"
|
|
322
|
+
- "foundation"
|
|
323
|
+
- "sites/*"
|
|
324
|
+
- "foundations/*"
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```json
|
|
328
|
+
// package.json (workspaces field for npm compatibility)
|
|
329
|
+
{
|
|
330
|
+
"workspaces": ["site", "foundation", "sites/*", "foundations/*"]
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
This means **no config changes when evolving your project**.
|
|
335
|
+
|
|
336
|
+
## Evolving Your Project
|
|
337
|
+
|
|
338
|
+
The workspace is pre-configured for growth. Choose your path:
|
|
339
|
+
|
|
340
|
+
**Add alongside existing structure:**
|
|
341
|
+
|
|
342
|
+
```bash
|
|
343
|
+
# Keep site/ and foundation/, add more in sites/ and foundations/
|
|
344
|
+
mkdir -p sites/docs
|
|
345
|
+
mkdir -p foundations/documentation
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Or migrate to multi-site structure:**
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
# Move and rename by purpose
|
|
352
|
+
mv site sites/marketing
|
|
353
|
+
mv foundation foundations/marketing
|
|
354
|
+
|
|
355
|
+
# Update package names and workspace dependencies in package.json files
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Both patterns work simultaneously—evolve gradually as needed.
|
|
359
|
+
|
|
360
|
+
## Releasing a Foundation
|
|
361
|
+
|
|
362
|
+
Publish your foundation to [uniweb.app](https://uniweb.app) to make it available for your sites:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
uniweb login # First time only
|
|
366
|
+
uniweb build
|
|
367
|
+
uniweb publish # Publishes the foundation in the current directory
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Each release creates a new version. Sites link to foundations at runtime and control their own update strategy—automatic, minor-only, patch-only, or pinned to a specific version. You own your foundations and license them to sites—yours or your clients'.
|
|
371
|
+
|
|
372
|
+
You can also publish to npm:
|
|
373
|
+
|
|
374
|
+
```bash
|
|
375
|
+
npm publish
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## FAQ
|
|
379
|
+
|
|
380
|
+
**How is this different from MDX?**
|
|
381
|
+
|
|
382
|
+
MDX blends markdown and JSX—content authors write code. Uniweb keeps them separate: content stays in markdown, components stay in React. Content authors can't break components, and component updates don't require content changes.
|
|
383
|
+
|
|
384
|
+
**How is this different from Astro?**
|
|
385
|
+
|
|
386
|
+
Astro is a static site generator. Uniweb is a content architecture that works with any deployment (static, SSR, or platform-managed). The Foundation model means components are reusable across sites and ready for visual editing.
|
|
387
|
+
|
|
388
|
+
**Do I need uniweb.app?**
|
|
389
|
+
|
|
390
|
+
No. Local markdown files work great for developer-managed sites. The platform adds dynamic content, visual editing, and team collaboration when you need it.
|
|
391
|
+
|
|
392
|
+
**Is this SEO-friendly?**
|
|
393
|
+
|
|
394
|
+
Yes. Content is pre-embedded in the initial HTML—no fetch waterfalls, no layout shifts, instant interaction. Meta tags are generated per page for proper social sharing previews. SSG and SSR are also supported.
|
|
395
|
+
|
|
396
|
+
**What about dynamic routes?**
|
|
397
|
+
|
|
398
|
+
Pages can define data sources that auto-generate subroutes. A `/blog` page can have an index (listing) and a `[slug]` template that renders each post. No manual folders for every entry.
|
|
399
|
+
|
|
168
400
|
## Related Packages
|
|
169
401
|
|
|
170
|
-
- [`@uniweb/build`](https://github.com/uniweb/build)
|
|
171
|
-
- [`@uniweb/runtime`](https://github.com/uniweb/runtime)
|
|
402
|
+
- [`@uniweb/build`](https://github.com/uniweb/build) — Foundation build tooling
|
|
403
|
+
- [`@uniweb/runtime`](https://github.com/uniweb/runtime) — Runtime loader for sites
|
|
172
404
|
|
|
173
405
|
## License
|
|
174
406
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"uniweb": "
|
|
7
|
+
"uniweb": "src/index.js"
|
|
8
8
|
},
|
|
9
9
|
"files": [
|
|
10
10
|
"src"
|
package/src/index.js
CHANGED
|
@@ -7,9 +7,8 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
9
|
* npx uniweb create [project-name]
|
|
10
|
-
* npx uniweb create --template site
|
|
11
|
-
* npx uniweb create --template
|
|
12
|
-
* npx uniweb create --template workspace
|
|
10
|
+
* npx uniweb create --template single # site/ + foundation/ (default)
|
|
11
|
+
* npx uniweb create --template multi # sites/* + foundations/*
|
|
13
12
|
* npx uniweb build
|
|
14
13
|
* npx uniweb build --target foundation
|
|
15
14
|
*/
|
|
@@ -51,17 +50,13 @@ function title(message) {
|
|
|
51
50
|
|
|
52
51
|
// Template definitions
|
|
53
52
|
const templates = {
|
|
54
|
-
|
|
55
|
-
name: '
|
|
56
|
-
description: '
|
|
53
|
+
single: {
|
|
54
|
+
name: 'Single Project',
|
|
55
|
+
description: 'One site + one foundation in site/ and foundation/ (recommended)',
|
|
57
56
|
},
|
|
58
|
-
|
|
59
|
-
name: 'Site
|
|
60
|
-
description: '
|
|
61
|
-
},
|
|
62
|
-
foundation: {
|
|
63
|
-
name: 'Foundation Only',
|
|
64
|
-
description: 'A standalone foundation package',
|
|
57
|
+
multi: {
|
|
58
|
+
name: 'Multi-Site Project',
|
|
59
|
+
description: 'Multiple sites and foundations in sites/* and foundations/*',
|
|
65
60
|
},
|
|
66
61
|
}
|
|
67
62
|
|
|
@@ -126,19 +121,14 @@ async function main() {
|
|
|
126
121
|
message: 'What would you like to create?',
|
|
127
122
|
choices: [
|
|
128
123
|
{
|
|
129
|
-
title: templates.
|
|
130
|
-
description: templates.
|
|
131
|
-
value: '
|
|
132
|
-
},
|
|
133
|
-
{
|
|
134
|
-
title: templates.site.name,
|
|
135
|
-
description: templates.site.description,
|
|
136
|
-
value: 'site',
|
|
124
|
+
title: templates.single.name,
|
|
125
|
+
description: templates.single.description,
|
|
126
|
+
value: 'single',
|
|
137
127
|
},
|
|
138
128
|
{
|
|
139
|
-
title: templates.
|
|
140
|
-
description: templates.
|
|
141
|
-
value: '
|
|
129
|
+
title: templates.multi.name,
|
|
130
|
+
description: templates.multi.description,
|
|
131
|
+
value: 'multi',
|
|
142
132
|
},
|
|
143
133
|
],
|
|
144
134
|
},
|
|
@@ -169,14 +159,11 @@ async function main() {
|
|
|
169
159
|
|
|
170
160
|
// Generate project based on template
|
|
171
161
|
switch (templateType) {
|
|
172
|
-
case '
|
|
173
|
-
await
|
|
174
|
-
break
|
|
175
|
-
case 'site':
|
|
176
|
-
await createSite(projectDir, projectName)
|
|
162
|
+
case 'single':
|
|
163
|
+
await createSingleProject(projectDir, projectName)
|
|
177
164
|
break
|
|
178
|
-
case '
|
|
179
|
-
await
|
|
165
|
+
case 'multi':
|
|
166
|
+
await createMultiProject(projectDir, projectName)
|
|
180
167
|
break
|
|
181
168
|
}
|
|
182
169
|
|
|
@@ -186,15 +173,7 @@ async function main() {
|
|
|
186
173
|
log(`Next steps:\n`)
|
|
187
174
|
log(` ${colors.cyan}cd ${projectName}${colors.reset}`)
|
|
188
175
|
log(` ${colors.cyan}pnpm install${colors.reset}`)
|
|
189
|
-
|
|
190
|
-
if (templateType === 'workspace') {
|
|
191
|
-
log(` ${colors.cyan}pnpm dev${colors.reset}`)
|
|
192
|
-
} else if (templateType === 'site') {
|
|
193
|
-
log(` ${colors.cyan}pnpm dev${colors.reset}`)
|
|
194
|
-
} else {
|
|
195
|
-
log(` ${colors.cyan}pnpm build${colors.reset}`)
|
|
196
|
-
}
|
|
197
|
-
|
|
176
|
+
log(` ${colors.cyan}pnpm dev${colors.reset}`)
|
|
198
177
|
log('')
|
|
199
178
|
}
|
|
200
179
|
|
|
@@ -210,49 +189,62 @@ ${colors.bright}Commands:${colors.reset}
|
|
|
210
189
|
build Build the current project
|
|
211
190
|
|
|
212
191
|
${colors.bright}Create Options:${colors.reset}
|
|
213
|
-
--template <type> Project template (
|
|
192
|
+
--template <type> Project template (single, multi)
|
|
214
193
|
|
|
215
194
|
${colors.bright}Build Options:${colors.reset}
|
|
216
195
|
--target <type> Build target (foundation, site) - auto-detected if not specified
|
|
196
|
+
--platform <name> Deployment platform (e.g., vercel) for platform-specific output
|
|
217
197
|
|
|
218
198
|
${colors.bright}Examples:${colors.reset}
|
|
219
199
|
npx uniweb create my-project
|
|
220
|
-
npx uniweb create my-
|
|
221
|
-
npx uniweb create my-
|
|
200
|
+
npx uniweb create my-project --template single
|
|
201
|
+
npx uniweb create my-workspace --template multi
|
|
222
202
|
npx uniweb build
|
|
223
203
|
npx uniweb build --target foundation
|
|
224
204
|
|
|
225
205
|
${colors.bright}Templates:${colors.reset}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
foundation Standalone foundation package
|
|
206
|
+
single One site + one foundation in site/ and foundation/ (default)
|
|
207
|
+
multi Multiple sites and foundations in sites/* and foundations/*
|
|
229
208
|
`)
|
|
230
209
|
}
|
|
231
210
|
|
|
232
211
|
// Template generators
|
|
233
|
-
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Creates the common project base: package.json, pnpm-workspace.yaml, .gitignore
|
|
215
|
+
* Both single and multi templates share the same workspace configuration.
|
|
216
|
+
*/
|
|
217
|
+
function createProjectBase(projectDir, projectName, defaultFilter) {
|
|
234
218
|
mkdirSync(projectDir, { recursive: true })
|
|
235
219
|
|
|
236
|
-
// Root package.json
|
|
220
|
+
// Root package.json (workspaces field for npm compatibility)
|
|
237
221
|
writeJSON(join(projectDir, 'package.json'), {
|
|
238
222
|
name: projectName,
|
|
239
223
|
version: '0.1.0',
|
|
240
224
|
private: true,
|
|
241
225
|
type: 'module',
|
|
242
|
-
workspaces: ['packages/*'],
|
|
243
226
|
scripts: {
|
|
244
|
-
dev:
|
|
245
|
-
'dev:runtime':
|
|
227
|
+
dev: `pnpm --filter ${defaultFilter} dev`,
|
|
228
|
+
'dev:runtime': `VITE_FOUNDATION_MODE=runtime pnpm --filter ${defaultFilter} dev`,
|
|
246
229
|
build: 'pnpm -r build',
|
|
247
230
|
},
|
|
231
|
+
workspaces: [
|
|
232
|
+
'site',
|
|
233
|
+
'foundation',
|
|
234
|
+
'sites/*',
|
|
235
|
+
'foundations/*',
|
|
236
|
+
],
|
|
248
237
|
pnpm: {
|
|
249
238
|
onlyBuiltDependencies: ['esbuild', 'sharp'],
|
|
250
239
|
},
|
|
251
240
|
})
|
|
252
241
|
|
|
253
|
-
// pnpm-workspace.yaml
|
|
242
|
+
// pnpm-workspace.yaml (all patterns for seamless evolution)
|
|
254
243
|
writeFile(join(projectDir, 'pnpm-workspace.yaml'), `packages:
|
|
255
|
-
- '
|
|
244
|
+
- 'site'
|
|
245
|
+
- 'foundation'
|
|
246
|
+
- 'sites/*'
|
|
247
|
+
- 'foundations/*'
|
|
256
248
|
`)
|
|
257
249
|
|
|
258
250
|
// .gitignore
|
|
@@ -261,11 +253,19 @@ dist
|
|
|
261
253
|
.DS_Store
|
|
262
254
|
*.local
|
|
263
255
|
`)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Creates a single project with site/ and foundation/ as sibling packages.
|
|
260
|
+
* This is the default template for new projects.
|
|
261
|
+
*/
|
|
262
|
+
async function createSingleProject(projectDir, projectName) {
|
|
263
|
+
createProjectBase(projectDir, projectName, 'site')
|
|
264
264
|
|
|
265
265
|
// README.md
|
|
266
266
|
writeFile(join(projectDir, 'README.md'), `# ${projectName}
|
|
267
267
|
|
|
268
|
-
|
|
268
|
+
Structured Vite + React, ready to go.
|
|
269
269
|
|
|
270
270
|
## Quick Start
|
|
271
271
|
|
|
@@ -280,16 +280,21 @@ Open [http://localhost:3000](http://localhost:3000) to see your site.
|
|
|
280
280
|
|
|
281
281
|
\`\`\`
|
|
282
282
|
${projectName}/
|
|
283
|
-
├──
|
|
284
|
-
│ ├──
|
|
285
|
-
│ │
|
|
286
|
-
│ │
|
|
287
|
-
│ │
|
|
288
|
-
│
|
|
289
|
-
│
|
|
290
|
-
│
|
|
291
|
-
│
|
|
292
|
-
|
|
283
|
+
├── site/ # Content + configuration
|
|
284
|
+
│ ├── pages/ # File-based routing
|
|
285
|
+
│ │ └── home/
|
|
286
|
+
│ │ ├── page.yml # Page metadata
|
|
287
|
+
│ │ └── 1-hero.md # Section content
|
|
288
|
+
│ ├── locales/ # i18n (mirrors pages/)
|
|
289
|
+
│ ├── src/ # Site entry point
|
|
290
|
+
│ └── public/ # Static assets
|
|
291
|
+
│
|
|
292
|
+
├── foundation/ # Your components
|
|
293
|
+
│ └── src/
|
|
294
|
+
│ ├── components/ # React components
|
|
295
|
+
│ ├── meta.js # Foundation metadata
|
|
296
|
+
│ └── styles.css # Tailwind styles
|
|
297
|
+
│
|
|
293
298
|
├── package.json
|
|
294
299
|
└── pnpm-workspace.yaml
|
|
295
300
|
\`\`\`
|
|
@@ -302,8 +307,8 @@ ${projectName}/
|
|
|
302
307
|
pnpm dev
|
|
303
308
|
\`\`\`
|
|
304
309
|
|
|
305
|
-
Edit components in \`
|
|
306
|
-
Edit content in \`
|
|
310
|
+
Edit components in \`foundation/src/components/\` and see changes instantly via HMR.
|
|
311
|
+
Edit content in \`site/pages/\` to add or modify pages.
|
|
307
312
|
|
|
308
313
|
### Runtime Loading Mode
|
|
309
314
|
|
|
@@ -317,31 +322,23 @@ Use this to debug issues that only appear in production.
|
|
|
317
322
|
## Building for Production
|
|
318
323
|
|
|
319
324
|
\`\`\`bash
|
|
320
|
-
# Build all packages (foundations and sites)
|
|
321
325
|
pnpm build
|
|
322
|
-
|
|
323
|
-
# Build a specific package
|
|
324
|
-
pnpm --filter foundation build
|
|
325
|
-
pnpm --filter site build
|
|
326
|
-
|
|
327
|
-
# Build only certain packages
|
|
328
|
-
pnpm --filter marketing-site --filter docs-site build
|
|
329
326
|
\`\`\`
|
|
330
327
|
|
|
331
328
|
**Output:**
|
|
332
|
-
- \`
|
|
333
|
-
- \`
|
|
329
|
+
- \`foundation/dist/\` — Bundled components, CSS, and schema.json
|
|
330
|
+
- \`site/dist/\` — Production-ready static site
|
|
334
331
|
|
|
335
332
|
## Adding Components
|
|
336
333
|
|
|
337
|
-
1. Create a new folder in \`
|
|
334
|
+
1. Create a new folder in \`foundation/src/components/YourComponent/\`
|
|
338
335
|
2. Add \`index.jsx\` with your React component
|
|
339
336
|
3. Add \`meta.js\` describing the component's content slots and options
|
|
340
|
-
4. Export from \`
|
|
337
|
+
4. Export from \`foundation/src/index.js\`
|
|
341
338
|
|
|
342
339
|
## Adding Pages
|
|
343
340
|
|
|
344
|
-
1. Create a folder in \`
|
|
341
|
+
1. Create a folder in \`site/pages/your-page/\`
|
|
345
342
|
2. Add \`page.yml\` with page metadata
|
|
346
343
|
3. Add markdown files (\`1-section.md\`, \`2-section.md\`, etc.) for each section
|
|
347
344
|
|
|
@@ -350,98 +347,197 @@ Each markdown file specifies which component to use:
|
|
|
350
347
|
\`\`\`markdown
|
|
351
348
|
---
|
|
352
349
|
component: Hero
|
|
353
|
-
title: Your Title
|
|
354
|
-
subtitle: Your subtitle
|
|
355
350
|
---
|
|
356
351
|
|
|
357
|
-
|
|
352
|
+
# Your Title
|
|
353
|
+
|
|
354
|
+
Your subtitle or description here.
|
|
355
|
+
|
|
356
|
+
[Get Started](#)
|
|
358
357
|
\`\`\`
|
|
359
358
|
|
|
360
|
-
##
|
|
359
|
+
## Scaling Up
|
|
361
360
|
|
|
362
|
-
|
|
361
|
+
The workspace is pre-configured for growth—no config changes needed.
|
|
363
362
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
363
|
+
**Add a second site** (keep existing structure):
|
|
364
|
+
|
|
365
|
+
\`\`\`bash
|
|
366
|
+
mkdir -p sites/docs
|
|
367
|
+
# Create your docs site in sites/docs/
|
|
368
|
+
\`\`\`
|
|
369
|
+
|
|
370
|
+
**Or migrate to multi-site structure**:
|
|
371
|
+
|
|
372
|
+
\`\`\`bash
|
|
373
|
+
# Move and rename by purpose
|
|
374
|
+
mv site sites/marketing
|
|
375
|
+
mv foundation foundations/marketing
|
|
376
|
+
|
|
377
|
+
# Update package names in package.json files
|
|
378
|
+
# Update dependencies to reference new names
|
|
368
379
|
\`\`\`
|
|
369
380
|
|
|
370
|
-
|
|
381
|
+
Both patterns work simultaneously—evolve gradually as needed.
|
|
371
382
|
|
|
372
|
-
|
|
383
|
+
## Publishing Your Foundation
|
|
373
384
|
|
|
374
|
-
|
|
385
|
+
Your \`foundation/\` is already a complete package:
|
|
375
386
|
|
|
376
387
|
\`\`\`bash
|
|
377
|
-
|
|
388
|
+
cd foundation
|
|
389
|
+
npx uniweb build
|
|
390
|
+
npm publish
|
|
378
391
|
\`\`\`
|
|
379
392
|
|
|
380
|
-
|
|
393
|
+
## What is Uniweb?
|
|
394
|
+
|
|
395
|
+
Uniweb is a **Component Web Platform** that bridges content and components.
|
|
396
|
+
Foundations define the vocabulary (available components, options, design rules).
|
|
397
|
+
Sites provide content that flows through Foundations.
|
|
398
|
+
|
|
399
|
+
Learn more:
|
|
400
|
+
- [Uniweb on GitHub](https://github.com/uniweb)
|
|
401
|
+
- [CLI Documentation](https://github.com/uniweb/cli)
|
|
402
|
+
- [uniweb.app](https://uniweb.app) — Visual editing platform
|
|
403
|
+
|
|
404
|
+
`)
|
|
405
|
+
|
|
406
|
+
// Create site package
|
|
407
|
+
await createSite(join(projectDir, 'site'), 'site', true)
|
|
408
|
+
|
|
409
|
+
// Create foundation package
|
|
410
|
+
await createFoundation(join(projectDir, 'foundation'), 'foundation', true)
|
|
411
|
+
|
|
412
|
+
// Update site to reference workspace foundation
|
|
413
|
+
const sitePackageJson = join(projectDir, 'site/package.json')
|
|
414
|
+
const sitePkg = JSON.parse(readFile(sitePackageJson))
|
|
415
|
+
sitePkg.dependencies['foundation'] = 'workspace:*'
|
|
416
|
+
writeJSON(sitePackageJson, sitePkg)
|
|
417
|
+
|
|
418
|
+
success(`Created project: ${projectName}`)
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Creates a multi-site/foundation workspace with sites/ and foundations/ directories.
|
|
423
|
+
* Used for larger projects with multiple sites sharing foundations.
|
|
424
|
+
*/
|
|
425
|
+
async function createMultiProject(projectDir, projectName) {
|
|
426
|
+
createProjectBase(projectDir, projectName, 'marketing')
|
|
427
|
+
|
|
428
|
+
// README.md
|
|
429
|
+
writeFile(join(projectDir, 'README.md'), `# ${projectName}
|
|
430
|
+
|
|
431
|
+
A Uniweb workspace for multiple sites and foundations.
|
|
432
|
+
|
|
433
|
+
## Quick Start
|
|
381
434
|
|
|
382
435
|
\`\`\`bash
|
|
383
|
-
|
|
436
|
+
pnpm install
|
|
437
|
+
pnpm dev
|
|
384
438
|
\`\`\`
|
|
385
439
|
|
|
386
|
-
|
|
440
|
+
Open [http://localhost:3000](http://localhost:3000) to see the marketing site.
|
|
387
441
|
|
|
388
|
-
|
|
389
|
-
|
|
442
|
+
## Project Structure
|
|
443
|
+
|
|
444
|
+
\`\`\`
|
|
445
|
+
${projectName}/
|
|
446
|
+
├── sites/
|
|
447
|
+
│ └── marketing/ # Main marketing site
|
|
448
|
+
│ ├── pages/ # Content pages
|
|
449
|
+
│ ├── locales/ # i18n
|
|
450
|
+
│ └── src/
|
|
451
|
+
│
|
|
452
|
+
├── foundations/
|
|
453
|
+
│ └── marketing/ # Marketing foundation
|
|
454
|
+
│ └── src/
|
|
455
|
+
│ ├── components/ # React components
|
|
456
|
+
│ └── styles.css # Tailwind styles
|
|
457
|
+
│
|
|
458
|
+
├── package.json
|
|
459
|
+
└── pnpm-workspace.yaml
|
|
390
460
|
\`\`\`
|
|
391
461
|
|
|
392
|
-
|
|
462
|
+
## Development
|
|
393
463
|
|
|
394
464
|
\`\`\`bash
|
|
395
|
-
|
|
465
|
+
# Run the marketing site (default)
|
|
466
|
+
pnpm dev
|
|
467
|
+
|
|
468
|
+
# Run a specific site
|
|
469
|
+
pnpm --filter docs dev
|
|
470
|
+
|
|
471
|
+
# Runtime loading mode (tests production behavior)
|
|
472
|
+
pnpm dev:runtime
|
|
396
473
|
\`\`\`
|
|
397
474
|
|
|
398
|
-
##
|
|
475
|
+
## Adding More Sites
|
|
399
476
|
|
|
400
|
-
|
|
477
|
+
Create a new site in \`sites/\`:
|
|
401
478
|
|
|
479
|
+
\`\`\`bash
|
|
480
|
+
mkdir -p sites/docs
|
|
481
|
+
# Copy structure from sites/marketing or create manually
|
|
402
482
|
\`\`\`
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
└── site/
|
|
483
|
+
|
|
484
|
+
Update \`sites/docs/site.yml\` to specify which foundation:
|
|
485
|
+
|
|
486
|
+
\`\`\`yaml
|
|
487
|
+
name: docs
|
|
488
|
+
defaultLanguage: en
|
|
489
|
+
foundation: documentation # Or marketing, or any foundation
|
|
411
490
|
\`\`\`
|
|
412
491
|
|
|
413
|
-
|
|
492
|
+
## Adding More Foundations
|
|
414
493
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
494
|
+
Create a new foundation in \`foundations/\`:
|
|
495
|
+
|
|
496
|
+
\`\`\`bash
|
|
497
|
+
mkdir -p foundations/documentation
|
|
498
|
+
# Build components for documentation use case
|
|
418
499
|
\`\`\`
|
|
419
500
|
|
|
420
|
-
|
|
501
|
+
Name foundations by purpose: marketing, documentation, learning, etc.
|
|
421
502
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
503
|
+
## Building for Production
|
|
504
|
+
|
|
505
|
+
\`\`\`bash
|
|
506
|
+
# Build everything
|
|
507
|
+
pnpm build
|
|
508
|
+
|
|
509
|
+
# Build specific packages
|
|
510
|
+
pnpm --filter marketing build
|
|
511
|
+
pnpm --filter foundations/marketing build
|
|
512
|
+
\`\`\`
|
|
513
|
+
|
|
514
|
+
## Learn More
|
|
425
515
|
|
|
426
|
-
Learn more:
|
|
427
516
|
- [Uniweb on GitHub](https://github.com/uniweb)
|
|
428
|
-
- [
|
|
429
|
-
- [uniweb.app](https://uniweb.app) — Full publishing platform
|
|
517
|
+
- [uniweb.app](https://uniweb.app) — Visual editing platform
|
|
430
518
|
|
|
431
519
|
`)
|
|
432
520
|
|
|
433
|
-
// Create site
|
|
434
|
-
await createSite(join(projectDir, '
|
|
521
|
+
// Create first site in sites/marketing
|
|
522
|
+
await createSite(join(projectDir, 'sites/marketing'), 'marketing', true)
|
|
435
523
|
|
|
436
|
-
// Create foundation
|
|
437
|
-
await createFoundation(join(projectDir, '
|
|
524
|
+
// Create first foundation in foundations/marketing
|
|
525
|
+
await createFoundation(join(projectDir, 'foundations/marketing'), 'marketing', true)
|
|
438
526
|
|
|
439
527
|
// Update site to reference workspace foundation
|
|
440
|
-
const sitePackageJson = join(projectDir, '
|
|
528
|
+
const sitePackageJson = join(projectDir, 'sites/marketing/package.json')
|
|
441
529
|
const sitePkg = JSON.parse(readFile(sitePackageJson))
|
|
442
|
-
sitePkg.dependencies['
|
|
530
|
+
sitePkg.dependencies['marketing'] = 'workspace:*'
|
|
443
531
|
writeJSON(sitePackageJson, sitePkg)
|
|
444
532
|
|
|
533
|
+
// Update site.yml to reference the marketing foundation
|
|
534
|
+
writeFile(join(projectDir, 'sites/marketing/site.yml'), `name: marketing
|
|
535
|
+
defaultLanguage: en
|
|
536
|
+
|
|
537
|
+
# Foundation to use for this site
|
|
538
|
+
foundation: marketing
|
|
539
|
+
`)
|
|
540
|
+
|
|
445
541
|
success(`Created workspace: ${projectName}`)
|
|
446
542
|
}
|
|
447
543
|
|
|
@@ -623,13 +719,13 @@ order: 1
|
|
|
623
719
|
// pages/home/1-hero.md
|
|
624
720
|
writeFile(join(projectDir, 'pages/home/1-hero.md'), `---
|
|
625
721
|
component: Hero
|
|
626
|
-
title: Welcome to ${projectName}
|
|
627
|
-
subtitle: Built with Uniweb and Vite
|
|
628
|
-
ctaText: Get Started
|
|
629
|
-
ctaUrl: "#"
|
|
630
722
|
---
|
|
631
723
|
|
|
632
|
-
|
|
724
|
+
# Welcome to ${projectName}
|
|
725
|
+
|
|
726
|
+
Built with Uniweb and Vite.
|
|
727
|
+
|
|
728
|
+
[Get Started](#)
|
|
633
729
|
`)
|
|
634
730
|
|
|
635
731
|
// README.md (only for standalone site, not workspace)
|
|
@@ -677,19 +773,22 @@ order: 2
|
|
|
677
773
|
\`\`\`markdown
|
|
678
774
|
---
|
|
679
775
|
component: Hero
|
|
680
|
-
|
|
681
|
-
subtitle: Section subtitle
|
|
776
|
+
theme: dark
|
|
682
777
|
---
|
|
683
778
|
|
|
684
|
-
|
|
779
|
+
# Section Title
|
|
780
|
+
|
|
781
|
+
Section description here.
|
|
782
|
+
|
|
783
|
+
[Call to Action](#)
|
|
685
784
|
\`\`\`
|
|
686
785
|
|
|
687
786
|
## How It Works
|
|
688
787
|
|
|
689
788
|
- Each folder in \`pages/\` becomes a route (\`/home\`, \`/about\`, etc.)
|
|
690
789
|
- Section files are numbered to control order (\`1-*.md\`, \`2-*.md\`)
|
|
691
|
-
-
|
|
692
|
-
-
|
|
790
|
+
- Frontmatter specifies the component and configuration parameters
|
|
791
|
+
- Content in the markdown body is semantically parsed into structured data
|
|
693
792
|
|
|
694
793
|
## Configuration
|
|
695
794
|
|
|
@@ -901,22 +1000,36 @@ export { default } from './index.js'
|
|
|
901
1000
|
// src/components/Hero/index.jsx
|
|
902
1001
|
writeFile(join(projectDir, 'src/components/Hero/index.jsx'), `import React from 'react'
|
|
903
1002
|
|
|
904
|
-
export function Hero({ content }) {
|
|
905
|
-
|
|
1003
|
+
export function Hero({ content, params }) {
|
|
1004
|
+
// Extract from semantic parser structure
|
|
1005
|
+
const { title } = content.main?.header || {}
|
|
1006
|
+
const { paragraphs = [], links = [] } = content.main?.body || {}
|
|
1007
|
+
const { theme = 'light' } = params || {}
|
|
1008
|
+
|
|
1009
|
+
const isDark = theme === 'dark'
|
|
1010
|
+
const cta = links[0]
|
|
906
1011
|
|
|
907
1012
|
return (
|
|
908
|
-
<section className=
|
|
1013
|
+
<section className={\`py-20 px-6 \${isDark ? 'bg-gradient-to-br from-primary to-blue-700 text-white' : 'bg-gray-50'}\`}>
|
|
909
1014
|
<div className="max-w-4xl mx-auto text-center">
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1015
|
+
{title && (
|
|
1016
|
+
<h1 className="text-4xl md:text-5xl font-bold mb-6">{title}</h1>
|
|
1017
|
+
)}
|
|
1018
|
+
{paragraphs[0] && (
|
|
1019
|
+
<p className={\`text-lg mb-8 \${isDark ? 'text-blue-100' : 'text-gray-600'}\`}>
|
|
1020
|
+
{paragraphs[0]}
|
|
1021
|
+
</p>
|
|
913
1022
|
)}
|
|
914
|
-
{
|
|
1023
|
+
{cta && (
|
|
915
1024
|
<a
|
|
916
|
-
href={
|
|
917
|
-
className=
|
|
1025
|
+
href={cta.url}
|
|
1026
|
+
className={\`inline-block px-6 py-3 font-semibold rounded-lg transition-colors \${
|
|
1027
|
+
isDark
|
|
1028
|
+
? 'bg-white text-primary hover:bg-blue-50'
|
|
1029
|
+
: 'bg-primary text-white hover:bg-blue-700'
|
|
1030
|
+
}\`}
|
|
918
1031
|
>
|
|
919
|
-
{
|
|
1032
|
+
{cta.text}
|
|
920
1033
|
</a>
|
|
921
1034
|
)}
|
|
922
1035
|
</div>
|
|
@@ -930,26 +1043,45 @@ export default Hero
|
|
|
930
1043
|
// src/components/Hero/meta.js
|
|
931
1044
|
writeFile(join(projectDir, 'src/components/Hero/meta.js'), `/**
|
|
932
1045
|
* Hero Component Metadata
|
|
1046
|
+
*
|
|
1047
|
+
* Content comes from the markdown body (parsed semantically):
|
|
1048
|
+
* - H1 → title (content.main.header.title)
|
|
1049
|
+
* - Paragraphs → description (content.main.body.paragraphs)
|
|
1050
|
+
* - Links → CTA buttons (content.main.body.links)
|
|
933
1051
|
*/
|
|
934
1052
|
export default {
|
|
935
1053
|
title: 'Hero Banner',
|
|
936
|
-
description: 'A prominent header section with headline,
|
|
1054
|
+
description: 'A prominent header section with headline, description, and call-to-action',
|
|
937
1055
|
category: 'Headers',
|
|
938
1056
|
|
|
1057
|
+
// Content structure (informational - describes what the semantic parser provides)
|
|
939
1058
|
elements: {
|
|
940
1059
|
title: {
|
|
941
1060
|
label: 'Headline',
|
|
1061
|
+
description: 'From H1 in markdown',
|
|
942
1062
|
required: true,
|
|
943
1063
|
},
|
|
944
|
-
|
|
945
|
-
label: '
|
|
1064
|
+
paragraphs: {
|
|
1065
|
+
label: 'Description',
|
|
1066
|
+
description: 'From paragraphs in markdown',
|
|
946
1067
|
},
|
|
947
1068
|
links: {
|
|
948
1069
|
label: 'Call to Action',
|
|
1070
|
+
description: 'From links in markdown',
|
|
949
1071
|
},
|
|
950
1072
|
},
|
|
951
1073
|
|
|
1074
|
+
// Configuration parameters (set in frontmatter)
|
|
952
1075
|
properties: {
|
|
1076
|
+
theme: {
|
|
1077
|
+
type: 'select',
|
|
1078
|
+
label: 'Theme',
|
|
1079
|
+
options: [
|
|
1080
|
+
{ value: 'light', label: 'Light' },
|
|
1081
|
+
{ value: 'dark', label: 'Dark' },
|
|
1082
|
+
],
|
|
1083
|
+
default: 'light',
|
|
1084
|
+
},
|
|
953
1085
|
alignment: {
|
|
954
1086
|
type: 'select',
|
|
955
1087
|
label: 'Text Alignment',
|