uniweb 0.8.10 → 0.8.11

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
@@ -115,7 +115,7 @@ Frontmatter specifies the component type and configuration. The body contains th
115
115
 
116
116
  ### Beyond Markdown
117
117
 
118
- For content that doesn't fit markdown patterns—products, team members, events—use tagged code blocks:
118
+ For content that doesn't fit markdown patterns—products, team members, events—use tagged data blocks:
119
119
 
120
120
  ````markdown
121
121
  ```yaml:team-member
@@ -202,7 +202,7 @@ Open `foundation/src/sections/Hero.jsx`. The component receives parsed content:
202
202
 
203
203
  ```jsx
204
204
  export default function Hero({ content }) {
205
- const { title, paragraphs, links, imgs, items } = content
205
+ const { title, paragraphs, links, images, items } = content
206
206
  // Edit the JSX below...
207
207
  }
208
208
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.8.10",
3
+ "version": "0.8.11",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,11 +41,11 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/build": "0.8.9",
44
+ "@uniweb/build": "0.8.10",
45
45
  "@uniweb/content-reader": "1.1.4",
46
- "@uniweb/kit": "0.7.6",
47
- "@uniweb/core": "0.5.6",
48
- "@uniweb/runtime": "0.6.6",
49
- "@uniweb/semantic-parser": "1.1.4"
46
+ "@uniweb/core": "0.5.7",
47
+ "@uniweb/runtime": "0.6.7",
48
+ "@uniweb/kit": "0.7.7",
49
+ "@uniweb/semantic-parser": "1.1.5"
50
50
  }
51
51
  }
