Skip to content

Commit 81d7044

Browse files
authored
Merge pull request #5 from myuron/fix/focus-indicator-border-color
ci: GitHub Actions CIワークフローを追加
2 parents dd690aa + ebd446f commit 81d7044

17 files changed

Lines changed: 1623 additions & 213 deletions

File tree

.claude/plans/plan.md

Lines changed: 11 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,14 @@
1-
# graftx 未実装機能 ToDo
1+
# ヤンク取り消し機能(yトグル)
22

3-
## ナビゲーション
4-
- [x] `gg` — 先頭にジャンプ
5-
- [x] `G` — 末尾にジャンプ
3+
## 概要
4+
`y`キーでヤンクした後、もう一度`y`を押すとヤンクを取り消す(トグル動作)。
65

7-
## ファイル選択
8-
- [x] `Space` — カーソル行の選択トグル
9-
- [ ] `v` — ビジュアルモード(範囲選択)
10-
- [x] `Ctrl+a` — 全選択
11-
- [x] `Ctrl+r` — 選択状態を反転
12-
- [x] `Esc` — 選択を全解除
6+
## 実装方針
7+
- `App.yank()` メソッドに、既にYankBufが存在し、フォーカス中のペインがヤンク元と同じ場合にヤンクを取り消すロジックを追加
8+
- 取り消し時: YankBufをクリア、選択マーク(`*`)をクリア、ステータスメッセージを表示
139

