package quickterface
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Quick-to-program app interfaces in OCaml for terminal and web
Install
dune-project
Dependency
Authors
Maintainers
Sources
quickterface-0.1.0.tbz
sha256=8261e3819564fb5d05f1f313e62b73382152591d7a4349ae5b1b08a4fc2469f3
sha512=e739a971bb0e696ab716c168419c59a3d195922d2d1e4963106a845e3442ffa085b05106f36cceeec9b806bf7d6ef2c31e98db04911fbf73c5ac0ce626449d0f
doc/src/quickterface.terminal_app/log_item.ml.html
Source file log_item.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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283open! Core type t = | Output_text of { text : string; options : Quickterface.Output_text_options.t; } | Output_math of { math : Quickterface.Math.t; options : Quickterface.Output_text_options.t; } | Input_text of { prompt : string; text : string } let output_text ?(options = Quickterface.Output_text_options.default) text = Output_text { text; options } let output_math ?(options = Quickterface.Output_text_options.default) math = Output_math { math; options } let input_text ?(prompt = "> ") text = Input_text { prompt; text } let attr = function | Output_text { options; _ } -> Theme.text_output_from_options options | Output_math { options; _ } -> Theme.math_output_from_options options | Input_text _ -> Theme.text_input_frozen module Math_renderer = struct module I = Notty.I type image = Notty.image module Horizontally_aligned_image : sig type t val make_from_single_image : image -> center_line_index:int -> unit -> t val make_from_parts : ?top:image -> ?center:image -> ?bottom:image -> unit -> t val height : t -> int val to_notty : t -> image val ( <|> ) : t -> t -> t val hcat : t list -> t end = struct open I type t = { image : image; center_line_index : int } let make ~image ~center_line_index = if not (center_line_index < height image) then raise_s [%message "Center line index must be less than image height" (center_line_index : int)]; if not (center_line_index >= 0) then raise_s [%message "Center line index must be non-negative" (center_line_index : int)]; { image; center_line_index } let left_align_parts top center bottom = let max_width = max (width top) (max (width center) (width bottom)) in let padder img = pad ~r:(max_width - width img) img in (padder top, padder center, padder bottom) let make_from_single_image image ~center_line_index () = make ~image ~center_line_index let make_from_parts ?(top = empty) ?(center = void 1 1) ?(bottom = empty) () = if not (height center = 1) then raise_s [%message "Center image must be single row"]; let padded_top, padded_center, padded_bottom = left_align_parts top center bottom in make ~image:(padded_top <-> padded_center <-> padded_bottom) ~center_line_index:(height padded_top) let height { image; center_line_index = _ } = height image let to_parts { image; center_line_index } = let top_height = center_line_index in let bottom_height = I.height image - center_line_index - 1 in let top = I.crop ~b:(1 + bottom_height) image in let center = I.crop ~t:top_height ~b:bottom_height image in let bottom = I.crop ~t:(1 + top_height) image in (top, center, bottom) let to_notty { image; center_line_index = _ } = image let ( <|> ) img_x img_y = let img_x_top, img_x_center, img_x_bottom = to_parts img_x in let img_y_top, img_y_center, img_y_bottom = to_parts img_y in make_from_parts ~top:(img_x_top <|> img_y_top) ~center:(img_x_center <|> img_y_center) ~bottom:(img_x_bottom <|> img_y_bottom) () let hcat = function | [] -> make_from_parts () | h :: ts -> List.fold ts ~init:h ~f:( <|> ) end let render_math ~render_info:_ attr math = let open I in let open Horizontally_aligned_image in let rec render_math = let plain_string s = make_from_single_image (string attr s) ~center_line_index:0 () in let super_sub_script_helper ~base ~script ~side = let base_img = render_math base |> to_notty in let script_img = render_math script |> to_notty in let script_img_height = I.height script_img in let base_img_height = I.height base_img in let whole_image = match side with | `Superscript -> I.(pad ~l:(I.width base_img) script_img <-> base_img) | `Subscript -> I.(base_img <-> pad ~l:(I.width base_img) script_img) in let center_line_index = match side with | `Superscript -> script_img_height + (base_img_height / 2) | `Subscript -> base_img_height / 2 in make_from_single_image whole_image ~center_line_index () in function | Quickterface.Math.Char c -> plain_string (Char.to_string c) | Literal s -> plain_string s | Infinity -> plain_string "∞" | Pi -> plain_string "π" | E -> plain_string "e" | Equals -> plain_string "=" | Plus -> plain_string "+" | Minus -> plain_string "-" | Star -> plain_string "*" | C_dot -> plain_string "·" | Times -> plain_string "×" | Divide -> plain_string "÷" | Plus_minus -> plain_string "±" | Superscript { base; superscript } -> super_sub_script_helper ~base ~script:superscript ~side:`Superscript | Subscript { base; subscript } -> super_sub_script_helper ~base ~script:subscript ~side:`Subscript | Exp -> plain_string "exp" | Ln -> plain_string "ln" | Sin -> plain_string "sin" | Cos -> plain_string "cos" | List elements -> let element_imgs = List.map elements ~f:render_math in hcat element_imgs | Frac (num, denom) -> let num_img = render_math num |> to_notty |> pad ~l:1 ~r:1 in let denom_img = render_math denom |> to_notty |> pad ~l:1 ~r:1 in let max_width = max (I.width num_img) (I.width denom_img) in let line_img = uchar attr Notty_utils.uchar_box_drawing_light_horizontal max_width 1 in let centered_num_img = pad ~l:((max_width - I.width num_img) / 2) num_img in let centered_denom_img = pad ~l:((max_width - I.width denom_img) / 2) denom_img in make_from_parts ~top:centered_num_img ~center:line_img ~bottom:centered_denom_img () | Bracketed inner -> let inner_img = render_math inner |> to_notty in let bracket_height = I.height inner_img in let make_bracket_img ~single_line ~top ~mid ~bottom = if bracket_height = 1 then string attr single_line else uchar attr top 1 1 <-> uchar attr mid 1 (bracket_height - 2) <-> uchar attr bottom 1 1 in let left_bracket_img = make_bracket_img ~single_line:"(" ~top:Notty_utils.uchar_paren_drawing_light_top_left ~mid:Notty_utils.uchar_paren_drawing_light_mid_left ~bottom:Notty_utils.uchar_paren_drawing_light_bottom_left in let right_bracket_img = make_bracket_img ~single_line:")" ~top:Notty_utils.uchar_paren_drawing_light_top_right ~mid:Notty_utils.uchar_paren_drawing_light_mid_right ~bottom:Notty_utils.uchar_paren_drawing_light_bottom_right in let notty_image = I.(left_bracket_img <|> inner_img <|> right_bracket_img) in make_from_single_image notty_image ~center_line_index:(bracket_height / 2) () | Partial -> plain_string "∂" | Less_than -> plain_string "<" | Less_than_or_equal_to -> plain_string "≤" | Greater_than -> plain_string ">" | Greater_than_or_equal_to -> plain_string "≥" | Not_equal -> plain_string "≠" | Approximately_equals -> plain_string "≈" | Equivalent_to -> plain_string "≡" | Integral { lower; upper; body } -> let lower_img_notty_opt = Option.(lower >>| render_math >>| to_notty) in let upper_img_notty_opt = Option.(upper >>| render_math >>| to_notty) in let body_img = render_math body in let body_height = height body_img in let integral_symbol_img = if body_height <= 1 then plain_string "∫" else let notty_image = uchar attr Notty_utils.uchar_paren_top_half_integral 1 1 <-> uchar attr Notty_utils.uchar_paren_integral_extender 1 (body_height - 2) <-> uchar attr Notty_utils.uchar_paren_bottom_half_integral 1 1 in make_from_single_image notty_image ~center_line_index:(body_height / 2) () in let limits_image = let upper = Option.value ~default:I.empty upper_img_notty_opt in let lower = Option.value ~default:I.empty lower_img_notty_opt in let notty_image = I.(upper <-> void 0 1 <-> lower) in make_from_single_image notty_image ~center_line_index:(I.height upper) () in integral_symbol_img <|> limits_image <|> body_img in render_math math end let render_math ~render_info attr img = Math_renderer.( Horizontally_aligned_image.to_notty (render_math ~render_info attr img)) let render_string_handling_newlines attr string = string |> String.split ~on:'\n' |> List.map ~f:(fun x -> Notty.I.string attr x) |> Notty.I.vcat let render ~render_info t = let t_attr = attr t in (match t with | Output_text { text; _ } -> render_string_handling_newlines t_attr text | Output_math { math; _ } -> render_math ~render_info t_attr math | Input_text { prompt; text } -> render_string_handling_newlines t_attr [%string "%{prompt}%{text}"]) |> Notty_utils.boxed ~padding_control: (`To_min_boxed_size (Some (render_info.Render_info.screen_width, Right), None)) module For_testing = struct let render_math = render_math end
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>