Class: Clacky::RichUIController

Inherits:
Object
  • Object
show all
Includes:
Clacky::RichUI::ViewRenderer, UIInterface
Defined in:
lib/clacky/rich_ui/rich_ui_controller.rb

Constant Summary collapse

STREAMING_MARKDOWN_THRESHOLD =
240
STREAMING_MARKDOWN_CHUNK_SIZE =
6
STREAMING_MARKDOWN_DELAY =
0.03
COMMANDS =
[
  { label: "/clear", value: "/clear", description: "Clear output and restart session" },
  { label: "/config", value: "/config", description: "Open configuration" },
  { label: "/undo", value: "/undo", description: "Restore a previous task state" },
  { label: "/help", value: "/help", description: "Show commands" },
  { label: "/exit", value: "/exit", description: "Exit application", aliases: ["/quit"] }
].freeze
SKILL_DESC_MAX =

Max description length for slash-menu display. Skill descriptions can be hundreds of chars; RubyRich Composer renders each command as a single line and long lines wrap or clip unpredictably. Truncating at registration is simpler and more reliable than patching the gem’s render_command.

50

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Clacky::RichUI::ViewRenderer

build_fingerprint, compact_tool_arg, config_initial_selection, config_menu_choices, escape_tool_label, expand_ansi_multiline_spans, extract_thinking_and_content, format_args, format_tool_output, format_tool_value, mask_api_key, merge_model_form_values, normalize_markdown_for_terminal, normalize_todo, normalize_tool_args, parse_diff_stats, parse_tool_info, tool_activity_label, tool_category, tool_risk_level, tool_url_host, truncate_tool_label, validate_model_form

Methods included from UIInterface

#phase_end, #phase_start, #show_feedback_request, #stream_thinking_progress, #with_phase

Constructor Details

#initialize(config = {}) ⇒ RichUIController

Returns a new instance of RichUIController.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 43

def initialize(config = {})
  @config = {
    working_dir: config[:working_dir],
    mode: config[:mode],
    model: config[:model],
    theme: config[:theme]
  }
  @welcome_banner = Clacky::UI2::Components::WelcomeBanner.new
  @available_models = config[:model_names] || [config[:model] || "unknown"]
  @shell = RichAgentShell.new(
    title: "OpenClacky",
    subtitle: config[:working_dir].to_s,
    model: config[:model].to_s,
    theme: RubyRich::Theme.agent_dark,
    commands: COMMANDS
  )
  @shell.clacky_controller = self
  @layout = RichUI::LayoutAdapter.new(@shell)
  @input_callback = nil
  @interrupt_callback = nil
  @work_label = nil
  @ctrl_c_warning = nil
  @latest_latency = nil
  @always_allow_fingerprints = Set.new
  @mode_toggle_callback = nil
  @model_switch_callback = nil
  @time_machine_callback = nil
  @tasks_count = 0
  @total_cost = 0.0
  @running = false
  @turn_active = false
  @tracker = RichUI::EntryTracker.new
  @todo_items = []
  @explicit_todo_cycle = false
  @tool_activities = []
  @tool_activity_by_id = {}
  @legacy_progress = {}
  @stdout_lines = []
  @callback_threads = []
  @stream_threads = []

  wire_shell_callbacks
end

Instance Attribute Details

#available_modelsObject

Returns the value of attribute available_models.



41
42
43
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 41

def available_models
  @available_models
end

#configObject

Returns the value of attribute config.



41
42
43
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 41

def config
  @config
end

#ctrl_c_warningObject (readonly)

Returns the value of attribute ctrl_c_warning.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def ctrl_c_warning
  @ctrl_c_warning
end

#latest_latencyObject (readonly)

Returns the value of attribute latest_latency.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def latest_latency
  @latest_latency
end

#layoutObject (readonly)

Returns the value of attribute layout.



39
40
41
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 39

def layout
  @layout
end

#runningObject (readonly)

Returns the value of attribute running.



39
40
41
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 39

def running
  @running
end

#shellObject (readonly)

Returns the value of attribute shell.



39
40
41
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 39

def shell
  @shell
end

#statusObject (readonly)

Returns the value of attribute status.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def status
  @status
