hyperlist 1.9.1 → 1.9.2

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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +8 -1
  4. data/hyperlist +79 -85
  5. data/hyperlist.gemspec +1 -1
  6. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 485f51c3f998ca45514979e831f928a2ca907674483ba1ceef644ce6db22423e
4
- data.tar.gz: ca21fe8db31f2b1ac1a1e8059c92f94dbc17a8d42790247238f8073c77ef973d
3
+ metadata.gz: bb81e544d2363e852a1e29bb1eef3d2fd891e766b2ade2a3e5c56fdc3073302e
4
+ data.tar.gz: 14f24e55bfb6f3241ef9120cadcc680971617bfa5378893c3523e4eb1cb1105b
5
5
  SHA512:
6
- metadata.gz: d40e130fbff4cb2564f8a9c541257cd084072f1917067d529a86427c63b0e3716b7a6704f584cf64dc3e204fd0c63a71a20443f3bcef99bc5377bb43c657b7f3
7
- data.tar.gz: 5e6a7cdf9f8b2fb7e93382e41ba8d6e2e39d0403a0e64480d763d8385f5e4d7320684ea4b1fd7503d5a4ed08fe515d5176a04d8ab8723f9e43b7ab9d04a9e2b8
6
+ metadata.gz: 067b8ba96f97768aa5461e2458b7215cf045926875765ac631b450c9a15ed2313ded67f7986163c6fef8c5408dddf0160a10fa7933ffedbe23722822078e9060
7
+ data.tar.gz: c10a7a1e147fadd212445b9d5ee7baadada737f53ccc55aa89db86f95f2cb208edf1d56a373020c4ac51c5a898e5c2198cb1458cccbfea36526dff104cfe61a7
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to the HyperList Ruby TUI will be documented in this file.
4
4
 
5
+ ## [1.9.2] - 2025-12-02
6
+
7
+ ### Fixed
8
+ - **Presentation mode completely rewritten**
9
+ - Now dynamically folds/unfolds as you navigate (like hyperlist.vim)
10
+ - Entering presentation mode (C-P) now stays on the current item instead of jumping to the first line
11
+ - Moving up/down properly reveals the path to the current item while folding everything else
12
+ - Uses proper item tracking via real indices to handle fold state changes correctly
13
+
5
14
  ## [1.9.1] - 2025-12-02
6
15
 
7
16
  ### Added
data/README.md CHANGED
@@ -29,7 +29,14 @@ For historical context and the original VIM implementation, see: [hyperlist.vim]
29
29
  ### Help Screen
30
30
  ![HyperList Help](img/screenshot_help.png)
31
31
 
32
- ## What's New in v1.9.1
32
+ ## What's New in v1.9.2
33
+
34
+ ### Presentation Mode Fixed
35
+ - **Dynamic folding**: Presentation mode now works like hyperlist.vim - folds/unfolds dynamically as you navigate
36
+ - **Cursor preservation**: Entering presentation mode (C-P) stays on the current item instead of jumping to the first line
37
+ - **Proper navigation**: Moving up/down reveals the path to the current item while folding everything else
38
+
39
+ ## Previous Release: v1.9.1
33
40
 
34
41
  ### External File Change Detection
35
42
  - **Auto-detect external changes**: HyperList now monitors when the file is modified by other processes (vim, another Claude Code session, etc.)
data/hyperlist CHANGED
@@ -7,7 +7,7 @@
7
7
  # Check for help/version BEFORE loading any libraries
8
8
  if ARGV[0] == '-h' || ARGV[0] == '--help'
9
9
  puts <<~HELP
10
- HyperList v1.9.1 - Terminal User Interface for HyperList files
10
+ HyperList v1.9.2 - Terminal User Interface for HyperList files
11
11
 
12
12
  USAGE
13
13
  hyperlist [OPTIONS] [FILE]
@@ -52,7 +52,7 @@ if ARGV[0] == '-h' || ARGV[0] == '--help'
52
52
  HELP
53
53
  exit 0
54
54
  elsif ARGV[0] == '-v' || ARGV[0] == '--version'
55
- puts "HyperList v1.9.1"
55
+ puts "HyperList v1.9.2"
56
56
  exit 0
57
57
  end
58
58
 
@@ -73,7 +73,7 @@ class HyperListApp
73
73
  include Rcurses::Input
74
74
  include Rcurses::Cursor
75
75
 
76
- VERSION = "1.9.1"
76
+ VERSION = "1.9.2"
77
77
 
78
78
  def initialize(filename = nil)
79
79
  @filename = filename ? File.expand_path(filename) : nil
@@ -2043,28 +2043,28 @@ class HyperListApp
2043
2043
 
2044
2044
  def move_up
