uniweb 0.8.25 → 0.8.27

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.25",
3
+ "version": "0.8.27",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,12 +41,12 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.0.0",
44
- "@uniweb/core": "0.5.15",
45
- "@uniweb/runtime": "0.6.20",
46
- "@uniweb/kit": "0.7.16"
44
+ "@uniweb/core": "0.5.16",
45
+ "@uniweb/kit": "0.7.17",
46
+ "@uniweb/runtime": "0.6.22"
47
47
  },
48
48
  "peerDependencies": {
49
- "@uniweb/build": "0.8.24",
49
+ "@uniweb/build": "0.8.26",
50
50
  "@uniweb/content-reader": "1.1.4",
51
51
  "@uniweb/semantic-parser": "1.1.7"
52
52
  },
@@ -138,6 +138,15 @@ The decision rule: **would a content author need to change this?** Yes → it be
138
138
 
139
139
  Start with the content, not the component. Write the markdown a content author would naturally write, check what content shape the parser produces, *then* build the component to receive it.
140
140
 
141
+ **Markdown order ≠ rendering order.** The parser extracts content into a flat structure (`title`, `icons`, `images`, `paragraphs`). The component decides how to arrange these visually. Don't write markdown in visual order — write it in semantic order. Start sections with the heading, then add icons, images, and text in any order:
142
+
143
+ ```markdown
144
+ # Site Name ← title — always start with the heading
145
+ ![](lu-graduation-cap) ← icon — component controls where this renders
146
+ ```
147
+
148
+ Placing content *before* the first heading changes the parse: headings after body content become items, not the section title. This is by design — it's how repeating content groups (cards, features) are created.
149
+
141
150
  ### Section Format
142
151
 
143
152
  Each `.md` file is a section. Frontmatter on top, content below:
@@ -173,7 +182,7 @@ content = {
173
182
  pretitle: '', // Heading before main title (auto-detected)
174
183
  subtitle: '', // Heading after title (string or string[] for multi-line)
175
184
  paragraphs: [], // Text blocks
176
- links: [], // { href, label, role } — standalone links become buttons
185
+ links: [], // { href, label, role } — standalone links (not inside lists)
177
186
  images: [], // { src, alt, role, href }
178
187
  icons: [], // { library, name, role }
179
188
  videos: [], // { src, alt, role, poster, href }
@@ -249,6 +258,20 @@ Enterprise-grade security. │ content.items[1].paragraphs[0] = "Enterprise
249
258
 
250
259
  Headings before the main title become `pretitle`. Headings after the main title at a lower importance become `subtitle`. Headings that appear after body content (paragraphs, links, images) start the `items` array.
251
260
 
261
+ **Subtitle vs items:** A heading immediately after the title (no body content between them) becomes `subtitle`, not an item. If you want items without a subtitle, use a `---` divider or a paragraph to close the title group:
262
+
263
+ ```markdown
264
+ # Our Stats │ content.title = "Our Stats"
265
+ --- │ ← divider closes the title group
266
+ ## 15,000+ │ content.items[0].title = "15,000+"
267
+ Students from 90 countries │ content.items[0].paragraphs[0]
268
+
269
+ ## 200+ │ content.items[1].title = "200+"
270
+ Programs offered │ content.items[1].paragraphs[0]
271
+ ```
272
+
273
+ Without the `---`, `## 15,000+` would become `content.subtitle` instead of an item.
274
+
252
275
  ### Choosing how to model content
253
276
 
254
277
  You have three layers. Most of the design skill is choosing between them:
@@ -477,6 +500,8 @@ export default function Grid({ block, params }) {
477
500
  }
478
501
  ```
479
502
 
503
+ **Data and child blocks:** Page-level `data:` is available to all blocks on the page, including children. Each child block resolves data independently through the same page → site hierarchy. If a child component needs data, declare it in the child's `meta.js` or in the child section's frontmatter (`data: articles`).
504
+
480
505
  ### Section Backgrounds
481
506
 
482
507
  Set `background` in frontmatter — the runtime renders it automatically:
@@ -529,6 +554,7 @@ id: about # Stable identity (for page: links, survives moves)
529
554
  order: 2 # Navigation sort position
530
555
  pages: [team, history, ...] # Child page order (... = rest). Without ... = strict (hides unlisted)
531
556
  index: getting-started # Which child page is the index
557
+ redirect: academic # Redirect to child page (relative or absolute path, or URL)
532
558
  ```
533
559
 
534
560
  **site.yml:**
@@ -764,6 +790,7 @@ All non-reserved frontmatter fields become `params`. Reserved: `type`, `preset`,
764
790
  | `block.insets` | Block[] | Inline `@Component` references |
765
791
  | `block.getInset(refId)` | Block | Lookup inset by refId |
766
792
  | `block.properties` | object | Raw frontmatter |
793
+ | `block.rawContent` | object | ProseMirror document — passed internally when using `<Article block={block} />` |
767
794
  | `block.themeName` | string | `"light"`, `"medium"`, `"dark"` |
768
795
  | `block.stableId` | string | Stable ID from filename or `id:` |
769
796
 
@@ -817,10 +844,10 @@ Kit provides `H1` through `H6` — use the appropriate level for semantic hierar
817
844
  **Full content rendering** (article/docs sections where the author controls the flow):
818
845
 
819
846
  ```jsx
820
- import { Section, Render } from '@uniweb/kit'
847
+ import { Section, Article } from '@uniweb/kit'
821
848
 
822
849
  <Section block={block} width="lg" padding="md" />
823
- <Render content={block.parsedContent} block={block} />
850
+ <Article block={block} />
824
851
  ```
825
852
 
826
853
  **Visuals:**
@@ -835,7 +862,7 @@ import { Visual } from '@uniweb/kit'
835
862
 
836
863
  **Rendering text:** `H1`–`H6`, `P`, `Span`, `Div`, `Text` (with `as` prop)
837
864
 
838
- **Rendering content:** `Section` (Render + prose + layout), `Render` (ProseMirror → React), `ChildBlocks` (render child sections)
865
+ **Rendering content:** `Section` (full section with prose + layout), `Article` (prose content from `block.rawContent`), `Render` (ProseMirror nodes → React), `ChildBlocks` (render child sections)
839
866
 
840
867
  **Rendering media:** `Visual` (first non-empty: inset/video/image), `Image`, `Media`, `Icon`
841
868
 
@@ -867,15 +894,20 @@ useColorContext(block) → 'light' | 'medium' | 'dark' // current section cont
867
894
 
868
895
  ### Icon Component
869
896
 
870
- The `<Icon>` renders icons from content or explicit props. The simplest usage — spread an icon object from content:
897
+ The `<Icon>` renders icons from content or explicit props:
871
898
 
872
899
  ```jsx
873
- {content.icons.map((icon, i) => <Icon key={i} {...icon} />)}
900
+ {content.icons.map((icon, i) => <Icon key={i} {...icon} />)} // From content
901
+ <Icon name="search" /> // Lucide (default)
902
+ <Icon name="hi2-arrow-right" /> // Other library
903
+ <Icon name="close" /> // Built-in (no network)
874
904
  ```
875
905
 
876
- Props: `library` + `name` (from content), `svg` (direct SVG string), `url` (fetch from URL), `size` (default `'24'`), `className`. The legacy `icon` prop accepts shorthand strings (`"lu-house"`) or objects.
906
+ The `name` prop handles everything: built-in names, Lucide icons (default when no library prefix), and other libraries via prefix (`hi2-arrow-right`, `tb-star`). From content, spread the icon object which has `library` + `name` fields.
907
+
908
+ Other props: `svg` (direct SVG string), `url` (fetch from URL), `size` (default `'24'`), `className`.
877
909
 
878
- Built-in icons (no library needed): `check`, `close`, `menu`, `chevronDown`, `chevronRight`, `externalLink`, `download`, `play`, and a few others.
910
+ Built-in icons (instant, no network): `check`, `close`, `menu`, `chevronDown`, `chevronRight`, `externalLink`, `download`, `play`, and a few others.
879
911
 
880
912
  ### Content Patterns for Header and Footer
881
913
 
@@ -841,6 +841,16 @@ async function runAudit(siteRoot, config, args) {
841
841
  log(`\n${colors.dim}Run with --clean to remove stale entries.${colors.reset}`)
842
842
  }
843
843
  }
844
+
845
+ // Report entries that need inline tag updates
846
+ const needsTagsTotal = results.reduce((sum, r) => sum + (r.needsTags?.length || 0), 0)
847
+ if (needsTagsTotal > 0) {
848
+ log(`\n${colors.yellow}${needsTagsTotal} translation(s) have inline marks in the source but not in the translation.`)
849
+ log(`These translations won't preserve accent/span styling.${colors.reset}`)
850
+ if (!verbose) {
851
+ log(`${colors.dim}Run with --verbose to see which entries are affected.${colors.reset}`)
852
+ }
853
+ }
844
854
  } catch (err) {
845
855
  error(`Audit failed: ${err.message}`)
846
856
  process.exit(1)
package/src/index.js CHANGED
@@ -727,19 +727,32 @@ async function main() {
727
727
 
728
728
  // Initialize git repository
729
729
  if (!noGit) {
730
+ // Skip git init if already inside a git repo (common for monorepos/workspaces)
731
+ let insideGitRepo = false
730
732
  try {
731
- execSync('git --version', { stdio: 'ignore' })
733
+ execSync('git rev-parse --is-inside-work-tree', { cwd: projectDir, stdio: 'ignore' })
734
+ insideGitRepo = true
735
+ } catch {
736
+ // Not inside a git repo — proceed with init
737
+ }
738
+
739
+ if (insideGitRepo) {
740
+ log(` ${colors.dim}Skipping git init — already inside a git repository${colors.reset}`)
741
+ } else {
732
742
  try {
733
- execSync('git init', { cwd: projectDir, stdio: 'ignore' })
734
- execSync('git add -A', { cwd: projectDir, stdio: 'ignore' })
735
- execSync('git commit -m "Initial commit from uniweb"', { cwd: projectDir, stdio: 'ignore' })
736
- success('Git repository initialized')
743
+ execSync('git --version', { stdio: 'ignore' })
744
+ try {
745
+ execSync('git init', { cwd: projectDir, stdio: 'ignore' })
746
+ execSync('git add -A', { cwd: projectDir, stdio: 'ignore' })
747
+ execSync('git commit -m "Initial commit from uniweb"', { cwd: projectDir, stdio: 'ignore' })
748
+ success('Git repository initialized')
749
+ } catch {
750
+ log(` ${colors.yellow}Warning: Git repository initialized but initial commit failed${colors.reset}`)
751
+ log(` ${colors.dim}Run 'git commit -m "Initial commit"' after configuring git${colors.reset}`)
752
+ }
737
753
  } catch {
738
- log(` ${colors.yellow}Warning: Git repository initialized but initial commit failed${colors.reset}`)
739
- log(` ${colors.dim}Run 'git commit -m "Initial commit"' after configuring git${colors.reset}`)
754
+ // git not available skip silently
740
755
  }
741
- } catch {
742
- // git not available — skip silently
743
756
  }
744
757
  }
745
758