From 15f478b6c2da54729f0b0462e9cb55223510ded7 Mon Sep 17 00:00:00 2001 From: zy C Date: Tue, 29 Sep 2026 03:28:12 -0700 Subject: [PATCH] fix review findings: attachment edge cases, stale preview, image types - decode_picked: tolerate extensionless picks via file_extension fallback\n- stage_copy: stream the temp copy in 64KiB chunks instead of reading the whole file into memory\n- staged_completion: only echo completion for request tokens that already produced a staged pick, so a newly armed picker request isn't prematurely completed\n- clear_graph_surface: drop media_preview so a previous graph's preview can't persist across a graph switch\n- journal_media_view: restore tif/avif to the inline image list (parity with the retired extension) --- app/application.ml | 1 + app/journal_asset_import.ml | 53 ++++++++++++++++++++++++------------- app/journal_media_view.ml | 2 +- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/app/application.ml b/app/application.ml index 7e0d06f..98604b1 100644 --- a/app/application.ml +++ b/app/application.ml @@ -414,6 +414,7 @@ let clear_graph_surface state = ; next_request_generation = Int64.succ state.next_request_generation ; next_local_sequence = Int64.succ state.next_local_sequence ; modal = No_modal + ; media_preview = None } ;; diff --git a/app/journal_asset_import.ml b/app/journal_asset_import.ml index 4b667e3..985e188 100644 --- a/app/journal_asset_import.ml +++ b/app/journal_asset_import.ml @@ -61,13 +61,20 @@ let request_id_of_token token = | _ -> None ;; +(* Request tokens that already produced a staged pick; only those may echo + back as completion, or a freshly armed request is released before its + pick can land. *) +let spent_requests : (string, unit) Hashtbl.t = Hashtbl.create 8 + (* Staged picks are journal-owned temp copies already, so the picker can release its retained file as soon as the first pick lands — echoing the request token as the completion prop does that. *) let staged_completion request pending = match pending with | [] -> None - | _ :: _ -> Some (request_token request, None) + | _ :: _ -> + let token = request_token request in + if Hashtbl.mem spent_requests token then Some (token, None) else None ;; (* A picked asset held for a later import: the pick fields plus [token], which @@ -130,6 +137,13 @@ type picked_file = ; file_type : string } +let file_extension path = + let ext = Filename.extension path in + if String.length ext > 1 + then String.lowercase_ascii (String.sub ext 1 (String.length ext - 1)) + else "bin" +;; + (* The lui picker reports {"request":token,"files":[{path,name,content-type}]}; journal keeps the extension-era naming (extension of [name] for [file_type]). *) let decode_picked payload = @@ -155,12 +169,7 @@ let decode_picked payload = { request = token_string ; path ; title = name - ; file_type = - String.lowercase_ascii - (String.sub - (Filename.extension name) - 1 - (max 0 (String.length (Filename.extension name) - 1))) + ; file_type = file_extension name } | _ -> Error "Invalid attachment selection") | _ -> Error "Invalid attachment selection") @@ -169,13 +178,6 @@ let decode_picked payload = | _ -> Error "Invalid attachment selection" ;; -let file_extension path = - let ext = Filename.extension path in - if String.length ext > 1 - then String.lowercase_ascii (String.sub ext 1 (String.length ext - 1)) - else "bin" -;; - (* Staged picks get an immediate journal-owned temp copy: the picker's own retained/temp files are released as soon as the completion token echoes. *) let stage_copy path = @@ -186,11 +188,25 @@ let stage_copy path = in try let ic = open_in_bin path in - let length = in_channel_length ic in - let contents = really_input_string ic length in - close_in ic; let oc = open_out_bin dest in - output_string oc contents; + (try + let buffer = Bytes.create 0x10000 in + let rec pump () = + match input ic buffer 0 (Bytes.length buffer) with + | 0 -> () + | read -> + output oc buffer 0 read; + pump () + in + pump () + with + | exn -> + close_in_noerr ic; + close_out_noerr oc; + (try Sys.remove dest with + | _ -> ()); + raise exn); + close_in ic; close_out oc; Ok dest with @@ -276,6 +292,7 @@ let decode_event payload = match stage_copy file.path with | Error message -> Error message | Ok source_file -> + Hashtbl.replace spent_requests file.request (); Result.map (fun staged -> Picked (staged, request_id_of_token file.request)) (staged_of_pick file ~source_file)) diff --git a/app/journal_media_view.ml b/app/journal_media_view.ml index 8069daa..0fc0530 100644 --- a/app/journal_media_view.ml +++ b/app/journal_media_view.ml @@ -29,7 +29,7 @@ let is_http value = let is_image_type file_type = List.mem (String.lowercase_ascii file_type) - [ "jpg"; "jpeg"; "png"; "gif"; "webp"; "heic"; "heif"; "tiff"; "bmp" ] + [ "jpg"; "jpeg"; "png"; "gif"; "webp"; "heic"; "heif"; "tif"; "tiff"; "bmp"; "avif" ] ;; let item_value (item : Journal_media_runtime.item) =