hyperlist 1.7.0 → 1.8.1

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: 014e81876165c457903e5f20e8f848f2c59fce0d2672e700ff909873c03c260a
4
- data.tar.gz: c60b73c642984f70ba2bfdcceb7ca6a9bc501c46071cb362d9e9343778cd8f40
3
+ metadata.gz: 3ab71a37a8c51bc72f5a944172d2fae808da9ed42e4a21e13f937d2a4410596a
4
+ data.tar.gz: 346c2edd59e6a6e4a116d05a506ded75b313996baa06f938def3fb0947c05850
5
5
  SHA512:
6
- metadata.gz: 37ba0da349a30af46424cc374ece24e315c32a22ee48bf81f4893c43e20cdf18f42ec71c3b3fe123688f9b97597a29e8aa583884cceb361d44583645546aaf25
7
- data.tar.gz: 5a8737705ad4d231e6ce1a8a1b8f440cf8b0f3c2ffa7586052dc64be5a55d8bc4491ed721b07adb192e6674f0560a9ac35a6a067ed27831b200066b786456081
6
+ metadata.gz: b66f71b652663dbf1fcbcee37fa7478b718813a7dd234081aad77e9110d1e01c472f2ce3d2a325ad6cd1e922b2efacb398c531fab79cd594999c5e2db20ad039
7
+ data.tar.gz: 3f2a1573bb24704f3f3df797e8cd3766e78d265aa27c68ca41fc7326976a23ec361c9fc93c814b9a8afc7b9003921b7e0c3111e6bfbbc9df0ebf8efddee3401e
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
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
+
5
15
  ## [1.7.0] - 2025-09-18
6
16
 
7
17
  ### Added
data/README.md CHANGED
@@ -29,17 +29,26 @@ 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.4.4
33
-
34
- ### 🔧 Case Conversion Commands
35
- - **`gU`**: Convert current line to UPPERCASE
36
- - **`gu`**: Convert current line to lowercase
37
- - Works with all HyperList elements (checkboxes, operators, etc.)
38
-
39
- ### 🐛 Bug Fixes
40
- - Fixed color code display issues with numbered lists and operators
41
- - Improved handling of qualifiers like `[? conditional]`
42
- - Better coloring when combining numbered lists with operators (e.g., `1. NOT: item`)
32
+ ## What's New in v1.8.0
33
+
34
+ ### 📋 Multi-Line Paste Support
35
+ - **Paste multiple lines**: When pasting multi-line content into item insertion prompts ('o', 'O', 'a', 'A'), each line becomes a separate item
36
+ - **Visual feedback**: Shows `[+N lines]` indicator during multi-line paste
37
+ - **Smart insertion**: All pasted lines inserted as siblings at the same level
38
+ - Great for importing bullet lists from PDFs, emails, or other documents
39
+ - Requires rcurses 6.1.5+
40
+
41
+ ### 📄 PDF/LaTeX Export
42
+ - **Export to PDF**: `:export pdf filename.pdf` - Full LaTeX-based PDF generation
43
+ - **Export to LaTeX**: `:export latex filename.tex` - Get the LaTeX source
44
+ - **Professional output**: Color-coded elements, table of contents, headers
45
+ - **Complete HyperList support**: All syntax elements rendered beautifully
46
+ - Requires: texlive-latex-base and texlive-latex-extra packages
47
+
48
+ ### 📋 System Clipboard Integration
49
+ - **Yank to clipboard**: 'y' and 'Y' now copy to system clipboard
50
+ - **Middle-click paste**: Yanked items can be pasted into other terminals
51
+ - **Preserves indentation**: Copied text maintains proper structure
43
52
 
44
53
  ## Previous Version Features (v1.4.0)
45
54
 
@@ -149,6 +158,8 @@ All `:set` commands automatically update the file's configuration line.
149
158
  - **Dates**: `2025-08-12` or `2025-08-12 14:30`
150
159
 
151
160
  ### Export Formats
