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.
- checksums.yaml +4 -4
- data/bin/openai +73 -23
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e6298f3f1e4de53454d7d6503f2f790e10ee94a2bb49d3adea4a73a880e3fe93
|
4
|
+
data.tar.gz: 7065017d5738ed54d6d726f696d86a88c4f84832432acd67fe196f7cc788cf4f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
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
|
-
@
|
180
|
-
|
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.
|
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
|
-
@
|
518
|
+
@in_editline = false
|
519
|
+
update_input_prompt(input_history[new_index])
|
479
520
|
else
|
480
521
|
# Beyond history - empty input
|
481
|
-
@
|
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
|
-
@
|
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
|
-
@
|
631
|
-
|
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
|
-
@
|
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
|
-
@
|
667
|
-
|
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
|
-
|
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
|
-
@
|
872
|
-
|
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.
|
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-
|
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.
|
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
|