My agentic slop goes here. Not intended for anyone else!
1(** Kitty Terminal Graphics Protocol
2
3 This library implements the Kitty terminal graphics protocol, allowing
4 OCaml programs to display images in terminals that support the protocol
5 (Kitty, WezTerm, Konsole, Ghostty, etc.).
6
7 The protocol uses APC (Application Programming Command) escape sequences
8 to transmit and display pixel graphics. Images can be transmitted as raw
9 RGB/RGBA data or PNG, and displayed at specific positions with various
10 placement options.
11
12 {2 Basic Usage}
13
14 {[
15 (* Display a PNG image *)
16 let png_data = read_file "image.png" in
17 let cmd = Kgp.Command.transmit_and_display ~format:`Png () in
18 let buf = Buffer.create 1024 in
19 Kgp.Command.write buf cmd ~data:png_data;
20 print_string (Buffer.contents buf)
21 ]}
22
23 {2 Protocol Reference}
24
25 See {{:https://sw.kovidgoyal.net/kitty/graphics-protocol/} Kitty Graphics Protocol}
26 for the full specification. *)
27
28(** {1 Polymorphic Variant Types} *)
29
30type format = [ `Rgba32 | `Rgb24 | `Png ]
31(** Image data formats. [`Rgba32] is 32-bit RGBA (4 bytes per pixel),
32 [`Rgb24] is 24-bit RGB (3 bytes per pixel), [`Png] is PNG encoded data. *)
33
34type transmission = [ `Direct | `File | `Tempfile ]
35(** Transmission methods. [`Direct] sends data inline, [`File] reads from a path,
36 [`Tempfile] reads from a temp file that the terminal deletes after reading. *)
37
38type compression = [ `None | `Zlib ]
39(** Compression options. [`None] for raw data, [`Zlib] for RFC 1950 compression. *)
40
41type quiet = [ `Noisy | `Errors_only | `Silent ]
42(** Response suppression. [`Noisy] sends all responses (default),
43 [`Errors_only] suppresses OK responses, [`Silent] suppresses all. *)
44
45type cursor = [ `Move | `Static ]
46(** Cursor movement after displaying. [`Move] advances cursor (default),
47 [`Static] keeps cursor in place. *)
48
49type composition = [ `Alpha_blend | `Overwrite ]
50(** Composition modes. [`Alpha_blend] for full blending (default),
51 [`Overwrite] for simple pixel replacement. *)
52
53type delete =
54 [ `All_visible
55 | `All_visible_and_free
56 | `By_id of int * int option
57 | `By_id_and_free of int * int option
58 | `By_number of int * int option
59 | `By_number_and_free of int * int option
60 | `At_cursor
61 | `At_cursor_and_free
62 | `At_cell of int * int
63 | `At_cell_and_free of int * int
64 | `At_cell_z of int * int * int
65 | `At_cell_z_and_free of int * int * int
66 | `By_column of int
67 | `By_column_and_free of int
68 | `By_row of int
69 | `By_row_and_free of int
70 | `By_z_index of int
71 | `By_z_index_and_free of int
72 | `By_id_range of int * int
73 | `By_id_range_and_free of int * int
74 | `Frames
75 | `Frames_and_free ]
76(** Delete target specification. Each variant has two forms: one that only
77 removes placements (e.g., [`All_visible]) and one that also frees the
78 image data (e.g., [`All_visible_and_free]). Tuple variants contain
79 (image_id, optional_placement_id) or (x, y) coordinates. *)
80
81type animation_state = [ `Stop | `Loading | `Run ]
82(** Animation playback state. [`Stop] halts animation, [`Loading] runs but
83 waits for new frames at end, [`Run] runs normally and loops. *)
84
85(** {1 Type Modules} *)
86
87module Format : sig
88 type t = format
89
90 val to_int : t -> int
91 (** Convert to protocol integer value (32, 24, or 100). *)
92end
93
94module Transmission : sig
95 type t = transmission
96
97 val to_char : t -> char
98 (** Convert to protocol character ('d', 'f', or 't'). *)
99end
100
101module Compression : sig
102 type t = compression
103
104 val to_char : t -> char option
105 (** Convert to protocol character ([None] or [Some 'z']). *)
106end
107
108module Quiet : sig
109 type t = quiet
110
111 val to_int : t -> int
112 (** Convert to protocol integer (0, 1, or 2). *)
113end
114
115module Cursor : sig
116 type t = cursor
117
118 val to_int : t -> int
119 (** Convert to protocol integer (0 or 1). *)
120end
121
122module Composition : sig
123 type t = composition
124
125 val to_int : t -> int
126 (** Convert to protocol integer (0 or 1). *)
127end
128
129module Delete : sig
130 type t = delete
131end
132
133(** {1 Placement Options} *)
134
135module Placement : sig
136 type t = Kgp_placement.t
137 (** Placement configuration. *)
138
139 val make :
140 ?source_x:int ->
141 ?source_y:int ->
142 ?source_width:int ->
143 ?source_height:int ->
144 ?cell_x_offset:int ->
145 ?cell_y_offset:int ->
146 ?columns:int ->
147 ?rows:int ->
148 ?z_index:int ->
149 ?placement_id:int ->
150 ?cursor:cursor ->
151 ?unicode_placeholder:bool ->
152 unit ->
153 t
154 (** Create a placement configuration.
155
156 @param source_x Left edge of source rectangle in pixels (default 0)
157 @param source_y Top edge of source rectangle in pixels (default 0)
158 @param source_width Width of source rectangle (default: full width)
159 @param source_height Height of source rectangle (default: full height)
160 @param cell_x_offset X offset within the first cell in pixels
161 @param cell_y_offset Y offset within the first cell in pixels
162 @param columns Number of columns to display over (scales image)
163 @param rows Number of rows to display over (scales image)
164 @param z_index Stacking order (negative = under text)
165 @param placement_id Unique ID for this placement
166 @param cursor Cursor movement policy after display
167 @param unicode_placeholder Create virtual placement for Unicode mode *)
168
169 val empty : t
170 (** Empty placement with all defaults. *)
171end
172
173(** {1 Animation} *)
174
175module Frame : sig
176 type t = Kgp_frame.t
177 (** Animation frame configuration. *)
178
179 val make :
180 ?x:int ->
181 ?y:int ->
182 ?base_frame:int ->
183 ?edit_frame:int ->
184 ?gap_ms:int ->
185 ?composition:composition ->
186 ?background_color:int32 ->
187 unit ->
188 t
189 (** Create a frame specification.
190
191 @param x Left edge where frame data is placed (pixels)
192 @param y Top edge where frame data is placed (pixels)
193 @param base_frame 1-based frame number to use as background canvas
194 @param edit_frame 1-based frame number to edit (0 = new frame)
195 @param gap_ms Delay before next frame in milliseconds
196 @param composition How to blend pixels onto the canvas
197 @param background_color 32-bit RGBA background when no base frame *)
198
199 val empty : t
200 (** Empty frame spec with defaults. *)
201end
202
203module Animation : sig
204 type state = animation_state
205
206 type t = Kgp_animation.t
207 (** Animation control operations. *)
208
209 val set_state : ?loops:int -> state -> t
210 (** Set animation state.
211 @param loops Number of loops: 0 = ignored, 1 = infinite, n = n-1 loops *)
212
213 val set_gap : frame:int -> gap_ms:int -> t
214 (** Set the gap (delay) for a specific frame.
215 @param frame 1-based frame number
216 @param gap_ms Delay in milliseconds (negative = gapless) *)
217
218 val set_current_frame : int -> t
219 (** Make a specific frame (1-based) the current displayed frame. *)
220end
221
222module Compose : sig
223 type t = Kgp_compose.t
224 (** Composition operation. *)
225
226 val make :
227 source_frame:int ->
228 dest_frame:int ->
229 ?width:int ->
230 ?height:int ->
231 ?source_x:int ->
232 ?source_y:int ->
233 ?dest_x:int ->
234 ?dest_y:int ->
235 ?composition:composition ->
236 unit ->
237 t
238 (** Compose a rectangle from one frame onto another. *)
239end
240
241(** {1 Commands} *)
242
243module Command : sig
244 type t = Kgp_command.t
245 (** A graphics protocol command. *)
246
247 (** {2 Image Transmission} *)
248
249 val transmit :
250 ?image_id:int ->
251 ?image_number:int ->
252 ?format:format ->
253 ?transmission:transmission ->
254 ?compression:compression ->
255 ?width:int ->
256 ?height:int ->
257 ?size:int ->
258 ?offset:int ->
259 ?quiet:quiet ->
260 unit ->
261 t
262 (** Transmit image data without displaying. *)
263
264 val transmit_and_display :
265 ?image_id:int ->
266 ?image_number:int ->
267 ?format:format ->
268 ?transmission:transmission ->
269 ?compression:compression ->
270 ?width:int ->
271 ?height:int ->
272 ?size:int ->
273 ?offset:int ->
274 ?quiet:quiet ->
275 ?placement:Placement.t ->
276 unit ->
277 t
278 (** Transmit image data and display it immediately. *)
279
280 val query :
281 ?format:format ->
282 ?transmission:transmission ->
283 ?width:int ->
284 ?height:int ->
285 ?quiet:quiet ->
286 unit ->
287 t
288 (** Query terminal support without storing the image. *)
289
290 (** {2 Display} *)
291
292 val display :
293 ?image_id:int ->
294 ?image_number:int ->
295 ?placement:Placement.t ->
296 ?quiet:quiet ->
297 unit ->
298 t
299 (** Display a previously transmitted image. *)
300
301 (** {2 Deletion} *)
302
303 val delete : ?quiet:quiet -> delete -> t
304 (** Delete images or placements. *)
305
306 (** {2 Animation} *)
307
308 val frame :
309 ?image_id:int ->
310 ?image_number:int ->
311 ?format:format ->
312 ?transmission:transmission ->
313 ?compression:compression ->
314 ?width:int ->
315 ?height:int ->
316 ?quiet:quiet ->
317 frame:Frame.t ->
318 unit ->
319 t
320 (** Transmit animation frame data. *)
321
322 val animate : ?image_id:int -> ?image_number:int -> ?quiet:quiet -> Animation.t -> t
323 (** Control animation playback. *)
324
325 val compose : ?image_id:int -> ?image_number:int -> ?quiet:quiet -> Compose.t -> t
326 (** Compose animation frames. *)
327
328 (** {2 Output} *)
329
330 val write : Buffer.t -> t -> data:string -> unit
331 (** Write the command to a buffer. *)
332
333 val to_string : t -> data:string -> string
334 (** Convert command to a string. *)
335end
336
337(** {1 Response Parsing} *)
338
339module Response : sig
340 type t = Kgp_response.t
341 (** A parsed terminal response. *)
342
343 val parse : string -> t option
344 (** Parse a response from terminal output. *)
345
346 val is_ok : t -> bool
347 (** Check if the response indicates success. *)
348
349 val message : t -> string
350 (** Get the response message. *)
351
352 val error_code : t -> string option
353 (** Extract the error code if this is an error response. *)
354
355 val image_id : t -> int option
356 (** Get the image ID from the response. *)
357
358 val image_number : t -> int option
359 (** Get the image number from the response. *)
360
361 val placement_id : t -> int option
362 (** Get the placement ID from the response. *)
363end
364
365(** {1 Unicode Placeholders} *)
366
367module Unicode_placeholder : sig
368 val placeholder_char : Uchar.t
369 (** The Unicode placeholder character U+10EEEE. *)
370
371 val write :
372 Buffer.t ->
373 image_id:int ->
374 ?placement_id:int ->
375 rows:int ->
376 cols:int ->
377 unit ->
378 unit
379 (** Write placeholder characters to a buffer. *)
380
381 val row_diacritic : int -> Uchar.t
382 (** Get the combining diacritic for a row number (0-based). *)
383
384 val column_diacritic : int -> Uchar.t
385 (** Get the combining diacritic for a column number (0-based). *)
386
387 val id_high_byte_diacritic : int -> Uchar.t
388 (** Get the diacritic for the high byte of a 32-bit image ID. *)
389end
390
391(** {1 Terminal Detection} *)
392
393module Detect : sig
394 val make_query : unit -> string
395 (** Generate a query command to test graphics support. *)
396
397 val supports_graphics : Response.t option -> da1_received:bool -> bool
398 (** Determine if graphics are supported based on query results. *)
399end