- 회색 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
- onDeleted: () => void → onDeleted?: () => void (inbox mode 전용 명시)
- handleDelete 내부 onDeleted() → onDeleted?.()
- App.tsx 의 trash mode NoteCard 가 onDeleted prop 미전달 (dead-code 제거)
- API 시그니처 정리 — trash mode 는 onPermanentDelete/onRestore 만 의미
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- RecallBanner: 노트 제목 + N일 전 + 3 버튼 (열어보기/다음에/더 이상)
- 첫 렌더 시 emitRecallShown (recallShownIds Set 으로 per-session 1회 제약)
- snoozeUntilMs 만료 setTimeout (ExpiryBanner 패턴)
- 위치: ExpiryBanner 다음 (banner stack 끝)
- NoteCard 외곽 div 에 id="note-${note.id}" — "열어보기" scrollIntoView target
- 컬러 테마: 파랑 (#e8f0fe / #4a7ec0) — 다른 banner (적/황/적) 와 구별
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Splits the tag chip into two actions per F2 dogfood feedback:
- short click on chip text → applies the tag to the inbox filter
(Inbox header shows "🔎 필터: #tag (n개)" banner with ✕ 해제 button)
- × button on chip → immediately removes the tag and surfaces a
module-level pub/sub undo toast for 5 seconds; clicking the toast
restores the tag
`TagUndoToast` is a tiny self-contained component: `pushTagUndo()` from
NoteCard publishes an entry, the mounted `<TagUndoToast />` near the
end of `<App>` subscribes and renders it. Auto-dismiss after 5 s,
click-to-undo cancels the timer and runs the restore callback.
AI vs user tags share the same behaviour — only the chip background
colour distinguishes them, matching the F2 decision table.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inline 📅 YYYY-MM-DD badge appears in NoteCard between summary and
tags. Click to edit (HTML date input). Past dates: gray + line-through.
AI label shown when not user-edited (mirrors title/summary AI badge
policy). Empty state shows '📅 마감일 추가' link in gray.
New IPC inbox:setDueDate routes to NoteRepository.setDueDate which
sets due_date_edited_by_user=1 (per slice §1.1 invariant 2 — user
edit blocks future AI overwrite). Preload bridge + InboxApi type
extended.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 25 of the slice plan. Single-card view of a note with
local optimistic state plus the IPC writes. Per-status branches:
- pending: 'Inkling이 정리하는 중…' headline, raw text auto-open.
- failed: '정리 보류 — 원문은 안전합니다' with hover tooltip.
- done: editable title + summary (each with the gray 'AI' badge
hidden as soon as *_edited_by_user flips), tag chips with
AI subscript, optional 💡 user_intent row, IntentBanner
surfaced exactly when intent_prompted_at is null.
Tag click removes (per spec), title/summary edits route through
inboxApi.updateAiFields (which sets the edited flag server-side),
intent edits use inboxApi.setIntent. Delete confirms with '이
기억을 버릴까요? 되돌릴 수 없습니다.' and routes through
CaptureService.deleteNote so media gets cleaned up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Task 22 of the slice plan. Wires the Inbox window's renderer:
- index.html (replaces the placeholder shipped in Task 2) with
the full layout styles + module script.
- api.ts re-exports window.inkling.inbox as a typed InboxApi.
- recoveryToast.ts persists per-day toast dismissal in
localStorage (KST date key) so the banner never re-fires
after the user closes it.
- store.ts: zustand store with notes, continuity, pendingCount,
ollamaStatus, loadInitial(), refreshMeta(), upsert/remove.
- main.tsx mounts <App />.
- App.tsx orchestrates loadInitial + onNoteUpdated subscription
+ window-focus refresh, renders header / banners / note list.
- 7 component stubs (NoteCard / EditableField / IntentBanner /
RecoveryToast / ContinuityBadge / PendingBanner / OllamaBanner)
so the shell typechecks today; Tasks 23-28 swap each in.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>