Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Key files:
- `AppDelegate.swift`: app lifecycle and hotkey event monitors
- `OverlayWindowController.swift`: floating `NSPanel` lifecycle
- `OverlayContentView.swift`: overlay host and Apple Translation session injection
- `TextDetector.swift`: Accessibility selected-text detection plus clipboard fallback
- `TextDetector.swift`: clipboard text detection through `NSPasteboard`
- `TranslationEngine.swift`: Apple `TranslationSession` wrapper and language persistence
- `TranslationViewModel.swift`: translation state, debounce, retranslation flow, UI coordination

Expand Down Expand Up @@ -87,7 +87,7 @@ Keep SwiftUI views view-focused. Put AppKit/window/platform lifecycle code in de
Add XCTest coverage for new app behavior in `transScanTests/`.

Prefer focused tests around:
- text normalization and clipboard fallback
- text normalization and clipboard polling
- translation engine session/language behavior
- view model state transitions
- shortcut validation and persistence
Expand Down
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ Start with `AGENTS.md`. It is the shared workflow contract for how agents should

## Project Overview

transScan은 macOS 전용 번역 오버레이 앱이다. 사용자가 텍스트를 드래그한 뒤 설정된 단축키를 누르면 메뉴바 앱이 floating overlay를 띄우고, 선택 텍스트 또는 클립보드 텍스트를 Apple Translation framework로 번역해 보여준다. Dock 아이콘 없이 메뉴바에 상주하는 도구다.
transScan은 macOS 전용 번역 오버레이 앱이다. 사용자가 텍스트를 복사한 뒤 설정된 단축키를 누르면 메뉴바 앱이 floating overlay를 띄우고, 클립보드 텍스트를 Apple Translation framework로 번역해 보여준다. Dock 아이콘 없이 메뉴바에 상주하는 도구다.

## Architecture Decisions

- **번역 엔진**: Apple Translation framework의 `TranslationSession` 사용. OpenAI, Google Translate, DeepL, custom LLM API를 사용하지 않는다.
- **텍스트 감지**: Accessibility API(`AXUIElement`)로 선택 텍스트를 읽고, 실패하면 클립보드 fallback을 사용한다.
- **텍스트 감지**: 현재 안정 동작은 `NSPasteboard` 클립보드 변경 감지다. 직접 선택 텍스트 감지는 roadmap의 미래 목표로만 다룬다.
- **UI**: SwiftUI + `NSPanel`. 오버레이는 floating, non-activating, draggable 패널이다.
- **앱 형태**: `LSUIElement=YES` menu bar app. `MenuBarExtra`에서 오버레이 열기, 설정, 권한 설정, 종료를 제공한다.
- **앱 형태**: `LSUIElement=YES` menu bar app. `MenuBarExtra`에서 오버레이 열기, 설정, 종료를 제공한다.
- **단축키**: `HotkeyConfiguration`이 저장/검증을 담당한다. 기본값은 `⌥E`이고, 설정창에서 변경 가능하다.
- **브랜딩**: 앱 아이콘, 메뉴바 아이콘, README/마케팅 로고를 분리해서 관리한다. 메뉴바 아이콘은 template image asset이어야 한다.

Expand All @@ -31,7 +31,7 @@ transScan은 macOS 전용 번역 오버레이 앱이다. 사용자가 텍스트
2. `AppDelegate.swift` handles local/global hotkey monitors.
3. `OverlayWindowController.swift` owns the `NSPanel`.
4. `OverlayContentView.swift` injects the Translation session.
5. `TextDetector.swift` emits selected/clipboard text.
5. `TextDetector.swift` emits clipboard text.
6. `TranslationViewModel.swift` coordinates state and retranslation.
7. `TranslationEngine.swift` wraps Apple Translation and language persistence.

Expand Down
11 changes: 5 additions & 6 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ MVP는 macOS 전용 SwiftUI/AppKit 앱으로 구현되어 있습니다.
- `LSUIElement=YES` 기반 메뉴바 앱
- `NSPanel` + SwiftUI로 만든 항상 위에 뜨는 번역 오버레이
- `UserDefaults`에 저장되는 사용자 설정 단축키
- 클립보드 fallback과 Accessibility 선택 텍스트 감지
- `NSPasteboard` 기반 클립보드 텍스트 감지
- 소스 언어 자동 감지 또는 수동 선택
- 타겟 언어 선택 및 소스/타겟 언어 전환
- `.translationTask`를 통한 Apple Translation framework 세션 주입
Expand All @@ -49,7 +49,6 @@ MVP에서 제외한 범위:

- Xcode가 설치된 macOS 개발 환경
- 대상 macOS 버전의 Apple Translation framework 지원
- 선택 텍스트 감지를 위한 손쉬운 사용 권한

현재 Xcode 프로젝트는 generated Info.plist 설정, `LSUIElement=YES`, bundle id `com.finn.transScan`을 사용합니다.

Expand Down Expand Up @@ -81,7 +80,7 @@ xcodebuild -project transScan.xcodeproj -scheme transScan test
2. `AppDelegate.swift`가 global/local key monitor를 등록하고 오버레이를 토글합니다.
3. `OverlayWindowController.swift`가 플로팅 `NSPanel` 생명주기를 관리합니다.
4. `OverlayContentView.swift`가 SwiftUI 오버레이를 호스팅하고 `.translationTask`로 Translation session을 주입합니다.
5. `TextDetector.swift`가 Accessibility API와 클립보드 polling으로 텍스트를 감지합니다.
5. `TextDetector.swift`가 `NSPasteboard` 클립보드 변경을 감지합니다.
6. `TranslationViewModel.swift`가 감지 텍스트 debounce, 번역 상태, 언어 전환, UI 흐름을 관리합니다.
7. `TranslationEngine.swift`가 Apple Translation, 언어 저장, 언어팩 준비, 세션 준비 전 pending request를 처리합니다.

Expand All @@ -97,7 +96,7 @@ transScan/ macOS app source
OverlayContentView.swift overlay host and Translation session injection
TranslationViewModel.swift translation state and UI flow
TranslationEngine.swift Apple Translation wrapper
TextDetector.swift Accessibility and clipboard text detection
TextDetector.swift Clipboard text detection
SettingsView.swift shortcut/settings UI

transScanTests/ XCTest coverage
Expand Down Expand Up @@ -181,7 +180,7 @@ macOS 특화 작업에는 `build-macos-apps` 플러그인을 사용합니다.

중점 테스트:

- 텍스트 정규화와 클립보드 fallback
- 텍스트 정규화와 클립보드 polling
- 중복 텍스트 suppression
- translation engine 언어/세션 동작
- view model 상태 전환
Expand Down Expand Up @@ -212,4 +211,4 @@ PR에는 다음을 포함합니다.
- 관련 issue 또는 decision note
- build/test 결과
- UI 변경의 screenshot 또는 screen recording
- 손쉬운 사용 권한 또는 수동 macOS 검증 notes
- 클립보드 흐름 또는 수동 macOS 검증 notes
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The MVP is implemented as a macOS-only SwiftUI/AppKit app.
- Menu bar app with `LSUIElement=YES`
- Floating always-on-top overlay built with `NSPanel` + SwiftUI
- Configurable hotkey stored in `UserDefaults`
- Accessibility selected-text detection with clipboard fallback
- Clipboard-based text detection through `NSPasteboard`
- Source language auto-detect or manual source selection
- Target language picker and source/target swap
- Apple Translation framework session injection through `.translationTask`
Expand All @@ -49,7 +49,6 @@ MVP non-goals:

- macOS app development environment with Xcode
- Apple Translation framework support on the target macOS version
- Accessibility permission for selected-text detection

The current Xcode project uses generated Info.plist settings, `LSUIElement=YES`, and bundle id `com.finn.transScan`.

Expand Down Expand Up @@ -81,7 +80,7 @@ The high-level path is:
2. `AppDelegate.swift` registers global/local key monitors and toggles the overlay.
3. `OverlayWindowController.swift` owns the floating `NSPanel` lifecycle.
4. `OverlayContentView.swift` hosts the SwiftUI overlay and injects the Translation session with `.translationTask`.
5. `TextDetector.swift` watches selected text with Accessibility APIs and falls back to clipboard polling.
5. `TextDetector.swift` watches clipboard changes through `NSPasteboard`.
6. `TranslationViewModel.swift` debounces detected text, tracks translation state, handles language swaps, and coordinates UI updates.
7. `TranslationEngine.swift` wraps Apple Translation, language persistence, language-pack preparation, and pending requests while the session is not ready.

