hyperlist 1.5.2 → 1.7.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 +31 -1
- data/hyperlist +323 -158
- 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: 014e81876165c457903e5f20e8f848f2c59fce0d2672e700ff909873c03c260a
|
4
|
+
data.tar.gz: c60b73c642984f70ba2bfdcceb7ca6a9bc501c46071cb362d9e9343778cd8f40
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37ba0da349a30af46424cc374ece24e315c32a22ee48bf81f4893c43e20cdf18f42ec71c3b3fe123688f9b97597a29e8aa583884cceb361d44583645546aaf25
|
7
|
+
data.tar.gz: 5a8737705ad4d231e6ce1a8a1b8f440cf8b0f3c2ffa7586052dc64be5a55d8bc4491ed721b07adb192e6674f0560a9ac35a6a067ed27831b200066b786456081
|
data/CHANGELOG.md
CHANGED
@@ -2,11 +2,41 @@
|
|
2
2
|
|
3
3
|
All notable changes to the HyperList Ruby TUI will be documented in this file.
|
4
4
|
|
5
|
+
## [1.7.0] - 2025-09-18
|
6
|
+
|
7
|
+
### Added
|
8
|
+
- **Checkbox removal feature**
|
9
|
+
- **C-X key**: Remove checkboxes from items
|
10
|
+
- Removes all checkbox patterns: `[ ]`, `[X]`, `[x]`, `[O]`, `[-]`, `[_]`
|
11
|
+
- Also removes associated timestamps (YYYY-MM-DD HH.MM: format)
|
12
|
+
- Provides feedback messages for successful removal or no checkbox found
|
13
|
+
- Added to help documentation under SPECIAL FEATURES
|
14
|
+
- Complements existing v/V checkbox toggle functionality
|
15
|
+
|
16
|
+
### Enhanced
|
17
|
+
- **Cross-parent movement improvements**
|
18
|
+
- C-UP/C-DOWN now respect visibility principle (move only item if uncollapsed, item+children if collapsed)
|
19
|
+
- Added orphaned children reunification logic
|
20
|
+
- Items moved into collapsed areas automatically unfold destination
|
21
|
+
- Moved items are guaranteed to remain visible (safety net protection)
|
22
|
+
- Single-step movement: C-DOWN moves exactly one position down, not to end of descendants
|
23
|
+
|
24
|
+
## [1.6.0] - 2025-09-18
|
25
|
+
|
26
|
+
### BREAKING CHANGES
|
27
|
+
- **Complete reassignment of arrow key functionality**
|
28
|
+
- **RIGHT key**: Now uncollapses items (if collapsed)
|
29
|
+
- **LEFT key**: Now collapses items (if they have children)
|
30
|
+
- **TAB key**: Takes over old RIGHT key functionality (indent right)
|
31
|
+
- **S-TAB key**: Takes over old LEFT key functionality (indent left)
|
32
|
+
- This provides more intuitive navigation where arrow keys control visibility and TAB keys control indentation
|
33
|
+
- Updated help documentation to reflect new key mappings
|
34
|
+
|
5
35
|
## [1.5.2] - 2025-09-18
|
6
36
|
|
7
37
|
### Enhanced
|
8
38
|
- **Smart movement behavior for collapsed items**
|
9
|
-
- LEFT/RIGHT arrow keys
|
39
|
+
- LEFT/RIGHT arrow keys move collapsed trees as a unit when item is folded
|
10
40
|
- Uncollapsed items move individually (only the current item)
|
11
41
|
- Maintains "move what's visible" principle for intuitive navigation
|
12
42
|
- Works in both normal and split-view modes
|
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
|
@@ -1733,7 +1733,61 @@ class HyperListApp
|
|
1733
1733
|
end
|
1734
1734
|
end
|
1735
1735
|
end
|
1736
|
-
|
1736
|
+
|
1737
|
+
def collapse_item
|
1738
|
+
if @split_view && @active_pane == :split
|
1739
|
+
# Handle collapse in split pane
|
1740
|
+
visible_items = get_visible_split_items
|
1741
|
+
return if @split_current >= visible_items.length
|
1742
|
+
|
1743
|
+
item = visible_items[@split_current]
|
1744
|
+
real_idx = @split_items.index(item)
|
1745
|
+
|
1746
|
+
if real_idx && has_children_in_array?(real_idx, @split_items) && !item["fold"]
|
1747
|
+
item["fold"] = true
|
1748
|
+
end
|
1749
|
+
else
|
1750
|
+
# Handle collapse in main pane
|
1751
|
+
visible = get_visible_items
|
1752
|
+
return if @current >= visible.length
|
1753
|
+
|
1754
|
+
item = visible[@current]
|
1755
|
+
real_idx = get_real_index(item)
|
1756
|
+
|
1757
|
+
if real_idx && has_children?(real_idx, @items) && !@items[real_idx]["fold"]
|
1758
|
+
@items[real_idx]["fold"] = true
|
1759
|
+
record_last_action(:toggle_fold, nil)
|
1760
|
+
end
|
1761
|
+
end
|
1762
|
+
end
|
1763
|
+
|
1764
|
+
def uncollapse_item
|
1765
|
+
if @split_view && @active_pane == :split
|
1766
|
+
# Handle uncollapse in split pane
|
1767
|
+
visible_items = get_visible_split_items
|
1768
|
+
return if @split_current >= visible_items.length
|
1769
|
+
|
1770
|
+
item = visible_items[@split_current]
|
1771
|
+
real_idx = @split_items.index(item)
|
1772
|
+
|
1773
|
+
if real_idx && has_children_in_array?(real_idx, @split_items) && item["fold"]
|
1774
|
+
item["fold"] = false
|
1775
|
+
end
|
1776
|
+
else
|
1777
|
+
# Handle uncollapse in main pane
|
1778
|
+
visible = get_visible_items
|
1779
|
+
return if @current >= visible.length
|
1780
|
+
|
1781
|
+
item = visible[@current]
|
1782
|
+
real_idx = get_real_index(item)
|
1783
|
+
|
1784
|
+
if real_idx && has_children?(real_idx, @items) && @items[real_idx]["fold"]
|
1785
|
+
@items[real_idx]["fold"] = false
|
1786
|
+
record_last_action(:toggle_fold, nil)
|
1787
|
+
end
|
1788
|
+
end
|
1789
|
+
end
|
1790
|
+
|
1737
1791
|
def move_up
|
1738
1792
|
if @presentation_mode
|
1739
1793
|
# In presentation mode, we need to handle navigation differently
|
@@ -2578,26 +2632,132 @@ class HyperListApp
|
|
2578
2632
|
@message = "Pasted #{@clipboard.length} item(s)"
|
2579
2633
|
record_last_action(:paste, nil)
|
2580
2634
|
end
|
2581
|
-
|
2635
|
+
|
2636
|
+
def calculate_level_for_position(target_real_idx)
|
2637
|
+
# Calculate appropriate level for an item at target_real_idx position
|
2638
|
+
# based on surrounding items in the @items array
|
2639
|
+
|
2640
|
+
# If at the beginning, level 0
|
2641
|
+
return 0 if target_real_idx == 0
|
2642
|
+
|
2643
|
+
# Look at the previous item
|
2644
|
+
prev_item = @items[target_real_idx - 1]
|
2645
|
+
prev_level = prev_item["level"]
|
2646
|
+
|
2647
|
+
# Look at the next item if it exists
|
2648
|
+
if target_real_idx < @items.length
|
2649
|
+
next_item = @items[target_real_idx]
|
2650
|
+
next_level = next_item["level"]
|
2651
|
+
|
2652
|
+
# If next item is at same or lower level than previous,
|
2653
|
+
# we can be at the same level as previous (sibling)
|
2654
|
+
if next_level <= prev_level
|
2655
|
+
return prev_level
|
2656
|
+
else
|
2657
|
+
# Next item is deeper, so we could be parent (prev_level)
|
2658
|
+
# or child (prev_level + 1), choose to be sibling of previous
|
2659
|
+
return prev_level
|
2660
|
+
end
|
2661
|
+
else
|
2662
|
+
# At the end, can be sibling of previous item
|
2663
|
+
return prev_level
|
2664
|
+
end
|
2665
|
+
end
|
2666
|
+
|
2667
|
+
def find_orphaned_children(item_text, target_real_idx)
|
2668
|
+
# Look for children that might belong to the item being moved
|
2669
|
+
orphaned_children = []
|
2670
|
+
|
2671
|
+
# Search from target position onwards for potential children
|
2672
|
+
# Look for items at level 2 or higher that could be children
|
2673
|
+
(target_real_idx...@items.length).each do |i|
|
2674
|
+
item_level = @items[i]["level"]
|
2675
|
+
|
2676
|
+
# Stop if we hit an item at level 0 or 1 (new parent/sibling)
|
2677
|
+
break if item_level <= 1
|
2678
|
+
|
2679
|
+
# If we find items at level 2+, they could be orphaned children
|
2680
|
+
# (In the example: Subtask A1.1, A1.2 are level 2)
|
2681
|
+
if item_level >= 2
|
2682
|
+
orphaned_children << i
|
2683
|
+
else
|
2684
|
+
break # Hit a sibling, stop looking
|
2685
|
+
end
|
2686
|
+
end
|
2687
|
+
|
2688
|
+
orphaned_children
|
2689
|
+
end
|
2690
|
+
|
2691
|
+
def ensure_destination_visible(target_real_idx, item_level)
|
2692
|
+
# Unfold any collapsed areas that would hide an item at target_real_idx with item_level
|
2693
|
+
return if target_real_idx >= @items.length || target_real_idx < 0
|
2694
|
+
|
2695
|
+
# Walk backwards from the target position to find potential ancestor items
|
2696
|
+
(target_real_idx - 1).downto(0) do |i|
|
2697
|
+
item = @items[i]
|
2698
|
+
|
2699
|
+
# If this item is at a higher level (lower number) than our item will be, it's a potential ancestor
|
2700
|
+
if item["level"] < item_level
|
2701
|
+
# If this ancestor is folded, unfold it to make the destination visible
|
2702
|
+
if item["fold"] && has_children?(i, @items)
|
2703
|
+
item["fold"] = false
|
2704
|
+
end
|
2705
|
+
|
2706
|
+
# Update item_level to continue searching for higher ancestors
|
2707
|
+
item_level = item["level"]
|
2708
|
+
|
2709
|
+
# If we reach level 0, we're done
|
2710
|
+
break if item_level == 0
|
2711
|
+
end
|
2712
|
+
end
|
2713
|
+
end
|
2714
|
+
|
2715
|
+
def force_item_visible(target_real_idx)
|
2716
|
+
# Absolutely ensure the item at target_real_idx is visible - last resort safety net
|
2717
|
+
return if target_real_idx >= @items.length || target_real_idx < 0
|
2718
|
+
|
2719
|
+
target_item = @items[target_real_idx]
|
2720
|
+
target_level = target_item["level"]
|
2721
|
+
|
2722
|
+
# Walk backwards and unfold ALL ancestors, no matter what
|
2723
|
+
(target_real_idx - 1).downto(0) do |i|
|
2724
|
+
item = @items[i]
|
2725
|
+
|
2726
|
+
# If this item could be an ancestor (higher in hierarchy)
|
2727
|
+
if item["level"] < target_level
|
2728
|
+
# Force unfold it, regardless of whether it has children
|
2729
|
+
item["fold"] = false if item.has_key?("fold")
|
2730
|
+
|
2731
|
+
# Update target_level to continue searching
|
2732
|
+
target_level = item["level"]
|
2733
|
+
|
2734
|
+
# Continue until we reach level 0
|
2735
|
+
break if target_level == 0
|
2736
|
+
end
|
2737
|
+
end
|
2738
|
+
|
2739
|
+
# Force a cache clear to ensure visibility updates
|
2740
|
+
clear_cache if respond_to?(:clear_cache)
|
2741
|
+
end
|
2742
|
+
|
2582
2743
|
def move_item_up(with_children = false)
|
2583
2744
|
visible = get_visible_items
|
2584
2745
|
return if @current >= visible.length || @current == 0
|
2585
|
-
|
2746
|
+
|
2586
2747
|
save_undo_state # Save state before modification
|
2587
|
-
|
2588
|
-
|
2589
|
-
|
2590
|
-
|
2591
|
-
#
|
2592
|
-
|
2593
|
-
|
2594
|
-
|
2595
|
-
|
2596
|
-
|
2748
|
+
|
2749
|
+
current_item = visible[@current]
|
2750
|
+
current_real_idx = get_real_index(current_item)
|
2751
|
+
|
2752
|
+
# Get the previous visible item
|
2753
|
+
prev_visible_item = visible[@current - 1]
|
2754
|
+
prev_real_idx = get_real_index(prev_visible_item)
|
2755
|
+
|
2756
|
+
# Collect items to move (current item + children if requested)
|
2757
|
+
items_to_move = [current_item]
|
2597
2758
|
if with_children
|
2598
|
-
|
2599
|
-
|
2600
|
-
((real_idx + 1)...@items.length).each do |i|
|
2759
|
+
level = current_item["level"]
|
2760
|
+
((current_real_idx + 1)...@items.length).each do |i|
|
2601
2761
|
if @items[i]["level"] > level
|
2602
2762
|
items_to_move << @items[i]
|
2603
2763
|
else
|
@@ -2605,46 +2765,42 @@ class HyperListApp
|
|
2605
2765
|
end
|
2606
2766
|
end
|
2607
2767
|
end
|
2608
|
-
|
2609
|
-
#
|
2610
|
-
|
2611
|
-
|
2612
|
-
|
2613
|
-
#
|
2614
|
-
(
|
2615
|
-
|
2616
|
-
|
2617
|
-
|
2618
|
-
|
2619
|
-
|
2620
|
-
|
2621
|
-
|
2768
|
+
|
2769
|
+
# Calculate what level the moved item should have at the target position
|
2770
|
+
new_level = calculate_level_for_position(prev_real_idx)
|
2771
|
+
level_diff = new_level - items_to_move.first["level"]
|
2772
|
+
|
2773
|
+
# Unfold destination area BEFORE moving if needed
|
2774
|
+
ensure_destination_visible(prev_real_idx, new_level)
|
2775
|
+
|
2776
|
+
# Remove the items to move
|
2777
|
+
items_to_move.length.times { @items.delete_at(current_real_idx) }
|
2778
|
+
|
2779
|
+
# Adjust target position since we removed items after it
|
2780
|
+
adjusted_target = prev_real_idx
|
2781
|
+
if prev_real_idx > current_real_idx
|
2782
|
+
adjusted_target -= items_to_move.length
|
2622
2783
|
end
|
2623
|
-
|
2624
|
-
#
|
2625
|
-
|
2626
|
-
|
2627
|
-
|
2628
|
-
first_moved_item = items_to_move.first
|
2629
|
-
|
2630
|
-
# Remove items from their current position
|
2631
|
-
items_to_move.length.times { @items.delete_at(real_idx) }
|
2632
|
-
|
2633
|
-
# Insert before the target sibling
|
2634
|
-
items_to_move.reverse.each do |item_to_move|
|
2635
|
-
@items.insert(target_idx, item_to_move)
|
2784
|
+
|
2785
|
+
# Adjust levels of moved items
|
2786
|
+
items_to_move.each do |moved_item|
|
2787
|
+
moved_item["level"] += level_diff
|
2788
|
+
moved_item["level"] = [moved_item["level"], 0].max # Ensure level doesn't go negative
|
2636
2789
|
end
|
2637
|
-
|
2638
|
-
#
|
2639
|
-
|
2640
|
-
|
2790
|
+
|
2791
|
+
# Insert items at target position
|
2792
|
+
items_to_move.each_with_index do |item_to_move, idx|
|
2793
|
+
@items.insert(adjusted_target + idx, item_to_move)
|
2794
|
+
end
|
2795
|
+
|
2796
|
+
# FORCE the moved item to be visible (safety net)
|
2797
|
+
force_item_visible(adjusted_target)
|
2798
|
+
|
2799
|
+
# Update cursor to follow the moved item
|
2641
2800
|
new_visible = get_visible_items
|
2642
|
-
new_item_idx = new_visible.find_index { |v| v
|
2801
|
+
new_item_idx = new_visible.find_index { |v| get_real_index(v) == adjusted_target }
|
2643
2802
|
@current = new_item_idx if new_item_idx
|
2644
|
-
|
2645
|
-
# Renumber siblings at the moved item's level
|
2646
|
-
renumber_siblings(first_moved_item["level"])
|
2647
|
-
|
2803
|
+
|
2648
2804
|
@modified = true
|
2649
2805
|
@message = "Moved #{items_to_move.length} item(s) up"
|
2650
2806
|
record_last_action(:move_item_up, with_children)
|
@@ -2653,81 +2809,71 @@ class HyperListApp
|
|
2653
2809
|
def move_item_down(with_children = false)
|
2654
2810
|
visible = get_visible_items
|
2655
2811
|
return if @current >= visible.length - 1
|
2656
|
-
|
2812
|
+
|
2657
2813
|
save_undo_state # Save state before modification
|
2658
|
-
|
2659
|
-
|
2660
|
-
|
2661
|
-
|
2662
|
-
#
|
2663
|
-
|
2664
|
-
|
2665
|
-
|
2814
|
+
|
2815
|
+
current_item = visible[@current]
|
2816
|
+
current_real_idx = get_real_index(current_item)
|
2817
|
+
|
2818
|
+
# Get the next visible item
|
2819
|
+
next_visible_item = visible[@current + 1]
|
2820
|
+
next_real_idx = get_real_index(next_visible_item)
|
2821
|
+
|
2822
|
+
# Collect items to move (current item + children if requested)
|
2823
|
+
items_to_move = [current_item]
|
2666
2824
|
if with_children
|
2667
|
-
|
2668
|
-
|
2669
|
-
((real_idx + 1)...@items.length).each do |i|
|
2825
|
+
level = current_item["level"]
|
2826
|
+
((current_real_idx + 1)...@items.length).each do |i|
|
2670
2827
|
if @items[i]["level"] > level
|
2671
2828
|
items_to_move << @items[i]
|
2672
|
-
last_idx = i
|
2673
2829
|
else
|
2674
2830
|
break
|
2675
2831
|
end
|
2676
2832
|
end
|
2677
2833
|
end
|
2678
|
-
|
2679
|
-
#
|
2680
|
-
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
2684
|
-
|
2685
|
-
|
2686
|
-
|
2687
|
-
# Found next sibling, now find where it ends (including its children)
|
2688
|
-
next_sibling_end = i
|
2689
|
-
((i + 1)...@items.length).each do |j|
|
2690
|
-
if @items[j]["level"] > target_level
|
2691
|
-
next_sibling_end = j
|
2692
|
-
else
|
2693
|
-
break
|
2694
|
-
end
|
2695
|
-
end
|
2696
|
-
target_idx = next_sibling_end + 1
|
2697
|
-
break
|
2698
|
-
elsif @items[i]["level"] < target_level
|
2699
|
-
# Hit a parent level, can't move down
|
2700
|
-
return
|
2701
|
-
end
|
2834
|
+
|
2835
|
+
# Target is immediately after the next visible item (not after its children)
|
2836
|
+
target_real_idx = next_real_idx + 1
|
2837
|
+
|
2838
|
+
# Check if there are orphaned children at the target position
|
2839
|
+
# If so, position before them to reunite
|
2840
|
+
orphaned_children = find_orphaned_children(current_item["text"], target_real_idx)
|
2841
|
+
if !orphaned_children.empty?
|
2842
|
+
target_real_idx = orphaned_children.first
|
2702
2843
|
end
|
2703
|
-
|
2704
|
-
#
|
2705
|
-
|
2706
|
-
|
2707
|
-
|
2708
|
-
|
2709
|
-
|
2710
|
-
|
2711
|
-
|
2712
|
-
|
2713
|
-
|
2714
|
-
|
2715
|
-
|
2716
|
-
|
2844
|
+
|
2845
|
+
# Calculate what level the moved item should have at the target position
|
2846
|
+
new_level = calculate_level_for_position(target_real_idx)
|
2847
|
+
level_diff = new_level - items_to_move.first["level"]
|
2848
|
+
|
2849
|
+
# Unfold destination area BEFORE moving if needed
|
2850
|
+
ensure_destination_visible(target_real_idx, new_level)
|
2851
|
+
|
2852
|
+
# Remove the items to move
|
2853
|
+
items_to_move.length.times { @items.delete_at(current_real_idx) }
|
2854
|
+
|
2855
|
+
# Adjust target position since we removed items before it
|
2856
|
+
adjusted_target = target_real_idx - items_to_move.length
|
2857
|
+
|
2858
|
+
# Adjust levels of moved items
|
2859
|
+
items_to_move.each do |moved_item|
|
2860
|
+
moved_item["level"] += level_diff
|
2861
|
+
moved_item["level"] = [moved_item["level"], 0].max # Ensure level doesn't go negative
|
2862
|
+
end
|
2863
|
+
|
2864
|
+
# Insert items at target position
|
2717
2865
|
items_to_move.each_with_index do |item_to_move, idx|
|
2718
|
-
@items.insert(
|
2866
|
+
@items.insert(adjusted_target + idx, item_to_move)
|
2719
2867
|
end
|
2720
|
-
|
2721
|
-
#
|
2722
|
-
|
2723
|
-
|
2868
|
+
|
2869
|
+
# FORCE the moved item to be visible (safety net)
|
2870
|
+
force_item_visible(adjusted_target)
|
2871
|
+
|
2872
|
+
# Update cursor to follow the moved item
|
2724
2873
|
new_visible = get_visible_items
|
2725
|
-
new_item_idx = new_visible.find_index { |v| v
|
2874
|
+
new_item_idx = new_visible.find_index { |v| get_real_index(v) == adjusted_target }
|
2726
2875
|
@current = new_item_idx if new_item_idx
|
2727
|
-
|
2728
|
-
# Renumber siblings at the moved item's level
|
2729
|
-
renumber_siblings(first_moved_item["level"])
|
2730
|
-
|
2876
|
+
|
2731
2877
|
@modified = true
|
2732
2878
|
@message = "Moved #{items_to_move.length} item(s) down"
|
2733
2879
|
record_last_action(:move_item_down, with_children)
|
@@ -2876,7 +3022,35 @@ class HyperListApp
|
|
2876
3022
|
@modified = true
|
2877
3023
|
record_last_action(:toggle_checkbox_with_date, nil)
|
2878
3024
|
end
|
2879
|
-
|
3025
|
+
|
3026
|
+
def remove_checkbox
|
3027
|
+
visible = get_visible_items
|
3028
|
+
return if @current >= visible.length
|
3029
|
+
|
3030
|
+
save_undo_state # Save state before modification
|
3031
|
+
|
3032
|
+
item = visible[@current]
|
3033
|
+
real_idx = get_real_index(item)
|
3034
|
+
text = @items[real_idx]["text"]
|
3035
|
+
|
3036
|
+
# Remove checkbox patterns: [ ], [X], [x], [O], [-], [_]
|
3037
|
+
if text =~ /^\[[ X_Ox-]\]/
|
3038
|
+
# Remove the checkbox and any following space
|
3039
|
+
text = text.sub(/^\[[ X_Ox-]\]\s*/, '')
|
3040
|
+
|
3041
|
+
# Also remove any timestamp that might be associated with checkbox
|
3042
|
+
# Format: YYYY-MM-DD HH.MM: or similar
|
3043
|
+
text = text.sub(/^\d{4}-\d{2}-\d{2} \d{2}\.\d{2}:\s*/, '')
|
3044
|
+
|
3045
|
+
@items[real_idx]["text"] = text
|
3046
|
+
@modified = true
|
3047
|
+
@message = "Removed checkbox"
|
3048
|
+
record_last_action(:remove_checkbox, nil)
|
3049
|
+
else
|
3050
|
+
@message = "No checkbox to remove"
|
3051
|
+
end
|
3052
|
+
end
|
3053
|
+
|
2880
3054
|
def search_forward
|
2881
3055
|
@mode = :search
|
2882
3056
|
@search = @footer.ask("Search: ", @search || "")
|
@@ -3013,12 +3187,13 @@ class HyperListApp
|
|
3013
3187
|
help_lines << help_line("#{"u".fg("10")}", "Undo", "#{".".fg("10")}", "Repeat last action")
|
3014
3188
|
help_lines << help_line("#{"r".fg("10")}" + ", ".fg("10") + "#{"C-R".fg("10")}", "Redo")
|
3015
3189
|
help_lines << help_line("#{"S-UP".fg("10")}", "Move item up", "#{"S-DOWN".fg("10")}", "Move item down")
|
3016
|
-
help_lines << help_line("#{"C-UP".fg("10")}", "Move
|
3190
|
+
help_lines << help_line("#{"C-UP".fg("10")}", "Move up in visible list", "#{"C-DOWN".fg("10")}", "Move down in visible list")
|
3191
|
+
help_lines << help_line("#{"→".fg("10")}", "Uncollapse item", "#{"←".fg("10")}", "Collapse item")
|
3017
3192
|
help_lines << help_line("#{"Tab".fg("10")}", "Indent item+kids", "#{"S-Tab".fg("10")}", "Unindent item+kids")
|
3018
|
-
help_lines << help_line("#{"→".fg("10")}", "Indent item only", "#{"←".fg("10")}", "Unindent item only")
|
3019
3193
|
help_lines << ""
|
3020
3194
|
help_lines << "#{"SPECIAL FEATURES".fg("14")}"
|
3021
3195
|
help_lines << help_line("#{"v".fg("10")}", "Toggle checkbox", "#{"V".fg("10")}", "Checkbox with date")
|
3196
|
+
help_lines << help_line("#{"C-X".fg("10")}", "Remove checkbox", "", "")
|
3022
3197
|
help_lines << help_line("#{"C-E".fg("10")}", "Encrypt/decrypt line", "#{"C-U".fg("10")}", "Toggle State/Trans underline")
|
3023
3198
|
help_lines << help_line("#{"P".fg("10")}", "Presentation mode", "#{"Tab/S-Tab".fg("10")}", "Next/prev sibling (in P)")
|
3024
3199
|
help_lines << help_line("#{"Ma".fg("10")}", "Record macro 'a'", "#{"@a".fg("10")}", "Play macro 'a'")
|
@@ -4894,34 +5069,30 @@ class HyperListApp
|
|
4894
5069
|
toggle_checkbox
|
4895
5070
|
when "V"
|
4896
5071
|
toggle_checkbox_with_date
|
5072
|
+
when "C-X" # Ctrl-Shift-X for remove checkbox
|
5073
|
+
remove_checkbox
|
4897
5074
|
when "TAB"
|
4898
|
-
|
4899
|
-
indent_split_right(true) # with children
|
4900
|
-
else
|
4901
|
-
indent_right(true) # with children
|
4902
|
-
end
|
4903
|
-
when "S-TAB"
|
4904
|
-
if @split_view && @active_pane == :split
|
4905
|
-
indent_split_left(true) # with children
|
4906
|
-
else
|
4907
|
-
indent_left(true) # with children
|
4908
|
-
end
|
4909
|
-
when "RIGHT"
|
4910
|
-
# Move collapsed trees as a unit, but only the item if uncollapsed
|
5075
|
+
# Indent with move conditions (moved from RIGHT key)
|
4911
5076
|
should_move_children = should_move_with_children?
|
4912
5077
|
if @split_view && @active_pane == :split
|
4913
5078
|
indent_split_right(should_move_children)
|
4914
5079
|
else
|
4915
5080
|
indent_right(should_move_children)
|
4916
5081
|
end
|
4917
|
-
when "
|
4918
|
-
#
|
5082
|
+
when "S-TAB"
|
5083
|
+
# Unindent with move conditions (moved from LEFT key)
|
4919
5084
|
should_move_children = should_move_with_children?
|
4920
5085
|
if @split_view && @active_pane == :split
|
4921
5086
|
indent_split_left(should_move_children)
|
4922
5087
|
else
|
4923
5088
|
indent_left(should_move_children)
|
4924
5089
|
end
|
5090
|
+
when "RIGHT"
|
5091
|
+
# Uncollapse item if it is collapsed
|
5092
|
+
uncollapse_item
|
5093
|
+
when "LEFT"
|
5094
|
+
# Collapse item if it has children
|
5095
|
+
collapse_item
|
4925
5096
|
when " "
|
4926
5097
|
toggle_fold
|
4927
5098
|
when "u"
|
@@ -5709,21 +5880,11 @@ class HyperListApp
|
|
5709
5880
|
when "l"
|
5710
5881
|
go_to_first_child
|
5711
5882
|
when "LEFT"
|
5712
|
-
#
|
5713
|
-
|
5714
|
-
if @split_view && @active_pane == :split
|
5715
|
-
indent_split_left(should_move_children)
|
5716
|
-
else
|
5717
|
-
indent_left(should_move_children)
|
5718
|
-
end
|
5883
|
+
# Collapse item if it has children
|
5884
|
+
collapse_item
|
5719
5885
|
when "RIGHT"
|
5720
|
-
#
|
5721
|
-
|
5722
|
-
if @split_view && @active_pane == :split
|
5723
|
-
indent_split_right(should_move_children)
|
5724
|
-
else
|
5725
|
-
indent_right(should_move_children)
|
5726
|
-
end
|
5886
|
+
# Uncollapse item if it is collapsed
|
5887
|
+
uncollapse_item
|
5727
5888
|
when "PgUP" # Page Up
|
5728
5889
|
page_up
|
5729
5890
|
when "PgDOWN" # Page Down
|
@@ -5857,22 +6018,23 @@ class HyperListApp
|
|
5857
6018
|
move_item_up(false)
|
5858
6019
|
when "S-DOWN" # Move item down
|
5859
6020
|
move_item_down(false)
|
5860
|
-
when "C-UP" # Move
|
5861
|
-
move_item_up(
|
5862
|
-
when "C-DOWN" # Move
|
5863
|
-
move_item_down(
|
5864
|
-
when "C-K" # Alternative: Move
|
5865
|
-
move_item_up(
|
6021
|
+
when "C-UP" # Move up in visible list (with children only if collapsed)
|
6022
|
+
move_item_up(should_move_with_children?)
|
6023
|
+
when "C-DOWN" # Move down in visible list (with children only if collapsed)
|
6024
|
+
move_item_down(should_move_with_children?)
|
6025
|
+
when "C-K" # Alternative: Move up in visible list (for terminals that intercept C-UP)
|
6026
|
+
move_item_up(should_move_with_children?)
|
5866
6027
|
when "TAB"
|
5867
6028
|
if @presentation_mode
|
5868
6029
|
# In presentation mode, Tab goes to next sibling
|
5869
6030
|
jump_to_next_sibling
|
5870
6031
|
else
|
5871
|
-
# Normal mode: Indent with
|
6032
|
+
# Normal mode: Indent with move conditions (moved from RIGHT key)
|
6033
|
+
should_move_children = should_move_with_children?
|
5872
6034
|
if @split_view && @active_pane == :split
|
5873
|
-
indent_split_right(
|
6035
|
+
indent_split_right(should_move_children)
|
5874
6036
|
else
|
5875
|
-
indent_right(
|
6037
|
+
indent_right(should_move_children)
|
5876
6038
|
end
|
5877
6039
|
end
|
5878
6040
|
when "S-TAB" # Shift-Tab
|
@@ -5880,11 +6042,12 @@ class HyperListApp
|
|
5880
6042
|
# In presentation mode, Shift-Tab goes to previous sibling
|
5881
6043
|
jump_to_prev_sibling
|
5882
6044
|
else
|
5883
|
-
# Normal mode: Unindent with
|
6045
|
+
# Normal mode: Unindent with move conditions (moved from LEFT key)
|
6046
|
+
should_move_children = should_move_with_children?
|
5884
6047
|
if @split_view && @active_pane == :split
|
5885
|
-
indent_split_left(
|
6048
|
+
indent_split_left(should_move_children)
|
5886
6049
|
else
|
5887
|
-
indent_left(
|
6050
|
+
indent_left(should_move_children)
|
5888
6051
|
end
|
5889
6052
|
end
|
5890
6053
|
when "u"
|
@@ -5895,6 +6058,8 @@ class HyperListApp
|
|
5895
6058
|
toggle_checkbox
|
5896
6059
|
when "V"
|
5897
6060
|
toggle_checkbox_with_date
|
6061
|
+
when "C-X" # Ctrl-Shift-X for remove checkbox
|
6062
|
+
remove_checkbox
|
5898
6063
|
when "."
|
5899
6064
|
repeat_last_action
|
5900
6065
|
when "/"
|
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.
|
4
|
+
version: 1.7.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
|