CoolFace
Apppublic

KOOHAWN/AI_PDF_TOOLKIt

sourceHugging Faceupdated 2mo agoView on Hugging Face
0likes
GIF_MOTION_STUDIO_IMPLEMENTATION_PLAN.md888 linesDownload Raw Back to docs
1# GIF Motion Studio 구현 기획서2 3## 1. 문서 목적4 5이 문서는 `smart-pdf-&-ai-merger` 프로젝트에 편집 가능한 디자인 파일을 불러오고, 사용자가 선택한 레이어·오브젝트·영역에 디자인 강조용 모션 프리셋을 적용한 뒤 GIF로 미리보고 내보내는 기능을 구현하기 위한 기준 문서다.6 7대상 입력 형식은 장기적으로 다음을 포함한다.8 9- SVG10- PSD11- PDF12- PDF 호환 AI13- 네이티브 AI/EPS14- HTML15 16단, 모든 형식을 같은 수준으로 편집할 수 있다고 가정하지 않는다. 파일 형식마다 선택 가능한 대상과 적용 가능한 프리셋을 구분한다.17 18## 2. 제품 정의19 20기능명은 임시로 `GIF Motion Studio`로 정의한다.21 22기본 사용자 흐름은 다음과 같다.23 241. 디자인 파일 업로드252. 페이지 또는 아트보드 선택263. 레이어·오브젝트 또는 사각 영역 선택274. 모션 프리셋 선택285. 속도·방향·강도·색상·반복 설정296. 실시간 GIF 미리보기307. 스마트스토어용 860px GIF 내보내기318. 첫 프레임 또는 완성 프레임 PNG 함께 저장32 33원본 PSD·AI·PDF·SVG·HTML은 직접 변경하거나 덮어쓰지 않는다. 원본은 읽기 전용으로 유지하고, 선택 정보와 모션 설정은 별도의 `GifProject` 상태로 관리한다.34 35## 3. 핵심 구현 원칙36 37### 3.1 미리보기와 내보내기는 같은 계산식을 사용한다38 39화면 미리보기용 CSS 애니메이션과 GIF 내보내기용 애니메이션을 따로 작성하지 않는다.40 41하나의 순수 함수 `evaluatePreset(timeMs)`가 특정 시점의 위치, 투명도, 크기, 회전, 클리핑과 오버레이 값을 계산한다. 미리보기와 프레임 내보내기는 이 함수를 공통으로 호출한다.42 43### 3.2 프레임 상태는 절대값으로 계산한다44 45다음과 같은 누적 방식은 금지한다.46 47```ts48object.left += 2;49```50 51반드시 원본 상태를 기준으로 절대값을 계산한다.52 53```ts54const frame = evaluatePreset(context);55object.left = base.left + (frame.translateX ?? 0);56```57 58이 원칙을 지키지 않으면 반복 재생할 때 객체가 계속 이동하거나 미리보기와 GIF 결과가 달라진다.59 60### 3.3 객체 선택형과 영역 선택형을 구분한다61 62- SVG·PSD·HTML: 실제 요소 또는 레이어 선택이 가능하다.63- PDF·AI: 1차 버전에서는 페이지를 래스터 배경으로 렌더링하고 사각 영역만 선택한다.64 65PDF·AI의 사각 영역에는 슬라이드 이동이나 순차 등장처럼 원본 요소를 제거해야 하는 프리셋을 적용하지 않는다. 원본 위에 복사본이 겹쳐 보일 수 있기 때문이다.66 67### 3.4 원본 형식으로 다시 저장하지 않는다68 69이 기능은 PSD·AI·PDF 편집기가 아니다. 원본에서 읽은 레이어 또는 영역을 GIF 제작에 사용하는 기능이다.70 71출력 대상은 다음으로 제한한다.72 73- GIF74- 첫 프레임 PNG75- 마지막 완성 프레임 PNG76- 향후 `gif-project.zip`77 78## 4. 현재 프로젝트에서 재사용할 구조79 80### 프런트엔드81 82- `src/App.tsx`83  - `AppTab`에 `gif-studio` 추가84  - 홈 기능 카드 추가85  - 좌측 메뉴 추가86  - `GifStudioTab` 마운트87- `src/features/illustrator/IllustratorViewerTab.tsx`88  - PSD 레이어 순회 로직 참고89  - PDF.js 페이지 렌더링 로직 참고90  - AI 파일의 PDF 헤더 검사 로직 참고91- `src/features/images/imageClient.ts`92  - 웹 API와 Electron IPC 분기 패턴 참고93 94### 백엔드와 Electron95 96- `server.cjs`97  - Multer 업로드 제한 패턴98  - 임시 디렉터리 생성·삭제 패턴99  - Worker Thread 실행 패턴100- `main.cjs`101  - Electron IPC Worker 실행 패턴102  - 저장 위치 선택 패턴103- `preload.cjs`104  - 안전한 Electron API 노출 패턴105- `workers/image.worker.cjs`106  - 작업별 Worker 진입 구조 참고107- `lib/imageProcessor.cjs`108  - Sharp 출력과 파일명 정리 구조 참고109 110기존 뷰어를 먼저 공통 모듈로 리팩터링하지 않는다. GIF 기능을 신규 모듈로 구현하고 안정화한 뒤 중복 코드를 공통 유틸리티로 옮긴다.111 112## 5. 라이브러리 구성113 114### 5.1 기존 라이브러리115 116| 라이브러리 | 용도 |117| --- | --- |118| `pdfjs-dist` | PDF 페이지를 Canvas로 렌더링 |119| `pdf-lib` | PDF 페이지·문서 구조 확인 |120| `ag-psd` | PSD 레이어·위치·이미지 데이터 읽기 |121| `sharp` | 애니메이션 GIF 인코딩·최적화 |122| `jszip` | 향후 프로젝트 파일 및 결과 묶음 |123| `motion` | 편집기 UI 전환 효과에만 사용 |124| `pixelmatch` | 시각 회귀 테스트 |125| `pngjs` | 테스트 프레임 생성·분석 |126 127### 5.2 새로 추가할 라이브러리128 129#### `fabric`130 131편집 캔버스의 핵심 라이브러리로 사용한다.132 133- 오브젝트 선택134- 다중 선택135- 사각 영역 선택136- 확대·이동·회전137- 객체 순서 관리138- SVG 파싱139- Canvas 내보내기140- 프로젝트 직렬화 보조141 142#### `dompurify`143 144SVG와 HTML에서 스크립트, 이벤트 속성, 위험한 URL을 제거한다.145 146#### `vitest`147 148다음 순수 로직의 단위 테스트에 사용한다.149 150- easing151- 프리셋 계산152- 좌표 변환153- 프레임 샘플링154- GIF 옵션 검증155 156### 5.3 1차 버전에서 추가하지 않을 라이브러리157 158- FFmpeg159- `gif.js`160- `gifenc`161- Lottie162- 영상 편집용 타임라인 라이브러리163 164최종 GIF 인코딩은 기존 Sharp를 사용한다.165 166## 6. 권장 코드 구조167 168```text169src/170  features/171    gif-studio/172      GifStudioTab.tsx173 174      components/175        GifFileDropzone.tsx176        GifEditorShell.tsx177        GifCanvasStage.tsx178        GifLayerPanel.tsx179        GifPresetPanel.tsx180        GifInspectorPanel.tsx181        GifTimelineStrip.tsx182        GifPlaybackControls.tsx183        GifExportDialog.tsx184        GifFormatWarning.tsx185 186      model/187        types.ts188        gifProjectReducer.ts189        initialProject.ts190        selectors.ts191        history.ts192 193      importers/194        importDesignFile.ts195        importSvgDocument.ts196        importPsdDocument.ts197        importPdfDocument.ts198        importAiDocument.ts199        importHtmlDocument.ts200        importerUtils.ts201 202      canvas/203        createEditorCanvas.ts204        createPreviewCanvas.ts205        createExportCanvas.ts206        fabricObjectFactory.ts207        selectionController.ts208        regionSelection.ts209        coordinateSystem.ts210 211      presets/212        presetTypes.ts213        presetRegistry.ts214        easing.ts215        evaluators/216          fadePulse.ts217          popZoom.ts218          slideReveal.ts219          staggerReveal.ts220          markerSweep.ts221          lightSweep.ts222          clickPulse.ts223          beforeAfterWipe.ts224 225      rendering/226        animationEvaluator.ts227        previewPlayer.ts228        frameRenderer.ts229        frameSampler.ts230        overlayRenderer.ts231 232      export/233        gifClient.ts234        electronGifClient.ts235        webGifClient.ts236        exportValidation.ts237        frameBlobEncoder.ts238 239      utils/240        fileType.ts241        dimensions.ts242        objectUrl.ts243        cleanup.ts244 245lib/246  gifEncoder.cjs247 248workers/249  gif.worker.cjs250```251 252## 7. 공통 데이터 모델253 254```ts255export type DesignSourceKind =256  | 'svg'257  | 'psd'258  | 'pdf'259  | 'ai'260  | 'html';261 262export type SelectionCapability =263  | 'object'264  | 'multi-object'265  | 'region'266  | 'text';267 268export interface Rect {269  x: number;270  y: number;271  width: number;272  height: number;273}274 275export interface DesignNode {276  id: string;277  name: string;278  type: 'group' | 'bitmap' | 'vector' | 'text' | 'background';279  bounds: Rect;280  visible: boolean;281  locked: boolean;282  selectable: boolean;283  parentId?: string;284  childIds?: string[];285  assetId?: string;286  sourceMetadata?: Record<string, unknown>;287}288 289export interface DesignPage {290  id: string;291  name: string;292  width: number;293  height: number;294  nodeIds: string[];295  backgroundAssetId?: string;296}297 298export interface DesignDocument {299  id: string;300  name: string;301  sourceKind: DesignSourceKind;302  pages: DesignPage[];303  nodes: Record<string, DesignNode>;304  capabilities: SelectionCapability[];305  warnings: string[];306}307```308 309애니메이션 프로젝트는 별도 모델로 관리한다.310 311```ts312export interface GifProject {313  version: 1;314  documentId: string;315  activePageId: string;316  selection: SelectionTarget | null;317  clips: AnimationClip[];318  playback: PlaybackSettings;319  export: GifExportSettings;320}321 322export type SelectionTarget =323  | { kind: 'objects'; nodeIds: string[] }324  | { kind: 'region'; rect: Rect };325 326export interface AnimationClip {327  id: string;328  presetId: GifPresetId;329  target: SelectionTarget;330  startMs: number;331  durationMs: number;332  delayMs: number;333  easing: EasingId;334  params: Record<string, number | string | boolean>;335}336 337export interface PlaybackSettings {338  durationMs: number;339  loop: boolean;340  fps: number;341}342 343export interface GifExportSettings {344  width: number;345  fps: number;346  loopCount: number;347  colors: 128 | 256;348  dither: number;349  effort: number;350  holdFirstMs: number;351  holdLastMs: number;352}353```354 355프로젝트 상태에는 함수, `HTMLCanvasElement`, Fabric 객체를 직접 저장하지 않는다. 저장 가능한 순수 값만 넣는다.356 357## 8. 프리셋 엔진358 359```ts360export interface FrameState {361  opacity?: number;362  translateX?: number;363  translateY?: number;364  scaleX?: number;365  scaleY?: number;366  rotation?: number;367  clipProgress?: number;368  overlay?: OverlayState;369}370 371export interface PresetEvaluatorContext {372  timeMs: number;373  durationMs: number;374  easing: EasingId;375  params: Record<string, unknown>;376  targetBounds: Rect;377  targetIndex: number;378  targetCount: number;379}380 381export type PresetEvaluator = (382  context: PresetEvaluatorContext383) => FrameState;384```385 386### 8.1 1차 프리셋387 388| 프리셋 | 객체/레이어 | PDF·AI 영역 |389| --- | ---: | ---: |390| 페이드 펄스 | 가능 | 가능 |391| 팝·줌 | 가능 | 제한적 |392| 슬라이드 등장 | 가능 | 불가 |393| 순차 등장 | 가능 | 불가 |394| 마커 스윕 | 가능 | 가능 |395| 라이트 스윕 | 가능 | 가능 |396| 테두리 펄스 | 가능 | 가능 |397| 클릭 포인터 | 가능 | 가능 |398| 가격 숫자 전환 | 2차 | 불가 |399| Before/After | 2차 | 2차 |400 401### 8.2 프리셋 정의402 403```ts404export interface PresetDefinition {405  id: GifPresetId;406  label: string;407  supportedTargets: Array<'objects' | 'region'>;408  requiredCapabilities: SelectionCapability[];409  defaultDurationMs: number;410  defaultParams: Record<string, unknown>;411}412```413 414지원하지 않는 프리셋은 숨기지 않고 비활성 상태로 보여주며, 적용할 수 없는 이유를 표시한다.415 416## 9. 형식별 가져오기417 418### 9.1 SVG419 4201. SVG 문자열 읽기4212. DOMPurify로 스크립트와 위험 속성 제거4223. Fabric `loadSVGFromString()` 실행4234. reviver에서 원본 요소 ID와 태그 정보를 Fabric 객체에 연결4245. 각 객체에 고유한 `gifNodeId` 부여4256. 원래 z-index와 그룹 구조 보존426 427SVG 필터·마스크·외부 폰트가 원본과 다를 수 있으므로 다음 모드를 함께 제공한다.428 429- 요소 편집 모드430- 원본 모양 우선 래스터 모드431 432### 9.2 PSD433 4341. `ag-psd`의 `readPsd()`로 읽기4352. 레이어 트리 순회4363. leaf layer의 canvas를 Fabric Image로 변환4374. left, top, opacity, hidden 적용4385. 그룹과 레이어 순서 보존4396. 합성 미리보기는 원본 비교용으로 별도 보관440 441제한사항:442 443- 조정 레이어444- 클리핑 마스크445- 일부 블렌딩446- 스마트 오브젝트447- 일부 최신 Photoshop 효과448- 완전한 텍스트 스타일 재현449- PSB 공식 보장 불가450 451레이어 합성이 원본과 크게 다르면 합성 이미지 기반 영역 선택 모드로 fallback한다.452 453### 9.3 PDF454 4551. PDF.js로 페이지 목록 읽기4562. 페이지 선택4573. 목표 출력 폭 기준으로 Canvas 렌더링4584. 렌더링 이미지를 잠긴 배경으로 추가4595. 사각 영역 선택만 허용460 4611차 버전에서는 PDF 내부의 개별 텍스트·이미지·패스를 객체로 분해하지 않는다.462 463### 9.4 AI/EPS464 4651. 파일의 PDF 헤더 확인4662. PDF 호환 AI이면 PDF importer 사용4673. 네이티브 AI/EPS이면 기존 `/preview-illustrator` 호출4684. Ghostscript 결과 PDF를 PDF importer로 전달4695. PDF와 동일하게 사각 영역 선택만 허용470 471### 9.5 HTML472 473HTML은 별도 최종 단계로 분리한다.474 475필요 요소:476 477- DOMPurify478- sandbox iframe479- DOM 요소별 안정적인 ID480- iframe과 편집기 사이의 좌표 동기화481- 외부 이미지·폰트 CORS 처리482- Headless Chromium 또는 Electron offscreen rendering483- 네트워크 접근 제한484- 프레임별 스크린샷485 486HTML을 SVG·PSD 단계와 같은 작업에서 구현하지 않는다.487 488## 10. 캔버스 분리489 490### Editor Canvas491 492- 사용자 선택493- 선택 핸들494- 확대·이동495- 영역 박스496- 레이어 편집497 498### Preview Canvas499 500- Editor 상태 복제501- 선택 핸들 제거502- `requestAnimationFrame`으로 미리보기503- 프리셋 계산 결과 표시504 505### Export Canvas506 507- 숨겨진 Fabric `StaticCanvas`508- 860px 또는 원본 출력 크기509- 고정 fps로 프레임 생성510- 화면 확대 배율과 독립511 512```ts513const frameCount = Math.ceil((durationMs / 1000) * fps);514 515for (let index = 0; index < frameCount; index += 1) {516  const timeMs = (index / fps) * 1000;517  applyAbsoluteFrameState(exportCanvas, project, timeMs);518  exportCanvas.renderAll();519  frames.push(await canvasToPngBlob(exportCanvas));520}521```522 523## 11. GIF 내보내기524 525브라우저가 PNG 프레임을 생성하고 서버는 GIF 인코딩만 담당한다.526 527장점:528 529- 미리보기와 결과가 같은 렌더러 사용530- 서버가 PSD·SVG·PDF를 다시 해석할 필요 없음531- 웹과 Electron에서 프리셋 계산식 공유532 533### 11.1 웹 API534 535```http536POST /gif-export537Content-Type: multipart/form-data538 539frames: frame-000.png540frames: frame-001.png541...542options: {543  "width": 860,544  "fps": 12,545  "loopCount": 0,546  "colors": 256,547  "dither": 0.75,548  "effort": 7,549  "delays": [83, 83, 83, 500]550}551```552 553권장 제한:554 555- 최대 90프레임556- 최대 출력 폭 1600px557- 프레임당 최대 8MB558- 전체 업로드 최대 80MB559- PNG만 허용560- 최대 길이 8초561- 임시 디렉터리는 `finally`에서 삭제562 563### 11.2 Sharp 인코더564 565```js566const sharp = require('sharp');567 568async function encodeAnimatedGif({569  framePaths,570  outputPath,571  delays,572  loop,573  colors,574  dither,575  effort,576}) {577  if (!Array.isArray(framePaths) || framePaths.length < 2) {578    throw new Error('GIF에는 2개 이상의 프레임이 필요합니다.');579  }580 581  await sharp(framePaths, {582    join: { animated: true },583  })584    .gif({585      delay: delays,586      loop,587      colours: colors,588      dither,589      effort,590      keepDuplicateFrames: true,591    })592    .toFile(outputPath);593 594  return outputPath;595}596 597module.exports = { encodeAnimatedGif };598```599 600인코딩은 `workers/gif.worker.cjs`에서 실행한다.601 602### 11.3 Electron603 604`preload.cjs`:605 606```js607gifExport: (payload) => ipcRenderer.invoke('gif-export', payload)608```609 610`main.cjs` 처리 순서:611 6121. `dialog.showSaveDialog()`로 저장 위치 선택6132. renderer에서 받은 PNG `Uint8Array`를 임시 파일로 기록6143. GIF Worker 실행6154. 선택 경로에 GIF 저장6165. 임시 파일 삭제617 618IPC로 원시 RGBA 프레임을 보내지 않는다. 압축 PNG만 전달한다.619 620## 12. 상태 관리621 622초기 버전은 `useReducer`를 사용한다.623 624```ts625interface GifStudioState {626  status: 'idle' | 'importing' | 'ready' | 'previewing' | 'exporting';627  document: DesignDocument | null;628  project: GifProject | null;629  selectedNodeIds: string[];630  selectedRegion: Rect | null;631  activePresetId: GifPresetId | null;632  currentTimeMs: number;633  importWarnings: string[];634  error: string | null;635}636```637 638Undo/redo는 첫 MVP 이후 reducer action history로 추가한다.639 640## 13. UI 구성641 642### 좌측 패널643 644- 페이지·아트보드 목록645- 레이어 트리646- 레이어 검색647- 표시·숨김648- 객체 선택과 영역 선택 전환649 650### 중앙 캔버스651 652- 디자인 표시653- 확대·축소654- Fit to screen655- 선택 테두리656- 안전 영역657- 재생 시 편집 핸들 숨김658 659### 우측 패널660 661- 프리셋 목록662- 프리셋 썸네일663- 속도664- 방향665- 강도666- 색상667- 반복668- 시작·종료 정지시간669 670### 하단 컨트롤671 672- 재생·정지673- 처음으로674- 현재 시간675- 총 길이676- 단일 scrubber677- 적용된 clip 표시678 6791차 버전에서는 복수 트랙 전문 타임라인을 구현하지 않는다.680 681## 14. 성능·품질 기준682 683- 기본 출력: 860px, 10fps684- 고품질 출력: 860px, 12fps685- 최대 길이: 8초686- 최대 프레임: 90687- 기본 색상: 256688- 첫·마지막 프레임 정지시간 지원689- Preview는 축소 해상도 사용 가능690- Export만 최종 해상도로 렌더링691- Object URL은 사용 종료 시 revoke692- PSD canvas와 프레임 Blob 정리693- base64 문자열 배열 사용 금지694- 진행률과 취소 기능 제공695- `AbortController`로 내보내기 취소696 697## 15. 테스트 계획698 699### 단위 테스트700 701- easing의 0, 0.5, 1 입력702- 프리셋 시작·중간·마지막 상태703- 영역 좌표 변환704- fps별 프레임 수705- 첫·마지막 hold delay706- 지원하지 않는 target/preset 조합707- 최대 프레임·크기 검증708 709### Importer 테스트710 711- 단순 SVG712- 2개 레이어 PSD713- 2페이지 PDF714- PDF 호환 AI715- 네이티브 AI 변환 실패716 717### GIF 인코더 테스트718 7191. 빨강·초록·파랑 PNG 3개 생성7202. Sharp GIF 생성7213. 애니메이션 metadata 읽기7224. `pages === 3` 확인7235. width, height, loop, delay 확인724 725### 시각 회귀 테스트726 727기존 `pixelmatch`와 `pngjs`로 각 프리셋의 시작·중간·마지막 프레임을 비교한다.728 729### 기본 검증 명령730 731```powershell732node .\node_modules\typescript\bin\tsc --noEmit733node .\node_modules\vite\bin\vite.js build734node --check server.cjs735node --check main.cjs736node --check workers/gif.worker.cjs737node --check lib/gifEncoder.cjs738```739 740## 16. 단계별 구현 계획741 742### 단계 1: GIF 인코더 기술 검증743 744구현:745 746- `lib/gifEncoder.cjs`747- `workers/gif.worker.cjs`748- 3개 PNG를 GIF로 생성하는 검증 스크립트749 750완료 조건:751 752- 3프레임 GIF 생성753- 프레임 수·delay·loop 검증754- Node 문법 검사 통과755 756### 단계 2: PNG 기반 편집기 뼈대757 758구현:759 760- Fabric Canvas761- PNG 업로드762- 사각 영역 선택763- 마커·라이트·테두리 프리셋764- 미리보기765- 웹 GIF 내보내기766 767완료 조건:768 769- 선택 영역이 정확히 표시됨770- 미리보기와 내보낸 GIF의 핵심 프레임 일치771- 860px 결과 생성772 773### 단계 3: SVG 객체 선택774 775구현:776 777- DOMPurify 처리778- SVG 객체 분리779- 레이어 선택780- 팝·슬라이드·순차 등장781- z-index 보존782 783완료 조건:784 785- 단순 SVG에서 객체 선택 가능786- 다중 선택 순차 등장 가능787- unsupported 요소 경고788 789### 단계 4: PSD 레이어 선택790 791구현:792 793- 레이어 트리794- PSD layer canvas → Fabric Image795- 표시·숨김796- 레이어 모션797- 합성 이미지 fallback798 799완료 조건:800 801- 테스트 PSD 레이어 순서 일치802- 원본 합성 미리보기 제공803- 지원 불완전 파일 경고804 805### 단계 5: PDF·AI806 807구현:808 809- PDF 페이지 선택810- PDF 호환 AI811- Ghostscript 변환 AI/EPS812- 영역형 프리셋 제한813 814완료 조건:815 816- PDF 페이지가 860px 기준으로 렌더링817- AI 변환 결과를 PDF importer가 처리818- 객체 이동형 프리셋 비활성화819 820### 단계 6: 앱·Electron 통합821 822구현:823 824- 홈 카드와 메뉴825- `/gif-export`826- Electron IPC827- 진행률·오류·취소828- 저장 대화상자829 830완료 조건:831 832- 웹과 Electron 양쪽에서 GIF 저장833- 임시 파일 정리834- TypeScript와 Vite 빌드 통과835 836### 단계 7: 고급 프리셋837 838- 가격 전환839- 숫자 카운트840- Before/After841- 복수 clip842- Undo/redo843- 프로젝트 저장·불러오기844 845### 단계 8: HTML 기술 검증·구현846 847HTML은 별도 작업으로 수행한다. 다른 형식 구현과 같은 단계에 포함하지 않는다.848 849## 17. GPT-5.6 Sol High 실행 권장안850 851GPT-5.6 Sol High에는 이 문서 전체를 참고자료로 제공하되, 한 번에 전체 구현을 지시하지 않는다.852 853### 권장 방식854 855- 단계 1부터 순서대로 진행856- 한 작업에서는 한 단계만 구현857- 단계가 끝날 때마다 지정된 검증 수행858- 결과와 남은 위험을 보고한 뒤 중단859- 다음 단계는 새 작업에서 시작860- 기존 파일의 관련 없는 변경 금지861- 기존 Viewer를 초기 단계에서 리팩터링하지 않음862 863### 한 번에 시키면 위험한 범위864 865- 모든 파일 형식 동시 지원866- HTML 보안과 Chromium 렌더링867- PDF·AI 내부 객체 자동 분리868- PSD 원본과 100% 동일한 레이어 합성869- 전문 타임라인870- 고급 프리셋과 프로젝트 저장871- 성능 최적화까지 동시 구현872 873### 모델에 전달할 핵심 지시문874 875> 전체 기능을 한 번에 구현하지 말 것. 현재 단계의 파일 범위 밖을 수정하지 말고, 해당 단계의 타입 검사·빌드·핵심 동작 검증이 통과한 뒤 중단하여 결과를 보고할 것. 미리보기와 내보내기는 반드시 동일한 순수 프리셋 평가 함수를 사용한다. PDF·AI의 사각 영역에는 객체 이동형 프리셋을 허용하지 않는다. 기존 뷰어와 이미지 도구의 동작을 깨뜨리지 않는다.876 877## 18. 최종 판단878 879GPT-5.6 Sol High는 이 기능을 구현할 모델로 충분히 적합한 선택이다. 다만 이 판단은 단계별 구현을 전제로 한다.880 881- 단계 1~2: Sol High로 충분882- 단계 3~4: Sol High로 가능, 시각 검증 필요883- 단계 5~6: Sol High로 가능, 웹·Electron 경로를 분리 검증해야 함884- 단계 7: 별도 작업 권장885- 단계 8 HTML: 별도 기술 설계와 강한 검증 필요886 887전체 범위를 한 번에 지시하는 방식은 모델 등급과 무관하게 권장하지 않는다.888