slk 0.4.0 → 0.4.2

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.
@@ -2,11 +2,17 @@
2
2
 
3
3
  module Slk
4
4
  module Support
5
- # Shared module for inline image display in iTerm2/WezTerm/Mintty terminals
5
+ # Shared module for inline image display in terminals supporting
6
+ # iTerm2 protocol (iTerm2/WezTerm/Mintty) or Kitty graphics protocol (Ghostty/Kitty)
6
7
  # Includes special handling for tmux passthrough sequences
7
8
  module InlineImages
8
- # Check if terminal supports iTerm2 inline image protocol
9
+ # Check if terminal supports any inline image protocol
9
10
  def inline_images_supported?
11
+ iterm2_protocol_supported? || kitty_graphics_supported?
12
+ end
13
+
14
+ # Check if terminal supports iTerm2 inline image protocol
15
+ def iterm2_protocol_supported?
10
16
  # iTerm2, WezTerm, Mintty support inline images
11
17
  # LC_TERMINAL persists through tmux/ssh
12
18
  ['iTerm.app', 'WezTerm'].include?(ENV.fetch('TERM_PROGRAM', nil)) ||
@@ -15,31 +21,88 @@ module Slk
15
21
  ENV['TERM'] == 'mintty'
16
22
  end
17
23
 
24
+ # Check if terminal supports Kitty graphics protocol (Ghostty, Kitty)
25
+ def kitty_graphics_supported?
26
+ ENV['TERM_PROGRAM'] == 'ghostty' ||
27
+ ENV['GHOSTTY_RESOURCES_DIR'] ||
28
+ ENV['TERM']&.include?('kitty') ||
29
+ tmux_client_is_kitty_compatible?
30
+ end
31
+
32
+ # Check if tmux client terminal supports Kitty graphics
33
+ def tmux_client_is_kitty_compatible?
34
+ return false unless in_tmux?
35
+
36
+ @tmux_client_is_kitty_compatible ||= begin
37
+ output = begin
38
+ `tmux display-message -p '\#{client_termname}'`.chomp
39
+ rescue StandardError
40
+ ''
41
+ end
42
+ output.include?('ghostty') || output.include?('kitty')
43
+ end
44
+ end
45
+
18
46
  # Check if running inside tmux
19
47
  def in_tmux?
20
48
  # tmux sets TERM to screen-* or tmux-*
21
49
  ENV['TERM']&.include?('screen') || ENV['TERM']&.start_with?('tmux')
22
50
  end
23
51
 
24
- # Print an inline image using iTerm2 protocol
52
+ # Print an inline image using the appropriate protocol
25
53
  # In tmux, uses passthrough sequence and cursor positioning
26
54
  def print_inline_image(path, height: 1)
27
- data = read_image_data(path)
55
+ data = read_image_data_for_protocol(path)
28
56
  return unless data
29
57
 
30
58
  encoded = [data].pack('m0')
31
- in_tmux? ? print_tmux_image(encoded, height) : print_iterm_image(encoded, height)
59
+
60
+ if kitty_graphics_supported?
61
+ in_tmux? ? print_tmux_kitty_image(encoded, height) : print_kitty_image(encoded, height)
62
+ else
63
+ in_tmux? ? print_tmux_iterm_image(encoded, height) : print_iterm_image(encoded, height)
64
+ end
32
65
  end
33
66
 
34
- def read_image_data(path)
67
+ def read_image_data_for_protocol(path)
35
68
  return nil unless File.exist?(path)
36
69
 
37
- File.binread(path)
70
+ data = File.binread(path)
71
+ return nil unless data
72
+
73
+ # Kitty protocol requires PNG format; convert GIF/JPEG if needed
74
+ if kitty_graphics_supported? && !png_data?(data)
75
+ convert_to_png(path)
76
+ else
77
+ data
78
+ end
38
79
  rescue IOError, SystemCallError
39
80
  nil
40
81
  end
41
82
 
42
- def print_tmux_image(encoded, height)
83
+ def png_data?(data)
84
+ # PNG files start with magic bytes: 137 80 78 71 13 10 26 10
85
+ data[0, 8]&.bytes == [137, 80, 78, 71, 13, 10, 26, 10]
86
+ end
87
+
88
+ def convert_to_png(path)
89
+ # Use sips (macOS) to convert to PNG
90
+ require 'tempfile'
91
+ temp = Tempfile.new(['emoji', '.png'])
92
+ temp.close
93
+
94
+ system('sips', '-s', 'format', 'png', path, '--out', temp.path,
95
+ out: File::NULL, err: File::NULL)
96
+
97
+ return nil unless File.exist?(temp.path) && File.size(temp.path).positive?
98
+
99
+ File.binread(temp.path)
100
+ ensure
101
+ temp&.unlink
102
+ end
103
+
104
+ # iTerm2 protocol methods
105
+ def print_tmux_iterm_image(encoded, height)
43
106
  fmt = "\ePtmux;\e\e]1337;File=inline=1;preserveAspectRatio=0;" \
