fatty 0.99.8.2 → 0.99.8.4
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.
- checksums.yaml +4 -4
- data/lib/fatty/config.rb +49 -1
- data/lib/fatty/curses/context.rb +10 -0
- data/lib/fatty/input_field.rb +6 -2
- data/lib/fatty/renderer/curses.rb +6 -0
- data/lib/fatty/renderer.rb +4 -0
- data/lib/fatty/session/output_session.rb +18 -3
- data/lib/fatty/terminal.rb +26 -3
- data/lib/fatty/themes/manager.rb +2 -0
- data/lib/fatty/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b46a42de2b3925689cb1b1383a85b01e143493db9271294b031eb78e3bcceb6b
|
|
4
|
+
data.tar.gz: 5ace58f489a5a0cfc0dfd623b76d2ff3f0074da7ceb776cbea86b25c42f49eaa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d01d6ee255dc289fe9828c96a16aaf267abdf4d3a95fb1e9eb26cee0dbdb1cc0523d66dd7f6cfd14a200a0654cb49df4c483e74b56865007e58a2365fb63e4d
|
|
7
|
+
data.tar.gz: 12aa3e2a67f801583f43a6de8e07dc560fb247c61d90cafe2285c49c4f12502307dde31f5c07942f982f272a3b19933f981527bb378628b4bb5a3343532f699d
|
data/lib/fatty/config.rb
CHANGED
|
@@ -148,6 +148,35 @@ module Fatty
|
|
|
148
148
|
File.join(app_user_config_dir, "#{name}.yml")
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
+
# Persist a user-adjustable preference in the current application's config
|
|
152
|
+
# file. Preferences are kept in the app layer so applications using Fatty
|
|
153
|
+
# do not overwrite one another's selections. When no app layer is
|
|
154
|
+
# configured, use Fatty's global user config.
|
|
155
|
+
def self.set_preference(name, value)
|
|
156
|
+
path = app_config_path || File.join(user_config_dir, "config.yml")
|
|
157
|
+
|
|
158
|
+
config = if app_config_path
|
|
159
|
+
read_app("config")
|
|
160
|
+
elsif File.exist?(path)
|
|
161
|
+
YAML.safe_load_file(
|
|
162
|
+
path,
|
|
163
|
+
aliases: true,
|
|
164
|
+
permitted_classes: [Date, DateTime, Time],
|
|
165
|
+
symbolize_names: true,
|
|
166
|
+
) || {}
|
|
167
|
+
else
|
|
168
|
+
{}
|
|
169
|
+
end
|
|
170
|
+
config[name.to_sym] = value
|
|
171
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
172
|
+
File.write(path, YAML.dump(yaml_safe_value(config)))
|
|
173
|
+
reset_reader!
|
|
174
|
+
true
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
Fatty.warn("Could not save preference #{name.inspect}: #{e.class}: #{e.message}", tag: :config)
|
|
177
|
+
false
|
|
178
|
+
end
|
|
179
|
+
|
|
151
180
|
# The per-app themes directory, either under
|
|
152
181
|
# ~/.config/fatty/apps/<app_name>/themes (by default) or under the themes
|
|
153
182
|
# directory in the config directory the library consumer sets in
|
|
@@ -216,7 +245,12 @@ module Fatty
|
|
|
216
245
|
return {} unless path
|
|
217
246
|
return {} unless File.exist?(path)
|
|
218
247
|
|
|
219
|
-
YAML.
|
|
248
|
+
YAML.safe_load_file(
|
|
249
|
+
path,
|
|
250
|
+
aliases: true,
|
|
251
|
+
permitted_classes: [Date, DateTime, Time],
|
|
252
|
+
symbolize_names: true,
|
|
253
|
+
) || {}
|
|
220
254
|
end
|
|
221
255
|
|
|
222
256
|
def self.merge_config(base, overlay)
|
|
@@ -256,6 +290,19 @@ module Fatty
|
|
|
256
290
|
value || {}
|
|
257
291
|
end
|
|
258
292
|
|
|
293
|
+
def self.yaml_safe_value(value)
|
|
294
|
+
case value
|
|
295
|
+
when Hash
|
|
296
|
+
value.to_h { |key, item| [key.to_s, yaml_safe_value(item)] }
|
|
297
|
+
when Array
|
|
298
|
+
value.map { |item| yaml_safe_value(item) }
|
|
299
|
+
when Symbol
|
|
300
|
+
value.to_s
|
|
301
|
+
else
|
|
302
|
+
value
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
259
306
|
def self.normalize_app_name(name)
|
|
260
307
|
text = name.to_s.strip
|
|
261
308
|
text.empty? ? nil : text
|
|
@@ -269,6 +316,7 @@ module Fatty
|
|
|
269
316
|
private_class_method :merge_config
|
|
270
317
|
private_class_method :blank_config?
|
|
271
318
|
private_class_method :normalize_config_value
|
|
319
|
+
private_class_method :yaml_safe_value
|
|
272
320
|
private_class_method :normalize_app_name
|
|
273
321
|
end
|
|
274
322
|
end
|
data/lib/fatty/curses/context.rb
CHANGED
|
@@ -153,7 +153,9 @@ module Fatty
|
|
|
153
153
|
def resume
|
|
154
154
|
return unless @started
|
|
155
155
|
|
|
156
|
+
::Curses.reset_prog_mode
|
|
156
157
|
::Curses.refresh
|
|
158
|
+
touch_windows!
|
|
157
159
|
enable_bracketed_paste!
|
|
158
160
|
nil
|
|
159
161
|
end
|
|
@@ -171,6 +173,14 @@ module Fatty
|
|
|
171
173
|
@started = false
|
|
172
174
|
end
|
|
173
175
|
|
|
176
|
+
def touch_windows!
|
|
177
|
+
[@output_win, @status_win, @input_win, @alert_win].each do |win|
|
|
178
|
+
win.touch if win&.respond_to?(:touch)
|
|
179
|
+
end
|
|
180
|
+
::Curses.stdscr.touch if ::Curses.respond_to?(:stdscr) && ::Curses.stdscr.respond_to?(:touch)
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
174
184
|
# Map a Fatty::Ansi::Style to a curses attribute.
|
|
175
185
|
#
|
|
176
186
|
# - If style has no explicit fg/bg, we keep the themed role pair.
|
data/lib/fatty/input_field.rb
CHANGED
|
@@ -519,9 +519,13 @@ module Fatty
|
|
|
519
519
|
end
|
|
520
520
|
|
|
521
521
|
def autosuggestion_suffix
|
|
522
|
-
|
|
522
|
+
suggestion = autosuggestion.to_s
|
|
523
|
+
text = buffer.text.to_s
|
|
524
|
+
return "" if suggestion.empty?
|
|
525
|
+
return "" unless suggestion.start_with?(text)
|
|
526
|
+
return "" if suggestion == text
|
|
523
527
|
|
|
524
|
-
|
|
528
|
+
suggestion.delete_prefix(text)
|
|
525
529
|
end
|
|
526
530
|
|
|
527
531
|
def accept_autosuggestion!
|
data/lib/fatty/renderer.rb
CHANGED
|
@@ -24,6 +24,7 @@ module Fatty
|
|
|
24
24
|
@line_numbers = false
|
|
25
25
|
@narrow_query = nil
|
|
26
26
|
@visible_lines = nil
|
|
27
|
+
@pending_command_reset = false
|
|
27
28
|
mode = Fatty::Config.config.dig(:output, :mode)&.to_sym || :paging
|
|
28
29
|
@default_output_mode = mode
|
|
29
30
|
@pager = Fatty::Pager.new(output: @output, viewport: @viewport, mode: mode, lines: -> { visible_lines })
|
|
@@ -52,6 +53,7 @@ module Fatty
|
|
|
52
53
|
[]
|
|
53
54
|
end
|
|
54
55
|
when :append
|
|
56
|
+
prepare_for_command_output!
|
|
55
57
|
case payload[:mode]
|
|
56
58
|
when :scrolling
|
|
57
59
|
pager.toggle_paging_mode if pager.mode == :paging
|
|
@@ -80,16 +82,21 @@ module Fatty
|
|
|
80
82
|
[]
|
|
81
83
|
when :clear
|
|
82
84
|
reset_output!
|
|
85
|
+
renderer.clear_physical_screen!
|
|
83
86
|
[]
|
|
84
87
|
when :resize
|
|
85
88
|
resize_output!
|
|
86
89
|
[]
|
|
87
90
|
when :begin_command
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
@narrow_query = nil
|
|
92
|
+
invalidate_visible_lines!
|
|
93
|
+
@pending_command_reset = true
|
|
90
94
|
[]
|
|
91
95
|
when :finish_command
|
|
92
|
-
|
|
96
|
+
unless @pending_command_reset
|
|
97
|
+
pager.finish_command!
|
|
98
|
+
end
|
|
99
|
+
@pending_command_reset = false
|
|
93
100
|
[]
|
|
94
101
|
when :quit_paging
|
|
95
102
|
pager.quit
|
|
@@ -508,6 +515,14 @@ module Fatty
|
|
|
508
515
|
@pager.reset!(mode: mode)
|
|
509
516
|
end
|
|
510
517
|
|
|
518
|
+
def prepare_for_command_output!
|
|
519
|
+
return unless @pending_command_reset
|
|
520
|
+
|
|
521
|
+
reset_for_command!
|
|
522
|
+
pager.begin_command!(anchor: output.lines.length)
|
|
523
|
+
@pending_command_reset = false
|
|
524
|
+
end
|
|
525
|
+
|
|
511
526
|
# When the pager is active, the last output row is reserved for the pager
|
|
512
527
|
# InputField. This returns the viewport to use for rendering the output
|
|
513
528
|
# content itself.
|
data/lib/fatty/terminal.rb
CHANGED
|
@@ -97,6 +97,7 @@ module Fatty
|
|
|
97
97
|
@last_render_time = nil
|
|
98
98
|
@restore_cursor_after_render = true
|
|
99
99
|
@render_count = 0
|
|
100
|
+
@deferred_render = false
|
|
100
101
|
@deferred_count = 0
|
|
101
102
|
end
|
|
102
103
|
|
|
@@ -161,6 +162,9 @@ module Fatty
|
|
|
161
162
|
loop_count = 0
|
|
162
163
|
while @running
|
|
163
164
|
loop_count += 1
|
|
165
|
+
deferred_render = @deferred_render
|
|
166
|
+
@deferred_render = false
|
|
167
|
+
render_frame if deferred_render
|
|
164
168
|
@session_dirty = false
|
|
165
169
|
@immediate_render = false
|
|
166
170
|
if (cmd = event_source.next_event)
|
|
@@ -194,8 +198,11 @@ module Fatty
|
|
|
194
198
|
yield
|
|
195
199
|
ensure
|
|
196
200
|
ctx.resume
|
|
201
|
+
reconcile_terminal_size!
|
|
197
202
|
renderer.invalidate!
|
|
203
|
+
renderer.clear_physical_screen! if renderer.context.truecolor
|
|
198
204
|
render_frame
|
|
205
|
+
@deferred_render = true
|
|
199
206
|
end
|
|
200
207
|
|
|
201
208
|
# A command is either bound for this Terminal or it's meant to be
|
|
@@ -470,9 +477,9 @@ module Fatty
|
|
|
470
477
|
command.action == :resize
|
|
471
478
|
end
|
|
472
479
|
|
|
473
|
-
def handle_resize
|
|
474
|
-
rows
|
|
475
|
-
cols
|
|
480
|
+
def handle_resize(rows: nil, cols: nil)
|
|
481
|
+
rows ||= ::Curses.lines
|
|
482
|
+
cols ||= ::Curses.cols
|
|
476
483
|
size = [rows, cols]
|
|
477
484
|
|
|
478
485
|
return [] if size == @last_handled_resize_size
|
|
@@ -520,6 +527,22 @@ module Fatty
|
|
|
520
527
|
@inside_resize_term = false if did_resize_term
|
|
521
528
|
end
|
|
522
529
|
|
|
530
|
+
def reconcile_terminal_size!
|
|
531
|
+
return unless @screen && @ctx
|
|
532
|
+
|
|
533
|
+
rows, cols = STDOUT.winsize
|
|
534
|
+
return if rows.to_i <= 0 || cols.to_i <= 0
|
|
535
|
+
|
|
536
|
+
if [rows, cols] == [@screen.rows, @screen.cols]
|
|
537
|
+
@ctx.apply_layout(@screen)
|
|
538
|
+
renderer.screen = @screen
|
|
539
|
+
else
|
|
540
|
+
handle_resize(rows:, cols:)
|
|
541
|
+
end
|
|
542
|
+
rescue SystemCallError, IOError
|
|
543
|
+
nil
|
|
544
|
+
end
|
|
545
|
+
|
|
523
546
|
# --- Initialization ---------------------------------------------------------
|
|
524
547
|
|
|
525
548
|
#
|
data/lib/fatty/themes/manager.rb
CHANGED
data/lib/fatty/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fatty
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.99.8.
|
|
4
|
+
version: 0.99.8.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel E. Doherty
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 2026-09-18 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: curses
|
|
@@ -263,7 +263,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
263
263
|
- !ruby/object:Gem::Version
|
|
264
264
|
version: '0'
|
|
265
265
|
requirements: []
|
|
266
|
-
rubygems_version:
|
|
266
|
+
rubygems_version: 3.6.2
|
|
267
267
|
specification_version: 4
|
|
268
268
|
summary: Stateful terminal UI with editable input and scrollback
|
|
269
269
|
test_files: []
|