sight 0.1.0 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05abdb72bec3313b413b826f36a5f5a5590374d7d61640460a41eddd75a57ab6
4
- data.tar.gz: 27d5fb5d877eebf0152ffa070201e2bb0755256cb466027bc3f61c26bccd4a16
3
+ metadata.gz: 24b700f118e81b323388d9dc1413bcb493a8c8625664099fcff2eeb524fbe4e7
4
+ data.tar.gz: c0b1e78505b39f82909607431c5c7cf27a56545292abb0967887f2a60be04420
5
5
  SHA512:
6
- metadata.gz: 41a607fda53ab9822d968c8d2af280b71db14bb3edf96752dfd8ce26450277d44e19ad85f1677940506b75f80503f6511201291795d031d42aab10beb598bbe5
7
- data.tar.gz: 254d6c49a5abb2ead9130daad53fc874523a282b1b69c50b9df0f015280ea7fb47ff8a099f3b21badd10b074c0b39941749c7b200f913a358a7e6635029954dd
6
+ metadata.gz: b3e9f75deef46a4da613023322b833f0bbf86c7f2cc5f3ccde44f30fd7bd0cb1712d4925c5ed18a865e4ff6fe488d787eaba38939c52cea2e0a9d1f62fec4a97
7
+ data.tar.gz: 6530759983f83fed70bab30adb12fa6bae9990f5554e91170e5547cbeb58514759eb649fe322b1857d895ec0051017ab53ee2b9cbc35bb918b66199c9fbfbf51
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.2.0] - 2026-03-04
4
+
5
+ ### Changed
6
+
7
+ - Navigation is now hunk-based: `j`/`k` jump between hunks
8
+ - Active hunk is highlighted; inactive hunks are dimmed
9
+ - Removed line-scrolling keys (`f`/`b`/`d`/`u`/`g`/`G`) and arrow keys
10
+
3
11
  ## [0.1.0] - 2026-03-04
4
12
 
5
13
  ### Added
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Sight
2
2
 
3
- Terminal UI for browsing git diffs interactively with colors, scrolling, and file navigation.
3
+ Terminal UI for browsing git diffs interactively with colors, hunk navigation, and file navigation.
4
4
 
5
5
  ## Installation
6
6
 
@@ -20,16 +20,10 @@ sight
20
20
 
21
21
  | Key | Action |
22
22
  |-----|--------|
23
- | `j` / `↓` | Scroll down |
24
- | `k` / `↑` | Scroll up |
25
- | `d` | Half page down |
26
- | `u` | Half page up |
27
- | `f` | Full page down |
28
- | `b` | Full page up |
29
- | `g` | Go to top |
30
- | `G` | Go to bottom |
31
- | `n` / `→` | Next file |
32
- | `p` / `←` | Previous file |
23
+ | `j` | Next hunk |
24
+ | `k` | Previous hunk |
25
+ | `n` | Next file |
26
+ | `p` | Previous file |
33
27
  | `?` | Toggle help |
34
28
  | `q` / `Esc` | Quit |
35
29
 
data/lib/sight/app.rb CHANGED
@@ -5,13 +5,15 @@ require "curses"
5
5
  module Sight
6
6
  class App
7
7
  attr_reader :files, :file_lines
8
- attr_accessor :file_idx, :offset
8
+ attr_accessor :file_idx, :offset, :hunk_idx
9
9
 
10
10
  def initialize(files)
11
11
  @files = files
12
12
  @file_lines = files.map { build_file_lines(it) }
13
13
  @file_idx = 0
14
14
  @offset = 0
15
+ @hunk_idx = 0
16
+ @hunk_offsets_cache = {}
15
17
  end
16
18
 
17
19
  def run
@@ -49,6 +51,7 @@ module Sight
49
51
  Curses.init_pair(2, Curses::COLOR_RED, -1)
50
52
  Curses.init_pair(3, Curses::COLOR_CYAN, -1)
51
53
  Curses.init_pair(4, Curses::COLOR_YELLOW, -1)
54
+ Curses.init_pair(5, 240, -1)
52
55
  end
53
56
 
54
57
  def lines
@@ -78,20 +81,29 @@ module Sight
78
81
  dim = Curses.color_pair(0) | Curses::A_DIM
79
82
  content_width = width - gutter - 3
80
83
 
84
+ selected_start = hunk_offsets[hunk_idx]
85
+ selected_end = if hunk_idx + 1 < hunk_offsets.size
86
+ hunk_offsets[hunk_idx + 1]
87
+ else
88
+ lines.size
89
+ end
90
+
81
91
  scroll_height.times do |row|
82
92
  idx = offset + row
83
93
  break if idx >= lines.size
84
94
  line = lines[idx]
85
95
  win.setpos(row + 2, 0)
86
- win.attron(dim) { win.addstr("#{format_gutter(line.type, line.lineno, gutter)} ") }
87
- win.attron(color_for(line.type)) { win.addstr(line.text[0, content_width]) }
96
+ active = idx >= selected_start && idx < selected_end
97
+ win.attron(dim) { win.addstr("#{format_gutter(line.type, line.lineno, gutter)} \u2502 ") }
98
+ attr = active ? color_for(line.type) : Curses.color_pair(5)
99
+ win.attron(attr) { win.addstr(line.text[0, content_width]) }
88
100
  end
