uniweb 0.10.2 → 0.10.5

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
@@ -257,7 +257,7 @@ You can delete the `foundation/` folder entirely and point your site at a publis
257
257
 
258
258
  _Site-first_ — You're building a website. The foundation is your component library, co-developed with the site. This is the common case.
259
259
 
260
- _Foundation-first_ — You're building a component system. The site is a test harness with sample content. The real sites live elsewhere—other repositories, other teams, or managed on [uniweb.app](https://uniweb.app). Use `uniweb add site` to add multiple test sites exercising a shared foundation.
260
+ _Foundation-first_ — You're building a component system. The site is a test harness with sample content. The real sites live elsewhere—other repositories, other teams, or managed on [hub.uniweb.app](https://hub.uniweb.app). Use `uniweb add site` to add multiple test sites exercising a shared foundation.
261
261
 
262
262
  ## Growing Your Project
263
263
 
@@ -299,11 +299,11 @@ The structure you start with scales without rewrites:
299
299
 
300
300
  1. **Single project** — One site, one foundation. Develop and deploy together. Most projects stay here.
301
301
 
302
- 2. **Published foundation** — Release your foundation as an npm package or to [uniweb.app](https://uniweb.app). Other sites can use it without copying code.
302
+ 2. **Published foundation** — Release your foundation as an npm package or to [hub.uniweb.app](https://hub.uniweb.app). Other sites can use it without copying code.
303
303
 
304
304
  3. **Multiple sites** — Several sites share one foundation. Update components once, every site benefits.
305
305
 
306
- 4. **Platform-managed sites** — Sites built on [uniweb.app](https://uniweb.app) with visual editing tools can use your foundation. You develop components locally; content teams work in the browser.
306
+ 4. **Platform-managed sites** — Sites built on [hub.uniweb.app](https://hub.uniweb.app) with visual editing tools can use your foundation. You develop components locally; content teams work in the browser.
307
307
 
308
308
  Start with local files deployed anywhere. The same foundation works across all these scenarios.
309
309
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.10.2",
3
+ "version": "0.10.5",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,14 +41,14 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "0.7.1",
45
- "@uniweb/runtime": "0.8.2",
46
- "@uniweb/kit": "0.9.1"
44
+ "@uniweb/core": "0.7.3",
45
+ "@uniweb/kit": "0.9.3",
46
+ "@uniweb/runtime": "0.8.4"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.10.2",
50
- "@uniweb/content-reader": "1.1.4",
51
- "@uniweb/semantic-parser": "1.1.9"
49
+ "@uniweb/build": "0.11.2",
50
+ "@uniweb/semantic-parser": "1.1.11",
51
+ "@uniweb/content-reader": "1.1.5"
52
52
  },
53
53
  "peerDependenciesMeta": {
54
54
  "@uniweb/build": {
@@ -417,6 +417,31 @@ Access: `content.snippets[0]` → `{ language: 'jsx', code: 'function Hello() {.
417
417
 
418
418
  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`. `<Prose>` handles this automatically — it renders code snippets with syntax highlighting and skips tagged data blocks, which components access separately via `content.data`.
419
419
 
420
+ ### Math (LaTeX)
421
+
422
+ Authors write LaTeX directly in markdown; the browser renders real math natively. The LaTeX is compiled to MathML Core **at build time** — no runtime math library ships to the browser, no CSS from a math package is required.
423
+
424
+ Three forms, matching Pandoc / GitHub / VS Code / Jupyter / Obsidian convention:
425
+
426
+ | Form | Mode | Example |
427
+ |---|---|---|
428
+ | `$x^2$` | Inline | `Let $f(x) = ax + b$ be a function.` |
429
+ | `$$x^2$$` | Display (block when on its own line, inline display mid-paragraph) | `$$\sum_{i=1}^n i$$` |
430
+ | ` ```math ` fence | Display (multi-line friendly) | see below |
431
+ | `\$` | Literal `$` | `The price is \$20.` |
432
+
433
+ Multi-line display math uses a `math` code fence:
434
+
435
+ ````markdown
436
+ ```math
437
+ \int_0^\infty e^{-x^2}\,dx = \frac{\sqrt{\pi}}{2}
438
+ ```
439
+ ````
440
+
441
+ Disambiguation for `$...$`: a dollar-delimited span counts as math only when the body has no whitespace next to the delimiters *and* the closing `$` is not immediately followed by a digit. So `It costs $5 and $10 total` and `Budget: $200` stay as prose without any escaping, while `Let $f(x) = 5$ be a function` is math. Use `\$` when you need a literal `$` in content where the rules would otherwise trip it up.
442
+
443
+ Math rides through the same content pipeline as everything else — it appears in prerendered HTML, survives `compile('epub')` and `compile('pagedjs')`, and roundtrips cleanly through the editor. Component code needs nothing special; `<Prose>` and `<Text>` already render the MathML that the pipeline embedded.
444
+
420
445
  ### Composition: Nesting and Embedding
421
446
 
422
447
  Pages are sequences of sections — that's the obvious composition layer. But the framework supports real nesting: sections containing other sections, and sections containing embedded components. And it does this without leaving markdown.
@@ -891,7 +916,7 @@ fetch:
891
916
  limit: 3
892
917
  ```
893
918
 
894
- **Lean lists with `deferred:`.** Collections with heavy fields (article bodies, large nested arrays) can declare `deferred: [body]` in `site.yml`. The cascade payload omits those fields; per-record full files are emitted at `/data/<name>/<slug>.json` (markdown-backed collections) or fetched from an author-declared `detailUrl:` pattern (API-backed collections). On dynamic-route pages the focused entity's full record is delivered automatically; elsewhere components fetch on demand via the `useEntityDetail` kit hook.
919
+ **Lean lists with `deferred:`.** Collections with heavy fields (article bodies, large nested arrays) can declare `deferred: [body]` in `site.yml`. The cascade payload omits those fields; per-record full files are emitted at `/data/<name>/<slug>.json` (file-based collections — markdown, YAML, or JSON) or fetched from an author-declared `detailUrl:` pattern (API-backed collections). On dynamic-route pages the focused entity's full record is delivered automatically; elsewhere components fetch on demand via the `useEntityDetail` kit hook.
895
920
 
896
921
  **Component-side fetching.** When a component genuinely needs to fetch something on its own (a search box, a "load more" button, a popover that lazy-loads), use the kit hooks (`useFetched`, `useCacheEntry`, `useEntityDetail`). They share the framework's cache and dispatcher with declarative fetches; same-key requests dedupe automatically.
897
922
 
@@ -11,9 +11,10 @@
11
11
  */
12
12
 
13
13
  import { existsSync, readFileSync } from 'node:fs'
14
- import { resolve, join } from 'node:path'
14
+ import { resolve, join, dirname } from 'node:path'
15
15
  import { spawn } from 'node:child_process'
16
16
  import { writeFile, mkdir } from 'node:fs/promises'
17
+ import { createRequire } from 'node:module'
17
18
 
18
19
  // Import build utilities from @uniweb/build
19
20
  import {
@@ -123,6 +124,46 @@ function runCommand(command, args, cwd) {
123
124
  })
124
125
  }
125
126
 
127
+ /**
128
+ * Resolve the project's local Vite binary.
129
+ *
130
+ * We intentionally do NOT fall back to `npx vite`: npx resolves through npm's
131
+ * global cache, which may hold a stale Vite version (e.g. Vite 5 ignores
132
+ * lib.fileName for CSS and always emits style.css). Using the project's local
133
+ * Vite guarantees the version declared in package.json is the one that runs.
134
+ *
135
+ * @param {string} projectDir
136
+ * @returns {string} Absolute path to the vite CLI entry
137
+ */
138
+ function resolveLocalVite(projectDir) {
139
+ const require = createRequire(join(projectDir, 'package.json'))
140
+ let pkgJsonPath
141
+ try {
142
+ pkgJsonPath = require.resolve('vite/package.json')
143
+ } catch {
144
+ throw new Error(
145
+ `Vite is not installed in ${projectDir}.\n` +
146
+ `Run \`pnpm install\` (or npm/yarn install) in the project before building.`
147
+ )
148
+ }
149
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8'))
150
+ const binRel = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin?.vite
151
+ if (!binRel) {
152
+ throw new Error(`Could not find vite bin entry in ${pkgJsonPath}`)
153
+ }
154
+ return join(dirname(pkgJsonPath), binRel)
155
+ }
156
+
157
+ /**
158
+ * Run the project's local Vite with the given args.
159
+ * @param {string} projectDir
160
+ * @param {string[]} args - e.g. ['build']
161
+ */
162
+ async function runLocalVite(projectDir, args) {
163
+ const viteBin = resolveLocalVite(projectDir)
164
+ await runCommand(process.execPath, [viteBin, ...args], projectDir)
165
+ }
166
+
126
167
  /**
127
168
  * Build a foundation
128
169
  */
@@ -158,7 +199,7 @@ async function buildFoundation(projectDir, options = {}) {
158
199
 
159
200
  // Check if vite.config.js uses the generated entry
160
201
  // For now, just run the standard vite build
161
- await runCommand('npx', ['vite', 'build'], projectDir)
202
+ await runLocalVite(projectDir, ['build'])
162
203
 
163
204
  // Vite's foundation plugin generates dist/meta/schema.json
164
205
  // and processes preview images during the build.
@@ -351,7 +392,7 @@ async function buildSite(projectDir, options = {}) {
351
392
  info('Building site...')
352
393
 
353
394
  // Run vite build for sites
354
- await runCommand('npx', ['vite', 'build'], projectDir)
395
+ await runLocalVite(projectDir, ['build'])
355
396
 
356
397
  success('Site build complete')
357
398