hyperlist 1.1.6 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3394f0a19dc7d9641036a62d294be9dcb4d07627e8f28abbf52be6db409b0ce
4
- data.tar.gz: 226af1d1c88dc25128f9fb881e6195bb57f4ee9ec8248320d9c12834b1275aff
3
+ metadata.gz: aef5338683dfa697e59042f093e0db9a748ef53515952a295b1c9eb972519ac5
4
+ data.tar.gz: fbad5a0f6022d40cfd147846d3de81b317b41fff39036c9085a0230fc0a144c4
5
5
  SHA512:
6
- metadata.gz: e636855dff0f0179dcb0f7eb56dc67c577f3775cd1fbf93d9c49b1812ce896e34160331146c4a6e8cea5e7fa92cafba197244210bb48b24f5abfa86e1e7c266b
7
- data.tar.gz: b5793788afef9b63fe54471687ed3f03f6f100616f2aa89eb13a5fa5e36201cbfaabac24b7683f612084bfacf3ffb827bac1bf7e363958ece2de0f32fecedc7a
6
+ metadata.gz: fd23aaaaa0ffbb7b1d73fdc99a46d3fb08d265000e1e10651e747857a4ab5d78dbf523d967e87d4ca82a1f0f8ff46d3fc563cec4441c3a86de7a3651f0cb41e6
7
+ data.tar.gz: 98ae50b53b8a9b4578ed50c81b8dcd763af2273f44bca200f27627cdc808f61b620baa0d76c09a9a776107de3de637e50c1daf660c7c77903debb8ee1868909d
data/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to the HyperList Ruby TUI will be documented in this file.
4
4
 
5
+ ## [1.2.0] - 2025-08-20
6
+
7
+ ### Added
8
+ - **User-Defined Templates**
9
+ - Save any HyperList document as a reusable template (`:st` or `:save-template`)
10
+ - Template manager for listing and deleting custom templates (`:lt`, `:dt`)
11
+ - Enhanced template browser showing both built-in and user templates
12
+ - Template metadata including description and creation date
13
+ - Templates stored in `~/.hyperlist/templates/` for easy backup and sharing
14
+ - JSON format for template storage with full hierarchy preservation
15
+
16
+ ### Changed
17
+ - Template system now supports both built-in and user-created templates
18
+ - Template browser UI improved with separate sections for built-in vs user templates
19
+ - Help documentation updated with new template commands
20
+
21
+ ## [1.1.7] - 2025-08-15
22
+
23
+ ### Fixed
24
+ - **Updated rcurses dependency to 5.1.6**
25
+ - Fixes Ruby 3.4.5 hanging issue during terminal initialization
26
+ - rcurses now handles stdin.raw! blocking with timeout and stty fallback
27
+ - Fully backward compatible
28
+
5
29
  ## [1.1.6] - 2025-08-14
6
30
 
7
31
  ### Fixed