end

#tasks_countObject (readonly)

Returns the value of attribute tasks_count.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def tasks_count
  @tasks_count
end

#total_costObject (readonly)

Returns the value of attribute total_cost.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def total_cost
  @total_cost
end

#turn_activeObject (readonly)

Returns the value of attribute turn_active.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def turn_active
  @turn_active
end

#work_labelObject (readonly)

Returns the value of attribute work_label.



40
41
42
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 40

def work_label
  @work_label
end

Instance Method Details

#append_output(content) ⇒ Object



190
191
192
193
194
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 190

def append_output(content)
  return if content.nil?

  @shell.add_markdown(content.to_s)
end

#clear_inputObject



461
462
463
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 461

def clear_input
  @shell.composer.editor.clear
end

#handle_escObject

Esc cancellation stack (tui_design.md §2.8). Called from Composer’s @on_escape callback (before native escape). Returns true when handled (skip native), false to fall through.



672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 672

def handle_esc
  # Layer 1: Close any open dialog or slash menu
  if @shell.layout.dialog
    dialog = @shell.layout.dialog
    dialog.finish(nil) if dialog.respond_to?(:finish)
    @shell.layout.hide_dialog
    return true
  end
  if @shell.composer.menu_open?
    @shell.composer.send(:close_menu)
    return true
  end

  # Layer 2: Interrupt running turn
  if @turn_active
    @interrupt_callback&.call(input_was_empty: false)
    return true
  end

  # Layer 3: Discard queued draft (future; return true when done)

  # Layer 4+5: Fall through to Composer's native escape —
  #   editor with text → clear, empty editor → focus/no-op
  false
end

#initialize_and_show_banner(recent_user_messages: nil) ⇒ Object



87
88
89
90
91
92
93
94
95
96
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 87

def initialize_and_show_banner(recent_user_messages: nil)
  @running = true
  @shell.update_status(session_status)
  if recent_user_messages && !recent_user_messages.empty?
    @shell.add_separator("recent session")
    recent_user_messages.each { |message| @shell.add_user_message(message) }
  else
    add_plain_block(render_welcome_banner)
  end
end

#log(message, level: :info) ⇒ Object



196
197
198
199
200
201
202
203
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 196

def log(message, level: :info)
  case level.to_sym
  when :error then show_error(message)
  when :warning, :warn then show_warning(message)
  when :debug then nil
  else show_info(message)
  end
end

#on_input(&block) ⇒ Object



170
171
172
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 170

def on_input(&block)
  @input_callback = block
end

#on_interrupt(&block) ⇒ Object



174
175
176
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 174

def on_interrupt(&block)
  @interrupt_callback = block
end

#on_mode_toggle(&block) ⇒ Object



178
179
180
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 178

def on_mode_toggle(&block)
  @mode_toggle_callback = block
end

#on_model_switch(&block) ⇒ Object



182
183
184
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 182

def on_model_switch(&block)
  @model_switch_callback = block
end

#on_time_machine(&block) ⇒ Object



186
187
188
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 186

def on_time_machine(&block)
  @time_machine_callback = block
end

#request_confirmation(message, default: true) ⇒ Object



403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 403

def request_confirmation(message, default: true)
  tool_name, params = ViewRenderer.parse_tool_info(message)
  risk = ViewRenderer.tool_risk_level(tool_name)
  category = ViewRenderer.tool_category(tool_name)

  fingerprint = ViewRenderer.build_fingerprint(tool_name, params)
  return true if @always_allow_fingerprints.include?(fingerprint)

  show_info(message)
  dialog = RichUI::ApprovalDialog.new(
    tool_name: tool_name || "unknown",
    message: message,
    params: params,
    risk: risk,
    category: category
  )
  result = show_blocking_dialog(dialog)

  case result
  when :approve
    true
  when :always_allow
    @always_allow_fingerprints.add(fingerprint)
    true
  when :deny
    false
  else
    default
  end
end

#set_agent(_agent, _agent_profile = nil) ⇒ Object



168
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 168

def set_agent(_agent, _agent_profile = nil); end

#set_idle_statusObject



397
398
399
400
401
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 397

