[0.2.0]

Breaking changes

  • Compiling now runs make through elixir_make (compilers: [:elixir_make | Mix.compilers()]), so make and erl must be on PATH at build time; on Windows, nmake runs Makefile.win, which builds the cells NIF with cl when it is present. A C compiler and libdeflate stay optional: without a compiler FrenchCurve.Backend.Cells quantises in Elixir, and without libdeflate FrenchCurve.Deflate uses :zlib. Nothing to change in code; install make on build machines that lack it.

  • When libdeflate is built, kitty payloads sent with compress: are different bytes from the :zlib output of 0.1.4. Terminals decode the same pixels; tests that compare exact escape-sequence bytes need new expected values.

Added

  • FrenchCurve.Png.encode_indexed/4: a palette PNG, a byte a pixel, for a buffer of at most 256 colours, else {:error, :too_many_colours}.

  • FrenchCurve.Backend.Kitty.encode/2 and transmit_encoded/2: a raster's pixels encoded once and stored under any id without encoding them again; encode/2 takes format: :png (f=100), a palette PNG where the raster allows, else a truecolour one. transmit/3 is unchanged.

  • :cells protocol and FrenchCurve.Backend.Cells: draws a raster as block characters with a foreground and background colour per cell. Glyph sets via set: — :half (1×2), :quad (2×2), :sextant (2×3), :braille (2×4 dots), :octant (2×4). Options :background and :alpha_threshold (default 128).

  • FrenchCurve.Backend.Cells.render/2, to_ansi/1, glyph/2, sets/0, subpixels/1, available?/0, and default_set/1, which suggests a set from a protocol atom or an environment map (:sextant for iTerm2, :octant for kitty-protocol terminals, :half otherwise).

  • Cell rendering uses a native helper when a C compiler is available at build time; native: false on FrenchCurve.Backend.Cells.render/2 forces the Elixir path.

  • FrenchCurve.render/3 and FrenchCurve.to_terminal/3 accept :cells; to_terminal/3 returns the ANSI string. FrenchCurve.frame/3 returns nil for :cells, as for :braille.

  • FrenchCurve.Capability.backend(:cells) returns FrenchCurve.Backend.Cells. :cells is never returned by detect/0,1; name it explicitly.

  • FrenchCurve.Deflate.compress/1,2: zlib-format compression through libdeflate (levels 1..12) when the NIF is built, :zlib (levels clamped to 1..9) otherwise. FrenchCurve.Deflate.available?/0 reports which is in use.

  • Kitty :compress option on FrenchCurve.Backend.Kitty.render/2 and transmit/3 takes an integer compression level as well as true.

  • FrenchCurve.Capability.iterm2?/1: whether an environment map describes an iTerm2 session, locally or over ssh.

  • FRENCH_CURVE_ITERM2=kitty makes FrenchCurve.Capability.detect/1 return :kitty for iTerm2; inline or unset returns :iterm2.

Changed

  • The compiled native code is built into and loaded from priv/native/<system architecture> (FrenchCurve.Native) instead of priv/, so builds for macOS and Linux in one checkout no longer overwrite each other. mix.exs passes the folder to make as PRIV_DIR.

  • FrenchCurve.Raster.to_rgba_binary/1 of a raster with no pixels set returns its background repeated.

  • FrenchCurve.Capability.placements?/1 is false for iTerm2, including when it is detected as :kitty, so FrenchCurve.frame/3 retransmits each frame there instead of placing a stored image.

  • Packaging: Makefile, Makefile.win, c_src and CHANGELOG.md are in the Hex package files, and the package description reads "Terminal graphics for Elixir".

[0.1.4]

Added

  • FrenchCurve.Draw.fill_polygon/3: fills a closed polygon with fractional corner points, even-odd rule, outline included. Fewer than three points draw as a polyline.
  • FrenchCurve.Raster.crop/3: returns a {width, height} rectangle of a raster starting at {x, y}; the rectangle must lie inside the raster.
  • FrenchCurve.Backend.Kitty.place/2 options :x_offset and :y_offset (pixel shift inside the cursor cell) and :source ({x, y, width, height}, the part of the stored image to show).
  • FrenchCurve.Backend.Kitty.delete/1 option :z: removes every placement at that z-index, of any image, keeping the images stored. :id is required only when :z is not given.

Changed

[0.1.3]

Changed

  • Development dependency plumb raised to ~> 0.2. No library code changes.

[0.1.2]

Added

[0.1.1]

Added

  • FrenchCurve.Raster: fixed-size RGBA buffer with new/3, dimensions/1, in_bounds?/3, put_pixel/4, get_pixel/3, rows/1, to_rgba_binary/1, finalize/1, from_rgba/4.
  • FrenchCurve.Draw: line/4, polyline/3, rect/4, fill_rect/4, circle/4, fill_circle/4.
  • FrenchCurve.Chart.line/2 and area/2: plot a list of numbers across a raster, optionally smoothed.
  • FrenchCurve.Curve.catmull_rom/2 and to_pixels/1.
  • Backends FrenchCurve.Backend.Kitty, Sixel, Iterm2 and Braille.
  • FrenchCurve.render/2,3 returns each backend's native output; FrenchCurve.to_terminal/3 returns a binary for every protocol.
  • FrenchCurve.frame/3: returns {paint, clear, place} sequences for a region drawn repeatedly, picking between kitty transmit-and-place and transmit-and-display. FrenchCurve.image_id/1 maps any term to a kitty image id.
  • FrenchCurve.Backend.Kitty.transmit/3, place/2 and delete/1; render/2 options :fit, :compress, :cursor, :quiet, :id. All transmit/3, place/2 and delete/1 chunks carry q=2.
  • FrenchCurve.Capability.detect/0,1 reads KITTY_WINDOW_ID, TERM, TERM_PROGRAM, WEZTERM_PANE, LC_TERMINAL and KONSOLE_VERSION; iTerm2 is detected as :iterm2; empty variables count as unset. detect/0 is cached in :persistent_term.
  • FrenchCurve.Capability.probe/0, probe_complete?/1 and from_probe/1: detect the protocol from the terminal's replies to a query sequence.
  • FrenchCurve.Capability.placements?/0,1 and backend/1.
  • FrenchCurve.Geometry: cell_size_query/0, parse_cell_size/1, detect_cell_size/1, default_cell_px/0, braille_cell_px/0 and pixels_for_cells/2.
  • FrenchCurve.Png.encode/3.
  • FrenchCurve.Sprite (new/2, upload/3, place/3, delete/3) and FrenchCurve.Sprite.Registry (new/0, ensure/3). upload/3 takes kitty transmit options such as :compress.