덱 목록 무한 스크롤과 WebP 썸네일 최적화 추가

- 내 덱·공개 덱 목록을 무한 스크롤로 전환(공용 페이저 모듈 추가)
- 즐겨찾기 토글 시 카드만 in-place 갱신해 스크롤 위치 유지
- 저장 시 420px WebP 썸네일 생성·전송, 목록은 썸네일 우선 사용
- 삭제·미참조 정리에서 원본 PNG와 썸네일 함께 처리

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-02 18:17:58 +09:00
parent 63aabf61c4
commit a90bdcbe13
15 changed files with 516 additions and 85 deletions

View File

@@ -841,6 +841,7 @@
/** 저장·다운로드 캡처와 동일한 미리보기 기준 너비(px) */
const DECK_PREVIEW_SAVE_WIDTH = 1200;
const DECK_THUMBNAIL_WIDTH = 420;
/**
* 저장용 미리보기를 기준 너비로 정규화
@@ -880,6 +881,50 @@
});
}
/**
* 목록용 축소 WebP 썸네일 생성 (WebP 인코딩 실패 시 null)
* @param {string} dataUrl 원본 미리보기 data URL
* @param {number} [targetWidth]
* @returns {Promise<string|null>}
*/
async function createThumbnailWebp(
dataUrl,
targetWidth = DECK_THUMBNAIL_WIDTH,
) {
return new Promise((resolve) => {
const img = new Image();
img.onload = () => {
if (!img.width) {
resolve(null);
return;
}
const w = Math.min(targetWidth, img.width);
const h = Math.max(1, Math.round(img.height * (w / img.width)));
const canvas = document.createElement('canvas');
canvas.width = w;
canvas.height = h;
const ctx = canvas.getContext('2d');
if (!ctx) {
resolve(null);
return;
}
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, w, h);
ctx.drawImage(img, 0, 0, w, h);
let out = null;
try {
out = canvas.toDataURL('image/webp', 0.82);
} catch {
out = null;
}
// WebP 미지원 브라우저는 PNG로 폴백되므로 이 경우 전송하지 않는다.
resolve(out && out.startsWith('data:image/webp') ? out : null);
};
img.onerror = () => resolve(null);
img.src = dataUrl;
});
}
/**
* 저장·공유 로딩 UI
* @param {string} message
@@ -1131,10 +1176,12 @@
loader.setProgress(8, t('savingPreview', '미리보기 생성 중...'));
loader.startAuto(52);
let previewImage = null;
let thumbnailImage = null;
try {
previewImage = await capturePreviewPng({ highQuality: false });
if (previewImage) {
previewImage = await normalizePreviewForSave(previewImage);
thumbnailImage = await createThumbnailWebp(previewImage);
}
} catch (captureErr) {
console.warn('Preview capture failed:', captureErr);
@@ -1148,6 +1195,7 @@
name: getDeckSaveName(),
cards,
previewImage,
thumbnailImage,
isPublic,
});
loader.stopAuto();