14-
## ファイル操作(ヤンク・ペースト)
15-
- [x] `YankBuffer` 構造体の実装(`internal/pane`
16-
- [x] `y` — ヤンク(選択中 or カーソル行をヤンクバッファに格納)
17-
- [x] `p` — ペースト(ヤンクバッファをもう一方のペインにコピー)
18-
- [x] `P` — 上書きペースト(同名ファイル強制上書き)
19-
- [x] 同名ファイル存在時の確認ダイアログ(ポップアップ)
20-
21-
## ファイル操作(削除・作成・リネーム)
22-
- [x] `d` — ゴミ箱移動(`~/.Trash`
23-
- [x] `D` — 完全削除(`os.RemoveAll`
24-
- [x] `a` — 新規作成(末尾`/`でディレクトリ、それ以外はファイル)
25-
- [x] `r` — リネーム(ポップアップ入力)
26-
- [x] 削除確認ダイアログ(ポップアップ)
27-
28-
## FileSystem インターフェース追加実装(`internal/fs`
29-
- [x] `Copy(src, dst string) error`
30-
- [x] `Remove(path string) error`
31-
- [x] `Trash(path string) error`
32-
- [x] `Rename(oldPath, newPath string) error`
33-
- [x] `Create(path string, isDir bool) error`
34-
35-
## 検索・フィルタ
36-
- [x] `/` — 前方検索(ステータスバーにプロンプト表示)
37-
- [ ] `?` — 後方検索
38-
- [x] `n` — 次の一致へ移動
39-
- [x] `N` — 前の一致へ移動
40-
- [x] `f` — フィルタ(一致しないエントリを非表示)
41-
42-
## その他
43-
- [x] `.` — 隠しファイルの表示/非表示トグル
44-
- [x] ステータスバーへの操作フィードバック表示
45-
- [x] 親ディレクトリ復帰時のカーソル復元
10+
## タスク
11+
- [x] 1. 失敗するテストを書く (`internal/ui/keybinding_test.go`)
12+
- [x] 2. テストをパスするコードを実装
13+
- [x] 3. フォーマッター・リンター実行
14+
- [x] 4. ユニットテスト全体実行

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: CI
2+
permissions:
3+
contents: read
4+
on:
5+
pull_request:
6+
branches: [main]
7+
8+
jobs:
9+
ci:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-go@v5
14+
with:
15+
go-version-file: go.mod
16+
- name: Lint
17+
uses: golangci/golangci-lint-action@v7
18+
- name: Test
19+
run: go test ./...
20+
- name: Build
21+
run: go build ./...

README.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# graftx
2+
3+
A TUI file manager that accelerates file copying between repositories.
4+
5+
[日本語版 README](README_ja.md)
6+
7+
With a dual-pane layout displaying Source and Destination side by side, you can quickly perform file operations using only the keyboard with Vim-like keybindings.
8+
9+
```text
10+
┌─── Source ──────────┬─── Dest ────────────┐
11+
│ main.go │ main.go │
12+
│ internal/ │ internal/ │
13+
│ * README.md │ go.mod │
14+
│ go.mod │ │
15+
└─────────────────────┴─────────────────────┘
16+
[left] [q]Quit [Tab]Switch [h/j/k/l]Move
17+
```
18+
19+
## Requirements
20+
21+
- Go 1.21 or later
22+
- [ghq](https://github.com/x-motemen/ghq) — Used to list repositories
23+
24+
## Installation
25+
26+
```bash
27+
go install github.com/myuron/graftx@latest
28+
```
29+
30+
## Getting Started
31+
32+
```bash
33+
# Launch with the current directory as the right pane (Destination)
34+
graftx
35+
```
36+
37+
On launch, the right pane displays the contents of the current directory. Press `s` to select a source repository, which will be shown in the left pane.
38+
39+
## Fonts
40+
41+
File tree icons are supported via [Nerd Fonts](https://www.nerdfonts.com/). When using a terminal with Nerd Fonts installed, file-type-specific icons are displayed.
42+
43+
> The application works without Nerd Fonts, but icons may not render correctly.
44+
45+
## Keybindings
46+
47+
### Navigation
48+
49+
| Key | Action |
50+
|-----|--------|
51+
| `j` | Move cursor down |
52+
| `k` | Move cursor up |
53+
| `l` | Enter directory |
54+
| `h` | Go to parent directory |
55+
| `gg` | Jump to top |
56+
| `G` | Jump to bottom |
57+
| `Tab` | Switch focus between left and right panes |
58+
59+
### File Selection
60+
61+
| Key | Action |
62+
|-----|--------|
63+
| `Space` | Toggle selection on cursor line (cursor moves down after selection) |
64+
| `Ctrl+a` | Select all entries |
65+
| `Ctrl+r` | Invert selection |
66+
| `Esc` / `Ctrl+c` | Clear selection / Clear filter |
67+
68+
### Copy (Yank & Paste)
69+
70+
| Key | Action |
71+
|-----|--------|
72+
| `y` | Yank selected entries (or cursor line if none selected). Press `y` again in the same pane to cancel yank |
73+
| `p` | Paste yanked entries to the focused pane (skip existing files) |
74+
| `P` | Paste yanked entries with overwrite |
75+
76+
### File Operations
77+
78+
| Key | Action |
79+
|-----|--------|
80+
| `a` | Create new file/directory (append `/` for directory) |
81+
| `r` | Rename |
82+
| `d` | Move to trash (confirmation: `y`/`n`) |
83+
| `D` | Delete permanently (confirmation: `y`/`n`) |
84+
85+
### Search & Filter
86+
87+
| Key | Action |
88+
|-----|--------|
89+
| `/` | Forward search mode (press Enter to confirm, matches are highlighted) |
90+
| `n` | Jump to next search result |
91+
| `N` | Jump to previous search result |
92+
| `f` | Filter mode (show only entries containing the input string) |
93+
| `Esc` / `Ctrl+c` | Clear filter |
94+
95+
### Repository Selection
96+
97+
| Key | Action |
98+
|-----|--------|
99+
| `s` | Open repository selection popup (lists repositories managed by ghq) |
100+
101+
Keys available in the popup:
102+
103+
| Key | Action |
104+
|-----|--------|
105+
| Type characters | Filter (incremental search) |
106+
| `Ctrl+j` / `Ctrl+n` | Move cursor down |
107+
| `Ctrl+k` / `Ctrl+p` | Move cursor up |
108+
| `Enter` | Confirm selection |
109+
| `Esc` / `Ctrl+c` | Cancel |
110+
111+
### Other
112+
113+
| Key | Action |
114+
|-----|--------|
115+
| `.` | Toggle hidden files visibility |
116+
| `q` | Quit |
117+
118+
## Basic Usage
119+
120+
1. Run `graftx` (in the directory you want as the copy destination)
121+
2. Press `s` to select the source repository
122+
3. Navigate to the files you want to copy using `h`/`j`/`k`/`l` in the left pane
123+
4. Select with `Space` (multiple selections allowed), then `y` to yank
124+
5. Press `Tab` to switch to the right pane and navigate to the destination directory
125+
6. Press `p` to paste
126+
127+
## License
128+
129+
MIT

README_ja.md

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# graftx
2+
3+
リポジトリ間のファイルコピーを加速させるTUIファイルマネージャです。
4+
5+
[English README](README.md)
6+
7+
左右2ペイン構成で、コピー元(Source)とコピー先(Dest)を同時に表示し、Vimライクなキーバインドでキーボードだけで素早くファイル操作を完結できます。
8+
9+
```text
10+
┌─── Source ──────────┬─── Dest ────────────┐
11+
│ main.go │ main.go │
12+
│ internal/ │ internal/ │
13+
│ * README.md │ go.mod │
14+
│ go.mod │ │
15+
└─────────────────────┴─────────────────────┘
16+
[left] [q]Quit [Tab]Switch [h/j/k/l]Move
17+
```
18+
19+
## 必要環境
20+
21+
- Go 1.21 以上
22+
- [ghq](https://github.com/x-motemen/ghq) — リポジトリ一覧の取得に使用
23+
24+
## インストール
25+
26+
```bash
27+
go install github.com/myuron/graftx@latest
28+
```
29+
30+
## 起動
31+
32+
```bash
33+
# カレントディレクトリを右ペイン(コピー先)として起動
34+
graftx
35+
```
36+
37+
起動すると右ペインにカレントディレクトリの内容が表示されます。左ペイン(コピー元)は `s` キーでリポジトリを選択すると表示されます。
38+
39+
## フォント
40+
41+
ファイルツリーのアイコン表示に [Nerd Fonts](https://www.nerdfonts.com/) に対応しています。
42+
Nerd Fonts をインストールしたターミナルで使用すると、ファイルの種類に応じたアイコンが表示されます。
43+
44+
> Nerd Fonts が未インストールの環境でも動作しますが、アイコン部分が正しく表示されない場合があります。
45+
46+
## キーバインド
47+
48+
### ナビゲーション
49+
50+
| キー | 操作 |
51+
|------|------|
52+
| `j` | カーソルを下に移動 |
53+
| `k` | カーソルを上に移動 |
54+
| `l` | ディレクトリに入る |
55+
| `h` | 親ディレクトリに戻る |
56+
| `gg` | 先頭にジャンプ |
57+
| `G` | 末尾にジャンプ |
58+
| `Tab` | 左右ペインのフォーカス切り替え |
59+
60+
### ファイル選択
61+
62+
| キー | 操作 |
63+
|------|------|
64+
| `Space` | カーソル行の選択をトグル(選択後カーソルが1つ下に移動) |
65+
| `Ctrl+a` | 全エントリを選択 |
66+
| `Ctrl+r` | 選択を反転 |
67+
| `Esc` / `Ctrl+c` | 選択解除 / フィルタ解除 |
68+
69+
### コピー(ヤンク & ペースト)
70+
71+
| キー | 操作 |
72+
|------|------|
73+
| `y` | 選択中のエントリをヤンク(未選択ならカーソル行)。同じペインで再度 `y` を押すとヤンク取り消し |
74+
| `p` | ヤンクしたエントリをフォーカス中のペインにコピー(同名ファイルはスキップ) |
75+
| `P` | ヤンクしたエントリを上書きコピー |
76+
77+
### ファイル操作
78+
79+
| キー | 操作 |
80+
|------|------|
81+
| `a` | 新規ファイル/ディレクトリ作成(末尾に `/` を付けるとディレクトリ) |
82+
| `r` | リネーム |
83+
| `d` | ゴミ箱に移動(確認あり: `y`/`n`|
84+
| `D` | 完全削除(確認あり: `y`/`n`|
85+
86+
### 検索 & フィルタ
87+
88+
| キー | 操作 |
89+
|------|------|
90+
| `/` | 前方検索モード(入力後 Enter で確定、マッチ部分をハイライト表示) |
91+
| `n` | 次の検索結果に移動 |
92+
| `N` | 前の検索結果に移動 |
93+
| `f` | フィルタモード(入力した文字列を含むエントリのみ表示) |
94+
| `Esc` / `Ctrl+c` | フィルタ解除 |
95+
96+
### リポジトリ選択
97+
98+
| キー | 操作 |
99+
|------|------|
100+
| `s` | リポジトリ選択ポップアップを開く(ghq管理下のリポジトリ一覧を表示) |
101+
102+
ポップアップ内では以下のキーで操作します:
103+
104+
| キー | 操作 |
105+
|------|------|
106+
| 文字入力 | フィルタ(インクリメンタルサーチ) |
107+
| `Ctrl+j` / `Ctrl+n` | カーソルを下に移動 |
108+
| `Ctrl+k` / `Ctrl+p` | カーソルを上に移動 |
109+
| `Enter` | 選択確定 |
110+
| `Esc` / `Ctrl+c` | キャンセル |
111+
112+
### その他
113+
114+
| キー | 操作 |
115+
|------|------|
116+
| `.` | 隠しファイルの表示/非表示を切り替え |
117+
| `q` | 終了 |
118+
119+
## 基本的な使い方
120+
121+
1. `graftx` を起動する(コピー先にしたいディレクトリで実行)
122+
2. `s` を押してコピー元リポジトリを選択する
123+
3. 左ペインで `h`/`j`/`k`/`l` を使いコピーしたいファイルに移動
124+
4. `Space` で選択(複数可)し、`y` でヤンク
125+
5. `Tab` で右ペインに移動し、コピー先ディレクトリに移動
126+
6. `p` でペースト
127+
128+
## ライセンス
129+
130+
MIT

internal/fs/fs_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ func TestTrash_ゴミ箱移動(t *testing.T) {
200200
}
201201
trashPath := filepath.Join(home, ".Trash", "trash_me.txt")
202202
t.Cleanup(func() {
203-
os.Remove(trashPath)
203+
_ = os.Remove(trashPath)
204204
})
205205

206206
osFS := &OSFS{}

internal/fs/osfs.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (o *OSFS) copyFile(src, dst string) error {
5858
if err != nil {
5959
return err
6060
}
61-
defer srcFile.Close()
61+
defer func() { _ = srcFile.Close() }()
6262

6363
srcInfo, err := srcFile.Stat()
6464
if err != nil {
@@ -145,7 +145,7 @@ func (o *OSFS) Trash(path string) error {
145145
}
146146
}
147147
if !found {
148-
return fmt.Errorf("ゴミ箱内で一意のパスが見つかりません: %s", name)
148+
return fmt.Errorf("could not find unique path in trash: %s", name)
149149
}
150150
}
151151

0 commit comments

Comments
 (0)