hyperlist 1.6.0 → 1.8.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 +4 -4
- data/CHANGELOG.md +29 -0
- data/hyperlist +260 -121
- data/hyperlist.gemspec +1 -1
- data/test_visibility.hl +11 -0
- metadata +3 -3
- data/test_resize.hl +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 54c0ace316afeb7e7d109bb319f279c76dd849e098683ded98ccf5c33c68ffbc
|
4
|
+
data.tar.gz: 06ceebfd8bb666689dbef44939074d4a1aaa7a78fe4c12858a977550b2d38391
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d8e284b9201f5d2f7598c55473936569a99071312a2ab0735cdeae70737b8c47998a5d02cf29dbb2b38ffb1c65d851e49cf19809d514150a5b4764d9e1c4c8ba
|
7
|
+
data.tar.gz: 5b3e22980a16e467d0308ffd7e5912db3738dda7bde1c74e1a0fe871b8ca2d8e3cb21f782e8edb6a1af4fcf75d917731807fc00c7d4e9f83af0d57316892d19c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,35 @@
|
|
2
2
|
|
3
3
|
All notable changes to the HyperList Ruby TUI will be documented in this file.
|
4
4
|
|
5
|
+
## [1.8.0] - 2025-09-22
|
6
|
+
|
7
|
+
### Enhanced
|
8
|
+
- **Guaranteed item visibility after all movement operations**
|
9
|
+
- Current item now ALWAYS remains visible after TAB/S-TAB indentation operations
|
10
|
+
- Added visibility enforcement to `indent_right()` and `indent_left()` functions
|
11
|
+
- Added visibility enforcement to split-view indentation operations
|
12
|
+
- Prevents items from becoming invisible when moved under collapsed parents
|
13
|
+
- Complements existing visibility protection for C-UP/C-DOWN movements
|
14
|
+
|
15
|
+
## [1.7.0] - 2025-09-18
|
16
|
+
|
17
|
+
### Added
|
18
|
+
- **Checkbox removal feature**
|
19
|
+
- **C-X key**: Remove checkboxes from items
|
20
|
+
- Removes all checkbox patterns: `[ ]`, `[X]`, `[x]`, `[O]`, `[-]`, `[_]`
|
21
|
+
- Also removes associated timestamps (YYYY-MM-DD HH.MM: format)
|
22
|
+
- Provides feedback messages for successful removal or no checkbox found
|
23
|
+
- Added to help documentation under SPECIAL FEATURES
|
24
|
+
- Complements existing v/V checkbox toggle functionality
|
25
|
+
|
26
|
+
### Enhanced
|
27
|
+
- **Cross-parent movement improvements**
|
28
|
+
- C-UP/C-DOWN now respect visibility principle (move only item if uncollapsed, item+children if collapsed)
|
29
|
+
- Added orphaned children reunification logic
|
30
|
+
- Items moved into collapsed areas automatically unfold destination
|
31
|
+
- Moved items are guaranteed to remain visible (safety net protection)
|
32
|
+
- Single-step movement: C-DOWN moves exactly one position down, not to end of descendants
|
33
|
+
|
5
34
|
## [1.6.0] - 2025-09-18
|
6
35
|
|
7
36
|
### BREAKING CHANGES
|
data/hyperlist
CHANGED
@@ -72,7 +72,7 @@ class HyperListApp
|
|
72
72
|
include Rcurses::Input
|
73
73
|
include Rcurses::Cursor
|
74
74
|
|
75
|
-
VERSION = "1.
|
75
|
+
VERSION = "1.7.0"
|
76
76
|
|
77
77
|
def initialize(filename = nil)
|
78
78
|
@filename = filename ? File.expand_path(filename) : nil
|
@@ -1674,6 +1674,10 @@ class HyperListApp
|
|
1674
1674
|
end
|
1675
1675
|
end
|
1676
1676
|
|
1677
|
+
# Ensure the moved item remains visible (find its index in main items array)
|
1678
|
+
main_real_idx = @items.index(item)
|
1679
|
+
force_item_visible(main_real_idx) if main_real_idx
|
1680
|
+
|
1677
1681
|
@modified = true
|
1678
1682
|
@message = "Indented in split pane"
|
1679
1683
|
end
|
@@ -1702,6 +1706,10 @@ class HyperListApp
|
|
1702
1706
|
end
|
1703
1707
|
end
|
1704
1708
|
|
1709
|
+
# Ensure the moved item remains visible (find its index in main items array)
|
1710
|
+
main_real_idx = @items.index(item)
|
1711
|
+
force_item_visible(main_real_idx) if main_real_idx
|
1712
|
+
|
1705
1713
|
@modified = true
|
1706
1714
|
@message = "Unindented in split pane"
|
1707
1715
|
end
|
@@ -2632,26 +2640,132 @@ class HyperListApp
|
|
2632
2640
|
@message = "Pasted #{@clipboard.length} item(s)"
|
2633
2641
|
record_last_action(:paste, nil)
|
2634
2642
|
end
|
2635
|
-
|
2643
|
+
|
2644
|
+
def calculate_level_for_position(target_real_idx)
|
2645
|
+
# Calculate appropriate level for an item at target_real_idx position
|
2646
|
+
# based on surrounding items in the @items array
|
2647
|
+
|
2648
|
+
# If at the beginning, level 0
|
2649
|
+
return 0 if target_real_idx == 0
|
2650
|
+
|
2651
|
+
# Look at the previous item
|
2652
|
+
prev_item = @items[target_real_idx - 1]
|
2653
|
+
prev_level = prev_item["level"]
|
2654
|
+
|
2655
|
+
# Look at the next item if it exists
|
2656
|
+
if target_real_idx < @items.length
|
2657
|
+
next_item = @items[target_real_idx]
|
2658
|
+
next_level = next_item["level"]
|
2659
|
+
|
2660
|
+
# If next item is at same or lower level than previous,
|
2661
|
+
# we can be at the same level as previous (sibling)
|
2662
|
+
if next_level <= prev_level
|
2663
|
+
return prev_level
|
2664
|
+
else
|
2665
|
+
# Next item is deeper, so we could be parent (prev_level)
|
2666
|
+
# or child (prev_level + 1), choose to be sibling of previous
|
2667
|
+
return prev_level
|
2668
|
+
end
|
2669
|
+
else
|
2670
|
+
# At the end, can be sibling of previous item
|
2671
|
+
return prev_level
|
2672
|
+
end
|
2673
|
+
end
|
2674
|
+
|
2675
|
+
def find_orphaned_children(item_text, target_real_idx)
|
2676
|
+
# Look for children that might belong to the item being moved
|
2677
|
+
orphaned_children = []
|
2678
|
+
|
2679
|
+
# Search from target position onwards for potential children
|
2680
|
+
# Look for items at level 2 or higher that could be children
|
2681
|
+
(target_real_idx...@items.length).each do |i|
|
2682
|
+
item_level = @items[i]["level"]
|
2683
|
+
|
2684
|
+
# Stop if we hit an item at level 0 or 1 (new parent/sibling)
|
2685
|
+
break if item_level <= 1
|
2686
|
+
|
2687
|
+
# If we find items at level 2+, they could be orphaned children
|
2688
|
+
# (In the example: Subtask A1.1, A1.2 are level 2)
|
2689
|
+
if item_level >= 2
|
2690
|
+
orphaned_children << i
|
2691
|
+
else
|
2692
|
+
break # Hit a sibling, stop looking
|
2693
|
+
end
|
2694
|
+
end
|
2695
|
+
|
2696
|
+
orphaned_children
|
2697
|
+
end
|
2698
|
+
|
2699
|
+
def ensure_destination_visible(target_real_idx, item_level)
|
2700
|
+
# Unfold any collapsed areas that would hide an item at target_real_idx with item_level
|
2701
|
+
return if target_real_idx >= @items.length || target_real_idx < 0
|
2702
|
+
|
2703
|
+
# Walk backwards from the target position to find potential ancestor items
|
2704
|
+
(target_real_idx - 1).downto(0) do |i|
|
2705
|
+
item = @items[i]
|
2706
|
+
|
2707
|
+
# If this item is at a higher level (lower number) than our item will be, it's a potential ancestor
|
2708
|
+
if item["level"] < item_level
|
2709
|
+
# If this ancestor is folded, unfold it to make the destination visible
|
2710
|
+
if item["fold"] && has_children?(i, @items)
|
2711
|
+
item["fold"] = false
|
2712
|
+
end
|
2713
|
+
|
2714
|
+
# Update item_level to continue searching for higher ancestors
|
2715
|
+
item_level = item["level"]
|
2716
|
+
|
2717
|
+
# If we reach level 0, we're done
|
2718
|
+
break if item_level == 0
|
2719
|
+
end
|
2720
|
+
end
|
2721
|
+
end
|
2722
|
+
|
2723
|
+
def force_item_visible(target_real_idx)
|
2724
|
+
# Absolutely ensure the item at target_real_idx is visible - last resort safety net
|
2725
|
+
return if target_real_idx >= @items.length || target_real_idx < 0
|
2726
|
+
|
2727
|
+
target_item = @items[target_real_idx]
|
2728
|
+
target_level = target_item["level"]
|
2729
|
+
|
2730
|
+
# Walk backwards and unfold ALL ancestors, no matter what
|
2731
|
+
(target_real_idx - 1).downto(0) do |i|
|
2732
|
+
item = @items[i]
|
2733
|
+
|
2734
|
+
# If this item could be an ancestor (higher in hierarchy)
|
2735
|
+
if item["level"] < target_level
|
2736
|
+
# Force unfold it, regardless of whether it has children
|
2737
|
+
item["fold"] = false if item.has_key?("fold")
|
2738
|
+
|
2739
|
+
# Update target_level to continue searching
|
2740
|
+
target_level = item["level"]
|
2741
|
+
|
2742
|
+
# Continue until we reach level 0
|
2743
|
+
break if target_level == 0
|
2744
|
+
end
|
2745
|
+
end
|
2746
|
+
|
2747
|
+
# Force a cache clear to ensure visibility updates
|
2748
|
+
clear_cache if respond_to?(:clear_cache)
|
2749
|
+
end
|
2750
|
+
|
2636
2751
|
def move_item_up(with_children = false)
|
2637
2752
|
visible = get_visible_items
|
2638
2753
|
return if @current >= visible.length || @current == 0
|
2639
|
-
|
2754
|
+
|
2640
2755
|
save_undo_state # Save state before modification
|
2641
|
-
|
2642
|
-
|
2643
|
-
|
2644
|
-
|
2645
|
-
#
|
2646
|
-
|
2647
|
-
|
2648
|
-
|
2649
|
-
|
2650
|
-
|
2756
|
+
|
2757
|
+
current_item = visible[@current]
|
2758
|
+
current_real_idx = get_real_index(current_item)
|
2759
|
+
|
2760
|
+
# Get the previous visible item
|
2761
|
+
prev_visible_item = visible[@current - 1]
|
2762
|
+
prev_real_idx = get_real_index(prev_visible_item)
|
2763
|
+
|
2764
|
+
# Collect items to move (current item + children if requested)
|
2765
|
+
items_to_move = [current_item]
|
2651
2766
|
if with_children
|
2652
|
-
|
2653
|
-
|
2654
|
-
((real_idx + 1)...@items.length).each do |i|
|
2767
|
+
level = current_item["level"]
|
2768
|
+
((current_real_idx + 1)...@items.length).each do |i|
|
2655
2769
|
if @items[i]["level"] > level
|
2656
2770
|
items_to_move << @items[i]
|
2657
2771
|
else
|
@@ -2659,46 +2773,42 @@ class HyperListApp
|
|
2659
2773
|
end
|
2660
2774
|
end
|
2661
2775
|
end
|
2662
|
-
|
2663
|
-
#
|
2664
|
-
|
2665
|
-
|
2666
|
-
|
2667
|
-
#
|
2668
|
-
(
|
2669
|
-
|
2670
|
-
|
2671
|
-
|
2672
|
-
|
2673
|
-
|
2674
|
-
|
2675
|
-
|
2776
|
+
|
2777
|
+
# Calculate what level the moved item should have at the target position
|
2778
|
+
new_level = calculate_level_for_position(prev_real_idx)
|
2779
|
+
level_diff = new_level - items_to_move.first["level"]
|
2780
|
+
|
2781
|
+
# Unfold destination area BEFORE moving if needed
|
2782
|
+
ensure_destination_visible(prev_real_idx, new_level)
|
2783
|
+
|
2784
|
+
# Remove the items to move
|
2785
|
+
items_to_move.length.times { @items.delete_at(current_real_idx) }
|
2786
|
+
|
2787
|
+
# Adjust target position since we removed items after it
|
2788
|
+
adjusted_target = prev_real_idx
|
2789
|
+
if prev_real_idx > current_real_idx
|
2790
|
+
adjusted_target -= items_to_move.length
|
2676
2791
|
end
|
2677
|
-
|
2678
|
-
#
|
2679
|
-
|
2680
|
-
|
2681
|
-
|
2682
|
-
first_moved_item = items_to_move.first
|
2683
|
-
|
2684
|
-
# Remove items from their current position
|
2685
|
-
items_to_move.length.times { @items.delete_at(real_idx) }
|
2686
|
-
|
2687
|
-
# Insert before the target sibling
|
2688
|
-
items_to_move.reverse.each do |item_to_move|
|
2689
|
-
@items.insert(target_idx, item_to_move)
|
2792
|
+
|
2793
|
+
# Adjust levels of moved items
|
2794
|
+
items_to_move.each do |moved_item|
|
2795
|
+
moved_item["level"] += level_diff
|
2796
|
+
moved_item["level"] = [moved_item["level"], 0].max # Ensure level doesn't go negative
|
2690
2797
|
end
|
2691
|
-
|
2692
|
-
#
|
2693
|
-
|
2694
|
-
|
2798
|
+
|
2799
|
+
# Insert items at target position
|
2800
|
+
items_to_move.each_with_index do |item_to_move, idx|
|
2801
|
+
@items.insert(adjusted_target + idx, item_to_move)
|
2802
|
+
end
|
2803
|
+
|
2804
|
+
# FORCE the moved item to be visible (safety net)
|
2805
|
+
force_item_visible(adjusted_target)
|
2806
|
+
|
2807
|
+
# Update cursor to follow the moved item
|
2695
2808
|
new_visible = get_visible_items
|
2696
|
-
new_item_idx = new_visible.find_index { |v| v
|
2809
|
+
new_item_idx = new_visible.find_index { |v| get_real_index(v) == adjusted_target }
|
2697
2810
|
@current = new_item_idx if new_item_idx
|
2698
|
-
|
2699
|
-
# Renumber siblings at the moved item's level
|
2700
|
-
renumber_siblings(first_moved_item["level"])
|
2701
|
-
|
2811
|
+
|
2702
2812
|
@modified = true
|
2703
2813
|
@message = "Moved #{items_to_move.length} item(s) up"
|
2704
2814
|
record_last_action(:move_item_up, with_children)
|
@@ -2707,81 +2817,71 @@ class HyperListApp
|
|
2707
2817
|
def move_item_down(with_children = false)
|
2708
2818
|
visible = get_visible_items
|
2709
2819
|
return if @current >= visible.length - 1
|
2710
|
-
|
2820
|
+
|
2711
2821
|
save_undo_state # Save state before modification
|
2712
|
-
|
2713
|
-
|
2714
|
-
|
2715
|
-
|
2716
|
-
#
|
2717
|
-
|
2718
|
-
|
2719
|
-
|
2822
|
+
|
2823
|
+
current_item = visible[@current]
|
2824
|
+
current_real_idx = get_real_index(current_item)
|
2825
|
+
|
2826
|
+
# Get the next visible item
|
2827
|
+
next_visible_item = visible[@current + 1]
|
2828
|
+
next_real_idx = get_real_index(next_visible_item)
|
2829
|
+
|
2830
|
+
# Collect items to move (current item + children if requested)
|
2831
|
+
items_to_move = [current_item]
|
2720
2832
|
if with_children
|
2721
|
-
|
2722
|
-
|
2723
|
-
((real_idx + 1)...@items.length).each do |i|
|
2833
|
+
level = current_item["level"]
|
2834
|
+
((current_real_idx + 1)...@items.length).each do |i|
|
2724
2835
|
if @items[i]["level"] > level
|
2725
2836
|
items_to_move << @items[i]
|
2726
|
-
last_idx = i
|
2727
2837
|
else
|
2728
2838
|
break
|
2729
2839
|
end
|
2730
2840
|
end
|
2731
2841
|
end
|
2732
|
-
|
2733
|
-
#
|
2734
|
-
|
2735
|
-
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
# Found next sibling, now find where it ends (including its children)
|
2742
|
-
next_sibling_end = i
|
2743
|
-
((i + 1)...@items.length).each do |j|
|
2744
|
-
if @items[j]["level"] > target_level
|
2745
|
-
next_sibling_end = j
|
2746
|
-
else
|
2747
|
-
break
|
2748
|
-
end
|
2749
|
-
end
|
2750
|
-
target_idx = next_sibling_end + 1
|
2751
|
-
break
|
2752
|
-
elsif @items[i]["level"] < target_level
|
2753
|
-
# Hit a parent level, can't move down
|
2754
|
-
return
|
2755
|
-
end
|
2842
|
+
|
2843
|
+
# Target is immediately after the next visible item (not after its children)
|
2844
|
+
target_real_idx = next_real_idx + 1
|
2845
|
+
|
2846
|
+
# Check if there are orphaned children at the target position
|
2847
|
+
# If so, position before them to reunite
|
2848
|
+
orphaned_children = find_orphaned_children(current_item["text"], target_real_idx)
|
2849
|
+
if !orphaned_children.empty?
|
2850
|
+
target_real_idx = orphaned_children.first
|
2756
2851
|
end
|
2757
|
-
|
2758
|
-
#
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
2765
|
-
|
2766
|
-
|
2767
|
-
|
2768
|
-
|
2769
|
-
|
2770
|
-
|
2852
|
+
|
2853
|
+
# Calculate what level the moved item should have at the target position
|
2854
|
+
new_level = calculate_level_for_position(target_real_idx)
|
2855
|
+
level_diff = new_level - items_to_move.first["level"]
|
2856
|
+
|
2857
|
+
# Unfold destination area BEFORE moving if needed
|
2858
|
+
ensure_destination_visible(target_real_idx, new_level)
|
2859
|
+
|
2860
|
+
# Remove the items to move
|
2861
|
+
items_to_move.length.times { @items.delete_at(current_real_idx) }
|
2862
|
+
|
2863
|
+
# Adjust target position since we removed items before it
|
2864
|
+
adjusted_target = target_real_idx - items_to_move.length
|
2865
|
+
|
2866
|
+
# Adjust levels of moved items
|
2867
|
+
items_to_move.each do |moved_item|
|
2868
|
+
moved_item["level"] += level_diff
|
2869
|
+
moved_item["level"] = [moved_item["level"], 0].max # Ensure level doesn't go negative
|
2870
|
+
end
|
2871
|
+
|
2872
|
+
# Insert items at target position
|
2771
2873
|
items_to_move.each_with_index do |item_to_move, idx|
|
2772
|
-
@items.insert(
|
2874
|
+
@items.insert(adjusted_target + idx, item_to_move)
|
2773
2875
|
end
|
2774
|
-
|
2775
|
-
#
|
2776
|
-
|
2777
|
-
|
2876
|
+
|
2877
|
+
# FORCE the moved item to be visible (safety net)
|
2878
|
+
force_item_visible(adjusted_target)
|
2879
|
+
|
2880
|
+
# Update cursor to follow the moved item
|
2778
2881
|
new_visible = get_visible_items
|
2779
|
-
new_item_idx = new_visible.find_index { |v| v
|
2882
|
+
new_item_idx = new_visible.find_index { |v| get_real_index(v) == adjusted_target }
|
2780
2883
|
@current = new_item_idx if new_item_idx
|
2781
|
-
|
2782
|
-
# Renumber siblings at the moved item's level
|
2783
|
-
renumber_siblings(first_moved_item["level"])
|
2784
|
-
|
2884
|
+
|
2785
2885
|
@modified = true
|
2786
2886
|
@message = "Moved #{items_to_move.length} item(s) down"
|
2787
2887
|
record_last_action(:move_item_down, with_children)
|
@@ -2826,6 +2926,9 @@ class HyperListApp
|
|
2826
2926
|
end
|
2827
2927
|
end
|
2828
2928
|
|
2929
|
+
# Ensure the moved item remains visible
|
2930
|
+
force_item_visible(real_idx)
|
2931
|
+
|
2829
2932
|
@modified = true
|
2830
2933
|
record_last_action(:indent_right, with_children)
|
2831
2934
|
end
|
@@ -2854,6 +2957,9 @@ class HyperListApp
|
|
2854
2957
|
end
|
2855
2958
|
end
|
2856
2959
|
|
2960
|
+
# Ensure the moved item remains visible
|
2961
|
+
force_item_visible(real_idx)
|
2962
|
+
|
2857
2963
|
@modified = true
|
2858
2964
|
record_last_action(:indent_left, with_children)
|
2859
2965
|
end
|
@@ -2930,7 +3036,35 @@ class HyperListApp
|
|
2930
3036
|
@modified = true
|
2931
3037
|
record_last_action(:toggle_checkbox_with_date, nil)
|
2932
3038
|
end
|
2933
|
-
|
3039
|
+
|
3040
|
+
def remove_checkbox
|
3041
|
+
visible = get_visible_items
|
3042
|
+
return if @current >= visible.length
|
3043
|
+
|
3044
|
+
save_undo_state # Save state before modification
|
3045
|
+
|
3046
|
+
item = visible[@current]
|
3047
|
+
real_idx = get_real_index(item)
|
3048
|
+
text = @items[real_idx]["text"]
|
3049
|
+
|
3050
|
+
# Remove checkbox patterns: [ ], [X], [x], [O], [-], [_]
|
3051
|
+
if text =~ /^\[[ X_Ox-]\]/
|
3052
|
+
# Remove the checkbox and any following space
|
3053
|
+
text = text.sub(/^\[[ X_Ox-]\]\s*/, '')
|
3054
|
+
|
3055
|
+
# Also remove any timestamp that might be associated with checkbox
|
3056
|
+
# Format: YYYY-MM-DD HH.MM: or similar
|
3057
|
+
text = text.sub(/^\d{4}-\d{2}-\d{2} \d{2}\.\d{2}:\s*/, '')
|
3058
|
+
|
3059
|
+
@items[real_idx]["text"] = text
|
3060
|
+
@modified = true
|
3061
|
+
@message = "Removed checkbox"
|
3062
|
+
record_last_action(:remove_checkbox, nil)
|
3063
|
+
else
|
3064
|
+
@message = "No checkbox to remove"
|
3065
|
+
end
|
3066
|
+
end
|
3067
|
+
|
2934
3068
|
def search_forward
|
2935
3069
|
@mode = :search
|
2936
3070
|
@search = @footer.ask("Search: ", @search || "")
|
@@ -3067,12 +3201,13 @@ class HyperListApp
|
|
3067
3201
|
help_lines << help_line("#{"u".fg("10")}", "Undo", "#{".".fg("10")}", "Repeat last action")
|
3068
3202
|
help_lines << help_line("#{"r".fg("10")}" + ", ".fg("10") + "#{"C-R".fg("10")}", "Redo")
|
3069
3203
|
help_lines << help_line("#{"S-UP".fg("10")}", "Move item up", "#{"S-DOWN".fg("10")}", "Move item down")
|
3070
|
-
help_lines << help_line("#{"C-UP".fg("10")}", "Move
|
3204
|
+
help_lines << help_line("#{"C-UP".fg("10")}", "Move up in visible list", "#{"C-DOWN".fg("10")}", "Move down in visible list")
|
3071
3205
|
help_lines << help_line("#{"→".fg("10")}", "Uncollapse item", "#{"←".fg("10")}", "Collapse item")
|
3072
3206
|
help_lines << help_line("#{"Tab".fg("10")}", "Indent item+kids", "#{"S-Tab".fg("10")}", "Unindent item+kids")
|
3073
3207
|
help_lines << ""
|
3074
3208
|
help_lines << "#{"SPECIAL FEATURES".fg("14")}"
|
3075
3209
|
help_lines << help_line("#{"v".fg("10")}", "Toggle checkbox", "#{"V".fg("10")}", "Checkbox with date")
|
3210
|
+
help_lines << help_line("#{"C-X".fg("10")}", "Remove checkbox", "", "")
|
3076
3211
|
help_lines << help_line("#{"C-E".fg("10")}", "Encrypt/decrypt line", "#{"C-U".fg("10")}", "Toggle State/Trans underline")
|
3077
3212
|
help_lines << help_line("#{"P".fg("10")}", "Presentation mode", "#{"Tab/S-Tab".fg("10")}", "Next/prev sibling (in P)")
|
3078
3213
|
help_lines << help_line("#{"Ma".fg("10")}", "Record macro 'a'", "#{"@a".fg("10")}", "Play macro 'a'")
|
@@ -4948,6 +5083,8 @@ class HyperListApp
|
|
4948
5083
|
toggle_checkbox
|
4949
5084
|
when "V"
|
4950
5085
|
toggle_checkbox_with_date
|
5086
|
+
when "C-X" # Ctrl-Shift-X for remove checkbox
|
5087
|
+
remove_checkbox
|
4951
5088
|
when "TAB"
|
4952
5089
|
# Indent with move conditions (moved from RIGHT key)
|
4953
5090
|
should_move_children = should_move_with_children?
|
@@ -5895,12 +6032,12 @@ class HyperListApp
|
|
5895
6032
|
move_item_up(false)
|
5896
6033
|
when "S-DOWN" # Move item down
|
5897
6034
|
move_item_down(false)
|
5898
|
-
when "C-UP" # Move
|
5899
|
-
move_item_up(
|
5900
|
-
when "C-DOWN" # Move
|
5901
|
-
move_item_down(
|
5902
|
-
when "C-K" # Alternative: Move
|
5903
|
-
move_item_up(
|
6035
|
+
when "C-UP" # Move up in visible list (with children only if collapsed)
|
6036
|
+
move_item_up(should_move_with_children?)
|
6037
|
+
when "C-DOWN" # Move down in visible list (with children only if collapsed)
|
6038
|
+
move_item_down(should_move_with_children?)
|
6039
|
+
when "C-K" # Alternative: Move up in visible list (for terminals that intercept C-UP)
|
6040
|
+
move_item_up(should_move_with_children?)
|
5904
6041
|
when "TAB"
|
5905
6042
|
if @presentation_mode
|
5906
6043
|
# In presentation mode, Tab goes to next sibling
|
@@ -5935,6 +6072,8 @@ class HyperListApp
|
|
5935
6072
|
toggle_checkbox
|
5936
6073
|
when "V"
|
5937
6074
|
toggle_checkbox_with_date
|
6075
|
+
when "C-X" # Ctrl-Shift-X for remove checkbox
|
6076
|
+
remove_checkbox
|
5938
6077
|
when "."
|
5939
6078
|
repeat_last_action
|
5940
6079
|
when "/"
|
data/hyperlist.gemspec
CHANGED
data/test_visibility.hl
ADDED
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.
|
4
|
+
version: 1.8.0
|
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-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcurses
|
@@ -79,7 +79,7 @@ files:
|
|
79
79
|
- img/screenshot_help.png
|
80
80
|
- img/screenshot_sample.png
|
81
81
|
- sample.hl
|
82
|
-
-
|
82
|
+
- test_visibility.hl
|
83
83
|
homepage: https://github.com/isene/HyperList
|
84
84
|
licenses:
|
85
85
|
- Unlicense
|
data/test_resize.hl
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
Test HyperList for Terminal Resize
|
2
|
-
This is a test file
|
3
|
-
To verify terminal resize works
|
4
|
-
When you resize the terminal
|
5
|
-
The display should update correctly
|
6
|
-
Items to check:
|
7
|
-
Main content area adjusts
|
8
|
-
Footer stays at bottom
|
9
|
-
Split view (if active) rebalances
|
10
|
-
No visual corruption
|
11
|
-
Test scenarios:
|
12
|
-
Make terminal wider
|
13
|
-
Make terminal narrower
|
14
|
-
Make terminal taller
|
15
|
-
Make terminal shorter
|
16
|
-
Quick multiple resizes
|