Expand All @@ -97,7 +96,7 @@ transScan/ macOS app source
OverlayContentView.swift overlay host and Translation session injection
TranslationViewModel.swift translation state and UI flow
TranslationEngine.swift Apple Translation wrapper
TextDetector.swift Accessibility and clipboard text detection
TextDetector.swift Clipboard text detection
SettingsView.swift shortcut/settings UI

transScanTests/ XCTest coverage
Expand Down Expand Up @@ -181,7 +180,7 @@ App tests live in `transScanTests/`.

Prefer focused XCTest coverage around:

- text normalization and clipboard fallback
- text normalization and clipboard polling
- duplicate text suppression
- translation engine language/session behavior
- view model state transitions
Expand Down Expand Up @@ -212,4 +211,4 @@ For PRs, include:
- linked issue or decision note if applicable
- build/test results
- screenshots or screen recordings for UI changes
- notes about Accessibility permission or manual macOS verification
- notes about clipboard flow or manual macOS verification
21 changes: 17 additions & 4 deletions docs/ADR.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@

---

### ADR-002: 텍스트 입력 방식 — Accessibility API + Clipboard fallback
**결정**: 1순위 AXUIElement(Accessibility API)로 선택 텍스트 감지, 감지 실패 시 클립보드 fallback
**이유**: 사용자가 별도 복사 동작 없이 드래그만으로 번역 트리거 가능. PopClip 등 동일 방식 검증됨.
**트레이드오프**: Accessibility 권한 요청 필요 (System Settings > Privacy > Accessibility). 권한 미허용 시 클립보드 모드로만 동작.
### ADR-002: 텍스트 입력 방식 — Clipboard-first
**결정**: MVP의 안정 동작은 `NSPasteboard` 클립보드 변경 감지를 기준으로 한다.
**이유**: 현재 제품 흐름은 사용자가 명시적으로 복사한 텍스트를 팝업에서 계속 번역하는 경험에 맞춰져 있다. 클립보드는 앱별 구현 차이에 덜 흔들리고, 오버레이를 띄운 상태에서 새로 복사할 때마다 안정적으로 입력을 받을 수 있다.
**트레이드오프**: 드래그만으로 자동 번역되는 경험은 현재 MVP에서 보장하지 않는다. 사용자는 선택 후 복사 동작을 해야 하지만, 번역 결과는 오버레이를 띄운 상태에서 복사할 때마다 안정적으로 갱신된다.

---

Expand All @@ -31,3 +31,16 @@
**결정**: Dock에 표시하지 않는 메뉴바 앱 (Info.plist `LSUIElement = YES`) + 상단 메뉴바에 `NSStatusItem` 아이콘 상시 표시
**이유**: 번역 툴은 항상 백그라운드 실행 상태여야 하고, Dock 아이콘은 불필요한 노이즈. 메뉴바 아이콘으로 실행 여부 확인 및 종료 가능.
**트레이드오프**: 앱 종료/설정은 메뉴바 아이콘 클릭 메뉴에서만 접근 가능.

---

### ADR-005: 번역 상태 관리 — TranslationSession bridge + ViewModel
**결정**: SwiftUI `.translationTask`에서 생성된 `TranslationSession`을 `TranslationEngine`에 주입하고, 텍스트 감지/debounce/재번역/에러 상태는 `TranslationViewModel`에서 관리한다.
**이유**: Apple Translation Framework의 `TranslationSession`은 view layer lifecycle에 묶여 있어 engine이 직접 생성할 수 없다. 따라서 session 생성은 SwiftUI가 담당하고, engine은 주입받은 session으로 번역 요청을 처리해야 한다. 텍스트 감지 이벤트는 반복적으로 들어올 수 있으므로 ViewModel에서 debounce, pending request, language change, retranslation 상태를 한 흐름으로 조정한다.
**트레이드오프**: view layer와 translation engine 사이에 명시적인 bridge가 필요하다. `.translationTask(engine.configuration)`와 `engine.provideSession(session)` 연결이 깨지면 번역 요청이 pending 상태로 남을 수 있으므로, overlay host view와 engine wiring을 핵심 architecture rule로 유지해야 한다.

---

## Decision Log

- 2026-05-28: current runtime에서 Accessibility observer, selected-text event path, 권한 UI를 제거하고 `NSPasteboard` polling만 유지했다. 직접 선택 텍스트 감지는 별도 안정화 작업으로 되돌릴 때 ADR-002를 다시 갱신한다.
9 changes: 6 additions & 3 deletions docs/PRD.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# PRD: transScan