2045
2045
  if @presentation_mode
2046
- # In presentation mode, we need to handle navigation differently
2047
- visible_before = get_visible_items
2048
-
2046
+ # In presentation mode: move to target, then update folds
2047
+ visible = get_visible_items
2048
+ return if visible.empty?
2049
+
2050
+ # Calculate target position
2049
2051
  if @current == 0
2050
- # Wrap around to last item
2051
- target_index = visible_before.length - 1
2052
+ target_index = visible.length - 1
2052
2053
  else
2053
2054
  target_index = @current - 1
2054
2055
  end
2055
-
2056
- # Get the actual item we want to move to
2057
- target_item = visible_before[target_index]
2058
- target_real_idx = get_real_index(target_item)
2059
-
2060
- # Update presentation focus for the target item
2056
+
2057
+ # Get the real index of the target item
2058
+ target_real_idx = get_real_index(visible[target_index])
2059
+
2060
+ # Temporarily set current to target so update_presentation_focus works on it
2061
2061
  @current = target_index
2062
2062
  update_presentation_focus
2063
-
2064
- # Now find where the target item ended up after reorganization
2065
- visible_after = get_visible_items
2066
- visible_after.each_with_index do |item, idx|
2067
- if get_real_index(item) == target_real_idx
2063
+
2064
+ # After fold changes, find where the target item is now
2065
+ new_visible = get_visible_items
2066
+ new_visible.each_with_index do |item, idx|
2067
+ if item["_real_index"] == target_real_idx
2068
2068
  @current = idx
2069
2069
  break
2070
2070
  end
@@ -2072,9 +2072,8 @@ class HyperListApp
2072
2072
  else
2073
2073
  # Normal mode navigation
2074
2074
  max_items = get_visible_items.length - 1
2075
-
2075
+
2076
2076
  if @current == 0
2077
- # Wrap around to last item
2078
2077
  @current = max_items
2079
2078
  else
2080
2079
  @current = [@current - 1, 0].max
@@ -2084,29 +2083,29 @@ class HyperListApp
2084
2083
 
2085
2084
  def move_down
2086
2085
  if @presentation_mode
2087
- # In presentation mode, we need to handle navigation differently
2088
- visible_before = get_visible_items
2089
- max = visible_before.length - 1
2090
-
2091
- if @current == max
2092
- # Wrap around to first item
2086
+ # In presentation mode: move to target, then update folds
2087
+ visible = get_visible_items
2088
+ return if visible.empty?
2089
+ max = visible.length - 1
2090
+
2091
+ # Calculate target position
2092
+ if @current >= max
2093
2093
  target_index = 0
2094
2094
  else
2095
2095
  target_index = @current + 1
2096
2096
  end
2097
-
2098
- # Get the actual item we want to move to
2099
- target_item = visible_before[target_index]
2100
- target_real_idx = get_real_index(target_item)
2101
-
2102
- # Update presentation focus for the target item
2097
+
2098
+ # Get the real index of the target item
2099
+ target_real_idx = get_real_index(visible[target_index])
2100
+
2101
+ # Temporarily set current to target so update_presentation_focus works on it
2103
2102
  @current = target_index
2104
2103
  update_presentation_focus
2105
-
2106
- # Now find where the target item ended up after reorganization
2107
- visible_after = get_visible_items
2108
- visible_after.each_with_index do |item, idx|
2109
- if get_real_index(item) == target_real_idx
2104
+
2105
+ # After fold changes, find where the target item is now
2106
+ new_visible = get_visible_items
2107
+ new_visible.each_with_index do |item, idx|
2108
+ if item["_real_index"] == target_real_idx
2110
2109
  @current = idx
2111
2110
  break
2112
2111
  end
@@ -2114,9 +2113,8 @@ class HyperListApp
2114
2113
  else
2115
2114
  # Normal mode navigation
2116
2115
  max = get_visible_items.length - 1
2117
-
2118
- if @current == max
2119
- # Wrap around to first item
2116
+
2117
+ if @current >= max
2120
2118
  @current = 0
2121
2119
  else
2122
2120
  @current = [@current + 1, max].min
@@ -2345,84 +2343,80 @@ class HyperListApp
2345
2343
  # Enter presentation mode
2346
2344
  # Remember which item we're on before any folding changes
2347
2345
  visible_items = get_visible_items
2348
- if @current < visible_items.length
2349
- target_item = visible_items[@current]
2350
- end
2351
-
2346
+ return if visible_items.empty? || @current >= visible_items.length
2347
+
2348
+ # Get the real index of the current item
2349
+ target_real_idx = visible_items[@current]["_real_index"]
2350
+
2352
2351
  @presentation_mode = true
