openai-term 2.0 → 2.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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openai +73 -23
  3. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d93eaecbf599141d1cf428a2c72f6de86e8c6770aff9af919cf438ff03f2c6cc
4
- data.tar.gz: 5d335e40ae86757f7cf004b799dd7ebddf52f223a4f09e49ddf38dcadaae98ac
3
+ metadata.gz: e6298f3f1e4de53454d7d6503f2f790e10ee94a2bb49d3adea4a73a880e3fe93
4
+ data.tar.gz: 7065017d5738ed54d6d726f696d86a88c4f84832432acd67fe196f7cc788cf4f
5
5
  SHA512:
6
- metadata.gz: df381ff7515379eb62d4dcdd853e65ca677be5e31da91109ed1a86cc68bc1ea9ae063cd50048093e8ec922909c5a8e38896bda49bd03662e9037f3dfce6a23f0
7
- data.tar.gz: bec34eec79d74a74ddca15e07ec5e23eec65839dd9873f5a927dc1daba855481363563d6e1e9c3632e52788dd9047e4cd221698f0953114403b3f094d0631ab5
6
+ metadata.gz: 9e1dd35cb625a90b29907b0e2dd7cd4a3377619f6b4109b193dbfd2c73efa0219bf9b7e2c9de8a1806e9ac69ea9547ec865aa558cf9bd8a6dbee179801deea9b
7
+ data.tar.gz: 7076f6cfb610c248a6b11acf178aeb0bb76b24160ec770a51d4002a02e318ee3f45a1f59b01c1464f29866549619897751672117b7b7598c1d0003445d5fb992
data/bin/openai CHANGED
@@ -19,7 +19,7 @@ CONFIG_FILE = File.join(Dir.home, '.openai.conf')
19
19
  HISTORY_FILE = File.join(Dir.home, '.openai_history.json')
20
20
  DEFAULT_MODEL = "gpt-3.5-turbo"
21
21
  DEFAULT_MAX_TOKENS = 2048
22
- VERSION = "2.0"
22
+ VERSION = "2.1"
23
23
 
24
24
  # Global variables
25
25
  @model = DEFAULT_MODEL
@@ -42,6 +42,7 @@ VERSION = "2.0"
42
42
  @input_text = ""
43
43
  @chat_scroll = 0
44
44
  @selected_model = 0
45
+ @in_editline = false
45
46
 
46
47
  # Parse command line options
47
48
  def parse_options
@@ -58,7 +59,7 @@ def parse_options
58
59
  opts.on('-c', '--config FILE', 'Config file path') { |c| options[:config] = c }
59
60
  opts.on('-q', '--quiet', 'Skip TUI and output to stdout directly') { options[:quiet] = true }
60
61
  opts.on('-h', '--help', 'Display help') { puts opts; exit }
61
- opts.on('-v', '--version', 'Display version') { puts "OpenAI Terminal 2.0"; exit }
62
+ opts.on('-v', '--version', 'Display version') { puts "OpenAI Terminal 2.1"; exit }
62
63
  end
63
64
 
64
65
  optparse.parse!
@@ -176,13 +177,13 @@ def setup_ui
176
177
 
177
178
  # Ensure status pane is visible at startup and input pane is ready
178
179
  @status_pane.refresh
179
- @input_pane.text = ""
180
- @input_pane.refresh
180
+ @in_editline = false
181
+ update_input_prompt
181
182
  end
182
183
 
183
184
  # Update header
184
185
  def update_header
185
- title = "OpenAI Terminal v2.0".b.fg(226)
186
+ title = "OpenAI Terminal v2.1".b.fg(226)
186
187
  model_info = "Model: #{@model}".fg(117)
187
188
  tokens_info = "Max Tokens: #{@max_tokens}".fg(117)
188
189
 
@@ -203,7 +204,8 @@ def update_status
203
204
  "C-S:Save",
204
205
  "C-Y:Copy",
205
206
  "C-V:Version",
