package tiny_libs
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
From-scratch libraries for teaching: graphics, audio, compression, crypto, networking and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_libs.terminal/Curses.ml.html
Source file Curses.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126(* Claude Code * * Copyright (C) 2026 Yoann Padioleau * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License * (LGPL) as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. *) (* See Curses.mli *) (*****************************************************************************) (* Screens *) (*****************************************************************************) (* the rows are shared between versions: drawing on a row copies that row only *) type t = { rows : int; cols : int; cells : Vt.cell array array; cursor : (int * int) option } let create ~rows ~cols = { rows; cols; cells = Array.make rows (Array.make cols Vt.blank); cursor = None } let rows (t : t) = t.rows let cols (t : t) = t.cols let cursor (c : (int * int) option) (t : t) : t = { t with cursor = c } let cell (t : t) r c = if r >= 0 && r < t.rows && c >= 0 && c < t.cols then t.cells.(r).(c) else Vt.blank (* a string's UTF-8 characters, each as its bytes *) let glyphs (s : string) : string list = let n = String.length s in let rec go i acc = if i >= n then List.rev acc else let b = Char.code s.[i] in let len = if b land 0xE0 = 0xC0 then 2 else if b land 0xF0 = 0xE0 then 3 else if b land 0xF8 = 0xF0 then 4 else 1 in let len = min len (n - i) in go (i + len) (String.sub s i len :: acc) in go 0 [] let put ?(attrs = Vt.plain) (r : int) (c : int) (text : string) (t : t) : t = if r < 0 || r >= t.rows then t else begin let row = Array.copy t.cells.(r) in List.iteri (fun i g -> let c = c + i in if c >= 0 && c < t.cols then row.(c) <- { Vt.glyph = g; attrs }) (glyphs text); let cells = Array.copy t.cells in cells.(r) <- row; { t with cells } end let box ?attrs (top : int) (left : int) (height : int) (width : int) (t : t) : t = let edge = "+" ^ String.make (max 0 (width - 2)) '-' ^ "+" in let t = put ?attrs top left edge t |> put ?attrs (top + height - 1) left edge in List.fold_left (fun t r -> t |> put ?attrs r left "|" |> put ?attrs r (left + width - 1) "|") t (List.init (max 0 (height - 2)) (fun i -> top + 1 + i)) let text (t : t) : string list = Array.to_list t.cells |> List.map (fun row -> let s = String.concat "" (Array.to_list (Array.map (fun (c : Vt.cell) -> c.glyph) row)) in let n = ref (String.length s) in while !n > 0 && s.[!n - 1] = ' ' do decr n done; String.sub s 0 !n) (*****************************************************************************) (* The difference, as bytes *) (*****************************************************************************) let cup (r : int) (c : int) : string = Printf.sprintf "\x1b[%d;%dH" (r + 1) (c + 1) let sgr (a : Vt.attrs) : string = let color base (c : Vt.color) = match c with | Default -> [] | Black -> [ base ] | Red -> [ base + 1 ] | Green -> [ base + 2 ] | Yellow -> [ base + 3 ] | Blue -> [ base + 4 ] | Magenta -> [ base + 5 ] | Cyan -> [ base + 6 ] | White -> [ base + 7 ] in let codes = (0 :: (if a.bold then [ 1 ] else [])) @ (if a.reverse then [ 7 ] else []) @ color 30 a.fg @ color 40 a.bg in "\x1b[" ^ String.concat ";" (List.map string_of_int codes) ^ "m" let refresh ~(before : t) (after : t) : string = let b = Buffer.create 256 in (* where the terminal's cursor is: where the last refresh left it, None when unknown (hidden, or after the last column: the wrap is pending); and its colours, plain between refreshes *) let at = ref before.cursor and pen = ref Vt.plain in let send (r : int) (c : int) (cell : Vt.cell) = if cell.attrs <> !pen then begin Buffer.add_string b (sgr cell.attrs); pen := cell.attrs end; Buffer.add_string b cell.glyph; at := if c + 1 < after.cols then Some (r, c + 1) else None in for r = 0 to after.rows - 1 do (* a row shared by the two versions hasn't changed *) if r >= before.rows || before.cells.(r) != after.cells.(r) then for c = 0 to after.cols - 1 do let now = after.cells.(r).(c) in if cell before r c <> now then begin (match !at with | Some (r', c') when r' = r && c' = c -> () (* the gap sent again, when cheaper than the move and in the colours already set *) | Some (r', c') when r' = r && c' < c && c - c' < String.length (cup r c) && List.for_all (fun i -> after.cells.(r).(i).attrs = !pen) (List.init (c - c') (fun i -> c' + i)) -> for i = c' to c - 1 do send r i after.cells.(r).(i) done | _ -> Buffer.add_string b (cup r c)); send r c now end done done; if !pen <> Vt.plain then Buffer.add_string b (sgr Vt.plain); (match after.cursor with | Some (r, c) -> if !at <> Some (r, c) then Buffer.add_string b (cup r c); if before.cursor = None then Buffer.add_string b "\x1b[?25h" | None -> if before.cursor <> None then Buffer.add_string b "\x1b[?25l"); Buffer.contents b let redraw (t : t) : string = let blank = { (create ~rows:t.rows ~cols:t.cols) with cursor = Some (0, 0) } in (* the terminal's cursor shown, so that a hidden one is hidden *) "\x1b[0m\x1b[2J\x1b[H\x1b[?25h" ^ refresh ~before:blank t
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>