hyperlist 1.9.2 → 1.9.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bb81e544d2363e852a1e29bb1eef3d2fd891e766b2ade2a3e5c56fdc3073302e
4
- data.tar.gz: 14f24e55bfb6f3241ef9120cadcc680971617bfa5378893c3523e4eb1cb1105b
3
+ metadata.gz: 95521b846e9a01532da324a8b30dc47fda580098da9aab02dd611e6c70db018d
4
+ data.tar.gz: 6f6e2a7d77fd3c4e59059537c7ae3c4bcea323d60c8e55e80581ac2bd02f91f7
5
5
  SHA512:
6
- metadata.gz: 067b8ba96f97768aa5461e2458b7215cf045926875765ac631b450c9a15ed2313ded67f7986163c6fef8c5408dddf0160a10fa7933ffedbe23722822078e9060
7
- data.tar.gz: c10a7a1e147fadd212445b9d5ee7baadada737f53ccc55aa89db86f95f2cb208edf1d56a373020c4ac51c5a898e5c2198cb1458cccbfea36526dff104cfe61a7
6
+ metadata.gz: abe4b54cf8fb93e73af0e13c3cc3c2810ba770928791db82dac967c5ec76ccf285300c145f87b946a2026880b91faf5800ff86cbbcaa08ac450e4ed241a81a95
7
+ data.tar.gz: 5ecc3c0946cfb672814c6ced81cbd1cbd055127c7e6a2aed89a5885af33fa07d5aab93b4a3689311da061a0cba7d74781263aae5a20db3d76d64565c8b969b22
data/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  All notable changes to the HyperList Ruby TUI will be documented in this file.
4
4
 
5
+ ## [1.9.4] - 2025-12-02
6
+
7
+ ### Enhanced
8
+ - **Property coloring improvements**
9
+ - Added `&` symbol support in properties (e.g., "Day 1 & 2:" now colors correctly)
10
+ - Added chained property support (e.g., "09.03: Silje:" now colors the entire chain red)
11
+
12
+ ### Fixed
13
+ - **Performance optimization**
14
+ - Throttled external file change detection to run every 2 seconds instead of on every keypress
15
+ - Improves responsiveness when holding navigation keys (UP/DOWN)
16
+
17
+ ## [1.9.3] - 2025-12-02
18
+
19
+ ### Fixed
20
+ - **Help/documentation viewer navigation**
21
+ - Fixed invisible cursor on blank separator lines in help (?) and documentation (??) viewers
22
+ - Blank lines now contain a space so cursor highlighting is visible when navigating
23
+ - **Presentation mode state preservation**
24
+ - Help, documentation, and recent files viewers now properly save/restore presentation mode state
25
+ - Prevents presentation mode logic from interfering with viewer navigation
26
+
5
27
  ## [1.9.2] - 2025-12-02
6
28
 
7
29
  ### Fixed
data/README.md CHANGED
@@ -29,7 +29,23 @@ 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.2
32
+ ## What's New in v1.9.4
33
+
34
+ ### Property Coloring Improvements
35
+ - **Ampersand support**: Properties like "Day 1 & 2:" now color correctly
36
+ - **Chained properties**: Lines like "09.03: Silje: Opening" now color the entire "09.03: Silje:" chain red
37
+
38
+ ### Performance Optimization
39
+ - **Faster navigation**: File change detection now runs every 2 seconds instead of on every keypress
40
+ - Improved responsiveness when holding DOWN/UP keys
41
+
42
+ ## Previous Release: v1.9.3
43
+
44
+ ### Help Viewer Navigation Fixed
45
+ - Fixed invisible cursor on blank separator lines in help (?) and documentation (??) viewers
46
+ - Presentation mode state now properly preserved when entering/exiting viewers
47
+
48
+ ## Previous Release: v1.9.2
33
49
 
34
50
  ### Presentation Mode Fixed
35
51
  - **Dynamic folding**: Presentation mode now works like hyperlist.vim - folds/unfolds dynamically as you navigate
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.2 - Terminal User Interface for HyperList files
10
+ HyperList v1.9.4 - 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.2"
55
+ puts "HyperList v1.9.4"
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.2"
76
+ VERSION = "1.9.4"
77
77
 
