From e2058cfdbe30f51dcd6dd705e25e46d5657ba78c Mon Sep 17 00:00:00 2001 From: th-kim0823 Date: Mon, 11 May 2026 11:16:14 +0900 Subject: [PATCH] =?UTF-8?q?chore(release):=20v0.3.6=20=E2=80=94=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20modal=20=EB=B3=B5=EC=9B=90=20(v0.3.5=20?= =?UTF-8?q?=EC=9D=98=EB=8F=84=20=EC=A0=95=EC=A0=95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v0.3.5 의 이동 dropdown 단순화가 사용자 의도와 어긋남. 사용자는 dropdown 의 목적지 중복 (modal 도 목적지 묻기) 만 거슬렸지, 사유 입력 + AI 자동 분류 + 수동 status 선택을 한 곳에서 처리하는 modal 은 보존해야 하는 핵심 UX 였음. 단일 "이동" 버튼 → MoveStatusModal path 로 정정. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 20 +++ package-lock.json | 4 +- package.json | 2 +- .../inbox/components/MoveStatusModal.tsx | 150 ++++++++++++++++++ src/renderer/inbox/components/NoteCard.tsx | 135 +++++----------- src/renderer/inbox/components/statusLabel.ts | 26 --- tests/unit/MoveStatusModal.test.tsx | 98 ++++++++++++ tests/unit/NoteCard.test.tsx | 34 +--- 8 files changed, 317 insertions(+), 152 deletions(-) create mode 100644 src/renderer/inbox/components/MoveStatusModal.tsx delete mode 100644 src/renderer/inbox/components/statusLabel.ts create mode 100644 tests/unit/MoveStatusModal.test.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index c43b1ad..c8b14a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ 본 파일은 Inkling 의 버전별 사용자 영향 변경 사항을 기록한다. 형식은 [Keep a Changelog](https://keepachangelog.com/) 를 느슨하게 따른다. +## [0.3.6] — 2026-05-11 + +v0.3.5 의 이동 dropdown 단순화가 사용자 의도와 어긋난 점 정정. 이동 modal (사유 + AI 자동 분류 + 수동 status 선택) 은 보존해야 하는 핵심 UX 였음. + +### 수정 + +- **이동 dropdown → 단일 "이동" 버튼 + `MoveStatusModal` 복원.** v0.3.5 에서 dropdown 항목 클릭 = 즉시 setStatus 로 단순화한 path 를 되돌림. 사용자 의도는 dropdown 의 목적지 중복 (modal 도 목적지 묻기) 제거였지, modal 자체 제거가 아니었음. 단일 "이동" 버튼 → modal → 사유 입력 + AI 자동 분류 + 수동 status 선택 path 로 통일. +- **`MoveStatusModal.tsx` + 테스트 6 case 복원** — v0.3.5 에서 dead code 로 판단해 삭제했으나 다시 mount 됨. statusLabel 헬퍼 위치는 modal 내부로 회귀 (orphan `statusLabel.ts` 제거). +- **이동 후 `refreshMeta()` 호출 유지** — v0.3.5 D1 fix (setStatus IPC 가 pushNoteUpdated emit 안 함 → 헤더 탭 count stale) 는 modal `onMoved` callback path 에서도 동일하게 트리거. + +### 게이트 + +- 단위 734 → **736 PASS** (NoteCard 이동 case 3 → 2 + MoveStatusModal 6 복원) +- typecheck 0 errors (src) +- 신규 npm dependency 0 + +### 업그레이드 + +v0.3.5 인스톨러 위에 v0.3.6 인스톨러를 같은 위치에 실행하면 in-place 업그레이드. 데이터/마이그레이션 변경 없음. + ## [0.3.5] — 2026-05-11 v0.3.4 까지 누적된 dogfood UX 결함 7건 hotfix. 사용자가 막혔던 inbox/회고/이동 3건 + 그 부류의 동반 갭 4건. 데이터/마이그레이션 변경 없음 (스키마 v8 그대로). diff --git a/package-lock.json b/package-lock.json index 34da9fd..554489f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "inkling", - "version": "0.3.5", + "version": "0.3.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "inkling", - "version": "0.3.5", + "version": "0.3.6", "dependencies": { "better-sqlite3": "12.9.0", "electron-log": "5.2.0", diff --git a/package.json b/package.json index ba43d33..8aecc98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "inkling", - "version": "0.3.5", + "version": "0.3.6", "private": true, "description": "Inkling — local-first 한 줄 보관 도구", "author": "altair823 ", diff --git a/src/renderer/inbox/components/MoveStatusModal.tsx b/src/renderer/inbox/components/MoveStatusModal.tsx new file mode 100644 index 0000000..814f0b7 --- /dev/null +++ b/src/renderer/inbox/components/MoveStatusModal.tsx @@ -0,0 +1,150 @@ +import React, { useState } from 'react'; +import { inboxApi } from '../api.js'; +import type { NoteStatus } from '@shared/types'; + +interface Props { + noteId: string; + rawText: string; + summary: string; + onClose: () => void; + onMoved: (status: NoteStatus, reason: string | null) => void; +} + +/** + * v0.2.9 Cut B Task 7 — 메모 이동 Modal. + * + * 사유 입력 + 3 status 버튼 (완료/보관/휴지통) + AI 자동 분류. + */ +export function MoveStatusModal({ + noteId, + onClose, + onMoved +}: Props): React.ReactElement { + const [reason, setReason] = useState(''); + const [recommendation, setRecommendation] = useState<{ + status: NoteStatus; + rationale: string; + } | null>(null); + const [classifying, setClassifying] = useState(false); + + async function move(status: NoteStatus): Promise { + const trimmedReason = reason.trim() === '' ? null : reason.trim(); + await inboxApi.setStatus(noteId, status, trimmedReason); + onMoved(status, trimmedReason); + } + + async function classify(): Promise { + setClassifying(true); + setRecommendation(null); + try { + const r = await inboxApi.classifyStatus(noteId, reason); + setRecommendation({ status: r.recommended, rationale: r.rationale }); + } finally { + setClassifying(false); + } + } + + return ( +
+
+

메모 이동

+