uniweb 0.8.31 → 0.8.33

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniweb",
3
- "version": "0.8.31",
3
+ "version": "0.8.33",
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.5.18",
45
- "@uniweb/kit": "0.7.20",
46
- "@uniweb/runtime": "0.6.26"
45
+ "@uniweb/kit": "0.7.21",
46
+ "@uniweb/runtime": "0.6.28"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.8.30",
49
+ "@uniweb/build": "0.8.32",
50
50
  "@uniweb/content-reader": "1.1.4",
51
51
  "@uniweb/semantic-parser": "1.1.8"
52
52
  },
@@ -398,7 +398,7 @@ function Hello() {
398
398
 
399
399
  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')`.
400
400
 
401
- 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`.
401
+ 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`.
402
402
 
403
403
  ### Composition: Nesting and Embedding
404
404
 
@@ -875,12 +875,29 @@ Kit provides `H1` through `H6` — use the appropriate level for semantic hierar
875
875
  **Full content rendering** (article/docs sections where the author controls the flow):
876
876
 
877
877
  ```jsx
878
- import { Section, Article } from '@uniweb/kit'
878
+ import { Section, Prose } from '@uniweb/kit'
879
879
 
880
880
  <Section block={block} width="lg" padding="md" />
881
- <Article block={block} />
881
+ <Prose content={content} block={block} />
882
882
  ```
883
883
 
884
+ `Prose` renders from the parsed content sequence — headings, paragraphs, images, code snippets, lists, etc. — with prose typography. Tagged data blocks are **skipped** (they're structured data, not prose). Access them via `content.data` for custom rendering:
885
+
886
+ ```jsx
887
+ function Lesson({ content, block }) {
888
+ return (
889
+ <div>
890
+ <Prose content={content} block={block} />
891
+ {content.data.quiz && <Quiz data={content.data.quiz} />}
892
+ </div>
893
+ )
894
+ }
895
+ ```
896
+
897
+ Pass `content` (the parsed content object — has `.sequence`). Pass `block` too if the content uses insets. Also works as a pure typography wrapper: `<Prose>{children}</Prose>`.
898
+
899
+ `Article` is an older alternative that renders from `block.rawContent` (raw ProseMirror nodes) — it renders everything including data blocks. Prefer `Prose` for new components.
900
+
884
901
  **Visuals:**
885
902
 
886
903
  ```jsx
@@ -893,7 +910,7 @@ import { Visual } from '@uniweb/kit'
893
910
 
894
911
  **Rendering text:** `H1`–`H6`, `P`, `Span`, `Div`, `Text` (with `as` prop)
895
912
 
896
- **Rendering content:** `Section` (full section with prose + layout), `Article` (prose content from `block.rawContent`), `Render` (ProseMirror nodes → React), `ChildBlocks` (render child sections)
913
+ **Rendering content:** `Section` (full section with prose + layout), `Prose` (prose from parsed content sequence, skips data blocks), `Article` (raw ProseMirror rendering), `Render` (ProseMirror nodes → React), `ChildBlocks` (render child sections)
897
914
 
898
915
  **Rendering media:** `Visual` (first non-empty: inset/video/image), `Image`, `Media`, `Icon`
899
916
 
@@ -5,6 +5,8 @@
5
5
  import { existsSync, readFileSync, readdirSync } from 'node:fs'
6
6
  import { join, resolve, basename, dirname, relative } from 'node:path'
7
7
  import yaml from 'js-yaml'
8
+ import { getCliVersion } from '../versions.js'
9
+ import { readAgentsVersion } from '../utils/agents-stamp.js'
8
10
 
9
11
  // ANSI colors
10
12
  const colors = {
@@ -484,6 +486,29 @@ export async function doctor(args = []) {
484
486
  }
485
487
  }
486
488
 
489
+ // Check AGENTS.md freshness
490
+ log('')
491
+ const agentsPath = join(workspaceDir, 'AGENTS.md')
492
+ const agentsVersion = readAgentsVersion(agentsPath)
493
+ const cliVersion = getCliVersion()
494
+
495
+ if (!existsSync(agentsPath)) {
496
+ warn('AGENTS.md not found')
497
+ info(`Run: uniweb update`)
498
+ issues.push({ type: 'warn', message: 'AGENTS.md not found' })
499
+ } else if (!agentsVersion) {
500
+ // No stamp — manually created or pre-stamp version
501
+ warn('AGENTS.md has no version stamp (may be outdated)')
502
+ info(`Run: uniweb update`)
503
+ issues.push({ type: 'warn', message: 'AGENTS.md has no version stamp' })
504
+ } else if (agentsVersion !== cliVersion) {
505
+ warn(`AGENTS.md is outdated (v${agentsVersion} → v${cliVersion})`)
506
+ info(`Run: uniweb update`)
507
+ issues.push({ type: 'warn', message: `AGENTS.md outdated (v${agentsVersion} → v${cliVersion})` })
508
+ } else {
509
+ success(`AGENTS.md is up to date (v${cliVersion})`)
510
+ }
511
+
487
512
  // Summary
488
513
  log('')
489
514
  log('─'.repeat(50))
@@ -0,0 +1,55 @@
1
+ /**
2
+ * uniweb update - Update generated project files
3
+ *
4
+ * Regenerates AGENTS.md from the installed CLI version.
5
+ */
6
+
7
+ import { existsSync, writeFileSync } from 'node:fs'
8
+ import { join, resolve } from 'node:path'
9
+ import { findWorkspaceRoot } from '../utils/workspace.js'
10
+ import { readAgentsVersion, generateAgentsContent } from '../utils/agents-stamp.js'
11
+ import { getCliVersion } from '../versions.js'
12
+
13
+ // ANSI colors
14
+ const colors = {
15
+ reset: '\x1b[0m',
16
+ bright: '\x1b[1m',
17
+ dim: '\x1b[2m',
18
+ red: '\x1b[31m',
19
+ green: '\x1b[32m',
20
+ yellow: '\x1b[33m',
21
+ blue: '\x1b[36m'
22
+ }
23
+
24
+ const success = (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`)
25
+ const warn = (msg) => console.log(`${colors.yellow}⚠${colors.reset} ${msg}`)
26
+ const error = (msg) => console.log(`${colors.red}✗${colors.reset} ${msg}`)
27
+ const log = console.log
28
+
29
+ export async function update(args = []) {
30
+ const workspaceDir = findWorkspaceRoot(process.cwd())
31
+
32
+ if (!workspaceDir) {
33
+ error('Not in a Uniweb workspace')
34
+ log(`${colors.dim}Run this command from your project root or a package directory.${colors.reset}`)
35
+ process.exit(1)
36
+ }
37
+
38
+ const cliVersion = getCliVersion()
39
+ const agentsPath = join(workspaceDir, 'AGENTS.md')
40
+ const currentVersion = readAgentsVersion(agentsPath)
41
+
42
+ if (currentVersion === cliVersion) {
43
+ success(`AGENTS.md is already up to date (v${cliVersion})`)
44
+ return
45
+ }
46
+
47
+ const content = generateAgentsContent()
48
+ writeFileSync(agentsPath, content)
49
+
50
+ if (currentVersion) {
51
+ success(`Updated AGENTS.md (v${currentVersion} → v${cliVersion})`)
52
+ } else {
53
+ success(`Created AGENTS.md (v${cliVersion})`)
54
+ }
55
+ }
package/src/index.js CHANGED
@@ -35,6 +35,7 @@ import { publish } from './commands/publish.js'
35
35
  import { deploy } from './commands/deploy.js'
36
36
  import { invite } from './commands/invite.js'
37
37
  import { handoff } from './commands/handoff.js'
38
+ import { update } from './commands/update.js'
38
39
  import { template } from './commands/template.js'
39
40
  import {
40
41
  resolveTemplate,
@@ -479,6 +480,12 @@ async function main() {
479
480
  return
480
481
  }
481
482
 
483
+ // Handle update command
484
+ if (command === 'update') {
485
+ await update(args.slice(1))
486
+ return
487
+ }
488
+
482
489
  // Handle inspect command
483
490
  if (command === 'inspect') {
484
491
  await inspect(args.slice(1))
@@ -794,6 +801,7 @@ ${colors.bright}Commands:${colors.reset}
794
801
  inspect <path> Inspect parsed content shape of a markdown file or folder
795
802
  docs Generate component documentation
796
803
  doctor Diagnose project configuration issues
804
+ update Update AGENTS.md to match installed CLI version
797
805
  i18n <cmd> Internationalization (extract, sync, status)
798
806
  template publish Publish a site as a cloud template
799
807
  login Log in to your Uniweb account
@@ -0,0 +1,41 @@
1
+ /**
2
+ * AGENTS.md version stamp utilities
3
+ *
4
+ * The stamp is an HTML comment on the first line: <!-- uniweb-agents v0.8.32 -->
5
+ * Used by `doctor` (freshness check) and `update` (regeneration).
6
+ */
7
+
8
+ import { existsSync, readFileSync } from 'node:fs'
9
+ import { join, dirname } from 'node:path'
10
+ import { fileURLToPath } from 'node:url'
11
+ import { getCliVersion } from '../versions.js'
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url))
14
+
15
+ const STAMP_PATTERN = /^<!-- uniweb-agents v([\d.]+) -->/
16
+
17
+ /**
18
+ * Read the version stamp from an AGENTS.md file
19
+ * @param {string} filePath - Absolute path to AGENTS.md
20
+ * @returns {string|null} Version string or null if no stamp
21
+ */
22
+ export function readAgentsVersion(filePath) {
23
+ if (!existsSync(filePath)) return null
24
+ try {
25
+ const firstLine = readFileSync(filePath, 'utf8').split('\n')[0]
26
+ const match = firstLine.match(STAMP_PATTERN)
27
+ return match ? match[1] : null
28
+ } catch {
29
+ return null
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Generate AGENTS.md content with version stamp
35
+ * @returns {string} Full AGENTS.md content with stamp
36
+ */
37
+ export function generateAgentsContent() {
38
+ const partialsDir = join(__dirname, '..', '..', 'partials')
39
+ const agentsContent = readFileSync(join(partialsDir, 'agents.md'), 'utf8')
40
+ return `<!-- uniweb-agents v${getCliVersion()} -->\n${agentsContent}\n`
41
+ }
@@ -11,7 +11,7 @@ import { join, dirname } from 'node:path'
11
11
  import { fileURLToPath } from 'node:url'
12
12
  import yaml from 'js-yaml'
13
13
  import { copyTemplateDirectory, registerVersions } from '../templates/processor.js'
14
- import { getVersionsForTemplates } from '../versions.js'
14
+ import { getVersionsForTemplates, getCliVersion } from '../versions.js'
15
15
 
16
16
  const __dirname = dirname(fileURLToPath(import.meta.url))
17
17
  const TEMPLATES_DIR = join(__dirname, '..', '..', 'templates')
@@ -36,6 +36,9 @@ export async function scaffoldWorkspace(targetDir, context, options = {}) {
36
36
  // because an empty packages: [] makes pnpm search parent directories.
37
37
  const skip = context.workspaceGlobs?.length ? [] : ['pnpm-workspace.yaml']
38
38
 
39
+ // Inject CLI version for AGENTS.md stamp
40
+ context = { ...context, cliVersion: getCliVersion() }
41
+
39
42
  const templatePath = join(TEMPLATES_DIR, 'workspace')
40
43
  await copyTemplateDirectory(templatePath, targetDir, context, {
41
44
  onProgress: options.onProgress,
package/src/versions.js CHANGED
@@ -22,6 +22,15 @@ function getCliPackageJson() {
22
22
  return JSON.parse(readFileSync(packagePath, 'utf8'))
23
23
  }
24
24
 
25
+ /**
26
+ * Get the CLI's own version
27
+ *
28
+ * @returns {string} CLI version (e.g., "0.8.32")
29
+ */
30
+ export function getCliVersion() {
31
+ return getCliPackageJson().version
32
+ }
33
+
25
34
  /**
26
35
  * Extract version number from version spec (e.g., "^0.1.4" -> "0.1.4")
27
36
  */
@@ -1 +1,2 @@
1
+ <!-- uniweb-agents v{{cliVersion}} -->
1
2
  {{> agents}}