Skip to content
Kward Search API index

Class: Kward::PluginUI

Inherits:
Object
  • Object
show all
Defined in:
lib/kward/plugins/ui.rb

Overview

Frontend-neutral structured UI exposed to trusted plugin handlers.

Frontends provide a small backend that handles blocking requests and non-blocking events. Unsupported requests fail closed: selection, question, and input return nil, while confirmation returns false.

Defined Under Namespace

Classes: Backend

Constant Summary collapse

FEATURES =
%i[question select confirm input progress notify].freeze
NOTIFICATION_LEVELS =
%i[info success warning error].freeze
MAX_TEXT_BYTES =
16_384
MAX_OPTIONS =
100
MAX_ID_BYTES =
128

Instance Method Summary collapse

Constructor Details

#initialize(backend: nil, say_callback: nil, cancellation: nil) ⇒ PluginUI

Returns a new instance of PluginUI.



53
54
55
56
57
# File 'lib/kward/plugins/ui.rb', line 53

def initialize(backend: nil, say_callback: nil, cancellation: nil)
  @backend = backend || Backend.new
  @say_callback = say_callback
  @cancellation = cancellation
end

Instance Method Details

#capabilities ⇒ Object



59
60
61
# File 'lib/kward/plugins/ui.rb', line 59

def capabilities
  @backend.capabilities.dup.freeze
end

#confirm(title, message = nil, default: false) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/kward/plugins/ui.rb', line 88

def confirm(title, message = nil, default: false)
  if message.nil?
    message = title
    title = "Confirm"
  end
  answer = request(
    :confirm,
    title: required_text(title, "confirm title"),
    message: required_text(message, "confirm message"),
    default: default == true
  )
  answer == true
end

#input(title, placeholder = nil, default: nil) ⇒ Object

Raises:

  • (ArgumentError)


102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/kward/plugins/ui.rb', line 102

def input(title, placeholder = nil, default: nil)
  answer = request(
    :input,
    title: required_text(title, "input title"),
    placeholder: optional_text(placeholder),
    default: optional_text(default)
  )
  return nil if answer.nil?
  raise ArgumentError, "plugin UI input answer must be a string" unless answer.is_a?(String)

  bounded_text(answer, "input answer")
end

#notify(message, level = :info, **options) ⇒ Object

Raises:

  • (ArgumentError)


115
116
117
118
119
120
121
122
123
# File 'lib/kward/plugins/ui.rb', line 115

def notify(message, level = :info, **options)
  level = options[:level] if options.key?(:level)
  level = level.to_sym
  raise ArgumentError, "unsupported plugin notification level: #{level}" unless NOTIFICATION_LEVELS.include?(level)

  payload = { message: required_text(message, "notification message"), level: level }
  @say_callback&.call(payload[:message]) unless @backend.emit(:notify, immutable(payload))
  nil
end

#progress(id = nil, message = nil, percent: nil, done: false, **values) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/kward/plugins/ui.rb', line 125

def progress(id = nil, message = nil, percent: nil, done: false, **values)
  id ||= values[:id]
  message ||= values[:message]
  payload = {
    id: required_text(id, "progress id", max_bytes: MAX_ID_BYTES),
    message: required_text(message, "progress message"),
    percent: normalize_percent(percent),
    done: done == true
  }.compact
  unless @backend.emit(:progress, immutable(payload))
    fallback = payload[:percent] ? "#{payload[:message]} (#{payload[:percent]}%)" : payload[:message]
    @say_callback&.call(fallback)
  end
  nil
end

#question(questions) ⇒ Object



67
68
69
70
# File 'lib/kward/plugins/ui.rb', line 67

def question(questions)
  normalized = QuestionContract.normalize_questions(questions)
  request(:question, questions: normalized)
end

#select(title, options, message: nil) ⇒ Object

Raises:

  • (ArgumentError)


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/kward/plugins/ui.rb', line 72

def select(title, options, message: nil)
  normalized = normalize_options(options)
  answer = request(
    :select,
    title: required_text(title, "select title"),
    message: optional_text(message),
    options: normalized
  )
  return nil if answer.nil?

  selected = normalized.find { |option| option[:value] == answer.to_s || option[:label] == answer.to_s }
  raise ArgumentError, "plugin UI returned an unknown selection" unless selected

  selected[:value]
end

#supported?(feature) ⇒ Boolean

Returns:

  • (Boolean)


63
64
65
# File 'lib/kward/plugins/ui.rb', line 63

def supported?(feature)
  @backend.supported?(feature)
end

#with_cancellation(cancellation) ⇒ Object



141
142
143
# File 'lib/kward/plugins/ui.rb', line 141

def with_cancellation(cancellation)
  self.class.new(backend: @backend, say_callback: @say_callback, cancellation: cancellation)
end