data/README.md CHANGED
@@ -6,7 +6,8 @@
6
6
  [![GitHub stars](https://img.shields.io/github/stars/isene/HyperList.svg)](https://github.com/isene/HyperList/stargazers)
7
7
  [![Stay Amazing](https://img.shields.io/badge/Stay-Amazing-blue.svg)](https://isene.org)
8
8
 
9
- <img src="hyperlist_logo.svg" align="left" width="150" height="150">
9
+ <img src="img/hyperlist_logo.svg" align="left" width="150" height="150">
10
+ <br clear="left"/>
10
11
 
11
12
  A powerful Terminal User Interface (TUI) application for creating, editing, and managing HyperLists - a methodology for describing anything in a hierarchical, structured format.
12
13
 
@@ -23,12 +24,21 @@ For historical context and the original VIM implementation, see: [hyperlist.vim]
23
24
  ## Screenshots
24
25
 
25
26
  ### Main Interface
26
- ![HyperList Main View](screenshot_sample.png)
27
+ ![HyperList Main View](img/screenshot_sample.png)
27
28
 
28
29
  ### Help Screen
29
- ![HyperList Help](screenshot_help.png)
30
+ ![HyperList Help](img/screenshot_help.png)
31
+
32
+ ## What's New in v1.2.0
30
33
 
31
- ## What's New in v1.1.0
34
+ ### 📝 User-Defined Templates
35
+ - **Save as Template**: Save any HyperList document as a reusable template (`:st`)
36
+ - **Template Manager**: List and delete your custom templates (`:lt`, `:dt`)
37
+ - **Enhanced Template Browser**: Shows both built-in and user templates
38
+ - **Template Metadata**: Includes description and creation date
39
+ - Templates stored in `~/.hyperlist/templates/` for easy backup and sharing
40
+
41
+ ## Previous Updates (v1.1.0)
32
42
 
33
43
  ### 🔐 Encryption Support
34
44
  - **File-level encryption** for sensitive files (dot files like `.passwords.hl`)
@@ -62,7 +72,7 @@ For historical context and the original VIM implementation, see: [hyperlist.vim]
62
72
  - **Powerful Navigation**: Jump between items, references, and markers
63
73
  - **Full Editing Capabilities**: Create, edit, delete, move, and reorganize items
64
74
  - **Checkbox Support**: Multiple checkbox types with completion tracking
65
- - **Template System**: Jump to and fill in template markers
75
+ - **Template System**: Built-in templates plus save/load custom templates
66
76
  - **Presentation Mode**: Focus on current item with auto-collapse
67
77
 
68
78
  ### Security Features
@@ -158,6 +168,7 @@ hyperlist file.txt # Open any text file
158
168
  - `R` - Go to reference
159
169
  - `F` - Open file reference
160
170
  - `P` - Presentation mode (with auto-collapse)
171
+ - `t` - Insert template (built-in or custom)
161
172
  - `?` - Help screen
162
173
 
163
174
  #### File Commands
@@ -169,6 +180,12 @@ hyperlist file.txt # Open any text file
169
180
  - `:export md` - Export to Markdown
170
181
  - `:graph` - Export to PNG
171
182
 
183
+ #### Template Commands
184
+ - `:st` - Save current document as template
185
+ - `:dt` - Delete a user template
186
+ - `:lt` - List all user templates
187
+ - `t` - Browse and insert templates
188
+
172
189
  ## Examples
173
190
 
174
191
  ### Simple Todo List
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.1.6 - Terminal User Interface for HyperList files
10
+ HyperList v1.2.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.1.6"
55
+ puts "HyperList v1.2.0"
56
56
  exit 0
57
57
  end
58
58
 
@@ -64,13 +64,15 @@ require 'cgi'
64
64
  require 'openssl'
65
65
  require 'digest'
66
66
  require 'base64'
67
+ require 'fileutils'
68
+ require 'json'
67
69
 
68
70
  class HyperListApp
69
71
  include Rcurses
70
72
  include Rcurses::Input
71
73
  include Rcurses::Cursor
72
74
 
73
- VERSION = "1.1.6"
75
+ VERSION = "1.2.0"
74
76
 
75
77
  def initialize(filename = nil)
76
78
  @filename = filename ? File.expand_path(filename) : nil
@@ -98,6 +100,8 @@ class HyperListApp
98
100
  @auto_save_enabled = false
99
101
  @auto_save_interval = 60 # seconds
100
102
  @last_auto_save = Time.now
103
+ @templates_dir = File.expand_path("~/.hyperlist/templates")
104
+ ensure_templates_dir
101
105
  @templates = load_templates
102
106
  @macro_recording = false
103
107
  @macro_register = {} # Store macros by key
@@ -136,8 +140,11 @@ class HyperListApp
136
140
  end
137
141
 
138
142
  def setup_ui
139
- Rcurses.clear_screen
140
- Cursor.hide
143
+ # Only do rcurses operations if initialized
144
+ if defined?(Rcurses.instance_variable_get) && Rcurses.instance_variable_get(:@initialized)
145
+ Rcurses.clear_screen
146
+ Cursor.hide
147
+ end
141
148
 
142
149
  # Ensure we have the latest terminal size
143
150
  if IO.console
@@ -2169,7 +2176,7 @@ class HyperListApp
2169
2176
  help_lines << help_line("#{"C-E".fg("10")}", "Encrypt/decrypt line", "#{"C-U".fg("10")}", "Toggle State/Trans underline")
2170
2177
  help_lines << help_line("#{"R".fg("10")}", "Go to reference", "#{"F".fg("10")}", "Open file")
2171
2178
  help_lines << help_line("#{"N".fg("10")}", "Next = marker", "#{"P".fg("10")}", "Presentation mode")
2172
- help_lines << help_line("#{"t".fg("10")}", "Insert template", "#{":template".fg("10")}", "Show templates")
2179
+ help_lines << help_line("#{"t".fg("10")}", "Insert template", "#{":st".fg("10")}", "Save as template")
2173
2180
  help_lines << help_line("#{"Ma".fg("10")}", "Record macro 'a'", "#{"@a".fg("10")}", "Play macro 'a'")
2174
2181
  help_lines << help_line("#{":vsplit".fg("10")}", "Split view vertically", "#{"w".fg("10")}", "Switch panes")
2175
2182
  help_lines << ""
@@ -2180,6 +2187,11 @@ class HyperListApp
2180
2187
  help_lines << help_line("#{":graph :g".fg("10")}", "Export to PNG graph", "#{":vsplit :vs".fg("10")}", "Split view")
2181
2188
  help_lines << help_line("#{":as on".fg("10")}", "Enable autosave", "#{":as off".fg("10")}", "Disable autosave")
2182
2189
  help_lines << help_line("#{":as N".fg("10")}", "Set interval (secs)", "#{":as".fg("10")}", "Show autosave status")
2190
+ help_lines << ""
2191
+ help_lines << "#{"TEMPLATES".fg("14")}"
2192
+ help_lines << help_line("#{":st".fg("10")}", "Save as template", "#{":dt".fg("10")}", "Delete template")
2193
+ help_lines << help_line("#{":lt".fg("10")}", "List user templates", "#{"t".fg("10")}", "Insert template")
2194
+ help_lines << ""
2183
2195
  help_lines << help_line("#{"q".fg("10")}", "Quit (asks to save)", "#{"Q".fg("10")}", "Force quit")
2184
2196
  help_lines << ""
2185
2197
  help_lines << "#{"COLOR SCHEME".fg("14")}"
@@ -2853,8 +2865,18 @@ class HyperListApp
2853
2865
  when "autosave", "as"
2854
2866
  status = @auto_save_enabled ? "enabled" : "disabled"
2855
2867
  @message = "Auto-save is #{status} (interval: #{@auto_save_interval}s)"
2856
- when "t"
2868
+ when "t", "template", "templates"
2857
2869
  show_templates
2870
+ when "save-template", "savetemplate", "st"
2871
+ save_as_template
2872
+ when /^save-template\s+(.+)/, /^savetemplate\s+(.+)/, /^st\s+(.+)/
2873
+ save_as_template($1.strip)
2874
+ when "delete-template", "deletetemplate", "dt"
2875
+ delete_template
2876
+ when /^delete-template\s+(.+)/, /^deletetemplate\s+(.+)/, /^dt\s+(.+)/
2877
+ delete_template($1.strip)
2878
+ when "list-templates", "listtemplates", "lt"
2879
+ show_template_manager(:list)
2858
2880
  when "foldlevel"
2859
2881
  level = @footer.ask("Fold to level (0-9): ", "")
2860
2882
  if level =~ /^[0-9]$/
@@ -3202,8 +3224,13 @@ class HyperListApp
3202
3224
  end
3203
3225
  end
3204
3226
 
3227
+ def ensure_templates_dir
3228
+ FileUtils.mkdir_p(@templates_dir) unless File.exist?(@templates_dir)
3229
+ end
3230
+
3205
3231
  def load_templates
3206
- {
3232
+ # Start with built-in templates
3233
+ templates = {
3207
3234
  "project" => [
3208
3235
  {"text" => "Project: =Project Name=", "level" => 0},
3209
3236
  {"text" => "[_] Define project scope", "level" => 1},
@@ -3306,6 +3333,214 @@ class HyperListApp
3306
3333
  {"text" => "=Tips, variations, serving suggestions=", "level" => 2}
3307
3334
  ]
3308
3335
  }
3336
+
3337
+ # Load user templates from templates directory
3338
+ if File.directory?(@templates_dir)
3339
+ Dir.glob(File.join(@templates_dir, "*.hlt")).each do |template_file|
3340
+ template_name = File.basename(template_file, ".hlt")
3341
+ begin
3342
+ template_data = JSON.parse(File.read(template_file))
3343
+ # Convert template data to the expected format
3344
+ if template_data.is_a?(Hash) && template_data["items"]
3345
+ templates[template_name] = template_data["items"].map do |item|
3346
+ {"text" => item["text"], "level" => item["level"]}
3347
+ end
3348
+ end
3349
+ rescue => e
3350
+ # Skip invalid template files
3351
+ end
3352
+ end
3353
+ end
3354
+
3355
+ templates
3356
+ end
3357
+
3358
+ def save_as_template(name = nil)
3359
+ # Ask for template name if not provided
3360
+ if name.nil? || name.empty?
3361
+ name = @footer.ask("Template name: ", "")
3362
+ return if name.nil? || name.empty?
3363
+ end
3364
+
3365
+ # Sanitize template name
3366
+ name = name.gsub(/[^a-zA-Z0-9_-]/, '_')
3367
+
3368
+ # Ask for description
3369
+ description = @footer.ask("Template description (optional): ", "")
3370
+
3371
+ # Prepare template data
3372
+ template_data = {
3373
+ "name" => name,
3374
+ "description" => description,
3375
+ "created" => Time.now.strftime("%Y-%m-%dT%H:%M:%S"),
3376
+ "items" => @items.map do |item|
3377
+ {
3378
+ "text" => item["text"],
3379
+ "level" => item["level"]
3380
+ }
3381
+ end
3382
+ }
3383
+
3384
+ # Save template file
3385
+ template_path = File.join(@templates_dir, "#{name}.hlt")
3386
+
3387
+ # Check if template already exists
3388
+ if File.exist?(template_path)
3389
+ response = @footer.ask("Template '#{name}' already exists. Overwrite? (y/n): ", "")
3390
+ return unless response.downcase == 'y'
3391
+ end
3392
+
3393
+ File.write(template_path, JSON.pretty_generate(template_data))
3394
+ @message = "Template '#{name}' saved successfully"
3395
+ end
3396
+
3397
+ def delete_template(name = nil)
3398
+ # Get list of user templates
3399
+ user_templates = get_user_templates
3400
+
3401
+ if user_templates.empty?
3402
+ @message = "No user templates found"
3403
+ return
3404
+ end
3405
+
3406
+ # Show template list if name not provided
3407
+ if name.nil? || name.empty?
3408
+ show_template_manager(:delete)
3409
+ return
3410
+ end
3411
+
3412
+ template_path = File.join(@templates_dir, "#{name}.hlt")
3413
+
3414
+ if File.exist?(template_path)
3415
+ response = @footer.ask("Delete template '#{name}'? (y/n): ", "")
3416
+ if response.downcase == 'y'
3417
+ File.delete(template_path)
3418
+ @message = "Template '#{name}' deleted"
3419
+ # Reload templates
3420
+ @templates = load_templates
3421
+ end
3422
+ else
3423
+ @message = "Template '#{name}' not found"
3424
+ end
3425
+ end
3426
+
3427
+ def get_user_templates
3428
+ templates = []
3429
+ if File.directory?(@templates_dir)
3430
+ Dir.glob(File.join(@templates_dir, "*.hlt")).each do |template_file|
3431
+ template_name = File.basename(template_file, ".hlt")
3432
+ begin
3433
+ template_data = JSON.parse(File.read(template_file))
3434
+ templates << {
3435
+ "name" => template_name,
3436
+ "description" => template_data["description"] || "",
3437
+ "created" => template_data["created"] || "",
3438
+ "path" => template_file
3439
+ }
3440
+ rescue => e
3441
+ # Skip invalid template files
3442
+ end
3443
+ end
3444
+ end
3445
+ templates.sort_by { |t| t["name"] }
3446
+ end
3447
+
3448
+ def show_template_manager(action = :list)
3449
+ # Store original state
3450
+ original_state = {
3451
+ items: @items.dup,
3452
+ current: @current,
3453
+ offset: @offset,
3454
+ filename: @filename,
3455
+ modified: @modified
3456
+ }
3457
+
3458
+ user_templates = get_user_templates
3459
+
3460
+ # Build template manager view
3461
+ @items = []
3462
+ case action
3463
+ when :delete
3464
+ @items << {"text" => "DELETE TEMPLATE (press Enter to delete, q to cancel)", "level" => 0, "fold" => false, "raw" => true}
3465
+ else
3466
+ @items << {"text" => "USER TEMPLATES (press Enter to select, q to cancel)", "level" => 0, "fold" => false, "raw" => true}
3467
+ end
3468
+ @items << {"text" => "="*50, "level" => 0, "fold" => false, "raw" => true}
3469
+
3470
+ if user_templates.empty?
3471
+ @items << {"text" => "No user templates found", "level" => 0, "fold" => false, "raw" => true}
3472
+ else
3473
+ user_templates.each_with_index do |template, idx|
3474
+ desc = template["description"].empty? ? "No description" : template["description"]
3475
+ created = template["created"].empty? ? "" : " (#{Time.parse(template["created"]).strftime('%Y-%m-%d')})"
3476
+ @items << {
3477
+ "text" => "#{idx+1}. #{template["name"]}: #{desc}#{created}",
3478
+ "level" => 0,
3479
+ "fold" => false,
3480
+ "raw" => true,
3481
+ "template_name" => template["name"],
3482
+ "template_action" => action
3483
+ }
3484
+ end
3485
+ end
3486
+
3487
+ @current = 2 # Start at first template
3488
+ @offset = 0
3489
+
3490
+ selected_template = nil
3491
+ exit_loop = false
3492
+
3493
+ # Template manager loop
3494
+ while !exit_loop
3495
+ begin
3496
+ render_main
3497
+ footer_text = case action
3498
+ when :delete
3499
+ "Delete Template | Enter: delete | q: cancel | j/k: navigate"
3500
+ else
3501
+ "User Templates | Enter: select | q: cancel | j/k: navigate"
3502
+ end
3503
+ @footer.text = footer_text
3504
+ @footer.refresh
3505
+
3506
+ c = getchr
3507
+ next if c.nil?
3508
+
3509
+ case c
3510
+ when "q", "ESC", "C-c", "Q"
3511
+ exit_loop = true
3512
+ when "j", "DOWN"
3513
+ @current = [@current + 1, @items.length - 1].min
3514
+ when "k", "UP"
3515
+ @current = [@current - 1, 2].max
3516
+ when "ENTER", "RETURN", "\n", "\r"
3517
+ if @current >= 2 && @current < @items.length
3518
+ item = @items[@current]
3519
+ if item && item.is_a?(Hash) && item["template_name"]
3520
+ selected_template = item["template_name"]
3521
+ exit_loop = true
3522
+ end
3523
+ end
3524
+ end
3525
+ rescue => e
3526
+ @message = "Error in template manager: #{e.message}"
3527
+ exit_loop = true
3528
+ end
3529
+ end
3530
+
3531
+ # Restore original state
3532
+ @items = original_state[:items]
3533
+ @current = original_state[:current]
3534
+ @offset = original_state[:offset]
3535
+ @filename = original_state[:filename]
3536
+ @modified = original_state[:modified]
3537
+
3538
+ # Process selected template
3539
+ if selected_template && action == :delete
3540
+ delete_template(selected_template)
3541
+ end
3542
+
3543
+ selected_template
3309
3544
  end
3310
3545
 
3311
3546
  def show_templates
@@ -3318,24 +3553,33 @@ class HyperListApp
3318
3553
  modified: @modified
3319
3554
  }
3320
3555
 
3321
- # Create template selection view
3556
+ # Create template selection view combining built-in and user templates
3322
3557
  template_list = [
3323
- ["project", "Project Plan - Complete project management template"],
3324
- ["meeting", "Meeting Agenda - Structure for meeting notes"],
3325
- ["daily", "Daily Planner - Daily task and schedule template"],
3326
- ["checklist", "Simple Checklist - Basic checkbox list"],
3327
- ["brainstorm", "Brainstorming Session - Idea generation template"],
3328
- ["recipe", "Recipe - Cooking recipe structure"]
3558
+ ["project", "Project Plan - Complete project management template", "built-in"],
3559
+ ["meeting", "Meeting Agenda - Structure for meeting notes", "built-in"],
3560
+ ["daily", "Daily Planner - Daily task and schedule template", "built-in"],
3561
+ ["checklist", "Simple Checklist - Basic checkbox list", "built-in"],
3562
+ ["brainstorm", "Brainstorming Session - Idea generation template", "built-in"],
3563
+ ["recipe", "Recipe - Cooking recipe structure", "built-in"]
3329
3564
  ]
3330
3565
 
3566
+ # Add user templates to the list
3567
+ user_templates = get_user_templates
3568
+ user_templates.each do |template|
3569
+ desc = template["description"].empty? ? "User template" : template["description"]
3570
+ template_list << [template["name"], desc, "user"]
3571
+ end
3572
+
3331
3573
  # Build template selection items
3332
3574
  @items = []
3333
3575
  @items << {"text" => "TEMPLATES (press Enter to insert, q to cancel)", "level" => 0, "fold" => false, "raw" => true}
3334
3576
  @items << {"text" => "="*50, "level" => 0, "fold" => false, "raw" => true}
3335
3577
 
3336
- template_list.each_with_index do |(key, desc), idx|
3578
+ # Add built-in templates section
3579
+ @items << {"text" => "BUILT-IN TEMPLATES:", "level" => 0, "fold" => false, "raw" => true}
3580
+ template_list.select { |t| t[2] == "built-in" }.each_with_index do |(key, desc, type), idx|
3337
3581
  @items << {
3338
- "text" => "#{idx+1}. #{key.capitalize}: #{desc}",
3582
+ "text" => " #{idx+1}. #{key.capitalize}: #{desc}",
3339
3583
  "level" => 0,
3340
3584
  "fold" => false,
3341
3585
  "raw" => true,
@@ -3343,6 +3587,21 @@ class HyperListApp
3343
3587
  }
3344
3588
  end
3345
3589
 
3590
+ # Add user templates section if any exist
3591
+ if user_templates.any?
3592
+ @items << {"text" => "", "level" => 0, "fold" => false, "raw" => true}
3593
+ @items << {"text" => "USER TEMPLATES:", "level" => 0, "fold" => false, "raw" => true}
3594
+ template_list.select { |t| t[2] == "user" }.each_with_index do |(key, desc, type), idx|
3595
+ @items << {
3596
+ "text" => " #{key}: #{desc}",
3597
+ "level" => 0,
3598
+ "fold" => false,
3599
+ "raw" => true,
3600
+ "template_key" => key
3601
+ }
3602
+ end
3603
+ end
3604
+
3346
3605
  @current = 2 # Start at first template
3347
3606
  @offset = 0
3348
3607
 
@@ -4252,6 +4511,19 @@ class HyperListApp
4252
4511
  end
4253
4512
 
4254
4513
  def run
4514
+ # Initialize rcurses explicitly (no longer auto-initialized)
4515
+ Rcurses.init!
4516
+
4517
+ # Flush any buffered input (like the Enter key from running the command)
4518
+ while IO.select([$stdin], nil, nil, 0)
4519
+ $stdin.read_nonblock(1024) rescue break
4520
+ end
4521
+
4522
+ # Now that rcurses is initialized, set up the UI properly
4523
+ Rcurses.clear_screen
4524
+ Cursor.hide
4525
+ setup_ui
4526
+
4255
4527
  render
4256
4528
 
4257
4529
  loop do
@@ -4260,6 +4532,14 @@ class HyperListApp
4260
4532
 
4261
4533
  c = getchr
4262
4534
 
4535
+ # Skip nil input (shouldn't happen normally)
4536
+ next if c.nil?
4537
+
4538
+ # Skip newline at the very start (buffered from command execution)
4539
+ if c == "\n" && @last_key.nil?
4540
+ next
4541
+ end
4542
+
4263
4543
  # Track last key for double-key combinations
4264
4544
  prev_key = @last_key
4265
4545
  @last_key = c
@@ -4550,40 +4830,15 @@ class HyperListApp
4550
4830
  quit
4551
4831
  ensure
4552
4832
  Cursor.show
4553
- Rcurses.clear_screen
4833
+ # Screen clearing disabled for debugging
4834
+ # Rcurses.clear_screen
4554
4835
  end
4555
4836
  end
4556
4837
 
4557
4838
  # Main
4558
- if __FILE__ == $0 && !defined?($hyperlist_wrapper_mode)
4559
- # Normal operation - help/version already handled at top of file
4560
-
4561
- # For Ruby 3.4+, explicitly initialize rcurses if needed
4562
- if RUBY_VERSION >= "3.4.0"
4563
- begin
4564
- Rcurses.init! unless Rcurses.respond_to?(:initialized?) && Rcurses.initialized?
4565
- rescue => e
4566
- # If initialization fails, try to provide helpful error
4567
- puts "Failed to initialize terminal interface: #{e.message}"
4568
- puts "Try running with: ruby #{__FILE__} #{ARGV.join(' ')}"
4569
- exit 1
4570
- end
4571
- end
4572
-
4839
+ # When installed as a gem executable, this file is loaded (not executed directly)
4840
+ # so we can't use __FILE__ == $0. Just run unless we already handled help/version.
4841
+ unless ARGV[0] == '-h' || ARGV[0] == '--help' || ARGV[0] == '-v' || ARGV[0] == '--version'
4573
4842
  app = HyperListApp.new(ARGV[0])
4574
-
4575
- begin
4576
- app.run
4577
- ensure
4578
- # Ensure proper cleanup for Ruby 3.4+
4579
- if RUBY_VERSION >= "3.4.0"
4580
- begin
4581
- Rcurses.done! if defined?(Rcurses.done!)
4582
- rescue
4583
- # Fallback cleanup
4584
- print "\e[?25h" # Show cursor
4585
- system("stty sane 2>/dev/null")
4586
- end
4587
- end
4588
- end
4843
+ app.run
4589
4844
  end
data/hyperlist.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |spec|
2
2
  spec.name = "hyperlist"
3
- spec.version = "1.1.6"
3
+ spec.version = "1.2.0"
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.5"
31
+ spec.add_runtime_dependency "rcurses", "~> 5.1", ">= 5.1.6"
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.1.6
4
+ version: 1.2.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-08-14 00:00:00.000000000 Z
11
+ date: 2025-08-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '5.1'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 5.1.5
22
+ version: 5.1.6
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '5.1'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 5.1.5
32
+ version: 5.1.6
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: minitest
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -73,18 +73,12 @@ files:
73
73
  - CHANGELOG.md
74
74
  - LICENSE
75
75
  - README.md
76
- - debug_instructions.md
77
- - diagnose.rb
78
- - diagnose_ruby34.rb
79
- - diagnose_safe.rb
80
- - fix_terminal.sh
81
76
  - hyperlist
82
77
  - hyperlist.gemspec
83
- - hyperlist_logo.svg
84
- - hyperlist_ruby34_wrapper.rb
78
+ - img/hyperlist_logo.svg
79
+ - img/screenshot_help.png
80
+ - img/screenshot_sample.png
85
81
  - sample.hl
86
- - screenshot_help.png
87
- - screenshot_sample.png
88
82
  - test.hl
89
83
  homepage: https://github.com/isene/HyperList
90
84
  licenses:
@@ -1,112 +0,0 @@
1
- # Debugging HyperList Issues
2
-
3
- ## For the user experiencing startup crashes:
4
-
5
- ### 1. First, verify you have the latest versions:
6
-
7
- ```bash
8
- gem list hyperlist
9
- # Should show: hyperlist (1.1.3)
10
-
11
- gem list rcurses
12
- # Should show: rcurses (5.1.4) or higher
13
- ```
14
-
15
- ### 2. If not, update to the latest:
16
-
17
- ```bash
18
- gem update hyperlist
19
- gem update rcurses
20
- ```
21
-
22
- ### 3. Run with debug mode to see errors:
23
-
24
- ```bash
25
- # Set debug environment variable
26
- export RCURSES_DEBUG=1
27
- export DEBUG=1
28
-
29
- # Then run hyperlist
30
- hyperlist
31
-
32
- # Or run in one line:
33
- DEBUG=1 RCURSES_DEBUG=1 hyperlist
34
- ```
35
-
36
- ### 4. Alternative: Run with Ruby directly to see errors:
37
-
38
- ```bash
39
- # Find where hyperlist is installed
40
- which hyperlist
41
-
42
- # Run it directly with Ruby to see any errors
43
- ruby $(which hyperlist)
44
- ```
45
-
46
- ### 5. Check Ruby version compatibility:
47
-
48
- ```bash
49
- ruby --version
50
- # HyperList requires Ruby 3.0.0 or higher
51
- ```
52
-
53
- ### 6. Try running with verbose Ruby output:
54
-
55
- ```bash
56
- ruby -w $(which hyperlist)
57
- ```
58
-
59
- ### 7. Check for missing dependencies:
60
-
61
- ```bash
62
- # This will show if rcurses is properly installed
63
- ruby -e "require 'rcurses'; puts 'rcurses loaded successfully'"
64
- ```
65
-
66
- ### 8. Create a simple test file to isolate the issue:
67
-
68
- Create a file called `test_hyperlist.rb`:
69
-
70
- ```ruby
71
- #!/usr/bin/env ruby
72
-
73
- puts "Ruby version: #{RUBY_VERSION}"
74
- puts "Testing rcurses..."
75
-
76
- begin
77
- require 'rcurses'
78
- puts "✓ rcurses loaded successfully (version from gem)"
79
- rescue LoadError => e
80
- puts "✗ Failed to load rcurses: #{e.message}"
81
- exit 1
82
- end
83
-
84
- puts "\nTesting basic rcurses functionality..."
85
- begin
86
- include Rcurses
87
- puts "✓ Rcurses module included"
88
- puts "✓ All basic checks passed!"
89
- rescue => e
90
- puts "✗ Error: #{e.message}"
91
- exit 1
92
- end
93
- ```
94
-
95
- Run it with:
96
- ```bash
97
- ruby test_hyperlist.rb
98
- ```
99
-
100
- ### 9. If all else fails, capture output:
101
-
102
- ```bash
103
- # Run with script to capture all output
104
- script -c "hyperlist" hyperlist_output.txt
105
-
106
- # Then send us the hyperlist_output.txt file
107
- ```
108
-
109
- ## Notes:
110
- - GDB won't work with Ruby scripts (it's for compiled binaries)
111
- - The new error handling in rcurses 5.1.4+ should show errors after terminal cleanup
112
- - Setting DEBUG=1 or RCURSES_DEBUG=1 will show full stack traces
data/diagnose.rb DELETED
@@ -1,115 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # HyperList Diagnostic Script
3
- # Run this to help diagnose installation/startup issues
4
-
5
- puts "=" * 60
6
- puts "HyperList Diagnostic Report"
7
- puts "=" * 60
8
-
9
- # Check Ruby version
10
- puts "\n1. Ruby Version:"
11
- puts " #{RUBY_VERSION} (#{RUBY_PLATFORM})"
12
- if RUBY_VERSION < "3.0.0"
13
- puts " ⚠️ WARNING: HyperList requires Ruby 3.0.0 or higher"
14
- else
15
- puts " ✓ Ruby version OK"
16
- end
17
-
18
- # Check gem versions
19
- puts "\n2. Gem Versions:"
20
- begin
21
- hyperlist_version = `gem list hyperlist --local`.match(/hyperlist \(([\d.]+)\)/)[1] rescue "not found"
22
- rcurses_version = `gem list rcurses --local`.match(/rcurses \(([\d.]+(?:,\s*[\d.]+)*)\)/)[1] rescue "not found"
23
-
24
- puts " hyperlist: #{hyperlist_version}"
25
- if hyperlist_version >= "1.1.3"
26
- puts " ✓ HyperList version OK"
27
- else
28
- puts " ⚠️ Please update: gem update hyperlist"
29
- end
30
-
31
- puts " rcurses: #{rcurses_version}"
32
- if rcurses_version.include?("5.1.4") || rcurses_version >= "5.1.4"
33
- puts " ✓ rcurses version OK"
34
- else
35
- puts " ⚠️ Please update: gem update rcurses"
36
- end
37
- rescue => e
38
- puts " Error checking gems: #{e.message}"
39
- end
40
-
41
- # Test loading rcurses
42
- puts "\n3. Testing rcurses load:"
43
- begin
44
- require 'rcurses'
45
- puts " ✓ rcurses loaded successfully"
46
-
47
- # Check if we can access rcurses modules
48
- include Rcurses
49
- puts " ✓ Rcurses modules accessible"
50
- rescue LoadError => e
51
- puts " ✗ Failed to load rcurses: #{e.message}"
52
- puts " Try: gem install rcurses"
53
- rescue => e
54
- puts " ✗ Error with rcurses: #{e.message}"
55
- end
56
-
57
- # Test terminal capabilities
58
- puts "\n4. Terminal Check:"
59
- puts " TERM: #{ENV['TERM'] || 'not set'}"
60
- puts " TTY: #{$stdin.tty? ? 'Yes' : 'No'}"
61
- puts " Encoding: #{Encoding.default_external}"
62
-
63
- # Check for HyperList executable
64
- puts "\n5. HyperList Installation:"
65
- hyperlist_path = `which hyperlist`.strip
66
- if hyperlist_path.empty?
67
- puts " ✗ hyperlist command not found in PATH"
68
- puts " Try: gem install hyperlist"
69
- else
70
- puts " ✓ Found at: #{hyperlist_path}"
71
-
72
- # Check if it's executable
73
- if File.executable?(hyperlist_path)
74
- puts " ✓ File is executable"
75
- else
76
- puts " ⚠️ File is not executable"
77
- puts " Try: chmod +x #{hyperlist_path}"
78
- end
79
- end
80
-
81
- # Try to load core dependencies
82
- puts "\n6. Core Dependencies:"
83
- deps = ['io/console', 'date', 'cgi', 'openssl', 'digest', 'base64']
84
- deps.each do |dep|
85
- begin
86
- require dep
87
- puts " ✓ #{dep}"
88
- rescue LoadError => e
89
- puts " ✗ #{dep}: #{e.message}"
90
- end
91
- end
92
-
93
- # Test creating a basic rcurses pane (without initializing terminal)
94
- puts "\n7. Testing Rcurses Pane Creation (simulation):"
95
- begin
96
- # Don't actually initialize the terminal, just test the class exists
97
- if defined?(Rcurses::Pane)
98
- puts " ✓ Rcurses::Pane class exists"
99
- else
100
- puts " ✗ Rcurses::Pane class not found"
101
- end
102
- rescue => e
103
- puts " ✗ Error: #{e.message}"
104
- end
105
-
106
- puts "\n" + "=" * 60
107
- puts "Diagnostic complete!"
108
- puts "=" * 60
109
-
110
- puts "\nIf everything shows ✓ but hyperlist still crashes:"
111
- puts "1. Run with debug mode: DEBUG=1 RCURSES_DEBUG=1 hyperlist"
112
- puts "2. Check for error log: cat ~/.hyperlist_error.log"
113
- puts "3. Try running directly: ruby #{hyperlist_path}"
114
-
115
- puts "\nPlease share this diagnostic output when reporting issues."
data/diagnose_ruby34.rb DELETED
@@ -1,145 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # Diagnostic script for Ruby 3.4.5 compatibility issues
3
-
4
- puts "Ruby Compatibility Diagnostic for HyperList"
5
- puts "=" * 50
6
- puts "Ruby version: #{RUBY_VERSION}"
7
- puts "Ruby platform: #{RUBY_PLATFORM}"
8
- puts
9
-
10
- # Test 1: Basic rcurses functionality
11
- puts "Test 1: Basic rcurses require and init..."
12
- begin
13
- require 'rcurses'
14
- puts " ✓ rcurses loaded successfully"
15
- puts " Rcurses version: #{Rcurses::VERSION}" if defined?(Rcurses::VERSION)
16
- rescue LoadError => e
17
- puts " ✗ Failed to load rcurses: #{e.message}"
18
- exit 1
19
- end
20
-
21
- # Test 2: Rcurses initialization with error capture
22
- puts "\nTest 2: Rcurses initialization..."
23
- begin
24
- # Capture any output during init
25
- original_stdout = $stdout
26
- original_stderr = $stderr
27
-
28
- # Create StringIO to capture output
29
- require 'stringio'
30
- captured_out = StringIO.new
31
- captured_err = StringIO.new
32
-
33
- $stdout = captured_out
34
- $stderr = captured_err
35
-
36
- # Try to initialize
37
- Rcurses.init!
38
-
39
- # Restore output
40
- $stdout = original_stdout
41
- $stderr = original_stderr
42
-
43
- # Check if anything was captured
44
- out_content = captured_out.string
45
- err_content = captured_err.string
46
-
47
- if !out_content.empty?
48
- puts " Captured stdout during init: #{out_content.inspect}"
49
- end
50
- if !err_content.empty?
51
- puts " Captured stderr during init: #{err_content.inspect}"
52
- end
53
-
54
- puts " ✓ Rcurses initialized"
55
-
56
- # Test basic functionality
57
- puts " Testing basic screen operations..."
58
- print "\e[2J\e[H" # Clear screen
59
- print "Test"
60
- sleep 0.1
61
- print "\e[2J\e[H" # Clear again
62
-
63
- puts " ✓ Basic operations work"
64
-
65
- rescue => e
66
- puts " ✗ Failed during initialization: #{e.message}"
67
- puts " Backtrace:"
68
- e.backtrace.first(5).each { |line| puts " #{line}" }
69
- ensure
70
- # Try to restore terminal
71
- begin
72
- Rcurses.done! if defined?(Rcurses.done!)
73
- rescue
74
- # Fallback terminal restore
75
- print "\e[?25h" # Show cursor
76
- system("stty sane 2>/dev/null")
77
- end
78
- end
79
-
80
- # Test 3: Check for method availability issues in Ruby 3.4
81
- puts "\nTest 3: Ruby 3.4 specific checks..."
82
-
83
- # Check IO methods that might have changed
84
- if IO.respond_to?(:console)
85
- puts " ✓ IO.console available"
86
- else
87
- puts " ✗ IO.console not available"
88
- end
89
-
90
- begin
91
- require 'io/console'
92
- $stdin.raw { }
93
- puts " ✓ IO#raw method works"
94
- rescue => e
95
- puts " ✗ IO#raw failed: #{e.message}"
96
- end
97
-
98
- # Test 4: Check encoding
99
- puts "\nTest 4: Encoding checks..."
100
- puts " Default external: #{Encoding.default_external}"
101
- puts " Default internal: #{Encoding.default_internal}"
102
- puts " Console encoding: #{$stdout.external_encoding}" if $stdout.respond_to?(:external_encoding)
103
-
104
- # Test 5: Simple hyperlist initialization test
105
- puts "\nTest 5: HyperList class initialization..."
106
- begin
107
- # Load just the class definition part
108
- hyperlist_code = File.read(File.join(File.dirname(__FILE__), 'hyperlist'))
109
-
110
- # Extract just the class without running main
111
- class_only = hyperlist_code.split(/^if __FILE__ == \$0/)[0]
112
-
113
- # Try to evaluate it
114
- eval(class_only)
115
-
116
- puts " ✓ HyperList class loaded"
117
-
118
- # Try to create instance (without running)
119
- app = HyperListApp.new
120
- puts " ✓ HyperListApp instance created"
121
-
122
- rescue => e
123
- puts " ✗ Failed to load HyperList: #{e.message}"
124
- puts " Error at: #{e.backtrace.first}"
125
- end
126
-
127
- # Test 6: Terminal capability check
128
- puts "\nTest 6: Terminal capabilities..."
129
- puts " TERM: #{ENV['TERM']}"
130
- puts " Columns: #{`tput cols`.strip}" rescue nil
131
- puts " Lines: #{`tput lines`.strip}" rescue nil
132
-
133
- # Test 7: Check for signal handling changes
134
- puts "\nTest 7: Signal handling..."
135
- begin
136
- old_handler = Signal.trap("WINCH") { }
137
- Signal.trap("WINCH", old_handler)
138
- puts " ✓ SIGWINCH trap works"
139
- rescue => e
140
- puts " ✗ SIGWINCH trap failed: #{e.message}"
141
- end
142
-
143
- puts "\n" + "=" * 50
144
- puts "Diagnostic complete!"
145
- puts "\nPlease share this output to help identify the Ruby 3.4.5 compatibility issue."
data/diagnose_safe.rb DELETED
@@ -1,198 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # HyperList Safe Diagnostic Script
3
- # This version ensures terminal remains in a sane state
4
-
5
- # First, ensure terminal is in sane state
6
- system("stty sane 2>/dev/null")
7
-
8
- # Ensure we're not in raw mode and output is flushed properly
9
- $stdout.sync = true
10
- $stderr.sync = true
11
-
12
- # Don't load rcurses until we explicitly test it
13
- puts "=" * 60
14
- puts "HyperList Safe Diagnostic Report"
15
- puts "=" * 60
16
-
17
- # Check Ruby version
18
- puts "\n1. Ruby Version:"
19
- puts " #{RUBY_VERSION} (#{RUBY_PLATFORM})"
20
- if RUBY_VERSION < "3.0.0"
21
- puts " WARNING: HyperList requires Ruby 3.0.0 or higher"
22
- elsif RUBY_VERSION >= "3.4.0"
23
- puts " WARNING: Ruby 3.4+ is a development version and may have compatibility issues"
24
- puts " RECOMMENDED: Use Ruby 3.3.x for stability"
25
- else
26
- puts " OK: Ruby version supported"
27
- end
28
-
29
- # Check gem versions WITHOUT loading them
30
- puts "\n2. Installed Gem Versions:"
31
- begin
32
- hyperlist_output = `gem list hyperlist --local 2>&1`
33
- rcurses_output = `gem list rcurses --local 2>&1`
34
-
35
- if hyperlist_output.include?("hyperlist")
36
- version = hyperlist_output.match(/hyperlist \(([\d.]+)/)[1] rescue "unknown"
37
- puts " hyperlist: #{version}"
38
- if version >= "1.1.3"
39
- puts " OK: HyperList version is current"
40
- else
41
- puts " UPDATE NEEDED: gem update hyperlist"
42
- end
43
- else
44
- puts " hyperlist: NOT INSTALLED"
45
- puts " Run: gem install hyperlist"
46
- end
47
-
48
- if rcurses_output.include?("rcurses")
49
- # Extract just the first/latest version
50
- version = rcurses_output.match(/rcurses \(([\d.]+)/)[1] rescue "unknown"
51
- puts " rcurses: #{version}"
52
- if version >= "5.1.4"
53
- puts " OK: rcurses version is current"
54
- else
55
- puts " UPDATE NEEDED: gem update rcurses"
56
- end
57
- else
58
- puts " rcurses: NOT INSTALLED"
59
- puts " Run: gem install rcurses"
60
- end
61
- rescue => e
62
- puts " Error checking gems: #{e.message}"
63
- end
64
-
65
- # Check terminal environment
66
- puts "\n3. Terminal Environment:"
67
- puts " TERM: #{ENV['TERM'] || 'not set'}"
68
- puts " LANG: #{ENV['LANG'] || 'not set'}"
69
- puts " TTY: #{$stdin.tty? ? 'Yes' : 'No'}"
70
- puts " Terminal columns: #{`tput cols`.strip rescue 'unknown'}"
71
- puts " Terminal rows: #{`tput lines`.strip rescue 'unknown'}"
72
-
73
- # Check for HyperList executable
74
- puts "\n4. HyperList Executable:"
75
- hyperlist_path = `which hyperlist 2>/dev/null`.strip
76
- if hyperlist_path.empty?
77
- puts " NOT FOUND in PATH"
78
-
79
- # Check common gem bin paths
80
- gem_paths = [
81
- "#{ENV['HOME']}/.local/share/gem/ruby/*/bin/hyperlist",
82
- "#{ENV['HOME']}/.gem/ruby/*/bin/hyperlist",
83
- "/usr/local/bin/hyperlist",
84
- "/usr/bin/hyperlist"
85
- ]
86
-
87
- found = false
88
- gem_paths.each do |pattern|
89
- Dir.glob(pattern).each do |path|
90
- if File.exist?(path)
91
- puts " Found at: #{path}"
92
- puts " Add to PATH: export PATH=\"#{File.dirname(path)}:$PATH\""
93
- found = true
94
- break
95
- end
96
- end
97
- break if found
98
- end
99
- else
100
- puts " Found at: #{hyperlist_path}"
101
- if File.executable?(hyperlist_path)
102
- puts " OK: File is executable"
103
- else
104
- puts " ERROR: File is not executable"
105
- puts " Fix: chmod +x #{hyperlist_path}"
106
- end
107
- end
108
-
109
- # Test basic dependencies WITHOUT loading rcurses
110
- puts "\n5. Core Ruby Dependencies:"
111
- deps = ['io/console', 'date', 'cgi', 'openssl', 'digest', 'base64']
112
- deps.each do |dep|
113
- begin
114
- require dep
115
- puts " OK: #{dep}"
116
- rescue LoadError => e
117
- puts " MISSING: #{dep} - #{e.message}"
118
- end
119
- end
120
-
121
- # Separate rcurses test with isolation
122
- puts "\n6. Testing rcurses (isolated):"
123
- puts " Running isolated test..."
124
-
125
- # Create a separate test script to isolate rcurses
126
- test_script = <<~RUBY
127
- begin
128
- require 'rcurses'
129
- puts "RCURSES_LOAD:OK"
130
-
131
- # Check if main classes exist without initializing
132
- if defined?(Rcurses::Pane)
133
- puts "RCURSES_PANE:OK"
134
- else
135
- puts "RCURSES_PANE:FAIL"
136
- end
137
- rescue LoadError => e
138
- puts "RCURSES_LOAD:FAIL:\#{e.message}"
139
- rescue => e
140
- puts "RCURSES_ERROR:\#{e.class}:\#{e.message}"
141
- end
142
- RUBY
143
-
144
- # Run test in subprocess to avoid terminal corruption
145
- result = `ruby -e "#{test_script}" 2>&1`
146
-
147
- if result.include?("RCURSES_LOAD:OK")
148
- puts " OK: rcurses loads successfully"
149
- else
150
- puts " ERROR: rcurses failed to load"
151
- error = result.match(/RCURSES_LOAD:FAIL:(.+)/)
152
- puts " Details: #{error[1]}" if error
153
- end
154
-
155
- if result.include?("RCURSES_PANE:OK")
156
- puts " OK: Rcurses::Pane class available"
157
- else
158
- puts " ERROR: Rcurses::Pane class not found"
159
- end
160
-
161
- if result.include?("RCURSES_ERROR")
162
- error = result.match(/RCURSES_ERROR:(.+):(.+)/)
163
- puts " ERROR: #{error[1]} - #{error[2]}" if error
164
- end
165
-
166
- # Test terminal sanity
167
- puts "\n7. Terminal State Check:"
168
- system("stty -a > /dev/null 2>&1")
169
- if $?.success?
170
- puts " OK: Terminal responds to stty"
171
- else
172
- puts " ERROR: Terminal not responding properly"
173
- end
174
-
175
- # Try to restore terminal just in case
176
- system("stty sane 2>/dev/null")
177
- system("tput reset 2>/dev/null")
178
-
179
- puts "\n" + "=" * 60
180
- puts "Diagnostic complete!"
181
- puts "=" * 60
182
-
183
- puts "\nRECOMMENDATIONS:"
184
- if RUBY_VERSION >= "3.4.0"
185
- puts "1. IMPORTANT: You're using Ruby #{RUBY_VERSION} (development version)"
186
- puts " Install Ruby 3.3.x for stability:"
187
- puts " - Arch Linux: sudo pacman -S ruby"
188
- puts " - Or use: rbenv install 3.3.4"
189
- end
190
-
191
- puts "\nTo test rcurses isolation:"
192
- puts " ruby -e \"require 'rcurses'; puts 'Loaded OK'\""
193
-
194
- puts "\nTo restore terminal if corrupted:"
195
- puts " stty sane"
196
- puts " reset"
197
-
198
- puts "\nPlease share this diagnostic output when reporting issues."
data/fix_terminal.sh DELETED
@@ -1,33 +0,0 @@
1
- #!/bin/bash
2
- # Terminal restoration script for when things go wrong
3
-
4
- echo "Restoring terminal to sane state..."
5
-
6
- # Reset terminal to sane defaults
7
- stty sane 2>/dev/null
8
-
9
- # Clear any raw mode settings
10
- stty echo 2>/dev/null
11
- stty icanon 2>/dev/null
12
- stty icrnl 2>/dev/null
13
-
14
- # Reset terminal completely
15
- tput reset 2>/dev/null
16
- reset 2>/dev/null
17
-
18
- # Clear screen
19
- clear
20
-
21
- # Show cursor
22
- tput cnorm 2>/dev/null
23
- echo -e "\033[?25h" # ANSI escape to show cursor
24
-
25
- echo "Terminal restored!"
26
- echo ""
27
- echo "If your terminal is still broken, try:"
28
- echo " 1. Close and reopen your terminal"
29
- echo " 2. Run: reset"
30
- echo " 3. Run: stty sane"
31
- echo ""
32
- echo "To check terminal state:"
33
- echo " stty -a"
@@ -1,57 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # encoding: utf-8
3
-
4
- # Wrapper for Ruby 3.4+ compatibility
5
- # This ensures proper initialization of rcurses
6
-
7
- # Handle help/version before loading libraries
8
- if ARGV[0] == '-h' || ARGV[0] == '--help' || ARGV[0] == '-v' || ARGV[0] == '--version'
9
- # Pass through to main hyperlist
10
- load File.join(File.dirname(__FILE__), 'hyperlist')
11
- exit 0
12
- end
13
-
14
- # Explicitly initialize rcurses before loading the main app
15
- require 'rcurses'
16
-
17
- begin
18
- # Initialize rcurses with proper error handling
19
- Rcurses.init!
20
-
21
- # Now load and run the main hyperlist app
22
- # We need to prevent the main file from running automatically
23
- $hyperlist_wrapper_mode = true
24
-
25
- # Load the hyperlist file
26
- load File.join(File.dirname(__FILE__), 'hyperlist')
27
-
28
- # Create and run the app
29
- app = HyperListApp.new(ARGV[0])
30
- app.run
31
-
32
- rescue Interrupt
33
- # Handle Ctrl+C gracefully
34
- Rcurses.done! if defined?(Rcurses.done!)
35
- puts "\nInterrupted"
36
- exit 0
37
- rescue => e
38
- # Ensure terminal is restored on error
39
- Rcurses.done! if defined?(Rcurses.done!)
40
-
41
- # Fallback terminal restoration
42
- print "\e[?25h" # Show cursor
43
- system("stty sane 2>/dev/null")
44
-
45
- puts "Error: #{e.message}"
46
- puts "Backtrace:" if ENV['DEBUG']
47
- e.backtrace.first(10).each { |line| puts " #{line}" } if ENV['DEBUG']
48
- exit 1
49
- ensure
50
- # Always try to restore terminal
51
- begin
52
- Rcurses.done! if defined?(Rcurses.done!)
53
- rescue
54
- print "\e[?25h"
55
- system("stty sane 2>/dev/null")
56
- end
57
- end
File without changes
File without changes