def set_idle_status
  @turn_active = false
  @work_label = nil
  update_sessionbar(status: "idle")
end

#set_input_tips(message, type: :info) ⇒ Object



465
466
467
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 465

def set_input_tips(message, type: :info)
  update_sessionbar(status: "#{type}: #{message}")
end

#set_skill_loader(skill_loader, agent_profile = nil) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 151

def set_skill_loader(skill_loader, agent_profile = nil)
  return unless skill_loader

  skills = skill_loader.user_invocable_skills
  skills = skills.select { |s| s.allowed_for_agent?(agent_profile.name) } if agent_profile

  skills.each do |skill|
    desc = skill.description.to_s
    desc = desc.length > SKILL_DESC_MAX ? "#{desc[0, SKILL_DESC_MAX - 1]}" : desc
    @shell.composer.register_command(
      name: skill.slash_command,
      description: desc
      # No handler — text falls through to submit callback → CLI → agent
    )
  end
end

#set_working_statusObject



391
392
393
394
395
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 391

def set_working_status
  @turn_active = true
  @work_label ||= "working…"
  update_sessionbar(status: "working")
end

#show_assistant_message(content, files:) ⇒ Object



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 205

def show_assistant_message(content, files:)
  thinking_text, clean_text = extract_thinking_and_content(content)
  unless thinking_text.to_s.strip.empty?
    # Show live thinking with spinner + timer in fixed area
    @shell.thinking_live.start_thinking
    stream_thinking_live(thinking_text.strip)
    elapsed = @shell.thinking_live.start_time
    elapsed = elapsed ? (Time.now - elapsed).round(1) : 0.0
    @shell.thinking_live.finish_thinking
    # Also add collapsed thinking block for reference (Ctrl+O to expand)
    @shell.add_thinking(thinking_text.strip, status: "#{elapsed}s", collapsed: true)
    # Hide the live area so transcript expands back to full height
    @shell.thinking_live.idle!
  end
  text = clean_text
  stream_thread = nil
  stream_thread = add_conversation_markdown(text) unless text.nil? || text.strip.empty?
  if stream_thread.is_a?(Thread)
    add_file_summary_after(stream_thread, files)
  else
    add_file_summary(files)
  end
end

#show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil) ⇒ Object



316
317
318
319
320
321
322
323
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 316

def show_complete(iterations:, cost:, duration: nil, cache_stats: nil, awaiting_user_feedback: false, cost_source: nil)
  set_idle_status
  return if awaiting_user_feedback || iterations <= 5

  parts = ["Completed #{iterations} iterations", "cost $#{cost.round(4)}"]
  parts << "#{duration.round(1)}s" if duration
  append_output(parts.join(" · "))
end

#show_config_modal(current_config, test_callback: nil) ⇒ Object



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 482

def show_config_modal(current_config, test_callback: nil)
  return nil unless @running

  loop do
    choices = config_menu_choices(current_config)
    result = show_menu_dialog(
      title: "Model Configuration",
      choices: choices,
      selected_index: config_initial_selection(choices)
    )
    return nil if result.nil?

    case result[:action]
    when :switch
      return result
    when :add
      new_model = show_model_edit_form(nil, test_callback: test_callback)
      if new_model
        anthropic_format = new_model[:provider] == "anthropic"
        current_config.add_model(
          model: new_model[:model],
          api_key: new_model[:api_key],
          base_url: new_model[:base_url],
          anthropic_format: anthropic_format
        )
        new_id = current_config.models.last["id"]
        return { action: :add, model_id: new_id }
      end
    when :edit
      current_model = current_config.current_model
      edited = show_model_edit_form(current_model, test_callback: test_callback)
      if edited
        current_model["api_key"] = edited[:api_key]
        current_model["model"] = edited[:model]
        current_model["base_url"] = edited[:base_url]
        return { action: :edit, model_id: current_model["id"] }
      end
    when :delete
      if current_config.models.length <= 1
        show_warning("Cannot delete the last model.")
        next
      end

      current_config.remove_model(current_config.current_model_index)
      new_current = current_config.current_model
      return { action: :delete, model_id: new_current && new_current["id"] }
    when :close
      return nil
    end
  end
end

#show_diff(old_content, new_content, max_lines: 50) ⇒ Object