@@ -175,13 +175,14 @@ content = {
175
175
  subtitle2: '', // Third-level heading
176
176
  paragraphs: [], // Text blocks
177
177
  links: [], // { href, label, role } — standalone links become buttons
178
- imgs: [], // { src, alt, role }
178
+ images: [], // { src, alt, role, href }
179
179
  icons: [], // { library, name, role }
180
- videos: [], // Video embeds
180
+ videos: [], // { src, alt, role, poster, href }
181
181
  insets: [], // Inline @Component references — { refId }
182
182
  lists: [], // [[{ paragraphs, links, lists, ... }]] — each list item is an object, not a string
183
183
  quotes: [], // Blockquotes
184
- data: {}, // From tagged code blocks (```yaml:tagname) and (```js:tagname)
184
+ snippets: [], // Fenced code [{ language, code }]
185
+ data: {}, // From tagged data blocks (```yaml:tagname, ```json:tagname)
185
186
  headings: [], // Overflow headings after subtitle2
186
187
  items: [], // Each has the same flat structure — from headings after body content
187
188
  sequence: [], // All elements in document order
@@ -204,7 +205,7 @@ Lightning quick. ← items[0].paragraphs[0]
204
205
  Enterprise-grade. ← items[1].paragraphs[0]
205
206
  ```
206
207
 
207
- **Items have the full content shape** — this is the most commonly overlooked feature. Each item has `title`, `pretitle`, `subtitle`, `paragraphs`, `links`, `icons`, `lists`, and even `data` (tagged blocks). You don't need workarounds for structured content within items:
208
+ **Items have the full content shape** — this is the most commonly overlooked feature. Each item has `title`, `pretitle`, `subtitle`, `paragraphs`, `links`, `icons`, `lists`, `snippets`, and even `data` (tagged data blocks). You don't need workarounds for structured content within items:
208
209
 
209
210
  ```markdown
210
211
  ### The Problem ← items[0].pretitle
@@ -257,7 +258,7 @@ You have three layers. Most of the design skill is choosing between them:
257
258
 
258
259
  **Frontmatter params** — `columns: 3`, `variant: centered`, `theme: dark`. Configuration that an author might change but that isn't *content*. Would changing this value change the section's *meaning*, or just its *presentation*? Presentation → param. Meaning → content.
259
260
 
260
- **Tagged data blocks** — for content that doesn't fit markdown patterns. Products with SKUs, team members with roles, event schedules, pricing metadata, form definitions. When the information is genuinely structured data that a content author still owns, a well-named tagged block (`yaml:pricing`, `yaml:speakers`, `yaml:config`) is clearer than contorting markdown into a data format.
261
+ **Tagged data blocks** — for content that doesn't fit markdown patterns. Products with SKUs, team members with roles, event schedules, pricing metadata, form definitions. When the information is genuinely structured data that a content author still owns, a well-named tagged block (`yaml:pricing`, `yaml:speakers`, `yaml:config`) is clearer than contorting markdown into a data format. Supported formats: `yaml` and `json`. The format is a serialization format (how to parse the data), not a language for display. Tagged blocks are parsed at build time into JS objects and delivered as `content.data.tagName`.
261
262
 
262
263
  Read the markdown out loud. If a content author would understand what every line does and how to edit it, you've chosen the right layer. The moment markdown feels like it's encoding data rather than expressing content, step up to a tagged block — that's fine. A well-documented `yaml:pricing` block is better than a markdown structure that puzzles the author.
263
264
 
@@ -320,7 +321,7 @@ Custom SVGs: `![Logo](./logo.svg){role=icon}`
320
321
  ```markdown
321
322
  [text](url){target=_blank} <!-- Open in new tab -->
322
323
  [text](./file.pdf){download} <!-- Download -->
323
- ![alt](./img.jpg){role=banner} <!-- Role determines array: imgs, icons, or videos -->
324
+ ![alt](./img.jpg){role=banner} <!-- Role determines array: images, icons, or videos -->
324
325
  ```
325
326
 
326
327
  **Quote values that contain spaces:** `{note="Ready to go"}` not `{note=Ready to go}`. Unquoted values end at the first space.
@@ -344,9 +345,11 @@ This is [less important]{muted} context.
344
345
 
345
346
  Sites can define additional named styles in `theme.yml`'s `inline:` section.
346
347
 
347
- ### Structured Data
348
+ ### Fenced Code in Content
348
349
 
349
- Tagged code blocks pass structured data via `content.data`:
350
+ Fenced code in markdown serves two distinct purposes depending on whether it has a tag:
351
+
352
+ **Tagged data blocks** — structured data parsed into JS objects. The format (`yaml`/`json`) is a serialization format, not a display language. The tag is the key in `content.data`:
350
353
 
351
354
  ````markdown
352
355
  ```yaml:form
@@ -357,11 +360,21 @@ submitLabel: Send
357
360
  ```
358
361
  ````
359
362
 
360
- Access: `content.data?.form` → `{ fields: [...], submitLabel: "Send" }`
363
+ Access: `content.data?.form` → `{ fields: [...], submitLabel: "Send" }`. Supported formats: `yaml` (or `yml`) and `json`.
364
+
365
+ **Code snippets** — display content with a language for syntax highlighting. Available in `content.snippets` as `[{ language, code }]`:
366
+
367
+ ````markdown
368
+ ```jsx
369
+ function Hello() {
370
+ return <h1>Hello world</h1>
371
+ }
372
+ ```
373
+ ````
361
374
 
362
- > **FIXME:** The claim below is wrong. I think tagged blocks an only be YAML/JSON. We could add regular code blocks to the parsed content as an array of code blocks or "pre" elements.
375
+ Access: `content.snippets[0]` `{ language: 'jsx', code: 'function Hello() {...}' }`. The `language` attribute is a display hint for syntax highlighting, not a parsing format. Filter by language: `content.snippets.filter(s => s.language === 'css')`.
363
376
 
364
- **Untagged code blocks** are only visible to sequential-rendering components. If a component needs to access code blocks by name, tag them (`jsx:before`, `jsx:after` → `content.data?.before`, `content.data?.after`).
377
+ Both appear in `content.sequence` for document-order rendering. The difference: tagged data blocks are parsed and extracted to `content.data`; code snippets are preserved and collected in `content.snippets`.
365
378
 
366
379
  ### Composition: Nesting and Embedding
367
380
 
@@ -378,7 +391,7 @@ In other frameworks, this is where you'd reach for MDX, or prop-drill a componen
378
391
  Standard markdown image syntax — `![alt](@Component){attributes}`. The content author placed a full React component with content and params, and it looks like an image reference. The developer builds `NetworkDiagram` as an ordinary React component with `inset: true` in its `meta.js`. The kit's `<Visual>` component renders the first non-empty candidate — so the same section type works whether the author provides a static image, a video, or an interactive component:
379
392
 
380
393
  ```jsx
381
- <Visual inset={block.insets[0]} video={content.videos[0]} image={content.imgs[0]} className="rounded-2xl" />
394
+ <Visual inset={block.insets[0]} video={content.videos[0]} image={content.images[0]} className="rounded-2xl" />
382
395
  ```
383
396
 
384
397
  The content author controls what goes in the visual slot. The developer's component doesn't need to know or care whether it's rendering an image or a ThreeJS scene.
@@ -799,7 +812,7 @@ import { Section, Render } from '@uniweb/kit'
799
812
  ```jsx
800
813
  import { Visual } from '@uniweb/kit'
801
814
 
802
- <Visual inset={block.insets[0]} video={content.videos[0]} image={content.imgs[0]} className="rounded-2xl" />
815
+ <Visual inset={block.insets[0]} video={content.videos[0]} image={content.images[0]} className="rounded-2xl" />
803
816
  ```
804
817
 
805
818
  ### Kit API by Use Case
@@ -977,7 +990,7 @@ export default function Hero({ content, block, params }) {
977
990
  links={content.links}
978
991
  block={block}
979
992
  // Content that only some variants use
980
- images={content.imgs}
993
+ images={content.images}
981
994
  formData={content.data?.quote}
982
995
  // Translated params — author vocabulary → developer props
983
996
  interval={params.slideInterval}
@@ -149,7 +149,7 @@ function guaranteeItemStructure(item) {
149
149
  subtitle: item.subtitle || '',
150
150
  paragraphs: item.paragraphs || [],
151
151
  links: item.links || [],
152
- imgs: item.imgs || [],
152
+ images: item.images || [],
153
153
  lists: item.lists || [],
154
154
  icons: item.icons || [],
155
155
  videos: item.videos || [],
@@ -178,7 +178,7 @@ function guaranteeContentStructure(parsedContent) {
178
178
  alignment: content.alignment || null,
179
179
  paragraphs: content.paragraphs || [],
180
180
  links: content.links || [],
181
- imgs: content.imgs || [],
181
+ images: content.images || [],
182
182
  lists: content.lists || [],
183
183
  icons: content.icons || [],
184
184
  videos: content.videos || [],
@@ -7,7 +7,7 @@ import { H1, H2, P, Link, cn } from '@uniweb/kit'
7
7
  * Uses semantic tokens so it adapts to any theme context automatically.
8
8
  */
9
9
  export default function Section({ content, params }) {
10
- const { title, pretitle, subtitle, paragraphs = [], links = [], imgs = [] } = content || {}
10
+ const { title, pretitle, subtitle, paragraphs = [], links = [], images = [] } = content || {}
11
11
 
12
12
  const {
13
13
  align = 'center',
@@ -77,9 +77,9 @@ export default function Section({ content, params }) {
77
77
  </div>
78
78
  )}
79
79
 
80
- {imgs.length > 0 && (
80
+ {images.length > 0 && (
81
81
  <div className="mt-8">
82
- {imgs.map((img, index) => (
82
+ {images.map((img, index) => (
83
83
  <img
84
84
  key={index}
85
85
  src={img.url || img.src}
@@ -10,7 +10,7 @@ export default {
10
10
  subtitle: 'Secondary heading',
11
11
  paragraphs: 'Body text',
12
12
  links: 'Call-to-action buttons',
13
- imgs: 'Section images',
13
+ images: 'Section images',
14
14
  },
15
15
 
16
16
  params: {