hyperlist 1.5.0 → 1.5.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 (5) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/hyperlist +86 -41
  4. data/hyperlist.gemspec +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e9aaa16290fdb66f194d55acfd1120e20b7b5c4f8b49203c20967580738cc4d
4
- data.tar.gz: 8155928a2850fff84d471b287f45f38e490d907cb172dba0e18fbd8da06e18ea
3
+ metadata.gz: ec123fe286a3c475f76868ed8d0cdd1a512f7323c6da8d9446d2ea7e3913ce61
4
+ data.tar.gz: 637a405887c8725cd229ba0dbd8bfa0d0f75cffa787f336a59477a32a70b1669
5
5
  SHA512:
6
- metadata.gz: 1df02a40d21b232190692f1bc7a2e08b516fa66e0a8a35743efd8743bceabe1548ea3b20bc3ab5a8f277558467af98c8bc8c8bece6322154471e40ba0dd2ab18
7
- data.tar.gz: 8cbfa174bec6a8f3043942bd212f8b3b24548fe1ec1e42b4ef183f9db37bdd6dc0c934945bf99a29c85638b4a5d098c917d0e150c53feda7637e3e18f752075e
6
+ metadata.gz: c1ec3ebc57c928ab5c583da3ab2663cbaf73da09e36818f14e0973f3392583ebb5dc4ad1ee543f8263b386709677b70b73227b71c8145738f5afeee14e7a6af4
7
+ data.tar.gz: 8d2ff0119d45decfba5fbef08d96d19ebf92f63d0ce33fe35350a3a53cd92fbb94cc845fe85a951d746c374b87b2932c50f5bd54623b953edab162e8eee901c8
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.5.2] - 2025-09-18
6
+
7
+ ### Enhanced
8
+ - **Smart movement behavior for collapsed items**
9
+ - LEFT/RIGHT arrow keys now move collapsed trees as a unit when item is folded
10
+ - Uncollapsed items move individually (only the current item)
11
+ - Maintains "move what's visible" principle for intuitive navigation
12
+ - Works in both normal and split-view modes
13
+
5
14
  ## [1.4.5] - 2025-09-02
6
15
 
7
16
  ### Added
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.4.3 - Terminal User Interface for HyperList files
10
+ HyperList v1.5.1 - 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.4.0"
55
+ puts "HyperList v1.5.1"
56
56
  exit 0
57
57
  end
58
58
 
@@ -72,7 +72,7 @@ class HyperListApp
72
72
  include Rcurses::Input
73
73
  include Rcurses::Cursor
74
74
 
75
- VERSION = "1.5.0"
75
+ VERSION = "1.5.1"
76
76
 
77
77
  def initialize(filename = nil)
78
78
  @filename = filename ? File.expand_path(filename) : nil
@@ -135,9 +135,6 @@ class HyperListApp
135
135
  @cols = ENV['COLUMNS']&.to_i || 80
136
136
  end
137
137
 
138
- # Debug: uncomment to see terminal size
139
- # puts "Terminal size: #{@rows}x#{@cols}"
140
-
141
138
  # Setup terminal resize handler
142
139
  setup_resize_handler
143
140
 
@@ -175,9 +172,8 @@ class HyperListApp
175
172
  # This is how RTFM works - it passes colored command output to panes with nil colors
176
173
  @main = Pane.new(1, 1, split_width - 1, @rows - 1, nil, nil)
177
174
  @split_pane = Pane.new(split_width + 1, 1, split_width - 1, @rows - 1, nil, nil)
178
- # Footer: Use @rows for y-position (this puts it at the actual bottom)
179
175
  @footer = Pane.new(1, @rows, @cols, 1, 15, 8)
180
-
176
+
181
177
  # Add separator with explicit background to overwrite emoji overflow
182
178
  @separator = Pane.new(split_width, 1, 1, @rows - 1, 15, 0)
183
179
  # Build separator with explicit clearing
@@ -192,7 +188,6 @@ class HyperListApp
192
188
  # Use nil colors like RTFM to avoid ANSI wrapping issues
193
189
  # This prevents the corruption that happens with narrow terminals
194
190
  @main = Pane.new(1, 1, @cols, @rows - 1, nil, nil)
195
- # Footer: Use @rows for y-position (this puts it at the actual bottom)
196
191
  @footer = Pane.new(1, @rows, @cols, 1, 15, 8)
197
192
  end
198
193
  end
