charming 0.2.0 → 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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/lib/charming/application.rb +96 -9
  4. data/lib/charming/audio/player.rb +104 -0
  5. data/lib/charming/audio/system.rb +69 -0
  6. data/lib/charming/cli.rb +63 -7
  7. data/lib/charming/controller/action_hooks.rb +124 -0
  8. data/lib/charming/controller/class_methods.rb +15 -1
  9. data/lib/charming/controller/dispatching.rb +31 -5
  10. data/lib/charming/controller/focus.rb +9 -0
  11. data/lib/charming/controller/focus_management.rb +0 -7
  12. data/lib/charming/controller/session_state.rb +16 -1
  13. data/lib/charming/controller/sidebar_navigation.rb +63 -28
  14. data/lib/charming/controller.rb +62 -10
  15. data/lib/charming/database/commands.rb +123 -11
  16. data/lib/charming/events/focus_event.rb +12 -0
  17. data/lib/charming/events/paste_event.rb +11 -0
  18. data/lib/charming/events/task_progress_event.rb +21 -0
  19. data/lib/charming/generators/app_generator.rb +38 -1
  20. data/lib/charming/generators/database_installer.rb +4 -15
  21. data/lib/charming/generators/migration_generator.rb +116 -0
  22. data/lib/charming/generators/migration_timestamp.rb +29 -0
  23. data/lib/charming/generators/model_generator.rb +4 -2
  24. data/lib/charming/generators/templates/app/application_controller.template +1 -1
  25. data/lib/charming/generators/templates/app/database_config.template +3 -1
  26. data/lib/charming/generators/templates/app/layout.template +1 -1
  27. data/lib/charming/generators/templates/app/spec_helper.template +2 -1
  28. data/lib/charming/generators/templates/app/view.template +1 -1
  29. data/lib/charming/internal/terminal/memory_backend.rb +6 -0
  30. data/lib/charming/internal/terminal/tty_backend.rb +64 -2
  31. data/lib/charming/presentation/component.rb +7 -0
  32. data/lib/charming/presentation/components/audio.rb +31 -0
  33. data/lib/charming/presentation/components/autocomplete.rb +108 -0
  34. data/lib/charming/presentation/components/badge.rb +31 -0
  35. data/lib/charming/presentation/components/breadcrumbs.rb +29 -0
  36. data/lib/charming/presentation/components/command_palette.rb +8 -5
  37. data/lib/charming/presentation/components/error_screen.rb +72 -0
  38. data/lib/charming/presentation/components/form.rb +9 -0
  39. data/lib/charming/presentation/components/fuzzy_matcher.rb +83 -0
  40. data/lib/charming/presentation/components/help_overlay.rb +65 -0
  41. data/lib/charming/presentation/components/markdown.rb +6 -2
  42. data/lib/charming/presentation/components/modal.rb +45 -5
  43. data/lib/charming/presentation/components/multi_select_list.rb +85 -0
  44. data/lib/charming/presentation/components/progressbar.rb +0 -1
  45. data/lib/charming/presentation/components/status_bar.rb +75 -0
  46. data/lib/charming/presentation/components/tab_bar.rb +103 -0
  47. data/lib/charming/presentation/components/table.rb +40 -9
  48. data/lib/charming/presentation/components/text_area.rb +47 -10
  49. data/lib/charming/presentation/components/text_input.rb +79 -4
  50. data/lib/charming/presentation/components/toast.rb +51 -0
  51. data/lib/charming/presentation/components/tree.rb +176 -0
  52. data/lib/charming/presentation/components/viewport/content_lines.rb +55 -0
  53. data/lib/charming/presentation/components/viewport/line_window.rb +71 -0
  54. data/lib/charming/presentation/components/viewport/position.rb +67 -0
  55. data/lib/charming/presentation/components/viewport.rb +37 -122
  56. data/lib/charming/presentation/layout/builder.rb +4 -1
  57. data/lib/charming/presentation/layout/overlay.rb +6 -4
  58. data/lib/charming/presentation/layout/pane.rb +2 -1
  59. data/lib/charming/presentation/layout/pane_geometry.rb +16 -8
  60. data/lib/charming/presentation/layout/screen_layout.rb +12 -3
  61. data/lib/charming/presentation/layout/split.rb +37 -3
  62. data/lib/charming/presentation/markdown/renderer.rb +99 -63
  63. data/lib/charming/presentation/markdown/style_config.rb +10 -5
  64. data/lib/charming/presentation/markdown/syntax_highlighter.rb +11 -1
  65. data/lib/charming/presentation/markdown/table_renderer.rb +60 -0
  66. data/lib/charming/presentation/markdown/text_wrapper.rb +40 -0
  67. data/lib/charming/presentation/markdown/url_resolver.rb +27 -0
  68. data/lib/charming/presentation/templates/erb_handler.rb +35 -2
  69. data/lib/charming/presentation/ui/ansi_codes.rb +11 -0
  70. data/lib/charming/presentation/ui/ansi_slicer.rb +20 -13
  71. data/lib/charming/presentation/ui/color_support.rb +129 -0
  72. data/lib/charming/presentation/ui/theme.rb +7 -0
  73. data/lib/charming/presentation/ui/themes/catppuccin-latte.json +35 -0
  74. data/lib/charming/presentation/ui/themes/catppuccin-mocha.json +35 -0
  75. data/lib/charming/presentation/ui/themes/gruvbox-dark.json +33 -0
  76. data/lib/charming/presentation/ui/themes/nord.json +32 -0
  77. data/lib/charming/presentation/ui/themes/tokyonight.json +34 -0
  78. data/lib/charming/presentation/ui/width.rb +27 -2
  79. data/lib/charming/router.rb +1 -1
  80. data/lib/charming/runtime.rb +122 -15
  81. data/lib/charming/tasks/cancelled.rb +11 -0
  82. data/lib/charming/tasks/inline_executor.rb +10 -4
  83. data/lib/charming/tasks/progress.rb +30 -0
  84. data/lib/charming/tasks/task.rb +24 -4
  85. data/lib/charming/tasks/threaded_executor.rb +35 -11
  86. data/lib/charming/test_helper.rb +120 -0
  87. data/lib/charming/version.rb +1 -1
  88. data/lib/charming.rb +43 -1
  89. metadata +36 -49
