telescope-term 1.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/telescope +266 -27
  3. metadata +7 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9d7390a8fecd0f7985cb6c6396edc3611f51daa61afc810e4636487345dda37
4
- data.tar.gz: 525cc188610ac7dc332850e26b95fedada8c77acb6bddb498e260c4b5769e26f
3
+ metadata.gz: ea52b6c70e7459e1d2883070863c43c61ea83254c894d14205b75f664b10419a
4
+ data.tar.gz: 262471d0faf1eaee00418bf48969cfa0675cc5bfb0167ad4f3427d64b342c240
5
5
  SHA512:
6
- metadata.gz: 15fb388da4c9bdced12b57cfdce35d466b19df4fc4ffe6ab1597bfeb248c66f05bddc2e1b79454833289e2dd1680a9fd45cf55b2afb6021d9c94c23df6646ea1
7
- data.tar.gz: 78e47762e001126575ad985841efd7fc73c17f8b6cf710fc11616a8f8bad902e744754e2b2439c59e2ced2421a97e87ec784254ee6ff7e84686c7fd1401ff41d
6
+ metadata.gz: e5d1d868063f0eb0d1b0f6028e43851e9c320c699bd32718c18df9050ff6dde8b85cf14246df84f43f10b129066698a1548c0f3d97f38374b6ccfc5495fb3bbe
7
+ data.tar.gz: 59da347391bd03414dfcbb92a0651b189d5df5825135e437e8c9e1d9ee2f608ee9e5a5d86d8a3c58dfb7417c3de373250843463d6bbc21533136ad3fdc5f8180
data/bin/telescope CHANGED
@@ -32,9 +32,16 @@ rescue StandardError => e
32
32
  end
33
33
  Pane = Rcurses::Pane
34
34
  require 'date'
35
+ require 'fileutils'
36
+ require 'json'
37
+ require 'csv'
38
+
39
+ # Version
40
+ VERSION = "2.0"
35
41
 
36
42
  # Persistence paths
37
43
  SAVE = File.join(Dir.home, '.telescope')
44
+ CONFIG = File.join(Dir.home, '.telescope_config')
38
45
 
39
46
  # HELP TEXT {{{1
40
47
  HELP1 = " WELCOME TO THE TERMINAL TELESCOPE APPLICATION\n".b + '
@@ -50,8 +57,8 @@ HELP1 = " WELCOME TO THE TERMINAL TELESCOPE APPLICATION\n".b + '
50
57
  '
51
58
 
52
59
  HELP2 = " TERMININAL TELESCOPE APPLICATION\n\n Keys and their actions\n".b + '
53
- t Add telescope (name,app,fl)
54
- e Add eyepiece (name,fl,afov)
60
+ t Add telescope (name,app,fl[,notes])
61
+ e Add eyepiece (name,fl,afov[,notes])
55
62
  ENTER Edit selected
56
63
  TAB Switch panels
57
64
  UP/DOWN Move cursor
@@ -61,7 +68,11 @@ HELP2 = " TERMININAL TELESCOPE APPLICATION\n\n Keys and their actions\n".b + '
61
68
  o Toggle order by Telescope APP and Eyepiece FL
62
69
  SPACE Tag/untag
63
70
  u Untag all
71
+ A Tag all (bulk operation)
64
72
  Ctrl-o Create observation log with tagged equipment
73
+ x Export tagged items to CSV
74
+ X Export all items to JSON
75
+ v Show version information
65
76
  D Delete item
66
77
  r Refresh all panes
67
78
  q/Q Quit (save/no save)
@@ -93,20 +104,69 @@ HELP3 = " Abbreviations and their meaning\n".b + '
93
104
  2BLW = With a 2xBarlow (magnification, then rest)'.fg(229)
94
105
 
95
106
 
