My agentic slop goes here. Not intended for anyone else!
1(** OCaml implementation of the Kitty graphics protocol.
2
3 This library provides a clean API for generating escape sequences to display
4 images and animations in terminals that support the Kitty graphics protocol.
5
6 The protocol uses APC (Application Program Command) escape sequences to transmit
7 and display images with extensive control over placement, sizing, and composition.
8
9 See {{: https://sw.kovidgoyal.net/kitty/graphics-protocol/} Kitty Graphics Protocol}
10 for the complete specification.
11*)
12
13(** {1 Core Types} *)
14
15(** Image format for raw pixel data. *)
16type format =
17 | RGB (** 24-bit RGB (3 bytes per pixel) *)
18 | RGBA (** 32-bit RGBA (4 bytes per pixel) *)
19 | PNG (** PNG format *)
20
21(** Compression method for image data. *)
22type compression =
23 | No_compression
24 | Zlib
25
26(** Transmission medium. *)
27type transmission =
28 | Direct of {
29 format : format;
30 data : bytes;
31 width : int; (** Width in pixels *)
32 height : int; (** Height in pixels *)
33 compression : compression;
34 }
35 | File of {
36 path : string;
37 }
38
39(** Image identifier (number or string up to 24 chars). *)
40type image_id = private string
41
42(** Placement identifier. *)
43type placement_id = private int
44
45(** {1 Image Identifiers} *)
46
47(** [image_id_of_int n] creates an image ID from an integer.
48 @raise Invalid_argument if [n] is negative *)
49val image_id_of_int : int -> image_id
50
51(** [image_id_of_string s] creates an image ID from a string.
52 @raise Invalid_argument if [s] is empty or longer than 24 characters *)
53val image_id_of_string : string -> image_id
54
55(** [placement_id_of_int n] creates a placement ID from an integer.
56 @raise Invalid_argument if [n] is negative *)
57val placement_id_of_int : int -> placement_id
58
59(** {1 Placement Configuration} *)
60
61(** Placement configuration for displaying images. *)
62type placement
63
64(** [v ?image_id ?placement_id ?x ?y ?width ?height ?rows ?columns ?z_index
65 ?cursor_movement transmission ()] creates a placement configuration.
66
67 @param image_id Image identifier for reuse/deletion
68 @param placement_id Placement identifier for this specific display
69 @param x Left edge offset in pixels
70 @param y Top edge offset in pixels
71 @param width Width in pixels (scales image if different from source)
72 @param height Height in pixels (scales image if different from source)
73 @param rows Height in terminal cells
74 @param columns Width in terminal cells
75 @param z_index Z-order for layering (-2^31 to 2^31-1, default 0)
76 @param cursor_movement If false, cursor doesn't move after display (default true)
77 @param transmission The image data transmission method *)
78val v :
79 ?image_id:image_id ->
80 ?placement_id:placement_id ->
81 ?x:int ->
82 ?y:int ->
83 ?width:int ->
84 ?height:int ->
85 ?rows:int ->
86 ?columns:int ->
87 ?z_index:int ->
88 ?cursor_movement:bool ->
89 transmission ->
90 unit ->
91 placement
92
93(** {1 Rendering} *)
94
95(** [render placement] generates the complete escape sequence(s) for the placement.
96 For large images that require chunking, this returns a single string with all chunks. *)
97val render : placement -> string
98
99(** [render_chunked placement] generates escape sequences as a list of chunks.
100 Useful for streaming large images or interleaving with other output. *)
101val render_chunked : placement -> string list
102
103(** {1 Deletion} *)
104
105(** Delete images or placements. *)
106module Delete : sig
107 (** Deletion target. *)
108 type target =
109 | By_id of image_id
110 | By_image_id of image_id
111 | By_placement_id of placement_id
112 | At_cursor
113 | All
114
115 (** [render target] generates the escape sequence to delete images. *)
116 val render : target -> string
117end
118
119(** {1 Animation} *)
120
121(** Animation support for multi-frame images. *)
122module Animation : sig
123 (** Frame composition mode. *)
124 type composition =
125 | Blend (** Alpha blend with previous frame *)
126 | Overwrite (** Replace previous frame *)
127
128 (** Animation frame configuration. *)
129 type frame
130
131 (** [frame ?composition ?gap transmission frame_number] creates a frame configuration.
132
133 @param composition How to compose this frame with previous frames
134 @param gap Gap before next frame in milliseconds (0-65535)
135 @param transmission The frame image data
136 @param frame_number Frame index (1-based) *)
137 val frame :
138 ?composition:composition ->
139 ?gap:int ->
140 transmission ->
141 int ->
142 frame
143
144 (** Animation control. *)
145 type control =
146 | Set_gap of {
147 frame_number : int; (** Which frame to modify *)
148 gap : int; (** New gap in milliseconds *)
149 }
150 | Set_loop of int (** Number of loops (0 = infinite) *)
151 | Stop (** Stop animation *)
152 | Run (** Resume animation *)
153
154 (** [render_frame image_id frame] generates escape sequence for transmitting an animation frame.
155
156 @param image_id The animation's image identifier *)
157 val render_frame : image_id -> frame -> string
158
159 (** [render_control image_id control] generates escape sequence for animation control. *)
160 val render_control : image_id -> control -> string
161end
162
163(** {1 Fmt-style Formatters} *)
164
165(** [pp placement] creates a Fmt formatter that displays an image.
166
167 Example:
168 {[
169 let img = Graphics.v (Graphics.File { path = "image.png" }) () in
170 Fmt.pr "Here's an image: %a" Graphics.pp img
171 ]}
172*)
173val pp : placement -> unit Fmt.t
174
175(** [pp_delete target] creates a Fmt formatter that deletes images.
176
177 Example:
178 {[
179 Fmt.pr "Clearing screen: %a" Graphics.pp_delete Graphics.Delete.All
180 ]}
181*)
182val pp_delete : Delete.target -> unit Fmt.t
183
184(** [pp_animation_frame image_id frame] creates a Fmt formatter for animation frames. *)
185val pp_animation_frame : image_id -> Animation.frame -> unit Fmt.t
186
187(** [pp_animation_control image_id control] creates a Fmt formatter for animation control. *)
188val pp_animation_control : image_id -> Animation.control -> unit Fmt.t
189
190(** {1 Convenience Functions} *)
191
192(** [display_png_file ?x ?y ?width ?height ?rows ?columns path] displays a PNG file.
193
194 @param path Path to PNG file
195 @return Escape sequence string *)
196val display_png_file :
197 ?x:int ->
198 ?y:int ->
199 ?width:int ->
200 ?height:int ->
201 ?rows:int ->
202 ?columns:int ->
203 string ->
204 string
205
206(** [display_png_bytes ?x ?y ?width ?height ?rows ?columns ~width:w ~height:h data]
207 displays PNG data from bytes.
208
209 @param width Image width in pixels
210 @param height Image height in pixels
211 @param data PNG-encoded image data
212 @return Escape sequence string *)
213val display_png_bytes :
214 ?x:int ->
215 ?y:int ->
216 ?width:int ->
217 ?height:int ->
218 ?rows:int ->
219 ?columns:int ->
220 w:int ->
221 h:int ->
222 bytes ->
223 string
224
225(** [delete_all ()] generates escape sequence to delete all images. *)
226val delete_all : unit -> string
227
228(** [delete_by_id id] generates escape sequence to delete image by ID. *)
229val delete_by_id : image_id -> string