Fix closure compiler issues on clipboard js impl

With minor naming fixes
This commit is contained in:
Andrey Antukh 2025-11-17 23:11:52 +01:00
parent 32e1b55658
commit 3b52051113
6 changed files with 72 additions and 47 deletions

View File

@ -288,7 +288,7 @@
(ptk/reify ::paste-from-clipboard
ptk/WatchEvent
(watch [_ _ _]
(->> (clipboard/from-clipboard)
(->> (clipboard/from-navigator)
(rx/mapcat default-paste-from-blob)
(rx/take 1)))))
@ -476,7 +476,7 @@
(js/console.error "Clipboard error:" cause))
(rx/empty)))]
(->> (clipboard/from-clipboard)
(->> (clipboard/from-navigator)
(rx/mapcat #(.text %))
(rx/map decode-entry)
(rx/take 1)
@ -944,8 +944,7 @@
(defn- paste-html-text
[html]
(js/console.log html)
(dm/assert! (string? html))
(assert (string? html))
(ptk/reify ::paste-html-text
ptk/WatchEvent
(watch [_ state _]
@ -953,9 +952,7 @@
root (dwtxt/create-root-from-html html style)
text (.-textContent root)
content (tc/dom->cljs root)]
(js/console.log "root" root "content" content)
(when (types.text/valid-content? content)
(js/console.log "valid-content")
(let [id (uuid/next)
width (max 8 (min (* 7 (count text)) 700))
height 16

View File

@ -31,7 +31,7 @@
on-click (mf/use-fn
(fn [e]
(js/console.log "event" e)
(let [stream (clipboard/from-clipboard)]
(let [stream (clipboard/from-navigator)]
(rx/sub! stream
(fn [data]
(js/console.log "data" data))))))]

View File

@ -181,7 +181,7 @@
handle-hover-copy-paste
(mf/use-callback
(fn []
(->> (clipboard/from-clipboard)
(->> (clipboard/from-navigator)
(rx/mapcat #(.text %))
(rx/take 1)
(rx/subs!
@ -802,7 +802,6 @@
;; FIXME: optimize because it is rendered always
(mf/defc context-menu*
[]
(let [mdata (mf/deref menu-ref)

View File

@ -6,8 +6,9 @@
(ns app.util.clipboard
(:require
["./clipboard.js" :as clipboard]
["./clipboard.js" :as impl]
[app.common.transit :as t]
[app.util.dom :as dom]
[beicon.v2.core :as rx]))
(def image-types
@ -16,31 +17,50 @@
"image/jpeg"
"image/svg+xml"])
(def clipboard-settings #js {:decodeTransit t/decode-str})
(def ^:private default-options
#js {:decodeTransit t/decode-str})
(defn from-clipboard []
(->> (rx/from (clipboard/fromClipboard clipboard-settings))
(defn- from-data-transfer
"Get clipboard stream from DataTransfer instance"
[data-transfer]
(->> (rx/from (impl/fromDataTransfer data-transfer default-options))
(rx/mapcat #(rx/from %))))
(defn from-data-transfer [data-transfer]
(->> (rx/from (clipboard/fromDataTransfer data-transfer clipboard-settings))
(defn from-navigator
[]
(->> (rx/from (impl/fromNavigator default-options))
(rx/mapcat #(rx/from %))))
(defn from-clipboard-data [clipboard-data]
(from-data-transfer clipboard-data))
(defn from-clipboard-event
"Get clipboard stream from clipboard event"
[event]
(let [cdata (.-clipboardData ^js event)]
(from-data-transfer cdata)))
(defn from-clipboard-event [event]
(from-clipboard-data (.-clipboardData event)))
(defn from-synthetic-clipboard-event
"Get clipboard stream from syntetic clipboard event"
[event]
(let [target
(dom/get-target event)
(defn from-synthetic-clipboard-event [event]
(let [target (.-target ^js event)]
(when (and (not (.-isContentEditable ^js target)) ;; ignore when pasting into
(not= (.-tagName ^js target) "INPUT")) ;; an editable control
(from-clipboard-event (. ^js event getBrowserEvent)))))
content-editable?
(dom/is-content-editable? target)
(defn from-drop-event [event]
(from-data-transfer (.-dataTransfer event)))
is-input?
(= (dom/get-tag-name target) "INPUT")]
;; ignore when pasting into an editable control
(when-not (or content-editable? is-input?)
(-> event
(dom/event->browser-event)
(from-clipboard-event)))))
(defn from-drop-event
"Get clipboard stream from drop event"
[event]
(from-data-transfer (.-dataTransfer ^js event)))
;; FIXME: rename to `write-text`
(defn to-clipboard
[data]
(assert (string? data) "`data` should be string")
@ -52,6 +72,7 @@
(js/ClipboardItem.
(js-obj mimetype promise)))
;; FIXME: this API is very confuse
(defn to-clipboard-promise
[mimetype promise]
(let [clipboard (unchecked-get js/navigator "clipboard")

View File

@ -36,20 +36,25 @@ const exclusiveTypes = [
* @param {Blob} [defaultReturn]
* @returns {Blob}
*/
function parseClipboardItemText(
text,
options,
defaultReturn = new Blob([text], { type: "text/plain" }),
) {
let decodedTransit = false;
try { decodedTransit = options?.decodeTransit?.(text) ?? false }
catch (error) { /* NOOP */ }
function parseText(text, options) {
options = options || {};
const decodeTransit = options["decodeTransit"];
if (decodeTransit) {
try {
decodeTransit(text);
return new Blob([text], { type: "application/transit+json" });
} catch (_error) {
// NOOP
}
}
if (/^<svg[\s>]/i.test(text)) {
return new Blob([text], { type: "image/svg+xml" });
} else if (decodedTransit) {
return new Blob([text], { type: "application/transit+json" });
} else {
return new Blob([text], { type: "text/plain" });
}
return defaultReturn;
}
/**
@ -57,9 +62,8 @@ function parseClipboardItemText(
* @param {ClipboardSettings} [options]
* @returns {Promise<Array<Blob>>}
*/
export async function fromClipboard(options) {
export async function fromNavigator(options) {
const items = await navigator.clipboard.read();
console.log("items", items);
return Promise.all(
Array.from(items).map(async (item) => {
const itemAllowedTypes = Array.from(item.types)
@ -73,9 +77,10 @@ export async function fromClipboard(options) {
const blob = await item.getType("text/plain");
if (blob.size < maxParseableSize) {
const text = await blob.text();
return parseClipboardItemText(text, options);
return parseText(text, options);
} else {
return blob;
}
return blob;
}
const type = itemAllowedTypes.at(0);
@ -105,9 +110,10 @@ export async function fromDataTransfer(dataTransfer, options) {
const type = item.type;
item.getAsString((text) => {
if (type === "text/plain") {
return resolve(parseClipboardItemText(text, options));
return resolve(parseText(text, options));
} else {
return resolve(new Blob([text], { type }));
}
return resolve(new Blob([text], { type }));
});
});
}
@ -119,9 +125,7 @@ export async function fromDataTransfer(dataTransfer, options) {
.reduce((filtered, item) => {
if (
exclusiveTypes.includes(item.type) &&
filtered.find((filteredItem) =>
exclusiveTypes.includes(filteredItem.type),
)
filtered.find((filteredItem) => exclusiveTypes.includes(filteredItem.type))
) {
return filtered;
}

View File

@ -23,7 +23,7 @@
(extend-type BrowserEvent
cljs.core/IDeref
(-deref [it] (.getBrowserEvent it)))
(-deref [it] (.getBrowserEvent ^js it)))
(declare get-window-size)
@ -360,6 +360,10 @@
(when (some? el)
(.-innerText el)))
(defn is-content-editable?
[^js el]
(.-isContentEditable ^js el))
(defn query
([^string selector]
(query globals/document selector))