feat(brand): refresh Cook app identity
* feat(brand): refresh Cook app icons * fix(native): use 食用手册 as app display name * fix(ci): require pnpm 11 for deployments
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 12 KiB |
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
<color name="ic_launcher_background">#07855F</color>
|
||||
</resources>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<resources>
|
||||
<string name="app_name">cook</string>
|
||||
<string name="title_activity_main">cook</string>
|
||||
<string name="app_name">食用手册</string>
|
||||
<string name="title_activity_main">食用手册</string>
|
||||
<string name="package_name">cn.yunyoujun.cook</string>
|
||||
<string name="custom_url_scheme">cn.yunyoujun.cook</string>
|
||||
</resources>
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { CapacitorConfig } from '@capacitor/cli'
|
||||
|
||||
const config: CapacitorConfig = {
|
||||
appId: 'cn.yunyoujun.cook',
|
||||
appName: 'cook',
|
||||
appName: '食用手册',
|
||||
webDir: 'dist',
|
||||
|
||||
server: {
|
||||
|
||||
28
design-qa.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Design QA — 食用手册应用图标
|
||||
|
||||
- Date: 2026-08-17
|
||||
- Visual target: Product Design ideation option 2
|
||||
- Surfaces: Web/PWA, iOS launcher, Android launcher, YunLeFun native authorization sheet
|
||||
|
||||
## Visual fidelity
|
||||
|
||||
- Preserved the selected top-down pot composition, emerald background, cream vessel, and green/red/gold ingredients.
|
||||
- Flattened incidental raster shading into five stable brand colors without changing the silhouette or hierarchy.
|
||||
- Produced a transparent `cook-mark` and a full-square `cook-app-icon` from the same selected artwork.
|
||||
- Shifted the foreground down 16 units on the 1254-unit canvas so its measured vertical bounds are centered at 626.5.
|
||||
|
||||
## Platform checks
|
||||
|
||||
- 16, 24, 32, and 64 px previews keep the pot and three ingredient groups distinguishable.
|
||||
- The full-square source has no precomposed corner mask or shadow; iOS and Android apply their own masks.
|
||||
- iOS Simulator shows the installed name as “食用手册” and the new launcher icon without clipping.
|
||||
- The YunLeFun authorization sheet renders the same PNG at 64 pt without overlap, fallback text, or transparency artifacts.
|
||||
- Web/PWA and iOS PNGs are opaque; the Android adaptive foreground retains transparency over the emerald background layer.
|
||||
|
||||
## Build gates
|
||||
|
||||
- Cook lint, typecheck, targeted authentication tests, icon metadata tests, and static production build passed.
|
||||
- Android `assembleDebug` passed with the local OpenJDK 21 runtime.
|
||||
- YunLeFun/icons build, tests, lint, typecheck, docs build, and package dry run passed.
|
||||
|
||||
final result: passed
|
||||
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 69 KiB |
@@ -5,7 +5,7 @@
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>cook</string>
|
||||
<string>食用手册</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"private": true,
|
||||
"packageManager": "pnpm@11.21.0",
|
||||
"engines": {
|
||||
"node": ">=22.0.0"
|
||||
"node": ">=22.0.0",
|
||||
"pnpm": "11.21.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run convert && npm run generate",
|
||||
|
||||
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
15
public/brand/cook-app-icon.svg
Normal file
|
After Width: | Height: | Size: 25 KiB |
14
public/brand/cook-mark.svg
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 682 B After Width: | Height: | Size: 25 KiB |
BIN
public/icon.ico
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 25 KiB |
57
test/nativeAppMetadata.test.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Buffer } from 'node:buffer'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { resolve } from 'node:path'
|
||||
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
const root = resolve(import.meta.dirname, '..')
|
||||
|
||||
function readPngMetadata(path: string) {
|
||||
const data = readFileSync(resolve(root, path))
|
||||
|
||||
expect(data.subarray(0, 8)).toEqual(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))
|
||||
|
||||
return {
|
||||
colorType: data[25],
|
||||
height: data.readUInt32BE(20),
|
||||
width: data.readUInt32BE(16),
|
||||
}
|
||||
}
|
||||
|
||||
describe('native app metadata', () => {
|
||||
it('uses 食用手册 as the installed app name on iOS and Android', () => {
|
||||
const capacitorConfig = readFileSync(resolve(root, 'capacitor.config.ts'), 'utf8')
|
||||
const iosInfo = readFileSync(resolve(root, 'ios/App/App/Info.plist'), 'utf8')
|
||||
const androidStrings = readFileSync(
|
||||
resolve(root, 'android/app/src/main/res/values/strings.xml'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
expect(capacitorConfig).toContain('appName: \'食用手册\'')
|
||||
expect(iosInfo).toContain('<string>食用手册</string>')
|
||||
expect(androidStrings).toContain('<string name="app_name">食用手册</string>')
|
||||
expect(androidStrings).toContain('<string name="title_activity_main">食用手册</string>')
|
||||
})
|
||||
|
||||
it('ships the selected Cook icon across web, iOS, and Android', () => {
|
||||
const appIcon = readFileSync(resolve(root, 'public/brand/cook-app-icon.svg'), 'utf8')
|
||||
const mark = readFileSync(resolve(root, 'public/brand/cook-mark.svg'), 'utf8')
|
||||
const androidBackground = readFileSync(
|
||||
resolve(root, 'android/app/src/main/res/values/ic_launcher_background.xml'),
|
||||
'utf8',
|
||||
)
|
||||
|
||||
expect(appIcon).toContain('viewBox="0 0 1254 1254"')
|
||||
expect(appIcon).toContain('<path fill="#07855F" d="M0 0h1254v1254H0z"/>')
|
||||
expect(appIcon).toContain('<g transform="translate(0 16)">')
|
||||
expect(mark).not.toContain('M0 0h1254v1254H0z')
|
||||
expect(mark).toContain('<g transform="translate(0 16)">')
|
||||
expect(androidBackground).toContain('#07855F')
|
||||
|
||||
expect(readPngMetadata('public/pwa-192x192.png')).toMatchObject({ width: 192, height: 192, colorType: 2 })
|
||||
expect(readPngMetadata('public/pwa-512x512.png')).toMatchObject({ width: 512, height: 512, colorType: 2 })
|
||||
expect(readPngMetadata('public/apple-touch-icon.png')).toMatchObject({ width: 180, height: 180, colorType: 2 })
|
||||
expect(readPngMetadata('ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png')).toMatchObject({ width: 1024, height: 1024, colorType: 2 })
|
||||
expect(readPngMetadata('android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png')).toMatchObject({ width: 108, height: 108, colorType: 6 })
|
||||
})
|
||||
})
|
||||