@@ -1,12 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "timeout"
4
+
3
5
  module Charming
4
6
  module Tasks
5
7
  # Task is the unit of work submitted to a task executor. It pairs a *name* (used by
6
- # `on_task` handlers to route the result) with a *block* to invoke on the executor.
7
- Task = Data.define(:name, :block) do
8
- # Invokes the task's block in the executor's thread and returns its value (or raises).
9
- def call = block.call
8
+ # `on_task` handlers to route the result) with a *block* to invoke on the executor,
9
+ # plus an optional *timeout* in seconds.
10
+ Task = Data.define(:name, :block, :timeout) do
11
+ def initialize(name:, block:, timeout: nil)
12
+ super
13
+ end
14
+
15
+ # Invokes the task's block, passing *progress* when the block accepts an argument.
16
+ # Enforces the timeout (raising Tasks::Cancelled) when one was configured.
17
+ def call(progress = nil)
18
+ return invoke(progress) unless timeout
19
+
20
+ Timeout.timeout(timeout, Cancelled, "task #{name} timed out after #{timeout}s") do
21
+ invoke(progress)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def invoke(progress)
28
+ block.arity.zero? ? block.call : block.call(progress)
29
+ end
10
30
  end
11
31
  end
12
32
  end
@@ -5,30 +5,53 @@ module Charming
5
5
  # ThreadedExecutor runs submitted tasks on background Ruby threads. Each submission
6
6
  # creates a new thread that invokes the block and pushes the resulting TaskEvent
7
7
  # onto the shared *queue*. Threads are tracked so `shutdown` can wait (or kill)
8
- # in-flight work.
8
+ # in-flight work, and tracked by name so `cancel` can interrupt a specific task.
9
9
  class ThreadedExecutor
10
10
  # *queue* is the thread-safe Queue (typically `runtime.@task_queue`) into which
11
11
  # completed TaskEvents are pushed.
12
12
  def initialize(queue)
13
13
  @queue = queue
14
14
  @threads = []
15
+ @threads_by_name = {}
15
16
  @mutex = Mutex.new
17
+ @shutting_down = false
16
18
  end
17
19
 
