压缩图片
This commit is contained in:
212
docs/superpowers/plans/2026-04-14-image-optimization-redesign.md
Normal file
212
docs/superpowers/plans/2026-04-14-image-optimization-redesign.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Image Optimization Redesign Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Restore the prior image/code state, create an `assets_backup` copy, and re-run image optimization so only materially beneficial conversions are adopted while oversized images are compressed to a better size/quality point.
|
||||
|
||||
**Architecture:** Revert only the previous assistant-generated changes back to the repository baseline, then operate from that clean baseline with a standalone Python optimizer script. The script will evaluate candidates per file, keep the better format, update references only for accepted conversions, and emit a report for verification.
|
||||
|
||||
**Tech Stack:** Python 3, `unittest`, ImageMagick `magick`, git, static HTML/CSS/JS assets
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Restore Baseline And Create Backup
|
||||
|
||||
**Files:**
|
||||
- Create: `assets_backup/**`
|
||||
- Modify: `.gitignore`
|
||||
- Modify: `assets/css/about.css`
|
||||
- Modify: `assets/css/css.css`
|
||||
- Modify: `assets/css/history.css`
|
||||
- Modify: `assets/css/style.css`
|
||||
- Modify: `*.html`
|
||||
- Modify: `classiccase/*.html`
|
||||
- Modify: `assets/images/**/*.{png,jpg,jpeg,gif}`
|
||||
- Delete: `assets/images/**/*.webp`
|
||||
- Delete: `huace/arrow.webp`
|
||||
- Delete: `backup/original-images-20260414-155153.tar.gz`
|
||||
- Delete: `scripts/optimize_images.py`
|
||||
|
||||
- [ ] **Step 1: Capture the assistant-created delta**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
git diff --name-only
|
||||
```
|
||||
Expected: only the previously assistant-modified image, html, css, `.gitignore`, and generated `webp`/script artifacts are listed.
|
||||
|
||||
- [ ] **Step 2: Restore tracked files to repository baseline**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
git restore --source=HEAD -- .gitignore assets/css/about.css assets/css/css.css assets/css/history.css assets/css/style.css changzhan.html chuandai.html classiccase.html classiccase/ index.html contact.html company.html company2.html culture.html dianshang.html footer.html header.html ilog.html jining.html joinus.html new_1.html new_2.html new_3.html new_5.html new_6.html product_Logistics.html product_eCommerce.html product_ecommerce_bctp.html product_ecommerce_freight_booking.html product_ecommerce_net_cargo.html product_ecommerce_obh.html product_full_logistics_chain_cargo_agent.html product_full_logistics_chain_platform.html product_full_logistics_chain_ship_agent.html product_full_logistics_chain_vehicle_transport.html product_full_logistics_chain_warehouse.html product_port.html product_port_cdi.html product_port_cdi_bulk.html product_port_cdi_container.html product_shipping.html product_shipping_boat.html product_shipping_company.html solution_inland_river_shipping.html solution_logistics_chain.html solution_port_supply_chain.html solution_supply_chain.html tech.html yunmatou.html assets/images huace/arrow.png
|
||||
```
|
||||
Expected: tracked files revert to `HEAD`.
|
||||
|
||||
- [ ] **Step 3: Remove generated artifacts**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
find assets/images -type f -name '*.webp' -delete
|
||||
rm -f huace/arrow.webp backup/original-images-20260414-155153.tar.gz scripts/optimize_images.py
|
||||
```
|
||||
Expected: previously generated `webp` files and old helper artifacts are removed.
|
||||
|
||||
- [ ] **Step 4: Create a full assets backup**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
rm -rf assets_backup
|
||||
cp -a assets assets_backup
|
||||
```
|
||||
Expected: `assets_backup` exists as a byte-for-byte working backup of the restored baseline assets tree.
|
||||
|
||||
- [ ] **Step 5: Verify the backup and restore state**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
git status --short
|
||||
find assets_backup -maxdepth 2 -type d | sort | sed -n '1,20p'
|
||||
```
|
||||
Expected: only `assets_backup/` is untracked before the new optimization work begins.
|
||||
|
||||
### Task 2: Add Red-Green Coverage For Selection Rules
|
||||
|
||||
**Files:**
|
||||
- Create: `tests/test_optimize_assets.py`
|
||||
- Create: `scripts/optimize_assets.py`
|
||||
|
||||
- [ ] **Step 1: Write the failing tests**
|
||||
|
||||
```python
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from scripts.optimize_assets import should_keep_conversion, classify_large_image
|
||||
|
||||
|
||||
class OptimizeAssetsRulesTest(unittest.TestCase):
|
||||
def test_rejects_webp_when_not_smaller_than_source(self) -> None:
|
||||
self.assertFalse(should_keep_conversion(120_000, 123_000))
|
||||
|
||||
def test_accepts_materially_smaller_webp_for_large_file(self) -> None:
|
||||
self.assertTrue(should_keep_conversion(500_000, 430_000))
|
||||
|
||||
def test_flags_large_by_filesize(self) -> None:
|
||||
self.assertTrue(classify_large_image(301_000, 1200, 900))
|
||||
|
||||
def test_flags_large_by_dimensions(self) -> None:
|
||||
self.assertTrue(classify_large_image(120_000, 3000, 1800))
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the tests to confirm RED**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python3 -m unittest tests.test_optimize_assets -v
|
||||
```
|
||||
Expected: FAIL with import or symbol errors because `scripts/optimize_assets.py` does not exist yet.
|
||||
|
||||
- [ ] **Step 3: Write the minimal implementation**
|
||||
|
||||
```python
|
||||
def should_keep_conversion(source_size: int, candidate_size: int) -> bool:
|
||||
savings = source_size - candidate_size
|
||||
if savings <= 0:
|
||||
return False
|
||||
if source_size < 150 * 1024:
|
||||
return savings >= 8 * 1024
|
||||
return (savings / source_size) >= 0.05
|
||||
|
||||
|
||||
def classify_large_image(source_size: int, width: int, height: int) -> bool:
|
||||
return source_size > 300 * 1024 or max(width, height) > 2560
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the tests to confirm GREEN**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python3 -m unittest tests.test_optimize_assets -v
|
||||
```
|
||||
Expected: PASS with 4 tests run successfully.
|
||||
|
||||
### Task 3: Implement The Optimizer And Rewrite References
|
||||
|
||||
**Files:**
|
||||
- Modify: `scripts/optimize_assets.py`
|
||||
- Test: `tests/test_optimize_assets.py`
|
||||
- Modify: `assets/css/**/*.css`
|
||||
- Modify: `*.html`
|
||||
- Modify: `classiccase/*.html`
|
||||
- Modify: `product_*.html`
|
||||
- Modify: `solution_*.html`
|
||||
|
||||
- [ ] **Step 1: Expand tests for path rewriting and candidate selection**
|
||||
|
||||
```python
|
||||
def test_prefers_original_when_candidate_savings_is_below_threshold(self) -> None:
|
||||
self.assertFalse(should_keep_conversion(200_000, 191_000))
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the tests to confirm RED**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python3 -m unittest tests.test_optimize_assets -v
|
||||
```
|
||||
Expected: FAIL on the new threshold case.
|
||||
|
||||
- [ ] **Step 3: Implement the full optimizer**
|
||||
|
||||
```python
|
||||
# Main behavior:
|
||||
# 1. Walk assets image files from the restored baseline.
|
||||
# 2. For each image, optimize original format in place only if smaller.
|
||||
# 3. For large images, evaluate multiple quality/resize candidates.
|
||||
# 4. Generate webp only when it beats the optimized source and passes thresholds.
|
||||
# 5. Update text references only for accepted conversions.
|
||||
# 6. Emit a summary report with baseline and final totals.
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run targeted verification**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python3 -m unittest tests.test_optimize_assets -v
|
||||
python3 scripts/optimize_assets.py
|
||||
```
|
||||
Expected: tests pass; script prints accepted conversions, skipped conversions, and size totals.
|
||||
|
||||
### Task 4: Final Verification
|
||||
|
||||
**Files:**
|
||||
- Verify: `assets/**`
|
||||
- Verify: `assets_backup/**`
|
||||
- Verify: `*.html`
|
||||
- Verify: `assets/css/*.css`
|
||||
|
||||
- [ ] **Step 1: Verify there are no missing accepted references**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python3 scripts/optimize_assets.py --check
|
||||
```
|
||||
Expected: exit 0 with no missing rewritten references.
|
||||
|
||||
- [ ] **Step 2: Verify resulting size totals**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
du -sh assets assets_backup
|
||||
find assets -type f \\( -iname '*.webp' -o -iname '*.png' -o -iname '*.jpg' -o -iname '*.jpeg' -o -iname '*.gif' \\) -printf '%s\n' | awk '{s+=$1} END {printf "assets raster total: %.2f MB\n", s/1024/1024}'
|
||||
```
|
||||
Expected: `assets_backup` remains as backup; `assets` shows reduced effective payload compared with the restored baseline.
|
||||
|
||||
- [ ] **Step 3: Verify git diff is scoped correctly**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
git status --short
|
||||
```
|
||||
Expected: only the intended backup, script, tests, optimized images, and rewritten references are listed.
|
||||
Reference in New Issue
Block a user