78
78
  def initialize(filename = nil)
79
79
  @filename = filename ? File.expand_path(filename) : nil
@@ -538,9 +538,16 @@ class HyperListApp
538
538
  end
539
539
 
540
540
  # Check if file was modified externally and prompt for reload
541
+ # Throttled to only check every 2 seconds to avoid filesystem I/O on every keypress
541
542
  def check_file_changed
542
543
  return unless @filename && File.exist?(@filename) && @file_mtime
543
544
 
545
+ # Throttle: only check every 2 seconds
546
+ now = Time.now
547
+ @last_file_check ||= Time.at(0)
548
+ return if (now - @last_file_check) < 2
549
+ @last_file_check = now
550
+
544
551
  current_mtime = File.mtime(@filename) rescue nil
545
552
  return unless current_mtime && current_mtime > @file_mtime
546
553
 
@@ -596,7 +603,11 @@ class HyperListApp
596
603
  saved_offset = @offset
597
604
  saved_filename = @filename
598
605
  saved_modified = @modified
599
-
606
+ saved_presentation_mode = @presentation_mode
607
+
608
+ # Disable presentation mode for recent files viewer
609
+ @presentation_mode = false
610
+
600
611
  # Create items for recent files display
601
612
  @items = []
602
613
  @items << {"text" => "RECENT FILES (press Enter to open, q to cancel)", "level" => 0, "fold" => false, "raw" => true}
@@ -627,6 +638,7 @@ class HyperListApp
627
638
  @offset = saved_offset
628
639
  @filename = saved_filename
629
640
  @modified = saved_modified
641
+ @presentation_mode = saved_presentation_mode
630
642
  break
631
643
  when "j", "DOWN"
632
644
  move_down if @current < @items.length - 1
@@ -641,9 +653,11 @@ class HyperListApp
641
653
  @offset = saved_offset
642
654
  @filename = saved_filename
643
655
  @modified = saved_modified
656
+ @presentation_mode = saved_presentation_mode
644
657
  @message = "Unsaved changes! Save first before opening another file"
645
658
  break
646
659
  else
660
+ @presentation_mode = saved_presentation_mode
647
661
  @filename = File.expand_path(selected_file)
648
662
  load_file(@filename)
649
663
  @current = 0
@@ -663,9 +677,11 @@ class HyperListApp
663
677
  @offset = saved_offset
664
678
  @filename = saved_filename
665
679
  @modified = saved_modified
680
+ @presentation_mode = saved_presentation_mode
666
681
  @message = "Unsaved changes! Save first before opening another file"
667
682
  break
668
683
  else
684
+ @presentation_mode = saved_presentation_mode
669
685
  @filename = File.expand_path(selected_file)
670
686
  load_file(@filename)
671
687
  @current = 0
@@ -677,7 +693,7 @@ class HyperListApp
677
693
  end
678
694
  end
679
695
  end
680
-
696
+
681
697
  def save_file
682
698
  return unless @filename
683
699
 
@@ -1610,18 +1626,35 @@ class HyperListApp
1610
1626
  "#{$1}:".fg(colors["red"]) # Red for timestamp properties
1611
1627
  end
1612
1628
 
1613
- # Handle operators and properties with colon pattern
1629
+ # Handle chained properties first (e.g., "09.03: Silje: Opening")
1630
+ # Match sequences of "word: word: word:" at the start of a line, color all red
1631
+ result = safe_regex_replace(result, /^(\s*)((?:[a-zA-Z0-9][a-zA-Z0-9_\-()& .\/=]*:\s*)+)/) do |match|
1632
+ if match =~ /^(\s*)((?:[a-zA-Z0-9][a-zA-Z0-9_\-()& .\/=]*:\s*)+)/
1633
+ prefix = $1 || ""
1634
+ chain = $2
1635
+ # Check if this is an operator (ALL-CAPS only)
1636
+ if chain =~ /^[A-Z][A-Z_\-() \/=]*:\s*$/
1637
+ prefix + chain.fg(colors["blue"]) # Blue for operators
1638
+ else
1639
+ prefix + chain.fg(colors["red"]) # Red for property chains
1640
+ end
1641
+ else
1642
+ match
1643
+ end
1644
+ end
1645
+
1646
+ # Handle operators and properties with colon pattern (for inline occurrences)
1614
1647
  # Operators: ALL-CAPS followed by colon (with or without space)
