uniweb 0.2.46 → 0.2.48

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 CHANGED
@@ -167,7 +167,7 @@ export function Hero({ content, params }) {
167
167
  }
168
168
  ```
169
169
 
170
- The parser extracts semantic elements from markdown—`title` from H1, `paragraphs` from body text, `links` from `[text](url)`, and so on. The `items` array contains child groups created from H3 headings (useful for features, pricing tiers, team members, etc.). See [Content Structure](./docs/content-structure.md) for details.
170
+ The parser extracts semantic elements from markdown—`title` from the first heading, `paragraphs` from body text, `links` from `[text](url)`, and so on. The `items` array contains child groups created when headings appear after content (useful for features, pricing tiers, team members, etc.). See [Content Structure](./docs/content-structure.md) for details.
171
171
 
172
172
  Change the JSX, save, and the dev server hot-reloads your changes.
173
173
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.2.46",
3
+ "version": "0.2.48",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,9 +37,9 @@
37
37
  "js-yaml": "^4.1.0",
38
38
  "prompts": "^2.4.2",
39
39
  "tar": "^7.0.0",
40
- "@uniweb/build": "0.1.26",
41
- "@uniweb/core": "0.1.12",
40
+ "@uniweb/runtime": "0.2.15",
41
+ "@uniweb/build": "0.1.27",
42
42
  "@uniweb/kit": "0.1.7",
43
- "@uniweb/runtime": "0.2.13"
43
+ "@uniweb/core": "0.1.12"
44
44
  }
45
45
  }
@@ -92,11 +92,100 @@ Description paragraph.
92
92
 
93
93
  ### Content Structure
94
94
 
95
- The semantic parser extracts markdown into:
95
+ The semantic parser extracts markdown into a **flat structure**:
96
96
 
97
- - **`content.main.header`** - title, pretitle, subtitle
98
- - **`content.main.body`** - paragraphs, links, imgs, lists
99
- - **`content.items`** - Groups created by H3 headings (each with its own header/body)
97
+ ```js
98
+ {
99
+ // Headers (from headings)
100
+ title: '', // Main heading
101
+ pretitle: '', // Heading before main title (auto-detected)
102
+ subtitle: '', // Heading after main title
103
+ subtitle2: '', // Third heading level
104
+
105
+ // Body content
106
+ paragraphs: [], // Text blocks
107
+ links: [], // All links (including buttons, documents)
108
+ imgs: [], // Images (role: image, banner, gallery, background)
109
+ icons: [], // Icons (role: icon)
110
+ videos: [], // Videos (role: video)
111
+ lists: [], // Bullet/ordered lists
112
+ quotes: [], // Blockquotes
113
+ data: {}, // Structured data from tagged code blocks
114
+ headings: [], // Overflow headings after title/subtitle/subtitle2
115
+
116
+ // Child content groups
117
+ items: [], // Created by headings after content
118
+
119
+ // Document-order rendering
120
+ sequence: [], // All elements in original order
121
+ }
122
+ ```
123
+
124
+ **Heading interpretation is semantic, not literal:**
125
+ - `#` in markdown doesn't always become `<h1>` — the component decides
126
+ - A pretitle is auto-detected when a heading precedes a more important one (H3→H1, H2→H1)
127
+ - Items are created when any heading appears after body content
128
+
129
+ ### Attributes on Links and Media
130
+
131
+ Both links and media support attributes using curly braces:
132
+
133
+ ```markdown
134
+ [text](url){key=value .class #id booleanAttr}
135
+ ![alt](url){role=banner width=1200 loading=lazy}
136
+ ```
137
+
138
+ **Links and buttons:**
139
+ ```markdown
140
+ [Learn more](/about) <!-- Standard link -->
141
+ [Get Started](button:/signup) <!-- Button (prefix) -->
142
+ [Get Started](/signup){.button variant=primary} <!-- Button (class) -->
143
+ [Download](./report.pdf){download} <!-- Download link -->
144
+ ```
145
+
146
+ Links are unified in the `links` array with a `role` attribute:
147
+ - `role: "link"` — Standard hyperlink (default)
148
+ - `role: "button"` / `"button-primary"` — CTA buttons
149
+ - `role: "document"` — Downloadable files (auto-detected from extension)
150
+
151
+ **Media classification by role:**
152
+ ```markdown
153
+ ![Hero](./hero.jpg) <!-- imgs array (default) -->
154
+ ![Hero](./hero.jpg){role=banner} <!-- imgs array with role -->
155
+ ![Logo](./logo.svg){role=icon} <!-- icons array -->
156
+ ![Demo](./demo.mp4){role=video} <!-- videos array -->
157
+ ```
158
+
159
+ All use image syntax but `role` determines which array they go into.
160
+
161
+ ### Structured Data
162
+
163
+ Tagged code blocks pass structured data to components via `content.data`:
164
+
165
+ ````markdown
166
+ ```yaml:form
167
+ fields:
168
+ - name: email
169
+ type: email
170
+ required: true
171
+ submitLabel: Send
172
+ ```
173
+ ````
174
+
175
+ Access in component: `content.data?.form` → `{ fields: [...], submitLabel: "Send" }`
176
+
177
+ Supported formats: `json:tag-name`, `yaml:tag-name`
178
+
179
+ ### Asset Paths
180
+
181
+ Assets can use relative or absolute paths:
182
+
183
+ ```markdown
184
+ ![Photo](./photo.jpg) <!-- Relative to markdown file -->
185
+ ![Hero](/images/hero.jpg) <!-- From public/ or assets/ folder -->
186
+ ```
187
+
188
+ Build optimizes images (PNG/JPG → WebP), generates content-hashed filenames, and auto-creates video posters and PDF previews when not explicitly provided.
100
189
 