206
- "C-I:Image"
207
+ "C-I:Image",
208
+ "PgUp/PgDn:Scroll"
207
209
  ].join(" ")
208
210
  when :model_select
209
211
  shortcuts = "↑↓:Navigate Enter:Select ESC:Cancel"
@@ -416,7 +418,7 @@ def show_help_popup
416
418
  @help_visible = true
417
419
 
418
420
  help_text = <<~HELP
419
- #{"OpenAI Terminal Help".b.fg(226)}
421
+ #{"OpenAI Terminal Help v2.1".b.fg(226)}
420
422
 
421
423
  #{"Keyboard Shortcuts:".b.fg(117)}
422
424
 
@@ -429,6 +431,7 @@ def show_help_popup
429
431
  Ctrl-Y - Copy last AI response to clipboard
430
432
  Ctrl-V - Show version information
431
433
  Ctrl-I - Generate image
434
+ PgUp/PgDn - Scroll chat window up/down
432
435
  Any char - Start typing message
433
436
 
434
437
  #{"Features:".b.fg(117)}
@@ -459,6 +462,43 @@ def hide_help_popup
459
462
  [@header, @chat_pane, @input_pane, @status_pane].each(&:full_refresh)
460
463
  end
461
464
 
465
+ # Update input pane prompt with appropriate styling
466
+ def update_input_prompt(text = "")
467
+ if @in_editline
468
+ # Bright prompt when in editline mode (matches chat window)
469
+ @input_pane.text = "You: ".b.fg(226) + text
470
+ else
471
+ # Dimmed prompt when not in editline mode
472
+ @input_pane.text = "You: ".fg(240) + text
473
+ end
474
+ @input_pane.refresh
475
+ end
476
+
477
+ # Scroll chat pane
478
+ def scroll_chat_pane(lines)
479
+ return unless @chat_pane
480
+
481
+ # Get current scroll position
482
+ current_scroll = @chat_pane.ix || 0
483
+
484
+ # Calculate new scroll position
485
+ new_scroll = current_scroll + lines
486
+
487
+ # Get total lines and visible lines to calculate scroll limits
488
+ total_lines = @chat_pane.text ? @chat_pane.text.lines.count : 0
489
+ visible_lines = @chat_pane.h - 2 # Account for border
490
+ max_scroll = [total_lines - visible_lines, 0].max
491
+
492
+ # Constrain scroll position to valid range
493
+ new_scroll = [[new_scroll, 0].max, max_scroll].min
494
+
495
+ # Apply scroll if it changed
496
+ if new_scroll != current_scroll
497
+ @chat_pane.ix = new_scroll
498
+ @chat_pane.refresh
499
+ end
500
+ end
501
+
462
502
  # Navigate input history
463
503
  def navigate_input_history(direction, input_history, current_index)
464
504
  return if input_history.empty?
@@ -475,13 +515,13 @@ def navigate_input_history(direction, input_history, current_index)
475
515
 
476
516
  if new_index < input_history.size
477
517
  # Show historical message
478
- @input_pane.text = "You: #{input_history[new_index]}"
518
+ @in_editline = false
519
+ update_input_prompt(input_history[new_index])
479
520
  else
480
521
  # Beyond history - empty input
481
- @input_pane.text = "You: "
522
+ @in_editline = false
523
+ update_input_prompt
482
524
  end
483
-
484
- @input_pane.refresh
485
525
  end
486
526
 
487
527
  # Main input loop
@@ -493,7 +533,6 @@ def input_loop
493
533
  loop do
494
534
  key = getchr
495
535
 
496
-
497
536
  # Handle popup input first
498
537
  if @help_visible
499
538
  case key
@@ -610,11 +649,19 @@ def input_loop
610
649
  when "DOWN"
611
650
  navigate_input_history(1, input_history, history_index)
612
651
  history_index = @current_history_index
