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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34ec8fff70b1018305deaa94931516564e573406a713fac62bbf6691dfe120b8
4
- data.tar.gz: fcf1056c196bf42680fc1f78b8067db71a64ee342651c6684cb78f34d27f6d1d
3
+ metadata.gz: 763bacec25caa2a40043e19c2319b44c9433848e307281a761c4933ce345c227
4
+ data.tar.gz: e5fe48498bcf92d6cfccea1fedbec3d4cd33945096682e769a1464bb2248d0d2
5
5
  SHA512:
6
- metadata.gz: f706b1ff612ec9f6f08596833fe66b5fc1b50946c61aa76bb9c8a88ba327d6e595c5607fb439024a94e29d857b603801f577a05876864c8f8493a9bbdbc8a906
7
- data.tar.gz: 4610351de9645c8dd6855d25f70b800e74b088633a6cf8d3bc02e785f104ac62230e637923bf4c7aa9251973c434e4917aaa6c0c60d15a1d1a27bad7eb4760c6
6
+ metadata.gz: b55c954c5ccc8f78922973a8efd3c20a4891509fe9adc7c6baa71f91620d28952379096dbbeab6703799f0c58f6401ef3820e83f89eb0368d37d6e36e5db42b5
7
+ data.tar.gz: 890174a649f8cdc54d4506f3ff21075aa5d5f82f2be282101b19ad6e107b37271673272ac8c582ed96d2210a50375435ed31ee9d47fa2ce2259fdfba1749a0c8
@@ -4,21 +4,111 @@ require 'base64'
4
4
  module Termpix
5
5
  module Protocols
6
6
  # Kitty Graphics Protocol
7
- # After extensive testing: Kitty protocol fundamentally incompatible with curses
8
- # The protocol requires precise cursor control that conflicts with curses' buffer
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
- # Kitty graphics protocol does not work reliably with curses applications
13
- # The fundamental issue is that both Kitty and curses need exclusive control
14
- # of terminal state, causing unavoidable conflicts
15
- false
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)
@@ -1,3 +1,3 @@
1
1
  module Termpix
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
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 Sixel support first - works better with curses apps
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 everywhere but has brief flash in Kitty)
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.2.1
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: 2025-11-03 00:00:00.000000000 Z
11
+ date: 2026-01-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: 'Termpix v0.2.1: Performance optimization - only auto-orients images
14
- with EXIF rotation data. Provides clean API for displaying images in terminal using
15
- best available protocol (Sixel or w3m). Auto-detects terminal capabilities and falls
16
- back gracefully.'
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: []