Skip to content

Commit 1faef41

Browse files
committed
Keep avatar asset URLs aligned with image bytes
Character and intro avatar files contained JPEG data while being served through .png paths. The assets now use .jpg filenames, data/component references point at those URLs, and the repository verifier checks public image signatures against their extensions so static MIME assumptions stay honest. Constraint: Keep existing image bytes and avoid introducing image-processing dependencies. Rejected: Re-encode all avatars as PNG | unnecessary binary churn and no current image tooling dependency. Confidence: high Scope-risk: narrow Directive: When adding public image assets, ensure file signatures match their URL extensions before committing. Tested: python3 scripts/check_repository.py; npm run verify; npm audit --omit=dev; git diff --check
1 parent ec574b4 commit 1faef41

10 files changed

Lines changed: 47 additions & 12 deletions

File tree

components/chat/screens/IntroView.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export default function IntroView() {
6464
<div className="w-full max-w-sm flex flex-col items-center gap-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
6565
<div className="flex flex-col items-center gap-3">
6666
<div className="relative">
67-
<ImageShim src="/avatar-girl.png" alt="游戏角色" width={90} height={90} className="rounded-2xl object-cover shadow-xl ring-4 ring-white" />
67+
<ImageShim src="/avatar-girl-intro.jpg" alt="游戏角色" width={90} height={90} className="rounded-2xl object-cover shadow-xl ring-4 ring-white" />
6868
<span className="absolute -top-2 -right-2 text-white text-[16px] font-bold rounded-full px-1.5 py-0.5">❤️</span>
6969
</div>
7070

data/girls.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"lin": {
33
"id": "lin",
44
"name": "林晓柔",
5-
"avatar": "/lin.png",
5+
"avatar": "/lin.jpg",
66
"tags": [
77
"段位:🌟🌟",
88
"白银捞女",
@@ -14,7 +14,7 @@
1414
"tan": {
1515
"id": "tan",
1616
"name": "谭佳欣",
17-
"avatar": "/tan.png",
17+
"avatar": "/tan.jpg",
1818
"tags": [
1919
"段位:🌟🌟🌟🌟🌟",
2020
"钻石海王",
@@ -26,7 +26,7 @@
2626
"su": {
2727
"id": "su",
2828
"name": "苏佳怡",
29-
"avatar": "/su.png",
29+
"avatar": "/su.jpg",
3030
"tags": [
3131
"段位:🌟🌟🌟🌟",
3232
"病娇白切黑",
@@ -38,7 +38,7 @@
3838
"shen": {
3939
"id": "shen",
4040
"name": "沈书仪",
41-
"avatar": "/shen.png",
41+
"avatar": "/shen.jpg",
4242
"tags": [
4343
"段位:🌟🌟🌟🌟🌟",
4444
"高阶海后",
@@ -50,7 +50,7 @@
5050
"chen": {
5151
"id": "chen",
5252
"name": "陈盈盈",
53-
"avatar": "/chen.png",
53+
"avatar": "/chen.jpg",
5454
"tags": [
5555
"段位:🌟",
5656
"恋爱脑青梅",

data/summary.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@
66
"asset_paths": [
77
"/avatar-boy.jpg",
88
"/avatar-girl.jpg",
9-
"/avatar-girl.png",
10-
"/chen.png",
11-
"/lin.png",
12-
"/shen.png",
13-
"/su.png",
14-
"/tan.png"
9+
"/avatar-girl-intro.jpg",
10+
"/chen.jpg",
11+
"/lin.jpg",
12+
"/shen.jpg",
13+
"/su.jpg",
14+
"/tan.jpg"
1515
]
1616
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

scripts/check_repository.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
ROOT / 'data' / 'girls.json',
4141
ROOT / 'data' / 'chapters.json',
4242
ROOT / 'data' / 'scenes.json',
43+
ROOT / 'data' / 'summary.json',
4344
ROOT / 'data' / 'tactics.json',
4445
ROOT / 'scripts' / 'verify_visual_baseline.py',
4546
ROOT / 'scripts' / 'verify_ai_integration.mjs',
@@ -54,6 +55,7 @@
5455
girls = json.loads((ROOT / 'data' / 'girls.json').read_text())
5556
chapters = json.loads((ROOT / 'data' / 'chapters.json').read_text())
5657
scenes = json.loads((ROOT / 'data' / 'scenes.json').read_text())
58+
summary = json.loads((ROOT / 'data' / 'summary.json').read_text())
5759
tactics = json.loads((ROOT / 'data' / 'tactics.json').read_text())
5860

5961
env_example = (ROOT / '.env.example').read_text()
@@ -164,13 +166,43 @@
164166
raise SystemExit('chapters.json must be a non-empty object')
165167
if not isinstance(scenes, list) or not scenes:
166168
raise SystemExit('scenes.json must be a non-empty array')
169+
if not isinstance(summary, dict):
170+
raise SystemExit('summary.json must be an object')
167171
if not isinstance(tactics, list) or not tactics:
168172
raise SystemExit('tactics.json must be a non-empty array')
169173

170174
scene_ids = {scene['id'] for scene in scenes}
171175
missing_public_assets: list[str] = []
172176
missing_scene_links: list[str] = []
173177
implicit_silent_choices: list[str] = []
178+
bad_public_image_types: list[str] = []
179+
180+
181+
def public_image_type(path: Path) -> str:
182+
header = path.read_bytes()[:12]
183+
if header.startswith(b'\x89PNG\r\n\x1a\n'):
184+
return 'png'
185+
if header.startswith(b'\xff\xd8\xff'):
186+
return 'jpg'
187+
if header.startswith(b'\x00\x00\x01\x00'):
188+
return 'ico'
189+
return 'unknown'
190+
191+
192+
for public_path in PUBLIC_ROOT.iterdir():
193+
if not public_path.is_file():
194+
continue
195+
suffix = public_path.suffix.lower()
196+
if suffix not in {'.png', '.jpg', '.jpeg', '.ico'}:
197+
continue
198+
detected_type = public_image_type(public_path)
199+
expected_type = 'jpg' if suffix in {'.jpg', '.jpeg'} else suffix.lstrip('.')
200+
if detected_type != expected_type:
201+
bad_public_image_types.append(f'{public_path.relative_to(ROOT)} is {detected_type}, expected {expected_type}')
202+
203+
for asset_path in summary.get('asset_paths', []):
204+
if isinstance(asset_path, str) and asset_path.startswith('/') and not (PUBLIC_ROOT / asset_path.lstrip('/')).exists():
205+
missing_public_assets.append(asset_path)
174206

175207
for girl_id, girl in girls.items():
176208
avatar = girl.get('avatar', '')
@@ -206,6 +238,9 @@
206238
if missing_public_assets:
207239
raise SystemExit('Missing public assets: ' + ', '.join(sorted(set(missing_public_assets))))
208240

241+
if bad_public_image_types:
242+
raise SystemExit('Public image extensions must match file signatures: ' + '; '.join(bad_public_image_types))
243+
209244
if missing_scene_links:
210245
preview = ', '.join(missing_scene_links[:10])
211246
raise SystemExit(f'Broken scene links detected ({len(missing_scene_links)}): {preview}')

0 commit comments

Comments
 (0)