@@ -661,7 +656,6 @@ class HyperListApp
661
656
  content += "\n" unless content.end_with?("\n")
662
657
  content += "\n" + @config_line
663
658
  else
664
- # Debug: Check why config line is missing
665
659
  if @fold_level != 99
666
660
  # Rebuild config line if we have config settings but no line
667
661
  update_config_line
@@ -1604,17 +1598,57 @@ class HyperListApp
1604
1598
  def has_children?(idx, items)
1605
1599
  return false if idx >= items.length - 1
1606
1600
  return false if idx < 0
1607
-
1601
+
1608
1602
  current_item = items[idx]
1609
1603
  next_item = items[idx + 1]
1610
-
1604
+
1611
1605
  # For visible items array
1612
1606
  if current_item.is_a?(Hash) && next_item.is_a?(Hash)
1613
1607
  return next_item["level"] > current_item["level"]
1614
1608
  end
1615
-
1609
+
1616
1610
  false
1617
1611
  end
1612
+
1613
+ def should_move_with_children?
1614
+ if @split_view && @active_pane == :split
1615
+ # Handle split pane
1616
+ visible_items = get_visible_split_items
1617
+ return false if @split_current >= visible_items.length
1618
+
1619
+ item = visible_items[@split_current]
1620
+ real_idx = @split_items.index(item)
1621
+
1622
+ # Move with children only if item is folded (collapsed) and has children
1623
+ return real_idx && has_children_in_array?(real_idx, @split_items) && item["fold"]
1624
+ else
1625
+ # Handle main pane
1626
+ visible = get_visible_items
1627
+ return false if @current >= visible.length
1628
+
1629
+ item = visible[@current]
1630
+ real_idx = get_real_index(item)
1631
+
1632
+ # Move with children only if item is folded (collapsed) and has children
1633
+ return real_idx && has_children?(real_idx, @items) && @items[real_idx]["fold"]
1634
+ end
1635
+ end
1636
+
1637
+ def get_last_descendant_index(idx, items)
1638
+ # Find the index after the last descendant of item at idx
1639
+ # Returns idx + 1 if no children
1640
+ return idx + 1 if idx >= items.length - 1
1641
+ return idx + 1 unless has_children?(idx, items)
1642
+
1643
+ current_level = items[idx]["level"]
1644
+ last_idx = idx + 1
1645
+
1646
+ while last_idx < items.length && items[last_idx]["level"] > current_level
1647
+ last_idx += 1
1648
+ end
1649
+
1650
+ last_idx
1651
+ end
1618
1652
 
1619
1653
  def indent_split_right(with_children = false)
1620
1654
  visible_items = get_visible_split_items
@@ -2251,13 +2285,21 @@ class HyperListApp
2251
2285
 
2252
2286
  def insert_line_with_text(text)
2253
2287
  return unless text && !text.strip.empty?
2254
-
2288
+
2255
2289
  save_undo_state # Save state before modification
2256
2290
  visible = get_visible_items
2257
2291
  if @current < visible.length
2258
2292
  level = visible[@current]["level"]
2259
2293
  real_idx = get_real_index(visible[@current])
2260
- @items.insert(real_idx + 1, {"text" => text, "level" => level, "fold" => false})
2294
+
2295
+ # If current item has collapsed children, insert after all descendants
2296
+ if @items[real_idx]["fold"] && has_children?(real_idx, @items)
2297
+ insert_idx = get_last_descendant_index(real_idx, @items)
2298
+ else
2299
+ insert_idx = real_idx + 1
2300
+ end
2301
+
2302
+ @items.insert(insert_idx, {"text" => text, "level" => level, "fold" => false})
2261
2303
  else
2262
2304
  @items << {"text" => text, "level" => 0, "fold" => false}
2263
2305
  end
@@ -2297,17 +2339,26 @@ class HyperListApp
2297
2339
 
2298
2340
  def insert_child_with_text(text)
2299
2341
  return unless text && !text.strip.empty?
2300
-
2342
+
2301
2343
  save_undo_state # Save state before modification
2302
2344
  visible = get_visible_items
2303
2345
  if @current < visible.length
2304
2346
  level = visible[@current]["level"] + 1
2305
2347
  real_idx = get_real_index(visible[@current])
