From 5e31d5388bd0dd5cd5c7ad8c4513a9d0cac2896a Mon Sep 17 00:00:00 2001 From: kyeunga25 Date: Sat, 25 Jul 2026 03:01:13 +0800 Subject: [PATCH] release: prepare v0.2.2 privacy and security update --- .github/workflows/ci.yml | 7 +- .gitignore | 1 + AGENTS.md | 17 - CHANGELOG.md | 24 +- README.md | 9 +- README.zh-Hant.md | 9 +- TODO.md | 43 - docs/DEPLOYMENT.md | 2 - docs/LEGAL_PRIVACY_AND_DATA_POLICY.md | 118 ++ docs/RELEASING.md | 2 + ...CT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md | 1602 ----------------- package-lock.json | 369 ++-- package.json | 4 +- src/app/App.tsx | 2 +- src/components/footer/Footer.tsx | 17 +- src/components/header/Header.tsx | 25 +- src/components/info/InfoDialog.tsx | 242 ++- src/components/upload/UploadPanel.tsx | 23 +- src/core/validation.ts | 123 +- src/i18n/i18n.tsx | 52 +- src/styles/index.css | 83 +- tests/e2e/wallpect.spec.ts | 36 + tests/integration/i18n.test.tsx | 2 + tests/unit/profiles.test.ts | 38 + 24 files changed, 938 insertions(+), 1912 deletions(-) delete mode 100644 AGENTS.md delete mode 100644 TODO.md create mode 100644 docs/LEGAL_PRIVACY_AND_DATA_POLICY.md delete mode 100644 docs/WALLPECT_PRODUCT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c595b06..1fbb78e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,12 +5,15 @@ on: branches: [main, develop] pull_request: +permissions: + contents: read + jobs: quality: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 22 cache: npm diff --git a/.gitignore b/.gitignore index 04a0f52..3ef30b8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ test-results/ .env.* !.env.example .dev.vars +AGENTS.md *.local *.pem *.key diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index a9fadcd..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,17 +0,0 @@ -# Wallpect 貢獻者指引 / Contributor instructions - -- 禁止批量刪除文件或目錄;每次只可刪除一個明確路徑的文件。 -- 使用 TypeScript strict mode,並將裝置資料放在 UI 元件之外。 -- 上傳圖片的內容必須只留在瀏覽器中;不要加入上傳 API 或圖片分析。 -- 預覽與匯出必須共用相同的變換及渲染器計算。 -- 不要把估算的設定檔資料標示為已驗證。 -- 每個重要里程碑後執行 `npm run lint`、`npm test` 與 `npm run build`。 - -English: - -- Never batch-delete files or directories. Delete only one explicit file path at a time. -- Use TypeScript strict mode and keep device data outside UI components. -- Uploaded image content must stay in the browser. Do not add upload APIs or image analytics. -- Preview and export must share the same transform and renderer calculations. -- Do not label estimated profile data as verified. -- Run `npm run lint`, `npm test`, and `npm run build` after material milestones. diff --git a/CHANGELOG.md b/CHANGELOG.md index 7488db1..23c4ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,27 @@ All notable changes to Wallpect are recorded in this file. The format follows [K ## [Unreleased] +## [0.2.2] - 2026-07-25 + +### Added + +- Added Traditional Chinese and English terms, privacy, disclaimer, trademark, and data-source notices inside the app. +- Added a public legal, privacy, and data policy with a review gate for future third-party sources, SDKs, and APIs. +- Added profile validation for safe source URLs, real review dates, finite geometry, in-bounds overlays, and unique slugs. +- Added automated coverage for the legal and privacy interface on desktop and mobile. + +### Changed + +- Documented the exact browser-memory, versioned `localStorage`, browser-cache, and Cloudflare network-data boundaries. +- Prevented overlapping image decodes and restored same-file selection after a decode attempt. +- Updated Wrangler to `4.114.0`, pinned GitHub Actions to reviewed commits, and limited CI permissions to read-only repository contents. +- Tightened public-repository hygiene and release review guidance. + +### Security + +- Restricted device-profile source URLs to credential-free HTTPS links on `support.apple.com`. +- Confirmed zero known npm vulnerabilities in both the complete and production dependency graphs at release preparation time. + ## [0.2.1] - 2026-07-23 ### Changed @@ -52,6 +73,7 @@ All notable changes to Wallpect are recorded in this file. The format follows [K - Uploaded images remain in browser memory and are not sent to an upload API or analytics service. - Local secrets, credentials, build output, browser reports, and editor/OS metadata are excluded from version control. -[Unreleased]: https://github.com/kyeunga25/wallpect/compare/v0.2.1...HEAD +[Unreleased]: https://github.com/kyeunga25/wallpect/compare/v0.2.2...HEAD +[0.2.2]: https://github.com/kyeunga25/wallpect/compare/v0.2.1...v0.2.2 [0.2.1]: https://github.com/kyeunga25/wallpect/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/kyeunga25/wallpect/releases/tag/v0.2.0 diff --git a/README.md b/README.md index f870a35..4e5be62 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,9 @@ Wallpect helps you compose an image for a selected Apple device, inspect likely 4. Adjust the composition and enable the guides you want to inspect. 5. Choose PNG, JPEG, or WebP and export at the displayed resolution. -Your image stays in browser memory and is released when it is replaced or the page is closed. Only preferences such as the recent device identifier may be saved in `localStorage`. +Your image stays in browser memory and is released when it is replaced, the page is refreshed, or the page is closed. Only the selected locale and up to four recent device identifiers are saved in versioned `localStorage`. Normal requests for public site files still pass through Cloudflare, but they do not contain your selected image bytes. + +The website provides directly accessible Traditional Chinese and English privacy, terms, disclaimer, and data-source notices. Maintenance and third-party source/API rules are documented in the [Terms, Privacy, and Data Policy](docs/LEGAL_PRIVACY_AND_DATA_POLICY.md).

Wallpect mobile workspace showing the device selector and wallpaper preview @@ -112,8 +114,7 @@ Preview and export both call `renderWallpaper`. Pan offsets use the target canva - [Device profile guide](docs/DEVICE_PROFILE_GUIDE.md) - [Deployment guide](docs/DEPLOYMENT.md) - [Release guide](docs/RELEASING.md) -- [Product requirements and implementation plan](docs/WALLPECT_PRODUCT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md) -- [Implementation checklist](TODO.md) +- [Legal, privacy, and data policy](docs/LEGAL_PRIVACY_AND_DATA_POLICY.md) - [Changelog](CHANGELOG.md) ## Contributing @@ -125,3 +126,5 @@ Issues and focused pull requests are welcome. Read [CONTRIBUTING.md](CONTRIBUTIN Wallpect is available under the [MIT License](LICENSE). Wallpect is an independent tool. It is not affiliated with or endorsed by Apple Inc. Apple product names are used only to identify compatible device profiles. + +Device specifications are manually curated from linked public manufacturer support pages. Wallpect does not use an Apple or third-party device-data API at runtime and does not scrape or mirror Apple website content. See the [Terms, Privacy, and Data Policy](docs/LEGAL_PRIVACY_AND_DATA_POLICY.md) for the detailed limitations and data-governance gate. diff --git a/README.zh-Hant.md b/README.zh-Hant.md index f1b06ac..63c3990 100644 --- a/README.zh-Hant.md +++ b/README.zh-Hant.md @@ -43,7 +43,9 @@ Wallpect 讓你按所選 Apple 裝置調整圖片構圖、檢查可能遮擋內 4. 調整構圖,並開啟需要檢查的輔助線。 5. 選擇 PNG、JPEG 或 WebP,再按顯示的解析度匯出。 -圖片只會保存在瀏覽器記憶體,當圖片被取代或頁面關閉時便會釋放。只有最近使用的裝置識別碼等偏好可能儲存在 `localStorage`。 +圖片只會保存在瀏覽器記憶體,當圖片被取代、頁面重新整理或關閉時便會釋放。只有介面語言及最多四個最近使用的裝置識別碼會儲存在版本化 `localStorage`;公開網站檔案的正常請求仍會經過 Cloudflare,但不包含你選擇的圖片 bytes。 + +網站內可直接查閱繁中/英文的私隱、使用條款、免責及資料來源說明。維護與新增第三方來源/API 的規則記錄於 [條款、私隱與資料政策](docs/LEGAL_PRIVACY_AND_DATA_POLICY.md)。