18
20
  # Wraps *block* in a Task and spawns a new thread to invoke it. The thread's
19
21
  # return value (or rescued exception) is pushed onto the queue as a TaskEvent.
20
- # Returns nil immediately.
21
- def submit(name, &block)
22
- task = Task.new(name: name.to_sym, block: block)
23
- thread = Thread.new(task) { |t| @queue << run(t) }
24
- @mutex.synchronize { @threads << thread }
22
+ # Blocks that accept an argument receive a Progress reporter. *timeout* (seconds)
23
+ # cancels the task when exceeded. Returns nil immediately. Raises if called after
24
+ # shutdown has begun.
25
+ def submit(name, timeout: nil, &block)
26
+ task = Task.new(name: name.to_sym, block: block, timeout: timeout)
27
+ @mutex.synchronize do
28
+ raise "cannot submit task after shutdown" if @shutting_down
29
+
30
+ thread = Thread.new(task) { |t| @queue << run(t) }
31
+ @threads << thread
32
+ @threads_by_name[task.name] = thread
33
+ end
34
+ nil
35
+ end
36
+
37
+ # Cancels the named in-flight task by raising Tasks::Cancelled in its thread.
38
+ # The task completes with a TaskEvent whose error is the Cancelled exception.
39
+ # No-op when the task isn't running.
40
+ def cancel(name)
41
+ thread = @mutex.synchronize { @threads_by_name[name.to_sym] }
42
+ return unless thread&.alive?
43
+
44
+ thread.raise(Cancelled, "task #{name} cancelled")
25
45
  nil
26
46
  end
27
47
 
28
48
  # Waits up to *timeout* seconds for in-flight threads to finish, then kills any
29
- # remaining live threads. Used by Runtime during teardown.
30
- def shutdown(timeout: 0.0)
31
- threads = @mutex.synchronize { @threads.dup }
49
+ # remaining live threads. Refuses new submissions once called.
50
+ def shutdown(timeout: 2.0)
51
+ threads = @mutex.synchronize do
52
+ @shutting_down = true
53
+ @threads.dup
54
+ end
32
55
  threads.each { |thread| thread.join(timeout) }
33
56
  threads.each do |thread|
34
57
  next unless thread.alive?
@@ -40,9 +63,10 @@ module Charming
40
63
 
41
64
  private
42
65
 
43
- # Invokes the task's block and wraps the result (or rescued exception) in a TaskEvent.
66
+ # Invokes the task's block (passing a Progress reporter) and wraps the result
67
+ # (or rescued exception) in a TaskEvent.
44
68
  def run(task)
45
- Events::TaskEvent.new(name: task.name, value: task.call)
69
+ Events::TaskEvent.new(name: task.name, value: task.call(Progress.new(@queue, task.name)))
46
70
  rescue StandardError, ScriptError => e
47
71
  Events::TaskEvent.new(name: task.name, error: e)
48
72
  end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "charming"
