v0.2.8 Cut A — 이미지 렌더링 + 앱 아이콘 (F22 + chore) #26

Merged
altair823 merged 6 commits from worktree-v028-cut-a-image-icon into main 2026-05-09 05:57:51 +00:00
Owner

Summary

v0.2.8 Cut A — F22 (이미지 렌더링) + chore (앱 아이콘). 작은 polish cut.

  • F22 이미지 렌더링: NoteCard 의 회색 placeholder div → <img src="inkling-media://${m.relPath}">. main process 에 custom protocol 등록 (inkling-media://), <profileDir>/media/ 하위 파일 서빙 + path traversal 두 layer 검사 (raw URL ../%2e%2e 차단 + normalize startsWith). 클릭 시 inbox:open-media IPC → shell.openPath 로 OS 기본 viewer 열기.
  • chore 앱 아이콘: assets/icon.svg (이전 turn 작성) → electron-icon-builder + sharp (SVG → PNG 1024 fallback) + finalize-icons.mjs (출력 위치 정규화) → build/icon.{ico,icns,png} 산출 + package.json build.win/mac/linux.icon 매핑.

변경 내역 (6 commits)

  • 470384b Task 1 — inkling-media:// custom protocol + path traversal 검사
  • f6bea62 Task 2 — NoteCard 이미지 <img> 렌더링 + onClick (cast 임시)
  • 9cdea15 Task 3 — IPC inbox:open-media + path traversal + NoteCard cast 정리
  • 4d4dac5 Task 4 — 앱 아이콘 (assets/icon.svg → ICO/ICNS/PNG) + electron-builder config
  • 29259ee Task 5 — v0.2.8 release + dogfood F22 promoted 마킹
  • 6db449f chore — final review minor 3건 cleanup (no-op replace 제거 / 빈 relPath 거절 / alt="" 코멘트)

테스트 / 빌드

  • 단위: 460 → 472 pass (+12: inferMime 5 + protocol handler 3 + NoteCard <img> 2 + IPC 2)
  • typecheck: 0 errors
  • e2e: 1/1 pass
  • 신규 산출물: build/icon.ico (361 KB, 7 multi-res) + build/icon.icns (119 KB) + build/icon.png (45 KB, 1024×1024)

Mac/Linux 빌드 핸드오프

npm run dist:linux 또는 dist:mac 머지 후 release 단계에서 검증 가능. macOS host 에서 dmg/AppImage/deb 산출 가능 (이전 v0.2.7 release 와 동일 흐름).

Test Plan

  • dogfood 1회: 이미지 첨부 노트 → inbox 표시 → thumbnail 클릭 → OS viewer (Win Photos / macOS Preview) 열림
  • 새 아이콘이 트레이 / Windows taskbar / dock 정확 표시 (Inkling Setup 0.2.8.exe 빌드 + 설치)
  • Linux dist 머지 후 검증 — AppImage/deb 산출 + Linux VM smoke
  • path traversal 시도 (URL bar 또는 dev tools 로 inkling-media://media/../etc/passwd) 가 403 응답 + OS viewer 열리지 않는지

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

## Summary v0.2.8 Cut A — F22 (이미지 렌더링) + chore (앱 아이콘). 작은 polish cut. - **F22 이미지 렌더링**: NoteCard 의 회색 placeholder div → `<img src="inkling-media://${m.relPath}">`. main process 에 custom protocol 등록 (`inkling-media://`), `<profileDir>/media/` 하위 파일 서빙 + path traversal 두 layer 검사 (raw URL `..`/`%2e%2e` 차단 + normalize startsWith). 클릭 시 `inbox:open-media` IPC → `shell.openPath` 로 OS 기본 viewer 열기. - **chore 앱 아이콘**: `assets/icon.svg` (이전 turn 작성) → `electron-icon-builder` + `sharp` (SVG → PNG 1024 fallback) + `finalize-icons.mjs` (출력 위치 정규화) → `build/icon.{ico,icns,png}` 산출 + `package.json` build.win/mac/linux.icon 매핑. ## 변경 내역 (6 commits) - `470384b` Task 1 — `inkling-media://` custom protocol + path traversal 검사 - `f6bea62` Task 2 — NoteCard 이미지 `<img>` 렌더링 + onClick (cast 임시) - `9cdea15` Task 3 — IPC `inbox:open-media` + path traversal + NoteCard cast 정리 - `4d4dac5` Task 4 — 앱 아이콘 (assets/icon.svg → ICO/ICNS/PNG) + electron-builder config - `29259ee` Task 5 — v0.2.8 release + dogfood F22 promoted 마킹 - `6db449f` chore — final review minor 3건 cleanup (no-op replace 제거 / 빈 relPath 거절 / alt="" 코멘트) ## 테스트 / 빌드 - 단위: 460 → **472 pass** (+12: inferMime 5 + protocol handler 3 + NoteCard `<img>` 2 + IPC 2) - typecheck: **0 errors** - e2e: **1/1 pass** - 신규 산출물: `build/icon.ico` (361 KB, 7 multi-res) + `build/icon.icns` (119 KB) + `build/icon.png` (45 KB, 1024×1024) ## Mac/Linux 빌드 핸드오프 `npm run dist:linux` 또는 `dist:mac` 머지 후 release 단계에서 검증 가능. macOS host 에서 dmg/AppImage/deb 산출 가능 (이전 v0.2.7 release 와 동일 흐름). ## Test Plan - [ ] dogfood 1회: 이미지 첨부 노트 → inbox 표시 → thumbnail 클릭 → OS viewer (Win Photos / macOS Preview) 열림 - [ ] 새 아이콘이 트레이 / Windows taskbar / dock 정확 표시 (`Inkling Setup 0.2.8.exe` 빌드 + 설치) - [ ] Linux dist 머지 후 검증 — AppImage/deb 산출 + Linux VM smoke - [ ] path traversal 시도 (URL bar 또는 dev tools 로 `inkling-media://media/../etc/passwd`) 가 403 응답 + OS viewer 열리지 않는지 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) &lt;noreply@anthropic.com&gt;
altair823 added 6 commits 2026-05-09 05:31:07 +00:00
- registerSchemesAsPrivileged: inkling-media 스킴을 secure + supportFetchAPI + stream 으로 등록 (whenReady 이전 호출 필수).
- registerInklingMediaProtocol: profileDir/media 하위 파일을 raw URL traversal (.., %2e%2e) 검사 + normalize 후 mediaRoot 봉쇄로 이중 검증 후 readFile.
- inferMime: png/jpg/jpeg/gif/webp → image/*, 그 외 → application/octet-stream.
- src/main/index.ts: 모듈 import 직후 registerSchemesAsPrivileged(), whenReady 안 paths 결정 직후 registerInklingMediaProtocol(paths.profileDir).
- tests/unit/inklingMedia.test.ts: 8 unit (5 inferMime + 3 handler — valid/403/404). vitest 의 new Request() 가 url 을 normalize 하므로 raw url 보존을 위해 minimal mock req 사용.
- 회색 placeholder div → <img src=inkling-media://...> 로 교체
- onClick 으로 inboxApi.openMedia(relPath) 호출 (현재는 InboxApi 인터페이스에 부재 → unknown cast 사용; Task 3 에서 정식 시그니처 추가 후 cast 제거 예정)
- alt='' 로 decorative 처리 (role=presentation), title 에 relPath 유지
- flex-wrap 추가 — 다수 이미지 시 줄바꿈

Tests: tests/unit/NoteCard.test.tsx 신규 2건 (img src 검증, click → openMedia 호출)
회귀: 468 → 470 pass
- electron-icon-builder + sharp devDep 추가
- assets/icon.svg → build/icon.{ico,icns,png} 산출 + git 추적
- electron-icon-builder 가 SVG 직접 input 안 받음 (Jimp MIME 에러) — sharp 로 SVG → PNG 1024 변환 후 input
- scripts/svg-to-png.mjs (sharp 사용 SVG→PNG) + scripts/finalize-icons.mjs (build/icons/ → build/ 정규 위치 정리)
- package.json build.{win,mac,linux}.icon 키 추가
- .gitignore: build/icons/ 와 build/icon-source.png (중간 산출물) 무시, build/icon.* 는 추적
- typecheck 0 errors + 472/472 단위 통과 유지 (회귀 없음)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- inklingMedia.ts:39 no-op replace 제거 + 명료한 host+pathname 결합 코멘트
- inbox:open-media 빈 relPath 명시적 거절 (typeof + length 검사)
- NoteCard <img> alt="" decorative 의도 코멘트

472/472 + typecheck 0 유지.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Author
Owner

코드 리뷰 — v0.2.8 Cut A

Scope: 6 commits / +2300+ insertions (대부분 build/icon.* 바이너리)

Spec coverage 10/10

Cut A design 의 모든 섹션이 task 매핑됨.

Spec 구현
§3-1 protocol scheme + handler Task 1 (470384b)
§3-2 NoteCard <img> Task 2 (f6bea62)
§3-3 IPC inbox:open-media Task 3 (9cdea15)
§3-4 보안 (defense in depth) Task 1 + Task 3 (raw URL ../%2e%2e + normalize startsWith 두 layer)
§4 앱 아이콘 (devDep + scripts + builder config + 산출물) Task 4 (4d4dac5)
§5 테스트 460 → 472 (+12) 전체 task
§6 Risk fallback Task 4 (sharp + finalize-icons.mjs)

코드 품질

Strengths

  • Defense in depth path traversal: protocol handler 와 IPC handler 각각 두 layer 검사 (raw URL + normalize startsWith) — 두 곳 모두 동일 패턴 일관 적용
  • Privileged scheme 등록 timing: app.whenReady() 이전 registerSchemesAsPrivileged() — Electron 표준 준수
  • Test 의 raw URL mock: Request constructor 의 normalize 회피하는 minimal mock 패턴 — traversal 검사 로직 검증 의도 정확
  • Type 안전성: openMedia: Promise<{ ok: true } | { ok: false; reason: string }> discriminated union — Task 2 임시 cast → Task 3 정식 시그니처로 정리
  • 빌드 fallback: electron-icon-builder 의 SVG 미지원 한계를 sharp 로 우회 + finalize-icons.mjs 위치 정규화 — 재현 가능 + 명시적

Minor (PR 머지 전 cleanup 6db449f 에서 모두 처리됨)

  • inklingMedia.ts:39 의 no-op replace${url.host}${url.pathname} 로 단순화
  • inbox:open-media 의 빈 relPath 검사 부재typeof relPath !== 'string' || relPath.length === 0 명시 거절
  • NoteCard alt="" decorative 의도 불명확 → 한 줄 코멘트 추가

Critical / Important 없음.

Architecture

  • 단위 분해: protocol/inklingMedia.ts 가 inferMime + scheme 등록 + handler 등록 세 책임 분리. <img> (renderer 표시) ↔ inkling-media:// (asset 서빙) ↔ inbox:open-media (OS viewer) 세 경로 명확히 분리
  • 책임 명확성: 각 layer 가 자기 layer 만 담당. 누설 없음
  • 일관성: 두 path traversal 검사 baseline (mediaRoot + sep startsWith) 동일

Risk 잔재

  • Dogfood 미검증 — 실제 packaged build (asar) 의 inkling-media:// 동작 미확인. release 후 수동 검증.
  • Mac/Linux 빌드 미검증dist:mac / dist:linux 미수행. 머지 후 macOS host 핸드오프 (이전 v0.2.7 release 와 동일 흐름).
  • e2e packaged 미검증 — Playwright e2e 1개는 dev 환경. packaged protocol 등록 검증 X.

머지 권장

  1. PR # 26 머지 (5 task + 1 cleanup commit)
  2. tag v0.2.8 + npm run dist:win → Windows exe 빌드
  3. Gitea release v0.2.8 생성 + exe attach (이전 v0.2.7 release 와 동일 흐름)
  4. macOS host: dist:mac + dist:linux 후 dmg/AppImage/deb 추가 attach

Overall

Ready to merge. Spec 100% coverage, 472 unit + 1 e2e + typecheck 0, defense in depth 보안, 3 minor 모두 fix. Cut B (v0.2.9) 로 진행 가능.

🤖 Reviewed by Claude Opus 4.7 (1M context)

## 코드 리뷰 — v0.2.8 Cut A **Scope**: 6 commits / +2300+ insertions (대부분 build/icon.* 바이너리) ### Spec coverage ✅ 10/10 [Cut A design](docs/superpowers/specs/2026-05-09-v028-cut-a-design.md) 의 모든 섹션이 task 매핑됨. | Spec | 구현 | |---|---| | §3-1 protocol scheme + handler | Task 1 (`470384b`) | | §3-2 NoteCard `<img>` | Task 2 (`f6bea62`) | | §3-3 IPC inbox:open-media | Task 3 (`9cdea15`) | | §3-4 보안 (defense in depth) | Task 1 + Task 3 (raw URL `..`/`%2e%2e` + normalize startsWith 두 layer) | | §4 앱 아이콘 (devDep + scripts + builder config + 산출물) | Task 4 (`4d4dac5`) | | §5 테스트 460 → 472 (+12) | 전체 task | | §6 Risk fallback | Task 4 (sharp + finalize-icons.mjs) | ### 코드 품질 **Strengths** - **Defense in depth path traversal**: protocol handler 와 IPC handler 각각 두 layer 검사 (raw URL + normalize startsWith) — 두 곳 모두 동일 패턴 일관 적용 - **Privileged scheme 등록 timing**: `app.whenReady()` 이전 `registerSchemesAsPrivileged()` — Electron 표준 준수 - **Test 의 raw URL mock**: `Request` constructor 의 normalize 회피하는 minimal mock 패턴 — traversal 검사 로직 검증 의도 정확 - **Type 안전성**: `openMedia: Promise<{ ok: true } | { ok: false; reason: string }>` discriminated union — Task 2 임시 cast → Task 3 정식 시그니처로 정리 - **빌드 fallback**: electron-icon-builder 의 SVG 미지원 한계를 sharp 로 우회 + finalize-icons.mjs 위치 정규화 — 재현 가능 + 명시적 **Minor (PR 머지 전 cleanup `6db449f` 에서 모두 처리됨)** - ~~`inklingMedia.ts:39` 의 no-op replace~~ → `${url.host}${url.pathname}` 로 단순화 - ~~`inbox:open-media` 의 빈 relPath 검사 부재~~ → `typeof relPath !== 'string' || relPath.length === 0` 명시 거절 - ~~NoteCard `alt=""` decorative 의도 불명확~~ → 한 줄 코멘트 추가 Critical / Important 없음. ### Architecture - **단위 분해**: `protocol/inklingMedia.ts` 가 inferMime + scheme 등록 + handler 등록 세 책임 분리. `<img>` (renderer 표시) ↔ `inkling-media://` (asset 서빙) ↔ `inbox:open-media` (OS viewer) 세 경로 명확히 분리 - **책임 명확성**: 각 layer 가 자기 layer 만 담당. 누설 없음 - **일관성**: 두 path traversal 검사 baseline (`mediaRoot + sep` startsWith) 동일 ### Risk 잔재 - **Dogfood 미검증** — 실제 packaged build (asar) 의 `inkling-media://` 동작 미확인. release 후 수동 검증. - **Mac/Linux 빌드 미검증** — `dist:mac` / `dist:linux` 미수행. 머지 후 macOS host 핸드오프 (이전 v0.2.7 release 와 동일 흐름). - **e2e packaged 미검증** — Playwright e2e 1개는 dev 환경. packaged protocol 등록 검증 X. ### 머지 권장 1. PR # 26 머지 (5 task + 1 cleanup commit) 2. tag `v0.2.8` + `npm run dist:win` → Windows exe 빌드 3. Gitea release v0.2.8 생성 + exe attach (이전 v0.2.7 release 와 동일 흐름) 4. macOS host: `dist:mac` + `dist:linux` 후 dmg/AppImage/deb 추가 attach ### Overall **Ready to merge.** Spec 100% coverage, 472 unit + 1 e2e + typecheck 0, defense in depth 보안, 3 minor 모두 fix. Cut B (v0.2.9) 로 진행 가능. 🤖 Reviewed by Claude Opus 4.7 (1M context)
altair823 merged commit b20473a593 into main 2026-05-09 05:57:51 +00:00
altair823 deleted branch worktree-v028-cut-a-image-icon 2026-05-09 05:58:13 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: altair823-org/inkling#26