diff --git a/package.json b/package.json index 657845d331..0d1cf1976e 100644 --- a/package.json +++ b/package.json @@ -114,7 +114,7 @@ "@capacitor/android": "7.2.0", "@capacitor/app": "7.0.1", "@capacitor/camera": "7.0.1", - "@capacitor/clipboard": "7.0.1", + "@capacitor/clipboard": "^7.0.2", "@capacitor/core": "7.2.0", "@capacitor/device": "^7.0.2", "@capacitor/dialog": "^7.0.2", diff --git a/src/main/frontend/components/block.cljs b/src/main/frontend/components/block.cljs index 2463ee4115..95c119ac1d 100644 --- a/src/main/frontend/components/block.cljs +++ b/src/main/frontend/components/block.cljs @@ -355,7 +355,9 @@ (let [handle-copy! (fn [_e] (-> (util/copy-image-to-clipboard image-src) - (p/then #(notification/show! "Copied!" :success)))) + (p/then #(notification/show! "Copied!" :success)) + (p/catch (fn [error] + (js/console.error error))))) handle-delete! (fn [_e] (when-let [block-id (get-blockid)] @@ -381,47 +383,38 @@ :href src :title title :full-text full-text})))))))] - [:.asset-action-bar {:aria-hidden "true"} - (shui/button-group - (shui/button - {:variant :outline - :size :icon - :class "h-7 w-7" - :on-pointer-down util/stop - :on-click (fn [e] - (shui/popup-show! (.closest (.-target e) ".asset-action-bar") - (fn [] - [:div - {:on-click #(shui/popup-hide!)} - (shui/dropdown-menu-item - {:on-click #(some-> (db/entity [:block/uuid (get-blockid)]) - (editor-handler/edit-block! :max {:container-id :unknown-container}))} - [:span.flex.items-center.gap-1 - (ui/icon "edit") (t :asset/edit-block)]) - (shui/dropdown-menu-item - {:on-click handle-copy!} - [:span.flex.items-center.gap-1 - (ui/icon "copy") (t :asset/copy)]) - (when (util/electron?) - (shui/dropdown-menu-item - {:on-click (fn [e] - (util/stop e) - (if local? - (ipc/ipc "openFileInFolder" image-src) - (js/window.apis.openExternal image-src)))} - [:span.flex.items-center.gap-1 - (ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))])) - - (when-not config/publishing? - [:<> - (shui/dropdown-menu-separator) - (shui/dropdown-menu-item - {:on-click handle-delete!} - [:span.flex.items-center.gap-1.text-red-700 - (ui/icon "trash") (t :asset/delete)])])]) - {:align :start - :dropdown-menu? true}))} - (shui/tabler-icon "dots-vertical")))])])]))) + (when asset-block + [:.asset-action-bar {:aria-hidden "true"} + (shui/dropdown-menu + {:on-pointer-down util/stop} + (shui/dropdown-menu-trigger + {:as-child true} + (shui/button + {:variant :outline + :size :icon + :class "h-6 w-6"} + (shui/tabler-icon "dots-vertical"))) + (shui/dropdown-menu-content + (shui/dropdown-menu-item + {:on-click handle-copy!} + [:span.flex.items-center.gap-1 + (ui/icon "copy") (t :asset/copy)]) + (when (util/electron?) + (shui/dropdown-menu-item + {:on-click (fn [e] + (util/stop e) + (if local? + (ipc/ipc "openFileInFolder" image-src) + (js/window.apis.openExternal image-src)))} + [:span.flex.items-center.gap-1 + (ui/icon "folder-pin") (t (if local? :asset/show-in-folder :asset/open-in-browser))])) + (when-not config/publishing? + [:<> + (shui/dropdown-menu-separator) + (shui/dropdown-menu-item + {:on-click handle-delete!} + [:span.flex.items-center.gap-1.text-red-700 + (ui/icon "trash") (t :asset/delete)])])))]))])]))) (rum/defcs ^:large-vars/cleanup-todo resizable-image < (rum/local nil ::size) diff --git a/src/main/frontend/util.cljc b/src/main/frontend/util.cljc index d7c4326815..3d8d44853e 100644 --- a/src/main/frontend/util.cljc +++ b/src/main/frontend/util.cljc @@ -1364,13 +1364,44 @@ (set! (.-src image) data-url)))) #?(:cljs - (defn write-blob-to-clipboard - [blob] - (->> blob - (js-obj (.-type blob)) - (js/ClipboardItem.) - (array) - (js/navigator.clipboard.write)))) + (def native-clipboard (gobj/get CapacitorClipboard "Clipboard"))) + +#?(:cljs + (do + ;; Helper: Blob -> data URL (returns a JS Promise) + (defn blob->data-url [blob] + (js/Promise. + (fn [resolve reject] + (let [reader (js/FileReader.)] + (set! (.-onload reader) + (fn [_e] + ;; result is a "data:;base64,..." string + (resolve (.-result reader)))) + (set! (.-onerror reader) + (fn [_e] + (reject (.-error reader)))) + (.readAsDataURL reader blob))))) + + (defn write-blob-to-clipboard [blob] + (if native-clipboard + ;; 1) Native (Capacitor) path – expects a data URL + (-> (blob->data-url blob) + (.then (fn [data-url] + ;; Your Capacitor plugin signature: { image: } + (.write native-clipboard #js {:image data-url}))) + (.then (fn [] + (js/console.log "Copied via native clipboard"))) + (.catch (fn [err] + (js/console.error "Native clipboard failed" err)))) + + ;; 2) Web Clipboard API path (desktop browsers etc.) + (let [item (js/ClipboardItem. + (js-obj (.-type blob) blob))] + (-> (.write (.-clipboard js/navigator) (array item)) + (.then (fn [] + (js/console.log "Copied via web clipboard"))) + (.catch (fn [err] + (js/console.error "Web clipboard failed" err))))))))) #?(:cljs (defn copy-image-to-clipboard diff --git a/yarn.lock b/yarn.lock index ceb33e297f..31a78a4852 100644 --- a/yarn.lock +++ b/yarn.lock @@ -299,10 +299,10 @@ tslib "^2.8.1" xml2js "^0.6.2" -"@capacitor/clipboard@7.0.1": - version "7.0.1" - resolved "https://registry.yarnpkg.com/@capacitor/clipboard/-/clipboard-7.0.1.tgz#159fefa56b7d23632e9d08c9efd9cbd75a868b21" - integrity sha512-n4XEHma7apLOYvyeaR9S5u3uGzDYG7WeQxmtZlwP01HneIzMnusVgw4Im6I+pMBcoUN9TfVdf6eqKph97B1bAw== +"@capacitor/clipboard@^7.0.2": + version "7.0.2" + resolved "https://registry.yarnpkg.com/@capacitor/clipboard/-/clipboard-7.0.2.tgz#fe9ea01032581f68feda79c19304c5f82e1995f1" + integrity sha512-Cm37cNPvSzuao3HlcrQT18py5WUfKdaNWBHFuQcubBzBuKhqtAC9P6xZ2d0ucPFdxvsEOgO7mjs2BWIpaKqpgg== "@capacitor/core@7.2.0": version "7.2.0"