107
+ # CONFIGURATION {{{1
108
+ # Default configuration
109
+ @config = {
110
+ 'colors' => {
111
+ 'ts_header_bg' => '00524b',
112
+ 'ep_header_bg' => '4c3c1d',
113
+ 'tag_color' => 46,
114
+ 'cursor_bg' => 234,
115
+ 'text_color' => 248,
116
+ 'check_good' => 112,
117
+ 'check_bad' => 208
118
+ },
119
+ 'auto_backup' => true,
120
+ 'backup_count' => 5
121
+ }
122
+
123
+ # Load configuration if it exists
124
+ if File.exist?(CONFIG)
125
+ begin
126
+ loaded_config = eval(File.read(CONFIG))
127
+ @config.merge!(loaded_config) if loaded_config.is_a?(Hash)
128
+ rescue
129
+ # Use defaults if config file is corrupted
130
+ end
131
+ end
132
+
96
133
  # INITIALIZATION {{{1
97
134
  # Data stores
98
- @ts = [] # Telescopes: [name, app, fl]
99
- @ep = [] # Eyepieces: [name, fl, afov]
135
+ @ts = [] # Telescopes: [name, app, fl, notes]
136
+ @ep = [] # Eyepieces: [name, fl, afov, notes]
100
137
  @tstag = [] # Telescope tags
101
138
  @eptag = [] # Eyepiece tags
102
139
  @cursor_ts = 0 # Telescope cursor index
103
140
  @cursor_ep = 0 # Eyepiece cursor index
104
141
 
142
+ # BACKUP SYSTEM {{{1
143
+ def create_backup
144
+ return unless @config['auto_backup'] && File.exist?(SAVE)
145
+
146
+ backup_dir = File.join(Dir.home, '.telescope_backups')
147
+ Dir.mkdir(backup_dir) unless Dir.exist?(backup_dir)
148
+
149
+ timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
150
+ backup_file = File.join(backup_dir, ".telescope_#{timestamp}")
151
+
152
+ FileUtils.cp(SAVE, backup_file)
153
+
154
+ # Clean old backups
155
+ backups = Dir.glob(File.join(backup_dir, '.telescope_*')).sort
156
+ while backups.size > @config['backup_count']
157
+ File.delete(backups.shift)
158
+ end
159
+ end
160
+
105
161
  # Load saved data if present
106
162
  SAVE = File.join(Dir.home, '.telescope') # Persistence path
107
163
 
108
164
  if File.exist?(SAVE)
165
+ create_backup
109
166
  load SAVE # expects plaintext: @ts = [...] and @ep = [...]
167
+ # Convert old format to new format with notes
168
+ @ts.map! { |t| t.size == 3 ? t + [''] : t }
169
+ @ep.map! { |e| e.size == 3 ? e + [''] : e }
110
170
  else
111
171
  @ts = []
112
172
  @ep = []
@@ -155,6 +215,40 @@ def tfov(tfl, epfl, afov); (afov.to_f / magx(tfl, epfl)); end
155
215
  def pupl(app, tfl, epfl); (app.to_f / magx(tfl, epfl)); end
156
216
  def magx(tfl, epfl); (tfl.to_f / epfl); end
157
217
 
218
+ # VALIDATION FUNCTIONS {{{1
219
+ def validate_telescope_input(input_array)
220
+ return false unless input_array.size >= 3
221
+
222
+ name, app, fl = input_array[0..2]
223
+ return false if name.nil? || name.strip.empty?
224
+ return false unless app.to_f > 0
225
+ return false unless fl.to_f > 0
226
+
227
+ true
228
+ end
229
+
230
+ def validate_eyepiece_input(input_array)
231
+ return false unless input_array.size >= 3
232
+
233
+ name, fl, afov = input_array[0..2]
234
+ return false if name.nil? || name.strip.empty?
235
+ return false unless fl.to_f > 0
236
+ return false unless afov.to_f > 0 && afov.to_f <= 180
237
+
238
+ true
239
+ end
240
+
241
+ def safe_file_write(file, content)
242
+ begin
243
+ File.write(file, content)
244
+ true
245
+ rescue => e
246
+ @pST.say(" Error writing file: #{e.message} - Press any key")
247
+ getchr
248
+ false
249
+ end
250
+ end
251
+
158
252
  # FUNCTIONS {{{1
159
253
  def refresh_all #{{{2
160
254
  Rcurses.clear_screen
@@ -175,7 +269,7 @@ def render_ts #{{{2
175
269
  name = t[0]