Wallpect 流動版工作區,顯示裝置選擇器及桌布預覽 @@ -112,8 +114,7 @@ Wallpect 以不含 Worker script 的 Cloudflare Workers Static Assets 模式部 - [裝置設定檔指南](docs/DEVICE_PROFILE_GUIDE.md) - [部署指南](docs/DEPLOYMENT.md) - [發布指南](docs/RELEASING.md) -- [產品需求與實作計劃](docs/WALLPECT_PRODUCT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md) -- [實作清單](TODO.md) +- [條款、私隱與資料政策](docs/LEGAL_PRIVACY_AND_DATA_POLICY.md) - [版本記錄](CHANGELOG.md) ## 參與貢獻 @@ -125,3 +126,5 @@ Wallpect 以不含 Worker script 的 Cloudflare Workers Static Assets 模式部 Wallpect 採用 [MIT License](LICENSE)。 Wallpect 是獨立工具,與 Apple Inc. 無關,亦未獲其背書。Apple 產品名稱只用於識別相容的裝置設定檔。 + +裝置規格由連結的公開製造商支援頁面人工整理;Wallpect 不會在執行時使用 Apple 或第三方裝置資料 API,也不會自動擷取或鏡像 Apple 網站內容。詳細限制及資料治理方法見 [條款、私隱與資料政策](docs/LEGAL_PRIVACY_AND_DATA_POLICY.md)。 diff --git a/TODO.md b/TODO.md deleted file mode 100644 index f03498c..0000000 --- a/TODO.md +++ /dev/null @@ -1,43 +0,0 @@ -# Wallpect 實作清單 / Implementation checklist - -## 中文 - -- [x] 建立 React、Vite、TypeScript strict mode、lint、格式化、Vitest 與 Playwright 基礎 -- [x] 加入經驗證的 Apple 裝置設定檔 schema 與代表性資料集 -- [x] 加入本機上傳驗證與只存在記憶體的圖片生命週期 -- [x] 讓預覽與匯出共用適配、變換與 Canvas 渲染器 -- [x] 加入資料驅動的 iPhone、iPad、Mac、開孔與螢幕遮罩 -- [x] 加入拖曳、縮放、平移、旋轉、填滿/完整顯示、重設、置中與鍵盤控制 -- [x] 加入預覽模式、覆疊、裝置資訊與準確度說明 -- [x] 加入精確解析度的 PNG、JPEG、WebP 下載 -- [x] 加入桌面/流動版版面及無障礙控制 -- [x] 加入 Workers Static Assets headers、設定、離線應用程式外殼、CI 與文檔 -- [x] 加入單元、整合、端對端與瀏覽器視覺檢查 -- [x] 在 Chromium、WebKit、Microsoft Edge 與 Firefox 執行自動相容性檢查 -- [x] 加入預設繁體中文,並支援簡體中文與英文切換 -- [x] 完成提交前的依賴、機密、瀏覽器介面與安全 header 審查 -- [x] 以 Workers Builds 自動建立分支預覽,並從 `main` 部署 `wallpect.k-y.cc` -- [x] 將正式網域改為 Workers Custom Domain,並移除多餘的 Cloudflare Pages 專案 -- [x] 建立可重複的版本發布、正式驗證及 Workers 回退清單 -- [ ] 完成實體裝置 Safari、低記憶體流動裝置、CMYK JPEG 與 EXIF 手動 QA - -## English - -- [x] Bootstrap React, Vite, TypeScript strict mode, lint, formatting, Vitest, and Playwright -- [x] Add validated Apple Device Profile schema and representative data set -- [x] Add local upload validation and memory-only image lifecycle -- [x] Share fit/transform/Canvas renderer between preview and export -- [x] Add data-driven iPhone, iPad, Mac, cutout, and screen masks -- [x] Add drag, zoom, pan, rotate, Fill/Fit, reset, center, and keyboard controls -- [x] Add preview modes, overlays, device information, and accuracy notes -- [x] Add PNG, JPEG, WebP exact-resolution download -- [x] Add desktop/mobile layout and accessible controls -- [x] Add Workers Static Assets headers, configuration, offline shell, CI, and documentation -- [x] Add unit, integration, E2E, and visual browser checks -- [x] Run automated compatibility checks in Chromium, WebKit, Microsoft Edge, and Firefox -- [x] Default to Traditional Chinese and support Simplified Chinese and English switching -- [x] Complete pre-commit dependency, secret, browser-surface, and security-header review -- [x] Build branch previews automatically and deploy `main` to `wallpect.k-y.cc` with Workers Builds -- [x] Move the production hostname to a Workers Custom Domain and remove the redundant Cloudflare Pages project -- [x] Document repeatable release, production verification, and Workers rollback procedures -- [ ] Complete physical-device Safari, low-memory mobile, CMYK JPEG, and EXIF manual QA diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index cf196a9..272884d 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -5,7 +5,6 @@ ### 專案設定 ```text -Project: wallpect Production branch: main Build command: npm run build Deploy command: npx wrangler deploy @@ -95,7 +94,6 @@ PLAYWRIGHT_BASE_URL=https:// npm run test:e2e ### Project settings ```text -Project: wallpect Production branch: main Build command: npm run build Deploy command: npx wrangler deploy diff --git a/docs/LEGAL_PRIVACY_AND_DATA_POLICY.md b/docs/LEGAL_PRIVACY_AND_DATA_POLICY.md new file mode 100644 index 0000000..d2550a7 --- /dev/null +++ b/docs/LEGAL_PRIVACY_AND_DATA_POLICY.md @@ -0,0 +1,118 @@ +# Wallpect 條款、私隱與資料政策 + +生效日期:2026 年 7 月 25 日 +適用版本:0.2.2 + +> 本文件記錄 Wallpect 測試版目前的產品行為及維護規則,只屬一般資料,並非法律意見。若加入收費、帳戶、廣告、分析、個人資料表格、使用者內容託管或新司法管轄區,必須先接受獨立法律審閱。 + +## 1. 使用條款摘要 + +- 使用 Wallpect 即表示使用者同意合法使用服務,不干擾、規避安全控制、令服務過載或濫用服務。 +- 使用者選擇的圖片仍由使用者控制;使用者有責任確保自己有權使用、編輯及匯出圖片。 +- Wallpect 是測試階段的構圖輔助工具,按現況及可用情況提供。裝置設定檔、安全區域、覆疊及預覽可能不完整、屬概略、已過時或受作業系統設定影響。 +- 重要輸出應在目標裝置驗證。任何免責及責任限制只在適用法律容許的範圍內生效,並不排除法律不容排除的權利或責任。 + +網站上的完整使用者版本位於「條款與資料」對話框。 + +## 2. 現行私隱資料清單 + +| 資料/處理 | 位置 | 用途 | 保留 | 傳送對象 | +| ----------------------------------------------- | ------------------------------------ | ---------------------------- | ---------------------------------- | ------------------------------------------------------ | +| 使用者選擇的圖片 bytes、檔名、Canvas 像素及變換 | 瀏覽器記憶體 | 預覽與匯出 | 更換圖片、重新整理或關閉頁面時清除 | 不上傳;沒有圖片 API 或第三方圖片分析 | +| 語言偏好 | `localStorage`:`wallpect:locale:v1` | 還原介面語言 | 直至使用者改寫或清除網站資料 | 不由應用程式傳送 | +| 最多四個最近裝置 ID | `localStorage`:版本化 key | 提供最近使用裝置捷徑 | 直至改寫或清除網站資料 | 不由應用程式傳送 | +| 公開應用程式資源 | Browser Cache/Service Worker Cache | 提升載入可靠性 | 由版本化 cache 及瀏覽器政策管理 | 不包含使用者選擇的圖片 | +| 一般網絡請求 metadata | Cloudflare 網絡 | 傳送、保護及快取公開網站檔案 | 受 Cloudflare 服務及私隱政策規管 | Cloudflare;可能包括 IP、路由、系統設定及請求 metadata | + +Wallpect 前端不設定廣告 Cookie、不載入客戶端分析、不建立帳戶、不收集表格資料,也不把使用者選擇的圖片傳送到 Cloudflare。正常瀏覽公開網站仍會產生網絡請求;不應把「圖片不上傳」誤寫成「託管供應商完全不處理任何網絡資料」。 + +參考:[香港私隱公署《擬備收集個人資料聲明及私隱政策聲明指引》](https://www.pcpd.org.hk/tc_chi/resources_centre/publications/files/GN_picspps_c.pdf)、[Cloudflare Privacy Policy](https://www.cloudflare.com/policies/privacy/)。 + +## 3. 裝置資料來源與準確度 + +目前裝置資料由維護者人工整理,來源只限設定檔內連結的 `https://support.apple.com` 公開技術規格及支援頁面。Wallpect: + +- 不在執行時呼叫 Apple 或第三方裝置資料 API; +- 不自動擷取、爬取或鏡像 Apple 網站; +- 不複製 Apple 網站文章、產品圖片、Logo、介面資產或宣傳素材; +- 只保存相容性識別所需的產品名稱、硬件規格事實、來源 URL、檢查日期及 Wallpect 自行整理的準確度 metadata; +- 把安全區域、圓角、開孔與系統覆疊清楚標示為 `measured`、`derived` 或 `estimated`,不把估算資料標示為 `verified`。 + +每個設定檔必須通過 schema 驗證:有限及正數尺寸、有效日期、唯一 ID/slug,以及無憑證的 `https://support.apple.com` 來源 URL。來源頁可能變更或停止提供,所以來源連結不構成持續準確的保證。 + +Apple 的香港網站條款限制複製、重新發佈、鏡像及自動擷取其內容,因此 Wallpect 只作人工、有限的規格事實整理並連回來源;如使用模式或商業模式改變,應重新取得法律意見或適當授權。參考:[Apple 網站使用條款](https://www.apple.com/hk/legal/internet-services/terms/site.html)。 + +## 4. 商標、版權與獨立性 + +- `Wallpect` 不包含 Apple 商標,品牌視覺不使用 Apple Logo、產品圖片、官方圖示或仿製 Apple 網站 trade dress。 +- Apple、iPhone、iPad、Mac、MacBook、iMac 及相關名稱只用於識別相容裝置設定檔,顯著程度低於 Wallpect 品牌。 +- Wallpect 是獨立工具,未獲 Apple Inc. 授權、贊助或背書。 +- 使用者圖片的權利不會因本機處理而轉移給 Wallpect;使用者仍須確保自己有權使用圖片。 +- Wallpect 程式碼按 repository 的 [MIT License](../LICENSE) 發布;MIT License 不授予第三方商標、資料或使用者圖片的權利。 + +參考:[Apple 使用商標和版權的指引](https://www.apple.com/hk/legal/intellectual-property/guidelinesfor3rdparties.html)。 + +## 5. 新增第三方來源或 API 的合併門檻 + +在新增任何官方或第三方資料來源、SDK 或 API 前,pull request 必須記錄: + +1. 供應者、正式文件與條款/授權 URL; +2. 取得方法(人工、下載資料集或 API)及准許的用途; +3. 可保存、修改、快取、重新發佈及展示的欄位; +4. 必須展示的 attribution、商標或版權聲明; +5. 更新頻率、資料刪除/撤回機制及來源失效處理; +6. 速率限制、錯誤處理、供應鏈風險與 schema 驗證; +7. 是否涉及 IP、裝置資料、識別碼、圖片或其他個人資料; +8. API key 的保存位置。Secret 不得放入前端、Git、文件、測試 fixture 或公開 build; +9. runtime 會否把圖片、檔名、Canvas 像素或本機路徑傳出瀏覽器;這些資料目前一律禁止外傳; +10. 法律/私隱審閱結論及產品 UI 需要新增的通知或同意。 + +未能確認使用權、再發布權或資料處理邊界時,不應合併來源或 API。 + +## 6. 必須重新審閱本政策的變更 + +- 加入帳戶、登入、付款、廣告、分析、遙測、表格或電郵; +- 加入 Worker API、資料庫、遠端圖片處理或圖片上傳; +- 顯示第三方圖片、字體、影片或嵌入內容; +- 使用新的資料供應者、API、SDK、爬蟲或自動同步; +- 更改 Cloudflare 產品、日誌、分析或保留設定; +- 開始商業化、提供保證、面向兒童或進入其他司法管轄區; +- 使用 Apple 或其他品牌的圖像、介面資產、商標或宣傳材料。 + +--- + +# Wallpect Terms, Privacy, and Data Policy + +Effective date: 25 July 2026 +Applies to version: 0.2.2 + +> This document records the current behavior and maintenance rules of the Wallpect beta. It is general information, not legal advice. Independent legal review is required before adding payments, accounts, advertising, analytics, personal-data forms, hosted user content, or new jurisdictions. + +## 1. Terms summary + +- Users agree to use Wallpect lawfully and not disrupt, bypass security controls, overload, or misuse the service. +- Selected images remain under the user's control. The user is responsible for having permission to use, edit, and export them. +- Wallpect is a beta composition aid provided as is and as available. Profiles, safe areas, overlays, and previews may be incomplete, approximate, outdated, or affected by operating-system settings. +- Important output should be checked on the target device. Disclaimers and liability limits apply only to the extent permitted by applicable law and do not exclude non-excludable rights or liabilities. + +The user-facing version is available in the website's “Legal & data” dialog. + +## 2. Current privacy inventory + +Wallpect keeps selected image bytes, filenames, Canvas pixels, and transforms in browser memory and does not upload them. It stores only the selected locale and up to four recent device identifiers in versioned `localStorage`. Public app assets may be cached for reliability, but these caches do not contain user-selected images. + +The site is hosted on Cloudflare. Requests for public files pass through Cloudflare, which may process IP addresses, routing data, system configuration, and request metadata under its privacy policy. The app itself does not load advertising cookies, client-side analytics, account systems, or personal-data forms. + +References: [Hong Kong PCPD guidance](https://www.pcpd.org.hk/tc_chi/resources_centre/publications/files/GN_picspps_c.pdf), [Cloudflare Privacy Policy](https://www.cloudflare.com/policies/privacy/). + +## 3. Device data and source rules + +Profiles are manually curated from linked public `https://support.apple.com` specification and support pages. Wallpect does not call an Apple or third-party device-data API at runtime, scrape or mirror Apple websites, or reproduce Apple articles, product images, logos, interface assets, or marketing material. Derived geometry remains clearly labelled and is never promoted to verified without evidence. + +References: [Apple Website Terms of Use](https://www.apple.com/hk/legal/internet-services/terms/site.html), [Apple trademark and copyright guidelines](https://www.apple.com/hk/legal/intellectual-property/guidelinesfor3rdparties.html). + +## 4. Third-party and API gate + +Any new source, SDK, or API must document its provider, official terms or licence, permitted fields and uses, redistribution and attribution rules, retention and withdrawal handling, rate limits, schema validation, privacy impact, secret storage, and image-egress boundary before merge. Unknown usage or redistribution rights block adoption. + +Re-review this policy whenever the project adds accounts, payments, analytics, telemetry, forms, remote image processing, uploads, a database, third-party media, automated data ingestion, a new hosting/logging product, commercialization, or another jurisdiction. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index bc05d5c..a18de9e 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -14,6 +14,7 @@ Wallpect 使用 Semantic Versioning,版本資料以 `package.json`、`package- 3. 同步更新 `package.json` 與 `package-lock.json`。 4. 將 `CHANGELOG.md` 的 `Unreleased` 內容移至帶日期的版本標題。 5. 更新受影響的 README、部署、準確度、裝置資料或產品文件。 +6. 審核所有已追蹤及新增檔案,排除私人資料、憑證、供應商識別碼、內部規劃及非公開商業資料;公開設定只保留部署所必需的產品名稱與網址。 ### 2. 本機驗證 @@ -69,6 +70,7 @@ Wallpect follows Semantic Versioning. `package.json`, `package-lock.json`, `CHAN 3. Keep `package.json` and `package-lock.json` in sync. 4. Move `CHANGELOG.md` entries from `Unreleased` into a dated version section. 5. Update affected README, deployment, accuracy, device-data, or product documentation. +6. Review every tracked and new file for personal data, credentials, provider identifiers, internal planning, and non-public business information. Keep only product names and URLs required for public deployment. ### 2. Validate locally diff --git a/docs/WALLPECT_PRODUCT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md b/docs/WALLPECT_PRODUCT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md deleted file mode 100644 index c850413..0000000 --- a/docs/WALLPECT_PRODUCT_REQUIREMENTS_AND_IMPLEMENTATION_PLAN.md +++ /dev/null @@ -1,1602 +0,0 @@ -# Wallpect — 產品需求與實作計劃 - -> Product Requirements Document(PRD)、現行實作基準與後續計劃 -> 工作名稱:**Wallpect** -> Repository:`wallpect` -> 正式網址:`wallpect.k-y.cc` -> 部署平台:Cloudflare Workers Static Assets -> 文件狀態:Implemented v0.2.0(2026-07-19) -> 目標:維護可公開使用的 Apple-first 工具,同時保留擴展至 Android 及其他裝置的架構 - ---- - -## 1. 專案摘要 - -Wallpect 是一個以 Apple 裝置壁紙為核心的瀏覽器工具,MVP 優先支援不同型號的 iPhone、iPad 與 Mac。用戶可以: - -1. 上傳一張圖片; -2. 選擇特定 Apple 裝置與型號; -3. 立即看到該圖片放入模擬裝置螢幕後的效果; -4. 按裝置的真實輸出解像度、長寬比例、圓角、瀏海、Dynamic Island、選單列或其他遮擋區域預覽; -5. 在裝置預覽內直接縮放、拖曳、旋轉及調整裁切; -6. 將調整結果重新輸出為該裝置適用的精確像素尺寸; -7. 下載後直接在 iPhone、iPad 或 Mac 的系統壁紙設定中選擇該圖片,盡量毋須再手動調整尺寸。 - -預覽畫面必須呈現「模擬裝置外殼 + 真實比例螢幕區域 + 作為 Wallpaper 的圖片 + 可選系統 UI Overlay」。圖片必須位於裝置螢幕遮罩內,並被圓角、瀏海、Dynamic Island、相機開孔或螢幕邊界正確裁切。 - -產品的核心價值不是單純 Resize,而是: - -> 讓用戶先在指定 Apple 裝置上完成構圖與裁切,再下載已按目標裝置解像度輸出的壁紙,減少在系統設定壁紙時再次縮放或移動圖片的需要。 - -MVP 應優先採用本地瀏覽器處理圖片,避免將用戶圖片上傳到伺服器,降低基礎設施成本、私隱風險和日常維護工作。 - -### 1.1 現行實作快照 - -- 74 個資料驅動的 Apple 顯示設定檔:27 個 iPhone、20 個 iPad、27 個 Mac/顯示器; -- PNG、JPEG、WebP 本機解碼及精確像素匯出; -- 預覽與匯出共用 `renderWallpaper`、變換及適配計算; -- React Context 管理編輯狀態,CSS 建立響應式介面,Canvas 2D 負責圖片渲染; -- Vitest、React Testing Library 及 Playwright 涵蓋核心計算、整合流程與瀏覽器矩陣; -- 使用 Workers Builds 將非 production branch 建成預覽版本,並從 `main` 自動部署 assets-only Worker; -- 沒有後端圖片 API、帳戶系統、圖片分析或遠端錯誤記錄。 - -本文後續的 MVP 要求除非標示為 Post-MVP,均視為現行實作基準;仍未完成的手動 QA 項目記錄於 `TODO.md`。 - ---- - -## 2. 名稱與識別 - -### 2.1 工作名稱 - -- 英文名稱:**Wallpect** -- 中文描述:**手機壁紙畫幅預覽器** -- 英文描述:**Wallpaper Resolution & Safe Area Previewer** -- 標語候選: - - `See the exact fit before you set it.` - - `Preview every pixel, crop with confidence.` - - `Fit your wallpaper to every screen.` - -### 2.2 專案命名 - -```text -GitHub repository: wallpect -Cloudflare Worker: wallpect -Production: wallpect.k-y.cc -Development preview: Workers non-production branch preview URL -``` - -### 2.3 品牌使用原則 - -- 不在主品牌名稱中使用 Apple、iPhone 或其他裝置品牌。 -- Apple、iPhone、iPad 等名稱只用於描述支援的裝置。 -- 頁尾加入: - - `Wallpect is not affiliated with or endorsed by Apple Inc.` -- 不使用 Apple 官方 Logo 或模仿 Apple 官方產品介面作為 Wallpect 品牌識別。 -- 「Wallpect」目前只作工作名稱;正式商業化前仍需再進行商標及網域查核。 - ---- - -## 3. 產品目標 - -### 3.1 主要目標 - -1. 提供準確的手機螢幕解像度與長寬比例資料。 -2. 讓用戶直觀預覽壁紙在指定手機上的裁切結果。 -3. 顯示重要遮擋區域及安全區域。 -4. 允許用戶調整圖片位置、縮放與背景填充方式。 -5. 匯出符合指定裝置像素尺寸的圖片。 -6. 圖片預設只在用戶瀏覽器內處理。 -7. 讓新裝置資料可透過 Device Profile 更新,而不需要大幅修改 UI。 -8. 將運行成本維持在 Cloudflare 免費或極低成本範圍。 - -### 3.2 Apple-first 產品範圍 - -MVP 的裝置資料、介面及測試應以 Apple 產品為主,優先次序如下: - -1. **iPhone** - - 第一優先; - - 覆蓋不同螢幕尺寸、長寬比例及頂部結構; - - 包括 Dynamic Island、Notch、舊式無瀏海型號; - - 支援 Lock Screen 與 Clean Screen 預覽; - - 輸出為該型號的目標壁紙像素尺寸。 - -2. **iPad** - - 第二優先; - - 覆蓋 iPad、iPad mini、iPad Air、iPad Pro 的代表型號; - - 支援直向及橫向; - - 顯示圓角、狀態列及可能的系統 UI 安全區域; - - 同一張圖片可分別保存直向與橫向調整結果。 - -3. **Mac** - - 第三優先; - - 覆蓋代表性的 MacBook Air、MacBook Pro、iMac 及 Apple Display; - - 以內置顯示器或指定螢幕的原生像素尺寸作為輸出目標; - - 預覽可顯示瀏海、選單列、Dock 與桌面安全區域; - - 支援桌面 Fill、Fit、Stretch、Center 等模式的近似模擬。 - -MVP 不要求一次收錄所有歷代 Apple 裝置。應先建立一組具代表性、資料可驗證的型號,再透過資料驅動的 Device Profile 持續增加。 - -### 3.3 裝置模擬準確度原則 - -「模擬裝置的模樣及大小」在產品中應定義為: - -- 裝置外形、螢幕比例、螢幕邊界、圓角、邊框、瀏海及 Dynamic Island 位置應按 Profile 顯示; -- 不同裝置並排時,外觀尺寸應按實際螢幕尺寸或機身尺寸的相對比例呈現; -- 單一裝置編輯模式可自動放大至工作區,但必須保留正確長寬比例; -- 網頁預覽不聲稱在未校準顯示器上呈現真實厘米/毫米物理尺寸; -- 裝置外殼只用於預覽,不寫入最終壁紙; -- 最終輸出只包含調整後的壁紙圖像,不包含手機、平板或電腦外框; -- 系統 UI Overlay 預設不寫入輸出。 - -### 3.4 次要目標 - -1. 支援多裝置同時預覽。 -2. 支援批量輸出。 -3. 提供可分享的設定 Preset。 -4. 後續加入 Android、平板、桌面及摺疊裝置。 -5. 後續加入智能主體定位及背景延伸。 - -### 3.5 非目標(MVP 不做) - -- 不做完整 Photoshop 類圖片編輯器。 -- 不做社交平台圖片模板工具。 -- 不做 App Store 宣傳截圖編輯器。 -- 不做帳戶系統。 -- 不做雲端專案儲存。 -- 不做多人協作。 -- 不做生成式 AI。 -- 不做原生 iOS 或 Android App。 -- 不保證完全模擬所有 iOS 動態效果、字體縮放、桌布景深及系統級演算法。 - ---- - -## 4. 目標用戶 - -### 4.1 普通手機用戶 - -需求: - -- 想知道一張圖片放在自己手機上會否被裁走。 -- 想避免人臉、角色、Logo 或文字被瀏海、Dynamic Island 或時鐘遮住。 -- 想輸出準確尺寸的壁紙。 - -### 4.2 壁紙設計者與插畫師 - -需求: - -- 為不同手機型號預先製作適配版本。 -- 減少逐部手機手動測試。 -- 需要一致、可重複的輸出流程。 - -### 4.3 內容創作者與小型設計工作者 - -需求: - -- 為客戶快速生成多個手機版本。 -- 確認圖片主體在不同長寬比中仍然合適。 -- 批量輸出及使用統一命名。 - -### 4.4 開發者與 UI 設計師 - -需求: - -- 查閱裝置尺寸、像素密度及安全區域。 -- 測試 Splash image、背景圖或全螢幕視覺素材。 - ---- - -## 5. 核心使用流程 - -### 5.1 首次進入 - -1. 用戶開啟 `wallpect.k-y.cc`。 -2. 首頁直接顯示: - - 上傳圖片區; - - 裝置選擇器; - - 私隱說明; - - 支援格式及尺寸限制。 -3. 用戶不需要註冊。 - -### 5.2 上傳圖片 - -支援: - -- PNG -- JPEG / JPG -- WebP - -建議限制: - -- 單張最大 30 MB; -- 最長邊最大 12,000 px; -- 若超過瀏覽器穩定處理範圍,提示用戶先壓縮; -- MVP 不支援 HEIC,除非所選前端解碼方案已穩定支援。 - -上傳後: - -- 讀取圖片寬度、高度、比例和檔案大小; -- 不自動上傳至任何伺服器; -- 顯示 `Processed locally in your browser`。 - -### 5.3 選擇 Apple 裝置 - -用戶可以按以下方式選擇: - -- 產品類別:iPhone/iPad/Mac; -- 產品系列; -- 型號; -- 螢幕方向; -- 常用/最近使用裝置; -- 搜尋型號。 - -選擇型號後,系統必須立即載入: - -- 模擬裝置外殼; -- 與該型號匹配的螢幕遮罩; -- 真實輸出解像度; -- 長寬比例; -- 圓角; -- Dynamic Island/Notch/瀏海; -- Safe Area; -- 可選 Lock Screen、Home Screen 或 Desktop Overlay; -- 預設的壁紙適配模式。 - -MVP 裝置優先: - -- iPhone:先覆蓋 Dynamic Island、Notch、無瀏海、小尺寸、標準尺寸及 Plus/Max; -- iPad:至少覆蓋 mini、標準 iPad、Air、Pro 的代表尺寸; -- Mac:至少覆蓋 13/14/15/16 吋級別的代表 MacBook,以及一款 iMac 或 Apple 顯示器 Profile。 - -### 5.4 模擬裝置預覽與調整 - -上傳圖片並選擇裝置後,系統必須直接進入可編輯的模擬裝置預覽: - -1. 先繪製裝置外殼或螢幕框; -2. 在裝置螢幕區域內顯示上傳圖片作為 Wallpaper; -3. 使用螢幕 Shape Mask 裁切圖片; -4. 在圖片之上顯示可選的系統 UI Overlay; -5. 允許用戶直接拖曳圖片及使用控制項調整。 - -用戶可調整: - -- 圖片縮放; -- X 軸位置; -- Y 軸位置; -- 旋轉; -- Fill/Fit 模式; -- 背景色; -- 模糊背景延伸; -- 重設; -- 自動置中; -- 針對直向/橫向分別保存調整; -- 鎖定主體位置(Post-MVP 可加入)。 - -互動要求: - -- 圖片可在模擬螢幕內直接拖曳; -- 滑鼠滾輪/觸控手勢可縮放; -- 雙擊或按 Reset 可恢復預設構圖; -- 圖片超出螢幕區域的部分應被遮罩,不可顯示在裝置外殼上; -- 預覽的圖片構圖必須與最終輸出使用同一套 Transform; -- 切換 Overlay 不可改變圖片位置與縮放。 - -顯示資訊: - -- 原圖像素; -- 裝置輸出像素; -- 原圖比例; -- 裝置比例; -- 預計裁切百分比; -- 被裁切的上下/左右區域; -- 安全區域; -- 可能被系統 UI 遮擋的區域; -- 下載後建議使用的系統壁紙顯示模式。 - -### 5.5 切換預覽模式 - -MVP 建議有三種模式: - -1. **Clean Screen** - - 只顯示螢幕本體及裁切。 -2. **Lock Screen** - - 顯示時鐘、日期、Dynamic Island/Notch、底部快捷操作等近似遮擋層。 -3. **Safe Area** - - 顯示安全區域、危險區域及邊界。 - -Post-MVP: - -4. **Home Screen** - - 顯示 App icon grid、Dock、Widget 區域。 -5. **Always-On Display** -6. **Landscape** -7. **StandBy Mode** - -### 5.6 匯出 - -MVP 匯出: - -- PNG; -- JPEG; -- WebP; -- 原始裝置像素; -- 自訂品質; -- 自訂檔名; -- 單一裝置下載。 - -檔名預設: - -```text -wallpect-{device-slug}-{orientation}-{width}x{height}.{ext} -``` - -例子: - -```text -wallpect-iphone-15-pro-portrait-1179x2556.png -``` - -### 5.7 下載後直接使用 - -輸出目標是讓用戶下載後可直接到 Apple 系統設定選擇圖片,而不需要另外使用圖片編輯器調整尺寸。 - -系統必須: - -- 使用目標 Device Profile 的指定輸出像素; -- 將已確認的縮放、位置、旋轉及背景處理寫入圖片; -- 不把模擬裝置外框、控制點或安全區域寫入正常輸出; -- 在下載前顯示目標裝置與輸出尺寸; -- 提供簡短使用提示,例如: - - iPhone/iPad:在相片或設定中選擇圖片作為壁紙; - - Mac:在系統設定的 Wallpaper 中選擇下載圖片; -- 對可能被系統再次自動縮放的情況提供提示。 - -重要限制: - -- Wallpect 可以輸出正確尺寸與預先調整好的構圖; -- Apple 系統仍可能因 iOS/iPadOS/macOS 版本、Extend Wallpaper、Perspective Zoom、景深效果、顯示縮放或 Fill 模式再作處理; -- 因此產品目標是「盡量毋須再手動調整」,而不是保證所有系統版本完全不改變圖片。 - -Post-MVP: - -- 多裝置 ZIP; -- 批量匯出; -- Preset; -- 下載清單; -- 儲存上次設定。 - ---- - -## 6. 功能需求 - -## FR-001:本地圖片載入 - -系統必須: - -- 在瀏覽器內解碼圖片; -- 不將圖片內容傳送到 Cloudflare Worker、第三方 API 或分析服務; -- 在圖片載入失敗時顯示可理解錯誤; -- 在重新整理或離開頁面時清除記憶體中的圖片資料。 - -驗收: - -- 開啟瀏覽器 DevTools Network 面板時,圖片內容不應出現在外部 Request。 -- 使用者可在離線狀態下完成基本預覽及輸出,前提是網站資源已載入。 - -## FR-002:裝置資料選擇 - -每個裝置必須包含: - -- 品牌; -- 型號; -- 裝置 Slug; -- 發布年份; -- 螢幕方向; -- 物理解像度; -- 邏輯 Viewport; -- Device Pixel Ratio; -- Aspect Ratio; -- 圓角半徑; -- 頂部遮擋區類型; -- Safe Area Insets; -- 狀態列區域; -- Home Indicator 區域; -- 資料來源與最後更新日期; -- Accuracy Level。 - -驗收: - -- 用戶可以搜尋裝置型號。 -- 選擇裝置後,預覽比例立即更新。 -- UI 顯示該裝置的輸出像素尺寸。 - -## FR-003:Apple 裝置外觀與螢幕模擬 - -系統必須: - -- 根據 Device Profile 顯示 iPhone、iPad 或 Mac 的裝置外形; -- 顯示正確長寬比例、螢幕圓角、邊框及 Cutout; -- 將 Wallpaper Canvas 放在裝置螢幕遮罩內; -- 對 iPhone/iPad 支援直向,並在 Profile 允許時支援橫向; -- 對 Mac 顯示螢幕外框,並可開關瀏海、選單列及 Dock; -- 提供「只看螢幕」與「顯示完整裝置」模式; -- 使用相對比例表示不同裝置大小; -- 不使用未獲授權的 Apple 官方產品圖片作為必要功能資產。 - -驗收: - -- 選擇不同型號後,裝置外形、螢幕比例與 Cutout 會同步更新; -- Wallpaper 僅在螢幕區域內可見; -- 任何超出螢幕遮罩的圖像不會出現在裝置外殼; -- 裝置外框不包含在正常下載圖片中; -- 同一個 Profile 的 Preview 與 Export 尺寸邏輯一致。 - -## FR-004:圖片適配 - -系統必須提供: - -- Cover/Fill; -- Contain/Fit; -- 自訂 Zoom; -- X/Y 平移; -- Reset; -- 自動置中。 - -驗收: - -- 圖片不應因 UI 重繪而突然跳位。 -- 在同一設定下輸出結果應與預覽構圖一致。 -- 縮放和平移操作在主流桌面瀏覽器中保持流暢。 - -## FR-005:安全區域與遮擋層 - -系統必須能切換: - -- 安全區域邊界; -- Dynamic Island/Notch; -- 狀態列; -- Lock Screen 時鐘; -- 底部操作區; -- Home Indicator。 - -驗收: - -- 所有 Overlay 只影響預覽,不寫入輸出圖片,除非用戶明確選擇匯出帶標記版本。 -- Overlay 可透過一個總開關關閉。 -- 每個 Overlay 都能顯示圖例或 Tooltip。 - -## FR-006:準確度提示 - -系統必須明確說明: - -- 裝置硬件像素尺寸可以準確記錄; -- 實際壁紙顯示可能受 iOS 版本、Perspective Zoom、Extend Wallpaper、景深效果、顯示縮放和用戶設定影響; -- Lock Screen UI 屬近似模擬,不是 Apple 官方渲染結果。 - -Accuracy Level 建議: - -- `verified`:由官方規格及實機/可信資料交叉確認; -- `high`:官方解像度已確認,部分安全區域由可靠資料推算; -- `estimated`:部分遮擋或圓角資料為推算; -- `legacy`:舊裝置資料未完整更新。 - -## FR-007:輸出 - -系統必須: - -- 按目標 Apple 裝置 Profile 指定的輸出像素尺寸輸出; -- 將用戶已完成的縮放、位置、旋轉及背景處理寫入圖片; -- 保持預覽與輸出構圖一致; -- 支援 PNG、JPEG、WebP; -- 允許選擇 JPEG/WebP 品質; -- 顯示目標裝置、輸出尺寸及方向; -- 顯示預估檔案大小或在輸出後顯示實際大小; -- 不在圖片中加入 Watermark; -- 正常輸出不包含裝置外殼及 Overlay; -- 下載完成後顯示在 iPhone、iPad 或 Mac 設定壁紙的簡短提示。 - -## FR-008:響應式網站 - -網站必須: - -- 支援桌面; -- 支援平板; -- 可在手機瀏覽器內基本使用; -- 桌面版優先提供最佳編輯體驗。 - -建議 Layout: - -- Desktop: - - 左側:上傳、裝置及數值控制; - - 中央:裝置預覽; - - 右側:尺寸資訊、Overlay、輸出。 -- Mobile: - - 預覽置頂; - - 控制區使用 Bottom Sheet 或 Tabs; - - 保持可觸控拖曳和縮放。 - -## FR-009:無障礙 - -最低要求: - -- 所有按鈕有可讀 Label; -- 可用鍵盤操作主要功能; -- 輸入欄位有明確單位; -- Overlay 不只依靠顏色表示; -- 錯誤訊息與表單欄位關聯; -- 焦點樣式可見; -- 支援 `prefers-reduced-motion`。 - ---- - -## 7. 非功能需求 - -### 7.1 效能 - -目標: - -- 初次載入 JavaScript 經壓縮後盡量低於 500 KB; -- Lighthouse Performance 桌面版目標 ≥ 90; -- 10 MB 圖片載入後,基本拖曳和縮放保持流暢; -- 現行預覽在主執行緒使用縮小 Canvas;如實測出現長任務,再評估 Web Worker/OffscreenCanvas; -- 避免每次拖曳都重新生成完整輸出 Canvas。 - -### 7.2 私隱 - -- 圖片預設只在本地處理; -- 不保存圖片; -- 不使用需要讀取圖片內容的第三方分析; -- 現行版本不載入 Web Analytics 或第三方遙測; -- 頁面內置 Privacy 說明。 - -### 7.3 安全 - -- 不執行圖片內嵌腳本; -- 驗證 MIME Type 及實際可解碼性; -- 限制圖片尺寸及記憶體使用; -- 使用 Content Security Policy; -- 所有依賴鎖定版本; -- 不在前端放置秘密或 API Key。 - -### 7.4 相容性 - -MVP 支援最近兩個主要版本: - -- Chrome; -- Edge; -- Firefox; -- Safari。 - -### 7.5 可維護性 - -- TypeScript strict mode; -- Device Profile 與 UI 分離; -- 核心圖片計算函式必須有單元測試; -- 不將裝置數據散落在 JSX/Component 中; -- 所有裝置尺寸使用明確單位。 - ---- - -## 8. 技術架構 - -### 8.1 現行技術棧 - -```text -Framework: React -Build tool: Vite -Language: TypeScript -Styling: CSS -State: React Context and reducer state -Canvas: Canvas 2D API -Image lifecycle: browser memory + object URLs -Testing: Vitest + React Testing Library + Playwright -Linting: ESLint -Formatting: Prettier -Deployment: Cloudflare Workers Static Assets -Package manager: npm -``` - -優先原則: - -- MVP 不需要後端。 -- 不需要 Cloudflare D1、R2、KV 或 Queues。 -- 不需要登入。 -- 不需要 Worker API。 -- 所有圖片編輯及輸出在瀏覽器完成。 - -### 8.2 邏輯模組 - -```text -UI Layer -├── Upload -├── Device Selector -├── Editor Controls -├── Preview -├── Overlay Controls -└── Export Panel - -Application Layer -├── Editor State -├── Device Profile Selection -├── Preview Mode -└── Export Settings - -Domain Layer -├── Crop / Fit Calculation -├── Coordinate Transform -├── Safe Area Model -├── Resolution Validation -└── Export Naming - -Infrastructure Layer -├── Canvas Renderer -├── Image Decoder -├── File Download -├── Local Storage -└── Device Profile Loader -``` - -### 8.3 現行 Repository 結構 - -```text -wallpect/ -├── public/ -│ ├── _headers -│ ├── assets/ -│ └── sw.js -├── src/ -│ ├── app/ -│ │ └── App.tsx -│ ├── components/ -│ │ ├── upload/ -│ │ ├── device-selector/ -│ │ ├── editor-controls/ -│ │ ├── preview/ -│ │ ├── overlays/ -│ │ └── export/ -│ ├── core/ -│ │ ├── fit.ts -│ │ ├── transforms.ts -│ │ ├── renderer.ts -│ │ ├── export.ts -│ │ └── validation.ts -│ ├── data/ -│ │ ├── devices/ -│ │ │ └── apple/ -│ │ └── schemas/ -│ ├── hooks/ -│ ├── i18n/ -│ ├── state/ -│ ├── styles/ -│ ├── types/ -│ └── utils/ -├── tests/ -│ ├── unit/ -│ ├── integration/ -│ └── e2e/ -├── docs/ -│ ├── DEVICE_PROFILE_GUIDE.md -│ ├── ACCURACY_POLICY.md -│ ├── DEPLOYMENT.md -│ └── RELEASING.md -├── .github/ -│ └── workflows/ -├── AGENTS.md -├── README.md -├── package.json -├── tsconfig.json -└── vite.config.ts -``` - ---- - -## 9. Device Profile 資料模型 - -### 9.1 TypeScript Interface - -```ts -export type DeviceAccuracy = "verified" | "high" | "estimated" | "legacy"; - -export interface Insets { - top: number; - right: number; - bottom: number; - left: number; -} - -export interface Rect { - x: number; - y: number; - width: number; - height: number; -} - -export interface DeviceProfile { - id: string; - brand: "Apple"; - platform: "iOS" | "iPadOS" | "macOS"; - category: "iphone" | "ipad" | "macbook" | "imac" | "display"; - family: string; - model: string; - slug: string; - releaseYear: number; - - chassis?: { - physicalWidthMm?: number; - physicalHeightMm?: number; - physicalDepthMm?: number; - screenDiagonalInches?: number; - bezelStyle?: "edge-to-edge" | "classic" | "laptop" | "desktop-display"; - assetId?: string; - }; - - display: { - physicalWidthPx: number; - physicalHeightPx: number; - logicalWidthPt?: number; - logicalHeightPt?: number; - devicePixelRatio?: number; - aspectRatio: number; - orientation: "portrait" | "landscape"; - cornerRadiusPx?: number; - }; - - cutout: { - type: "none" | "notch" | "dynamic-island" | "camera-hole" | "custom"; - boundsPx?: Rect; - }; - - safeArea: { - insetsPx: Insets; - source: "official" | "measured" | "derived" | "estimated"; - }; - - overlays: { - statusBar?: Rect; - lockScreenClock?: Rect; - homeIndicator?: Rect; - bottomActions?: Rect[]; - menuBar?: Rect; - dock?: Rect; - }; - - wallpaperBehavior?: { - systemMayZoom: boolean; - systemMayExtend: boolean; - notes?: string[]; - }; - - metadata: { - sourceUrls: string[]; - lastVerified: string; - accuracy: DeviceAccuracy; - notes?: string[]; - }; -} -``` - -### 9.2 JSON 例子 - -```json -{ - "id": "apple-iphone-example", - "brand": "Apple", - "family": "iPhone", - "model": "Example Model", - "slug": "iphone-example", - "releaseYear": 2025, - "display": { - "physicalWidthPx": 1206, - "physicalHeightPx": 2622, - "logicalWidthPt": 402, - "logicalHeightPt": 874, - "devicePixelRatio": 3, - "aspectRatio": 0.4600, - "orientation": "portrait", - "cornerRadiusPx": 150 - }, - "cutout": { - "type": "dynamic-island", - "boundsPx": { - "x": 480, - "y": 36, - "width": 246, - "height": 90 - } - }, - "safeArea": { - "insetsPx": { - "top": 180, - "right": 72, - "bottom": 120, - "left": 72 - }, - "source": "estimated" - }, - "metadata": { - "sourceUrls": [], - "lastVerified": "2026-07-13", - "accuracy": "estimated" - } -} -``` - -注意:以上例子只用於展示 Schema,不可當作真實裝置資料。 - ---- - -## 10. 圖片適配演算法 - -### 10.1 Cover 模式 - -目標: - -- 圖片完全覆蓋裝置輸出畫布; -- 允許部分內容被裁切; -- 使用者可調整焦點。 - -基本公式: - -```text -scale = max(canvasWidth / imageWidth, canvasHeight / imageHeight) -renderWidth = imageWidth × scale -renderHeight = imageHeight × scale -``` - -### 10.2 Contain 模式 - -目標: - -- 完整顯示圖片; -- 可能出現留白或背景延伸。 - -```text -scale = min(canvasWidth / imageWidth, canvasHeight / imageHeight) -``` - -### 10.3 使用者 Transform - -Editor state 建議: - -```ts -export interface ImageTransform { - scale: number; - translateX: number; - translateY: number; - rotationDeg: number; - fitMode: "cover" | "contain"; -} -``` - -必須確保: - -- Preview Canvas 和 Export Canvas 使用同一套 transform 計算; -- UI 座標與輸出像素座標之間有明確轉換; -- 不依賴 DOM screenshot 產生最終圖片; -- 最終輸出直接由 Canvas render。 - -### 10.4 背景模式 - -MVP: - -- Solid color; -- Transparent(只適用 PNG/WebP); -- Blurred extension。 - -Blurred extension 流程: - -1. 先用 Cover 模式將圖片放大成背景; -2. 套用模糊; -3. 再用 Contain 或使用者 transform 放置前景; -4. 避免模糊背景越過 Alpha 邊界產生黑邊。 - ---- - -## 11. UI 與互動規格 - -### 11.1 首頁/工作區 - -頁面區域: - -1. Header -2. Upload and Device Controls -3. Main Device Preview -4. Adjustment Controls -5. Device Information -6. Export -7. Privacy and Accuracy Notes -8. Footer - -### 11.2 Header - -包含: - -- Wallpect Logo/文字; -- `About`; -- `Device data`; -- `Privacy`; -- GitHub link; -- `Beta` 標記。 - -### 11.3 Upload Panel - -內容: - -- Drag and drop; -- Browse files; -- 支援格式; -- 本地處理說明; -- 圖片 metadata; -- Remove/Replace。 - -### 11.4 Device Selector - -選項: - -- 搜尋框; -- 品牌 Filter; -- 系列 Filter; -- 型號列表; -- 收藏或最近選擇; -- 裝置卡顯示尺寸和年份。 - -### 11.5 Preview - -需求: - -- 使用 SVG/CSS 建立裝置外框; -- 內部圖片由 Canvas 顯示; -- 外框不應寫入最終 Wallpaper; -- 可開關: - - Frame; - - Safe area; - - Cutout; - - Lock screen UI; - - Grid; - - Center guides。 - -### 11.6 Editor Controls - -最低包括: - -- Zoom Slider; -- X position; -- Y position; -- Rotate; -- Fit mode; -- Background; -- Reset; -- Center; -- Keyboard step controls。 - -快捷鍵候選: - -```text -Arrow keys: move -Shift + Arrow: larger move -+ / -: zoom -R: reset -0: center -``` - -### 11.7 Device Information - -顯示: - -- Model; -- Physical resolution; -- Logical viewport; -- Device pixel ratio; -- Aspect ratio; -- Safe area; -- Accuracy; -- Last verified; -- Notes。 - -### 11.8 Export Panel - -選項: - -- Format; -- Quality; -- Exact resolution; -- Filename; -- Download; -- Export preview with guides(可選,預設關閉)。 - ---- - -## 12. 錯誤處理 - -錯誤類型: - -- 不支援格式; -- 檔案太大; -- 圖片解碼失敗; -- 瀏覽器記憶體不足; -- Canvas 尺寸超過限制; -- Device Profile 無效; -- 匯出失敗; -- 瀏覽器不支援必要 API。 - -錯誤訊息原則: - -- 指出發生甚麼; -- 說明是否有替代方案; -- 不只顯示 `Something went wrong`; -- 不清除用戶目前設定,除非必要。 - -例子: - -```text -This image is too large for your browser to process safely. -Try exporting or resizing it below 12,000 pixels on the longest side. -``` - ---- - -## 13. 監察與私隱邊界 - -現行版本只使用: - -- GitHub Actions 檢查記錄; -- Workers Builds 及 deployment 記錄; -- 前端 Error Boundary 及瀏覽器本機 console。 - -現行版本沒有 Web Analytics、圖片分析、遠端錯誤記錄或產品事件遙測。不得收集: - -- 用戶圖片; -- 圖片內容; -- 圖片檔名,如可能包含個人資料; -- Canvas pixel data; -- 裝置本地檔案路徑。 - -日後如建議加入分析,必須先有清楚的資料清單、保留期限及私隱審閱;任何分析不得接觸圖片 bytes、檔名、Canvas 像素或本機路徑。 - ---- - -## 14. 測試計劃 - -### 14.1 單元測試 - -必須測試: - -- Cover scale; -- Contain scale; -- Transform 座標; -- Crop 邊界; -- Aspect ratio; -- Safe area 驗證; -- Filename generator; -- Device Profile schema; -- Export dimensions。 - -### 14.2 Component 測試 - -- 圖片上傳; -- Device selector; -- Slider; -- Overlay toggle; -- Export form; -- Error messages。 - -### 14.3 E2E 測試 - -主要流程: - -1. 開啟網站; -2. 上傳測試圖片; -3. 選擇 iPhone Profile; -4. 調整 Zoom 及位置; -5. 開關 Safe Area; -6. 匯出 PNG; -7. 驗證下載檔案尺寸; -8. 驗證重新整理後圖片不被保存。 - -### 14.4 視覺檢查 - -Repository 保存桌面及流動版參考截圖;以下狀態目前以 Playwright 版面斷言及人工檢查為主,尚未建立自動 screenshot baseline: - -- Desktop workspace; -- Mobile workspace; -- Notch device; -- Dynamic Island device; -- Safe area overlay; -- Lock screen overlay; -- Error states。 - -### 14.5 手動 QA - -至少測試: - -- 小圖; -- 超大圖; -- 超闊圖; -- 超長圖; -- 透明 PNG; -- CMYK JPEG(如瀏覽器可解碼); -- 旋轉 EXIF; -- Safari; -- Firefox; -- 低記憶體手機瀏覽器。 - ---- - -## 15. Cloudflare Workers 部署 - -### 15.1 MVP - -使用 Workers Static Assets 及 Workers Builds: - -```text -Repository: wallpect -Build command: npm run build -Deploy command: npx wrangler deploy -Non-production deploy command: npx wrangler versions upload -Assets directory: dist -Production branch: main -``` - -流量與預覽: - -```text -main → wallpect.k-y.cc Workers Custom Domain -non-production branch → Workers version preview URL -``` - -`wallpect.k-y.cc` 由 Worker Custom Domain 直接管理;正式部署、分支預覽及版本回退均以 Workers 為準,不依賴 Pages project。完整設定及驗證程序見 `docs/DEPLOYMENT.md` 與 `docs/RELEASING.md`。 - -### 15.2 Headers - -至少設定: - -```text -Content-Security-Policy -X-Content-Type-Options: nosniff -Referrer-Policy -Permissions-Policy -Cross-Origin-Opener-Policy where compatible -``` - -需要確認 Canvas、Blob URL、service worker 和下載功能不被 CSP 阻擋。 - -### 15.3 快取 - -- 靜態 JS/CSS 使用 fingerprint 長期快取; -- HTML 使用較短快取; -- 不快取任何用戶圖片,因為圖片不離開瀏覽器。 - ---- - -## 16. 開發里程碑 - -## Milestone 0:Repository 初始化 - -交付: - -- Vite + React + TypeScript; -- ESLint; -- Prettier; -- Vitest; -- Playwright; -- Workers Static Assets build; -- README; -- AGENTS.md; -- 基本 CI。 - -驗收: - -- `npm ci` -- `npm run dev` -- `npm run check` -- `npm run worker:check` - -全部正常。 - -## Milestone 1:Apple Device Profile 系統 - -交付: - -- Device Profile schema; -- JSON/TS validator; -- iPhone、iPad、Mac Profile 資料結構; -- 首批具代表性的 Apple 型號; -- Device selector; -- Profile metadata; -- Accuracy badge; -- 裝置相對尺寸及 Chassis metadata。 - -驗收: - -- 可加入新裝置而不修改 Preview Component 核心邏輯; -- 無效 Profile 在 Build 或 Test 階段失敗。 - -## Milestone 2:圖片上傳與 Apple 裝置模擬 Preview - -交付: - -- Drag and drop; -- File validation; -- Canvas preview; -- iPhone/iPad/Mac 裝置外形; -- 螢幕 Shape Mask; -- Wallpaper 顯示於裝置螢幕內; -- Cover/Contain; -- Zoom; -- Pan; -- Reset; -- Device frame; -- Full-device/screen-only 切換。 - -驗收: - -- 預覽與 Transform 穩定; -- 圖片不離開瀏覽器; -- 大圖有安全限制。 - -## Milestone 3:Overlay 與資訊 - -交付: - -- Safe area; -- Cutout; -- Status bar; -- Lock screen clock; -- Device metadata panel; -- Accuracy notice。 - -驗收: - -- Overlay 可開關; -- Overlay 不寫入預設輸出; -- 不同 Profile 使用不同位置資料。 - -## Milestone 4:Export - -交付: - -- PNG; -- JPEG; -- WebP; -- 品質選擇; -- Exact resolution; -- Filename; -- Download。 - -驗收: - -- 下載圖片尺寸完全等於 Device Profile 的物理解像度; -- 輸出構圖與預覽一致; -- 透明背景只在支援格式出現。 - -## Milestone 5:Responsive、Accessibility 與部署 - -交付: - -- Desktop/Mobile UI; -- Keyboard; -- Focus states; -- Error handling; -- Workers Builds deployment; -- `wallpect.k-y.cc`; -- 內置 Privacy 說明; -- Accuracy policy 及內置準確度說明。 - -驗收: - -- 主流瀏覽器通過 smoke test; -- Lighthouse 基本指標達標; -- 公開網址可正常使用。 - -## Milestone 6:Post-MVP - -候選: - -- 多裝置 grid; -- ZIP export; -- Android profiles; -- Home Screen overlay; -- 自訂裝置; -- Preset; -- 可安裝 PWA; -- Smart focal point; -- AI background extension; -- Developer API。 - ---- - -## 17. MVP 優先級 - -### P0:必須完成 - -- 本地圖片上傳; -- Apple Device Profile; -- iPhone、iPad、Mac 類別選擇器; -- 首批具代表性的 Apple 型號; -- 模擬裝置外形與螢幕遮罩; -- Wallpaper 顯示於模擬裝置螢幕內; -- 準確像素尺寸; -- Cover/Contain; -- 直接拖曳、Zoom/Pan; -- Safe area; -- Cutout; -- 單一裝置精確尺寸輸出; -- 下載後直接使用提示; -- Cloudflare Workers Static Assets; -- 私隱說明; -- Accuracy 說明。 - -### P1:應該完成 - -- Lock Screen overlay; -- JPEG/WebP; -- Mobile responsive; -- Keyboard; -- Device search; -- Recently used; -- Blur background; -- Export filename。 - -### P2:可以延後 - -- 多裝置同時預覽; -- ZIP; -- Android; -- Home Screen; -- Login; -- Cloud save; -- AI; -- API。 - ---- - -## 18. 驗收標準與 Definition of Done - -MVP 完成必須同時符合: - -1. 用戶無需登入即可使用。 -2. 用戶圖片不會上傳至伺服器。 -3. 用戶可選擇至少 10 個具代表性的 iPhone Profile、至少 4 個 iPad Profile,以及至少 4 個 Mac/Display Profile。 -4. 選擇型號後,UI 會顯示對應的模擬裝置外形、螢幕遮罩、解像度、比例及資料準確度。 -5. 上傳圖片會作為 Wallpaper 顯示於模擬裝置螢幕內。 -6. 用戶可直接拖曳、移動、縮放和旋轉圖片。 -7. 用戶可顯示或隱藏 Safe Area、Cutout 及相關系統 UI Overlay。 -8. 用戶可下載已套用調整、並符合目標裝置像素尺寸的輸出圖片。 -9. 正常輸出不包含模擬裝置外框或 Overlay。 -10. 預覽和輸出構圖一致。 -11. 基本功能在 Chrome、Safari、Firefox、Edge 可用。 -12. `wallpect.k-y.cc` 已部署。 -13. README、Device Profile Guide 和 Accuracy Policy 完成。 -14. 核心計算有單元測試。 -15. E2E 覆蓋完整的「上傳 → 選擇 Apple 裝置 → 預覽 → 調整 → 下載」流程。 -16. 無已知 P0 級別錯誤。 - ---- - -## 19. Codex 實作指令 - -以下內容應放進 `AGENTS.md` 或交給 Codex 作為執行規則。 - -### 19.1 開始前 - -Codex 必須: - -1. 閱讀本文件; -2. 檢查現有 repository 狀態; -3. 不假設 repository 為空; -4. 先建立或更新 TODO; -5. 將工作拆成可驗證的小步驟; -6. 每完成一個 Milestone 都執行 lint、test 和 build。 - -### 19.2 實作原則 - -- 使用 TypeScript strict mode。 -- 不在 Component 中硬編碼 Device Profile。 -- 不加入沒有 MVP 用途的後端。 -- 不加入帳戶、付款、資料庫或 AI。 -- 不將圖片傳送到伺服器。 -- 不使用 DOM screenshot 產生匯出圖片。 -- Preview 與 Export 共用核心 transform 邏輯。 -- 優先使用瀏覽器原生 API。 -- 避免大型圖片編輯依賴,除非有明確理由。 -- 所有外部依賴需要記錄用途。 -- 每個重要計算函式都需要測試。 -- 所有裝置 Profile 必須通過 Schema 驗證。 -- 不將估算數據標示為 verified。 -- 不聲稱提供 Apple 官方預覽。 - -### 19.3 Codex 執行順序 - -```text -1. Inspect repository -2. Bootstrap project -3. Add quality tooling -4. Define domain types and schemas -5. Add initial iPhone, iPad, and Mac device profiles -6. Implement image upload -7. Implement transform calculations -8. Implement device shell and screen-mask renderer -9. Render wallpaper inside the simulated device -10. Implement direct manipulation, device frame, and overlays -11. Implement exact-resolution export -12. Add responsive UI -13. Add tests -14. Add Cloudflare Workers deployment -15. Update documentation -16. Run full validation -``` - -### 19.4 每個階段輸出 - -Codex 每次工作應回報: - -- 修改了甚麼; -- 主要設計決定; -- 執行了哪些測試; -- 尚未完成內容; -- 已知限制; -- 下一個建議步驟。 - -### 19.5 禁止事項 - -Codex 不應: - -- 靜默改變產品名稱; -- 自行加入帳戶系統; -- 自行加入收費牆; -- 自行改用需要圖片上傳的第三方服務; -- 在未驗證數據時聲稱「Pixel-perfect」; -- 將 Apple 專有素材提交至 repository; -- 提交 API Key、Token 或 Cloudflare Secret; -- 跳過測試直接標示完成。 - ---- - -## 20. 建議初始 GitHub Issues - -```text -1. Bootstrap React + TypeScript + Vite project -2. Add linting, formatting, unit tests, and E2E tests -3. Define DeviceProfile schema and validation -4. Add initial iPhone, iPad, and Mac device profiles -5. Implement proportional Apple device shell renderer -6. Implement screen shape masks for iPhone, iPad, and Mac -7. Implement local image upload and validation -8. Implement cover and contain calculations -9. Implement direct drag, zoom, pan, and rotation editor state -10. Render uploaded image as wallpaper inside selected device -11. Add device frame, cutout, and Mac notch overlays -12. Add safe-area, lock-screen, menu-bar, and Dock overlays -13. Implement exact-resolution image export without device frame -14. Add responsive layout and mobile controls -15. Add privacy and accuracy documentation -16. Configure Cloudflare Workers Builds deployment -17. Add end-to-end upload-to-export test -``` - ---- - -## 21. 建議 README 開頭 - -```md -# Wallpect - -Wallpect is a privacy-first wallpaper preview and fitting tool. - -Upload an image, select a phone model, inspect its exact resolution, -aspect ratio, crop area, cutout, and safe-area overlays, then export a -wallpaper at the target device resolution. - -## Principles - -- Images stay in the browser -- Exact output dimensions -- Device profiles are data-driven -- Preview and export share the same rendering logic -- Accuracy levels are disclosed -``` - ---- - -## 22. 已知風險 - -### 22.1 裝置資料準確度 - -風險: - -- 官方可能只公布螢幕解像度,不公布所有壁紙安全區域; -- 不同 iOS 版本可能改變 Lock Screen UI; -- Wallpaper 系統可能自動 Zoom 或 Extend。 - -應對: - -- 每個 Profile 加 Accuracy Level; -- 顯示來源及驗證日期; -- 將硬件尺寸與 UI Overlay 分開; -- 提供「Report incorrect data」入口。 - -### 22.2 瀏覽器記憶體 - -風險: - -- 超高解像度圖片可能造成 Canvas 崩潰; -- 手機 Safari 記憶體限制較低。 - -應對: - -- 上傳前驗證; -- 建立最大像素面積限制; -- Preview 使用較低解析度; -- Export 才建立目標尺寸 Canvas; -- 完成後主動釋放 Object URL 和 Canvas。 - -### 22.3 商標與產品名稱 - -風險: - -- Wallpect 可能日後出現同名產品; -- Apple 品牌使用有風險。 - -應對: - -- 正式推出前再查商標; -- 不在品牌主名使用 Apple; -- 加非官方聲明; -- 不模仿 Apple 官方 Logo。 - -### 22.4 維護成本 - -風險: - -- 新裝置每年發布; -- Android 型號數量龐大。 - -應對: - -- 先 Apple-first; -- Device Profile 資料驅動; -- 建立 Contributor Guide; -- Profile 可由 Community PR 更新; -- 不承諾一開始覆蓋所有手機。 - ---- - -## 23. 後續商業化選項 - -MVP 先維持免費工具。後續可考慮: - -- 批量輸出; -- ZIP; -- 無限自訂裝置; -- 設計師 Preset; -- API; -- AI 主體偵測; -- AI 背景延伸; -- 專業 Device Pack; -- 自訂品牌 Overlay; -- 白標工具。 - -商業化不應破壞: - -- 本地圖片處理; -- 基本裝置預覽; -- 基本精確輸出。 - ---- - -## 24. 最終產品原則 - -Wallpect 的優先次序: - -1. **準確** -2. **私隱** -3. **簡單** -4. **快速** -5. **可維護** -6. **可擴展** -7. **商業化** - -所有實作決定應先回答: - -> 這項功能是否令用戶更容易判斷圖片在指定裝置上是否合適? - -若答案不明確,應延後至 Post-MVP。 diff --git a/package-lock.json b/package-lock.json index 9ef1751..6d37415 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "wallpect", - "version": "0.2.1", + "version": "0.2.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wallpect", - "version": "0.2.1", + "version": "0.2.2", "license": "MIT", "dependencies": { "lucide-react": "1.24.0", @@ -32,7 +32,7 @@ "typescript-eslint": "8.63.0", "vite": "8.1.4", "vitest": "4.1.10", - "wrangler": "4.112.0" + "wrangler": "4.114.0" }, "engines": { "node": ">=22" @@ -386,9 +386,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260714.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260714.1.tgz", - "integrity": "sha512-ZWXqAN8G7Cx9hMRQuk+59ziJhR3j1F4iO+Qs8aHdfKZ3Dq5Yi/57xvkJTgCGBnW1YU/L78r8f6HEy51bwbTpNw==", + "version": "1.20260722.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260722.1.tgz", + "integrity": "sha512-vZOP8vIS3NwnuaO+gz0FZ7kIGeiO3bZmxV35Ph9zOXKSREhDFlH7wQ7mkCdhW3O4jnXsew+XT7b+DNEI2CcJGQ==", "cpu": [ "x64" ], @@ -403,9 +403,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260714.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260714.1.tgz", - "integrity": "sha512-tueWxWC3wyCbMG6zRAxsMXX0YLgrRWbiAPYFQ2uJ7dUH8G+5E7UTWaQS9B1HdJ0bpKFW1NWxhs1o2noKVFSUYg==", + "version": "1.20260722.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260722.1.tgz", + "integrity": "sha512-EmIQymihDq6WNdER4+LF8Qn80yqayBUpJ+tkOO7wmY8pmgfyXjIUFNXotl21AHovTeu2seR7HdVUgeN/BilCWw==", "cpu": [ "arm64" ], @@ -420,9 +420,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260714.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260714.1.tgz", - "integrity": "sha512-1VChTZRb0l0F7R4e1G5RtLKV4oFi6x+rQgxh2+yu887j3l/3TLgatuv1L8/5zhc9gKEhATTxOh0e52Rtd9dDWQ==", + "version": "1.20260722.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260722.1.tgz", + "integrity": "sha512-jvZ3k9fxcnEn04s80CgIYxQfpOyAiz/8qC42DP8EBa9tR27qWyg9wmm31zIobVlrgBZn/+8NfdP73avRGcQOjQ==", "cpu": [ "x64" ], @@ -437,9 +437,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260714.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260714.1.tgz", - "integrity": "sha512-rMm3G+NirG2UdgHIRDdF1asNC6FqgIzZzkRG+VDhhDGcVxAQwvrMT1E38BivEvHr3G04MB4AfhcOczX0+GtRkQ==", + "version": "1.20260722.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260722.1.tgz", + "integrity": "sha512-BOSB55SMNdy+DA5uj2WirgiNanpHGis5PVvXH1wSfvjRKr4JGgWK+EZzxz0RFUo6QjjQQC/NimEzNZ7va7jmKg==", "cpu": [ "arm64" ], @@ -454,9 +454,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260714.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260714.1.tgz", - "integrity": "sha512-cGqnU3Hg2YZS/k3SAqrMp1DjpdsyFde72tWltdl6ZT9+SFz/Zrk/8gyTU1TcxC4YApXeNVH5TyU5cOGPgUJ0pg==", + "version": "1.20260722.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260722.1.tgz", + "integrity": "sha512-sYM8YgUpKnRz2xjvdJLX1Ojzoi4MlA4gk8WTTExhGydjYB2UTs5NIbv0ZmpKgMoK9io3ixgmiW56ZnTbcWOdiA==", "cpu": [ "x64" ], @@ -1333,9 +1333,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", + "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", "cpu": [ "arm64" ], @@ -1346,19 +1346,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.1" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", + "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", "cpu": [ "x64" ], @@ -1369,19 +1369,39 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.1" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", + "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", + "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", "cpu": [ "arm64" ], @@ -1396,9 +1416,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", + "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", "cpu": [ "x64" ], @@ -1413,9 +1433,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", + "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", "cpu": [ "arm" ], @@ -1433,9 +1453,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", + "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", "cpu": [ "arm64" ], @@ -1453,9 +1473,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", + "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", "cpu": [ "ppc64" ], @@ -1473,9 +1493,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", + "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", "cpu": [ "riscv64" ], @@ -1493,9 +1513,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", + "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", "cpu": [ "s390x" ], @@ -1513,9 +1533,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", + "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", "cpu": [ "x64" ], @@ -1533,9 +1553,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", + "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", "cpu": [ "arm64" ], @@ -1553,9 +1573,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", + "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", "cpu": [ "x64" ], @@ -1573,9 +1593,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", + "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", "cpu": [ "arm" ], @@ -1589,19 +1609,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.1" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", + "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", "cpu": [ "arm64" ], @@ -1615,19 +1635,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.1" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", + "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", "cpu": [ "ppc64" ], @@ -1641,19 +1661,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.1" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", + "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", "cpu": [ "riscv64" ], @@ -1667,19 +1687,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.1" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", + "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", "cpu": [ "s390x" ], @@ -1693,19 +1713,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.1" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", + "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", "cpu": [ "x64" ], @@ -1719,19 +1739,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.1" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", + "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", "cpu": [ "arm64" ], @@ -1745,19 +1765,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", + "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", "cpu": [ "x64" ], @@ -1771,39 +1791,56 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.1" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", + "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", + "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", "cpu": [ "wasm32" ], "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.2" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", + "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", "cpu": [ "arm64" ], @@ -1814,16 +1851,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", + "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", "cpu": [ "ia32" ], @@ -1834,16 +1871,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", + "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", "cpu": [ "x64" ], @@ -1854,7 +1891,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -4212,16 +4249,16 @@ } }, "node_modules/miniflare": { - "version": "4.20260714.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260714.0.tgz", - "integrity": "sha512-MYlTCLdWCPqvrYY2uLwOjXwmglXuiHE3TGGkbOW4BwjUPa1r07E0iuHwrNDIs/sxK21r+o90Jx58AV2KeNdJZw==", + "version": "4.20260722.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260722.0.tgz", + "integrity": "sha512-LW6ABMhCx/yIEFBLC/DO4yAhdm2T/G7jp7pr5T2kj895+CCIaHZqpMXdW9O6YE48LcYcCJChwWc8aEs1vpbTXw==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", - "sharp": "0.34.5", + "sharp": "0.35.2", "undici": "7.28.0", - "workerd": "1.20260714.1", + "workerd": "1.20260722.1", "ws": "8.21.0", "youch": "4.1.0-beta.10" }, @@ -4652,48 +4689,48 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", + "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", "dev": true, - "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.4" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.2", + "@img/sharp-darwin-x64": "0.35.2", + "@img/sharp-freebsd-wasm32": "0.35.2", + "@img/sharp-libvips-darwin-arm64": "1.3.1", + "@img/sharp-libvips-darwin-x64": "1.3.1", + "@img/sharp-libvips-linux-arm": "1.3.1", + "@img/sharp-libvips-linux-arm64": "1.3.1", + "@img/sharp-libvips-linux-ppc64": "1.3.1", + "@img/sharp-libvips-linux-riscv64": "1.3.1", + "@img/sharp-libvips-linux-s390x": "1.3.1", + "@img/sharp-libvips-linux-x64": "1.3.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", + "@img/sharp-libvips-linuxmusl-x64": "1.3.1", + "@img/sharp-linux-arm": "0.35.2", + "@img/sharp-linux-arm64": "0.35.2", + "@img/sharp-linux-ppc64": "0.35.2", + "@img/sharp-linux-riscv64": "0.35.2", + "@img/sharp-linux-s390x": "0.35.2", + "@img/sharp-linux-x64": "0.35.2", + "@img/sharp-linuxmusl-arm64": "0.35.2", + "@img/sharp-linuxmusl-x64": "0.35.2", + "@img/sharp-webcontainers-wasm32": "0.35.2", + "@img/sharp-win32-arm64": "0.35.2", + "@img/sharp-win32-ia32": "0.35.2", + "@img/sharp-win32-x64": "0.35.2" } }, "node_modules/sharp/node_modules/semver": { @@ -5301,9 +5338,9 @@ } }, "node_modules/workerd": { - "version": "1.20260714.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260714.1.tgz", - "integrity": "sha512-oIbQzfdyl9UQUnG6XLegcSq0Mgt/7WKDbFOoqGgOWCS+/fhyGB460uKEgdAQQ9RHCO/ttcNCX/KiMIQzdoeu3Q==", + "version": "1.20260722.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260722.1.tgz", + "integrity": "sha512-NycKuc1x2onvsRfGGpM093vRlLFU2zHDAM0+APpccfg4+gZxDGCH27RmdDvkeBuoZyYqgLo3oAfF6re4mvC3vQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -5314,17 +5351,17 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260714.1", - "@cloudflare/workerd-darwin-arm64": "1.20260714.1", - "@cloudflare/workerd-linux-64": "1.20260714.1", - "@cloudflare/workerd-linux-arm64": "1.20260714.1", - "@cloudflare/workerd-windows-64": "1.20260714.1" + "@cloudflare/workerd-darwin-64": "1.20260722.1", + "@cloudflare/workerd-darwin-arm64": "1.20260722.1", + "@cloudflare/workerd-linux-64": "1.20260722.1", + "@cloudflare/workerd-linux-arm64": "1.20260722.1", + "@cloudflare/workerd-windows-64": "1.20260722.1" } }, "node_modules/wrangler": { - "version": "4.112.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.112.0.tgz", - "integrity": "sha512-5H+XUD0TySCv1LuktFHDIEOkboH2nTfQs+35L+USt3MtntjDTMVIJprLgQcL2WBjulOyjxpd1vyTiSTJVW5MjQ==", + "version": "4.114.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.114.0.tgz", + "integrity": "sha512-M65P25t5UHA1TIJfgZXDcj+YzVobgKdRguM2QPz0xnxLFuOcuE3ErgllDht0iaho7MS4o0g/Bb4YK2+GT+bibg==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { @@ -5332,10 +5369,10 @@ "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.28.1", - "miniflare": "4.20260714.0", + "miniflare": "4.20260722.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260714.1" + "workerd": "1.20260722.1" }, "bin": { "cf-wrangler": "bin/cf-wrangler.js", @@ -5349,7 +5386,7 @@ "fsevents": "2.3.3" }, "peerDependencies": { - "@cloudflare/workers-types": "^5.20260714.1" + "@cloudflare/workers-types": "^5.20260722.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { diff --git a/package.json b/package.json index 5e997d5..a2a0f86 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "wallpect", "private": true, - "version": "0.2.1", + "version": "0.2.2", "description": "Privacy-first wallpaper preview and fitting tool for Apple devices.", "license": "MIT", "homepage": "https://wallpect.k-y.cc", @@ -56,6 +56,6 @@ "typescript-eslint": "8.63.0", "vite": "8.1.4", "vitest": "4.1.10", - "wrangler": "4.112.0" + "wrangler": "4.114.0" } } diff --git a/src/app/App.tsx b/src/app/App.tsx index a3bdb1c..021b045 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -62,7 +62,7 @@ function Workspace() { -