1615
1648
  # Properties: Mixed case followed by colon and space
1616
1649
  # Modified pattern to also match after ANSI placeholders (⟨ANSI\d+⟩)
1617
- result = safe_regex_replace(result, /(\A|\s+|⟨ANSI\d+⟩)([a-zA-Z][a-zA-Z0-9_\-() .\/=]*):(\s*)/) do |match|
1650
+ result = safe_regex_replace(result, /(\s+|⟨ANSI\d+⟩)([a-zA-Z][a-zA-Z0-9_\-()& .\/=]*):(\s*)/) do |match|
1618
1651
  # Extract parts from the match
1619
- if match =~ /(\A|\s+|⟨ANSI\d+⟩)([a-zA-Z][a-zA-Z0-9_\-() .\/=]*):(\s*)/
1652
+ if match =~ /(\s+|⟨ANSI\d+⟩)([a-zA-Z][a-zA-Z0-9_\-()& .\/=]*):(\s*)/
1620
1653
  prefix_space = $1
1621
1654
  text_part = $2
1622
1655
  space_after = $3 || ""
1623
1656
  colon_space = ":#{space_after}"
1624
-
1657
+
1625
1658
  # Check if it's an operator (ALL-CAPS with optional _, -, (), /, =, spaces)
1626
1659
  if text_part =~ /^[A-Z][A-Z_\-() \/=]*$/
1627
1660
  prefix_space + text_part.fg(colors["blue"]) + colon_space.fg(colors["blue"]) # Blue for operators (including S: and T:)
@@ -3738,9 +3771,9 @@ class HyperListApp
3738
3771
  # Build help text using consistent formatting
3739
3772
  help_lines = []
3740
3773
  help_lines << " Press #{"?".fg("10")} for full documentation, #{"UP/DOWN".fg("10")} to scroll, or any other key to return"
3741
- help_lines << ""
3774
+ help_lines << " "
3742
3775
  help_lines << "#{"HYPERLIST KEY BINDINGS".b}"
3743
- help_lines << ""
3776
+ help_lines << " "
3744
3777
  help_lines << "#{"NAVIGATION".fg("14")}"
3745
3778
  help_lines << help_line("#{"j/↓".fg("10")}", "Move down", "#{"k/↑".fg("10")}", "Move up")
3746
3779
  help_lines << help_line("#{"h".fg("10")}", "Go to parent", "#{"l".fg("10")}", "Go to first child")
@@ -3749,10 +3782,10 @@ class HyperListApp
3749
3782
  help_lines << help_line("#{"R".fg("10")}", "Jump to reference", "#{"F".fg("10")}", "Open file/URL")
