Commit e0eee5b

mo khan <mo@mokhan.ca>
2026-09-07 21:33:36
make Builtins::Terminal standalone, no longer inheriting from core's minimal Output
1 parent 2382881
Changed files (1)
lib
elelem
builtins
lib/elelem/builtins/terminal.rb
@@ -2,7 +2,50 @@
 
 module Elelem
   module Builtins
-    class Terminal < Elelem::Terminal
+    class Terminal
+      def initialize(stream: $stdout, **)
+        @stream = stream
+        @dots_thread = nil
+        @at_line_start = true
+      end
+
+      def say(text, as: nil)
+        stop_dots
+        return if blank?(text)
+
+        text = render(text, as) if as
+        write "#{text}\n"
+        @at_line_start = true
+      end
+
+      def print(text)
+        stop_dots
+        return if blank?(text)
+
+        write text
+        @at_line_start = false
+      end
+
+      def thinking(text)
+        print("\e[2;3m#{text}\e[0m") unless blank?(text)
+      end
+
+      def doing(name, args, state: "+")
+        gap
+        say "#{state} \e[36m#{name}\e[0m(#{args})"
+      end
+
+      def waiting
+        stop_dots
+        @dots_thread = Thread.new do
+          loop do
+            @stream.print "."
+            @stream.flush
+            sleep 0.1
+          end
+        end
+      end
+
       def display_file(path, fallback: nil)
         system("bat", "--paging=never", path) || say(fallback || path)
       end
@@ -10,7 +53,8 @@ module Elelem
       private
 
       def render(text, as)
-        return super unless as == :markdown
+        return "\n  ✗ #{text}" if as == :error
+        return text unless as == :markdown
 
         gap
         width = @stream.winsize[1] rescue 80
@@ -23,8 +67,23 @@ module Elelem
         text
       end
 
+      def gap
+        stop_dots
+        write("\n") unless @at_line_start
+        @at_line_start = true
+      end
+
+      def stop_dots
+        return unless @dots_thread
+
+        @dots_thread.kill
+        @dots_thread = nil
+        write("\n")
+        @at_line_start = true
+      end
+
       def write(text)
-        super
+        @stream.print text
         (@transcript ||= open_transcript).print text
       end
 
@@ -34,6 +93,10 @@ module Elelem
         session_id = "#{Time.now.utc.strftime("%Y%m%dT%H%M%SZ")}-#{Process.pid}"
         File.open(File.join(state_dir, "#{session_id}.transcript"), "a").tap { |f| f.sync = true }
       end
+
+      def blank?(text)
+        text.nil? || text.to_s.strip.empty?
+      end
     end
   end
 end