176
270
  app = t[1].to_i
177
271
  tfl = t[2].to_i
178
- tag_ts = @tstag[i] ? ' ' + '▐'.b.fg(46) : ' '
272
+ tag_ts = @tstag[i] ? ' ' + '▐'.b.fg(@config['colors']['tag_color']) : ' '
179
273
  txt = tag_ts
180
274
  txt += name.to_s.ljust(18)
181
275
  txt += app.to_s.rjust(7)
@@ -203,8 +297,8 @@ def render_ts #{{{2
203
297
  pad = @pTS.w - Rcurses.display_width(txt.pure)
204
298
  pad = 0 if pad.negative?
205
299
  txt += ' ' * pad
206
- txt = txt.bg(234) if i == @pTS.index
207
- txt = txt.fg(248) if i != @pTS.index
300
+ txt = txt.bg(@config['colors']['cursor_bg']) if i == @pTS.index
301
+ txt = txt.fg(@config['colors']['text_color']) if i != @pTS.index
208
302
  @pTS.text += txt
209
303
  end
210
304
  @pTS.refresh
@@ -213,12 +307,51 @@ end
213
307
 
214
308
  def ep_nice(app, tfl, e) #{{{2
215
309
  r = (tfl / app)
216
- out = ' '
217
- out += (e/6 > r ? ' ✓'.fg(112) : ' ✗'.fg(208))
218
- out += (e/3 > r && e/6 <= r ? ' ✓'.fg(112) : ' ✗'.fg(208))
219
- out += (e/1.5 > r && e/3 <= r ? ' ✓'.fg(112) : ' ✗'.fg(208))
220
- out += (e >= r && e/1.5 <= r ? ' ✓'.fg(112) : ' ✗'.fg(208))
221
- out += (e < r ? ' '.fg(112) : ' ✗'.fg(208))
310
+ out = ''
311
+ # Enhanced color coding with background highlights for optimal ranges - aligned with header
312
+ # Header: " *FLD GLXY PLNT DBL* >2*< "
313
+ # Positions: 6 12 18 24 30
314
+
315
+ out += ' ' # 5 spaces to center under *FLD (position 6)
316
+
317
+ if e/6 > r
318
+ out += ' ✓ '.fg(@config['colors']['check_good']).bg(22) # Dark green bg for star fields
319
+ else
320
+ out += ' ✗ '.fg(@config['colors']['check_bad'])
321
+ end
322
+
323
+ out += ' ' # 3 spaces to center under GLXY (position 12)
324
+
325
+ if e/3 > r && e/6 <= r
326
+ out += ' ✓ '.fg(@config['colors']['check_good']).bg(17) # Dark blue bg for galaxies
327
+ else
328
+ out += ' ✗ '.fg(@config['colors']['check_bad'])
329
+ end
330
+
331
+ out += ' ' # 3 spaces to center under PLNT (position 18)
332
+
333
+ if e/1.5 > r && e/3 <= r
334
+ out += ' ✓ '.fg(@config['colors']['check_good']).bg(52) # Dark magenta bg for planets
335
+ else
336
+ out += ' ✗ '.fg(@config['colors']['check_bad'])
337
+ end
338
+
339
+ out += ' ' # 3 spaces to center under DBL* (position 24)
340
+
341
+ if e >= r && e/1.5 <= r
342
+ out += ' ✓ '.fg(@config['colors']['check_good']).bg(94) # Orange bg for double stars
343
+ else
344
+ out += ' ✗ '.fg(@config['colors']['check_bad'])
345
+ end
346
+
347
+ out += ' ' # 3 spaces to center under >2*< (position 30)
348
+
349
+ if e < r
350
+ out += ' ✓ '.fg(@config['colors']['check_good']).bg(88) # Dark red bg for tight doubles
351
+ else
352
+ out += ' ✗ '.fg(@config['colors']['check_bad'])
353
+ end
354
+
222
355
  out
223
356
  end
224
357
 
@@ -230,7 +363,7 @@ def render_ep #{{{2
230
363
  name = e[0]
231
364
  epfl = e[1].to_f
