termpix 0.3.2 → 0.4.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.
- checksums.yaml +4 -4
- data/lib/termpix/protocols.rb +28 -63
- data/lib/termpix/version.rb +1 -1
- data/lib/termpix.rb +1 -15
- metadata +5 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 02dd8d6eb36d91f8db048b0302303bd720fbe246d0934bae5f31a4062a11f00a
|
|
4
|
+
data.tar.gz: 55745158a3f0657c3457c422ce8fd1bf33b7201132c921274c18ede7c49d9830
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4871c317ab480d3df29ef922958f44fce76fddb54cd6f37d2539ce940ea3229fa754c49479fd2b0865f4b255b559a6663454a244d118b638add4e3e7341144c9
|
|
7
|
+
data.tar.gz: ce9f36b37d5f3e8c56c1aa5c6027da0a6b3849dc65a457465547b160f0038dc5d22268d5aea2c4cd245b2d2be390ba7d8c55b89a985655f2ee443be52cd6fe12
|
data/lib/termpix/protocols.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Termpix
|
|
|
7
7
|
# Uses Unicode placeholder mode (U=1) for curses/TUI compatibility
|
|
8
8
|
# Images are tied to placeholder characters that curses manages as text
|
|
9
9
|
module Kitty
|
|
10
|
-
@
|
|
10
|
+
@active_image_ids = []
|
|
11
11
|
@image_cache = {} # path -> image_id mapping
|
|
12
12
|
|
|
13
13
|
def self.display(image_path, x:, y:, max_width:, max_height:)
|
|
@@ -32,8 +32,6 @@ module Termpix
|
|
|
32
32
|
image_id = 1 if image_id == 0
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
old_image_id = @current_image_id
|
|
36
|
-
|
|
37
35
|
unless @image_cache[cache_key]
|
|
38
36
|
# Transmit the image with scaling
|
|
39
37
|
escaped = Shellwords.escape(image_path)
|
|
@@ -50,11 +48,8 @@ module Termpix
|
|
|
50
48
|
chunks.each_with_index do |chunk, idx|
|
|
51
49
|
more = idx < chunks.length - 1 ? 1 : 0
|
|
52
50
|
if idx == 0
|
|
53
|
-
# First chunk: specify format (f=100 for PNG), action (a=t for transmit)
|
|
54
|
-
# i=image_id, m=more_chunks
|
|
55
51
|
print "\e_Ga=t,f=100,i=#{image_id},m=#{more};#{chunk}\e\\"
|
|
56
52
|
else
|
|
57
|
-
# Continuation chunks
|
|
58
53
|
print "\e_Gm=#{more};#{chunk}\e\\"
|
|
59
54
|
end
|
|
60
55
|
end
|
|
@@ -63,30 +58,21 @@ module Termpix
|
|
|
63
58
|
@image_cache[cache_key] = image_id
|
|
64
59
|
end
|
|
65
60
|
|
|
66
|
-
# Position cursor and
|
|
67
|
-
#
|
|
68
|
-
# Don't specify c/r - let kitty use image's native size (already scaled by ImageMagick)
|
|
69
|
-
print "\e[#{y};#{x}H" # Move cursor to position
|
|
61
|
+
# Position cursor and place the image (keep existing images)
|
|
62
|
+
print "\e[#{y};#{x}H"
|
|
70
63
|
print "\e_Ga=p,i=#{image_id},C=1\e\\"
|
|
71
64
|
$stdout.flush
|
|
72
65
|
|
|
73
|
-
|
|
74
|
-
if old_image_id && old_image_id != image_id
|
|
75
|
-
print "\e_Ga=d,d=i,i=#{old_image_id}\e\\"
|
|
76
|
-
$stdout.flush
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
@current_image_id = image_id
|
|
66
|
+
@active_image_ids << image_id unless @active_image_ids.include?(image_id)
|
|
80
67
|
true
|
|
81
68
|
end
|
|
82
69
|
|
|
83
70
|
def self.clear
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
print "\e_Ga=d,d=i,i=#{@current_image_id}\e\\"
|
|
87
|
-
$stdout.flush
|
|
88
|
-
@current_image_id = nil
|
|
71
|
+
@active_image_ids.each do |id|
|
|
72
|
+
print "\e_Ga=d,d=i,i=#{id}\e\\"
|
|
89
73
|
end
|
|
74
|
+
$stdout.flush unless @active_image_ids.empty?
|
|
75
|
+
@active_image_ids.clear
|
|
90
76
|
true
|
|
91
77
|
end
|
|
92
78
|
|
|
@@ -105,7 +91,7 @@ module Termpix
|
|
|
105
91
|
end
|
|
106
92
|
# Fall back to common defaults (10x20 pixels per cell)
|
|
107
93
|
[10, 20]
|
|
108
|
-
rescue
|
|
94
|
+
rescue LoadError, IOError, StandardError
|
|
109
95
|
[10, 20]
|
|
110
96
|
end
|
|
111
97
|
end
|
|
@@ -115,7 +101,7 @@ module Termpix
|
|
|
115
101
|
def self.get_dimensions(image_path)
|
|
116
102
|
escaped = Shellwords.escape(image_path)
|
|
117
103
|
dimensions = `identify -format "%wx%h" #{escaped}[0] 2>/dev/null`.strip
|
|
118
|
-
return nil if dimensions.empty?
|
|
104
|
+
return nil if dimensions.empty? || !dimensions.match?(/\A\d+x\d+\z/)
|
|
119
105
|
dimensions.split('x').map(&:to_i)
|
|
120
106
|
end
|
|
121
107
|
|
|
@@ -154,35 +140,6 @@ module Termpix
|
|
|
154
140
|
end
|
|
155
141
|
end
|
|
156
142
|
|
|
157
|
-
# Überzug++ Protocol
|
|
158
|
-
module Ueberzug
|
|
159
|
-
def self.display(image_path, x:, y:, max_width:, max_height:)
|
|
160
|
-
# Get terminal pixel dimensions
|
|
161
|
-
terminfo = `xwininfo -id $(xdotool getactivewindow 2>/dev/null) 2>/dev/null`
|
|
162
|
-
return unless terminfo && !terminfo.empty?
|
|
163
|
-
|
|
164
|
-
term_w = terminfo.match(/Width: (\d+)/)[1].to_i
|
|
165
|
-
term_h = terminfo.match(/Height: (\d+)/)[1].to_i
|
|
166
|
-
|
|
167
|
-
# Calculate character dimensions
|
|
168
|
-
char_w = term_w / `tput cols`.to_i
|
|
169
|
-
char_h = term_h / `tput lines`.to_i
|
|
170
|
-
|
|
171
|
-
# Convert character positions to pixels
|
|
172
|
-
img_x = char_w * x
|
|
173
|
-
img_y = char_h * y
|
|
174
|
-
img_w = char_w * max_width
|
|
175
|
-
img_h = char_h * max_height
|
|
176
|
-
|
|
177
|
-
# TODO: Implement actual Überzug++ protocol
|
|
178
|
-
# For now, placeholder
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def self.clear
|
|
182
|
-
system('clear')
|
|
183
|
-
end
|
|
184
|
-
end
|
|
185
|
-
|
|
186
143
|
# w3mimgdisplay Protocol
|
|
187
144
|
module W3m
|
|
188
145
|
@imgdisplay = '/usr/lib/w3m/w3mimgdisplay'
|
|
@@ -192,12 +149,17 @@ module Termpix
|
|
|
192
149
|
terminfo = `xwininfo -id $(xdotool getactivewindow 2>/dev/null) 2>/dev/null`
|
|
193
150
|
return unless terminfo && !terminfo.empty?
|
|
194
151
|
|
|
195
|
-
|
|
196
|
-
|
|
152
|
+
w_match = terminfo.match(/Width: (\d+)/)
|
|
153
|
+
h_match = terminfo.match(/Height: (\d+)/)
|
|
154
|
+
return unless w_match && h_match
|
|
155
|
+
term_w = w_match[1].to_i
|
|
156
|
+
term_h = h_match[1].to_i
|
|
197
157
|
|
|
198
158
|
# Calculate character dimensions
|
|
199
159
|
cols = `tput cols`.to_i
|
|
160
|
+
cols = 80 if cols <= 0
|
|
200
161
|
lines = `tput lines`.to_i
|
|
162
|
+
lines = 24 if lines <= 0
|
|
201
163
|
char_w = term_w / cols
|
|
202
164
|
char_h = term_h / lines
|
|
203
165
|
|
|
@@ -230,8 +192,8 @@ module Termpix
|
|
|
230
192
|
display_path = image_path
|
|
231
193
|
end
|
|
232
194
|
|
|
233
|
-
dimensions = `identify -format "%wx%h" #{Shellwords.escape(display_path)} 2>/dev/null`.strip
|
|
234
|
-
return if dimensions.empty?
|
|
195
|
+
dimensions = `identify -format "%wx%h" #{Shellwords.escape(display_path)}[0] 2>/dev/null`.strip
|
|
196
|
+
return if dimensions.empty? || !dimensions.match?(/\A\d+x\d+\z/)
|
|
235
197
|
|
|
236
198
|
img_w, img_h = dimensions.split('x').map(&:to_i)
|
|
237
199
|
|
|
@@ -245,9 +207,8 @@ module Termpix
|
|
|
245
207
|
end
|
|
246
208
|
|
|
247
209
|
# Display using w3mimgdisplay protocol
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
3;' | #{@imgdisplay} 2>/dev/null`
|
|
210
|
+
cmd = "0;1;#{img_x};#{img_y};#{img_w};#{img_h};;;;;#{display_path}\n4;\n3;\n"
|
|
211
|
+
IO.popen([@imgdisplay], 'w', err: '/dev/null') { |io| io.write(cmd) }
|
|
251
212
|
|
|
252
213
|
# Don't delete temp file - keep it cached for performance
|
|
253
214
|
end
|
|
@@ -257,8 +218,11 @@ module Termpix
|
|
|
257
218
|
terminfo = `xwininfo -id $(xdotool getactivewindow 2>/dev/null) 2>/dev/null`
|
|
258
219
|
return true unless terminfo && !terminfo.empty?
|
|
259
220
|
|
|
260
|
-
|
|
261
|
-
|
|
221
|
+
w_match = terminfo.match(/Width: (\d+)/)
|
|
222
|
+
h_match = terminfo.match(/Height: (\d+)/)
|
|
223
|
+
return true unless w_match && h_match
|
|
224
|
+
term_w = w_match[1].to_i
|
|
225
|
+
term_h = h_match[1].to_i
|
|
262
226
|
|
|
263
227
|
# Calculate character dimensions
|
|
264
228
|
char_w = term_w / term_width
|
|
@@ -271,7 +235,8 @@ module Termpix
|
|
|
271
235
|
img_max_h = char_h * height + 2
|
|
272
236
|
|
|
273
237
|
# Use w3mimgdisplay command "6" to clear just the image area
|
|
274
|
-
|
|
238
|
+
cmd = "6;#{img_x};#{img_y};#{img_max_w};#{img_max_h};\n4;\n3;\n"
|
|
239
|
+
IO.popen([@imgdisplay], 'w', err: '/dev/null') { |io| io.write(cmd) }
|
|
275
240
|
true
|
|
276
241
|
end
|
|
277
242
|
end
|
data/lib/termpix/version.rb
CHANGED
data/lib/termpix.rb
CHANGED
|
@@ -25,19 +25,16 @@ module Termpix
|
|
|
25
25
|
Protocols::Kitty.display(image_path, x: x, y: y, max_width: max_width, max_height: max_height)
|
|
26
26
|
when :sixel
|
|
27
27
|
Protocols::Sixel.display(image_path, x: x, y: y, max_width: max_width, max_height: max_height)
|
|
28
|
-
when :ueberzug
|
|
29
|
-
Protocols::Ueberzug.display(image_path, x: x, y: y, max_width: max_width, max_height: max_height)
|
|
30
28
|
when :w3m
|
|
31
29
|
Protocols::W3m.display(image_path, x: x, y: y, max_width: max_width, max_height: max_height)
|
|
32
30
|
else
|
|
33
31
|
return false
|
|
34
32
|
end
|
|
35
33
|
|
|
36
|
-
@current_image = image_path
|
|
37
34
|
true
|
|
38
35
|
end
|
|
39
36
|
|
|
40
|
-
# Clear
|
|
37
|
+
# Clear all displayed images
|
|
41
38
|
def clear(x: 0, y: 0, width: 80, height: 24, term_width: 80, term_height: 24)
|
|
42
39
|
return false unless @protocol
|
|
43
40
|
|
|
@@ -46,13 +43,10 @@ module Termpix
|
|
|
46
43
|
Protocols::Kitty.clear
|
|
47
44
|
when :sixel
|
|
48
45
|
Protocols::Sixel.clear
|
|
49
|
-
when :ueberzug
|
|
50
|
-
Protocols::Ueberzug.clear
|
|
51
46
|
when :w3m
|
|
52
47
|
Protocols::W3m.clear(x: x, y: y, width: width, height: height, term_width: term_width, term_height: term_height)
|
|
53
48
|
end
|
|
54
49
|
|
|
55
|
-
@current_image = nil
|
|
56
50
|
true
|
|
57
51
|
end
|
|
58
52
|
|
|
@@ -95,14 +89,6 @@ module Termpix
|
|
|
95
89
|
return :sixel if check_dependency('convert')
|
|
96
90
|
end
|
|
97
91
|
|
|
98
|
-
# Überzug++ - disabled for now (implementation incomplete)
|
|
99
|
-
# TODO: Implement proper Überzug++ JSON-RPC communication
|
|
100
|
-
# if command_exists?('ueberzug') || command_exists?('ueberzugpp')
|
|
101
|
-
# if check_dependencies('xwininfo', 'xdotool', 'identify')
|
|
102
|
-
# return :ueberzug
|
|
103
|
-
# end
|
|
104
|
-
# end
|
|
105
|
-
|
|
106
92
|
# Fall back to w3m (works on urxvt, xterm, etc.)
|
|
107
93
|
if command_exists?('/usr/lib/w3m/w3mimgdisplay')
|
|
108
94
|
if check_dependencies('xwininfo', 'xdotool', 'identify')
|
metadata
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: termpix
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-03-27 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description: 'Termpix v0.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
best available protocol (Kitty, Sixel, or w3m). Auto-detects terminal capabilities
|
|
17
|
-
and falls back gracefully.'
|
|
13
|
+
description: 'Termpix v0.4.1: Kitty protocol now supports multiple simultaneous images.
|
|
14
|
+
Track array of active image IDs instead of single image. clear() removes all active
|
|
15
|
+
images at once.'
|
|
18
16
|
email: g@isene.com
|
|
19
17
|
executables: []
|
|
20
18
|
extensions: []
|