89
101
  end
90
102
 
91
103
  def render_status_bar(win, width)
92
104
  win.setpos(Curses.lines - 1, 0)
93
105
  win.attron(Curses.color_pair(4) | Curses::A_REVERSE) do
94
- status = " File #{file_idx + 1}/#{files.size} | Line #{offset + 1}/#{lines.size} "
106
+ status = " File #{file_idx + 1}/#{files.size} | Hunk #{hunk_idx + 1}/#{hunk_offsets.size} | Line #{offset + 1}/#{lines.size} "
95
107
  win.addstr(status.ljust(width))
96
108
  end
97
109
  end
@@ -126,41 +138,24 @@ module Sight
126
138
  end
127
139
  end
128
140
 
129
- def scroll_to(delta)
130
- max = [0, lines.size - scroll_height].max
131
- self.offset = (offset + delta).clamp(0, max)
132
- end
133
-
134
141
  def handle_input
135
142
  key = Curses.getch
136
143
  case key
137
144
  when "q", 27 then return false
138
- when "j", Curses::KEY_DOWN then scroll_to(1)
139
- when "k", Curses::KEY_UP then scroll_to(-1)
140
- when "f" then scroll_to(scroll_height)
141
- when "b" then scroll_to(-scroll_height)
142
- when "d" then scroll_to(scroll_height / 2)
143
- when "u" then scroll_to(-scroll_height / 2)
144
- when "g" then self.offset = 0
145
- when "G" then scroll_to(lines.size)
146
- when "n", Curses::KEY_RIGHT then jump_file(1)
147
- when "p", Curses::KEY_LEFT then jump_file(-1)
145
+ when "j" then jump_hunk(1)
146
+ when "k" then jump_hunk(-1)
147
+ when "n" then jump_file(1)
148
+ when "p" then jump_file(-1)
148
149
  when "?" then show_help
149
150
  end
150
151
  true
151
152
  end
152
153
 
153
154
  HELP_KEYS = [
154
- ["j / ↓", "Scroll down"],
155
- ["k / ↑", "Scroll up"],
156
- ["d", "Half Page down"],
157
- ["u", "Half page up"],
158
- ["f", "Full page down"],
159
- ["b", "Full page up"],
160
- ["g", "Go to top"],
161
- ["G", "Go to bottom"],
162
- ["n / →", "Next file"],
163
- ["p / ←", "Previous file"],
155
+ ["j", "Next hunk"],
156
+ ["k", "Previous hunk"],
157
+ ["n", "Next file"],
158
+ ["p", "Previous file"],
164
159
  ["q / Esc", "Quit"],
165
160
  ["?", "Toggle this help"]
166
161
  ].freeze
@@ -201,11 +196,33 @@ module Sight
201
196
  end
202
197
  end
203
198
 
199
+ def hunk_offsets
200
+ @hunk_offsets_cache[file_idx] ||= begin
201
+ offsets = []
202
+ line_idx = 0
203
+ files[file_idx].hunks.each do |hunk|
204
+ offsets << line_idx
205
+ line_idx += hunk.lines.size
206
+ end
207
+ offsets
208
+ end
209
+ end
210
+
211
+ def jump_hunk(delta)
212
+ return if hunk_offsets.empty?
213
+ self.hunk_idx = (hunk_idx + delta).clamp(0, hunk_offsets.size - 1)
214
+ target = hunk_offsets[hunk_idx]
215
+ margin = [2, scroll_height / 4].min
216
+ max = [0, lines.size - scroll_height].max
217
+ self.offset = [target - margin, 0].max.clamp(0, max)
218
+ end
219
+
204
220
  def jump_file(direction)
205
221
  new_idx = (file_idx + direction).clamp(0, files.size - 1)
206
222
  return if new_idx == file_idx
207
223
  self.file_idx = new_idx
208
224
  self.offset = 0
225
+ self.hunk_idx = 0
209
226
  end
210
227
  end
211
228
  end
data/lib/sight/cli.rb CHANGED
@@ -9,7 +9,7 @@ module Sight
9
9
  puts "Usage: sight"
10
10
  puts "Interactive git diff viewer (staged + unstaged)"
11
11
  puts
12
- puts "Keys: j/k/↑/↓ scroll, f/b page, d/u half-page, g/G top/bottom, n/p/→/← next/prev file, ? help, q quit"
12
+ puts "Keys: j/k hunks, n/p files, ? help, q quit"
13
13
  return
14
14
  end
15
15
 
@@ -20,7 +20,7 @@ module Sight
20
20
 
21
21
  raw = Git.diff
22
22
  if raw.empty?
23
- warn "No diff output."
23
+ warn "No diff output"
24
24
  return
25
25
  end
26
26
 
data/lib/sight/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Sight
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ariel Rzezak