232
365
  afov = e[2].to_f
233
- tag_ep = @eptag[i] ? ' ' + '▐'.b.fg(46) : ' '
366
+ tag_ep = @eptag[i] ? ' ' + '▐'.b.fg(@config['colors']['tag_color']) : ' '
234
367
  txt = tag_ep
235
368
  txt += name.to_s.ljust(18)
236
369
  txt += epfl.to_s.rjust(7)
@@ -255,14 +388,14 @@ def render_ep #{{{2
255
388
  dms2 = "#{deg2}°#{min2_s}'#{sec2_s}\""
256
389
  txt += dms2.rjust(11)
257
390
  txt += (pupl(app, tfl, epfl) / 2).rts(1).rjust(6)
258
- txt = txt.fg(248) if i != @pEP.index
391
+ txt = txt.fg(@config['colors']['text_color']) if i != @pEP.index
259
392
  txt += ep_nice(app, tfl, epfl)
260
393
  txt[0] = '→' if i == @pEP.index
261
394
  # ANSI safe padding
262
395
  pad = @pEP.w - Rcurses.display_width(txt.pure)
263
396
  pad = 0 if pad.negative?
264
397
  txt += ' ' * pad
265
- txt = txt.bg(234) if i == @pEP.index
398
+ txt = txt.bg(@config['colors']['cursor_bg']) if i == @pEP.index
266
399
  @pEP.text += txt
267
400
  end
268
401
  @pEP.refresh
@@ -349,15 +482,89 @@ def observe #{{{2
349
482
  getchr
350
483
  end
351
484
 
485
+ def export_csv #{{{2
486
+ date = Date.today.iso8601
487
+ file = File.join(Dir.home, "telescope_export_#{date}.csv")
488
+
489
+ CSV.open(file, 'w') do |csv|
490
+ # Export tagged telescopes
491
+ csv << ['TYPE', 'NAME', 'PARAM1', 'PARAM2', 'PARAM3', 'NOTES']
492
+ sel_ts = @tstag.each_index.select { |i| @tstag[i] }
493
+ sel_ts.each do |ts_idx|
494
+ t = @ts[ts_idx]
495
+ csv << ['TELESCOPE', t[0], t[1], t[2], '', t[3] || '']
496
+ end
497
+
498
+ # Export tagged eyepieces
499
+ sel_ep = @eptag.each_index.select { |i| @eptag[i] }
500
+ sel_ep.each do |ep_idx|
501
+ e = @ep[ep_idx]
502
+ csv << ['EYEPIECE', e[0], e[1], e[2], '', e[3] || '']
503
+ end
504
+ end
505
+
506
+ @pST.say(" Exported tagged items to #{file} - Press any key")
507
+ getchr
508
+ end
509
+
510
+ def export_json #{{{2
511
+ date = Date.today.iso8601
512
+ file = File.join(Dir.home, "telescope_export_#{date}.json")
513
+
514
+ data = {
515
+ 'export_date' => date,
516
+ 'telescopes' => @ts.map { |t| { 'name' => t[0], 'aperture' => t[1], 'focal_length' => t[2], 'notes' => t[3] || '' } },
517
+ 'eyepieces' => @ep.map { |e| { 'name' => e[0], 'focal_length' => e[1], 'afov' => e[2], 'notes' => e[3] || '' } }
518
+ }
519
+
520
+ File.write(file, JSON.pretty_generate(data))
521
+ @pST.say(" Exported all items to #{file} - Press any key")
522
+ getchr
523
+ end
524
+
525
+ def show_version #{{{2
526
+ local_version = VERSION
527
+
528
+ begin
529
+ remote_version = Gem.latest_version_for('telescope-term').version
530
+ version_info = +" VERSION INFORMATION\n\n"
531
+ version_info << "Local version: #{local_version}\n"
532
+ version_info << "Latest RubyGems version: #{remote_version}\n\n"
533
+
534
+ if Gem::Version.new(remote_version) > Gem::Version.new(local_version)
535
+ version_info << "Update available! Run: gem update telescope-term".fg(@config['colors']['check_bad'])
536
+ else
537
+ version_info << "You have the latest version!".fg(@config['colors']['check_good'])
538
+ end
539
+
540
+ version_info << "\n\nGem info: https://rubygems.org/gems/telescope-term"
541
+ version_info << "\nSource code: https://github.com/isene/telescope"
542
+ rescue StandardError => e
543
+ version_info = +" VERSION INFORMATION\n\n"
544
+ version_info << "Local version: #{local_version}\n"
545
+ version_info << "Could not check latest version: #{e.message}".fg(@config['colors']['check_bad'])
546
+ version_info << "\n\nGem info: https://rubygems.org/gems/telescope-term"
547
+ version_info << "\nSource code: https://github.com/isene/telescope"
548
+ end
549
+
550
+ @pObs.clear
551
+ @pObs.full_refresh
552
+ @pObs.say(version_info)
553
+ @pST.say(" Press any key to continue")
554
+ getchr
555
+ refresh_all
556
+ end
557
+
558
+
352
559
  # PANE SETUP {{{1
