hyperlist 1.1.7 → 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 +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +22 -5
- data/hyperlist +302 -20
- data/hyperlist.gemspec +1 -1
- metadata +5 -13
- data/debug_instructions.md +0 -112
- data/diagnose.rb +0 -115
- data/diagnose_ruby34.rb +0 -145
- data/diagnose_ruby34_detailed.rb +0 -156
- data/diagnose_safe.rb +0 -198
- data/fix_terminal.sh +0 -33
- data/hyperlist_ruby34_wrapper.rb +0 -57
- data/rcurses_ruby34_patch.rb +0 -149
- /data/{hyperlist_logo.svg → img/hyperlist_logo.svg} +0 -0
- /data/{screenshot_help.png → img/screenshot_help.png} +0 -0
- /data/{screenshot_sample.png → img/screenshot_sample.png} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aef5338683dfa697e59042f093e0db9a748ef53515952a295b1c9eb972519ac5
|
4
|
+
data.tar.gz: fbad5a0f6022d40cfd147846d3de81b317b41fff39036c9085a0230fc0a144c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fd23aaaaa0ffbb7b1d73fdc99a46d3fb08d265000e1e10651e747857a4ab5d78dbf523d967e87d4ca82a1f0f8ff46d3fc563cec4441c3a86de7a3651f0cb41e6
|
7
|
+
data.tar.gz: 98ae50b53b8a9b4578ed50c81b8dcd763af2273f44bca200f27627cdc808f61b620baa0d76c09a9a776107de3de637e50c1daf660c7c77903debb8ee1868909d
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,22 @@
|
|
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
|
+
|
5
21
|
## [1.1.7] - 2025-08-15
|
6
22
|
|
7
23
|
### Fixed
|
data/README.md
CHANGED
@@ -6,7 +6,8 @@
|
|
6
6
|
[](https://github.com/isene/HyperList/stargazers)
|
7
7
|
[](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
|
-

|
27
|
+

|
27
28
|
|
28
29
|
### Help Screen
|
29
|
-

|
30
|
+

|
31
|
+
|
32
|
+
## What's New in v1.2.0
|
30
33
|
|
31
|
-
|
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**:
|
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.
|
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.
|
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.
|
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
|
-
|
140
|
-
|
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", "#{":
|
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
|
-
|
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,13 +4830,15 @@ class HyperListApp
|
|
4550
4830
|
quit
|
4551
4831
|
ensure
|
4552
4832
|
Cursor.show
|
4553
|
-
|
4833
|
+
# Screen clearing disabled for debugging
|
4834
|
+
# Rcurses.clear_screen
|
4554
4835
|
end
|
4555
4836
|
end
|
4556
4837
|
|
4557
4838
|
# Main
|
4558
|
-
|
4559
|
-
|
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'
|
4560
4842
|
app = HyperListApp.new(ARGV[0])
|
4561
4843
|
app.run
|
4562
4844
|
end
|
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.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-
|
11
|
+
date: 2025-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rcurses
|
@@ -73,20 +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_ruby34_detailed.rb
|
80
|
-
- diagnose_safe.rb
|
81
|
-
- fix_terminal.sh
|
82
76
|
- hyperlist
|
83
77
|
- hyperlist.gemspec
|
84
|
-
- hyperlist_logo.svg
|
85
|
-
-
|
86
|
-
-
|
78
|
+
- img/hyperlist_logo.svg
|
79
|
+
- img/screenshot_help.png
|
80
|
+
- img/screenshot_sample.png
|
87
81
|
- sample.hl
|
88
|
-
- screenshot_help.png
|
89
|
-
- screenshot_sample.png
|
90
82
|
- test.hl
|
91
83
|
homepage: https://github.com/isene/HyperList
|
92
84
|
licenses:
|