4
+
5
+ module Charming
6
+ # TestHelper provides controller- and component-level test ergonomics for Charming apps,
7
+ # in the spirit of Rails' ActionController::TestCase:
8
+ #
9
+ # require "charming/test_helper"
10
+ #
11
+ # RSpec.describe HomeController do
12
+ # include Charming::TestHelper
13
+ #
14
+ # let(:ctrl) { build_controller(HomeController) }
15
+ #
16
+ # it "renders a greeting" do
17
+ # expect(ctrl.dispatch(:show)).to render_text("Welcome")
18
+ # end
19
+ #
20
+ # it "quits on q" do
21
+ # expect(press(ctrl_class: HomeController, key: "q")).to be_quit
22
+ # end
23
+ # end
24
+ #
25
+ # Helpers:
26
+ # - `build_controller(klass, app:, screen:, route:)` — controller instance wired to an app
27
+ # - `key_event("ctrl+p")` — build a KeyEvent from a human-readable string
28
+ # - `press(controller_or_class, "down")` — dispatch a key press, returns the Response
29
+ # - `press_sequence(klass, ["down", "down", "enter"], app:)` — dispatch several presses
30
+ #
31
+ # RSpec matchers (when RSpec is loaded):
32
+ # - `expect(response).to render_text("...")` / `render_match(/.../)`
33
+ # - `expect(response).to be_quit` / `be_navigate` (predicate matchers on Response)
34
+ # - `expect(response).to navigate_to("/path")`
35
+ module TestHelper
36
+ # Builds a controller instance with sensible test defaults: a fresh Application,
37
+ # an 80x24 screen, and no event.
38
+ def build_controller(controller_class, app: nil, screen: nil, route: nil, event: nil)
39
+ app ||= Charming::Application.new
40
+ screen ||= Charming::Screen.new(width: 80, height: 24)
41
+ controller_class.new(application: app, event: event, screen: screen, route: route)
42
+ end
43
+
44
+ # Builds a KeyEvent from a human-readable string like "q", "down", "ctrl+p",
45
+ # or "shift+tab". Modifier order is irrelevant.
46
+ def key_event(description)
47
+ parts = description.to_s.split("+")
48
+ key = parts.pop
49
+ modifiers = parts.map(&:downcase)
50
+ char = (key.length == 1) ? key : nil
51
+ Charming::Events::KeyEvent.new(
52
+ key: key.to_sym,
53
+ char: char,
54
+ ctrl: modifiers.include?("ctrl") || modifiers.include?("control"),
55
+ alt: modifiers.include?("alt"),
56
+ shift: modifiers.include?("shift")
57
+ )
58
+ end
59
+
60
+ # Dispatches a single key press against *controller_class* and returns the Response.
61
+ # Pass `app:` to share session state across presses.
62
+ def press(controller_class, key, app:, screen: nil, route: nil)
63
+ controller = build_controller(controller_class, app: app, screen: screen, route: route, event: key_event(key))
64
+ controller.dispatch_key
65
+ end
66
+
67
+ # Dispatches each key in *keys* in order against fresh controller instances sharing
68
+ # *app*'s session (mirroring the runtime's controller-per-event model). Returns the
69
+ # last Response.
70
+ def press_sequence(controller_class, keys, app:, screen: nil, route: nil)
71
+ keys.map { |key| press(controller_class, key, app: app, screen: screen, route: route) }.last
72
+ end
73
+
74
+ # Builds a MemoryBackend pre-seeded with KeyEvents parsed from *keys*, ready to be
75
+ # passed to Charming::Runtime for integration-style tests.
76
+ def memory_backend(*keys, width: 80, height: 24)
77
+ events = keys.map { |key| key.is_a?(String) ? key_event(key) : key }
78
+ Charming::Internal::Terminal::MemoryBackend.new(events: events, width: width, height: height)
79
+ end
80
+ end
81
+ end
82
+
83
+ if defined?(RSpec)
84
+ # Both matchers compare against the ANSI-stripped body: styled output interleaves
85
+ # escape codes mid-phrase (each styled segment emits its own codes), so raw
86
+ # substring matching would fail on any text spanning two styles.
87
+ RSpec::Matchers.define :render_text do |expected|
88
+ match do |response|
89
+ response.respond_to?(:body) &&
90
+ Charming::UI::Width.strip_ansi(response.body.to_s).include?(expected)
91
+ end
92
+
93
+ failure_message do |response|
94
+ body = response.respond_to?(:body) ? Charming::UI::Width.strip_ansi(response.body.to_s) : response.inspect
95
+ "expected response body to include #{expected.inspect}, got:\n#{body}"
96
+ end
97
+ end
98
+
99
+ RSpec::Matchers.define :render_match do |pattern|
100
+ match do |response|
101
+ response.respond_to?(:body) &&
102
+ Charming::UI::Width.strip_ansi(response.body.to_s).match?(pattern)
103
+ end
104
+
105
+ failure_message do |response|
106
+ body = response.respond_to?(:body) ? Charming::UI::Width.strip_ansi(response.body.to_s) : response.inspect
107
+ "expected response body to match #{pattern.inspect}, got:\n#{body}"
108
+ end
109
+ end
110
+
111
+ RSpec::Matchers.define :navigate_to do |expected_path|
112
+ match do |response|
113
+ response.respond_to?(:navigate?) && response.navigate? && response.path == expected_path
114
+ end
115
+
116
+ failure_message do |response|
117
+ "expected a navigation response to #{expected_path.inspect}, got: #{response.inspect}"
118
+ end
119
+ end
120
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Charming
4
- VERSION = "0.2.0"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/charming.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/inflector"
4
+ require "active_support/string_inquirer"
4
5
  require "logger"