2306
-
2348
+
2349
+ # If current item has collapsed children, insert as last child
2350
+ if @items[real_idx]["fold"] && has_children?(real_idx, @items)
2351
+ # Insert just before the next sibling (at the end of all descendants)
2352
+ insert_idx = get_last_descendant_index(real_idx, @items)
2353
+ else
2354
+ # No collapsed children, insert right after current item
2355
+ insert_idx = real_idx + 1
2356
+ end
2357
+
2307
2358
  # Unfold parent if needed
2308
2359
  @items[real_idx]["fold"] = false
2309
-
2310
- @items.insert(real_idx + 1, {"text" => text, "level" => level, "fold" => false})
2360
+
2361
+ @items.insert(insert_idx, {"text" => text, "level" => level, "fold" => false})
2311
2362
  else
2312
2363
  @items << {"text" => text, "level" => 1, "fold" => false}
2313
2364
  end
@@ -4003,13 +4054,6 @@ class HyperListApp
4003
4054
  begin
4004
4055
  c = getchr
4005
4056
 
4006
- # Debug logging
4007
- if ENV['DEBUG']
4008
- File.open('/tmp/hyperlist_debug.log', 'a') do |f|
4009
- f.puts "Password prompt key: #{c.inspect}"
4010
- end
4011
- end
4012
-
4013
4057
  case c
4014
4058
  when "ENTER", "\r", "\n"
4015
4059
  break
@@ -4676,11 +4720,6 @@ class HyperListApp
4676
4720
  # Skip truly nil input
4677
4721
  next if c.nil?
4678
4722
 
4679
- # Debug logging to file
4680
- File.open('/tmp/hyperlist_debug.log', 'a') do |f|
4681
- f.puts "Template key received: #{c.inspect} (length: #{c.length}, ord: #{c.bytes.inspect})"
4682
- end if ENV['DEBUG']
4683
-
4684
4723
  case c
4685
4724
  when "q", "ESC", "C-c", "Q"
4686
4725
  exit_loop = true
@@ -4868,16 +4907,20 @@ class HyperListApp
4868
4907
  indent_left(true) # with children
4869
4908
  end
4870
4909
  when "RIGHT"
4910
+ # Move collapsed trees as a unit, but only the item if uncollapsed
4911
+ should_move_children = should_move_with_children?
4871
4912
  if @split_view && @active_pane == :split
4872
- indent_split_right(false)
4913
+ indent_split_right(should_move_children)
4873
4914
  else
4874
- indent_right(false)
4915
+ indent_right(should_move_children)
4875
4916
  end
4876
4917
  when "LEFT"
4918
+ # Move collapsed trees as a unit, but only the item if uncollapsed
4919
+ should_move_children = should_move_with_children?
4877
4920
  if @split_view && @active_pane == :split
4878
- indent_split_left(false)
4921
+ indent_split_left(should_move_children)
4879
4922
  else
4880
- indent_left(false)
4923
+ indent_left(should_move_children)
4881
4924
  end
4882
4925
  when " "
4883
4926
  toggle_fold
@@ -5666,18 +5709,20 @@ class HyperListApp
5666
5709
  when "l"
5667
5710
  go_to_first_child
5668
5711
  when "LEFT"
5669
- # Unindent only the current item
5712
+ # Move collapsed trees as a unit, but only the item if uncollapsed
5713
+ should_move_children = should_move_with_children?
5670
5714
  if @split_view && @active_pane == :split
5671
- indent_split_left(false)
5715
+ indent_split_left(should_move_children)
5672
5716
  else
5673
- indent_left(false)
5717
+ indent_left(should_move_children)
5674
5718
  end
5675
5719
  when "RIGHT"
5676
- # Indent only the current item
5720
+ # Move collapsed trees as a unit, but only the item if uncollapsed
5721
+ should_move_children = should_move_with_children?
5677
5722
  if @split_view && @active_pane == :split
5678
- indent_split_right(false)
5723
+ indent_split_right(should_move_children)
5679
5724
  else
5680
- indent_right(false)
5725
+ indent_right(should_move_children)
5681
5726
  end
5682
5727
  when "PgUP" # Page Up
5683
5728
  page_up
data/hyperlist.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "hyperlist"
3
- spec.version = "1.5.0"
3
+ spec.version = "1.5.2"
4
4
  spec.authors = ["Geir Isene"]
5
5
  spec.email = ["g@isene.com"]
6
6
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hyperlist
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.0
4
+ version: 1.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: "."
10
10
  cert_chain: []
11
- date: 2025-09-03 00:00:00.000000000 Z
11
+ date: 2025-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses