uniweb 1.21.18 → 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 +4 -4
- package/partials/agents.md +2 -0
- package/src/commands/doctor.js +88 -1
- package/src/framework-index.json +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "uniweb",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.20",
|
|
4
4
|
"description": "Create structured Vite + React sites with content/code separation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
"prompts": "^2.4.2",
|
|
43
43
|
"tar": "^7.5.21",
|
|
44
44
|
"@uniweb/content-writer": "~1.3.1",
|
|
45
|
-
"@uniweb/semantic-parser": "~1.13.0",
|
|
46
45
|
"@uniweb/core": "~1.4.12",
|
|
47
|
-
"@uniweb/schemas": "~1.6.5"
|
|
46
|
+
"@uniweb/schemas": "~1.6.5",
|
|
47
|
+
"@uniweb/semantic-parser": "~1.13.0"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
|
-
"@uniweb/build": "~1.10.
|
|
50
|
+
"@uniweb/build": "~1.10.13",
|
|
51
51
|
"@uniweb/content-reader": "~1.5.7",
|
|
52
52
|
"@uniweb/semantic-parser": "~1.13.0"
|
|
53
53
|
},
|
package/partials/agents.md
CHANGED
|
@@ -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]
|
package/src/commands/doctor.js
CHANGED
|
@@ -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
|
|
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 })
|
package/src/framework-index.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-10-
|
|
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.
|
|
9
|
+
"version": "1.10.13"
|
|
10
10
|
},
|
|
11
11
|
"@uniweb/content-reader": {
|
|
12
12
|
"version": "1.5.7"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"version": "1.1.4"
|
|
34
34
|
},
|
|
35
35
|
"@uniweb/projections": {
|
|
36
|
-
"version": "1.2.
|
|
36
|
+
"version": "1.2.7"
|
|
37
37
|
},
|
|
38
38
|
"@uniweb/runtime": {
|
|
39
39
|
"version": "1.2.12"
|