uniweb 0.79.2 → 0.80.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/package.json +8 -8
- package/partials/agents.md +26 -14
- package/src/commands/doctor.js +19 -1
- package/src/commands/starter.js +9 -5
- package/src/framework-index.json +9 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.0",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,17 +41,17 @@
|
|
|
41
41
|
"js-yaml": "^4.1.0",
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
|
-
"@uniweb/
|
|
45
|
-
"@uniweb/schemas": "^0.9.3",
|
|
46
|
-
"@uniweb/kit": "^0.19.11",
|
|
47
|
-
"@uniweb/runtime": "^0.28.3",
|
|
44
|
+
"@uniweb/runtime": "^0.28.4",
|
|
48
45
|
"@uniweb/content-writer": "^0.3.4",
|
|
49
|
-
"@uniweb/
|
|
46
|
+
"@uniweb/core": "^0.35.0",
|
|
47
|
+
"@uniweb/kit": "^0.19.13",
|
|
48
|
+
"@uniweb/schemas": "^0.10.0",
|
|
49
|
+
"@uniweb/semantic-parser": "^1.5.1"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@uniweb/build": "^0.
|
|
52
|
+
"@uniweb/build": "^0.74.0",
|
|
53
53
|
"@uniweb/content-reader": "^1.2.7",
|
|
54
|
-
"@uniweb/semantic-parser": "^1.
|
|
54
|
+
"@uniweb/semantic-parser": "^1.5.1"
|
|
55
55
|
},
|
|
56
56
|
"peerDependenciesMeta": {
|
|
57
57
|
"@uniweb/build": {
|
package/partials/agents.md
CHANGED
|
@@ -366,6 +366,7 @@ content = {
|
|
|
366
366
|
images: [], // { src, alt, role, href }
|
|
367
367
|
icons: [], // { library, name, role }
|
|
368
368
|
videos: [], // { src, alt, role, poster, href }
|
|
369
|
+
documents: [], // { url, alt, caption, role: 'pdf', preview?, author?, description? } — from {role=pdf}
|
|
369
370
|
insets: [], // Inline @Component references — { refId }
|
|
370
371
|
lists: [], // [[{ paragraphs, links, lists, … }]] — each item is an object, not a string
|
|
371
372
|
quotes: [], // Blockquotes
|
|
@@ -524,12 +525,13 @@ Optional attributes: `{size=20}`, `{color=red}`. Custom SVGs: `{target=_blank} <!-- Open in new tab -->
|
|
526
527
|
[text](./file.pdf){download} <!-- Download -->
|
|
527
|
-
{role=banner} <!-- Role determines array: images, icons, or
|
|
528
|
+
{role=banner} <!-- Role determines array: images, icons, videos or documents -->
|
|
528
529
|
{role=video} <!-- → content.videos[], not content.images[] -->
|
|
530
|
+
{role=pdf} <!-- → content.documents[] — a file, drawn as a link -->
|
|
529
531
|
{href=/products} <!-- Clickable media: href + target ride along -->
|
|
530
532
|
```
|
|
531
533
|
|
|
532
|
-
Sizing and loading ride on the image: `{width=800 height=600 loading=lazy fit=cover position=center}`. A video adds `{poster=./thumb.jpg autoplay muted loop controls}`; a `{role=pdf}` document adds `{preview=./cover.jpg author=… description=…}`.
|
|
534
|
+
Sizing and loading ride on the image: `{width=800 height=600 loading=lazy fit=cover position=center}`. A video adds `{poster=./thumb.jpg autoplay muted loop controls}`; a `{role=pdf}` document adds `{preview=./cover.jpg author=… description=…}` and arrives in `content.documents` — `{ url, name, mime, size?, alt, caption, … }` — declare it in `content:` as `documents`.
|
|
533
535
|
|
|
534
536
|
**Quote values containing spaces:** `{note="Ready to go"}`, not `{note=Ready to go}` — unquoted values end at the first space.
|
|
535
537
|
|
|
@@ -1087,7 +1089,7 @@ Names only — for signatures and props, read the package: it's on disk at `node
|
|
|
1087
1089
|
|
|
1088
1090
|
**Text:** `H1`–`H6`, `P`, `Span`, `Div`, `Text` (with `as`)
|
|
1089
1091
|
**Content:** `Section`, `Prose`, `Article`, `Render` (ProseMirror → React), `ChildBlocks`, `splitContent()`
|
|
1090
|
-
**Media:** `Visual` (first
|
|
1092
|
+
**Media:** `Visual` (a `content.media` item by its kind; or the first of inset/video/image), `Image`, `Media` (video), `Icon`, `Asset`
|
|
1091
1093
|
**Navigation:** `Link`, `useActiveRoute()`, `useWebsite()`, `useRouting()`
|
|
1092
1094
|
**Header/layout:** `useScrolled(threshold)`, `useMobileMenu()`, `useAppearance()`
|
|
1093
1095
|
**Overlays:** `Overlay` (modals, palettes, drawers, toasts — portals out of the layout, contains focus; read the note below before hand-rolling one)
|
|
@@ -1098,7 +1100,7 @@ Names only — for signatures and props, read the package: it's on disk at `node
|
|
|
1098
1100
|
@import "@uniweb/kit/prose-tokens.css"; /* the plugin, wired to theme.yml */
|
|
1099
1101
|
```
|
|
1100
1102
|
|
|
1101
|
-
Nothing to install — the import brings the plugin with it. **Skip the import and prose is completely unstyled
|
|
1103
|
+
Nothing to install — the import brings the plugin with it. **Skip the import and prose is completely unstyled** — lists lose their bullets and numbers, and text its typography — while the markup looks right. The foundation build warns about it, and `uniweb doctor` flags it. Use **one** `prose` container per subtree — the `--tw-prose-*` variables are inherited, so a nested one silently resets them and the outer container goes on looking correct. The section that renders the document is usually the better owner; a layout should supply column width and padding.
|
|
1102
1104
|
|
|
1103
1105
|
**Documentation shells:** `useHeadings()` (the page's headings + the one being read, derived from content so it prerenders), `website.getBranchHierarchy({ route, for })` (the page tree for one branch). Kit ships no ready-made layout — a layout is your foundation's design; write it in `src/layouts/` and use these for the behaviour.
|
|
1104
1106
|
**Layout helpers:** `useGridLayout(columns, { gap })`, `useAccordion({ multiple, defaultOpen })`
|
|
@@ -1160,21 +1162,23 @@ Header.as = 'header'
|
|
|
1160
1162
|
export default {
|
|
1161
1163
|
title: 'Feature Grid',
|
|
1162
1164
|
description: 'Grid of feature cards with icons',
|
|
1163
|
-
|
|
1164
|
-
//
|
|
1165
|
-
//
|
|
1166
|
-
// site: a genre like 'marketing' belongs to a template
|
|
1167
|
-
purpose: 'Explain', // free-form; a single verb reads well
|
|
1165
|
+
family: 'features', // the standard section type it is — the editor's
|
|
1166
|
+
// illustration and label (`uniweb families`). Optional;
|
|
1167
|
+
// a name like `Features` resolves on its own
|
|
1168
1168
|
// hidden: true, // Exclude from export entirely (internal helpers)
|
|
1169
1169
|
// background: 'self', // Component renders its own background
|
|
1170
1170
|
// inset: true, // Available for @ComponentName in markdown
|
|
1171
|
-
// visuals: 1, // Expects 1 visual
|
|
1172
1171
|
// children: true, // Accepts child sections
|
|
1173
1172
|
|
|
1174
1173
|
content: {
|
|
1175
1174
|
title: 'Section heading',
|
|
1176
1175
|
paragraphs: 'Introduction [0-1]',
|
|
1177
|
-
items:
|
|
1176
|
+
items: {
|
|
1177
|
+
label: 'Feature cards [3-6]',
|
|
1178
|
+
content: { icon: 'Icon [1]', title: 'Feature', paragraphs: 'Description' }, // one entry
|
|
1179
|
+
},
|
|
1180
|
+
// media: 'Photo, video or diagram [1]', // an image, a video, or an embedded component
|
|
1181
|
+
// sequence: 'Prose and media', // renders content.sequence — the section as written
|
|
1178
1182
|
},
|
|
1179
1183
|
|
|
1180
1184
|
params: {
|
|
@@ -1195,6 +1199,13 @@ export default {
|
|
|
1195
1199
|
|
|
1196
1200
|
**All defaults belong in `meta.js`, not inline in component code.** `meta.js` is also the catalog entry another agent reads to discover your section type (Part 2, step 2) — write `description` and `content:` for that reader.
|
|
1197
1201
|
|
|
1202
|
+
**`content:` names what the component reads**, each as a label for the author with an optional count (`'Cards [3-6]'`): `title`, `pretitle`, `subtitle`, `paragraphs`, `links`, `lists`, `items`, `media`, `icons`, `documents`, `snippets`, `tables`, `math`, `quotes`, `sequence`.
|
|
1203
|
+
|
|
1204
|
+
- **`media` is an image, a video, or an embedded component.** `media: 'Hero media [1]'` takes any of the three; `media: { label, types: ['image'] }` narrows it; `image:`, `videos:` and `insets:` are the same slot narrowed to one type. Read it as `content.media` — the author's order, each item with its `kind` — and render it with `<Visual media={content.media} block={block} />`. Rendering the slot with `<Image>` drops a diagram an author placed there, and kit's `<Media>` is a video player.
|
|
1205
|
+
- **`sequence`** says the component renders `content.sequence` — the whole section as written, headline included. `{ label, except: ['table', 'math'] }` names the kinds it leaves out.
|
|
1206
|
+
- **`items: { label, content: { … } }`** says what each entry holds, in the same vocabulary.
|
|
1207
|
+
- The build warns on what it can't read, and refuses `visuals:` (declare `media`) and the `content:` element `data` (declare the block's tag in `data:`).
|
|
1208
|
+
|
|
1198
1209
|
> **`meta.js` is a user interface, not just metadata.** When a foundation is published, every `meta.js` is registered as the foundation's schema, and the visual editor builds its controls from it: the build generates a `schema.json` from every `meta.js` in the foundation, and the editor renders it: a `select` param with `options` becomes a dropdown, a `boolean` becomes a toggle, `label` and `description` become the words a non-technical author reads, and each entry in `presets` becomes a one-click choice. That's the real reason param naming matters — `variant: centered` and `renderMode: flex-center` aren't a style preference, they're the difference between a legible control and a baffling one. Write `meta.js` as though someone who will never see your code has to use it, because that's exactly who does.
|
|
1199
1210
|
|
|
1200
1211
|
### Two worlds: section types and your own components
|
|
@@ -1617,7 +1628,7 @@ Back up your database **before** running this. It is not reversible.
|
|
|
1617
1628
|
|
|
1618
1629
|
That makes one distinction worth holding onto. You **may** declare a schema describing the form *definition's envelope* — `title`, `description`, `fields` as a map — and get build-time validation that an authored form is well-formed. What you can't declare is a schema whose fields are *the form's* fields (`name`, `email`, …); that's describing the visitor's answers, which arrive at runtime and belong to a form you've never seen. Declare the key either way — it is what delivers the block — and give it a schema only if you want it checked, and only of the envelope (`{}` declares the key with none).
|
|
1619
1630
|
|
|
1620
|
-
**Reading one in a component.** Declare the
|
|
1631
|
+
**Reading one in a component.** Declare it the way the author writes the fence — `data: { 'md:faq': 'Questions and answers [3+]' }`. The value is the author's label (its count counts the entries), or `{ label, hint, content: { title: 'Question', paragraphs: 'Answer' } }` to say what each entry holds. The component still reads `content.data.faq`, which gives you both views: `items` for anything row-shaped (an accordion, a step list), `sequence` when you don't recognize the tag and want to render it faithfully in document order. Both are derived, so nothing is stored twice. (`faq: {}` also declares the key, saying nothing about the block.)
|
|
1621
1632
|
|
|
1622
1633
|
```jsx
|
|
1623
1634
|
function Faq({ content }) {
|
|
@@ -1635,10 +1646,11 @@ function Faq({ content }) {
|
|
|
1635
1646
|
{variant=compact}
|
|
1636
1647
|
```
|
|
1637
1648
|
|
|
1638
|
-
The developer builds `NetworkDiagram` as an ordinary React component with `inset: true` in `meta.js`.
|
|
1649
|
+
The developer builds `NetworkDiagram` as an ordinary React component with `inset: true` in `meta.js`. The section that hosts it declares a `media` slot, and kit's `<Visual>` renders the author's item by its kind — so one section type works whether the author supplies an image, a video, or an interactive component:
|
|
1639
1650
|
|
|
1640
1651
|
```jsx
|
|
1641
|
-
|
|
1652
|
+
// meta.js: content: { title: 'Headline', media: 'Illustration [1]' }
|
|
1653
|
+
<Visual media={content.media} block={block} className="rounded-2xl" />
|
|
1642
1654
|
```
|
|
1643
1655
|
|
|
1644
1656
|
**Insets are full section types** — they receive `{ content, params, block }`. The alt text becomes `content.title` and attributes become `params`: `{note="Ready to go"}` → `content.title = "npm create uniweb"`, `params.note = "Ready to go"`.
|
package/src/commands/doctor.js
CHANGED
|
@@ -189,6 +189,24 @@ function loadSiteYml(dir) {
|
|
|
189
189
|
* `node_modules` — this answers "did the developer write this", so a match
|
|
190
190
|
* inside a dependency is the wrong answer.
|
|
191
191
|
*/
|
|
192
|
+
/**
|
|
193
|
+
* Does this foundation render kit's <Prose> or <Article> — under its own name or an alias?
|
|
194
|
+
*
|
|
195
|
+
* The JSX tag alone missed `import { Article as ArticleBody } from '@uniweb/kit'`, which
|
|
196
|
+
* renders `<ArticleBody>`: two official templates shipped that way with no prose styles
|
|
197
|
+
* until 2026-09-29. So the import counts too. (The foundation build checks the same thing
|
|
198
|
+
* from its module graph, which no alias can hide from.)
|
|
199
|
+
*
|
|
200
|
+
* @param {string} dir - the foundation's source directory
|
|
201
|
+
* @returns {boolean}
|
|
202
|
+
*/
|
|
203
|
+
export function rendersKitProse(dir) {
|
|
204
|
+
return (
|
|
205
|
+
sourceMatches(dir, /<(Prose|Article)\b/) ||
|
|
206
|
+
sourceMatches(dir, /import\s*\{[^}]*\b(Prose|Article)\b[^}]*\}\s*from\s*['"]@uniweb\/kit['"]/)
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
192
210
|
function sourceMatches(dir, pattern) {
|
|
193
211
|
let found = false
|
|
194
212
|
forEachSourceFile(dir, (text) => {
|
|
@@ -1217,8 +1235,8 @@ export async function doctor(args = []) {
|
|
|
1217
1235
|
|
|
1218
1236
|
// Does anything here actually ask for prose? Either the class in the
|
|
1219
1237
|
// stylesheet, or kit's components, which render it.
|
|
1238
|
+
const usesProseComponent = rendersKitProse(f.path)
|
|
1220
1239
|
const usesProseClass = /(^|[\s"'`])prose(\s|["'`]|$)/m.test(styles)
|
|
1221
|
-
const usesProseComponent = sourceMatches(f.path, /<(Prose|Article)\b/)
|
|
1222
1240
|
|
|
1223
1241
|
if (!usesProseClass && !usesProseComponent) continue
|
|
1224
1242
|
|
package/src/commands/starter.js
CHANGED
|
@@ -90,6 +90,8 @@ export async function generateStarter({ name, sectionDir, preset, json, write })
|
|
|
90
90
|
const metaPath = join(sectionDir, 'meta.js')
|
|
91
91
|
const meta = existsSync(metaPath) ? await loadMeta(metaPath) : {}
|
|
92
92
|
const result = starterContent({ name, ...meta }, { preset })
|
|
93
|
+
// What the declaration says that could not be read — the build warns the same way.
|
|
94
|
+
result.problems = describeContent({ name, ...meta }).problems
|
|
93
95
|
|
|
94
96
|
const doc = buildDoc(result.content)
|
|
95
97
|
const markdown = doc ? serializeSection(result.params, doc) : ''
|
|
@@ -157,11 +159,14 @@ export function reportStarter(result, { write } = {}) {
|
|
|
157
159
|
console.log('')
|
|
158
160
|
console.log(` ${c.yellow}!${c.reset} Not filled: ${result.unfilled.join(', ')}`)
|
|
159
161
|
console.log(
|
|
160
|
-
` ${c.dim}
|
|
161
|
-
)
|
|
162
|
-
console.log(
|
|
163
|
-
` ${c.dim}A \`data\` block needs a schema: a @/ ref resolves at build, and an empty {} declares no shape.${c.reset}`,
|
|
162
|
+
` ${c.dim}A starter makes up no file, video or embedded component, and writes no table, equation,${c.reset}`,
|
|
164
163
|
)
|
|
164
|
+
console.log(` ${c.dim}quote or concept block (md:<tag>) — add those in the section.${c.reset}`)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (result.problems?.length) {
|
|
168
|
+
console.log('')
|
|
169
|
+
for (const problem of result.problems) console.log(` ${c.yellow}!${c.reset} ${problem}`)
|
|
165
170
|
}
|
|
166
171
|
|
|
167
172
|
if (write) {
|
|
@@ -189,7 +194,6 @@ export async function declarationFor(result) {
|
|
|
189
194
|
icons: 'Icon [1]',
|
|
190
195
|
videos: 'Video [1]',
|
|
191
196
|
snippets: 'Code sample [1]',
|
|
192
|
-
data: 'The data block the author writes',
|
|
193
197
|
}
|
|
194
198
|
// ⛔ The slot → declaration spelling comes from @uniweb/schemas, not from a
|
|
195
199
|
// copy here. `image` declares and `images` delivers; that mapping is the
|
package/src/framework-index.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-09-
|
|
3
|
+
"generatedAt": "2026-09-29T20:02:35.777Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/api": {
|
|
6
6
|
"version": "0.6.8",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
]
|
|
11
11
|
},
|
|
12
12
|
"@uniweb/build": {
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.74.0",
|
|
14
14
|
"path": "framework/build",
|
|
15
15
|
"deps": [
|
|
16
16
|
"@uniweb/content-reader",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
]
|
|
56
56
|
},
|
|
57
57
|
"@uniweb/kit": {
|
|
58
|
-
"version": "0.19.
|
|
58
|
+
"version": "0.19.13",
|
|
59
59
|
"path": "framework/kit",
|
|
60
60
|
"deps": [
|
|
61
61
|
"@uniweb/core",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"deps": []
|
|
72
72
|
},
|
|
73
73
|
"@uniweb/press": {
|
|
74
|
-
"version": "0.4.
|
|
74
|
+
"version": "0.4.18",
|
|
75
75
|
"path": "framework/press",
|
|
76
76
|
"deps": []
|
|
77
77
|
},
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
]
|
|
85
85
|
},
|
|
86
86
|
"@uniweb/runtime": {
|
|
87
|
-
"version": "0.28.
|
|
87
|
+
"version": "0.28.4",
|
|
88
88
|
"path": "framework/runtime",
|
|
89
89
|
"deps": [
|
|
90
90
|
"@uniweb/core",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"deps": []
|
|
98
98
|
},
|
|
99
99
|
"@uniweb/schemas": {
|
|
100
|
-
"version": "0.
|
|
100
|
+
"version": "0.10.0",
|
|
101
101
|
"path": "framework/schemas",
|
|
102
102
|
"deps": []
|
|
103
103
|
},
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"deps": []
|
|
108
108
|
},
|
|
109
109
|
"@uniweb/semantic-parser": {
|
|
110
|
-
"version": "1.
|
|
110
|
+
"version": "1.5.1",
|
|
111
111
|
"path": "framework/semantic-parser",
|
|
112
112
|
"deps": []
|
|
113
113
|
},
|
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
"deps": []
|
|
118
118
|
},
|
|
119
119
|
"@uniweb/templates": {
|
|
120
|
-
"version": "0.17.
|
|
120
|
+
"version": "0.17.9",
|
|
121
121
|
"path": "framework/templates",
|
|
122
122
|
"deps": []
|
|
123
123
|
},
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"deps": []
|
|
128
128
|
},
|
|
129
129
|
"@uniweb/unipress": {
|
|
130
|
-
"version": "0.10.
|
|
130
|
+
"version": "0.10.17",
|
|
131
131
|
"path": "framework/unipress",
|
|
132
132
|
"deps": [
|
|
133
133
|
"@uniweb/build",
|