termpix 0.2.1 → 0.3.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/lib/termpix/protocols.rb +97 -7
- data/lib/termpix/version.rb +1 -1
- data/lib/termpix.rb +13 -7
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 763bacec25caa2a40043e19c2319b44c9433848e307281a761c4933ce345c227
|
|
4
|
+
data.tar.gz: e5fe48498bcf92d6cfccea1fedbec3d4cd33945096682e769a1464bb2248d0d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b55c954c5ccc8f78922973a8efd3c20a4891509fe9adc7c6baa71f91620d28952379096dbbeab6703799f0c58f6401ef3820e83f89eb0368d37d6e36e5db42b5
|
|
7
|
+
data.tar.gz: 890174a649f8cdc54d4506f3ff21075aa5d5f82f2be282101b19ad6e107b37271673272ac8c582ed96d2210a50375435ed31ee9d47fa2ce2259fdfba1749a0c8
|
data/lib/termpix/protocols.rb
CHANGED
|
@@ -4,21 +4,111 @@ require 'base64'
|
|
|
4
4
|
module Termpix
|
|
5
5
|
module Protocols
|
|
6
6
|
# Kitty Graphics Protocol
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# management. Recommend disabling Kitty protocol for curses-based apps.
|
|
7
|
+
# Uses Unicode placeholder mode (U=1) for curses/TUI compatibility
|
|
8
|
+
# Images are tied to placeholder characters that curses manages as text
|
|
10
9
|
module Kitty
|
|
10
|
+
@current_image_id = nil
|
|
11
|
+
@image_cache = {} # path -> image_id mapping
|
|
12
|
+
|
|
11
13
|
def self.display(image_path, x:, y:, max_width:, max_height:)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
|
|
14
|
+
return false unless File.exist?(image_path)
|
|
15
|
+
|
|
16
|
+
# Get terminal cell size in pixels
|
|
17
|
+
cell_width, cell_height = get_cell_size
|
|
18
|
+
return false unless cell_width && cell_height
|
|
19
|
+
|
|
20
|
+
# Calculate pixel dimensions for the image area
|
|
21
|
+
pixel_width = max_width * cell_width
|
|
22
|
+
pixel_height = max_height * cell_height
|
|
23
|
+
|
|
24
|
+
# Check if we have this image cached
|
|
25
|
+
cache_key = "#{image_path}:#{pixel_width}x#{pixel_height}"
|
|
26
|
+
image_id = @image_cache[cache_key]
|
|
27
|
+
|
|
28
|
+
unless image_id
|
|
29
|
+
# Generate new image ID (1-4294967295)
|
|
30
|
+
image_id = (Time.now.to_f * 1000).to_i % 4294967295
|
|
31
|
+
image_id = 1 if image_id == 0
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
old_image_id = @current_image_id
|
|
35
|
+
|
|
36
|
+
unless @image_cache[cache_key]
|
|
37
|
+
# Transmit the image with scaling
|
|
38
|
+
escaped = Shellwords.escape(image_path)
|
|
39
|
+
|
|
40
|
+
# Create scaled PNG data using ImageMagick
|
|
41
|
+
png_data = `convert #{escaped}[0] -auto-orient -resize #{pixel_width}x#{pixel_height}\\> PNG:- 2>/dev/null`
|
|
42
|
+
return false if png_data.empty?
|
|
43
|
+
|
|
44
|
+
# Encode as base64 and chunk it (max 4096 bytes per chunk)
|
|
45
|
+
encoded = Base64.strict_encode64(png_data)
|
|
46
|
+
chunks = encoded.scan(/.{1,4096}/)
|
|
47
|
+
|
|
48
|
+
# Transmit image in chunks
|
|
49
|
+
chunks.each_with_index do |chunk, idx|
|
|
50
|
+
more = idx < chunks.length - 1 ? 1 : 0
|
|
51
|
+
if idx == 0
|
|
52
|
+
# First chunk: specify format (f=100 for PNG), action (a=t for transmit)
|
|
53
|
+
# i=image_id, m=more_chunks
|
|
54
|
+
print "\e_Ga=t,f=100,i=#{image_id},m=#{more};#{chunk}\e\\"
|
|
55
|
+
else
|
|
56
|
+
# Continuation chunks
|
|
57
|
+
print "\e_Gm=#{more};#{chunk}\e\\"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
$stdout.flush
|
|
61
|
+
|
|
62
|
+
@image_cache[cache_key] = image_id
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Position cursor and display the NEW image FIRST
|
|
66
|
+
# a=p (place), i=image_id, C=1 (don't move cursor)
|
|
67
|
+
# Don't specify c/r - let kitty use image's native size (already scaled by ImageMagick)
|
|
68
|
+
print "\e[#{y};#{x}H" # Move cursor to position
|
|
69
|
+
print "\e_Ga=p,i=#{image_id},C=1\e\\"
|
|
70
|
+
$stdout.flush
|
|
71
|
+
|
|
72
|
+
# NOW delete old placement (after new one is visible) - atomic swap
|
|
73
|
+
if old_image_id && old_image_id != image_id
|
|
74
|
+
print "\e_Ga=d,d=i,i=#{old_image_id}\e\\"
|
|
75
|
+
$stdout.flush
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@current_image_id = image_id
|
|
79
|
+
true
|
|
16
80
|
end
|
|
17
81
|
|
|
18
82
|
def self.clear
|
|
83
|
+
# Actually clear the image
|
|
84
|
+
if @current_image_id
|
|
85
|
+
print "\e_Ga=d,d=i,i=#{@current_image_id}\e\\"
|
|
86
|
+
$stdout.flush
|
|
87
|
+
@current_image_id = nil
|
|
88
|
+
end
|
|
19
89
|
true
|
|
20
90
|
end
|
|
21
91
|
|
|
92
|
+
def self.get_cell_size
|
|
93
|
+
# Query terminal for cell size using XTWINOPS
|
|
94
|
+
# Or estimate from terminal size
|
|
95
|
+
begin
|
|
96
|
+
require 'io/console'
|
|
97
|
+
rows, cols = IO.console.winsize
|
|
98
|
+
# Get pixel size if available
|
|
99
|
+
if IO.console.respond_to?(:winsize_pixels)
|
|
100
|
+
prows, pcols = IO.console.winsize_pixels rescue nil
|
|
101
|
+
if prows && pcols && prows > 0 && pcols > 0
|
|
102
|
+
return [pcols / cols, prows / rows]
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
# Fall back to common defaults (10x20 pixels per cell)
|
|
106
|
+
[10, 20]
|
|
107
|
+
rescue
|
|
108
|
+
[10, 20]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
22
112
|
private
|
|
23
113
|
|
|
24
114
|
def self.get_dimensions(image_path)
|
data/lib/termpix/version.rb
CHANGED
data/lib/termpix.rb
CHANGED
|
@@ -61,6 +61,12 @@ module Termpix
|
|
|
61
61
|
!@protocol.nil?
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
# Check if protocol supports atomic replace (show new without clearing first)
|
|
65
|
+
# Kitty can replace images without flash; w3m/sixel need explicit clear
|
|
66
|
+
def atomic_replace?
|
|
67
|
+
@protocol == :kitty
|
|
68
|
+
end
|
|
69
|
+
|
|
64
70
|
# Get information about the current protocol
|
|
65
71
|
def info
|
|
66
72
|
{
|
|
@@ -73,17 +79,17 @@ module Termpix
|
|
|
73
79
|
private
|
|
74
80
|
|
|
75
81
|
def detect_protocol
|
|
76
|
-
# Check for
|
|
82
|
+
# Check for Kitty terminal first - use native kitty protocol
|
|
83
|
+
if ENV['TERM'] == 'xterm-kitty' || ENV['KITTY_WINDOW_ID']
|
|
84
|
+
return :kitty if check_dependency('convert')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Check for Sixel support - works well with curses apps
|
|
77
88
|
# Note: urxvt/rxvt-unicode does NOT support sixel unless specially compiled
|
|
78
|
-
# Kitty's sixel support doesn't work properly (shows ASCII) - use w3m instead
|
|
79
89
|
if ENV['TERM']&.match(/^xterm(?!-kitty)|^mlterm|^foot/)
|
|
80
90
|
return :sixel if check_dependency('convert')
|
|
81
91
|
end
|
|
82
92
|
|
|
83
|
-
# Kitty graphics protocol disabled - fundamentally incompatible with curses
|
|
84
|
-
# After extensive testing: Kitty protocol requires exclusive terminal control
|
|
85
|
-
# that conflicts with curses' buffer management. Use w3m fallback instead.
|
|
86
|
-
|
|
87
93
|
# Überzug++ - disabled for now (implementation incomplete)
|
|
88
94
|
# TODO: Implement proper Überzug++ JSON-RPC communication
|
|
89
95
|
# if command_exists?('ueberzug') || command_exists?('ueberzugpp')
|
|
@@ -92,7 +98,7 @@ module Termpix
|
|
|
92
98
|
# end
|
|
93
99
|
# end
|
|
94
100
|
|
|
95
|
-
# Fall back to w3m (works
|
|
101
|
+
# Fall back to w3m (works on urxvt, xterm, etc.)
|
|
96
102
|
if command_exists?('/usr/lib/w3m/w3mimgdisplay')
|
|
97
103
|
if check_dependencies('xwininfo', 'xdotool', 'identify')
|
|
98
104
|
return :w3m
|
metadata
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: termpix
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Geir Isene
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description: 'Termpix v0.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
description: 'Termpix v0.3.0: Native Kitty graphics protocol - no more flash in kitty
|
|
14
|
+
terminal. Provides clean API for displaying images in terminal using best available
|
|
15
|
+
protocol (Kitty, Sixel, or w3m). Auto-detects terminal capabilities and falls back
|
|
16
|
+
gracefully.'
|
|
17
17
|
email: g@isene.com
|
|
18
18
|
executables: []
|
|
19
19
|
extensions: []
|