353
560
  # Top telescopes, eyepieces below, status at bottom
354
561
  @max_h, @max_w = IO.console.winsize
355
562
  @pTS = Pane.new( 2, 2, @max_w - 2, 8, nil, nil)
356
- @pTSh = Pane.new( 2, 2, @max_w - 2, 1, 255, "00524b")
357
- @pTSa = Pane.new( 1, @max_h, @max_w, 1, 255, "00524b")
563
+ @pTSh = Pane.new( 2, 2, @max_w - 2, 1, 255, @config['colors']['ts_header_bg'])
564
+ @pTSa = Pane.new( 1, @max_h, @max_w, 1, 255, @config['colors']['ts_header_bg'])
358
565
  @pEP = Pane.new( 2, 11, @max_w - 2, @max_h - 12, nil, nil)
359
- @pEPh = Pane.new( 2, 11, @max_w - 2, 1, 255, "4c3c1d")
360
- @pEPa = Pane.new( 1, @max_h, @max_w, 1, 255, "4c3c1d")
566
+ @pEPh = Pane.new( 2, 11, @max_w - 2, 1, 255, @config['colors']['ep_header_bg'])
567
+ @pEPa = Pane.new( 1, @max_h, @max_w, 1, 255, @config['colors']['ep_header_bg'])
361
568
  @pST = Pane.new( 1, @max_h, @max_w, 1, 255, 236)
362
569
 
363
570
  @pHlp = Pane.new( @max_w/2 - 30, @max_h/2 - 13, 60, 26, 252, 233)
@@ -366,7 +573,7 @@ end
366
573
  @pTSh.text = ' TELESCOPES APP(mm) FL(mm) F/? <MGN xEYE MINx MAXx SEP-R SEP-D *FLD GLXY PLNT DBL* >2*< MOON SUN'.b
367
574
  @pEPh.text = ' EYEPIECES FL(mm) AFOV'.b
368
575
  @pEPh.text += ' MAGX TFOV PPL 2blw tfov ppl'.b.i.fg("00827b")
369
- @pEPh.text += ' *FLD GLXY PLNT DBL* >2*<'.bg("4c3c1d")
576
+ @pEPh.text += ' *FLD GLXY PLNT DBL* >2*<'.bg(@config['colors']['ep_header_bg'])
370
577
  @pST.text = ' t/e = Add telescope/eyepiece, ENTER = Edit item, q/Q = Quit, ? = Help'
371
578
  @pHlp.border = true
372
579
  @pObs.border = true
@@ -381,6 +588,7 @@ end
381
588
  @ep_unsorted = @ep.dup
382
589
  @focus = @pTS
383
590
  @current = @ts
591
+ @comparison_mode = false
384
592
 
385
593
  # TRAP WIN SIZE CHANGE {{{1
386
594
  Signal.trap('WINCH') do
@@ -389,6 +597,8 @@ Signal.trap('WINCH') do
389
597
  end
390
598
 
391
599
  # MAIN LOOP {{{1
600
+ # Initialize rcurses (required for rcurses 6.0.0+)
601
+ Rcurses.init!
392
602
  refresh_all
393
603
  loop do
