slk 0.12.1 → 0.13.0
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/CHANGELOG.md +7 -0
- data/lib/slk/commands/later.rb +104 -28
- data/lib/slk/commands/messages.rb +1 -22
- data/lib/slk/formatters/saved_item_formatter.rb +38 -2
- data/lib/slk/support/attachment_fetching.rb +38 -0
- data/lib/slk/support/image_preview.rb +93 -0
- data/lib/slk/version.rb +1 -1
- data/lib/slk.rb +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbd57363408aa79edfa6cbbe3e875034d2fbcb9e3c2a7ec8ea1e9acea30c9167
|
|
4
|
+
data.tar.gz: bf9ab8b668ad9d04191ddb9b63a1c502d3652059c3b5bbc16113dff1d94eb8bc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71d05dc4a9f5b46c49e5a14cad5c5e9b79042ece234142bb16f53dde80da67a5beafd1a2af0f30ef33b2bb8ef5381d64393979e640c996810bb536005e9a8180
|
|
7
|
+
data.tar.gz: 49956b5f79adb5e03f4bac033109626a315f9a1326904e321d4dcf2e17d189d7ac500bb820e2dab52199243d916c204b6ecef94b0c445c8287d591bdd1bfaa20
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.0] - 2026-10-09
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `slk later` now lists each saved message's files (`[File: name]`) and attachment images (`[Image: name]`), and prints an "N files not downloaded" hint like `slk messages`. Pass `--fetch-attachments` to download them to `~/.cache/slk/files/` and show the local paths. With `--json`, it also adds a `file_paths` map (file ID to local path).
|
|
15
|
+
- With `--fetch-attachments`, `slk later` draws downloaded images inline under their `[File:]` line in Ghostty, Kitty, iTerm2 and WezTerm, including inside tmux. This uses [chafa](https://hpjansson.org/chafa/) when it's installed. In other terminals, without chafa, or when output is piped, only the local path is shown. Pass `--no-image-preview` or set `SLK_IMAGE_PREVIEW=0` to download without drawing images.
|
|
16
|
+
|
|
10
17
|
## [0.12.1] - 2026-10-06
|
|
11
18
|
|
|
12
19
|
### Fixed
|
data/lib/slk/commands/later.rb
CHANGED
|
@@ -9,6 +9,17 @@ module Slk
|
|
|
9
9
|
# rubocop:disable Metrics/ClassLength
|
|
10
10
|
class Later < Base
|
|
11
11
|
include Support::InlineImages
|
|
12
|
+
include Support::AttachmentFetching
|
|
13
|
+
include Support::ImagePreview
|
|
14
|
+
|
|
15
|
+
BOOLEAN_FLAGS = {
|
|
16
|
+
'--completed' => :completed,
|
|
17
|
+
'--in-progress' => :in_progress,
|
|
18
|
+
'--counts' => :counts,
|
|
19
|
+
'--no-content' => :no_content,
|
|
20
|
+
'--no-image-preview' => :no_image_preview,
|
|
21
|
+
'--workspace-emoji' => :workspace_emoji
|
|
22
|
+
}.freeze
|
|
12
23
|
|
|
13
24
|
def execute
|
|
14
25
|
result = validate_options
|
|
@@ -86,6 +97,7 @@ module Slk
|
|
|
86
97
|
unless @options[:no_content]
|
|
87
98
|
message = fetch_message_content(workspace, item)
|
|
88
99
|
json_item[:message] = message if message
|
|
100
|
+
add_json_file_paths(json_item, workspace, item, message)
|
|
89
101
|
end
|
|
90
102
|
|
|
91
103
|
json_item
|
|
@@ -93,7 +105,13 @@ module Slk
|
|
|
93
105
|
end
|
|
94
106
|
# rubocop:enable Metrics/MethodLength
|
|
95
107
|
|
|
96
|
-
|
|
108
|
+
def add_json_file_paths(json_item, workspace, item, message)
|
|
109
|
+
return unless @options[:fetch_attachments]
|
|
110
|
+
|
|
111
|
+
paths = attachment_paths_for(workspace, item, message)
|
|
112
|
+
json_item[:file_paths] = paths unless paths.empty?
|
|
113
|
+
end
|
|
114
|
+
|
|
97
115
|
def display_formatted(workspace, items)
|
|
98
116
|
if items.empty?
|
|
99
117
|
puts 'No saved items found.'
|
|
@@ -101,37 +119,93 @@ module Slk
|
|
|
101
119
|
end
|
|
102
120
|
|
|
103
121
|
@fetch_failures = 0
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
display_with_workspace_emoji(workspace, items)
|
|
107
|
-
else
|
|
108
|
-
display_without_workspace_emoji(workspace, items)
|
|
109
|
-
end
|
|
122
|
+
@displayed_messages = []
|
|
123
|
+
display_each_item(workspace, items)
|
|
110
124
|
show_fetch_failure_summary
|
|
125
|
+
print_file_summary(@displayed_messages) unless @options[:fetch_attachments]
|
|
111
126
|
end
|
|
112
|
-
# rubocop:enable Metrics/MethodLength
|
|
113
127
|
|
|
114
|
-
def
|
|
115
|
-
|
|
128
|
+
def display_each_item(workspace, items)
|
|
129
|
+
printer = buffered_display? ? method(:display_item_buffered) : method(:display_item_direct)
|
|
116
130
|
items.each do |item|
|
|
117
131
|
message = @options[:no_content] ? nil : fetch_message_content(workspace, item)
|
|
118
|
-
|
|
132
|
+
printer.call(item, workspace, message, attachment_paths_for(workspace, item, message))
|
|
119
133
|
end
|
|
120
134
|
end
|
|
121
135
|
|
|
122
|
-
def
|
|
123
|
-
|
|
136
|
+
def display_item_direct(item, workspace, message, file_paths)
|
|
137
|
+
@direct_formatter ||= build_formatter(output)
|
|
138
|
+
format_item(@direct_formatter, item, workspace, message, file_paths)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Capture one item's output, then reprint it line by line so workspace
|
|
142
|
+
# emoji and downloaded images can be drawn inline as each item arrives.
|
|
143
|
+
def display_item_buffered(item, workspace, message, file_paths)
|
|
124
144
|
buffer = StringIO.new
|
|
125
|
-
|
|
126
|
-
|
|
145
|
+
format_item(build_formatter(create_buffer_output(buffer)), item, workspace, message, file_paths)
|
|
146
|
+
previews = preview_paths(file_paths)
|
|
147
|
+
buffer.string.each_line do |line|
|
|
148
|
+
print_buffered_line(line.chomp, workspace)
|
|
149
|
+
print_line_previews(line, previews)
|
|
150
|
+
end
|
|
151
|
+
end
|
|
127
152
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
153
|
+
def format_item(formatter, item, workspace, message, file_paths)
|
|
154
|
+
formatter.display_item(item, workspace, message: message, width: display_width,
|
|
155
|
+
truncate: @options[:truncate], file_paths: file_paths)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def print_buffered_line(line, workspace)
|
|
159
|
+
if workspace_emoji_images?
|
|
160
|
+
print_with_workspace_emoji(line, workspace)
|
|
161
|
+
else
|
|
162
|
+
puts line
|
|
131
163
|
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Draw a preview under the [File: ...] / [Image: ...] line that names it
|
|
167
|
+
def print_line_previews(line, previews)
|
|
168
|
+
previews.each do |path|
|
|
169
|
+
next unless line.include?("[File: #{path}]") || line.include?("[Image: #{path}]")
|
|
132
170
|
|
|
133
|
-
|
|
134
|
-
|
|
171
|
+
previews.delete(path)
|
|
172
|
+
print_image_preview(path)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def preview_paths(file_paths)
|
|
177
|
+
return [] unless attachment_previews?
|
|
178
|
+
|
|
179
|
+
file_paths.values.uniq.select { |path| previewable_image?(path) }
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Workspace emoji are terminal escape sequences, so skip them in markdown mode
|
|
183
|
+
def workspace_emoji_images?
|
|
184
|
+
@options[:workspace_emoji] && !@options[:markdown] && inline_images_supported?
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def attachment_previews?
|
|
188
|
+
return false unless @options[:fetch_attachments]
|
|
189
|
+
return false if @options[:markdown] || @options[:quiet] || @options[:no_image_preview]
|
|
190
|
+
|
|
191
|
+
image_previews_supported?
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def buffered_display?
|
|
195
|
+
workspace_emoji_images? || attachment_previews?
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Track the message for the "N files not downloaded" summary and, with
|
|
199
|
+
# --fetch-attachments, download its files/images to the local cache.
|
|
200
|
+
# @return [Hash] file_id / "att_<ts>_<idx>" => local path
|
|
201
|
+
def attachment_paths_for(workspace, item, message)
|
|
202
|
+
return {} unless message
|
|
203
|
+
|
|
204
|
+
model = Models::Message.from_api(message, channel_id: item.channel_id)
|
|
205
|
+
@displayed_messages&.push(model)
|
|
206
|
+
return {} unless @options[:fetch_attachments]
|
|
207
|
+
|
|
208
|
+
fetch_attachment_files([model], workspace)
|
|
135
209
|
end
|
|
136
210
|
|
|
137
211
|
def show_fetch_failure_summary
|
|
@@ -187,20 +261,19 @@ module Slk
|
|
|
187
261
|
in_progress: false,
|
|
188
262
|
counts: false,
|
|
189
263
|
no_content: false,
|
|
264
|
+
no_image_preview: false,
|
|
190
265
|
truncate: false,
|
|
191
266
|
workspace_emoji: false
|
|
192
267
|
)
|
|
193
268
|
end
|
|
194
269
|
|
|
195
270
|
def handle_option(arg, args, remaining)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
when '--workspace-emoji' then @options[:workspace_emoji] = true
|
|
203
|
-
else super
|
|
271
|
+
if %w[-n --limit].include?(arg)
|
|
272
|
+
@options[:limit] = args.shift.to_i
|
|
273
|
+
elsif BOOLEAN_FLAGS.key?(arg)
|
|
274
|
+
@options[BOOLEAN_FLAGS[arg]] = true
|
|
275
|
+
else
|
|
276
|
+
super
|
|
204
277
|
end
|
|
205
278
|
end
|
|
206
279
|
|
|
@@ -221,6 +294,7 @@ module Slk
|
|
|
221
294
|
def help_text
|
|
222
295
|
help = Support::HelpFormatter.new('slk later [options]')
|
|
223
296
|
help.description('Show saved "Later" items from Slack.')
|
|
297
|
+
help.note('Set SLK_IMAGE_PREVIEW=0 to always skip inline image previews.')
|
|
224
298
|
add_options_section(help)
|
|
225
299
|
help.render
|
|
226
300
|
end
|
|
@@ -233,6 +307,8 @@ module Slk
|
|
|
233
307
|
s.option('--in-progress', 'Show in-progress items')
|
|
234
308
|
s.option('--counts', 'Show summary counts only')
|
|
235
309
|
s.option('--no-content', 'Skip fetching message text')
|
|
310
|
+
s.option('--fetch-attachments', 'Download files to ~/.cache/slk/files/ (inline image previews need chafa)')
|
|
311
|
+
s.option('--no-image-preview', 'Download attachments but skip inline image previews')
|
|
236
312
|
s.option('--workspace-emoji', 'Show workspace emoji as inline images')
|
|
237
313
|
s.option('--no-emoji', 'Show :emoji: codes instead of unicode')
|
|
238
314
|
s.option('--width N', 'Wrap text at N columns')
|
|
@@ -9,6 +9,7 @@ module Slk
|
|
|
9
9
|
# rubocop:disable Metrics/ClassLength
|
|
10
10
|
class Messages < Base
|
|
11
11
|
include Support::InlineImages
|
|
12
|
+
include Support::AttachmentFetching
|
|
12
13
|
|
|
13
14
|
# rubocop:disable Metrics/MethodLength
|
|
14
15
|
def execute
|
|
@@ -346,28 +347,6 @@ module Slk
|
|
|
346
347
|
end
|
|
347
348
|
end
|
|
348
349
|
|
|
349
|
-
def print_file_summary(messages)
|
|
350
|
-
file_count = messages.sum { |m| m.files.size + downloadable_attachment_count(m) }
|
|
351
|
-
return if file_count.zero?
|
|
352
|
-
|
|
353
|
-
label = file_count == 1 ? '1 file' : "#{file_count} files"
|
|
354
|
-
puts
|
|
355
|
-
info("#{label} not downloaded. Use --fetch-attachments to download.")
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
def downloadable_attachment_count(message)
|
|
359
|
-
message.attachments.count { |a| a['image_url'] || a['thumb_url'] }
|
|
360
|
-
end
|
|
361
|
-
|
|
362
|
-
def fetch_attachment_files(messages, workspace)
|
|
363
|
-
paths = Support::XdgPaths.new
|
|
364
|
-
downloader = Services::FileDownloader.new(
|
|
365
|
-
cache_dir: paths.cache_dir,
|
|
366
|
-
on_debug: ->(msg) { debug(msg) }
|
|
367
|
-
)
|
|
368
|
-
downloader.download_message_files(messages, workspace)
|
|
369
|
-
end
|
|
370
|
-
|
|
371
350
|
def find_workspace_emoji(workspace_name, emoji_name)
|
|
372
351
|
return nil if emoji_name.empty?
|
|
373
352
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Slk
|
|
4
4
|
module Formatters
|
|
5
5
|
# Formats saved/later items for terminal display
|
|
6
|
+
# rubocop:disable Metrics/ClassLength
|
|
6
7
|
class SavedItemFormatter
|
|
7
8
|
def initialize(output:, mention_replacer:, text_processor:, on_debug: nil)
|
|
8
9
|
@output = output
|
|
@@ -13,7 +14,9 @@ module Slk
|
|
|
13
14
|
|
|
14
15
|
# Display a single saved item
|
|
15
16
|
# @param truncate [Boolean] if true, truncate to single line at width instead of wrapping
|
|
16
|
-
|
|
17
|
+
# @param file_paths [Hash] file_id / "att_<ts>_<idx>" => local path for downloaded attachments
|
|
18
|
+
# rubocop:disable Metrics/ParameterLists
|
|
19
|
+
def display_item(item, workspace, message: nil, width: nil, truncate: false, file_paths: {})
|
|
17
20
|
status_badge = format_status_badge(item)
|
|
18
21
|
due_info = format_due_info(item)
|
|
19
22
|
|
|
@@ -22,9 +25,13 @@ module Slk
|
|
|
22
25
|
@output.puts header_parts.join(' | ') unless header_parts.empty?
|
|
23
26
|
|
|
24
27
|
# Message content
|
|
25
|
-
|
|
28
|
+
if message
|
|
29
|
+
display_message(message, workspace, width: width, truncate: truncate)
|
|
30
|
+
display_attachments(message, file_paths || {})
|
|
31
|
+
end
|
|
26
32
|
@output.puts # blank line between items
|
|
27
33
|
end
|
|
34
|
+
# rubocop:enable Metrics/ParameterLists
|
|
28
35
|
|
|
29
36
|
private
|
|
30
37
|
|
|
@@ -113,6 +120,34 @@ module Slk
|
|
|
113
120
|
lines[1..].each { |line| @output.puts " #{line.rstrip}" } if lines.length > 1
|
|
114
121
|
end
|
|
115
122
|
|
|
123
|
+
def display_attachments(message, file_paths)
|
|
124
|
+
display_files(message['files'] || [], file_paths)
|
|
125
|
+
display_attachment_images(message['attachments'] || [], message['ts'], file_paths)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def display_files(files, file_paths)
|
|
129
|
+
files.each do |file|
|
|
130
|
+
label = file_paths[file['id']] || file['name'] || file['title'] || 'file'
|
|
131
|
+
@output.puts " #{@output.blue("[File: #{label}]")}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def display_attachment_images(attachments, message_ts, file_paths)
|
|
136
|
+
attachments.each_with_index do |att, idx|
|
|
137
|
+
url = att['image_url'] || att['thumb_url']
|
|
138
|
+
next unless url
|
|
139
|
+
|
|
140
|
+
label = file_paths["att_#{message_ts}_#{idx}"] || att['title'] || image_filename(url)
|
|
141
|
+
@output.puts " #{@output.blue("[Image: #{label}]")}"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def image_filename(url)
|
|
146
|
+
File.basename(URI.parse(url).path)
|
|
147
|
+
rescue URI::InvalidURIError
|
|
148
|
+
'image'
|
|
149
|
+
end
|
|
150
|
+
|
|
116
151
|
def resolve_message_author(message, workspace)
|
|
117
152
|
if message['user']
|
|
118
153
|
@mentions.lookup_user_name(workspace, message['user']) || message['user']
|
|
@@ -140,5 +175,6 @@ module Slk
|
|
|
140
175
|
text.length > max_length ? "#{text[0...max_length]}..." : text
|
|
141
176
|
end
|
|
142
177
|
end
|
|
178
|
+
# rubocop:enable Metrics/ClassLength
|
|
143
179
|
end
|
|
144
180
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Slk
|
|
4
|
+
module Support
|
|
5
|
+
# Shared --fetch-attachments behavior for commands that display messages.
|
|
6
|
+
# Expects the including command to provide #debug and #info.
|
|
7
|
+
module AttachmentFetching
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# Download files and attachment images for the given messages.
|
|
11
|
+
# @return [Hash] file_id / "att_<ts>_<idx>" => local path
|
|
12
|
+
def fetch_attachment_files(messages, workspace)
|
|
13
|
+
downloader = Services::FileDownloader.new(
|
|
14
|
+
cache_dir: Support::XdgPaths.new.cache_dir,
|
|
15
|
+
on_debug: ->(msg) { debug(msg) }
|
|
16
|
+
)
|
|
17
|
+
downloader.download_message_files(messages, workspace)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def downloadable_file_count(messages)
|
|
21
|
+
messages.sum { |m| m.files.size + downloadable_attachment_count(m) }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def downloadable_attachment_count(message)
|
|
25
|
+
message.attachments.count { |a| a['image_url'] || a['thumb_url'] }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def print_file_summary(messages)
|
|
29
|
+
file_count = downloadable_file_count(messages)
|
|
30
|
+
return if file_count.zero?
|
|
31
|
+
|
|
32
|
+
label = file_count == 1 ? '1 file' : "#{file_count} files"
|
|
33
|
+
puts
|
|
34
|
+
info("#{label} not downloaded. Use --fetch-attachments to download.")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
|
|
5
|
+
module Slk
|
|
6
|
+
module Support
|
|
7
|
+
# Renders downloaded image attachments inline using chafa, which handles
|
|
8
|
+
# Kitty graphics (Ghostty/Kitty) and iTerm2 images, including tmux
|
|
9
|
+
# passthrough with Unicode placeholders so images scroll with the pane.
|
|
10
|
+
# Without a capable terminal or chafa, callers just show the file path.
|
|
11
|
+
# Set SLK_IMAGE_PREVIEW=0 (or false/no/off) to turn previews off.
|
|
12
|
+
# Expects Support::InlineImages to be included alongside.
|
|
13
|
+
module ImagePreview
|
|
14
|
+
PREVIEW_MAX_COLS = 100
|
|
15
|
+
PREVIEW_MAX_ROWS = 15
|
|
16
|
+
PREVIEWABLE_EXTENSIONS = %w[png jpg jpeg gif webp bmp tif tiff heic].freeze
|
|
17
|
+
DISABLED_VALUES = %w[0 false no off].freeze
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def image_previews_supported?
|
|
22
|
+
return @image_previews_supported if defined?(@image_previews_supported)
|
|
23
|
+
|
|
24
|
+
@image_previews_supported = !image_previews_disabled_by_env? && $stdout.tty? &&
|
|
25
|
+
!image_preview_format.nil? && chafa_available?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def image_previews_disabled_by_env?
|
|
29
|
+
DISABLED_VALUES.include?(ENV.fetch('SLK_IMAGE_PREVIEW', '').strip.downcase)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def image_preview_format
|
|
33
|
+
if kitty_graphics_supported?
|
|
34
|
+
'kitty'
|
|
35
|
+
elsif iterm2_protocol_supported?
|
|
36
|
+
'iterm'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def previewable_image?(path)
|
|
41
|
+
PREVIEWABLE_EXTENSIONS.include?(File.extname(path.to_s).delete('.').downcase)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Print an inline preview of the image at path, indented by indent columns.
|
|
45
|
+
# @return [Boolean] whether a preview was printed
|
|
46
|
+
def print_image_preview(path, indent: 2) # rubocop:disable Naming/PredicateMethod
|
|
47
|
+
return false unless previewable_image?(path) && File.exist?(path)
|
|
48
|
+
|
|
49
|
+
rendered = render_image_preview(path, indent)
|
|
50
|
+
return false unless rendered
|
|
51
|
+
|
|
52
|
+
$stdout.print(' ' * indent)
|
|
53
|
+
$stdout.print(rendered)
|
|
54
|
+
$stdout.print("\n") unless rendered.end_with?("\n", "\e[?25h")
|
|
55
|
+
$stdout.flush
|
|
56
|
+
true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def render_image_preview(path, indent)
|
|
60
|
+
stdout, _stderr, status = Open3.capture3(*chafa_args(path, indent))
|
|
61
|
+
status.success? && !stdout.empty? ? stdout : nil
|
|
62
|
+
rescue SystemCallError => e
|
|
63
|
+
debug("chafa failed: #{e.message}") if respond_to?(:debug, true)
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def chafa_args(path, indent)
|
|
68
|
+
args = ['chafa', "--format=#{image_preview_format}"]
|
|
69
|
+
args << '--passthrough=tmux' if in_tmux?
|
|
70
|
+
args + ["--size=#{preview_cols(indent)}x#{PREVIEW_MAX_ROWS}",
|
|
71
|
+
'--animate=off', '--probe=off', '--relative=off', path]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def preview_cols(indent)
|
|
75
|
+
[terminal_columns - indent - 1, PREVIEW_MAX_COLS].min.clamp(10, PREVIEW_MAX_COLS)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def terminal_columns
|
|
79
|
+
require 'io/console'
|
|
80
|
+
cols = IO.console&.winsize&.last.to_i
|
|
81
|
+
cols.positive? ? cols : 80
|
|
82
|
+
rescue StandardError
|
|
83
|
+
80
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def chafa_available?
|
|
87
|
+
ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).any? do |dir|
|
|
88
|
+
File.executable?(File.join(dir, 'chafa'))
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/slk/version.rb
CHANGED
data/lib/slk.rb
CHANGED
|
@@ -194,6 +194,8 @@ module Slk
|
|
|
194
194
|
autoload :XdgPaths, 'slk/support/xdg_paths'
|
|
195
195
|
autoload :SlackUrlParser, 'slk/support/slack_url_parser'
|
|
196
196
|
autoload :InlineImages, 'slk/support/inline_images'
|
|
197
|
+
autoload :AttachmentFetching, 'slk/support/attachment_fetching'
|
|
198
|
+
autoload :ImagePreview, 'slk/support/image_preview'
|
|
197
199
|
autoload :HelpFormatter, 'slk/support/help_formatter'
|
|
198
200
|
autoload :ErrorLogger, 'slk/support/error_logger'
|
|
199
201
|
autoload :UserResolver, 'slk/support/user_resolver'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: slk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Boehs
|
|
@@ -136,11 +136,13 @@ files:
|
|
|
136
136
|
- lib/slk/services/user_matcher.rb
|
|
137
137
|
- lib/slk/services/user_picker.rb
|
|
138
138
|
- lib/slk/services/who_target_resolver.rb
|
|
139
|
+
- lib/slk/support/attachment_fetching.rb
|
|
139
140
|
- lib/slk/support/check_in_time.rb
|
|
140
141
|
- lib/slk/support/date_parser.rb
|
|
141
142
|
- lib/slk/support/decimal_timestamp.rb
|
|
142
143
|
- lib/slk/support/error_logger.rb
|
|
143
144
|
- lib/slk/support/help_formatter.rb
|
|
145
|
+
- lib/slk/support/image_preview.rb
|
|
144
146
|
- lib/slk/support/inline_images.rb
|
|
145
147
|
- lib/slk/support/interactive_prompt.rb
|
|
146
148
|
- lib/slk/support/platform.rb
|