diff --git a/lib/bbs/flow.rb b/lib/bbs/flow.rb index 40dd691..380a2e0 100644 --- a/lib/bbs/flow.rb +++ b/lib/bbs/flow.rb @@ -75,6 +75,30 @@ module BBS def call(&block) @steps << { type: :call, block: block } end + + def line(color: :muted) + @steps << { type: :line, color: color } + end + + def section(title, color: :confirm) + @steps << { type: :section, title: title, color: color } + end + + def text(content = nil, style: :muted, &block) + @steps << { type: :text, text: content, block: block, style: style } + end + + def rows(empty: 'No items.', &block) + @steps << { type: :rows, block: block, empty: empty } + end + + def table(&block) + @steps << { type: :table, block: block } + end + + def wait_enter(prompt: 'Press ENTER to continue...') + @steps << { type: :wait_enter, prompt: prompt } + end end class MenuBuilder diff --git a/lib/bbs/flow_runner.rb b/lib/bbs/flow_runner.rb index 5936a04..9597a34 100644 --- a/lib/bbs/flow_runner.rb +++ b/lib/bbs/flow_runner.rb @@ -9,6 +9,13 @@ module BBS info: "\e[0;33m", prompt: "\e[1;37m", confirm: "\e[1;36m", + cyan: "\e[0;36m", + magenta: "\e[0;35m", + blue: "\e[0;34m", + yellow: "\e[0;33m", + white: "\e[1;37m", + green: "\e[1;32m", + red: "\e[1;31m", }.freeze attr_reader :session_id @@ -17,7 +24,7 @@ module BBS @session = session @session_id = session_id @flow = flow - @ctx = {} + @ctx = { session_id: session_id } end def run @@ -53,6 +60,12 @@ module BBS when :menu then run_menu(step) when :exit_menu then :exit_menu when :call then run_call(step) + when :line then run_line(step) + when :section then run_section(step) + when :text then run_text(step) + when :rows then run_rows(step) + when :table then run_table(step) + when :wait_enter then run_wait_enter(step) end end @@ -170,10 +183,73 @@ module BBS result == :halt ? :halt : nil rescue IOError, Errno::EPIPE, Errno::ECONNRESET :halt + rescue => e + warn "BBS call error: #{e.class}: #{e.message}\n#{e.backtrace.first(5).join("\n")}" + write "\r\n \e[1;31mError: #{e.message}\e[0m\r\n\r\n" + nil + end + + def run_line(step) + color = style_color(step[:color]) + write " #{color}#{'─' * 66}\e[0m\r\n" + end + + def run_section(step) + color = style_color(step[:color]) + title = step[:title] + pad = [70 - title.length - 6, 2].max + write "\r\n #{color}┌─ \e[1;37m#{title} #{color}#{'─' * pad}┐\e[0m\r\n\r\n" + end + + def run_text(step) + color = style_color(step[:style]) + content = step[:block] ? step[:block].call(@ctx) : step[:text] + write " #{color}#{content}\e[0m\r\n" if content + end + + def run_rows(step) + items = step[:block].call(@ctx) + items = Array(items) + if items.empty? + write " #{STYLES[:muted]}#{step[:empty]}\e[0m\r\n" + else + items.each { |row| write " #{row}\r\n" } + end + nil + rescue IOError, Errno::EPIPE, Errno::ECONNRESET + :halt + rescue => e + warn "BBS rows error: #{e.class}: #{e.message}" + write "\r\n #{STYLES[:error]}Error loading content.\e[0m\r\n" + nil + end + + def run_table(step) + pairs = step[:block].call(@ctx) + pairs.each do |label, value| + write " #{STYLES[:muted]}#{label.to_s.ljust(16)}#{STYLES[:prompt]}#{value}\e[0m\r\n" + end + nil + rescue IOError, Errno::EPIPE, Errno::ECONNRESET + :halt + rescue => e + warn "BBS table error: #{e.class}: #{e.message}" + write "\r\n #{STYLES[:error]}Error loading content.\e[0m\r\n" + nil + end + + def run_wait_enter(step) + write "\r\n #{STYLES[:muted]}#{step[:prompt]}\e[0m" + return :halt if readline.nil? + nil end # ── helpers ──────────────────────────────────────────────────────────────── + def style_color(value) + value.is_a?(Symbol) ? STYLES.fetch(value, STYLES[:muted]) : value.to_s + end + def apply_transform(value, transform) case transform when :upcase then value.upcase