package quill
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Interactive REPL and markdown notebooks
Install
dune-project
Dependency
Authors
Maintainers
Sources
raven-1.0.0.alpha3.tbz
sha256=96d35ce03dfbebd2313657273e24c2e2d20f9e6c7825b8518b69bd1d6ed5870f
sha512=90c5053731d4108f37c19430e45456063e872b04b8a1bbad064c356e1b18e69222de8bfcf4ec14757e71f18164ec6e4630ba770dbcb1291665de5418827d1465
doc/src/quill.book/build.ml.html
Source file build.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(*--------------------------------------------------------------------------- Copyright (c) 2026 The Raven authors. All rights reserved. SPDX-License-Identifier: ISC ---------------------------------------------------------------------------*) (* ───── File utilities ───── *) let read_file path = let ic = open_in path in Fun.protect ~finally:(fun () -> close_in ic) (fun () -> really_input_string ic (in_channel_length ic)) let write_file path content = let oc = open_out path in Fun.protect ~finally:(fun () -> close_out oc) (fun () -> output_string oc content) let rec mkdir_p dir = if Sys.file_exists dir then () else ( mkdir_p (Filename.dirname dir); try Unix.mkdir dir 0o755 with Unix.Unix_error (Unix.EEXIST, _, _) -> ()) let copy_file ~src ~dst = let ic = open_in_bin src in Fun.protect ~finally:(fun () -> close_in ic) (fun () -> let oc = open_out_bin dst in Fun.protect ~finally:(fun () -> close_out oc) (fun () -> let buf = Bytes.create 8192 in let rec loop () = let n = input ic buf 0 8192 in if n > 0 then ( output oc buf 0 n; loop ()) in loop ())) let rec copy_dir_contents ~src_dir ~dst_dir = if Sys.file_exists src_dir && Sys.is_directory src_dir then ( mkdir_p dst_dir; let entries = Sys.readdir src_dir in Array.iter (fun name -> let src = Filename.concat src_dir name in let dst = Filename.concat dst_dir name in if not (Sys.is_directory src) then copy_file ~src ~dst else copy_dir_contents ~src_dir:src ~dst_dir:dst) entries) (* ───── Path computation ───── *) let notebook_dir (project : Quill_project.t) (nb : Quill_project.notebook) = let dir = Filename.dirname nb.path in if dir = "." then project.root else Filename.concat project.root dir let prelude_path (project : Quill_project.t) (nb : Quill_project.notebook) = let dir = notebook_dir project nb in let path = Filename.concat dir "prelude.ml" in if Sys.file_exists path then Some path else None let relative_root_path (nb : Quill_project.notebook) = let dir = Filename.dirname nb.path in if dir = "." then "./" else let parts = String.split_on_char '/' dir in let depth = List.length (List.filter (fun s -> s <> "" && s <> ".") parts) in if depth = 0 then "./" else String.concat "" (List.init depth (fun _ -> "../")) (* ───── Build ───── *) let build_notebook ~create_kernel ~skip_eval ~output_dir ~live_reload_script (project : Quill_project.t) (nb : Quill_project.notebook) = let nb_path = Filename.concat project.root nb.path in let nb_dir = notebook_dir project nb in let md = read_file nb_path in let doc = Quill_markdown.of_string md in let doc = if skip_eval then doc else let create_kernel ~on_event = let k = create_kernel ~on_event in (match prelude_path project nb with | Some p -> let code = read_file p in k.Quill.Kernel.execute ~cell_id:"__prelude__" ~code | None -> ()); k in let prev_cwd = Sys.getcwd () in Sys.chdir nb_dir; Fun.protect ~finally:(fun () -> Sys.chdir prev_cwd) (fun () -> let doc = Quill.Doc.clear_all_outputs doc in Quill.Eval.run ~create_kernel doc) in let content = Render.chapter_html doc in let root_path = relative_root_path nb in let toc = Render.toc_html project ~current:nb ~root_path in let prev = match Quill_project.prev_notebook project nb with | Some p -> Some (root_path ^ Render.notebook_output_path p, p.title) | None -> None in let next = match Quill_project.next_notebook project nb with | Some n -> Some (root_path ^ Render.notebook_output_path n, n.title) | None -> None in let edit_url = match project.config.edit_url with | Some base -> Some (base ^ nb.path) | None -> None in let html = Render.page_html ~book_title:project.title ~chapter_title:nb.title ~toc_html:toc ~prev ~next ~root_path ~content ~edit_url ~live_reload_script in let output_path = Filename.concat output_dir (Render.notebook_output_path nb) in mkdir_p (Filename.dirname output_path); write_file output_path html; let asset_dirs = [ "figures"; "images"; "assets" ] in List.iter (fun name -> let src = Filename.concat nb_dir name in let dst = Filename.concat output_dir (Filename.concat (Filename.dirname nb.path) name) in copy_dir_contents ~src_dir:src ~dst_dir:dst) asset_dirs; Printf.printf " %s\n%!" nb.title; content (* ───── Search index ───── *) let json_escape_string s = let buf = Buffer.create (String.length s + 16) in Buffer.add_char buf '"'; String.iter (function | '"' -> Buffer.add_string buf {|\"|} | '\\' -> Buffer.add_string buf {|\\|} | '\n' -> Buffer.add_string buf {|\n|} | '\r' -> Buffer.add_string buf {|\r|} | '\t' -> Buffer.add_string buf {|\t|} | c -> Buffer.add_char buf c) s; Buffer.add_char buf '"'; Buffer.contents buf let search_entry ~title ~url ~body = Printf.sprintf {|{"title":%s,"url":%s,"body":%s}|} (json_escape_string title) (json_escape_string url) (json_escape_string body) let build_search_index ~output_dir ~toc (notebooks : (Quill_project.notebook * string) list) = let entries = List.map (fun (nb, content_html) -> let number_prefix = match Quill_project.number_string (Quill_project.number toc nb) with | "" -> "" | s -> s ^ ". " in let title = number_prefix ^ nb.title in let url = Render.notebook_output_path nb in let body = Render.strip_html_tags content_html in search_entry ~title ~url ~body) notebooks in let json = "[" ^ String.concat "," entries ^ "]" in write_file (Filename.concat output_dir "searchindex.json") json let build_print_page ~output_dir ~toc (project : Quill_project.t) (notebooks : (Quill_project.notebook * string) list) = let chapter_pairs = List.map (fun (nb, content_html) -> let number_prefix = match Quill_project.number_string (Quill_project.number toc nb) with | "" -> "" | s -> s ^ ". " in (number_prefix ^ nb.title, content_html)) notebooks in let html = Render.print_page_html ~book_title:project.title ~chapters:chapter_pairs in write_file (Filename.concat output_dir "print.html") html let build_index ~output_dir (project : Quill_project.t) ~live_reload_script = match Quill_project.notebooks project with | [] -> () | first :: _ -> let url = Render.notebook_output_path first in let html = Printf.sprintf {|<!DOCTYPE html> <html><head> <meta charset="utf-8"> <meta http-equiv="refresh" content="0; url=%s"> <title>%s</title> </head> <body><p>Redirecting to <a href="%s">%s</a>...</p>%s</body> </html>|} (Render.escape_html url) (Render.escape_html project.title) (Render.escape_html url) (Render.escape_html first.title) live_reload_script in write_file (Filename.concat output_dir "index.html") html let build ~create_kernel ?(skip_eval = false) ?output ?(live_reload_script = "") (project : Quill_project.t) = let output_dir = match output with | Some dir -> dir | None -> Filename.concat project.root "build" in mkdir_p output_dir; write_file (Filename.concat output_dir "style.css") Theme.style_css; let nbs = Quill_project.notebooks project in Printf.printf "Building %s (%d notebooks)\n%!" project.title (List.length nbs); let notebook_contents = List.map (fun nb -> let content = build_notebook ~create_kernel ~skip_eval ~output_dir ~live_reload_script project nb in (nb, content)) nbs in build_search_index ~output_dir ~toc:project.toc notebook_contents; build_print_page ~output_dir ~toc:project.toc project notebook_contents; build_index ~output_dir project ~live_reload_script; Printf.printf "Done → %s\n%!" output_dir
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>