5
6
  require "zeitwerk"
6
7
 
@@ -15,7 +16,8 @@ loader.inflector.inflect(
15
16
  "erb_handler" => "ErbHandler",
16
17
  "key_normalizer" => "KeyNormalizer",
17
18
  "mouse_parser" => "MouseParser",
18
- "tty_backend" => "TTYBackend"
19
+ "tty_backend" => "TTYBackend",
20
+ "url_resolver" => "URLResolver"
19
21
  )
20
22
  loader.collapse("#{__dir__}/charming/presentation")
21
23
  loader.setup
@@ -24,6 +26,17 @@ module Charming
24
26
  # Base error class for all Charming-specific exceptions (used by templates, generators, runtime, etc.).
25
27
  class Error < StandardError; end
26
28
 
29
+ # The current environment, read from CHARMING_ENV (default "development"). Returns a
30
+ # StringInquirer so callers can write `Charming.env.test?` / `Charming.env.production?`.
31
+ def self.env
32
+ @env ||= ActiveSupport::StringInquirer.new(ENV["CHARMING_ENV"] || "development")
33
+ end
34
+
35
+ # Overrides the environment (used by tests and the CLI).
36
+ def self.env=(value)
37
+ @env = value.nil? ? nil : ActiveSupport::StringInquirer.new(value.to_s)
38
+ end
39
+
27
40
  # Entry point for running a Charming application. Instantiates a Runtime for *application* and starts
28
41
  # the event loop. *backend* defaults to TTYBackend; tests pass MemoryBackend directly via `Charming::Runtime.new`.
29
42
  def self.run(application, backend: nil)
@@ -36,6 +49,35 @@ module Charming
36
49
  key = event.respond_to?(:key) ? event.key : event
37
50
  key.to_sym
38
51
  end
52
+
53
+ # Returns the key signature used for controller bindings, including modifier flags.
54
+ def self.key_signature(event)
55
+ key = key_of(event)
56
+ modifiers = []
57
+ modifiers << "ctrl" if event.respond_to?(:ctrl) && event.ctrl
58
+ modifiers << "alt" if event.respond_to?(:alt) && event.alt
59
+ modifiers << "shift" if event.respond_to?(:shift) && event.shift
60
+
61
+ return key if modifiers.empty?
62
+
63
+ :"#{modifiers.join("+")}+#{key}"
64
+ end
65
+
66
+ # Normalizes key declarations so `control+p` and `ctrl+p` resolve to the same binding.
67
+ def self.key_binding_name(name)
68
+ parts = name.to_s.split("+")
69
+ return name.to_sym if parts.size == 1
70
+
71
+ key = parts.pop.downcase
72
+ modifiers = parts.map do |part|
73
+ modifier = part.downcase
74
+ (modifier == "control") ? "ctrl" : modifier
75
+ end
76
+ ordered_modifiers = %w[ctrl alt shift].select { |modifier| modifiers.include?(modifier) }
77
+ ordered_modifiers += modifiers - ordered_modifiers
78
+
79
+ :"#{(ordered_modifiers + [key]).join("+")}"
80
+ end
39
81
  end
40
82
 
41
83
  Charming::Templates.register ".tui.erb", Charming::Templates::ErbHandler
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: charming
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - pando
@@ -29,26 +29,6 @@ dependencies:
29
29
  - - ">="
30
30
  - !ruby/object:Gem::Version
31
31
  version: 8.1.2
32
- - !ruby/object:Gem::Dependency
33
- name: activerecord
34
- requirement: !ruby/object:Gem::Requirement
35
- requirements:
36
- - - "~>"
37
- - !ruby/object:Gem::Version
38
- version: '8.1'
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- version: 8.1.2
42
- type: :runtime
43
- prerelease: false
44
- version_requirements: !ruby/object:Gem::Requirement
45
- requirements:
46
- - - "~>"
47
- - !ruby/object:Gem::Version
48
- version: '8.1'
49
- - - ">="
50
- - !ruby/object:Gem::Version
51
- version: 8.1.2
52
32
  - !ruby/object:Gem::Dependency
53
33
  name: activesupport
54
34
  requirement: !ruby/object:Gem::Requirement