652
+ when "PgUP"
653
+ # Scroll chat pane up
654
+ scroll_chat_pane(-10)
655
+ when "PgDOWN"
656
+ # Scroll chat pane down
657
+ scroll_chat_pane(10)
613
658
  when "C-I"
614
659
  # Image generation
615
- @input_pane.prompt = "Image: "
660
+ @in_editline = true
661
+ @input_pane.prompt = "Image: ".b.fg(226)
616
662
  @input_pane.text = ""
617
663
  @input_pane.editline
664
+ @in_editline = false
618
665
 
619
666
  # Only generate image if user didn't cancel (ESC)
620
667
  final_text = @input_pane.text&.strip || ""
@@ -627,16 +674,18 @@ def input_loop
627
674
  end
628
675
  # Reset input pane completely
629
676
  @input_pane.clear
630
- @input_pane.text = "You: "
631
- @input_pane.full_refresh
677
+ @in_editline = false
678
+ update_input_prompt
632
679
  else
633
680
  # Any printable character -> Enter input pane editline
634
681
  if key && key.length == 1 && key.match?(/[[:print:]]/)
635
682
  # Set up for editline
636
- @input_pane.prompt = "You: "
683
+ @in_editline = true
684
+ @input_pane.prompt = "You: ".b.fg(226)
637
685
  @input_pane.text = key
638
686
  initial_text = key
639
687
  @input_pane.editline
688
+ @in_editline = false
640
689
 
641
690
  # After editline returns, check what happened
642
691
  final_text = @input_pane.text&.strip || ""
@@ -661,10 +710,9 @@ def input_loop
661
710
  end
662
711
 
663
712
  # Always reset input pane completely (clears any remaining text)
664
- @input_pane.text = "You: "
665
713
  @input_pane.clear
666
- @input_pane.text = "You: "
667
- @input_pane.full_refresh
714
+ @in_editline = false
715
+ update_input_prompt
668
716
  end
669
717
  end
670
718
  end
@@ -703,7 +751,7 @@ end
703
751
  def update_conversation_list
704
752
  content = "Load Conversation (↑↓ to navigate, Enter to load, Esc to cancel):".b.fg(226) + "\n\n"
705
753
 
706
- @conversation_history.each_with_index do |conv, i|
754
+ @conversation_history.reverse.each_with_index do |conv, i|
707
755
  timestamp = conv["timestamp"]
708
756
  model = conv["model"]
709
757
  message_count = conv["messages"].size
@@ -729,7 +777,9 @@ end
729
777
  def load_selected_conversation
730
778
  return if @selected_conversation >= @conversation_history.size
731
779
 
732
- selected_conv = @conversation_history[@selected_conversation]
780
+ # Account for reverse order in display
781
+ actual_index = @conversation_history.size - 1 - @selected_conversation
782
+ selected_conv = @conversation_history[actual_index]
733
783
  @current_conversation = selected_conv["messages"].dup
734
784
 
735
785
  # Update chat display
@@ -868,8 +918,8 @@ def main
868
918
  return if result == :exit
869
919
 
870
920
  # Initialize input pane with prompt before starting loop
871
- @input_pane.text = "You: "
872
- @input_pane.refresh
921
+ @in_editline = false
922
+ update_input_prompt
873
923
 
874
924
  # Main loop
875
925
  input_loop
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openai-term
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.0'
4
+ version: '2.1'
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-07-06 00:00:00.000000000 Z
11
+ date: 2025-07-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ruby-openai
@@ -40,7 +40,8 @@ dependencies:
40
40
  version: '3.5'
41
41
  description: 'A modern terminal interface to OpenAI with a full TUI using rcurses.
42
42
  Features include interactive chat mode, conversation history, model selection, and
43
- more. Version 2.0: Complete rewrite using rcurses for a better terminal UI experience.'
43
+ more. Version 2.1: Added chat scrolling with PgUp/PgDown, improved prompt styling,
44
+ and latest conversations first.'
44
45
  email: g@isene.com
45
46
  executables:
46
47
  - openai