44
107
  "size=%<size>d;height=%<height>d:%<data>s\a\e\\\n "
45
108
  printf fmt, size: encoded.length, height: height, data: encoded
@@ -49,6 +112,26 @@ module Slk
49
112
  printf "\e]1337;File=inline=1;height=%<height>d:%<data>s\a", height: height, data: encoded
50
113
  end
51
114
 
115
+ # Kitty graphics protocol methods (Ghostty, Kitty)
116
+ # a=T: transmit and display, q=1: suppress OK response, f=100: PNG, r=rows, m=0: no more chunks
117
+ def print_kitty_image(encoded, height)
118
+ printf "\e_Ga=T,q=1,f=100,r=%<height>d,m=0;%<data>s\e\\", height: height, data: encoded
119
+ end
120
+
121
+ # Kitty graphics with Unicode placeholders for tmux (images clear/scroll with text)
122
+ # See: https://sw.kovidgoyal.net/kitty/graphics-protocol/#unicode-placeholders
123
+ def print_tmux_kitty_image(encoded, _height)
124
+ @kitty_image_id ||= 30
125
+ @kitty_image_id = (@kitty_image_id % 255) + 1
126
+ image_id = @kitty_image_id
127
+
128
+ # tmux passthrough: transmit image with Unicode placeholder mode (U=1), 2 cols x 1 row
129
+ $stdout.print "\ePtmux;\e\e_Ga=T,U=1,q=1,f=100,i=#{image_id},c=2,r=1,m=0;#{encoded}\e\e\\\e\\"
130
+ # Output placeholder cells (U+10EEEE + row/col diacritics) with foreground color = image_id
131
+ $stdout.print "\e[38;5;#{image_id}m\u{10EEEE}\u0305\u0305\u{10EEEE}\u0305\u030D\e[39m"
132
+ $stdout.flush
133
+ end
134
+
52
135
  # Print inline image with name on same line
53
136
  # Handles tmux cursor positioning to keep image and text on same line
54
137
  def print_inline_image_with_text(path, text, height: 1) # rubocop:disable Naming/PredicateMethod
@@ -56,11 +139,12 @@ module Slk
56
139
 
57
140
  print_inline_image(path, height: height)
58
141
 
59
- if in_tmux?
60
- # tmux: image ends with \n + space, cursor on next line
142
+ if in_tmux? && !kitty_graphics_supported?
143
+ # iTerm2 in tmux: image ends with \n + space, cursor on next line
61
144
  # Move up 1 line, right 3 cols (past image), then print text
62
145
  print "\e[1A\e[3C#{text}\n"
63
146
  else
147
+ # Direct output or Unicode placeholders (regular text, just print after)
64
148
  puts " #{text}"
65
149
  end
66
150
 
data/lib/slk/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Slk
4
- VERSION = '0.4.0'
4
+ VERSION = '0.4.2'
5
5
  end
data/lib/slk.rb CHANGED
@@ -33,6 +33,7 @@ module Slk
33
33
  autoload :Channel, 'slk/models/channel'
34
34
  autoload :Preset, 'slk/models/preset'
35
35
  autoload :SearchResult, 'slk/models/search_result'
36
+ autoload :SavedItem, 'slk/models/saved_item'
36
37
  end
37
38
 
38
39
  # Application services for configuration, caching, and API communication
@@ -54,11 +55,13 @@ module Slk
54
55
  autoload :TargetResolver, 'slk/services/target_resolver'
55
56
  autoload :SetupWizard, 'slk/services/setup_wizard'
56
57
  autoload :UserLookup, 'slk/services/user_lookup'
58
+ autoload :MessageResolver, 'slk/services/message_resolver'
57
59
  end
58
60
 
59
61
  # Output formatters for messages, durations, and emoji
60
62
  module Formatters
61
63
  autoload :Output, 'slk/formatters/output'
64
+ autoload :MarkdownOutput, 'slk/formatters/markdown_output'
62
65
  autoload :DurationFormatter, 'slk/formatters/duration_formatter'
63
66
  autoload :MentionReplacer, 'slk/formatters/mention_replacer'
64
67
  autoload :EmojiReplacer, 'slk/formatters/emoji_replacer'