@@ -111,20 +91,6 @@ dependencies:
111
91
  - - "~>"
112
92
  - !ruby/object:Gem::Version
113
93
  version: '5.0'
114
- - !ruby/object:Gem::Dependency
115
- name: sqlite3
116
- requirement: !ruby/object:Gem::Requirement
117
- requirements:
118
- - - "~>"
119
- - !ruby/object:Gem::Version
120
- version: '2.0'
121
- type: :runtime
122
- prerelease: false
123
- version_requirements: !ruby/object:Gem::Requirement
124
- requirements:
125
- - - "~>"
126
- - !ruby/object:Gem::Version
127
- version: '2.0'
128
94
  - !ruby/object:Gem::Dependency
129
95
  name: tty-cursor
130
96
  requirement: !ruby/object:Gem::Requirement
@@ -153,20 +119,6 @@ dependencies:
153
119
  - - "~>"
154
120
  - !ruby/object:Gem::Version
155
121
  version: '2.6'
156
- - !ruby/object:Gem::Dependency
157
- name: tty-progressbar
158
- requirement: !ruby/object:Gem::Requirement
159
- requirements:
160
- - - "~>"
161
- - !ruby/object:Gem::Version
162
- version: '0.18'
163
- type: :runtime
164
- prerelease: false
165
- version_requirements: !ruby/object:Gem::Requirement
166
- requirements:
167
- - - "~>"
168
- - !ruby/object:Gem::Version
169
- version: '0.18'
170
122
  - !ruby/object:Gem::Dependency
171
123
  name: tty-table
172
124
  requirement: !ruby/object:Gem::Requirement
@@ -238,8 +190,11 @@ files:
238
190
  - lib/charming.rb
239
191
  - lib/charming/application.rb
240
192
  - lib/charming/application_state.rb
193
+ - lib/charming/audio/player.rb
194
+ - lib/charming/audio/system.rb
241
195
  - lib/charming/cli.rb
242
196
  - lib/charming/controller.rb
197
+ - lib/charming/controller/action_hooks.rb
243
198
  - lib/charming/controller/class_methods.rb
244
199
  - lib/charming/controller/command_palette.rb
245
200
  - lib/charming/controller/component_dispatching.rb
@@ -250,10 +205,13 @@ files:
250
205
  - lib/charming/controller/session_state.rb
251
206
  - lib/charming/controller/sidebar_navigation.rb
252
207
  - lib/charming/database/commands.rb
208
+ - lib/charming/events/focus_event.rb
253
209
  - lib/charming/events/key_event.rb
254
210
  - lib/charming/events/mouse_event.rb
211
+ - lib/charming/events/paste_event.rb
255
212
  - lib/charming/events/resize_event.rb
256
213
  - lib/charming/events/task_event.rb
214
+ - lib/charming/events/task_progress_event.rb
257
215
  - lib/charming/events/timer_event.rb
258
216
  - lib/charming/generators/app_file_generator.rb
259
217
  - lib/charming/generators/app_generator.rb
@@ -262,6 +220,8 @@ files:
262
220
  - lib/charming/generators/controller_generator.rb
263
221
  - lib/charming/generators/database_installer.rb
264
222
  - lib/charming/generators/error.rb
223
+ - lib/charming/generators/migration_generator.rb
224
+ - lib/charming/generators/migration_timestamp.rb
265
225
  - lib/charming/generators/model_generator.rb
266
226
  - lib/charming/generators/name.rb
267
227
  - lib/charming/generators/screen_generator.rb
@@ -310,9 +270,14 @@ files:
310
270
  - lib/charming/internal/terminal/tty_backend.rb
311
271
  - lib/charming/presentation/component.rb
312
272
  - lib/charming/presentation/components/activity_indicator.rb
273
+ - lib/charming/presentation/components/audio.rb
274
+ - lib/charming/presentation/components/autocomplete.rb
275
+ - lib/charming/presentation/components/badge.rb
276
+ - lib/charming/presentation/components/breadcrumbs.rb
313
277
  - lib/charming/presentation/components/command_palette.rb
314
278
  - lib/charming/presentation/components/command_palette_modal.rb
315
279
  - lib/charming/presentation/components/empty_state.rb
