uniweb 0.14.1 → 0.14.3
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 +3 -3
- package/partials/agents.md +53 -6
- package/src/commands/doctor.js +81 -0
- package/src/framework-index.json +6 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.3",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.0.0",
|
|
44
44
|
"@uniweb/core": "0.8.0",
|
|
45
|
-
"@uniweb/kit": "0.10.
|
|
45
|
+
"@uniweb/kit": "0.10.3",
|
|
46
46
|
"@uniweb/runtime": "0.9.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"@uniweb/build": "0.16.
|
|
49
|
+
"@uniweb/build": "0.16.1",
|
|
50
50
|
"@uniweb/content-reader": "1.2.0",
|
|
51
51
|
"@uniweb/semantic-parser": "1.2.0"
|
|
52
52
|
},
|
package/partials/agents.md
CHANGED
|
@@ -338,7 +338,9 @@ content = {
|
|
|
338
338
|
lists: [], // [[{ paragraphs, links, lists, … }]] — each item is an object, not a string
|
|
339
339
|
quotes: [], // Blockquotes
|
|
340
340
|
snippets: [], // Fenced code — [{ language, code }]
|
|
341
|
-
data: {}, //
|
|
341
|
+
data: {}, // Tagged blocks — ```yaml:tag / ```json:tag give the parsed value,
|
|
342
|
+
// ```md:tag gives { items, sequence } (see Concept blocks)
|
|
343
|
+
tables: [], // Only when present — [{ rows: [{ cells: [{ children, header, align }] }] }]
|
|
342
344
|
headings: [], // Headings after subtitle, in document order
|
|
343
345
|
items: [], // Each has the same flat structure — from headings after body content
|
|
344
346
|
sequence: [], // All elements in document order
|
|
@@ -503,7 +505,7 @@ Sites can adjust these or add named styles in `theme.yml`'s `inline:` section. O
|
|
|
503
505
|
|
|
504
506
|
### Fenced code: data blocks vs snippets
|
|
505
507
|
|
|
506
|
-
Fenced code serves
|
|
508
|
+
Fenced code serves three purposes depending on its info string: `yaml:`/`json:` for data, `md:` for a named kind of prose (see *Concept blocks*), and a bare language for a code sample.
|
|
507
509
|
|
|
508
510
|
**Tagged data blocks** — structured data parsed into JS objects. The tag is the key in `content.data`; the format (`yaml`/`yml`/`json`) is a serialization format, not a display language.
|
|
509
511
|
|
|
@@ -869,14 +871,13 @@ Names only — for signatures and props, read the package: it's on disk at `node
|
|
|
869
871
|
**Header/layout:** `useScrolled(threshold)`, `useMobileMenu()`, `useAppearance()`
|
|
870
872
|
**Overlays:** `Overlay` (modals, palettes, drawers, toasts — portals out of the layout, contains focus; read the note below before hand-rolling one)
|
|
871
873
|
**Keyboard:** `useShortcut('mod+k', fn)`, `useShortcuts({…})`, `useShortcutLabel('mod+k')` — `mod` is Cmd on Apple platforms and Ctrl elsewhere, and the label renders per-platform so a hint never shows the wrong key
|
|
872
|
-
**Long-form prose:** `<Prose>` and the container you put `<Render>` output in both use Tailwind Typography's `prose` classes
|
|
874
|
+
**Long-form prose:** `<Prose>` and the container you put `<Render>` output in both use Tailwind Typography's `prose` classes. One line in the foundation's `styles.css` supplies them and points them at the site's theme:
|
|
873
875
|
|
|
874
876
|
```css
|
|
875
|
-
@
|
|
876
|
-
@import "@uniweb/kit/prose-tokens.css"; /* makes prose answer to theme.yml */
|
|
877
|
+
@import "@uniweb/kit/prose-tokens.css"; /* the plugin, wired to theme.yml */
|
|
877
878
|
```
|
|
878
879
|
|
|
879
|
-
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.
|
|
880
|
+
Nothing to install — the import brings the plugin with it. **Skip the import and prose is completely unstyled**, silently: the class is there and no rules are behind it. `uniweb doctor` flags that. 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.
|
|
880
881
|
|
|
881
882
|
**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.
|
|
882
883
|
**Layout helpers:** `useGridLayout(columns, { gap })`, `useAccordion({ multiple, defaultOpen })`
|
|
@@ -1335,12 +1336,58 @@ Pages are sequences of sections — the obvious layer. The framework also suppor
|
|
|
1335
1336
|
| Pattern | How authored | Use when |
|
|
1336
1337
|
|---|---|---|
|
|
1337
1338
|
| **Items** (`content.items`) | Heading groups within one `.md` | Repeating content in one section: cards, features, FAQ entries |
|
|
1339
|
+
| **Concept blocks** (`content.data[tag]`) | ` ```md:<tag> ` fence around markdown | A named *kind* of content — an FAQ, a callout, a set of steps |
|
|
1338
1340
|
| **Child sections** (`block.childBlocks`) | `@`-prefixed `.md` files + `nest:` | Children needing their own section type, rich content, or independent editing |
|
|
1339
1341
|
| **Insets** (`block.insets`) | `` in markdown | Self-contained visuals/widgets: charts, diagrams, code demos |
|
|
1340
1342
|
| **Block insets** (`block.insets`) | ` ```@Component ` fence around markdown | A component that *wraps* authored prose: callouts, disclosures, admonitions |
|
|
1341
1343
|
|
|
1342
1344
|
Does the author write content *inside* the nested element? **Yes** → child sections, or a block inset when the wrapper is presentational and lives mid-page. **No** (self-contained, param-driven) → inset. Repeating same-structure groups → items. These compose: a child section can contain insets; items work inside children; a block inset can contain both.
|
|
1343
1345
|
|
|
1346
|
+
### Concept blocks — naming *what* content is
|
|
1347
|
+
|
|
1348
|
+
A ` ```md:<tag> ` fence marks a run of prose as a named kind of thing. The author writes ordinary markdown; the tag says what it is:
|
|
1349
|
+
|
|
1350
|
+
````markdown
|
|
1351
|
+
```md:faq
|
|
1352
|
+
# What plans do you have?
|
|
1353
|
+
Three — Starter, Team and Enterprise.
|
|
1354
|
+
|
|
1355
|
+
# Can I change later?
|
|
1356
|
+
Any time, and we prorate the difference.
|
|
1357
|
+
```
|
|
1358
|
+
````
|
|
1359
|
+
|
|
1360
|
+
Every `#` heading starts an item, so this arrives as `content.data.faq`:
|
|
1361
|
+
|
|
1362
|
+
```js
|
|
1363
|
+
{
|
|
1364
|
+
items: [ { title, paragraphs, links, … }, … ], // one per heading
|
|
1365
|
+
sequence: [ … ] // the same content, in document order
|
|
1366
|
+
}
|
|
1367
|
+
```
|
|
1368
|
+
|
|
1369
|
+
**The shape is fixed by the fence, not by the tag.** Any tag works and none is special — the framework never learns what `faq` means. A body with **no** headings is the same block with a single titleless item, which is what a callout is:
|
|
1370
|
+
|
|
1371
|
+
````markdown
|
|
1372
|
+
```md:warning
|
|
1373
|
+
Back up your database **before** running this. It is not reversible.
|
|
1374
|
+
```
|
|
1375
|
+
````
|
|
1376
|
+
|
|
1377
|
+
**Why this instead of a component reference.** ` ```@Alert ` names *which component renders this*, which is a rendering decision sitting in content. `md:warning` names *what the content is* and leaves rendering to the foundation — so the same content works under a different foundation, and an editor can recognize the concept and offer a surface built for it.
|
|
1378
|
+
|
|
1379
|
+
**Reading one in a component.** `content.data[tag]` 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.
|
|
1380
|
+
|
|
1381
|
+
```jsx
|
|
1382
|
+
function Faq({ content }) {
|
|
1383
|
+
const faq = content.data?.faq
|
|
1384
|
+
if (!faq) return null
|
|
1385
|
+
return faq.items.map((item, i) => <Disclosure key={i} q={item.title} a={item.paragraphs} />)
|
|
1386
|
+
}
|
|
1387
|
+
```
|
|
1388
|
+
|
|
1389
|
+
**Two tags, not a tag plus a variant.** A warning and a note are different concepts, so they are `md:warning` and `md:note`. The fence takes no params.
|
|
1390
|
+
|
|
1344
1391
|
**Insets — embedding components in content.** Many section types need a "visual" — a hero's illustration, a split-content section's media. Classically an image or video. But what if it's a JSX + SVG diagram, a ThreeJS animation, an interactive playground? Elsewhere you'd reach for MDX or prop-drilling. Here the author writes standard image syntax:
|
|
1345
1392
|
|
|
1346
1393
|
```markdown
|
package/src/commands/doctor.js
CHANGED
|
@@ -169,6 +169,41 @@ function loadSiteYml(dir) {
|
|
|
169
169
|
* committed, and then looks like something you may edit. That is how
|
|
170
170
|
* hand-authored files ended up there in the first place.
|
|
171
171
|
*/
|
|
172
|
+
/**
|
|
173
|
+
* Whether any component source under `dir` matches `pattern`.
|
|
174
|
+
*
|
|
175
|
+
* Only the directories a foundation puts components in, and never
|
|
176
|
+
* `node_modules` — this answers "did the developer write this", so a match
|
|
177
|
+
* inside a dependency is the wrong answer.
|
|
178
|
+
*/
|
|
179
|
+
function sourceMatches(dir, pattern) {
|
|
180
|
+
const roots = ['sections', 'layouts', 'components']
|
|
181
|
+
const walk = (d, depth = 0) => {
|
|
182
|
+
if (depth > 6) return false
|
|
183
|
+
let entries
|
|
184
|
+
try {
|
|
185
|
+
entries = readdirSync(d, { withFileTypes: true })
|
|
186
|
+
} catch {
|
|
187
|
+
return false
|
|
188
|
+
}
|
|
189
|
+
for (const entry of entries) {
|
|
190
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) continue
|
|
191
|
+
const p = join(d, entry.name)
|
|
192
|
+
if (entry.isDirectory()) {
|
|
193
|
+
if (walk(p, depth + 1)) return true
|
|
194
|
+
} else if (/\.(jsx?|tsx?)$/.test(entry.name)) {
|
|
195
|
+
try {
|
|
196
|
+
if (pattern.test(readFileSync(p, 'utf8'))) return true
|
|
197
|
+
} catch {
|
|
198
|
+
// unreadable file — not this check's problem to report
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return false
|
|
203
|
+
}
|
|
204
|
+
return roots.some((r) => walk(join(dir, r)))
|
|
205
|
+
}
|
|
206
|
+
|
|
172
207
|
function checkGeneratedDataDir({ sitePath, siteName, siteYml, issues, shouldFix, fixed }) {
|
|
173
208
|
const dataDir = join(sitePath, 'public', DATA_DIR)
|
|
174
209
|
const declared = new Set(Object.keys(siteYml.collections || {}))
|
|
@@ -380,6 +415,52 @@ export async function doctor(args = []) {
|
|
|
380
415
|
}
|
|
381
416
|
}
|
|
382
417
|
|
|
418
|
+
// Prose without the tokens — a silent, total loss of styling.
|
|
419
|
+
//
|
|
420
|
+
// `@uniweb/kit/prose-tokens.css` brings Tailwind Typography with it and then
|
|
421
|
+
// points it at the site's theme.yml. A foundation that writes `prose` (or
|
|
422
|
+
// renders <Prose>/<Article>, which emit it) and does NOT import that file
|
|
423
|
+
// gets the class and no rules behind it: correct markup, zero styling, and
|
|
424
|
+
// nothing reported anywhere. Measured 2026-07-30 on a foundation missing only
|
|
425
|
+
// the plugin — every paragraph `margin-top: 0px`, no warning from Vite,
|
|
426
|
+
// Tailwind or the browser.
|
|
427
|
+
//
|
|
428
|
+
// kit closes this for anyone who imports the file; this closes it for anyone
|
|
429
|
+
// who does not, which is the case kit cannot reach.
|
|
430
|
+
for (const f of foundations) {
|
|
431
|
+
// `f.path` is already absolute (built above with join(workspaceDir, …)).
|
|
432
|
+
const stylesPath = join(f.path, 'styles.css')
|
|
433
|
+
if (!existsSync(stylesPath)) continue
|
|
434
|
+
|
|
435
|
+
const styles = readFileSync(stylesPath, 'utf8')
|
|
436
|
+
if (styles.includes('@uniweb/kit/prose-tokens.css')) continue
|
|
437
|
+
|
|
438
|
+
// Does anything here actually ask for prose? Either the class in the
|
|
439
|
+
// stylesheet, or kit's components, which render it.
|
|
440
|
+
const usesProseClass = /(^|[\s"'`])prose(\s|["'`]|$)/m.test(styles)
|
|
441
|
+
const usesProseComponent = sourceMatches(f.path, /<(Prose|Article)\b/)
|
|
442
|
+
|
|
443
|
+
if (!usesProseClass && !usesProseComponent) continue
|
|
444
|
+
|
|
445
|
+
const id = 'prose-without-tokens'
|
|
446
|
+
const reason = usesProseComponent
|
|
447
|
+
? 'renders <Prose> or <Article>'
|
|
448
|
+
: 'uses the `prose` class'
|
|
449
|
+
issues.push({
|
|
450
|
+
id,
|
|
451
|
+
type: 'warning',
|
|
452
|
+
foundation: f.name,
|
|
453
|
+
message: `${f.name} ${reason} but does not import prose-tokens.css`
|
|
454
|
+
})
|
|
455
|
+
warn(`[${id}] ${f.name} ${reason}, with no prose styling behind it`)
|
|
456
|
+
log(
|
|
457
|
+
` Tailwind Typography supplies the rules and it is not loaded, so the ` +
|
|
458
|
+
`markup is correct and completely unstyled — silently.`
|
|
459
|
+
)
|
|
460
|
+
log(` Add to ${f.folderName}/styles.css:`)
|
|
461
|
+
log(` ${colors.dim}@import "@uniweb/kit/prose-tokens.css";${colors.reset}`)
|
|
462
|
+
}
|
|
463
|
+
|
|
383
464
|
if (extensions.length > 0) {
|
|
384
465
|
log('')
|
|
385
466
|
success(`Found ${extensions.length} extension(s):`)
|
package/src/framework-index.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedAt": "2026-07-30T22:59:53.314Z",
|
|
4
4
|
"packages": {
|
|
5
5
|
"@uniweb/build": {
|
|
6
|
-
"version": "0.16.
|
|
6
|
+
"version": "0.16.1",
|
|
7
7
|
"path": "framework/build",
|
|
8
8
|
"deps": [
|
|
9
9
|
"@uniweb/content-reader",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"deps": []
|
|
23
23
|
},
|
|
24
24
|
"@uniweb/content-writer": {
|
|
25
|
-
"version": "0.3.
|
|
25
|
+
"version": "0.3.1",
|
|
26
26
|
"path": "framework/content-writer",
|
|
27
27
|
"deps": []
|
|
28
28
|
},
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"deps": []
|
|
46
46
|
},
|
|
47
47
|
"@uniweb/kit": {
|
|
48
|
-
"version": "0.10.
|
|
48
|
+
"version": "0.10.3",
|
|
49
49
|
"path": "framework/kit",
|
|
50
50
|
"deps": [
|
|
51
51
|
"@uniweb/core",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"deps": []
|
|
65
65
|
},
|
|
66
66
|
"@uniweb/projections": {
|
|
67
|
-
"version": "0.2.
|
|
67
|
+
"version": "0.2.1",
|
|
68
68
|
"path": "framework/projections",
|
|
69
69
|
"deps": [
|
|
70
70
|
"@uniweb/content-writer",
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"deps": []
|
|
101
101
|
},
|
|
102
102
|
"@uniweb/templates": {
|
|
103
|
-
"version": "0.
|
|
103
|
+
"version": "0.8.0",
|
|
104
104
|
"path": "framework/templates",
|
|
105
105
|
"deps": []
|
|
106
106
|
},
|