3750
3783
  help_lines << help_line("#{"ma".fg("10")}", "Set mark 'a'", "#{"'a".fg("10")}", "Jump to mark 'a'")
3751
3784
  help_lines << help_line("#{"''".fg("10")}", "Jump to prev position", "#{"N".fg("10")}", "Next = template marker")
3752
- help_lines << ""
3785
+ help_lines << " "
3753
3786
  help_lines << "#{"SEARCH".fg("14")}"
3754
3787
  help_lines << help_line("#{"/".fg("10")}", "Search forward", "#{"n".fg("10")}", "Next match")
3755
- help_lines << ""
3788
+ help_lines << " "
3756
3789
  help_lines << "#{"FOLDING".fg("14")}"
3757
3790
  help_lines << help_line("#{"Space".fg("10")}", "Toggle fold", "#{"za".fg("10")}", "Toggle all folds")
3758
3791
  help_lines << help_line("#{"zo".fg("10")}", "Open fold", "#{"zc".fg("10")}", "Close fold")
@@ -3760,7 +3793,7 @@ class HyperListApp
3760
3793
  help_lines << help_line("#{"1-9".fg("10")}", "Expand to level", "#{"0".fg("10")}", "Multi-digit fold level")
3761
3794
  help_lines << help_line("#{"▶".fg("245")}", "Collapsed (hidden)")
3762
3795
  help_lines << help_line("#{"▷".fg("245")}", "Expanded (visible)")
3763
- help_lines << ""
3796
+ help_lines << " "
3764
3797
  help_lines << "#{"EDITING".fg("14")}"
3765
3798
  help_lines << help_line("#{"i/Enter".fg("10")}", "Edit line", "#{"o".fg("10")}", "Insert line below")
3766
3799
  help_lines << help_line("#{"O".fg("10")}", "Insert line above", "#{"a".fg("10")}", "Insert child")
@@ -3776,7 +3809,7 @@ class HyperListApp
3776
3809
  help_lines << help_line("#{"C-UP".fg("10")}", "Move up in visible list", "#{"C-DOWN".fg("10")}", "Move down in visible list")
3777
3810
  help_lines << help_line("#{"→".fg("10")}", "Uncollapse item", "#{"←".fg("10")}", "Collapse item")
3778
3811
  help_lines << help_line("#{"Tab".fg("10")}", "Indent item+kids", "#{"S-Tab".fg("10")}", "Unindent item+kids")
3779
- help_lines << ""
3812
+ help_lines << " "
3780
3813
  help_lines << "#{"SPECIAL FEATURES".fg("14")}"
3781
3814
  help_lines << help_line("#{"v".fg("10")}", "Toggle checkbox", "#{"V".fg("10")}", "Checkbox with date")
3782
3815
  help_lines << help_line("#{"C-X".fg("10")}", "Remove checkbox", "", "")
@@ -3784,7 +3817,7 @@ class HyperListApp
3784
3817
  help_lines << help_line("#{"C-P".fg("10")}", "Presentation mode", "#{"Tab/S-Tab".fg("10")}", "Next/prev sibling (in P)")
3785
3818
  help_lines << help_line("#{"Ma".fg("10")}", "Record macro 'a'", "#{"@a".fg("10")}", "Play macro 'a'")
3786
3819
  help_lines << help_line("#{"w".fg("10")}", "Switch panes (split view)")
3787
- help_lines << ""
3820
+ help_lines << " "
3788
3821
  help_lines << "#{"FILE OPERATIONS".fg("14")}"
3789
3822
  help_lines << help_line("#{":w".fg("10")}", "Save", "#{":q".fg("10")}", "Quit")
3790
3823
  help_lines << help_line("#{":wq".fg("10")}", "Save and quit", "#{":e file".fg("10")}", "Open file")
@@ -3792,27 +3825,24 @@ class HyperListApp
3792
3825
  help_lines << help_line("#{":graph :g".fg("10")}", "Export to PNG graph", "#{":vsplit :vs".fg("10")}", "Split view")
3793
3826
  help_lines << help_line("#{":as on".fg("10")}", "Enable autosave", "#{":as off".fg("10")}", "Disable autosave")
3794
3827
  help_lines << help_line("#{":as N".fg("10")}", "Set interval (secs)", "#{":as".fg("10")}", "Show autosave status")
3795
- help_lines << ""
3828
+ help_lines << " "
3796
3829
  help_lines << "#{"TAGGING & BATCH OPS".fg("14")}"
3797
3830
  help_lines << help_line("#{"t".fg("10")}", "Tag/untag item", "#{"u".fg("10")}", "Clear all tags")
3798
3831
  help_lines << help_line("#{"C-T".fg("10")}", "Tag by regex pattern", "", "")
3799
3832
  help_lines << help_line("#{"[T:N]".fg("245")}", "Status shows N tagged", "#{"Blue bg".fg("245")}", "Tagged items")
3800
3833
  help_lines << help_line("#{"D/y/Tab".fg("245")}", "Ops work on tagged items")
3801
- help_lines << ""
3802
-
3834
+ help_lines << " "
3803
3835
  help_lines << "#{"TEMPLATES".fg("14")}"
3804
3836
  help_lines << help_line("#{"T".fg("10")}", "Insert template", "#{":st".fg("10")}", "Save as template")
3805
3837
  help_lines << help_line("#{":dt".fg("10")}", "Delete template", "#{":lt".fg("10")}", "List user templates")
3806
- help_lines << ""
3807
-
3838
+ help_lines << " "
3808
3839
  help_lines << "#{"CONFIGURATION".fg("14")}"
3809
3840
  help_lines << help_line("#{":set".fg("10")}", "Show all settings")
3810
- help_lines << help_line("#{":set o".fg("10")}", "Show option value")
3841
+ help_lines << help_line("#{":set o".fg("10")}", "Show option value")
3811
3842
  help_lines << help_line("#{":set o=val".fg("10")}", "Set option value")
3812
3843
  help_lines << help_line("#{"Options:".fg("245")}", "theme, wrap, fold_level")
3813
3844
  help_lines << help_line("#{"".fg("245")}", "show_numbers, tab_width")
3814
- help_lines << ""
3815
-
3845
+ help_lines << " "
3816
3846
  help_lines << "#{"HELP & QUIT".fg("14")}"
3817
3847
  help_lines << help_line("#{"?".fg("10")}", "This help", "#{"??".fg("10")}", "Full documentation")
3818
3848
  help_lines << help_line("#{"q".fg("10")}", "Quit (asks to save)", "#{"Q".fg("10")}", "Force quit")
@@ -3832,13 +3862,17 @@ class HyperListApp
3832
3862
  saved_current = @current
3833
3863
  saved_offset = @offset
3834
3864
  saved_modified = @modified
3835
-
3865
+ saved_presentation_mode = @presentation_mode
3866
+
3867
+ # Disable presentation mode for help viewer
3868
+ @presentation_mode = false
3869
+
3836
3870
  # Create temporary help items for scrolling
3837
3871
  @items = help.split("\n").map { |line| {"text" => line, "level" => 0, "fold" => false, "raw" => true} }
3838
3872
  @current = 0
3839
3873
  @offset = 0
3840
3874
  @modified = false
3841
-
3875
+
3842
3876
  # Help viewer loop
3843
3877
  loop do
3844
3878
  render_main
@@ -3880,8 +3914,9 @@ class HyperListApp
3880
3914
  @current = saved_current
3881
3915
  @offset = saved_offset
3882
3916
  @modified = saved_modified
3917
+ @presentation_mode = saved_presentation_mode
3883
3918
  end
3884
-
3919
+
3885
3920
  def show_documentation
3886
3921
  doc = <<~DOC
3887
3922
 
@@ -4360,13 +4395,17 @@ class HyperListApp
4360
4395
  saved_offset = @offset
4361
4396
  saved_filename = @filename
4362
4397
  saved_modified = @modified
4363
-
4398
+ saved_presentation_mode = @presentation_mode
4399
+
4400
+ # Disable presentation mode for documentation viewer
4401
+ @presentation_mode = false
4402
+
4364
4403
  # Create temporary documentation items
4365
4404
  @items = doc.split("\n").map { |line| {"text" => line, "level" => 0, "fold" => false, "raw" => true} }
4366
4405
  @current = 0
4367
4406
  @offset = 0
4368
4407
  @modified = false
4369
-
4408
+
4370
4409
  # Documentation viewer loop
4371
4410
  loop do
4372
4411
  render_main
@@ -4413,8 +4452,9 @@ class HyperListApp
4413
4452
  @offset = saved_offset
4414
4453
  @filename = saved_filename
4415
4454
  @modified = saved_modified
4455
+ @presentation_mode = saved_presentation_mode
4416
4456
  end
4417
-
4457
+
4418
4458
  def handle_command
4419
4459
  @mode = :command
4420
4460
  # Set command history (reversed for proper UP arrow navigation)
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.2"
3
+ spec.version = "1.9.4"
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.2
4
+ version: 1.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
@@ -70,6 +70,7 @@ extensions: []
70
70
  extra_rdoc_files: []
71
71
  files:
72
72
  - "./hyperlist"
73
+ - ".claude/tsc-cache/ae1f6800-54bf-4599-aee8-058bd15a57a3/affected-repos.txt"
73
74
  - CHANGELOG.md
74
75
  - LICENSE
75
76
  - README.md