uniweb 0.2.34 → 0.2.36

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": "uniweb",
3
- "version": "0.2.34",
3
+ "version": "0.2.36",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "src",
11
- "templates"
11
+ "templates",
12
+ "partials"
12
13
  ],
13
14
  "keywords": [
14
15
  "uniweb",
@@ -37,8 +38,8 @@
37
38
  "prompts": "^2.4.2",
38
39
  "tar": "^7.0.0",
39
40
  "@uniweb/build": "0.1.19",
41
+ "@uniweb/runtime": "0.2.10",
40
42
  "@uniweb/core": "0.1.8",
41
- "@uniweb/kit": "0.1.4",
42
- "@uniweb/runtime": "0.2.10"
43
+ "@uniweb/kit": "0.1.4"
43
44
  }
44
45
  }
@@ -0,0 +1,29 @@
1
+ ### Adding a New Page
2
+
3
+ 1. Create a folder: `site/pages/my-page/`
4
+ 2. Add `page.yml`:
5
+ ```yaml
6
+ title: My Page
7
+ ```
8
+ 3. Add section files: `1-hero.md`, `2-content.md`, etc.
9
+
10
+ ### Section Ordering
11
+
12
+ By default, sections are discovered from `.md` files and sorted by numeric prefix (`1-`, `2-`, `2.5-`).
13
+
14
+ For more control, use the `sections` property in `page.yml`:
15
+
16
+ ```yaml
17
+ title: My Page
18
+ sections:
19
+ - hero
20
+ - features
21
+ - pricing
22
+ ```
23
+
24
+ This approach makes reordering easy (just move lines) and allows temporarily removing sections by commenting them out.
25
+
26
+ **Options:**
27
+ - `sections: *` or omit — auto-discover and sort `.md` files (default)
28
+ - `sections: [hero, features]` — explicit ordering, no numeric prefixes needed
29
+ - `sections: []` or no `.md` files — pure route with no content sections
@@ -0,0 +1,3 @@
1
+ ## AI Assistance
2
+
3
+ See [CLAUDE.md](./CLAUDE.md) for detailed instructions that AI assistants can use.
@@ -0,0 +1,226 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance for AI assistants working with this Uniweb project.
4
+
5
+ ## Project Structure
6
+
7
+ Uniweb projects have two structures. A single project can be converted to multi-site:
8
+
9
+ **Single (one foundation, one site):**
10
+ ```
11
+ project/
12
+ ├── foundation/ # Purpose-built component library (React)
13
+ ├── site/ # Content (markdown pages)
14
+ └── pnpm-workspace.yaml
15
+ ```
16
+
17
+ **Multi-site (multiple foundations and sites):**
18
+ ```
19
+ project/
20
+ ├── foundations/ # Purpose-built component libraries
21
+ │ └── marketing/
22
+ ├── sites/ # Content sites
23
+ │ └── my-org/
24
+ └── pnpm-workspace.yaml
25
+ ```
26
+
27
+ - **Foundation**: React components. Components with `meta.js` are *exposed* to content authors.
28
+ - **Site**: Markdown content. Each section references a component via `type:` in frontmatter.
29
+
30
+ ## Commands
31
+
32
+ ```bash
33
+ pnpm install # Install dependencies
34
+ pnpm dev # Start dev server (runs default/main site)
35
+ pnpm build # Build for production
36
+ ```
37
+
38
+ Multi-site also supports:
39
+ ```bash
40
+ pnpm dev:all # Start all sites in parallel
41
+ pnpm build:all # Build all sites
42
+ ```
43
+
44
+ ## Discovering Components
45
+
46
+ Exposed components live in `foundation/src/components/` (or `foundations/*/src/components/`).
47
+
48
+ ```bash
49
+ # List exposed components (those with meta.js)
50
+ ls foundation/src/components/*/meta.js
51
+ ```
52
+
53
+ **Understanding a component:**
54
+
55
+ 1. **`meta.js`** - Defines the component's interface:
56
+ - `title`, `description` - What the component does
57
+ - `elements` - What content it expects (title, paragraphs, links, items, etc.)
58
+ - `properties` - Configurable parameters (theme, layout, etc.)
59
+ - `hidden: true` - Component exists but isn't selectable from frontmatter
60
+
61
+ 2. **`index.jsx`** - The React implementation
62
+
63
+ 3. **Existing content** - See how the component is used in `site/pages/`
64
+
65
+ **Note:** Components without `meta.js` are internal helpers used by other components.
66
+
67
+ ## Content Authoring
68
+
69
+ ### Section Format
70
+
71
+ Each section is a markdown file with YAML frontmatter:
72
+
73
+ ```markdown
74
+ ---
75
+ type: ComponentName # Must match an exposed component
76
+ theme: dark # Parameter from meta.js properties
77
+ ---
78
+
79
+ ### Eyebrow Text # H3 before H1 → pretitle
80
+
81
+ # Main Headline # H1 → title
82
+
83
+ ## Subtitle # H2 after H1 → subtitle
84
+
85
+ Description paragraph.
86
+
87
+ [Call to Action](#link)
88
+
89
+ ![Image](image.jpg)
90
+ ```
91
+
92
+ ### Content Structure
93
+
94
+ The semantic parser extracts markdown into:
95
+
96
+ - **`content.main.header`** - title, pretitle, subtitle
97
+ - **`content.main.body`** - paragraphs, links, imgs, lists
98
+ - **`content.items`** - Groups created by H3 headings (each with its own header/body)
99
+
100
+ ### Page Organization
101
+
102
+ ```
103
+ site/pages/ # or sites/*/pages/
104
+ ├── @header/ # Rendered on all pages
105
+ ├── @footer/ # Rendered on all pages
106
+ └── [page-name]/
107
+ ├── page.yml # title, description, order
108
+ └── 1-section.md # Numbered for ordering
109
+ ```
110
+
111
+ **page.yml options:**
112
+ ```yaml
113
+ title: About Us
114
+ description: Learn about our company
115
+ order: 2 # Sort order in navigation
116
+
117
+ # Section ordering (optional)
118
+ sections:
119
+ - hero
120
+ - features
121
+ - pricing
122
+ ```
123
+
124
+ **Section ordering:**
125
+ - Default: `.md` files discovered and sorted by numeric prefix (`1-`, `2-`, `2.5-`)
126
+ - Explicit: Use `sections: [hero, features]` — no prefixes needed, easy reordering
127
+ - Empty: `sections: []` or no `.md` files — pure route with no content
128
+ - Wildcard: `sections: *` — explicitly use default behavior
129
+
130
+ ## Component Development
131
+
132
+ ### Props Interface
133
+
134
+ ```jsx
135
+ function MyComponent({ content, params, block, website }) {
136
+ const { title, pretitle } = content.main?.header || {}
137
+ const { paragraphs = [], links = [] } = content.main?.body || {}
138
+ const items = content.items || []
139
+ // ...
140
+ }
141
+ ```
142
+
143
+ ### Using @uniweb/kit
144
+
145
+ ```jsx
146
+ import { H1, P, Link, Image, Section, cn } from '@uniweb/kit'
147
+ ```
148
+
149
+ - `H1, H2, H3, P, Span` - Typography (handles arrays, filters empty)
150
+ - `Link` - Smart routing
151
+ - `Image` - Optimized images
152
+ - `Section` - Layout wrapper
153
+ - `cn()` - Tailwind class merging
154
+
155
+ ### Creating a New Component
156
+
157
+ 1. Create `foundation/src/components/NewComponent/index.jsx`
158
+ 2. Create `foundation/src/components/NewComponent/meta.js`
159
+ 3. Export from `foundation/src/index.js`
160
+ 4. Rebuild: `cd foundation && pnpm build`
161
+
162
+ ### meta.js Structure
163
+
164
+ ```javascript
165
+ export default {
166
+ title: 'Component Name',
167
+ description: 'What it does',
168
+ // hidden: true, // Uncomment to hide from content authors
169
+ elements: {
170
+ title: { label: 'Headline', required: true },
171
+ paragraphs: { label: 'Description' },
172
+ links: { label: 'Call to Action' },
173
+ },
174
+ properties: {
175
+ theme: {
176
+ type: 'select',
177
+ label: 'Theme',
178
+ options: [
179
+ { value: 'light', label: 'Light' },
180
+ { value: 'dark', label: 'Dark' },
181
+ ],
182
+ default: 'light',
183
+ },
184
+ },
185
+ }
186
+ ```
187
+
188
+ ### Parameter Philosophy
189
+
190
+ Design parameters that describe **intent**, not implementation:
191
+
192
+ | Good | Bad |
193
+ |------|-----|
194
+ | `theme: "dark"` | `backgroundColor: "#1a1a1a"` |
195
+ | `layout: "split"` | `gridTemplateColumns: "1fr 1fr"` |
196
+ | `size: "large"` | `fontSize: "2rem"` |
197
+
198
+ ## Tailwind CSS v4
199
+
200
+ Theme is defined in `foundation/src/styles.css`:
201
+
202
+ ```css
203
+ @import "tailwindcss";
204
+ @source "./components/**/*.{js,jsx}";
205
+
206
+ @theme {
207
+ --color-primary: #3b82f6;
208
+ }
209
+ ```
210
+
211
+ Use with: `bg-primary`, `text-primary`, `bg-primary/10` (10% opacity)
212
+
213
+ ## Troubleshooting
214
+
215
+ **"Could not load foundation"**
216
+ - Single: Check `site/package.json` has `"foundation": "file:../foundation"`
217
+ - Multi: Check `sites/*/package.json` has `"default": "file:../../foundations/default"`
218
+
219
+ **Component not appearing**
220
+ 1. Verify `meta.js` exists and doesn't have `hidden: true`
221
+ 2. Check it's exported from `foundation/src/index.js`
222
+ 3. Rebuild: `cd foundation && pnpm build`
223
+
224
+ **Styles not applying**
225
+ 1. Verify `@source` in styles.css includes your component path
226
+ 2. Check custom color names match `@theme` definitions
@@ -0,0 +1,15 @@
1
+ ## Component Documentation
2
+
3
+ Generate up-to-date documentation for all foundation components:
4
+
5
+ ```bash
6
+ # From within the foundation directory
7
+ pnpm docs
8
+ ```
9
+
10
+ This creates `COMPONENTS.md` with full details on each component's:
11
+ - Content elements (title, paragraphs, links, images, items)
12
+ - Parameters (theme, layout, columns, etc.)
13
+ - Presets (pre-configured parameter combinations)
14
+
15
+ The documentation is generated from component `meta.js` files, so it's always current.
@@ -0,0 +1,13 @@
1
+ ## Deployment
2
+
3
+ Build and deploy the `site/dist` folder:
4
+
5
+ ```bash
6
+ pnpm build
7
+ ```
8
+
9
+ **Supported platforms:**
10
+ - **Vercel**: `cd site && vercel`
11
+ - **Netlify**: Build command `pnpm build`, publish `site/dist`
12
+ - **GitHub Pages**: Use GitHub Actions
13
+ - **Cloudflare Pages**: Connect repo, set `pnpm build` and `site/dist`
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Foundation Exports
3
+ *
4
+ * Optional file to export foundation-level capabilities to the runtime:
5
+ * - Layout: Custom page layout component (receives header, body, footer, sidebars)
6
+ * - props: Foundation-wide props accessible via website.foundationProps
7
+ *
8
+ * The Layout component receives pre-rendered page areas as props:
9
+ * - page, website: Runtime context
10
+ * - header, body, footer: Core page regions (pre-rendered React elements)
11
+ * - left/leftPanel, right/rightPanel: Sidebar panels
12
+ *
13
+ * If this file is not provided, the runtime uses a default layout (header -> body -> footer).
14
+ */
@@ -0,0 +1,5 @@
1
+ ## Learn More
2
+
3
+ - [Uniweb Documentation](https://github.com/uniweb/cli)
4
+ - [@uniweb/kit Components](https://www.npmjs.com/package/@uniweb/kit)
5
+ - [Tailwind CSS v4](https://tailwindcss.com)
@@ -0,0 +1,8 @@
1
+ ## Quick Start
2
+
3
+ ```bash
4
+ pnpm install # Install dependencies
5
+ pnpm dev # Start development server
6
+ ```
7
+
8
+ Visit `http://localhost:5173` to see your site.
@@ -0,0 +1,110 @@
1
+ ## Search Functionality
2
+
3
+ Uniweb sites support full-text search out of the box. Search indexes are generated automatically at build time.
4
+
5
+ ### Enabling Search in Your Foundation
6
+
7
+ To add search to your site, your foundation needs the **Fuse.js** library:
8
+
9
+ ```bash
10
+ cd foundation
11
+ pnpm add fuse.js
12
+ ```
13
+
14
+ Then use the search helpers from `@uniweb/kit`:
15
+
16
+ ```jsx
17
+ import { useSearch } from '@uniweb/kit/search'
18
+
19
+ function SearchComponent({ website }) {
20
+ const { query, results, isLoading, clear } = useSearch(website)
21
+
22
+ return (
23
+ <div>
24
+ <input
25
+ type="search"
26
+ placeholder="Search..."
27
+ onChange={e => query(e.target.value)}
28
+ />
29
+ {isLoading && <span>Searching...</span>}
30
+ <ul>
31
+ {results.map(result => (
32
+ <li key={result.id}>
33
+ <a href={result.href}>{result.title}</a>
34
+ <p dangerouslySetInnerHTML=\{{ __html: result.snippetHtml }} />
35
+ </li>
36
+ ))}
37
+ </ul>
38
+ </div>
39
+ )
40
+ }
41
+ ```
42
+
43
+ ### Search Configuration
44
+
45
+ Search is enabled by default. To customize, add to `site/site.yml`:
46
+
47
+ ```yaml
48
+ search:
49
+ enabled: true
50
+ exclude:
51
+ routes: [/admin, /private] # Don't index these routes
52
+ components: [Debug] # Don't index these component types
53
+ ```
54
+
55
+ ### How It Works
56
+
57
+ 1. **Build time**: Uniweb extracts text from all pages and sections
58
+ 2. **Output**: A `search-index.json` file is generated in `dist/`
59
+ 3. **Runtime**: Components fetch and search the index client-side
60
+ 4. **Multi-locale**: Each locale gets its own search index
61
+
62
+ ### Search API
63
+
64
+ The `website` object provides these methods:
65
+
66
+ ```javascript
67
+ // Check if search is available
68
+ website.isSearchEnabled() // Returns boolean
69
+
70
+ // Get the URL for the search index
71
+ website.getSearchIndexUrl() // "/search-index.json" or "/es/search-index.json"
72
+
73
+ // Get full search configuration
74
+ website.getSearchConfig() // { enabled, indexUrl, locale, include, exclude }
75
+ ```
76
+
77
+ ### Search Result Format
78
+
79
+ Results from `useSearch()` include:
80
+
81
+ | Field | Description |
82
+ |-------|-------------|
83
+ | `id` | Unique result identifier |
84
+ | `type` | `"page"` or `"section"` |
85
+ | `route` | Page route |
86
+ | `href` | Full link including anchor |
87
+ | `title` | Result title |
88
+ | `pageTitle` | Parent page title (for sections) |
89
+ | `snippetHtml` | Excerpt with `<mark>` highlighted matches |
90
+ | `snippetText` | Plain text excerpt |
91
+
92
+ ### Without @uniweb/kit
93
+
94
+ If you prefer not to use the kit helpers, you can fetch the index directly:
95
+
96
+ ```javascript
97
+ // Fetch the search index
98
+ const response = await fetch(website.getSearchIndexUrl())
99
+ const index = await response.json()
100
+
101
+ // Use with Fuse.js directly
102
+ import Fuse from 'fuse.js'
103
+ const fuse = new Fuse(index.entries, {
104
+ keys: ['title', 'content'],
105
+ threshold: 0.35,
106
+ includeMatches: true
107
+ })
108
+
109
+ const results = fuse.search('your query')
110
+ ```