394
604
  render_ts
@@ -398,7 +608,7 @@ loop do
398
608
  ch = getchr
399
609
  case ch
400
610
  when 'q'
401
- File.write(SAVE, "@ts = #{@ts.inspect}\n@ep = #{@ep.inspect}\n") && exit
611
+ safe_file_write(SAVE, "@ts = #{@ts.inspect}\n@ep = #{@ep.inspect}\n") && exit
402
612
  when 'Q'; exit
403
613
  when '?'
404
614
  @pHlp.full_refresh
@@ -412,22 +622,39 @@ loop do
412
622
  when 'r'
413
623
  refresh_all
414
624
  when 't'
415
- inp=@pTSa.ask('name, app, fl: ','').split(', ')
416
- next unless inp.size == 3
625
+ inp=@pTSa.ask('name, app, fl [, notes]: ','').split(', ')
626
+ unless validate_telescope_input(inp)
627
+ @pST.say(" Invalid input - Press any key")
628
+ getchr
629
+ next
630
+ end
631
+ inp << '' if inp.size == 3 # Add empty notes if not provided
417
632
  @ts<<inp
418
633
  @tstag<<false
419
634
  @ts_unsorted << inp # keep master list updated
420
635
  @current = @ts
421
636
  when 'e'
422
- inp=@pEPa.ask('name, fl, afov: ','').split(', ')
423
- next unless inp.size == 3
637
+ inp=@pEPa.ask('name, fl, afov [, notes]: ','').split(', ')
638
+ unless validate_eyepiece_input(inp)
639
+ @pST.say(" Invalid input - Press any key")
640
+ getchr
641
+ next
642
+ end
643
+ inp << '' if inp.size == 3 # Add empty notes if not provided
424
644
  @ep<<inp
425
645
  @eptag<<false
426
646
  @ep_unsorted << inp # keep master list updated
427
647
  @current = @ep
428
648
  when 'ENTER'
429
649
  val = @current[@focus.index].join(', ')
430
- arr=@pST.ask('Edit: ', val).split(', '); next unless arr.size==3
650
+ arr=@pST.ask('Edit: ', val).split(', ')
651
+ is_valid = @current.equal?(@ts) ? validate_telescope_input(arr) : validate_eyepiece_input(arr)
652
+ unless is_valid
653
+ @pST.say(" Invalid input - Press any key")
654
+ getchr
655
+ next
656
+ end
657
+ arr << '' if arr.size == 3 # Add empty notes if not provided
431
658
  @current[@focus.index] = arr
432
659
  when 'D'
433
660
  if @current.equal?(@ts)
@@ -495,6 +722,18 @@ loop do
495
722
  when 'u'
496
723
  @tstag.fill(false)
497
724
  @eptag.fill(false)
725
+ when 'A'
726
+ if @current.equal?(@ts)
727
+ @tstag.fill(true)
728
+ else
729
+ @eptag.fill(true)
730
+ end
731
+ when 'x'
732
+ export_csv
733
+ when 'X'
734
+ export_json
735
+ when 'v'
736
+ show_version
498
737
  when 'C-O'
499
738
  observe
500
739
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: telescope-term
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.1'
4
+ version: '2.0'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-14 00:00:00.000000000 Z
11
+ date: 2025-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses
@@ -16,19 +16,19 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '3.5'
19
+ version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '3.5'
26
+ version: '6.0'
27
27
  description: 'With this program you can list your telescopes and eyepieces and get
28
28
  a set of calculations done for each scope and for the combination of scope and eyepiece.
29
- Easy interface. Run the program, then hit ''?'' to show the help file. Version 1.0:
30
- A full rewrite using the rcurses library (https://github.com/isene/rcurses) - lots
31
- of improvements. 1.1: Removed tty startup/exit codes as rcurses now handles that.'
29
+ Easy interface. Run the program, then hit ''?'' to show the help file. Version 2.0:
30
+ Breaking change - requires rcurses 6.0.0+ with explicit initialization for Ruby
31
+ 3.4+ compatibility.'
32
32
  email: g@isene.com
33
33
  executables:
34
34
  - telescope