Skip to content
Merged
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
21 changes: 20 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,23 @@ jobs:
.cache/zig
key: ${{ runner.os }}-${{ runner.arch }}-ghostty-${{ hashFiles('scripts/prepare-ghostty.sh', 'scripts/ghostty-xcrun.sh', 'scripts/ghostty-libtool.sh', 'Dependencies/ghostty-config.patch') }}
- name: Check and package app
run: ./scripts/check.sh
run: |
./scripts/check.sh &
check_pid=$!
(
while kill -0 "$check_pid" 2>/dev/null; do
sleep 60
for process_name in swiftpm-testing ChopPackageTests; do
for process_id in $(pgrep -x "$process_name" || true); do
sample_file="$RUNNER_TEMP/chop-sample-$process_id.txt"
sample "$process_id" 2 10 -file "$sample_file" >/dev/null 2>&1 || true
if [[ -f "$sample_file" ]]; then
head -160 "$sample_file"
fi
done
done
done
) &
monitor_pid=$!
trap 'kill "$monitor_pid" 2>/dev/null || true' EXIT
wait "$check_pid"
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@

기본 앱은 창 열기, 크기 조절, 최소화, `⌘W`로 창 닫기, 다시 실행해 창 열기, `⌘Q`로 종료를 확인합니다.
터미널과 분할 기능이 추가되면 해당 기능의 입력·상태 유지·종료 동작도 확인합니다.

`check.sh`는 `swift test --no-parallel`로 전체 테스트를 실행합니다. AppKit 창과 터미널 엔진이
프로세스 상태를 공유하므로 비동기 UI 테스트가 서로 겹치지 않게 합니다.
10 changes: 6 additions & 4 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,12 @@ Chop의 라벨과 오류 문구는 medium으로 통일합니다. 셸·TUI가 출
- 오른쪽 패널은 기본300pt, 최소280pt이며 네이티브 경계로 폭을 조절합니다. 닫으면 파일 트리 경계도 없애고 세로 추가 영역을 제외한 폭을 터미널 canvas에 돌려줍니다. 화면에 최소 폭이 들어가지 않으면 열기를 거절하고, 초기 복원에서는 패널을 접습니다.
- 파일 트리의 경로 입력·홈/다운로드/문서 바로가기·파일 목록은 하나의 탐색 영역입니다. 바깥 inset12/gap8, 한 줄 경로 입력28pt, 파일 행28pt, 들여쓰기16pt, 시스템14pt medium 글꼴을 유지합니다. 헤더 버튼은 기존28pt 아이콘입니다. 후속 사용자 요청의 `design/references/file-shortcuts-row-user.png` / `layout-edit-button-user.png`에 따라 바로가기는 기존 세로108pt 묶음을 대체하는 가로 한 줄의 개별 native rounded 버튼입니다. 높이28pt/gap8,기존14pt medium 글자와 SF house/arrow.down.circle/doc14pt 아이콘을 사용하고,아이콘·글자는 native image-leading 배치로 함께 누릅니다. 전체 이름과 경로 툴팁·AX 이름은 유지합니다. 사용자 지정 행 배치가 이전 Finder 세로 열 정렬 계약을 대체하며 이미지는 image-px 관찰 자료입니다.
- 실제 파일 목록과 로딩/빈/오류 안내는 바로가기 아래의 별도 목록 면에 함께 둡니다. 배경은 fileListBackground(light F8F8F8/dark24262C)입니다. 버튼색을 그대로 넓은 면에 쓰면 너무 강하다는 후속 사용자 교정에 따라,버튼의 중립색을 본문 쪽으로 약35%만 반영한 연한 톤을 사용합니다. 헤더·버튼색은 유지하며 그라디언트·구분선은 추가하지 않습니다. 목록 면은 반경8pt/내부 inset8pt,바깥 좌우·하단 inset12pt로 구분합니다. 최소 패널280pt에서도 세 바로가기의 전체 이름·아이콘과 파일 행 클릭 영역이 유지돼야 합니다. 목록 자체의 선택·펼치기·키보드·스크롤·경로 드래그는 같은 NSOutlineView를 재사용합니다.
- 바로가기 아래의28pt native NSSearchField에서 현재 폴더와 하위 폴더의 파일·폴더 이름을 검색합니다. 기존14pt medium/gap8/좌우 inset12를 사용하며 입력 포커스 테두리 없이 실제 caret·native 검색/지우기 아이콘과 AX 이름을 유지합니다. 입력 후75ms 동안 기다린 뒤 백그라운드로 읽고,찾은 결과부터 75ms 간격으로 갱신을 제한하며 표시합니다. 검색어 편집 중에는 폴더별 목록을 최대10초·총10만 항목(빈 폴더도1개로 계산)까지 재사용하며 새로고침·폴더 이동은 캐시를 교체합니다. 새 검색·탐색·뷰 해제 때 이전 요청을 취소하며 실제 디렉터리 읽기에도 취소를 전달합니다. 검색 결과는 같은 목록 면에 상대 경로로 표시해 동명 파일을 구분하며 전체 경로는 툴팁/경로 복사/파일 URL 드래그에 보존합니다. 검색어를 지우면 일반 트리,결과 폴더를 더블클릭/Enter로 열면 해당 폴더의 일반 트리로 돌아옵니다. 새로고침은 같은 폴더의 검색어를 유지하고 목적지를 바꾸면 초기화합니다. 숨김·심볼릭 링크·패키지 하위 제외는 기존 트리 규칙을 따릅니다. 결과는 처음500개까지 표시하며 초과 안내/검색어 좁히기를 제공하고,일부 하위 폴더의 읽기 실패와루트 실패·0건·검색 중을 구분합니다. 파일 내용 검색은 현재 범위에 포함하지 않습니다.
- 바로가기 아래의28pt native NSSearchField에서 현재 폴더와 하위 폴더의 파일·폴더 이름을 검색합니다. 기존14pt medium/gap8/좌우 inset12를 사용하며 최신 사용자 요청의 중립색 blur inset 경계와 부드러운 hover/focus 전환을 적용하고 실제 caret·native 검색/지우기 아이콘과 AX 이름을 유지합니다. 입력 후75ms 동안 기다린 뒤 백그라운드로 읽고,찾은 결과부터 75ms 간격으로 갱신을 제한하며 표시합니다. 검색어 편집 중에는 폴더별 목록을 최대10초·총10만 항목(빈 폴더도1개로 계산)까지 재사용하며 새로고침·폴더 이동은 캐시를 교체합니다. 새 검색·탐색·뷰 해제 때 이전 요청을 취소하며 실제 디렉터리 읽기에도 취소를 전달합니다. 검색 결과는 같은 목록 면에 상대 경로로 표시해 동명 파일을 구분하며 전체 경로는 툴팁/경로 복사/파일 URL 드래그에 보존합니다. 검색어를 지우면 일반 트리,결과 폴더를 더블클릭/Enter로 열면 해당 폴더의 일반 트리로 돌아옵니다. 새로고침은 같은 폴더의 검색어를 유지하고 목적지를 바꾸면 초기화합니다. 숨김·심볼릭 링크·패키지 하위 제외는 기존 트리 규칙을 따릅니다. 결과는 처음500개까지 표시하며 초과 안내/검색어 좁히기를 제공하고,일부 하위 폴더의 읽기 실패와루트 실패·0건·검색 중을 구분합니다. 파일 내용 검색은 현재 범위에 포함하지 않습니다.

## Elevation & Depth

기본 화면은 평면으로 유지합니다. 팝업과 확인 시트는 네이티브 AppKit을 사용합니다.
사용자 요청에 따라 현재 입력 칸에 파란 포커스 테두리를 표시하지 않습니다. 터미널의 실제 입력 커서와 키보드 초점·칸 이동·저장 포커스는 유지합니다. 유효한 드래그 대상의 일시적인 경계는 입력 포커스와 별도 상태이며, 상태 변경 때문에 칸 크기를 바꾸지 않습니다. 네이티브 버튼·메뉴의 키보드 포커스 표현은 유지합니다. 추가 사용자 요청에 따라 파일 트리의 경로 입력창도 포커스 테두리를 표시하지 않으며, 실제 편집 커서·선택·키보드 입력은 유지합니다.
사용자 요청에 따라 현재 입력 칸에 파란 포커스 테두리를 표시하지 않습니다. 터미널의 실제 입력 커서와 키보드 초점·칸 이동·저장 포커스는 유지합니다. 유효한 드래그 대상의 일시적인 경계는 입력 포커스와 별도 상태이며, 상태 변경 때문에 칸 크기를 바꾸지 않습니다. 네이티브 버튼·메뉴의 키보드 포커스 표현은 유지합니다. 경로·검색·이름 변경 입력창은 2026-10-20 후속 요청에 따라 중립색 blur inset 경계로 초점을 표시합니다. 파란 외곽 focus ring은 생략하고 실제 편집 커서·선택·키보드 입력은 유지합니다.

## Shapes

Expand Down Expand Up @@ -109,7 +109,9 @@ Chop의 라벨과 오류 문구는 medium으로 통일합니다. 셸·TUI가 출
| 폴더 복구 | 여러 경로 문제는 한 번에 알림; 홈 경로 대체 | 실행 칸·빈 칸 모두 반영 |
| 저장 실패 | 재시도 메뉴; 미저장 종료는 취소 가능 | 쓰기 권한 실패 후 복구 |

칸 이동은 사용자가 요청한 직접 조작 모션입니다. 놓은 뒤 0.18초 easeInEaseOut으로 새 자리에 배치하며, 실제 상태 변경이나 입력을 애니메이션 완료까지 지연하지 않습니다. 빠른 반복 이동은 이전 표시 위치에서 새 모션을 시작합니다. 동작 줄이기가 켜져 있으면 배치 모션과 취소 복귀 애니메이션을 생략합니다. hover 장식·튕김 효과는 추가하지 않습니다.
칸 이동은 사용자가 요청한 직접 조작 모션입니다. 놓은 뒤 0.18초 easeInEaseOut으로 새 자리에 배치하며, 실제 상태 변경이나 입력을 애니메이션 완료까지 지연하지 않습니다. 빠른 반복 이동은 이전 표시 위치에서 새 모션을 시작합니다. 동작 줄이기가 켜져 있으면 배치 모션과 취소 복귀 애니메이션을 생략합니다. 칸 이동에 hover 장식·튕김 효과는 추가하지 않습니다. 입력창의 요청된 hover/focus 전환은 아래의 별도 계약을 따릅니다.
경로·검색·이름 변경 입력창은 `InsetInput.swift`가 공통 효과를 관리합니다. native NSTextField/NSSearchField 편집기·검색/지우기 버튼을 유지하며 경계만 안쪽으로 clip합니다. 경로/이름 반경6pt, 검색 반경14pt, 모두 높이28pt(이름 변경은 기존24→28pt)이며 기존14pt medium을 유지합니다. light 검정/dark 흰색의 얇은0.5pt 안쪽 경계와2.5pt blur, opacity 기본0.10/hover0.17/focus0.25를 사용합니다. 제품 선택값이며 레퍼런스 실측이 아닙니다. opacity만0.18초 easeInEaseOut으로 전환하고 빠른 전환은 현재 presentation에서 이어갑니다. 편집기/프레임/검색 실행을 모션 완료까지 지연하지 않습니다. 동작 줄이기에서는 애니메이션을 제거하고 즉시 최종 상태를 적용합니다. 이미지 생성은 크기·배율·appearance가 바뀔 때만 수행합니다. 실제 두 모드 기본/포커스·한글 붙여넣기·Tab·검색 취소·경로 Enter·이름 변경과 native 이벤트/조합/모션 검사를 구분해 [관찰](design/evidence/inset-input-motion.md)에 기록합니다.

사용자 문구는 `ux-copy` 기준에 따라 누른 직후의 행동을 설명하며, 구현용 API 용어는 화면에 표시하지 않습니다.

파일 탐색은 터미널 cwd와 독립적입니다. 폴더 경로는 `~`·절대/상대 경로를 문자 그대로 해석하며 셸 명령으로 실행하지 않습니다. 현재 폴더 한 단계만 백그라운드에서 읽고, 늦게 끝난 이전 탐색은 최신 루트를 덮어쓰지 않습니다. 폴더 우선 자연 정렬, 숨김 항목 제외, 심볼릭 링크/패키지의 leaf 처리를 사용합니다.
Expand All @@ -130,7 +132,7 @@ Chop의 라벨과 오류 문구는 medium으로 통일합니다. 셸·TUI가 출
웹 CSS 규칙을 macOS 단위로 측정했다고 주장하지 않습니다. 앱이 소유한 읽는 라벨은 14pt medium이며, 네이티브 제목 막대·메뉴·NSAlert의 크기/굵기·모양·기본 커서는 AppKit을 유지합니다. 셸 출력은 VT 스타일을 따릅니다.

- SF Symbols: plus, xmark, 확대/복귀 화살표, moon/sun.max, rectangle 및 rectangle.split.2x1/1x2/2x2, 이동용 arrow.up.and.down.and.arrow.left.and.right. 네이티브 도구 조작용이며 로고가 아닙니다.
- 원형 중앙 버튼과 AppKit 컨트롤/윈도우의 곡률·표준 그림자는 플랫폼 조작을 보존하는 예외입니다. 자체 그라디언트·그림자·hover 변형은 추가하지 않습니다.
- 원형 중앙 버튼과 AppKit 컨트롤/윈도우의 곡률·표준 그림자는 플랫폼 조작을 보존하는 예외입니다. 자체 그라디언트·그림자·hover 변형은 입력창에 명시된 사용자 요청의 blur inset/상태 전환 외에는 추가하지 않습니다.
- 헤더 확대·닫기를 상시 제공해 키보드/포인터로 찾을 수 있게 합니다. 본문 중앙 추가는 빈 상태/편집 모드에서만 표시합니다.
- 동일 동작의 메뉴/단축키 제공은 macOS 접근 경로입니다. 콘텐츠에 같은 CTA를 중복 배치하는 목적이 아닙니다.
- 네이티브 splitter는 실시간 크기 조절 조작부입니다. 장식용 영역 구분 바를 만들지 않습니다.
Expand Down
4 changes: 2 additions & 2 deletions Sources/Chop/FileBrowserView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ final class FileBrowserView: NSView, NSOutlineViewDataSource, NSOutlineViewDeleg
private var roots: [FileBrowserNode] = []
private let header = NSView()
private let titleLabel = NSTextField(labelWithString: "파일")
private let pathField = NSTextField()
private let searchField = NSSearchField()
private let pathField = InsetTextField()
private let searchField = InsetSearchField()
private let statusLabel = NSTextField(wrappingLabelWithString: "")
private let outline = FileOutlineView()
private let scrollView = NSScrollView()
Expand Down
Loading
Loading