@uniweb/build 0.7.6 → 0.8.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/build",
3
- "version": "0.7.6",
3
+ "version": "0.8.1",
4
4
  "description": "Build tooling for the Uniweb Component Web Platform",
5
5
  "type": "module",
6
6
  "exports": {
@@ -50,9 +50,9 @@
50
50
  "sharp": "^0.33.2"
51
51
  },
52
52
  "optionalDependencies": {
53
- "@uniweb/content-reader": "1.1.2",
54
- "@uniweb/schemas": "0.2.1",
55
- "@uniweb/runtime": "0.6.1"
53
+ "@uniweb/content-reader": "1.1.3",
54
+ "@uniweb/runtime": "0.6.1",
55
+ "@uniweb/schemas": "0.2.1"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
@@ -61,7 +61,7 @@
61
61
  "@tailwindcss/vite": "^4.0.0",
62
62
  "@vitejs/plugin-react": "^4.0.0 || ^5.0.0",
63
63
  "vite-plugin-svgr": "^4.0.0",
64
- "@uniweb/core": "0.5.1"
64
+ "@uniweb/core": "0.5.2"
65
65
  },
66
66
  "peerDependenciesMeta": {
67
67
  "vite": {
@@ -190,6 +190,11 @@ export function extractRuntimeSchema(fullMeta) {
190
190
 
191
191
  const runtime = {}
192
192
 
193
+ // Embed flag: signals this component is designed for inline embedding via @ refs
194
+ if (fullMeta.embed) {
195
+ runtime.embed = true
196
+ }
197
+
193
198
  // Background opt-out: 'self' means the component renders its own background
194
199
  // layer (solid colors, insets, effects), so the runtime skips its Background.
195
200
  if (fullMeta.background) {
@@ -178,6 +178,45 @@ async function readYamlFile(filePath) {
178
178
  }
179
179
  }
180
180
 
181
+ /**
182
+ * Extract inline child references from a ProseMirror document.
183
+ *
184
+ * Walks top-level nodes for `inline_child_ref` (produced by content-reader
185
+ * for `![alt](@ComponentName){params}` syntax). Each ref is removed from the
186
+ * document and replaced with an `inline_child_placeholder` node carrying a
187
+ * unique refId. The extracted refs are returned as an array.
188
+ *
189
+ * @param {Object} doc - ProseMirror document (mutated in place)
190
+ * @returns {Array} Array of { refId, type, params, alt }
191
+ */
192
+ function extractInlineChildren(doc) {
193
+ if (!doc?.content || !Array.isArray(doc.content)) return []
194
+
195
+ const inlineChildren = []
196
+ let refIndex = 0
197
+
198
+ for (let i = 0; i < doc.content.length; i++) {
199
+ const node = doc.content[i]
200
+ if (node.type === 'inline_child_ref') {
201
+ const { component, alt, ...params } = node.attrs || {}
202
+ const refId = `inline_${refIndex++}`
203
+ inlineChildren.push({
204
+ refId,
205
+ type: component,
206
+ params: Object.keys(params).length > 0 ? params : {},
207
+ alt: alt || null,
208
+ })
209
+ // Replace in-place with placeholder
210
+ doc.content[i] = {
211
+ type: 'inline_child_placeholder',
212
+ attrs: { refId },
213
+ }
214
+ }
215
+ }
216
+
217
+ return inlineChildren
218
+ }
219
+
181
220
  /**
182
221
  * Check if a file is a markdown file that should be processed.
183
222
  * Excludes:
@@ -579,6 +618,9 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
579
618
  // Convert markdown to ProseMirror
580
619
  const proseMirrorContent = markdownToProseMirror(markdown)
581
620
 
621
+ // Extract @ component references → inline children (mutates doc)
622
+ const inlineChildren = extractInlineChildren(proseMirrorContent)
623
+
582
624
  // Support 'data:' shorthand for collection fetch
583
625
  // data: team → fetch: { collection: team }
584
626
  // data: [team, articles] → fetch: { collection: team } (first item, others via inheritData)
@@ -601,6 +643,7 @@ async function processMarkdownFile(filePath, id, siteRoot, defaultStableId = nul
601
643
  params: { ...params, ...props },
602
644
  content: proseMirrorContent,
603
645
  fetch: parseFetchConfig(resolvedFetch),
646
+ ...(inlineChildren.length > 0 ? { inlineChildren } : {}),
604
647
  subsections: []
605
648
  }
606
649
 
@@ -1822,7 +1865,8 @@ export {
1822
1865
  extractItemName,
1823
1866
  parseWildcardArray,
1824
1867
  applyWildcardOrder,
1825
- getDirectChildName
1868
+ getDirectChildName,
1869
+ extractInlineChildren
1826
1870
  }
1827
1871
 
1828
1872
  export default collectSiteContent
@@ -16,64 +16,82 @@ import { generatePalettes, formatOklch } from './shade-generator.js'
16
16
  // These map abstract concepts to specific palette values
17
17
  const DEFAULT_CONTEXT_TOKENS = {
18
18
  light: {
19
- 'bg': 'var(--neutral-50)',
20
- 'bg-subtle': 'var(--neutral-100)',
21
- 'bg-muted': 'var(--neutral-200)',
22
- 'text': 'var(--neutral-950)',
23
- 'text-muted': 'var(--neutral-600)',
24
- 'text-subtle': 'var(--neutral-500)',
19
+ 'section': 'var(--neutral-50)',
20
+ 'card': 'var(--neutral-100)',
21
+ 'muted': 'var(--neutral-200)',
22
+ 'body': 'var(--neutral-950)',
25
23
  'heading': 'var(--neutral-900)',
26
- 'link': 'var(--primary-600)',
27
- 'link-hover': 'var(--primary-700)',
24
+ 'subtle': 'var(--neutral-600)',
28
25
  'border': 'var(--neutral-200)',
29
- 'border-muted': 'var(--neutral-100)',
30
26
  'ring': 'var(--primary-500)',
31
- 'btn-primary-bg': 'var(--primary-600)',
32
- 'btn-primary-text': 'white',
33
- 'btn-primary-hover': 'var(--primary-700)',
34
- 'btn-secondary-bg': 'var(--neutral-100)',
35
- 'btn-secondary-text': 'var(--neutral-900)',
36
- 'btn-secondary-hover': 'var(--neutral-200)',
27
+ 'link': 'var(--primary-600)',
28
+ 'link-hover': 'var(--primary-700)',
29
+ 'primary': 'var(--primary-600)',
30
+ 'primary-foreground': 'white',
31
+ 'primary-hover': 'var(--primary-700)',
32
+ 'secondary': 'var(--neutral-100)',
33
+ 'secondary-foreground': 'var(--neutral-900)',
34
+ 'secondary-hover': 'var(--neutral-200)',
35
+ 'success': '#16a34a',
36
+ 'success-subtle': '#f0fdf4',
37
+ 'warning': '#d97706',
38
+ 'warning-subtle': '#fffbeb',
39
+ 'error': '#dc2626',
40
+ 'error-subtle': '#fef2f2',
41
+ 'info': '#2563eb',
42
+ 'info-subtle': '#eff6ff',
37
43
  },
38
44
  medium: {
39
- 'bg': 'var(--neutral-100)',
40
- 'bg-subtle': 'var(--neutral-200)',
41
- 'bg-muted': 'var(--neutral-300)',
42
- 'text': 'var(--neutral-950)',
43
- 'text-muted': 'var(--neutral-700)',
44
- 'text-subtle': 'var(--neutral-600)',
45
+ 'section': 'var(--neutral-100)',
46
+ 'card': 'var(--neutral-200)',
47
+ 'muted': 'var(--neutral-300)',
48
+ 'body': 'var(--neutral-950)',
45
49
  'heading': 'var(--neutral-900)',
46
- 'link': 'var(--primary-600)',
47
- 'link-hover': 'var(--primary-700)',
50
+ 'subtle': 'var(--neutral-700)',
48
51
  'border': 'var(--neutral-300)',
49
- 'border-muted': 'var(--neutral-200)',
50
52
  'ring': 'var(--primary-500)',
51
- 'btn-primary-bg': 'var(--primary-600)',
52
- 'btn-primary-text': 'white',
53
- 'btn-primary-hover': 'var(--primary-700)',
54
- 'btn-secondary-bg': 'var(--neutral-200)',
55
- 'btn-secondary-text': 'var(--neutral-900)',
56
- 'btn-secondary-hover': 'var(--neutral-300)',
53
+ 'link': 'var(--primary-600)',
54
+ 'link-hover': 'var(--primary-700)',
55
+ 'primary': 'var(--primary-600)',
56
+ 'primary-foreground': 'white',
57
+ 'primary-hover': 'var(--primary-700)',
58
+ 'secondary': 'var(--neutral-200)',
59
+ 'secondary-foreground': 'var(--neutral-900)',
60
+ 'secondary-hover': 'var(--neutral-300)',
61
+ 'success': '#16a34a',
62
+ 'success-subtle': '#f0fdf4',
63
+ 'warning': '#d97706',
64
+ 'warning-subtle': '#fffbeb',
65
+ 'error': '#dc2626',
66
+ 'error-subtle': '#fef2f2',
67
+ 'info': '#2563eb',
68
+ 'info-subtle': '#eff6ff',
57
69
  },
58
70
  dark: {
59
- 'bg': 'var(--neutral-900)',
60
- 'bg-subtle': 'var(--neutral-800)',
61
- 'bg-muted': 'var(--neutral-700)',
62
- 'text': 'var(--neutral-50)',
63
- 'text-muted': 'var(--neutral-300)',
64
- 'text-subtle': 'var(--neutral-400)',
71
+ 'section': 'var(--neutral-900)',
72
+ 'card': 'var(--neutral-800)',
73
+ 'muted': 'var(--neutral-700)',
74
+ 'body': 'var(--neutral-50)',
65
75
  'heading': 'white',
66
- 'link': 'var(--primary-400)',
67
- 'link-hover': 'var(--primary-300)',
76
+ 'subtle': 'var(--neutral-400)',
68
77
  'border': 'var(--neutral-700)',
69
- 'border-muted': 'var(--neutral-800)',
70
78
  'ring': 'var(--primary-500)',
71
- 'btn-primary-bg': 'var(--primary-500)',
72
- 'btn-primary-text': 'white',
73
- 'btn-primary-hover': 'var(--primary-400)',
74
- 'btn-secondary-bg': 'var(--neutral-800)',
75
- 'btn-secondary-text': 'var(--neutral-100)',
76
- 'btn-secondary-hover': 'var(--neutral-700)',
79
+ 'link': 'var(--primary-400)',
80
+ 'link-hover': 'var(--primary-300)',
81
+ 'primary': 'var(--primary-500)',
82
+ 'primary-foreground': 'white',
83
+ 'primary-hover': 'var(--primary-400)',
84
+ 'secondary': 'var(--neutral-800)',
85
+ 'secondary-foreground': 'var(--neutral-100)',
86
+ 'secondary-hover': 'var(--neutral-700)',
87
+ 'success': '#4ade80',
88
+ 'success-subtle': '#052e16',
89
+ 'warning': '#fbbf24',
90
+ 'warning-subtle': '#451a03',
91
+ 'error': '#f87171',
92
+ 'error-subtle': '#450a0a',
93
+ 'info': '#60a5fa',
94
+ 'info-subtle': '#172554',
77
95
  },
78
96
  }
79
97
 
@@ -82,7 +100,7 @@ const DEFAULT_COLORS = {
82
100
  primary: '#3b82f6', // Blue
83
101
  secondary: '#64748b', // Slate
84
102
  accent: '#8b5cf6', // Purple
85
- neutral: '#71717a', // Zinc
103
+ neutral: '#78716c', // Stone
86
104
  }
87
105
 
88
106
  // Shade levels for CSS variable generation
@@ -134,7 +152,7 @@ function generateContextCSS(context, tokens = {}) {
134
152
 
135
153
  const vars = generateVarDeclarations(mergedTokens)
136
154
 
137
- return `.context-${context} {\n${vars}\n background-color: var(--bg);\n}`
155
+ return `.context-${context} {\n${vars}\n background-color: var(--section);\n}`
138
156
  }
139
157
 
140
158
  /**
@@ -148,17 +166,30 @@ function generateDarkSchemeCSS(config = {}) {
148
166
 
149
167
  // Dark scheme tokens - similar to dark context but at root level
150
168
  const darkTokens = {
151
- 'bg': 'var(--neutral-950)',
152
- 'bg-subtle': 'var(--neutral-900)',
153
- 'bg-muted': 'var(--neutral-800)',
154
- 'text': 'var(--neutral-50)',
155
- 'text-muted': 'var(--neutral-300)',
156
- 'text-subtle': 'var(--neutral-400)',
169
+ 'section': 'var(--neutral-950)',
170
+ 'card': 'var(--neutral-900)',
171
+ 'muted': 'var(--neutral-800)',
172
+ 'body': 'var(--neutral-50)',
157
173
  'heading': 'white',
174
+ 'subtle': 'var(--neutral-400)',
175
+ 'border': 'var(--neutral-800)',
176
+ 'ring': 'var(--primary-500)',
158
177
  'link': 'var(--primary-400)',
159
178
  'link-hover': 'var(--primary-300)',
160
- 'border': 'var(--neutral-800)',
161
- 'border-muted': 'var(--neutral-900)',
179
+ 'primary': 'var(--primary-500)',
180
+ 'primary-foreground': 'white',
181
+ 'primary-hover': 'var(--primary-400)',
182
+ 'secondary': 'var(--neutral-800)',
183
+ 'secondary-foreground': 'var(--neutral-100)',
184
+ 'secondary-hover': 'var(--neutral-700)',
185
+ 'success': '#4ade80',
186
+ 'success-subtle': '#052e16',
187
+ 'warning': '#fbbf24',
188
+ 'warning-subtle': '#451a03',
189
+ 'error': '#f87171',
190
+ 'error-subtle': '#450a0a',
191
+ 'info': '#60a5fa',
192
+ 'info-subtle': '#172554',
162
193
  }
163
194
 
164
195
  const vars = generateVarDeclarations(darkTokens)
@@ -11,6 +11,31 @@
11
11
  import { isValidColor, generatePalettes } from './shade-generator.js'
12
12
  import { getDefaultColors, getDefaultContextTokens } from './css-generator.js'
13
13
 
14
+ /**
15
+ * Named neutral presets mapping to Tailwind gray families
16
+ */
17
+ const NEUTRAL_PRESETS = {
18
+ stone: '#78716c',
19
+ zinc: '#71717a',
20
+ gray: '#6b7280',
21
+ slate: '#64748b',
22
+ neutral: '#737373',
23
+ }
24
+
25
+ /**
26
+ * Default inline text styles (content-author markdown: [text]{emphasis})
27
+ * These reference semantic tokens so they adapt to context automatically
28
+ */
29
+ const DEFAULT_INLINE = {
30
+ emphasis: {
31
+ color: 'var(--link)',
32
+ 'font-weight': '600',
33
+ },
34
+ muted: {
35
+ color: 'var(--subtle)',
36
+ },
37
+ }
38
+
14
39
  /**
15
40
  * Default appearance configuration
16
41
  */
@@ -84,6 +109,11 @@ function validateColors(colors) {
84
109
  continue
85
110
  }
86
111
 
112
+ // Accept neutral preset names (stone, zinc, gray, slate, neutral)
113
+ if (name === 'neutral' && NEUTRAL_PRESETS[value]) {
114
+ continue
115
+ }
116
+
87
117
  if (!isValidColor(value)) {
88
118
  errors.push(`Color "${name}" has invalid value: ${value}`)
89
119
  }
@@ -357,7 +387,12 @@ export function processTheme(rawConfig = {}, options = {}) {
357
387
 
358
388
  // Process colors
359
389
  const defaultColors = getDefaultColors()
360
- const rawColors = rawConfig.colors || {}
390
+ const rawColors = { ...(rawConfig.colors || {}) }
391
+
392
+ // Resolve named neutral presets to hex values
393
+ if (typeof rawColors.neutral === 'string' && NEUTRAL_PRESETS[rawColors.neutral]) {
394
+ rawColors.neutral = NEUTRAL_PRESETS[rawColors.neutral]
395
+ }
361
396
 
362
397
  // Filter to only valid colors (skip invalid ones in non-strict mode)
363
398
  const validColors = {}
@@ -382,7 +417,7 @@ export function processTheme(rawConfig = {}, options = {}) {
382
417
  warnings.push('No primary color specified, using default blue (#3b82f6)')
383
418
  }
384
419
  if (!rawConfig.colors?.neutral) {
385
- warnings.push('No neutral color specified, using default zinc (#71717a)')
420
+ warnings.push('No neutral color specified, using default stone (#78716c)')
386
421
  }
387
422
 
388
423
  // Process contexts
@@ -426,7 +461,8 @@ export function processTheme(rawConfig = {}, options = {}) {
426
461
  const background = rawConfig.background || null
427
462
 
428
463
  // Inline text styles (semantic names → CSS declarations)
429
- const inline = rawConfig.inline || null
464
+ // Merge framework defaults with user overrides (user values win)
465
+ const inline = { ...DEFAULT_INLINE, ...(rawConfig.inline || {}) }
430
466
 
431
467
  const config = {
432
468
  colors, // Raw colors for CSS generator