101
190
  ### Page Organization
102
191
 
@@ -161,10 +250,8 @@ The runtime provides **guarantees** for component props. No defensive null check
161
250
 
162
251
  ```jsx
163
252
  function MyComponent({ content, params, block }) {
164
- // Runtime guarantees these always exist:
165
- const { title, pretitle, subtitle } = content.main.header
166
- const { paragraphs, links, imgs } = content.main.body
167
- const items = content.items // Always an array (may be empty)
253
+ // Runtime guarantees these always exist (flat API):
254
+ const { title, pretitle, subtitle, paragraphs, links, imgs, items } = content
168
255
 
169
256
  // params already has defaults from meta.js merged in:
170
257
  const { theme, layout } = params
@@ -177,11 +264,21 @@ function MyComponent({ content, params, block }) {
177
264
  **Guaranteed content shape:**
178
265
  ```js
179
266
  content = {
180
- main: {
181
- header: { title: '', pretitle: '', subtitle: '' },
182
- body: { paragraphs: [], links: [], imgs: [], lists: [], icons: [] },
183
- },
184
- items: [],
267
+ title: '',
268
+ pretitle: '',
269
+ subtitle: '',
270
+ subtitle2: '',
271
+ paragraphs: [],
272
+ links: [],
273
+ imgs: [],
274
+ icons: [],
275
+ videos: [],
276
+ lists: [],
277
+ quotes: [],
278
+ data: {},
279
+ headings: [],
280
+ items: [], // Each item has the same flat structure
281
+ sequence: [], // For document-order rendering
185
282
  }
186
283
  ```
187
284
 
@@ -679,8 +776,8 @@ function Accordion({ content, params, block }) {
679
776
 
680
777
  return content.items.map((item, i) => (
681
778
  <div key={i}>
682
- <button onClick={() => toggle(i)}>{item.header.title}</button>
683
- {state.openItem === i && <p>{item.body.paragraphs[0]}</p>}
779
+ <button onClick={() => toggle(i)}>{item.title}</button>
780
+ {state.openItem === i && <p>{item.paragraphs[0]}</p>}
684
781
  </div>
685
782
  ))
686
783
  }