package quill
Interactive notebook for OCaml data science
Install
Dune Dependency
Authors
Maintainers
Sources
raven-1.0.0.alpha0.tbz
sha256=a9a8a9787f8250337187bb7b21cb317c41bfd2ecf08bcfe0ab407c7b6660764d
sha512=fe13cf257c487e41efe2967be147d80fa94bac8996d3aab2b8fd16f0bbbd108c15e0e58c025ec9bf294d4a0d220ca2ba00c3b1b42fa2143f758c5f0ee4c15782
doc/src/quill.editor/events.ml.html
Source file events.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
open Brr open Brr_ext open Dom_utils let log fmt = Printf.ksprintf (fun s -> Console.(log [ Jstr.v ("[events] " ^ s) ])) fmt (* Selection Handling *) let handle_selectionchange mounted_app dom_el (_ev : Ev.Type.void Ev.t) = match Window.get_selection G.window with | None -> log "selectionchange: No selection object" | Some sel -> if Selection.is_collapsed sel then match El.find_first_by_selector (Jstr.v "#editor") ~root:dom_el with | None -> log "selectionchange: Could not find editor element" | Some editor_div -> ( let offset = get_caret_offset_within editor_div in (* Parse current DOM to get the model *) let model = Vdom_blit.get mounted_app in match Model_dom.find_in_blocks model.Model.document offset with | Some ("inline", id) -> log "selectionchange: Focusing inline %d" id; Vdom_blit.process mounted_app (Update.Focus_inline_by_id id) | Some ("block", id) -> log "selectionchange: Focusing block %d" id; Vdom_blit.process mounted_app (Update.Focus_block_by_id id) | Some _ -> log "selectionchange: Found non-inline/block element at offset %d" offset | None -> log "selectionchange: No element found at offset %d" offset) else log "selectionchange: Selection is not collapsed" (* Input Handling *) let handle_input mounted_app (dom_el : El.t) (_ev : Ev.Input.t Ev.t) = let new_document = Model_dom.parse_dom dom_el in let msg = Update.Set_document new_document in let offset = get_caret_offset_within dom_el in Vdom_blit.process mounted_app msg; Vdom_blit.after_redraw mounted_app (fun () -> set_caret_offset_within dom_el offset) (* Remove Handling *) let handle_remove ~is_backspace mounted_app dom_el = let editor_div = match El.find_first_by_selector (Jstr.v "#editor") ~root:dom_el with | Some ed -> ed | None -> failwith "Could not find editor element" in let model = Vdom_blit.get mounted_app in let md = Quill_markdown.md_of_document model.Model.document in let start_offset, end_offset = get_selection_offsets_within editor_div in let new_md, new_offset = if start_offset = end_offset then (* Collapsed selection *) if is_backspace then ( log "handle_remove: Backspace pressed"; if start_offset > 0 then let new_md = String.sub md 0 (start_offset - 1) ^ String.sub md start_offset (String.length md - start_offset) in (new_md, start_offset - 1) else (md, start_offset)) else if (* Delete *) log "handle_remove: Delete pressed"; start_offset < String.length md then let new_md = String.sub md 0 start_offset ^ String.sub md (start_offset + 1) (String.length md - start_offset - 1) in (new_md, start_offset) else (md, start_offset) else ( (* Non-collapsed selection *) log "handle_remove: Non-collapsed selection"; let new_md = String.sub md 0 start_offset ^ String.sub md end_offset (String.length md - end_offset) in (new_md, start_offset)) in if new_md <> md then ( let new_document = Quill_markdown.document_of_md new_md in Vdom_blit.process mounted_app (Update.Set_document new_document); Vdom_blit.after_redraw mounted_app (fun () -> set_caret_offset_within editor_div new_offset)); true (* Prevent default behavior *) (* Keydown Handling *) let handle_execute_code ~code_execution_handler = let range_opt = get_current_range () in let start_node_opt = Option.map (fun range -> El.of_jv (Range.start_container range)) range_opt in let codeblock_opt = Option.bind start_node_opt find_codeblock_ancestor in match codeblock_opt with | None -> log "keydown (exec): Not inside a codeblock element"; false | Some code_el -> ( match get_element_codeblock_id code_el with | None -> log "keydown (exec): Could not parse codeblock ID"; false | Some block_id -> let code = inner_text code_el in log "keydown (exec): Performing effect for block %d" block_id; code_execution_handler block_id code; true) let handle_enter mounted_app dom_el = match El.find_first_by_selector (Jstr.v "#editor") ~root:dom_el with | None -> log "Could not find editor element"; false | Some editor_div -> let start_offset, end_offset = get_selection_offsets_within editor_div in let md = Jstr.to_string (El.text_content editor_div) in let inserted_text = "\n\n" in let new_md = if start_offset = end_offset then (* Collapsed selection: Insert two newlines at caret position *) String.sub md 0 start_offset ^ inserted_text ^ String.sub md start_offset (String.length md - start_offset) else (* Non-collapsed selection: Remove selected text and insert two newlines *) String.sub md 0 start_offset ^ inserted_text ^ String.sub md end_offset (String.length md - end_offset) in let new_offset = start_offset + String.length inserted_text in let doc = Quill_markdown.document_of_md (String.trim new_md) in let doc = Quill_markdown.normalize_blanklines doc in Vdom_blit.process mounted_app (Update.Set_document doc); Vdom_blit.after_redraw mounted_app (fun () -> set_caret_offset_within editor_div new_offset); true let handle_enter_in_codeblock mounted_app dom_el = match El.find_first_by_selector (Jstr.v "#editor") ~root:dom_el with | None -> log "Could not find editor element"; false | Some editor_div -> let start_offset, end_offset = get_selection_offsets_within editor_div in let md = Jstr.to_string (El.text_content editor_div) in let inserted_text = "\n" in let new_md = if start_offset = end_offset then (* Collapsed selection: Insert one newline at caret position *) String.sub md 0 start_offset ^ inserted_text ^ String.sub md start_offset (String.length md - start_offset) else (* Non-collapsed selection: Remove selected text and insert one newline *) String.sub md 0 start_offset ^ inserted_text ^ String.sub md end_offset (String.length md - end_offset) in let new_offset = start_offset + String.length inserted_text in let doc = Quill_markdown.document_of_md (String.trim new_md) in Vdom_blit.process mounted_app (Update.Set_document doc); Vdom_blit.after_redraw mounted_app (fun () -> set_caret_offset_within editor_div new_offset); true let handle_enter_key mounted_app dom_el = let range_opt = get_current_range () in let start_node_opt = Option.map (fun range -> El.of_jv (Range.start_container range)) range_opt in let parent_opt = Option.bind start_node_opt (find_block_parent ~stop_at:dom_el) in match parent_opt with | None -> log "keydown (enter): Not inside a known block element."; false | Some block_el -> ( let tag = get_element_tag block_el in match tag with | "PRE" -> handle_enter_in_codeblock mounted_app dom_el | _ -> handle_enter mounted_app dom_el) let handle_keydown ~code_execution_handler mounted_app (dom_el : El.t) (ev : Ev.Keyboard.t Ev.t) = let evt = Ev.as_type ev in let key = Jstr.to_string (Ev.Keyboard.key evt) in let meta_key = Ev.Keyboard.meta_key evt in let ctrl_key = Ev.Keyboard.ctrl_key evt in let should_prevent = match key with | "Enter" when meta_key || ctrl_key -> handle_execute_code ~code_execution_handler | "Enter" -> handle_enter_key mounted_app dom_el | "Backspace" -> handle_remove ~is_backspace:true mounted_app dom_el | "Delete" -> handle_remove ~is_backspace:false mounted_app dom_el | _ -> false in if should_prevent then ( log "keydown: Preventing default for key '%s'" key; Ev.prevent_default ev) else log "keydown: Allowing default for key '%s'" key let setup_event_listeners ~code_execution_handler (dom_el : El.t) mounted_app = let _input_listener = Ev.listen Ev.input (fun ev -> handle_input mounted_app dom_el ev) (El.as_target dom_el) in let _selection_listener = Ev.listen Ev.selectionchange (fun ev -> handle_selectionchange mounted_app dom_el ev) (Document.as_target G.document) in let _keydown_listener = Ev.listen Ev.keydown (fun ev -> handle_keydown ~code_execution_handler mounted_app dom_el ev) (El.as_target dom_el) in ()
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>