2353
- # Clear cache to force re-rendering
2352
+ # Clear cache to force re-rendering
2354
2353
  @processed_cache.clear
2355
2354
  update_presentation_focus
2356
-
2357
- # Make sure cursor is still on the same item after initial folding
2358
- if target_item
2359
- new_visible = get_visible_items
2360
- new_position = new_visible.index(target_item)
2361
- @current = new_position if new_position
2355
+
2356
+ # Find where the target item is after fold changes
2357
+ new_visible = get_visible_items
2358
+ new_visible.each_with_index do |item, idx|
2359
+ if item["_real_index"] == target_real_idx
2360
+ @current = idx
2361
+ break
2362
+ end
2362
2363
  end
2363
-
2364
- @message = "Presentation mode enabled - focus on current item"
2364
+
2365
+ @message = "Presentation mode enabled"
2365
2366
  end
2366
2367
  end
2367
2368
 
2368
2369
  def update_presentation_focus
2369
- # This method updates the focus in presentation mode
2370
- # It will be called whenever the cursor moves
2370
+ # Presentation mode: fold everything, then reveal path to current item
2371
+ # Like vim's: fold to level 0, then zv (reveal current line)
2371
2372
  return unless @presentation_mode
2372
-
2373
+
2373
2374
  visible_items = get_visible_items
2374
2375
  return if visible_items.empty? || @current >= visible_items.length
2375
-
2376
- # Remember which item we're focused on
2376
+
2377
+ # Get the current item before we change anything
2377
2378
  current_item = visible_items[@current]
2378
- current_level = current_item["level"]
2379
2379
  current_real_idx = get_real_index(current_item)
2380
-
2381
- # First, fold everything
2380
+ return unless current_real_idx
2381
+
2382
+ current_level = current_item["level"]
2383
+
2384
+ # Step 1: Fold everything that has children
2382
2385
  @items.each_with_index do |item, idx|
2383
2386
  item["fold"] = has_children?(idx, @items)
2384
2387
  item["presentation_focus"] = false
2385
2388
  end
2386
-
2387
- # Mark current item as in focus and unfold it in the main items array
2388
- if current_real_idx && current_real_idx < @items.length
2389
- @items[current_real_idx]["presentation_focus"] = true
2390
- @items[current_real_idx]["fold"] = false
2391
- end
2392
-
2393
- # Unfold all ancestors of current item
2394
- ancestor_indices = []
2389
+
2390
+ # Step 2: Unfold all ancestors (path from root to current item)
2395
2391
  search_level = current_level - 1
2396
2392
  idx = current_real_idx - 1
2397
-
2398
2393
  while idx >= 0 && search_level >= 0
2399
2394
  if @items[idx]["level"] == search_level
2400
- @items[idx]["fold"] = false
2401
- @items[idx]["presentation_focus"] = false # Ancestors visible but not focused
2402
- ancestor_indices << idx
2395
+ @items[idx]["fold"] = false # Unfold this ancestor
2403
2396
  search_level -= 1
2404
2397
  end
2405
2398
  idx -= 1
2406
2399
  end
2407
-
2408
- # Mark immediate children as in focus (only one level down)
2400
+
2401
+ # Step 3: Unfold the current item to show its direct children
2402
+ @items[current_real_idx]["fold"] = false
2403
+ @items[current_real_idx]["presentation_focus"] = true
2404
+
2405
+ # Step 4: Mark direct children as in focus (for visual highlighting)
2409
2406
  idx = current_real_idx + 1
2410
2407
  while idx < @items.length && @items[idx]["level"] > current_level
2411
2408
  if @items[idx]["level"] == current_level + 1
2412
2409
  @items[idx]["presentation_focus"] = true
2413
- # Don't unfold children - let them stay folded unless user explicitly unfolds
2414
2410
  end
2415
2411
  idx += 1
2416
2412
  end
2417
-
2418
- # Now recalculate the cursor position to point to the same item
2413
+
2414
+ # Step 5: Recalculate cursor position after fold changes
2419
2415
  new_visible_items = get_visible_items
2420
2416
  new_position = new_visible_items.index(current_item)
2421
- if new_position
2422
- @current = new_position
2423
- end
2424
-
2425
- # Clear cache to force re-rendering with new focus
2417
+ @current = new_position if new_position
2418
+
2419
+ # Clear cache to force re-rendering
2426
2420
  @processed_cache.clear
2427
2421
  end
2428
2422
 
data/hyperlist.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "hyperlist"
3
- spec.version = "1.9.1"
3
+ spec.version = "1.9.2"
4
4
  spec.authors = ["Geir Isene"]
5
5
  spec.email = ["g@isene.com"]
6
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.1
4
+ version: 1.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene