kajji 0.13.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -124
- package/package.json +6 -6
- package/scripts/postinstall.mjs +50 -0
- package/script/postinstall.mjs +0 -50
package/README.md
CHANGED
|
@@ -2,24 +2,16 @@
|
|
|
2
2
|
<img alt="kajji" src="./assets/kajji.gif">
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# kajji
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Reviewing local code has never been as prominent as it is today. Coding agents are writing line upon line, and your sorry eyes are the ones that need to trudge through it.
|
|
10
|
-
|
|
11
|
-
Kajji makes this new reality less painful with polished jj navigation and manipulation alongside Shiki-powered diff rendering with syntax highlighting and word-level diffs. To allow for jj's log to get the real estate it deserves when you're looking at the diff, while also allowing the width required for side-by-side diff rendering, kajji has two view modes: normal and diff. Switch with ctrl+x and try it out.
|
|
7
|
+
A terminal UI for [jj / Jujutsu](https://github.com/martinvonz/jj): navigate changes, inspect polished diffs, and run common jj workflows with ease and speed.
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
Kajji gives jj a fast, keyboard-first interface with a commit graph, file tree, operation log, bookmark management, GitHub helpers, revset filtering, and custom diff rendering with syntax highlighting, word-level emphasis, split/unified layouts, wrapping, and binary-file handling.
|
|
14
10
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Kajji is my attempt to bring the UX of lazygit to jj, while also aiming for top-class diff rendering and exploring leveraging coding agents effectively. I'm building this for myself first and foremost, but I hope it can be helpful to others too.
|
|
11
|
+

|
|
18
12
|
|
|
19
13
|
## Installation
|
|
20
14
|
|
|
21
|
-
> **Requirements**: [jj](https://github.com/martinvonz/jj)
|
|
22
|
-
|
|
23
15
|
```bash
|
|
24
16
|
# recommended if you use Homebrew
|
|
25
17
|
brew install eliaskc/tap/kajji
|
|
@@ -49,7 +41,7 @@ kajji uninstall --force # skip the confirmation prompt
|
|
|
49
41
|
|
|
50
42
|
### From source
|
|
51
43
|
|
|
52
|
-
|
|
44
|
+
Requirements: [Bun](https://bun.sh)
|
|
53
45
|
|
|
54
46
|
```bash
|
|
55
47
|
git clone https://github.com/eliaskc/kajji.git
|
|
@@ -58,43 +50,6 @@ bun install
|
|
|
58
50
|
bun dev
|
|
59
51
|
```
|
|
60
52
|
|
|
61
|
-
## Principles
|
|
62
|
-
|
|
63
|
-
- **Polish & simplicity** - Do less, but do it well.
|
|
64
|
-
- **Intuitive UX** - Sensible defaults, consistent patterns.
|
|
65
|
-
- **Snappy** - If it feels slow, it's a bug.
|
|
66
|
-
|
|
67
|
-
## Features
|
|
68
|
-
|
|
69
|
-
**Core jj operations:**
|
|
70
|
-
|
|
71
|
-
- [x] View commit log with graph
|
|
72
|
-
- [x] View diffs with syntax highlighting and word-level emphasis
|
|
73
|
-
- [x] New / edit / describe / squash / abandon
|
|
74
|
-
- [x] Rebase with revision picker
|
|
75
|
-
- [x] Split (suspends TUI for jj's native split)
|
|
76
|
-
- [x] Undo / redo with preview
|
|
77
|
-
- [x] Bookmarks (create, delete, rename, move)
|
|
78
|
-
- [x] Git fetch / push
|
|
79
|
-
- [x] Operation log with restore
|
|
80
|
-
- [ ] Conflict resolution
|
|
81
|
-
|
|
82
|
-
**TUI polish:**
|
|
83
|
-
|
|
84
|
-
- [x] Vim-style navigation (j/k, ctrl+u/d)
|
|
85
|
-
- [x] Mouse support (click, double-click, horizontal scroll)
|
|
86
|
-
- [x] Collapsible file tree with status colors
|
|
87
|
-
- [x] Help palette with fuzzy search (`?`)
|
|
88
|
-
- [x] Focus modes for normal browsing vs diff viewing
|
|
89
|
-
- [x] Line wrapping toggle (`w`) and split/unified view (`v`)
|
|
90
|
-
- [x] Binary file detection
|
|
91
|
-
- [x] Recent repository switcher
|
|
92
|
-
- [x] Automatic update notifications
|
|
93
|
-
- [x] Revset filtering and fuzzy search
|
|
94
|
-
- [x] JSONC config with live reload and schema autocomplete
|
|
95
|
-
- [x] Open files in editor from file view (`e`/`E`)
|
|
96
|
-
- [ ] Multi-select for batch operations
|
|
97
|
-
|
|
98
53
|
## Usage
|
|
99
54
|
|
|
100
55
|
Run `kajji` in any jj repository:
|
|
@@ -104,7 +59,45 @@ kajji # current directory
|
|
|
104
59
|
kajji /path/to/repo # specific directory
|
|
105
60
|
```
|
|
106
61
|
|
|
107
|
-
|
|
62
|
+
Kajji has two main viewing modes:
|
|
63
|
+
|
|
64
|
+
- **Normal mode** keeps the log, bookmarks, detail, and command log visible.
|
|
65
|
+
- **Diff focus mode** gives the detail pane more space for side-by-side diffs.
|
|
66
|
+
|
|
67
|
+
Toggle between them with `ctrl+x`.
|
|
68
|
+
|
|
69
|
+
## Features
|
|
70
|
+
|
|
71
|
+
**jj workflows**
|
|
72
|
+
|
|
73
|
+
- Commit log with graph and revset filtering
|
|
74
|
+
- File view with tree/list toggle, editor launching, and discard for working-copy files
|
|
75
|
+
- New, edit, describe, squash, abandon, duplicate, rebase, split, resolve
|
|
76
|
+
- Undo, redo, operation log, and restore
|
|
77
|
+
- Bookmark create/delete/rename/forget/set/move plus remote-only filtering
|
|
78
|
+
- Git fetch/push menus
|
|
79
|
+
- Open commits and PRs on GitHub
|
|
80
|
+
- Recent repository switcher
|
|
81
|
+
|
|
82
|
+
**Diff viewing**
|
|
83
|
+
|
|
84
|
+
- Custom unified and split diff renderers
|
|
85
|
+
- Syntax highlighting and word-level change emphasis
|
|
86
|
+
- Line wrapping and horizontal scrolling
|
|
87
|
+
- Hunk and file navigation
|
|
88
|
+
- Optional jj formatter output
|
|
89
|
+
- Binary file detection
|
|
90
|
+
|
|
91
|
+
**TUI polish**
|
|
92
|
+
|
|
93
|
+
- Vim-style navigation plus mouse support
|
|
94
|
+
- Focusable panels and tabs
|
|
95
|
+
- Command palette with fuzzy search (`?` / `ctrl+p`)
|
|
96
|
+
- Context-aware status hints
|
|
97
|
+
- JSONC config with schema autocomplete
|
|
98
|
+
- Automatic update notifications
|
|
99
|
+
|
|
100
|
+
## CLI
|
|
108
101
|
|
|
109
102
|
Kajji includes a small CLI for scripting and agent workflows:
|
|
110
103
|
|
|
@@ -122,90 +115,99 @@ kajji comment delete -r @ --file src/App.tsx
|
|
|
122
115
|
kajji comment delete -r @ --all -y
|
|
123
116
|
```
|
|
124
117
|
|
|
125
|
-
|
|
118
|
+
## Configuration
|
|
126
119
|
|
|
127
120
|
Kajji reads JSONC config from `~/.config/kajji/config.json` (comments and trailing commas are supported).
|
|
128
121
|
|
|
129
122
|
- Open it from the command palette (`?`) with `open config`
|
|
130
|
-
- Changes
|
|
131
|
-
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
161
|
-
| `
|
|
162
|
-
|
|
|
163
|
-
| `ctrl+
|
|
164
|
-
| `
|
|
165
|
-
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
|
191
|
-
|
|
|
192
|
-
| `
|
|
193
|
-
| `
|
|
194
|
-
| `
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
123
|
+
- Changes made through `open config` are reloaded when you return to kajji
|
|
124
|
+
- The default config includes `$schema: "https://kajji.sh/schema.json"` for editor autocomplete
|
|
125
|
+
|
|
126
|
+
Common settings include:
|
|
127
|
+
|
|
128
|
+
- `ui.themeMode`: `system`, `dark`, or `light`
|
|
129
|
+
- `ui.syntaxTheme.dark` / `ui.syntaxTheme.light`: syntax highlighting theme names
|
|
130
|
+
- `ui.showFileTree`: show files as a tree or flat list
|
|
131
|
+
- `diff.layout`: `auto`, `unified`, or `split`
|
|
132
|
+
- `diff.autoSwitchWidth`: terminal width where `auto` switches to split view
|
|
133
|
+
- `diff.wrap`: wrap long diff lines
|
|
134
|
+
- `diff.useJjFormatter`: use jj's configured diff formatter in the detail pane
|
|
135
|
+
- `hooks`: commands to run before specific operations
|
|
136
|
+
- `whatsNewDisabled` / `autoUpdatesDisabled`: update notification controls
|
|
137
|
+
|
|
138
|
+
## Keybindings
|
|
139
|
+
|
|
140
|
+
Default keybindings are contextual. Press `?` or `ctrl+p` in kajji for the full command list available in the current panel.
|
|
141
|
+
|
|
142
|
+
### Global and navigation
|
|
143
|
+
|
|
144
|
+
| Key | Action |
|
|
145
|
+
| ---------------------- | --------------------------------------- |
|
|
146
|
+
| `q` | Quit |
|
|
147
|
+
| `?` / `ctrl+p` | Command palette |
|
|
148
|
+
| `Tab` / `shift+Tab` | Next / previous panel |
|
|
149
|
+
| `1` / `2` / `3` / `4` | Focus log / refs / detail / command log |
|
|
150
|
+
| `j` / `k` or `↓` / `↑` | Move down / up |
|
|
151
|
+
| `ctrl+d` / `ctrl+u` | Page down / up |
|
|
152
|
+
| `Enter` | Open/drill into the selected item |
|
|
153
|
+
| `Escape` | Back, close, or clear filter |
|
|
154
|
+
| `[` / `]` or `h` / `l` | Previous / next tab |
|
|
155
|
+
| `/` | Filter revisions or bookmarks |
|
|
156
|
+
| `ctrl+r` | Refresh |
|
|
157
|
+
| `ctrl+x` | Toggle normal/diff focus mode |
|
|
158
|
+
| `ctrl+o` | Open recent repository |
|
|
159
|
+
|
|
160
|
+
### jj operations
|
|
161
|
+
|
|
162
|
+
| Key | Action |
|
|
163
|
+
| --------- | ------------------------------------------------------------------------------------------ |
|
|
164
|
+
| `n` / `N` | New change / new menu |
|
|
165
|
+
| `e` | Edit change, or open selected file in editor from file view |
|
|
166
|
+
| `E` | Open all files in editor from file view |
|
|
167
|
+
| `d` | Describe, delete bookmark, restore operation, or discard file changes depending on context |
|
|
168
|
+
| `s` | Squash |
|
|
169
|
+
| `a` | Abandon |
|
|
170
|
+
| `r` | Rebase, or rename bookmark in refs |
|
|
171
|
+
| `S` | Split |
|
|
172
|
+
| `D` | Duplicate |
|
|
173
|
+
| `R` | Resolve conflicts, or toggle remote-only bookmarks in refs |
|
|
174
|
+
| `u` / `U` | Undo / redo |
|
|
175
|
+
| `f` / `F` | Git fetch / fetch menu |
|
|
176
|
+
| `p` / `P` | Git push / push menu |
|
|
177
|
+
|
|
178
|
+
### Diff and files
|
|
179
|
+
|
|
180
|
+
| Key | Action |
|
|
181
|
+
| ---------------------- | ------------------------------------------------------------- |
|
|
182
|
+
| `-` | Toggle file tree/list in file view, or jj formatter in detail |
|
|
183
|
+
| `v` | Toggle split/unified diff |
|
|
184
|
+
| `w` | Toggle diff line wrapping |
|
|
185
|
+
| `h` / `l` or `←` / `→` | Horizontal scroll when wrapping is off |
|
|
186
|
+
| `[` / `]` | Previous / next hunk in custom diff view |
|
|
187
|
+
| `{` / `}` | Previous / next file in custom diff view |
|
|
188
|
+
|
|
189
|
+
### Bookmarks and GitHub
|
|
190
|
+
|
|
191
|
+
| Key | Action |
|
|
192
|
+
| --------- | -------------------------------------------------- |
|
|
193
|
+
| `c` | Create bookmark |
|
|
194
|
+
| `d` | Delete bookmark |
|
|
195
|
+
| `r` | Rename bookmark |
|
|
196
|
+
| `x` | Forget bookmark locally |
|
|
197
|
+
| `b` | Set bookmark on selected revision |
|
|
198
|
+
| `m` | Move bookmark |
|
|
199
|
+
| `o` / `O` | Open selected revision on GitHub (prompt / direct) |
|
|
200
|
+
| `o` | Open selected bookmark's commit or PR on GitHub |
|
|
199
201
|
|
|
200
202
|
## Built With
|
|
201
203
|
|
|
202
|
-
- [OpenTUI](https://github.com/sst/opentui) + [SolidJS](https://www.solidjs.com/) -
|
|
203
|
-
- [Bun](https://bun.sh) -
|
|
204
|
+
- [OpenTUI](https://github.com/sst/opentui) + [SolidJS](https://www.solidjs.com/) - TypeScript TUI framework
|
|
205
|
+
- [Bun](https://bun.sh) - JavaScript runtime and build tooling
|
|
204
206
|
- [jj (Jujutsu)](https://github.com/martinvonz/jj) - Git-compatible VCS
|
|
205
207
|
|
|
206
208
|
## Related Projects
|
|
207
209
|
|
|
208
|
-
- [lazygit](https://github.com/jesseduffield/lazygit) -
|
|
210
|
+
- [lazygit](https://github.com/jesseduffield/lazygit) - Git TUI
|
|
209
211
|
- [jjui](https://github.com/idursun/jjui) - Go-based jj TUI
|
|
210
212
|
- [lazyjj](https://github.com/Cretezy/lazyjj) - Rust-based jj TUI
|
|
211
213
|
|
package/package.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kajji",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "A terminal UI for Jujutsu: the rudder for your jj",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kajji": "./bin/kajji"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
|
-
"postinstall": "node ./
|
|
9
|
+
"postinstall": "node ./scripts/postinstall.mjs"
|
|
10
10
|
},
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"kajji-darwin-arm64": "0.
|
|
13
|
-
"kajji-darwin-x64": "0.
|
|
14
|
-
"kajji-linux-x64": "0.
|
|
15
|
-
"kajji-linux-arm64": "0.
|
|
12
|
+
"kajji-darwin-arm64": "0.14.1",
|
|
13
|
+
"kajji-darwin-x64": "0.14.1",
|
|
14
|
+
"kajji-linux-x64": "0.14.1",
|
|
15
|
+
"kajji-linux-arm64": "0.14.1"
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs"
|
|
4
|
+
import os from "node:os"
|
|
5
|
+
import path from "node:path"
|
|
6
|
+
import { fileURLToPath } from "node:url"
|
|
7
|
+
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
+
|
|
10
|
+
const platformMap = { darwin: "darwin", linux: "linux" }
|
|
11
|
+
const archMap = { x64: "x64", arm64: "arm64" }
|
|
12
|
+
|
|
13
|
+
const platform = platformMap[os.platform()]
|
|
14
|
+
const arch = archMap[os.arch()]
|
|
15
|
+
|
|
16
|
+
if (!platform || !arch) {
|
|
17
|
+
console.log(`Unsupported platform: ${os.platform()}-${os.arch()}`)
|
|
18
|
+
process.exit(0)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const packageName = `kajji-${platform}-${arch}`
|
|
22
|
+
|
|
23
|
+
let packageDir
|
|
24
|
+
try {
|
|
25
|
+
const packageJsonPath = require.resolve(`${packageName}/package.json`, {
|
|
26
|
+
paths: [path.join(__dirname, "..")],
|
|
27
|
+
})
|
|
28
|
+
packageDir = path.dirname(packageJsonPath)
|
|
29
|
+
} catch {
|
|
30
|
+
console.log(`Platform package ${packageName} not found, skipping symlink`)
|
|
31
|
+
process.exit(0)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const binaryPath = path.join(packageDir, "bin", "kajji")
|
|
35
|
+
const targetPath = path.join(__dirname, "..", "bin", "kajji-binary")
|
|
36
|
+
|
|
37
|
+
if (!fs.existsSync(binaryPath)) {
|
|
38
|
+
console.log(`Binary not found at ${binaryPath}`)
|
|
39
|
+
process.exit(0)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
if (fs.existsSync(targetPath)) {
|
|
44
|
+
fs.unlinkSync(targetPath)
|
|
45
|
+
}
|
|
46
|
+
fs.symlinkSync(binaryPath, targetPath)
|
|
47
|
+
console.log(`kajji binary linked: ${targetPath} -> ${binaryPath}`)
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.log(`Could not create symlink: ${err.message}`)
|
|
50
|
+
}
|
package/script/postinstall.mjs
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from "node:fs"
|
|
4
|
-
import os from "node:os"
|
|
5
|
-
import path from "node:path"
|
|
6
|
-
import { fileURLToPath } from "node:url"
|
|
7
|
-
|
|
8
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
9
|
-
|
|
10
|
-
const platformMap = { darwin: "darwin", linux: "linux" }
|
|
11
|
-
const archMap = { x64: "x64", arm64: "arm64" }
|
|
12
|
-
|
|
13
|
-
const platform = platformMap[os.platform()]
|
|
14
|
-
const arch = archMap[os.arch()]
|
|
15
|
-
|
|
16
|
-
if (!platform || !arch) {
|
|
17
|
-
console.log(`Unsupported platform: ${os.platform()}-${os.arch()}`)
|
|
18
|
-
process.exit(0)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const packageName = `kajji-${platform}-${arch}`
|
|
22
|
-
|
|
23
|
-
let packageDir
|
|
24
|
-
try {
|
|
25
|
-
const packageJsonPath = require.resolve(`${packageName}/package.json`, {
|
|
26
|
-
paths: [path.join(__dirname, "..")],
|
|
27
|
-
})
|
|
28
|
-
packageDir = path.dirname(packageJsonPath)
|
|
29
|
-
} catch {
|
|
30
|
-
console.log(`Platform package ${packageName} not found, skipping symlink`)
|
|
31
|
-
process.exit(0)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const binaryPath = path.join(packageDir, "bin", "kajji")
|
|
35
|
-
const targetPath = path.join(__dirname, "..", "bin", "kajji-binary")
|
|
36
|
-
|
|
37
|
-
if (!fs.existsSync(binaryPath)) {
|
|
38
|
-
console.log(`Binary not found at ${binaryPath}`)
|
|
39
|
-
process.exit(0)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
if (fs.existsSync(targetPath)) {
|
|
44
|
-
fs.unlinkSync(targetPath)
|
|
45
|
-
}
|
|
46
|
-
fs.symlinkSync(binaryPath, targetPath)
|
|
47
|
-
console.log(`kajji binary linked: ${targetPath} -> ${binaryPath}`)
|
|
48
|
-
} catch (err) {
|
|
49
|
-
console.log(`Could not create symlink: ${err.message}`)
|
|
50
|
-
}
|