161
+ - **PDF**: Professional LaTeX-based PDF with color coding and TOC
162
+ - **LaTeX**: Source .tex files for customization
152
163
  - **HTML**: Full-featured HTML with syntax highlighting
153
164
  - **Markdown**: GitHub-flavored Markdown
154
165
  - **Plain Text**: Clean text output
@@ -236,9 +247,11 @@ hyperlist file.txt # Open any text file
236
247
  - `:q` - Quit
237
248
  - `:wq` or `W` - Save and quit
238
249
  - `:e file` - Open file
250
+ - `:export pdf` - Export to PDF (requires LaTeX)
251
+ - `:export latex` - Export to LaTeX source
239
252
  - `:export html` - Export to HTML
240
253
  - `:export md` - Export to Markdown
241
- - `:graph` - Export to PNG
254
+ - `:graph` - Export to PNG graph
242
255
 
243
256
  #### Template Commands
244
257
  - `:st` - Save current document as template
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.5.1 - Terminal User Interface for HyperList files
10
+ HyperList v1.8.0 - 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.5.1"
55
+ puts "HyperList v1.8.0"
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.7.0"
75
+ VERSION = "1.8.0"
76
76
 
77
77
  def initialize(filename = nil)
78
78
  @filename = filename ? File.expand_path(filename) : nil
@@ -724,10 +724,12 @@ class HyperListApp
724
724
  when 'md', 'markdown' then '.md'
725
725
  when 'html' then '.html'
726
726
  when 'txt', 'text' then '.txt'
727
+ when 'latex', 'tex' then '.tex'
728
+ when 'pdf' then '.pdf'
727
729
  end
728
730
  export_file = base + extension
729
731
  end
730
-
732
+
731
733
  content = case format
732
734
  when 'md', 'markdown'
733
735
  export_to_markdown
@@ -735,10 +737,39 @@ class HyperListApp
735
737
  export_to_html
736
738
  when 'txt', 'text'
737
739
  export_to_text
740
+ when 'latex', 'tex'
741
+ export_to_latex
742
+ when 'pdf'
743
+ export_to_latex
738
744
  end
739
-
745
+
740
746
  File.open(export_file, 'w') { |f| f.write(content) }
741
- @message = "Exported to #{export_file}"
747
+
748
+ # For PDF, compile the LaTeX file
749
+ if format == 'pdf'
750
+ tex_file = export_file.sub(/\.pdf$/, '.tex')
751
+ File.open(tex_file, 'w') { |f| f.write(content) }
752
+
753
+ # Compile LaTeX to PDF
754
+ @message = "Compiling LaTeX to PDF..."
755
+ render_footer
756
+
757
+ result = system("pdflatex -interaction=nonstopmode -output-directory=#{File.dirname(tex_file)} #{tex_file} > /dev/null 2>&1")
758
+ if result
759
+ # Run twice for TOC
760
+ system("pdflatex -interaction=nonstopmode -output-directory=#{File.dirname(tex_file)} #{tex_file} > /dev/null 2>&1")
761
+ @message = "Exported to #{export_file}"
762
+ # Clean up auxiliary files
763
+ ['aux', 'log', 'out', 'toc'].each do |ext|
764
+ aux_file = tex_file.sub(/\.tex$/, ".#{ext}")
765
+ File.delete(aux_file) if File.exist?(aux_file)
766
+ end
767
+ else
768
+ @message = "PDF compilation failed. Check #{tex_file}"
769
+ end
770
+ else
771
+ @message = "Exported to #{export_file}"
772
+ end
742
773
  rescue => e
743
774
  @message = "Export failed: #{e.message}"
744
775
  end
@@ -948,7 +979,151 @@ class HyperListApp
948
979
  end
949
980
  lines.join("\n")
950
981
  end
