package tiny_appkits
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Application engines from scratch: a spreadsheet, rich text, paint, draw, CAD, editors and more
Install
dune-project
Dependency
Authors
Maintainers
Sources
0.3.6.tar.gz
md5=7c636383d146d30ac6f2fa234a6253c8
sha512=c79f3823c5f8f57e5038eb640d487c61168b84aa07c61999d6622ef9fd0c890e2b03b4c6a7cdbbe9352a49e25dda00ac7bb14693cee8e3d7beeed251351a2af0
doc/src/tiny_appkits.appkit_slides/Outline.ml.html
Source file Outline.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(* 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. *) type slide = { title : string; points : (int * string) list } (* how deep a line is indented, in columns, and what is after that *) let indent line = let n = String.length line in let rec go i cols = if i < n && line.[i] = ' ' then go (i + 1) (cols + 1) else if i < n && line.[i] = '\t' then go (i + 1) (cols + 2) else (i, cols) in let i, cols = go 0 0 in (cols, String.sub line i (n - i)) (* a slide per title line; the lines are read in order, the slide being filled kept last-first until the next title closes it *) let parse text = let close cur acc = match cur with Some s -> { s with points = List.rev s.points } :: acc | None -> acc in let cur, acc = List.fold_left (fun (cur, acc) line -> let cols, rest = indent line in if String.trim rest = "" then (cur, acc) else if cols = 0 then (Some { title = rest; points = [] }, close cur acc) else let level = max 1 (cols / 2) in let s = Option.value cur ~default:{ title = ""; points = [] } in (Some { s with points = (level, String.trim rest) :: s.points }, acc)) (None, []) (String.split_on_char '\n' text) in List.rev (close cur acc) let slide_at text offset = let before = String.sub text 0 (max 0 (min offset (String.length text))) in let lines = String.split_on_char '\n' before in (* the titles before the caret's line, and the caret's line itself if it is one; a point before any title is on slide 0 *) let titles = List.filter (fun l -> let cols, rest = indent l in cols = 0 && String.trim rest <> "") lines in let starts_untitled = match List.find_opt (fun l -> String.trim l <> "") (String.split_on_char '\n' text) with | Some l -> fst (indent l) > 0 | None -> false in max 0 (List.length titles - 1 + if starts_untitled then 1 else 0) let start_of text n = (* the offset of every title line, in order *) let rec titles pos acc = if pos > String.length text then List.rev acc else let e = match String.index_from_opt text pos '\n' with Some e -> e | None -> String.length text in let line = String.sub text pos (e - pos) in let cols, rest = indent line in let acc = if cols = 0 && String.trim rest <> "" then pos :: acc else acc in titles (e + 1) acc in match List.nth_opt (titles 0 []) n with Some pos -> pos | None -> String.length text let lines_of text n = (* the numbered lines that are not blank, and each slide's lines: a title opens one, a point joins the last (or an untitled first) *) let numbered = List.filter (fun (_, l) -> String.trim l <> "") (List.mapi (fun i l -> (i, l)) (String.split_on_char '\n' text)) in let slides = List.fold_left (fun acc (i, l) -> if fst (indent l) = 0 then (Some i, []) :: acc else match acc with (t, ps) :: rest -> (t, i :: ps) :: rest | [] -> [ (None, [ i ]) ]) [] numbered in match List.nth_opt (List.rev slides) n with Some (t, ps) -> (t, List.rev ps) | None -> (None, []) let line_span text k = let rec start pos k = if k = 0 then pos else match String.index_from_opt text pos '\n' with Some e -> start (e + 1) (k - 1) | None -> String.length text in let a = start 0 k in let e = match String.index_from_opt text a '\n' with Some e -> e | None -> String.length text in let rec content i = if i < e && (text.[i] = ' ' || text.[i] = '\t') then content (i + 1) else i in (a, content a, e) let to_text slides = String.concat "\n" (List.concat_map (fun s -> s.title :: List.map (fun (level, p) -> String.make (2 * level) ' ' ^ p) s.points) slides)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>