uniweb 0.80.8 → 0.81.1

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.80.8",
3
+ "version": "0.81.1",
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/core": "^0.35.4",
45
- "@uniweb/runtime": "^0.28.9",
44
+ "@uniweb/runtime": "^0.28.10",
46
45
  "@uniweb/content-writer": "^0.3.4",
47
- "@uniweb/schemas": "^0.10.1",
48
- "@uniweb/kit": "^0.19.14",
46
+ "@uniweb/schemas": "^0.10.2",
47
+ "@uniweb/core": "^0.36.0",
48
+ "@uniweb/kit": "^0.19.15",
49
49
  "@uniweb/semantic-parser": "^1.5.1"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "@uniweb/content-reader": "^1.2.8",
53
53
  "@uniweb/semantic-parser": "^1.5.1",
54
- "@uniweb/build": "^0.74.4"
54
+ "@uniweb/build": "^0.75.0"
55
55
  },
56
56
  "peerDependenciesMeta": {
57
57
  "@uniweb/build": {
@@ -1031,7 +1031,7 @@ import LessonHeader from '../../components/LessonHeader' // ❌ breaks if you re
1031
1031
 
1032
1032
  Within the same directory, use normal relative imports (`./AIFeedbackCard`).
1033
1033
 
1034
- **Foundation entry (`main.js`).** A single `export default { … }` whose top-level keys are the foundation's identity — `name` (what it registers as, `@org/<name>`) and `description` — and the capabilities it provides — `defaultLayout`, `defaultSection`, `viewTransitions`, `props`, `defaultInsets`, `xref`, `outputs`, `handlers` — plus an optional named `vars` export. The capabilities are read at render; the one thing a foundation declares that *isn't* — which host services it supports — lives in `package.json` instead (see [Declaring what your foundation supports](#declaring-what-your-foundation-supports)). Section types and layouts are auto-discovered and merged in by `@uniweb/build`. The build wraps your default export under `default.capabilities` in `dist/entry.js`; you never write that wrapper. The one place it matters: when you import your **own** `main.js` from a component (e.g. a download button calling `compileDocument(website, { foundation })`), you get the bare default object — pass it through directly, Press handles both shapes.
1034
+ **Foundation entry (`main.js`).** A single `export default { … }` whose top-level keys are the foundation's identity — `name` (what it registers as, `@org/<name>`) and `description` — and the capabilities it provides — `defaultLayout`, `defaultSection`, `viewTransitions`, `defaultInsets`, `xref`, `outputs`, `handlers` — plus an optional named `vars` export. The capabilities are read at render; the one thing a foundation declares that *isn't* — which host services it supports — lives in `package.json` instead (see [Declaring what your foundation supports](#declaring-what-your-foundation-supports)). Section types and layouts are auto-discovered and merged in by `@uniweb/build`. The build carries into `default.capabilities` in `dist/entry.js` only what a published page reads of your default export — the code it calls (`handlers`, `defaultInsets`, `xref`, `transports`, `outputs`) and its data, lean (`defaultLayout`, `defaultSection`, `viewTransitions`, `scroll`, each var's `default`, `type` and `applyTo`, each `data:` key's schema ref). `name`, `description`, a var's label or description and any key the framework does not read stay out of the published bundle: they are for the editor, which reads them from the foundation's schema. You never write that wrapper. The file is `main.js` — never `main.jsx`, which the build refuses; `main.js` may hold JSX. The one place it matters: when you import your **own** `main.js` from a component (e.g. a download button calling `compileDocument(website, { foundation })`), you get the bare default object — pass it through directly, Press handles both shapes.
1035
1035
 
1036
1036
  ### Props interface
1037
1037
 
@@ -2726,8 +2726,8 @@ That freezes sites using your foundation on the recorded version. Only reach for
2726
2726
  ```yaml
2727
2727
  # site.yml
2728
2728
  defaultLanguage: en
2729
- languages: [en, es, fr] # or [{ code: es, label: Español }, …], or '*' to
2730
- # auto-discover from the locales/ folder
2729
+ languages: [en, es, fr] # codes only — a language's name comes from its code;
2730
+ # or '*' to auto-discover from the locales/ folder
2731
2731
  publishLanguages: [en, es] # optional — fr stays a dev-previewable draft,
2732
2732
  # excluded from production output entirely
2733
2733
  ```
@@ -2742,6 +2742,8 @@ uniweb build # merges translations, emits dist/es/, dist/fr/
2742
2742
 
2743
2743
  Running `extract` before a build is the usual first mistake — it reads the compiled content, so a stale build yields a stale manifest. After content changes, `uniweb i18n sync` updates the manifest and `init` refreshes the language files; `status` and `audit` report coverage and stale entries.
2744
2744
 
2745
+ **A value is inline Markdown.** A sentence's links and bold go into its translation — `"Voir [Rôles](page:docs/roles)."` — or it renders without them, and coverage still counts it; `uniweb i18n status` lists these under *Loses markup*. A **button** — a line that is only a link — is the exception: translate its label alone and it keeps its link and options (`{role=primary}`). Buttons on consecutive lines are one string; give one label per line (`"Créer un groupe\nEn savoir plus"`).
2746
+
2745
2747
  **Two mechanisms.** *Hash-based strings* (the default) — `locales/{locale}.json` maps content hash → translation, so structure stays identical across languages. When one source string needs different translations depending on where it appears, the value takes an override form:
2746
2748
 
2747
2749
  ```json
@@ -2858,4 +2860,8 @@ Source repo (public, cloneable): **https://github.com/uniweb/docs** · any page
2858
2860
  | `development/` | Foundations, component patterns, project structures, data, layouts, i18n, migration, schemas |
2859
2861
  | `reference/` | site.yml, page.yml, content structure, meta.js, kit API, navigation, records, queries, data fetching, parametric pages, CLI, deployment |
2860
2862
 
2861
- The by-task table is in Part 0. For CLI flags, prefer `uniweb <command> --help` over this file — it's always current.
2863
+ The by-task table is in Part 0. For CLI flags, prefer `uniweb <command> --help` over this file — it's always current.
2864
+
2865
+ ## Notes about this project
2866
+
2867
+ Conventions, decisions, where things are — keep them in a file of your own (`NOTES.md`, say). `uniweb update` rewrites this file for each new version, and the one part it keeps is a block between `<!-- project-notes:start -->` and `<!-- project-notes:end -->`, each on a line of its own, which it moves to the end of the file. That block is the place for a line pointing to your notes, or for short notes themselves.
@@ -47,7 +47,9 @@ import prompts from 'prompts'
47
47
  import { findWorkspaceRoot } from '../utils/workspace.js'
48
48
  import {
49
49
  readAgentsVersion,
50
- generateAgentsContent
50
+ refreshAgentsContent,
51
+ PROJECT_NOTES_START,
52
+ PROJECT_NOTES_END
51
53
  } from '../utils/agents-stamp.js'
52
54
  import { getCliVersion } from '../versions.js'
53
55
  import { isNonInteractive } from '../utils/interactive.js'
@@ -775,9 +777,27 @@ async function refreshAgents(ctx) {
775
777
  return 'current'
776
778
  }
777
779
 
780
+ // The project's notes travel to the new file (`refreshAgentsContent`). Markers that do not pair
781
+ // up leave AGENTS.md as it is: where a block ends would be a guess, and a wrong one loses notes.
782
+ const refreshed = refreshAgentsContent(
783
+ existsSync(agentsPath) ? readFileSync(agentsPath, 'utf8') : null
784
+ )
785
+ if (refreshed.error) {
786
+ warn(`AGENTS.md left as it is: ${refreshed.error}.`)
787
+ log(
788
+ `${colors.dim}Your project notes are kept between ${PROJECT_NOTES_START} and ${PROJECT_NOTES_END}, each on a line of its own. Fix the markers, then re-run${colors.reset} ${colors.cyan}uniweb update${colors.reset}${colors.dim}.${colors.reset}`
789
+ )
790
+ log('')
791
+ if (agentsOnly) process.exit(1)
792
+ return 'skipped'
793
+ }
794
+ const keeping = refreshed.kept
795
+ ? `, keeping ${refreshed.kept === 1 ? 'its project notes' : `its ${refreshed.kept} project-notes blocks`}`
796
+ : ''
797
+
778
798
  if (dryRun) {
779
799
  info(
780
- `Dry-run: would ${currentAgentsVersion ? `update AGENTS.md (v${currentAgentsVersion} → v${cliVersion})` : `create AGENTS.md (v${cliVersion})`}.`
800
+ `Dry-run: would ${currentAgentsVersion ? `update AGENTS.md (v${currentAgentsVersion} → v${cliVersion})` : `create AGENTS.md (v${cliVersion})`}${keeping}.`
781
801
  )
782
802
  return 'skipped'
783
803
  }
@@ -798,9 +818,9 @@ async function refreshAgents(ctx) {
798
818
  }
799
819
  }
800
820
 
801
- writeFileSync(agentsPath, generateAgentsContent())
821
+ writeFileSync(agentsPath, refreshed.content)
802
822
  if (currentAgentsVersion) {
803
- success(`Updated AGENTS.md (v${currentAgentsVersion} → v${cliVersion}).`)
823
+ success(`Updated AGENTS.md (v${currentAgentsVersion} → v${cliVersion})${keeping}.`)
804
824
  return 'updated'
805
825
  }
806
826
  success(`Created AGENTS.md (v${cliVersion}).`)
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-01T14:16:42.270Z",
3
+ "generatedAt": "2026-10-02T03:03:06.390Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
- "version": "0.6.8",
6
+ "version": "0.6.9",
7
7
  "path": "framework/api",
8
8
  "deps": [
9
9
  "@uniweb/core"
10
10
  ]
11
11
  },
12
12
  "@uniweb/build": {
13
- "version": "0.74.4",
13
+ "version": "0.75.0",
14
14
  "path": "framework/build",
15
15
  "deps": [
16
16
  "@uniweb/content-reader",
@@ -35,7 +35,7 @@
35
35
  "deps": []
36
36
  },
37
37
  "@uniweb/core": {
38
- "version": "0.35.4",
38
+ "version": "0.36.0",
39
39
  "path": "framework/core",
40
40
  "deps": [
41
41
  "@uniweb/semantic-parser",
@@ -48,14 +48,14 @@
48
48
  "deps": []
49
49
  },
50
50
  "@uniweb/icons": {
51
- "version": "0.4.26",
51
+ "version": "0.4.27",
52
52
  "path": "framework/icons",
53
53
  "deps": [
54
54
  "@uniweb/core"
55
55
  ]
56
56
  },
57
57
  "@uniweb/kit": {
58
- "version": "0.19.14",
58
+ "version": "0.19.15",
59
59
  "path": "framework/kit",
60
60
  "deps": [
61
61
  "@uniweb/core",
@@ -76,7 +76,7 @@
76
76
  "deps": []
77
77
  },
78
78
  "@uniweb/projections": {
79
- "version": "0.7.12",
79
+ "version": "0.7.13",
80
80
  "path": "framework/projections",
81
81
  "deps": [
82
82
  "@uniweb/content-writer",
@@ -84,7 +84,7 @@
84
84
  ]
85
85
  },
86
86
  "@uniweb/runtime": {
87
- "version": "0.28.9",
87
+ "version": "0.28.10",
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.10.1",
100
+ "version": "0.10.2",
101
101
  "path": "framework/schemas",
102
102
  "deps": []
103
103
  },
@@ -117,7 +117,7 @@
117
117
  "deps": []
118
118
  },
119
119
  "@uniweb/templates": {
120
- "version": "0.17.10",
120
+ "version": "0.17.11",
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.17",
130
+ "version": "0.10.19",
131
131
  "path": "framework/unipress",
132
132
  "deps": [
133
133
  "@uniweb/build",
@@ -39,3 +39,72 @@ export function generateAgentsContent() {
39
39
  const agentsContent = readFileSync(join(partialsDir, 'agents.md'), 'utf8')
40
40
  return `<!-- uniweb-agents v${getCliVersion()} -->\n${agentsContent}\n`
41
41
  }
42
+
43
+ /**
44
+ * ⭐ THE ONE PART OF AGENTS.md A PROJECT KEEPS: its project-notes blocks. `update` rewrites the
45
+ * file from this CLI's partial, so a note written anywhere else in it is lost on the next version.
46
+ * A block runs from a `<!-- project-notes:start -->` line to a `<!-- project-notes:end -->` line,
47
+ * and is carried over verbatim, at the end of the new file. ⛔ Until 2026-10-01 nothing was kept.
48
+ *
49
+ * A marker is a line of its own, outside a code fence — so the partial can name the markers in a
50
+ * sentence without its own text being taken for a block.
51
+ */
52
+ export const PROJECT_NOTES_START = '<!-- project-notes:start -->'
53
+ export const PROJECT_NOTES_END = '<!-- project-notes:end -->'
54
+
55
+ const MARKER_LINE = /^<!--\s*project-notes:(start|end)\s*-->$/
56
+ const FENCE_LINE = /^(`{3,}|~{3,})/
57
+
58
+ /**
59
+ * The project-notes blocks of an AGENTS.md, in order, each from its start line to its end line.
60
+ *
61
+ * @param {string} text
62
+ * @returns {{ blocks: string[] } | { error: string }} an error when the markers do not pair up —
63
+ * where a block ends would be a guess, and a wrong one loses notes
64
+ */
65
+ export function readProjectNotes(text) {
66
+ const blocks = []
67
+ let block = null
68
+ let fence = null
69
+ let lineNumber = 0
70
+ for (const line of String(text ?? '').split(/(?<=\n)/)) {
71
+ lineNumber++
72
+ const trimmed = line.trim()
73
+ const fenceMark = trimmed.match(FENCE_LINE)?.[1]
74
+ if (fenceMark && (!fence || fenceMark.startsWith(fence))) fence = fence ? null : fenceMark
75
+ const marker = !fence && !fenceMark ? trimmed.match(MARKER_LINE)?.[1] : null
76
+
77
+ if (marker === 'start') {
78
+ if (block) return { error: `a second ${PROJECT_NOTES_START} on line ${lineNumber}, before the first one's ${PROJECT_NOTES_END}` }
79
+ block = [line]
80
+ } else if (marker === 'end') {
81
+ if (!block) return { error: `${PROJECT_NOTES_END} on line ${lineNumber} has no ${PROJECT_NOTES_START} before it` }
82
+ block.push(line)
83
+ blocks.push(block.join('').replace(/\s+$/, ''))
84
+ block = null
85
+ } else if (block) {
86
+ block.push(line)
87
+ }
88
+ }
89
+ if (block) return { error: `${PROJECT_NOTES_START} has no ${PROJECT_NOTES_END} after it` }
90
+ return { blocks }
91
+ }
92
+
93
+ /**
94
+ * AGENTS.md as `update` writes it: this CLI's partial, stamped, and the project-notes blocks of the
95
+ * file it replaces after it.
96
+ *
97
+ * @param {string|null} existing - the current AGENTS.md, or null when there is none
98
+ * @returns {{ content: string, kept: number } | { error: string }}
99
+ */
100
+ export function refreshAgentsContent(existing) {
101
+ const fresh = generateAgentsContent()
102
+ if (existing == null) return { content: fresh, kept: 0 }
103
+ const notes = readProjectNotes(existing)
104
+ if (notes.error) return notes
105
+ if (!notes.blocks.length) return { content: fresh, kept: 0 }
106
+ return {
107
+ content: `${fresh.replace(/\s+$/, '')}\n\n${notes.blocks.join('\n\n')}\n`,
108
+ kept: notes.blocks.length,
109
+ }
110
+ }
@@ -1,5 +1,4 @@
1
1
  // Foundation Configuration
2
- // Schema: schemas/foundation.schema.json
3
2
  //
4
3
  // ─── CSS Custom Properties ──────────────────────────────────────────────────────
5
4
  // Export `vars` to declare CSS custom properties that sites can override in theme.yml:
@@ -30,9 +29,5 @@ export default {
30
29
  // ─── Layout ─────────────────────────────────────────────────────────────────
31
30
  // Create layouts in layouts/MyLayout/index.jsx (auto-discovered).
32
31
  // defaultLayout: 'MyLayout',
33
-
34
- // ─── Props ──────────────────────────────────────────────────────────────────
35
- // Foundation-wide data accessible via website.foundationProps in components.
36
- // props: {},
37
32
  }
38
33
  {{/if}}
@@ -11,7 +11,7 @@ A website built with [Uniweb](https://github.com/uniweb/cli) — a component web
11
11
  ├── src/ # Foundation package (the site's source code; pkg name: src)
12
12
  │ ├── sections/ # Section types (selectable by content authors)
13
13
  │ ├── styles.css # Tailwind CSS v4 theme
14
- │ ├── main.js # Foundation declarations (vars, defaultLayout, props)
14
+ │ ├── main.js # Foundation declarations (name, vars, defaultLayout)
15
15
  │ └── vite.config.js # defineFoundationConfig()
16
16
  │
17
17
  ├── site/ # Content (markdown)