292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 292

def show_diff(old_content, new_content, max_lines: 50)
  require "diffy"
  diff = Diffy::Diff.new(old_content, new_content, context: 3).to_s
  stats = parse_diff_stats(diff)
  header = "─── Diff#{stats}#{" " unless stats.empty?}───"
  lines = diff.lines
  visible = lines.take(max_lines).join
  hidden = lines.length - max_lines
  trailer = hidden.positive? ? "\n... (#{hidden} more lines hidden)" : ""
  @shell.add_diff(content: "#{header}\n#{visible}#{trailer}")
rescue LoadError
  append_output("Old size: #{old_content.bytesize} bytes\nNew size: #{new_content.bytesize} bytes")
end

#show_error(message) ⇒ Object



334
335
336
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 334

def show_error(message)
  @shell.add_error_message(message.to_s)
end

#show_file_edit_preview(path) ⇒ Object



280
281
282
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 280

def show_file_edit_preview(path)
  append_output("Editing file: #{path || "(unknown)"}")
end

#show_file_error(error_message) ⇒ Object



284
285
286
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 284

def show_file_error(error_message)
  show_error(error_message)
end

#show_file_write_preview(path, is_new_file:) ⇒ Object



276
277
278
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 276

def show_file_write_preview(path, is_new_file:)
  append_output("#{is_new_file ? "Creating" : "Modifying"} file: #{path || "(unknown)"}")
end

#show_helpObject



469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 469

def show_help
  @shell.add_markdown(<<~HELP)
    Commands:
      /clear - Clear output and restart session
      /exit - Exit application

    Input:
      Shift+Enter - New line
      Up/Down - History navigation
      Ctrl+C - Interrupt current task
  HELP
end

#show_info(message, prefix_newline: true) ⇒ Object



325
326
327
328
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 325

def show_info(message, prefix_newline: true)
  _ = prefix_newline
  @shell.add_system_message(message.to_s)
end

#show_progress(message = nil, prefix_newline: true, progress_type: "thinking", phase: "active", metadata: {}) ⇒ Object



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 342

def show_progress(message = nil, prefix_newline: true, progress_type: "thinking", phase: "active", metadata: {})
  _ = prefix_newline
  type = progress_type.to_s
  if phase.to_s == "done"
    @legacy_progress.delete(type)&.finish(final_message: message)
    return
  end

  handle = @legacy_progress[type]
  if handle
    handle.update(message: message, metadata: )
  else
    @legacy_progress[type] = start_progress(message: message, style: type == "thinking" ? :primary : :quiet)
  end
end

#show_shell_preview(command) ⇒ Object



288
289
290
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 288

def show_shell_preview(command)
  append_output("$ #{command}")
end

#show_success(message) ⇒ Object



338
339
340
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 338

def show_success(message)
  @shell.add_system_message("OK: #{message}")
end

#show_token_usage(token_data) ⇒ Object



306
307
308
309
310
311
312
313
314
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 306

def show_token_usage(token_data)
  @shell.show_token_usage(
    input: token_data[:prompt_tokens],
    output: token_data[:completion_tokens],
    total: token_data[:total_tokens],
    cost: token_data[:cost]
  )
  @shell.sidebar.update_context(token_data) if @shell.sidebar
end

#show_tool_args(formatted_args) ⇒ Object



272
273
274
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 272

def show_tool_args(formatted_args)
  append_output("Args: #{formatted_args}")
end

#show_tool_call(name, args) ⇒ Object



240
241
242
243
244
245
246
247
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 240

def show_tool_call(name, args)
  id = @shell.start_tool_call(name: name.to_s, input: format_args(args), status: :running)
  if id
    @tracker.register_tool(id)
    track_tool_activity(id, tool_activity_label(name, args), :running)
    @work_label = "#{name}"
  end
end

#show_tool_error(error) ⇒ Object



262
263
264
265
266
267
268
269
270
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 262

def show_tool_error(error)
  message = error.is_a?(Exception) ? error.message : error.to_s
  if (id = @tracker.pop_tool_id)
    @shell.finish_tool_call(id, status: :error, output: format_tool_output(message, :error))
    update_tool_activity(id, :error)
  else
    @shell.add_error_message(message)
  end
