From c97a7faaa9986bf0319ec5e621612fd3232405a3 Mon Sep 17 00:00:00 2001 From: Mathieu Date: Mon, 8 Jun 2026 15:47:34 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20types=20ImageItem=20+=20d=C3=A9pendance?= =?UTF-8?q?s=20heic2any=20jszip?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Install heic2any and jszip dependencies - Create src/types/images.ts with ImageItem, ImageStatus, ImageProgress, and ZipEntry types - heic2any includes bundled TypeScript types (no declaration file needed) - ImageItem is the central data model for the image processing module with support for: - HEIC to JPEG conversion - AI-powered background removal - Shopify product lookup and upload - Progress tracking and error handling - Rotation and feather adjustments (cached PNG blob for fast re-processing) Co-Authored-By: Claude Sonnet 4.6 --- package-lock.json | 95 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + src/types/images.ts | 40 +++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 src/types/images.ts diff --git a/package-lock.json b/package-lock.json index 0c115e1..70eaf0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@prisma/client": "^7.8.0", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.10.0", + "heic2any": "^0.0.4", + "jszip": "^3.10.1", "next": "14.2.35", "next-auth": "^4.24.14", "react": "^18", @@ -4894,6 +4896,12 @@ "node": ">= 0.6" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -6620,6 +6628,12 @@ "node": ">= 0.4" } }, + "node_modules/heic2any": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/heic2any/-/heic2any-0.0.4.tgz", + "integrity": "sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA==", + "license": "MIT" + }, "node_modules/hono": { "version": "4.12.23", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.23.tgz", @@ -6740,6 +6754,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/import-fresh": { "version": "3.3.1", "dev": true, @@ -8560,6 +8580,54 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/keyv": { "version": "4.5.4", "dev": true, @@ -8606,6 +8674,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "dev": true, @@ -9527,6 +9604,12 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "dev": true, @@ -10028,6 +10111,12 @@ } } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "dev": true, @@ -10671,6 +10760,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "devOptional": true, diff --git a/package.json b/package.json index c64c8b2..10a30da 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "@prisma/client": "^7.8.0", "bcryptjs": "^3.0.3", "better-sqlite3": "^12.10.0", + "heic2any": "^0.0.4", + "jszip": "^3.10.1", "next": "14.2.35", "next-auth": "^4.24.14", "react": "^18", diff --git a/src/types/images.ts b/src/types/images.ts new file mode 100644 index 0000000..f91f5c9 --- /dev/null +++ b/src/types/images.ts @@ -0,0 +1,40 @@ +export type ImageStatus = + | 'pending' // ajouté, pas encore traité + | 'converting' // HEIC → JPEG en cours + | 'processing' // détourage IA en cours + | 'searching' // recherche produit Shopify en cours + | 'ready' // prêt à uploader + | 'uploading' // upload Shopify en cours + | 'uploaded' // uploadé avec succès + | 'not_found' // référence produit introuvable dans Shopify + | 'error' // erreur à n'importe quelle étape + +export interface ImageProgress { + key: string + percent: number +} + +export interface ImageItem { + id: string + filename: string // nom de fichier original (ex: "REF-1042.heic") + ref: string // référence produit extraite (ex: "REF-1042") + originalBlob: Blob // image originale (pour la vue "avant") + rawPngBlob: Blob | null // PNG transparent issu du détourage IA (cache pour feather) + processedBlob: Blob | null // JPEG final sur fond blanc, pivoté (pour upload + vue "après") + rotation: number // 0, 90, 180, 270 + feather: number // 0–5, défaut 2 + shopifyProductId: string | null + shopifyProductTitle: string | null + uploadedUrl: string | null + status: ImageStatus + error: string | null + progress: ImageProgress | null +} + +export interface ZipEntry { + folder: string // nom du dossier = référence produit + filename: string // nom de fichier dans le dossier + ext: string // extension sans point, en minuscules + blob: Blob // blob avec type MIME correct + isHeic: boolean +}