@@ -69,6 +72,8 @@ module Slk
69
72
  autoload :AttachmentFormatter, 'slk/formatters/attachment_formatter'
70
73
  autoload :BlockFormatter, 'slk/formatters/block_formatter'
71
74
  autoload :SearchFormatter, 'slk/formatters/search_formatter'
75
+ autoload :SavedItemFormatter, 'slk/formatters/saved_item_formatter'
76
+ autoload :TextProcessor, 'slk/formatters/text_processor'
72
77
  end
73
78
 
74
79
  # CLI commands implementing user-facing functionality
@@ -89,6 +94,7 @@ module Slk
89
94
  autoload :Emoji, 'slk/commands/emoji'
90
95
  autoload :Config, 'slk/commands/config'
91
96
  autoload :Help, 'slk/commands/help'
97
+ autoload :Later, 'slk/commands/later'
92
98
  end
93
99
 
94
100
  # Thin wrappers around Slack API endpoints
@@ -103,6 +109,7 @@ module Slk
103
109
  autoload :Usergroups, 'slk/api/usergroups'
104
110
  autoload :Activity, 'slk/api/activity'
105
111
  autoload :Search, 'slk/api/search'
112
+ autoload :Saved, 'slk/api/saved'
106
113
  end
107
114
 
108
115
  # Utility classes for paths, parsing, and helpers
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.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Boehs
@@ -32,6 +32,7 @@ files:
32
32
  - lib/slk/api/conversations.rb
33
33
  - lib/slk/api/dnd.rb
34
34
  - lib/slk/api/emoji.rb
35
+ - lib/slk/api/saved.rb
35
36
  - lib/slk/api/search.rb
36
37
  - lib/slk/api/threads.rb
37
38
  - lib/slk/api/usergroups.rb
@@ -45,6 +46,7 @@ files:
45
46
  - lib/slk/commands/dnd.rb
46
47
  - lib/slk/commands/emoji.rb
47
48
  - lib/slk/commands/help.rb
49
+ - lib/slk/commands/later.rb
48
50
  - lib/slk/commands/messages.rb
49
51
  - lib/slk/commands/presence.rb
50
52
  - lib/slk/commands/preset.rb
@@ -60,16 +62,20 @@ files:
60
62
  - lib/slk/formatters/duration_formatter.rb
61
63
  - lib/slk/formatters/emoji_replacer.rb
62
64
  - lib/slk/formatters/json_message_formatter.rb
65
+ - lib/slk/formatters/markdown_output.rb
63
66
  - lib/slk/formatters/mention_replacer.rb
64
67
  - lib/slk/formatters/message_formatter.rb
65
68
  - lib/slk/formatters/output.rb
66
69
  - lib/slk/formatters/reaction_formatter.rb
70
+ - lib/slk/formatters/saved_item_formatter.rb
67
71
  - lib/slk/formatters/search_formatter.rb
72
+ - lib/slk/formatters/text_processor.rb
68
73
  - lib/slk/models/channel.rb
69
74
  - lib/slk/models/duration.rb
70
75
  - lib/slk/models/message.rb
71
76
  - lib/slk/models/preset.rb
72
77
  - lib/slk/models/reaction.rb
78
+ - lib/slk/models/saved_item.rb
73
79
  - lib/slk/models/search_result.rb
74
80
  - lib/slk/models/status.rb
75
81
  - lib/slk/models/user.rb
@@ -83,6 +89,7 @@ files:
83
89
  - lib/slk/services/emoji_searcher.rb
84
90
  - lib/slk/services/encryption.rb
85
91
  - lib/slk/services/gemoji_sync.rb
92
+ - lib/slk/services/message_resolver.rb
86
93
  - lib/slk/services/preset_store.rb
87
94
  - lib/slk/services/reaction_enricher.rb
88
95
  - lib/slk/services/setup_wizard.rb
@@ -111,12 +118,6 @@ metadata:
111
118
  source_code_uri: https://github.com/ericboehs/slk
112
119
  changelog_uri: https://github.com/ericboehs/slk/blob/master/CHANGELOG.md
113
120
  rubygems_mfa_required: 'true'
114
- post_install_message: |
115
- slk 0.2.0: Config directory changed from slack-cli to slk
116
-
117
- If upgrading from 0.1.x, migrate your config:
118
- mv ${XDG_CONFIG_HOME:-~/.config}/slack-cli ${XDG_CONFIG_HOME:-~/.config}/slk
119
- mv ${XDG_CACHE_HOME:-~/.cache}/slack-cli ${XDG_CACHE_HOME:-~/.cache}/slk
120
121
  rdoc_options: []
121
122
  require_paths:
122
123
  - lib