951
-
982
+
983
+ def escape_latex(text)
984
+ # Escape special LaTeX characters
985
+ text = text.gsub('\\', '\\textbackslash{}')
986
+ text = text.gsub('&', '\\&')
987
+ text = text.gsub('%', '\\%')
988
+ text = text.gsub('$', '\\$')
989
+ text = text.gsub('#', '\\#')
990
+ text = text.gsub('_', '\\_')
991
+ text = text.gsub('^', '\\textasciicircum{}')
992
+ text = text.gsub('~', '\\textasciitilde{}')
993
+ text
994
+ end
995
+
996
+ def export_to_latex
997
+ lines = []
998
+
999
+ # LaTeX header
1000
+ lines << "% Generated by HyperList - LaTeX Export"
1001
+ lines << "% Export date: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
1002
+ lines << ""
1003
+ lines << "\\documentclass[11pt,a4paper]{article}"
1004
+ lines << "\\usepackage[utf8]{inputenc}"
1005
+ lines << "\\usepackage[T1]{fontenc}"
1006
+ lines << "\\usepackage[english]{babel}"
1007
+ lines << "\\usepackage[margin=2.5cm]{geometry}"
1008
+ lines << "\\usepackage{xcolor}"
1009
+ lines << "\\usepackage{enumitem}"
1010
+ lines << "\\usepackage{fancyhdr}"
1011
+ lines << "\\usepackage{hyperref}"
1012
+ lines << "\\usepackage{tcolorbox}"
1013
+ lines << "\\usepackage{fontawesome5}"
1014
+ lines << ""
1015
+ lines << "% HyperList color definitions"
1016
+ lines << "\\definecolor{hloperator}{RGB}{41,128,185}"
1017
+ lines << "\\definecolor{hlproperty}{RGB}{231,76,60}"
1018
+ lines << "\\definecolor{hlqualifier}{RGB}{39,174,96}"
1019
+ lines << "\\definecolor{hlhashtag}{RGB}{243,156,18}"
1020
+ lines << "\\definecolor{hlreference}{RGB}{155,89,182}"
1021
+ lines << "\\definecolor{hlcomment}{RGB}{127,140,141}"
1022
+ lines << "\\definecolor{hlquote}{RGB}{22,160,133}"
1023
+ lines << "\\definecolor{hlstate}{RGB}{46,204,113}"
1024
+ lines << "\\definecolor{hltransition}{RGB}{230,126,34}"
1025
+ lines << ""
1026
+ lines << "% HyperList commands"
1027
+ lines << "\\newcommand{\\hloperator}[1]{\\textcolor{hloperator}{\\textbf{#1}}}"
1028
+ lines << "\\newcommand{\\hlproperty}[1]{\\textcolor{hlproperty}{\\textit{#1}}}"
1029
+ lines << "\\newcommand{\\hlqualifier}[1]{\\textcolor{hlqualifier}{\\texttt{#1}}}"
1030
+ lines << "\\newcommand{\\hlhashtag}[1]{\\textcolor{hlhashtag}{\\textbf{#1}}}"
1031
+ lines << "\\newcommand{\\hlreference}[1]{\\textcolor{hlreference}{#1}}"
1032
+ lines << "\\newcommand{\\hlcomment}[1]{\\textcolor{hlcomment}{\\textit{#1}}}"
1033
+ lines << "\\newcommand{\\hlquote}[1]{\\textcolor{hlquote}{#1}}"
1034
+ lines << "\\newcommand{\\hlstate}[1]{\\begin{tcolorbox}[colback=hlstate!10,colframe=hlstate,title=State]#1\\end{tcolorbox}}"
1035
+ lines << "\\newcommand{\\hltransition}[1]{\\begin{tcolorbox}[colback=hltransition!10,colframe=hltransition,title=Action]#1\\end{tcolorbox}}"
1036
+ lines << "\\newcommand{\\hlsubstitution}[1]{\\textcolor{hlqualifier}{#1}}"
1037
+ lines << "\\newcommand{\\checkbox}{\\faSquare[regular]}"
1038
+ lines << "\\newcommand{\\checkboxdone}{\\faCheckSquare}"
1039
+ lines << "\\newcommand{\\checkboxprogress}{\\faMinusSquare[regular]}"
1040
+ lines << ""
1041
+ lines << "% Document setup"
1042
+ title = @filename ? File.basename(@filename, '.*') : 'HyperList Export'
1043
+ lines << "\\title{#{escape_latex(title)}}"
1044
+ lines << "\\author{Generated by HyperList}"
1045
+ lines << "\\date{#{Time.now.strftime('%Y-%m-%d')}}"
1046
+ lines << ""
1047
+ lines << "\\pagestyle{fancy}"
1048
+ lines << "\\fancyhf{}"
1049
+ lines << "\\fancyhead[R]{\\thepage}"
1050
+ lines << "\\fancyhead[L]{#{escape_latex(title)}}"
1051
+ lines << ""
1052
+ lines << "\\begin{document}"
1053
+ lines << "\\maketitle"
1054
+ lines << "\\tableofcontents"
1055
+ lines << "\\newpage"
1056
+ lines << ""
1057
+ lines << "\\begin{itemize}[leftmargin=0pt,itemsep=2pt,parsep=0pt]"
1058
+
1059
+ # Process each item
1060
+ prev_level = 0
1061
+ @items.each do |item|
1062
+ text = item["text"]
1063
+ level = item["level"]
1064
+
1065
+ # Handle level changes
1066
+ if level > prev_level
1067
+ (level - prev_level).times do
1068
+ lines << "\\begin{itemize}"
1069
+ end
1070
+ elsif level < prev_level
1071
+ (prev_level - level).times do
1072
+ lines << "\\end{itemize}"
1073
+ end
1074
+ end
1075
+
1076
+ # Escape LaTeX special characters first
1077
+ text = escape_latex(text)
1078
+
1079
+ # Convert checkboxes
1080
+ text = text.gsub(/\[_\]/, '\\checkbox{}')
1081
+ text = text.gsub(/\[x\]|\[X\]/, '\\checkboxdone{}')
1082
+ text = text.gsub(/\[O\]/, '\\checkboxprogress{}')
1083
+
1084
+ # Convert HyperList markup (must be after escaping)
1085
+ text = text.gsub(/ \*([^*]+)\* /, ' \\textbf{\1} ')
1086
+ text = text.gsub(/ \/([^\/]+)\/ /, ' \\textit{\1} ')
1087
+ text = text.gsub(/ _([^_]+)_ /, ' \\underline{\1} ')
1088
+
1089
+ # Convert HyperList elements
1090
+ text = text.gsub(/\[([^\]]+)\]/, '\\hlqualifier{[\1]}')
1091
+ text = text.gsub(/\{([^\}]+)\}/, '\\hlsubstitution{\{\1\}}')
1092
+ text = text.gsub(/(#[a-zA-Z0-9.:\/\\_&?%=\-*]+)/, '\\hlhashtag{\1}')
1093
+ text = text.gsub(/(<{1,2}[a-zA-Z0-9.:\/\\_&?%=\-* ]+>{1,2})/, '\\hlreference{\1}')
1094
+ text = text.gsub(/(\([^)]*\))/, '\\hlcomment{\1}')
1095
+ text = text.gsub(/(\"[^\"]*\")/, '\\hlquote{\1}')
1096
+
1097
+ # Convert States and Transitions
1098
+ if text =~ /^S: (.+)/
1099
+ text = "\\hlstate{State: #{$1}}"
1100
+ elsif text =~ /^T: (.+)/
1101
+ text = "\\hltransition{Action: #{$1}}"
1102
+ elsif text =~ /^\| (.+)/
1103
+ text = "\\hlstate{State: #{$1}}"
1104
+ elsif text =~ /^\/ (.+)/
1105
+ text = "\\hltransition{Action: #{$1}}"
1106
+ end
1107
+
1108
+ # Convert Operators and Properties
1109
+ text = text.gsub(/([A-Z_\-() \/]{2,}): /, '\\hloperator{\1:} ')
1110
+ text = text.gsub(/([a-zA-Z0-9,._&?%=\-\/+<>#'()*:]{2,}): /, '\\hlproperty{\1:} ')
1111
+
1112
+ lines << "\\item #{text}"
1113
+ prev_level = level
1114
+ end
1115
+
1116
+ # Close any remaining nested lists
1117
+ prev_level.times do
1118
+ lines << "\\end{itemize}"
1119
+ end
1120
+
1121
+ lines << "\\end{itemize}"
1122
+ lines << "\\end{document}"
1123
+
1124
+ lines.join("\n")
1125
+ end
1126
+
952
1127
  def render
953
1128
  render_main
954
1129
  render_split_pane if @split_view
@@ -1674,6 +1849,10 @@ class HyperListApp
1674
1849
  end
1675
1850
  end
1676
1851
 
1852
+ # Ensure the moved item remains visible (find its index in main items array)
1853
+ main_real_idx = @items.index(item)
1854
+ force_item_visible(main_real_idx) if main_real_idx
1855
+
1677
1856
  @modified = true
1678
1857
  @message = "Indented in split pane"
1679
1858
  end
@@ -1702,6 +1881,10 @@ class HyperListApp
1702
1881
  end
1703
1882
  end
1704
1883
 
1884
+ # Ensure the moved item remains visible (find its index in main items array)
1885
+ main_real_idx = @items.index(item)
1886
+ force_item_visible(main_real_idx) if main_real_idx
1887
+
1705
1888
  @modified = true
1706
1889
  @message = "Unindented in split pane"
1707
1890
  end
@@ -2324,19 +2507,41 @@ class HyperListApp
2324
2507
 
2325
2508
  def insert_line
2326
2509
  @mode = :insert
2327
-
2510
+
2328
2511
  input = @footer.ask("New item: ", "")
2329
-
2512
+
2330
2513
  if input && !input.strip.empty?
2331
- insert_line_with_text(input)
2514
+ # Collect all lines (first line + buffer)
2515
+ all_lines = [input]
2516
+ if @footer.multiline_buffer && !@footer.multiline_buffer.empty?
2517
+ all_lines += @footer.multiline_buffer.reject { |l| l.strip.empty? }
2518
+ @footer.multiline_buffer = []
2519
+ end
2520
+
2521
+ # Insert all lines as siblings at the same level
2522
+ all_lines.each do |line|
2523
+ insert_line_with_text(line.strip)
2524
+ @current -= 1 # Keep cursor at same position so next insert is sibling
2525
+ end
2526
+ @current += 1 # Move to last inserted item
2527
+
2332
2528
  record_last_action(:insert_line, input)
2333
2529
  end
2334
-
2530
+
2335
2531
  @mode = :normal
2336
2532
  @footer.clear # Clear footer immediately
2337
2533
  @footer.refresh
2338
2534
  end
2339
-
2535
+
2536
+ # Alias for 'o' key
2537
+ alias insert_line_below insert_line
2538
+
2539
+ # Method for 'O' key - insert line above
2540
+ def insert_line_above
2541
+ @current -= 1 if @current > 0
2542
+ insert_line
2543
+ end
2544
+
2340
2545
  def insert_line_with_text(text)
2341
2546
  return unless text && !text.strip.empty?
2342
2547
 
@@ -2363,14 +2568,27 @@ class HyperListApp
2363
2568
 
2364
2569
  def insert_child
2365
2570
  @mode = :insert
2366
-
2571
+
2367
2572
  input = @footer.ask("New child item: ", "")
2368
-
2573
+
2369
2574
  if input && !input.strip.empty?
2370
- insert_child_with_text(input)
2575
+ # Collect all lines (first line + buffer)
2576
+ all_lines = [input]
2577
+ if @footer.multiline_buffer && !@footer.multiline_buffer.empty?
2578
+ all_lines += @footer.multiline_buffer.reject { |l| l.strip.empty? }
2579
+ @footer.multiline_buffer = []
2580
+ end
2581
+
2582
+ # Insert all lines as siblings at the same level
2583
+ all_lines.each do |line|
2584
+ insert_child_with_text(line.strip)
2585
+ @current -= 1 # Keep cursor at same position so next insert is sibling
2586
+ end
2587
+ @current += 1 # Move to last inserted item
2588
+
2371
2589
  record_last_action(:insert_child, input)
2372
2590
  end
2373
-
2591
+
2374
2592
  @mode = :normal
2375
2593
  @footer.clear # Clear footer immediately
2376
2594
  @footer.refresh
@@ -2378,14 +2596,43 @@ class HyperListApp
2378
2596
 
2379
2597
  def insert_outdented
2380
2598
  @mode = :insert
2381
-
2599
+
2382
2600
  input = @footer.ask("New outdented item: ", "")
2383
-
2601
+
2384
2602
  if input && !input.strip.empty?
2385
- insert_outdented_with_text(input)
2603
+ # Collect all lines (first line + buffer)
2604
+ all_lines = [input]
2605
+ if @footer.multiline_buffer && !@footer.multiline_buffer.empty?
2606
+ all_lines += @footer.multiline_buffer.reject { |l| l.strip.empty? }
2607
+ @footer.multiline_buffer = []
2608
+ end
2609
+
2610
+ # Calculate target level BEFORE inserting anything
2611
+ visible = get_visible_items
2612
+ if @current < visible.length
2613
+ current_level = visible[@current]["level"]
2614
+ target_level = [current_level - 1, 0].max # Outdented level
2615
+ else
2616
+ target_level = 0
2617
+ end
2618
+
2619
+ # Insert first line normally
2620
+ insert_outdented_with_text(all_lines[0].strip)
2621
+
2622
+ # Insert remaining lines at same level as the first
2623
+ if all_lines.length > 1
2624
+ all_lines[1..-1].each do |line|
2625
+ visible = get_visible_items
2626
+ real_idx = get_real_index(visible[@current])
2627
+ @items.insert(real_idx + 1, {"text" => line.strip, "level" => target_level, "fold" => false})
2628
+ @current += 1
2629
+ end
2630
+ @modified = true
2631
+ end
2632
+
2386
2633
  record_last_action(:insert_outdented, input)
2387
2634
  end
2388
-
2635
+
2389
2636
  @mode = :normal
2390
2637
  @footer.clear # Clear footer immediately
2391
2638
  @footer.refresh
@@ -2570,14 +2817,14 @@ class HyperListApp
2570
2817
  def yank_line(with_children = false)
2571
2818
  visible = get_visible_items
2572
2819
  return if @current >= visible.length
2573
-
2820
+
2574
2821
  item = visible[@current]
2575
2822
  real_idx = get_real_index(item)
2576
-
2823
+
2577
2824
  @clipboard = []
2578
2825
  @clipboard << item.dup
2579
2826
  @clipboard_is_tree = with_children # Remember if this is a tree copy (Y) or single (y)
2580
-
2827
+
2581
2828
  # Copy children if requested
2582
2829
  if with_children
2583
2830
  level = item["level"]
@@ -2589,7 +2836,15 @@ class HyperListApp
2589
2836
  end
2590
2837
  end
2591
2838
  end
2592
-
2839
+
2840
+ # Copy to system clipboard for middle-click paste
2841
+ begin
2842
+ text_to_copy = @clipboard.map { |it| " " * it["level"] + it["text"] }.join("\n")
2843
+ Clipboard.copy(text_to_copy)
2844
+ rescue => e
2845
+ # Silently fail if clipboard gem not available
2846
+ end
2847
+
2593
2848
  @message = "Yanked #{@clipboard.length} item(s)"
2594
2849
  record_last_action(:yank_line, with_children)
2595
2850
  end
@@ -2918,6 +3173,9 @@ class HyperListApp
2918
3173
  end
2919
3174
  end
2920
3175
 
3176
+ # Ensure the moved item remains visible
3177
+ force_item_visible(real_idx)
3178
+
2921
3179
  @modified = true
2922
3180
  record_last_action(:indent_right, with_children)
2923
3181
  end
@@ -2946,6 +3204,9 @@ class HyperListApp
2946
3204
  end
2947
3205
  end
2948
3206
 
3207
+ # Ensure the moved item remains visible
3208
+ force_item_visible(real_idx)
3209
+
2949
3210
  @modified = true
2950
3211
  record_last_action(:indent_left, with_children)
2951
3212
  end
@@ -3202,7 +3463,7 @@ class HyperListApp
3202
3463
  help_lines << "#{"FILE OPERATIONS".fg("14")}"
3203
3464
  help_lines << help_line("#{":w".fg("10")}", "Save", "#{":q".fg("10")}", "Quit")
3204
3465
  help_lines << help_line("#{":wq".fg("10")}", "Save and quit", "#{":e file".fg("10")}", "Open file")
3205
- help_lines << help_line("#{":recent".fg("10")}", "Recent files", "#{":export :ex".fg("10")}", "Export md|html|txt")
3466
+ help_lines << help_line("#{":recent".fg("10")}", "Recent files", "#{":export :ex".fg("10")}", "Export md|html|txt|pdf")
3206
3467
  help_lines << help_line("#{":graph :g".fg("10")}", "Export to PNG graph", "#{":vsplit :vs".fg("10")}", "Split view")
3207
3468
  help_lines << help_line("#{":as on".fg("10")}", "Enable autosave", "#{":as off".fg("10")}", "Disable autosave")
3208
3469
  help_lines << help_line("#{":as N".fg("10")}", "Set interval (secs)", "#{":as".fg("10")}", "Show autosave status")
@@ -3886,12 +4147,12 @@ class HyperListApp
3886
4147
  @current = 0
3887
4148
  @offset = 0
3888
4149
  @modified = false
3889
- when /^(export|ex)\s+(md|markdown|html|txt|text)\s*(.*)$/
4150
+ when /^(export|ex)\s+(md|markdown|html|txt|text|latex|tex|pdf)\s*(.*)$/
3890
4151
  format = $2
3891
4152
  export_file = $3.empty? ? nil : $3
3892
4153
  export_to(format, export_file)
3893
4154
  when /^(export|ex)$/
3894
- @message = "Usage: :export [md|html|txt] [filename] (or :ex)"
4155
+ @message = "Usage: :export [md|html|txt|latex|pdf] [filename] (or :ex)"
3895
4156
  when "recent", "r"
3896
4157
  show_recent_files
3897
4158
  when "autosave on", "as on"
data/hyperlist.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "hyperlist"
3
- spec.version = "1.7.0"
3
+ spec.version = "1.8.1"
4
4
  spec.authors = ["Geir Isene"]
5
5
  spec.email = ["g@isene.com"]
6
6
 
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["."]
29
29
 
30
30
  # Runtime dependencies
31
- spec.add_runtime_dependency "rcurses", "~> 5.1", ">= 5.1.6"
31
+ spec.add_runtime_dependency "rcurses", "~> 6.1", ">= 6.1.5"
32
32
 
33
33
  # Development dependencies
34
34
  spec.add_development_dependency "minitest", "~> 5.0"
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.7.0
4
+ version: 1.8.1
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-22 00:00:00.000000000 Z
11
+ date: 2025-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.1'
19
+ version: '6.1'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 5.1.6
22
+ version: 6.1.5
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '5.1'
29
+ version: '6.1'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 5.1.6
32
+ version: 6.1.5
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: minitest
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -79,7 +79,6 @@ files:
79
79
  - img/screenshot_help.png
80
80
  - img/screenshot_sample.png
81
81
  - sample.hl
82
- - test_resize.hl
83
82
  homepage: https://github.com/isene/HyperList
84
83
  licenses:
85
84
  - 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