end

#show_tool_result(result) ⇒ Object



249
250
251
252
253
254
255
256
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 249

def show_tool_result(result)
  if (id = @tracker.pop_tool_id)
    @shell.finish_tool_call(id, status: :done, output: format_tool_output(result.to_s, :done))
    update_tool_activity(id, :done)
  else
    @shell.add_markdown(result.to_s)
  end
end

#show_tool_stdout(lines) ⇒ Object



258
259
260
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 258

def show_tool_stdout(lines)
  @stdout_lines.concat(Array(lines).map(&:to_s))
end

#show_warning(message) ⇒ Object



330
331
332
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 330

def show_warning(message)
  @shell.add_system_message("Warning: #{message}")
end

#startObject



98
99
100
101
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 98

def start
  initialize_and_show_banner unless @running
  start_input_loop
end

#start_input_loopObject



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 103

def start_input_loop
  @running = true
  begin
    @shell.start
  rescue AgentInterrupted
    # Ctrl+C (SIGINT via intr:true) raised AgentInterrupted.
    # Check viewport selection FIRST: when text is selected
    # (program-level drag-select), copy it to the clipboard and
    # clear the highlight, then retry the shell WITHOUT calling
    # the interrupt callback.  Otherwise route through the
    # normal interrupt callback (interrupt task / double-tap
    # warning / exit).
    vp = @shell.viewport
    selecting = vp.instance_variable_get(:@selecting)
    has_selection = selecting || vp.selected_text.to_s != ""

    if has_selection
      vp.send(:stop_selection) if selecting
      vp.instance_variable_set(:@selection_start, nil)
      vp.instance_variable_set(:@selection_end, nil)
      vp.instance_variable_set(:@selected_text, "")
      retry
    end

    # No selection — route through the interrupt callback.
    # The double-tap exit logic lives in the callback (cli.rb).
    input_was_empty = @shell.composer.value.to_s.empty?
    @interrupt_callback&.call(input_was_empty: input_was_empty)
    retry
  end
ensure
  @running = false
end

#start_progress(message: nil, style: :primary, quiet_on_fast_finish: false) ⇒ Object



358
359
360
361
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 358

def start_progress(message: nil, style: :primary, quiet_on_fast_finish: false)
  _ = quiet_on_fast_finish
  RichUI::ProgressHandleAdapter.new(@shell.start_progress(message || "Working", style: style))
end

#stop(clear_screen: true) ⇒ Object

Clears the screen on exit by default — the Rich UI repaints fullscreen and leaves no useful scrollback to preserve.



139
140
141
142
143
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 139

def stop(clear_screen: true)
  @running = false
  @shell.stop
  RubyRich::Terminal.clear if clear_screen
end

#update_sessionbar(tasks: nil, cost: nil, cost_source: nil, status: nil, latency: nil, session_id: nil) ⇒ Object



372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 372

def update_sessionbar(tasks: nil, cost: nil, cost_source: nil, status: nil, latency: nil, session_id: nil)
  _ = cost_source
  @latest_latency = nil
  if latency.is_a?(Hash)
    ms = latency[:ttft_ms] || latency[:duration_ms]
    @latest_latency = ms ? "#{(ms / 1000.0).round(1)}s" : nil
  end
  @tasks_count = tasks if tasks
  @total_cost = cost if cost
  @status = status if status
  @shell.update_status(session_status)
end

#update_todos(todos) ⇒ Object



385
386
387
388
389
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 385

def update_todos(todos)
  @todo_items = Array(todos).map { |todo| normalize_todo(todo) }
  @explicit_todo_cycle = true
  refresh_sidebar_tasks
end

#with_progress(message: nil, style: :primary, quiet_on_fast_finish: false) ⇒ Object



363
364
365
366
367
368
369
370
# File 'lib/clacky/rich_ui/rich_ui_controller.rb', line 363

def with_progress(message: nil, style: :primary, quiet_on_fast_finish: false)
  handle = start_progress(message: message, style: style, quiet_on_fast_finish: quiet_on_fast_finish)
  begin
    yield handle
  ensure
    handle.finish
  end
end