280
+ - lib/charming/presentation/components/error_screen.rb
316
281
  - lib/charming/presentation/components/form.rb
317
282
  - lib/charming/presentation/components/form/builder.rb
318
283
  - lib/charming/presentation/components/form/confirm.rb
@@ -321,16 +286,26 @@ files:
321
286
  - lib/charming/presentation/components/form/note.rb
322
287
  - lib/charming/presentation/components/form/select.rb
323
288
  - lib/charming/presentation/components/form/textarea.rb
289
+ - lib/charming/presentation/components/fuzzy_matcher.rb
290
+ - lib/charming/presentation/components/help_overlay.rb
324
291
  - lib/charming/presentation/components/keyboard_handler.rb
325
292
  - lib/charming/presentation/components/list.rb
326
293
  - lib/charming/presentation/components/markdown.rb
327
294
  - lib/charming/presentation/components/modal.rb
295
+ - lib/charming/presentation/components/multi_select_list.rb
328
296
  - lib/charming/presentation/components/progressbar.rb
329
297
  - lib/charming/presentation/components/spinner.rb
298
+ - lib/charming/presentation/components/status_bar.rb
299
+ - lib/charming/presentation/components/tab_bar.rb
330
300
  - lib/charming/presentation/components/table.rb
331
301
  - lib/charming/presentation/components/text_area.rb
332
302
  - lib/charming/presentation/components/text_input.rb
303
+ - lib/charming/presentation/components/toast.rb
304
+ - lib/charming/presentation/components/tree.rb
333
305
  - lib/charming/presentation/components/viewport.rb
306
+ - lib/charming/presentation/components/viewport/content_lines.rb
307
+ - lib/charming/presentation/components/viewport/line_window.rb
308
+ - lib/charming/presentation/components/viewport/position.rb
334
309
  - lib/charming/presentation/layout.rb
335
310
  - lib/charming/presentation/layout/builder.rb
336
311
  - lib/charming/presentation/layout/overlay.rb
@@ -346,6 +321,9 @@ files:
346
321
  - lib/charming/presentation/markdown/renderer.rb
347
322
  - lib/charming/presentation/markdown/style_config.rb
348
323
  - lib/charming/presentation/markdown/syntax_highlighter.rb
324
+ - lib/charming/presentation/markdown/table_renderer.rb
325
+ - lib/charming/presentation/markdown/text_wrapper.rb
326
+ - lib/charming/presentation/markdown/url_resolver.rb
349
327
  - lib/charming/presentation/template_view.rb
350
328
  - lib/charming/presentation/templates.rb
351
329
  - lib/charming/presentation/templates/erb_handler.rb
@@ -355,18 +333,27 @@ files:
355
333
  - lib/charming/presentation/ui/border.rb
356
334
  - lib/charming/presentation/ui/border_painter.rb
357
335
  - lib/charming/presentation/ui/canvas.rb
336
+ - lib/charming/presentation/ui/color_support.rb
358
337
  - lib/charming/presentation/ui/style.rb
359
338
  - lib/charming/presentation/ui/theme.rb
339
+ - lib/charming/presentation/ui/themes/catppuccin-latte.json
340
+ - lib/charming/presentation/ui/themes/catppuccin-mocha.json
341
+ - lib/charming/presentation/ui/themes/gruvbox-dark.json
342
+ - lib/charming/presentation/ui/themes/nord.json
360
343
  - lib/charming/presentation/ui/themes/phosphor.json
344
+ - lib/charming/presentation/ui/themes/tokyonight.json
361
345
  - lib/charming/presentation/ui/width.rb
362
346
  - lib/charming/presentation/view.rb
363
347
  - lib/charming/response.rb
364
348
  - lib/charming/router.rb
365
349
  - lib/charming/runtime.rb
366
350
  - lib/charming/screen.rb
351
+ - lib/charming/tasks/cancelled.rb
367
352
  - lib/charming/tasks/inline_executor.rb
353
+ - lib/charming/tasks/progress.rb
368
354
  - lib/charming/tasks/task.rb
369
355
  - lib/charming/tasks/threaded_executor.rb
356
+ - lib/charming/test_helper.rb
370
357
  - lib/charming/version.rb
371
358
  - sig/charming.rbs
372
359
  homepage: https://github.com/pandorocks/charming