## 목표
macOS에서 단축키 하나로 오버레이를 띄워, 드래그한 텍스트를 즉시 번역해주는 툴.
macOS에서 단축키 하나로 오버레이를 띄워, 복사한 텍스트를 즉시 번역해주는 툴.
브라우저나 앱을 전환하지 않고 읽으면서 바로 번역할 수 있게 한다.

## 사용자
Expand All @@ -11,8 +11,7 @@ macOS에서 단축키 하나로 오버레이를 띄워, 드래그한 텍스트
## 핵심 기능

1. **단축키 오버레이** — 설정창에서 직접 녹화한 단축키로 플로팅 번역 창 토글 (기본값: `⌥E`)
2. **선택 텍스트 자동 번역** — Accessibility API로 드래그 선택된 텍스트를 감지해 즉시 번역
- 선택 텍스트 감지 불가 시 클립보드 내용으로 fallback
2. **클립보드 기반 번역** — 사용자가 선택한 문장을 복사하면 클립보드 변경을 감지해 즉시 번역
3. **언어 자동 감지 + 타겟 언어 선택** — 소스 언어는 auto detect, 타겟 언어는 드롭다운으로 선택
4. **언어 방향 스왑** — `원어 → 번역어` 표시 영역을 클릭하면 방향 반전
5. **드래그 가능한 창** — 오버레이는 화면 하단 기본 위치, 마우스로 자유롭게 이동 가능
Expand All @@ -29,3 +28,7 @@ macOS에서 단축키 하나로 오버레이를 띄워, 드래그한 텍스트
- 미니멀, 다크/라이트 모드 시스템 설정 따름
- 오버레이는 작고 가벼운 느낌 — 읽는 흐름을 방해하지 않는 크기
- 언어 방향 표시(`EN → KO`)는 항상 상단에 노출

## Decision Log

- 2026-05-28: 현재 MVP 요구사항을 clipboard-first로 확정했다. Accessibility 기반 직접 선택 텍스트 감지는 앱별 안정성 검증 전까지 현재 기능이나 권한 요구사항으로 문서화하지 않는다.
2 changes: 1 addition & 1 deletion docs/UI_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
### 번역 결과 영역
- 번역된 텍스트 출력
- 입력 중(번역 대기 중)이면 로딩 인디케이터 표시
- 번역할 텍스트 없으면 "텍스트를 드래그하거나 복사하세요" 안내 문구
- 번역할 텍스트 없으면 "번역할 텍스트를 복사하세요" 안내 문구

## 창 동작

Expand Down
15 changes: 15 additions & 0 deletions docs/session-logs/2026-05-28.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## 한 일

문서와 코드가 어긋나 있던 Accessibility 선택 텍스트 흐름을 정리했다. `TextDetector`는 `NSPasteboard` polling만 담당하게 줄였고, 권한 없으면 오버레이가 막히던 `TranslationViewModel` 경로와 설정/메뉴의 권한 CTA를 제거했다.

DMG 배포 스크립트는 Release 최적화 기본값을 `-O`로 맞추고, DMG 서명 검증과 notarization 전 사전 검증을 추가했다. `xcodebuild -project transScan.xcodeproj -scheme transScan test -quiet`, `xcodebuild -project transScan.xcodeproj -scheme transScan build -quiet`, `bash -n`, `roadmap.progress.json` 검증, `git diff HEAD --check`는 통과했다.

PRD/ADR에는 2026-05-28 Decision Log를 추가해 현재 MVP는 clipboard-first이고 Accessibility 직접 선택 텍스트 감지는 미래 안정화 목표라는 결정을 남겼다.

## 이어할 것

다음 첫 작업은 mixed staged/un-staged diff를 검토해 커밋 단위를 확정하는 것이다. 그 다음 실제 Developer ID 서명 DMG로 `SIGNING_IDENTITY='Developer ID Application: ...' ./scripts/create_dmg.sh`와 `./scripts/notarize_dmg.sh build/transScan-*.dmg`를 실행해 `notarytool`, `stapler`, `spctl` 결과를 확인한다.

## 책 메모

문서만 clipboard-first로 바꾸고 런타임에 Accessibility 권한 guard가 남아 있으면, 권한이 없는 사용자에게는 클립보드 MVP 자체가 막힌다. 기능을 미래 목표로 내릴 때는 docs뿐 아니라 ViewModel guard, menu/settings CTA, detector event source까지 같이 제거해야 한다.
Loading