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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/hyperlist +86 -41
- data/hyperlist.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ec123fe286a3c475f76868ed8d0cdd1a512f7323c6da8d9446d2ea7e3913ce61
|
4
|
+
data.tar.gz: 637a405887c8725cd229ba0dbd8bfa0d0f75cffa787f336a59477a32a70b1669
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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.
|
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
|
-
|
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(
|
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(
|
4913
|
+
indent_split_right(should_move_children)
|
4873
4914
|
else
|
4874
|
-
indent_right(
|
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(
|
4921
|
+
indent_split_left(should_move_children)
|
4879
4922
|
else
|
4880
|
-
indent_left(
|
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
|
-
#
|
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(
|
5715
|
+
indent_split_left(should_move_children)
|
5672
5716
|
else
|
5673
|
-
indent_left(
|
5717
|
+
indent_left(should_move_children)
|
5674
5718
|
end
|
5675
5719
|
when "RIGHT"
|
5676
|
-
#
|
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(
|
5723
|
+
indent_split_right(should_move_children)
|
5679
5724
|
else
|
5680
|
-
indent_right(
|
5725
|
+
indent_right(should_move_children)
|
5681
5726
|
end
|
5682
5727
|
when "PgUP" # Page Up
|
5683
5728
|
page_up
|
data/hyperlist.gemspec
CHANGED
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.
|
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-
|
11
|
+
date: 2025-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcurses
|