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
18 changes: 10 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,16 @@ jobs:
(
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
ps -axo pid=,ppid=,stat=,etime=,comm= | awk '/swift|Chop|git|python/ {print}'
for process_id in $(pgrep -f 'swiftpm-testing|ChopPackageTests' || true); do
sample_file="$RUNNER_TEMP/chop-sample-$process_id.txt"
sample_error="$RUNNER_TEMP/chop-sample-$process_id-error.txt"
sample "$process_id" 2 10 -file "$sample_file" >"$sample_error" 2>&1 || true
if [[ -f "$sample_file" ]]; then
head -240 "$sample_file"
else
head -5 "$sample_error"
fi
done
done
) &
Expand Down
4 changes: 4 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ Chop의 라벨과 오류 문구는 medium으로 통일합니다. 셸·TUI가 출

## Layout

- GitHub 클론은 사용자 요청에 따라48pt 세로 추가 영역의 하단12pt에32pt native circular `arrow.down.to.line` 버튼으로 제공한다. 기존 중앙 plus·좌우8pt·옅은 단일 경계와 트리 개폐 정책은 유지한다. 파일 메뉴의 `GitHub 프로젝트 클론…`도 같은 시트를 열며, 한 번에 하나의 클론을 진행한다. 아이콘은 기존 SF Symbols 계열의 다운로드 의미이며 GitHub 로고를 새로 만들지 않는다.
- 클론 시트는520×408pt(chosen), 바깥24pt/그룹16pt/내부8pt,20pt semibold 목적 제목과 기존14pt medium·28pt 입력/버튼을 사용한다. 저장소 주소→저장 위치→폴더 이름 순서이며 자동 제안한 이름만 주소 변경에 맞춰 갱신한다. 기존 InsetTextField의 blur inset/hover·focus 모션을 재사용하며 표면/버튼/보조·오류 색은 AppKit native 역할을 따른다. 진행은 spinner·상태로 표시하고 중복 제출을 막으며, 취소/오류 후 입력 수정·재시도를 제공한다. 새 창 결과 안내를 상태 위치에 한 번만 두고, 진행 시 저장소 주소를 중복 재표시하지 않는다.
- GitHub HTTPS·SSH·owner/repo 입력을 canonical 주소로 정규화한다. 저장할 새 폴더의 충돌은 덮어쓰지 않으며 클론 완료 후에만 해당 경로로 독립 창·첫 셸·파일 트리를 연다. 취소와 앱 종료는 Git process group을 중단하고 이번 임시 폴더를 정리한 뒤 완료한다. 기존 Git 인증을 사용하며 앱 내 로그인·원격 저장소 생성·의존성 자동 실행은 제공하지 않는다. [요구·첫 화면·실제 검증](design/evidence/github-clone.md).

- 파일 메뉴의 `새 윈도우`(⌘N)는 현재 선택한 터미널 경로에서 별도 작업 공간을 엽니다. `프로젝트 폴더에서 새 윈도우…`(⌘⇧N)는 native 폴더 선택 후 셸·파일 트리의 시작 경로를 정합니다. Dock 메뉴에서도 두 동작을 제공하며 창이 없어도 사용할 수 있습니다. 제목은 `폴더 이름 — Chop`, 전환은 macOS 윈도우 메뉴를 사용합니다. 새 전용 사이드바나 상시 제목/설명 영역은 추가하지 않습니다.
- 창마다 셸·배치·이름·선택·확대·탭·파일 트리를 독립적으로 유지합니다. 명령과 메뉴는 활성 창을 대상으로 하며 화면 모드는 앱 전체에 적용합니다. 창 닫기는 그 창의 실행 작업만 확인하고 종료하며 창별 파일 트리 설정과 창 위치 저장값도 정리합니다. 앱 종료 때 복원할 창의 설정은 유지합니다. 앱 종료는 모든 창의 작업을 확인하고 v2 세션에 창 목록·포커스와 각 v1 작업 공간을 함께 저장합니다. 기존 단일 v1 저장은 첫 창으로 이전하고 파일 트리 환경설정도 이 창으로 옮깁니다. 재실행은 새 셸을 열며 프로세스·환경변수·명령을 저장하지 않습니다.
- 기본 진입은 좌상단 실행 셸 1개와 중앙 `+`가 있는 나머지 3칸입니다. 기존 단일 빈 칸 저장도 이 기본값으로 전환하며, 저장된 사용 중 레이아웃은 복원합니다.
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ libghostty 엔진과 Metal 렌더러를 사용합니다.

| 기능 | 설명 |
| --- | --- |
| GitHub 프로젝트 클론 | 오른쪽 추가 영역 하단에서 저장소를 클론하고, 새 창의 터미널과 파일 트리를 해당 폴더에서 엽니다. |
| 로컬 서버 주소 | 각 터미널의 실행 중인 로컬 포트를 헤더에 표시하고 브라우저로 엽니다. 여러 개면 주소 목록을 제공합니다. |
| 프로젝트별 윈도우 | 파일 메뉴나 Dock 메뉴에서 새 창을 엽니다. 프로젝트마다 셸·레이아웃·파일 트리를 따로 유지합니다. |
| 계속 터미널 추가 | 빈 칸 중앙의 `+` 또는 오른쪽 세로 영역의 `+`로 터미널을 엽니다. 네 칸을 채운 뒤에도 아래로 추가합니다. |
Expand Down Expand Up @@ -101,6 +102,18 @@ libghostty 엔진과 Metal 렌더러를 사용합니다.
<sub>넓게 볼 때는 탭으로. 다시 나눌 때도 기존 터미널은 그대로.</sub>
</p>

## GitHub 프로젝트 클론

터미널 추가 영역 하단의 **다운로드 아이콘** 또는 `파일 → GitHub 프로젝트 클론…`을 누릅니다.
GitHub 저장소의 HTTPS·SSH 주소나 `owner/repo`를 입력하고, 저장 위치와 폴더 이름을 정한 뒤
**클론하고 열기**를 누르면 새 프로젝트 창에서 터미널이 실행됩니다. 파일 트리도 같은 폴더에서 시작합니다.

진행 중에는 취소할 수 있으며, 실패하면 입력을 유지해 수정한 뒤 다시 시도합니다. 같은 이름의
폴더나 파일을 덮어쓰지 않습니다. 비공개 저장소는 이 Mac에 설정된 Git credential helper나 SSH agent를
사용합니다. 인증 설정이 없거나 SSH 호스트를 아직 확인하지 않았다면 먼저 터미널에서 Git 인증을 설정해야 합니다.
GitHub 계정 연결·저장소 목록 탐색·새 원격 저장소 생성은 포함하지 않습니다. 클론 후 의존성 설치나
저장소의 명령을 자동으로 실행하지 않습니다.

## 로컬 서버 주소

터미널에서 개발 서버를 실행하면 헤더에 `localhost:3000` 같은 주소가 표시됩니다. 하나면
Expand Down
24 changes: 21 additions & 3 deletions Sources/Chop/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
}
}

@objc private func cloneProject() { windowManager.requestClone() }

func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
let menu = NSMenu()
for (title, action) in [
Expand All @@ -100,15 +102,27 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
}

func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
if windowManager.needsConfirmClose {
if windowManager.needsConfirmClose || windowManager.isCloningProject {
let alert = NSAlert()
alert.messageText = "Chop을 종료할까요?"
alert.informativeText = "실행 중인 터미널 작업이 종료됩니다."
alert.informativeText =
windowManager.isCloningProject
? "실행 중인 터미널 작업과 프로젝트 클론이 종료됩니다."
: "실행 중인 터미널 작업이 종료됩니다."
alert.addButton(withTitle: "취소")
alert.addButton(withTitle: "종료")
if alert.runModal() != .alertSecondButtonReturn { return .terminateCancel }
}
return session.confirmTerminationIfUnsaved() ? .terminateNow : .terminateCancel
guard session.confirmTerminationIfUnsaved() else { return .terminateCancel }
if windowManager.isCloningProject {
windowManager.prepareToTerminate()
Task {
await windowManager.cancelClonesForTermination()
sender.reply(toApplicationShouldTerminate: true)
}
return .terminateLater
}
return .terminateNow
}

func applicationWillTerminate(_ notification: Notification) {
Expand Down Expand Up @@ -157,6 +171,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
if menuItem.action == #selector(newWindow) { return true }
if menuItem.action == #selector(openProjectWindow) { return projectPanel == nil }
if menuItem.action == #selector(cloneProject) { return true }
if menuItem.action == #selector(chooseAppearance(_:)) {
menuItem.state =
(menuItem.representedObject as? String) == engine.appearanceMode.rawValue ? .on : .off
Expand Down Expand Up @@ -219,6 +234,9 @@ final class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
withTitle: "프로젝트 폴더에서 새 윈도우…", action: #selector(openProjectWindow), keyEquivalent: "n")
project.keyEquivalentModifierMask = [.command, .shift]
project.target = self
let clone = fileMenu.addItem(
withTitle: "GitHub 프로젝트 클론…", action: #selector(cloneProject), keyEquivalent: "")
clone.target = self
fileItem.submenu = fileMenu
mainMenu.addItem(fileItem)

Expand Down
Loading
Loading