uniweb 1.21.19 → 1.21.20

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": "1.21.19",
3
+ "version": "1.21.20",
4
4
  "description": "Create structured Vite + React sites with content/code separation",
5
5
  "type": "module",
6
6
  "bin": {
@@ -41,15 +41,15 @@
41
41
  "js-yaml": "^4.3.2",
42
42
  "prompts": "^2.4.2",
43
43
  "tar": "^7.5.21",
44
- "@uniweb/schemas": "~1.6.5",
44
+ "@uniweb/content-writer": "~1.3.1",
45
45
  "@uniweb/core": "~1.4.12",
46
- "@uniweb/semantic-parser": "~1.13.0",
47
- "@uniweb/content-writer": "~1.3.1"
46
+ "@uniweb/schemas": "~1.6.5",
47
+ "@uniweb/semantic-parser": "~1.13.0"
48
48
  },
49
49
  "peerDependencies": {
50
- "@uniweb/semantic-parser": "~1.13.0",
50
+ "@uniweb/build": "~1.10.13",
51
51
  "@uniweb/content-reader": "~1.5.7",
52
- "@uniweb/build": "~1.10.12"
52
+ "@uniweb/semantic-parser": "~1.13.0"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@uniweb/build": {
@@ -733,6 +733,8 @@ end the list with `...`.**
733
733
 
734
734
  **The tab icon:** `favicon: /favicon.svg` in `site.yml`, a file in `public/`. Name it even when the file is `public/favicon.svg`: a static build finds `public/favicon.{svg,ico,png}` on its own, but a site published with `uniweb publish` links only the favicon `site.yml` names (`uniweb doctor --fix favicon-unnamed` writes the line).
735
735
 
736
+ **The home-screen icon:** `touchIcon: /touch-icon.png` in `site.yml` — the icon iOS puts on a home screen, which an SVG favicon never is. Make it a 180×180 PNG in `public/` with no transparency: iOS draws a transparent pixel black. Name it even when the file is `public/apple-touch-icon.png`, which iOS finds on a static host by itself: a site published with `uniweb publish` has only the touch icon `site.yml` names. `uniweb doctor` checks the file.
737
+
736
738
  ```yaml
737
739
  # site.yml
738
740
  keywords: [components, react, cms]
@@ -1307,6 +1307,92 @@ export async function checkFavicon({ sitePath, siteName, siteYml, issues, should
1307
1307
  log(` Add ${colors.green}favicon: ${value}${colors.reset} to site.yml — ${colors.dim}uniweb doctor --fix ${id}${colors.reset}`)
1308
1308
  }
1309
1309
 
1310
+ // The icon iOS puts on a home screen, from Apple's guidance (the archived Safari Web Content Guide,
1311
+ // read 2026-10-27): a PNG — an SVG favicon is not used — 180×180 for the largest iPhone screens,
1312
+ // with no transparency, which iOS draws black. A page that links none, iOS asks the site's root for
1313
+ // `/apple-touch-icon.png`.
1314
+ const TOUCH_ICON_SIZE = 180
1315
+ const TOUCH_ICON_FILE = 'apple-touch-icon.png'
1316
+
1317
+ /**
1318
+ * The touch icon: `site.yml`'s `touchIcon:`. A static build links the one it names, and a push
1319
+ * sends that file and no other, so a Uniweb host has none the site does not name — not even the
1320
+ * `/apple-touch-icon.png` iOS asks a static host for. A named one is measured: a PNG, square, at
1321
+ * least 180×180, every pixel opaque.
1322
+ */
1323
+ export async function checkTouchIcon({ sitePath, siteName, siteYml, issues, shouldFix, fixed: reportFixed }) {
1324
+ const named = typeof siteYml?.touchIcon === 'string' ? siteYml.touchIcon.trim() : ''
1325
+ if (!named) {
1326
+ if (!existsSync(join(sitePath, 'public', TOUCH_ICON_FILE))) {
1327
+ const id = 'touch-icon-none'
1328
+ issues.push({ id, type: 'info', site: siteName, message: 'site.yml names no touch icon, the icon iOS puts on a home screen' })
1329
+ info(`[${id}] site.yml names no touch icon — the icon iOS puts on a home screen and Safari shows on its start page`)
1330
+ log(` Add a ${TOUCH_ICON_SIZE}×${TOUCH_ICON_SIZE} PNG with no transparency to public/ and name it: ${colors.green}touchIcon: /touch-icon.png${colors.reset}`)
1331
+ return
1332
+ }
1333
+ const id = 'touch-icon-unnamed'
1334
+ const value = `/${TOUCH_ICON_FILE}`
1335
+ const issue = {
1336
+ id,
1337
+ type: 'info',
1338
+ site: siteName,
1339
+ message: `public/${TOUCH_ICON_FILE} is not named in site.yml, so a Uniweb host has no touch icon`,
1340
+ fixable: true
1341
+ }
1342
+ issues.push(issue)
1343
+ info(`[${id}] public/${TOUCH_ICON_FILE} is not named in site.yml — iOS finds it on a static host, but a Uniweb host is never sent it`)
1344
+ if (shouldFix?.(id)) {
1345
+ try {
1346
+ writeSiteConfig(sitePath, { touchIcon: value })
1347
+ issue.fixed = true
1348
+ reportFixed?.(`site.yml now names touchIcon: ${value}`)
1349
+ return
1350
+ } catch (err) {
1351
+ warn(`[${id}] could not write site.yml: ${err.message}`)
1352
+ }
1353
+ }
1354
+ log(` Add ${colors.green}touchIcon: ${value}${colors.reset} to site.yml — ${colors.dim}uniweb doctor --fix ${id}${colors.reset}`)
1355
+ return
1356
+ }
1357
+
1358
+ if (!isSitePath(named)) return
1359
+ const file = siteFile(sitePath, named)
1360
+ if (!file) {
1361
+ const id = 'touch-icon-missing'
1362
+ issues.push({ id, type: 'warn', site: siteName, message: `touchIcon: ${named} is not in public/ or assets/` })
1363
+ warn(`[${id}] touchIcon: ${named} is not in public/ or assets/ — every page links an icon that is not there`)
1364
+ return
1365
+ }
1366
+ const { readImageSize, isImageOpaque } = await import('@uniweb/build/images')
1367
+ const size = await readImageSize(file)
1368
+ if (!size || size.format !== 'png') {
1369
+ const id = 'touch-icon-format'
1370
+ const what = !size ? 'could not be read as an image' : `is ${size.format.toUpperCase()}, not a PNG`
1371
+ issues.push({ id, type: 'warn', site: siteName, message: `touchIcon: ${named} ${what}` })
1372
+ warn(`[${id}] touchIcon: ${named} ${what} — iOS takes a PNG`)
1373
+ return
1374
+ }
1375
+ if ((await isImageOpaque(file)) === false) {
1376
+ const id = 'touch-icon-transparent'
1377
+ issues.push({ id, type: 'warn', site: siteName, message: `touchIcon: ${named} has transparent pixels, which iOS draws black` })
1378
+ warn(`[${id}] touchIcon: ${named} has transparent pixels — iOS draws them black`)
1379
+ log(` Give the icon a background of its own.`)
1380
+ }
1381
+ const { width, height } = size
1382
+ const advice =
1383
+ width !== height
1384
+ ? 'not square, as a touch icon is'
1385
+ : width < TOUCH_ICON_SIZE
1386
+ ? `under ${TOUCH_ICON_SIZE}×${TOUCH_ICON_SIZE}, iPhone screens draw it soft`
1387
+ : null
1388
+ if (advice) {
1389
+ const id = 'touch-icon-shape'
1390
+ issues.push({ id, type: 'info', site: siteName, message: `touchIcon: ${named} is ${width}×${height}: ${advice}` })
1391
+ info(`[${id}] touchIcon: ${named} is ${width}×${height}: ${advice}`)
1392
+ log(` A ${TOUCH_ICON_SIZE}×${TOUCH_ICON_SIZE} PNG is the size iOS asks for.`)
1393
+ }
1394
+ }
1395
+
1310
1396
  /** The bare map in `queries.yml`, or `{}` when there is none. */
1311
1397
  function readQueriesYml(sitePath) {
1312
1398
  try {
@@ -1836,9 +1922,10 @@ async function runDoctor(args = []) {
1836
1922
  }
1837
1923
 
1838
1924
  await checkSiteAddress({ sitePath, siteName, siteYml, workspaceDir, issues })
1839
- // What a link to the site shows, and its tab icon.
1925
+ // What a link to the site shows, its tab icon, and its home-screen icon.
1840
1926
  await checkLinkPreviews({ sitePath, siteName, issues })
1841
1927
  await checkFavicon({ sitePath, siteName, siteYml, issues, shouldFix, fixed })
1928
+ await checkTouchIcon({ sitePath, siteName, siteYml, issues, shouldFix, fixed })
1842
1929
 
1843
1930
  // Forms need a destination, and having none is only visible on the page.
1844
1931
  await checkFormSubmitTarget({ sitePath, siteName, siteYml, issues })
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-10-28T03:03:01.883Z",
3
+ "generatedAt": "2026-10-28T04:08:25.023Z",
4
4
  "packages": {
5
5
  "@uniweb/api": {
6
6
  "version": "1.6.4"
7
7
  },
8
8
  "@uniweb/build": {
9
- "version": "1.10.12"
9
+ "version": "1.10.13"
10
10
  },
11
11
  "@uniweb/content-reader": {
12
12
  "version": "1.5.7"