mirror of https://github.com/logseq/logseq
Merge branch 'whiteboards' into feat/whiteboards-align-shapes
commit
3ce1e1c8f2
|
@ -37,7 +37,7 @@
|
|||
"https-proxy-agent": "5.0.0",
|
||||
"@sentry/electron": "2.5.1",
|
||||
"posthog-js": "1.10.2",
|
||||
"@logseq/rsapi": "0.0.38",
|
||||
"@logseq/rsapi": "0.0.44",
|
||||
"electron-deeplink": "1.0.10",
|
||||
"abort-controller": "3.0.0"
|
||||
},
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
(defn key-gen [] (rsapi/keygen))
|
||||
|
||||
(defn set-env [env private-key public-key]
|
||||
(rsapi/setEnv env private-key public-key))
|
||||
(defn set-env [graph-uuid env private-key public-key]
|
||||
(rsapi/setEnv graph-uuid env private-key public-key))
|
||||
|
||||
(defn get-local-files-meta [graph-uuid base-path file-paths]
|
||||
(rsapi/getLocalFilesMeta graph-uuid base-path (clj->js file-paths)))
|
||||
|
@ -27,17 +27,14 @@
|
|||
(defn delete-remote-files [graph-uuid base-path file-paths txid token]
|
||||
(rsapi/deleteRemoteFiles graph-uuid base-path (clj->js file-paths) txid token))
|
||||
|
||||
(defn update-remote-file [graph-uuid base-path file-path txid token]
|
||||
(rsapi/updateRemoteFile graph-uuid base-path file-path txid token))
|
||||
|
||||
(defn update-remote-files [graph-uuid base-path file-paths txid token]
|
||||
(rsapi/updateRemoteFiles graph-uuid base-path (clj->js file-paths) txid token true))
|
||||
|
||||
(defn encrypt-fnames [fnames]
|
||||
(mapv rsapi/encryptFname fnames))
|
||||
(defn encrypt-fnames [graph-uuid fnames]
|
||||
(rsapi/encryptFnames graph-uuid (clj->js fnames)))
|
||||
|
||||
(defn decrypt-fnames [fnames]
|
||||
(mapv rsapi/decryptFname fnames))
|
||||
(defn decrypt-fnames [graph-uuid fnames]
|
||||
(rsapi/decryptFnames graph-uuid (clj->js fnames)))
|
||||
|
||||
(defn encrypt-with-passphrase [passphrase data]
|
||||
(rsapi/ageEncryptWithPassphrase passphrase data))
|
||||
|
|
|
@ -564,9 +564,6 @@
|
|||
(defmethod handle :delete-remote-files [_ args]
|
||||
(apply rsapi/delete-remote-files (rest args)))
|
||||
|
||||
(defmethod handle :update-remote-file [_ args]
|
||||
(apply rsapi/update-remote-file (rest args)))
|
||||
|
||||
(defmethod handle :update-remote-files [_ args]
|
||||
(apply rsapi/update-remote-files (rest args)))
|
||||
|
||||
|
|
|
@ -227,7 +227,7 @@
|
|||
:open-new-window "New window"
|
||||
:sync-from-local-files "Refresh"
|
||||
:sync-from-local-files-detail "Import changes from local files"
|
||||
:sync-from-local-changes-detected "Refresh detects and processes files modified on your disk and diverged from the actual Logseq page content. Continue?"
|
||||
:sync-from-local-changes-detected "Refresh detects and processes files modified on your disk that have diverged from the current Logseq page content. Continue?"
|
||||
|
||||
:unlink "unlink"
|
||||
:search/publishing "Search"
|
||||
|
|
|
@ -62,12 +62,7 @@
|
|||
|
||||
(defn- <stat [path]
|
||||
(-> (p/chain (.stat Filesystem (clj->js {:path path}))
|
||||
#(js->clj % :keywordize-keys true)
|
||||
#(update % :type (fn [v]
|
||||
(case v
|
||||
"NSFileTypeDirectory" "directory"
|
||||
"NSFileTypeRegular" "file"
|
||||
v))))
|
||||
#(js->clj % :keywordize-keys true))
|
||||
(p/catch (fn [error]
|
||||
(js/console.error "stat Error: " path ": " error)
|
||||
nil))))
|
||||
|
|
|
@ -634,7 +634,7 @@
|
|||
(defprotocol IRSAPI
|
||||
(rsapi-ready? [this graph-uuid] "return true when rsapi ready")
|
||||
(<key-gen [this] "generate public+private keys")
|
||||
(<set-env [this prod? private-key public-key graph-uuid] "set environment")
|
||||
(<set-env [this graph-uuid prod? private-key public-key] "set environment")
|
||||
(<get-local-files-meta [this graph-uuid base-path filepaths] "get local files' metadata")
|
||||
(<get-local-all-files-meta [this graph-uuid base-path] "get all local files' metadata")
|
||||
(<rename-local-file [this graph-uuid base-path from to])
|
||||
|
@ -643,8 +643,8 @@
|
|||
(<delete-local-files [this graph-uuid base-path filepaths])
|
||||
(<update-remote-files [this graph-uuid base-path filepaths local-txid] "local -> remote, return err or txid")
|
||||
(<delete-remote-files [this graph-uuid base-path filepaths local-txid] "return err or txid")
|
||||
(<encrypt-fnames [this fnames])
|
||||
(<decrypt-fnames [this fnames]))
|
||||
(<encrypt-fnames [this graph-uuid fnames])
|
||||
(<decrypt-fnames [this graph-uuid fnames]))
|
||||
|
||||
(defprotocol IRemoteAPI
|
||||
(<user-info [this] "user info")
|
||||
|
@ -673,17 +673,17 @@
|
|||
it happens on macos (case-insensitive fs)
|
||||
|
||||
return canonicalized filepath if exists"
|
||||
[irsapi base-path filepath]
|
||||
[graph-uuid irsapi base-path filepath]
|
||||
(go
|
||||
(let [r (<! (<get-local-files-meta irsapi "" base-path [filepath]))]
|
||||
(let [r (<! (<get-local-files-meta irsapi graph-uuid base-path [filepath]))]
|
||||
(when (some-> r first :path (not= filepath))
|
||||
(-> r first :path)))))
|
||||
|
||||
|
||||
(defn <local-file-not-exist?
|
||||
[irsapi base-path filepath]
|
||||
[graph-uuid irsapi base-path filepath]
|
||||
(go
|
||||
(let [r (<! (<get-local-files-meta irsapi "" base-path [filepath]))]
|
||||
(let [r (<! (<get-local-files-meta irsapi graph-uuid base-path [filepath]))]
|
||||
|
||||
(or
|
||||
;; not found at all
|
||||
|
@ -720,13 +720,13 @@
|
|||
(rsapi-ready? [_ graph-uuid] (and (= graph-uuid graph-uuid') private-key' public-key'))
|
||||
(<key-gen [_] (go (js->clj (<! (p->c (ipc/ipc "key-gen")))
|
||||
:keywordize-keys true)))
|
||||
(<set-env [_ prod? private-key public-key graph-uuid]
|
||||
(<set-env [_ graph-uuid prod? private-key public-key]
|
||||
(when (not-empty private-key)
|
||||
(print (util/format "[%s] setting sync age-encryption passphrase..." graph-uuid)))
|
||||
(set! graph-uuid' graph-uuid)
|
||||
(set! private-key' private-key)
|
||||
(set! public-key' public-key)
|
||||
(p->c (ipc/ipc "set-env" (if prod? "prod" "dev") private-key public-key)))
|
||||
(p->c (ipc/ipc "set-env" graph-uuid (if prod? "prod" "dev") private-key public-key)))
|
||||
(<get-local-all-files-meta [_ graph-uuid base-path]
|
||||
(go
|
||||
(let [r (<! (<retry-rsapi #(p->c (ipc/ipc "get-local-all-files-meta" graph-uuid base-path))))]
|
||||
|
@ -781,9 +781,9 @@
|
|||
(<!
|
||||
(<retry-rsapi
|
||||
#(p->c (ipc/ipc "delete-remote-files" graph-uuid base-path filepaths local-txid token)))))))
|
||||
(<encrypt-fnames [_ fnames] (go (js->clj (<! (p->c (ipc/ipc "encrypt-fnames" fnames))))))
|
||||
(<decrypt-fnames [_ fnames] (go
|
||||
(let [r (<! (p->c (ipc/ipc "decrypt-fnames" fnames)))]
|
||||
(<encrypt-fnames [_ graph-uuid fnames] (go (js->clj (<! (p->c (ipc/ipc "encrypt-fnames" graph-uuid fnames))))))
|
||||
(<decrypt-fnames [_ graph-uuid fnames] (go
|
||||
(let [r (<! (p->c (ipc/ipc "decrypt-fnames" graph-uuid fnames)))]
|
||||
(if (instance? ExceptionInfo r)
|
||||
(ex-info "decrypt-failed" {:fnames fnames} (ex-cause r))
|
||||
(js->clj r))))))
|
||||
|
@ -806,7 +806,7 @@
|
|||
(go (let [r (<! (p->c (.keygen mobile-util/file-sync #js {})))]
|
||||
(-> r
|
||||
(js->clj :keywordize-keys true)))))
|
||||
(<set-env [_ prod? secret-key public-key graph-uuid]
|
||||
(<set-env [_ graph-uuid prod? secret-key public-key]
|
||||
(set! graph-uuid' graph-uuid)
|
||||
(set! private-key secret-key)
|
||||
(set! public-key' public-key)
|
||||
|
@ -899,14 +899,14 @@
|
|||
r
|
||||
(get (js->clj r) "txid")))))
|
||||
|
||||
(<encrypt-fnames [_ fnames]
|
||||
(<encrypt-fnames [_ _graph-uuid fnames]
|
||||
(go
|
||||
(let [r (<! (p->c (.encryptFnames mobile-util/file-sync
|
||||
(clj->js {:filePaths fnames}))))]
|
||||
(if (instance? ExceptionInfo r)
|
||||
(.-cause r)
|
||||
(get (js->clj r) "value")))))
|
||||
(<decrypt-fnames [_ fnames]
|
||||
(<decrypt-fnames [_ _graph-uuid fnames]
|
||||
(go (let [r (<! (p->c (.decryptFnames mobile-util/file-sync
|
||||
(clj->js {:filePaths fnames}))))]
|
||||
(if (instance? ExceptionInfo r)
|
||||
|
@ -1107,7 +1107,7 @@
|
|||
exp-r
|
||||
(let [file-meta-list* (persistent! file-meta-list)
|
||||
encrypted-path-list* (persistent! encrypted-path-list)
|
||||
path-list-or-exp (<! (<decrypt-fnames rsapi encrypted-path-list*))]
|
||||
path-list-or-exp (<! (<decrypt-fnames rsapi graph-uuid encrypted-path-list*))]
|
||||
(if (instance? ExceptionInfo path-list-or-exp)
|
||||
path-list-or-exp
|
||||
(let [encrypted-path->path-map (zipmap encrypted-path-list* path-list-or-exp)]
|
||||
|
@ -1124,12 +1124,12 @@
|
|||
(<get-remote-files-meta [this graph-uuid filepaths]
|
||||
{:pre [(coll? filepaths)]}
|
||||
(go
|
||||
(let [encrypted-paths* (<! (<encrypt-fnames rsapi filepaths))
|
||||
(let [encrypted-paths* (<! (<encrypt-fnames rsapi graph-uuid filepaths))
|
||||
r (<! (.<request this "get_files_meta" {:GraphUUID graph-uuid :Files encrypted-paths*}))]
|
||||
(if (instance? ExceptionInfo r)
|
||||
r
|
||||
(let [encrypted-paths (mapv :FilePath r)
|
||||
paths-or-exp (<! (<decrypt-fnames rsapi encrypted-paths))]
|
||||
paths-or-exp (<! (<decrypt-fnames rsapi graph-uuid encrypted-paths))]
|
||||
(if (instance? ExceptionInfo paths-or-exp)
|
||||
paths-or-exp
|
||||
(let [encrypted-path->path-map (zipmap encrypted-paths paths-or-exp)]
|
||||
|
@ -1152,7 +1152,7 @@
|
|||
|
||||
(<get-remote-file-versions [this graph-uuid filepath]
|
||||
(go
|
||||
(let [encrypted-path (first (<! (<encrypt-fnames rsapi [filepath])))]
|
||||
(let [encrypted-path (first (<! (<encrypt-fnames rsapi graph-uuid [filepath])))]
|
||||
(<! (.<request this "get_file_version_list" {:GraphUUID graph-uuid :File encrypted-path})))))
|
||||
|
||||
(<list-remote-graphs [this]
|
||||
|
@ -1168,7 +1168,7 @@
|
|||
encrypted-path->path-map
|
||||
(zipmap
|
||||
encrypted-paths
|
||||
(<! (<decrypt-fnames rsapi encrypted-paths)))
|
||||
(<! (<decrypt-fnames rsapi graph-uuid encrypted-paths)))
|
||||
txns
|
||||
(mapv
|
||||
(fn [txn] (update txn :path #(get encrypted-path->path-map %)))
|
||||
|
@ -1205,7 +1205,7 @@
|
|||
encrypted-path->path-map
|
||||
(zipmap
|
||||
encrypted-paths
|
||||
(<! (<decrypt-fnames rsapi encrypted-paths)))
|
||||
(<! (<decrypt-fnames rsapi graph-uuid encrypted-paths)))
|
||||
txns
|
||||
(mapv
|
||||
(fn [txn]
|
||||
|
@ -1334,11 +1334,11 @@
|
|||
[@graphs-txid local-txid remote-txid])))))
|
||||
|
||||
(defn- get-local-files-checksum
|
||||
[base-path relative-paths]
|
||||
[graph-uuid base-path relative-paths]
|
||||
(go
|
||||
(into {}
|
||||
(map (juxt #(.-path ^FileMetadata %) #(.-etag ^FileMetadata %)))
|
||||
(<! (<get-local-files-meta rsapi "" base-path relative-paths)))))
|
||||
(<! (<get-local-files-meta rsapi graph-uuid base-path relative-paths)))))
|
||||
|
||||
(declare sync-state--add-current-local->remote-files
|
||||
sync-state--add-current-remote->local-files
|
||||
|
@ -1406,7 +1406,8 @@
|
|||
(remove nil?))]
|
||||
|
||||
(doseq [relative-p (map relative-path filetxns)]
|
||||
(when-some [relative-p* (<! (<case-different-local-file-exist? rsapi base-path relative-p))]
|
||||
(when-some [relative-p*
|
||||
(<! (<case-different-local-file-exist? graph-uuid rsapi base-path relative-p))]
|
||||
(let [recent-remote->local-file-item {:remote->local-type :delete
|
||||
:checksum nil
|
||||
:path relative-p*}]
|
||||
|
@ -1433,7 +1434,7 @@
|
|||
(.-deleted? (first filetxns))
|
||||
(let [filetxn (first filetxns)]
|
||||
(assert (= 1 (count filetxns)))
|
||||
(if (<! (<local-file-not-exist? rsapi base-path (relative-path filetxn)))
|
||||
(if (<! (<local-file-not-exist? graph-uuid rsapi base-path (relative-path filetxn)))
|
||||
;; not exist, ignore
|
||||
true
|
||||
(let [r (<! (<delete-local-files rsapi graph-uuid base-path [(relative-path filetxn)]))]
|
||||
|
@ -1547,7 +1548,7 @@
|
|||
|
||||
|
||||
(defn- <file-change-event=>recent-remote->local-file-item
|
||||
[^FileChangeEvent e]
|
||||
[graph-uuid ^FileChangeEvent e]
|
||||
(go
|
||||
(let [tp (case (.-type e)
|
||||
("add" "change") :update
|
||||
|
@ -1555,7 +1556,7 @@
|
|||
path (relative-path e)]
|
||||
{:remote->local-type tp
|
||||
:checksum (if (= tp :delete) nil
|
||||
(val (first (<! (get-local-files-checksum (.-dir e) [path])))))
|
||||
(val (first (<! (get-local-files-checksum graph-uuid (.-dir e) [path])))))
|
||||
:path path})))
|
||||
|
||||
(defn- distinct-file-change-events-xf
|
||||
|
@ -1590,6 +1591,7 @@
|
|||
(map #(partition-all n %))
|
||||
cat))
|
||||
|
||||
(declare sync-state--valid-to-accept-filewatcher-event?)
|
||||
(defonce local-changes-chan (chan (async/dropping-buffer 1000)))
|
||||
(defn file-watch-handler
|
||||
"file-watcher callback"
|
||||
|
@ -1597,12 +1599,13 @@
|
|||
(when-let [current-graph (state/get-current-repo)]
|
||||
(when (string/ends-with? current-graph dir)
|
||||
(let [sync-state (state/get-file-sync-state current-graph)]
|
||||
(when (and sync-state (not (sync-state--stopped? sync-state)))
|
||||
(when (and sync-state (sync-state--valid-to-accept-filewatcher-event? sync-state))
|
||||
(when (or (:mtime stat) (= type "unlink"))
|
||||
(go
|
||||
(let [path (remove-dir-prefix dir path)
|
||||
files-meta (and (not= "unlink" type)
|
||||
(<! (<get-local-files-meta rsapi "" dir [path])))
|
||||
(<! (<get-local-files-meta
|
||||
rsapi (:current-syncing-graph-uuid sync-state) dir [path])))
|
||||
checksum (and (coll? files-meta) (some-> files-meta first :etag))]
|
||||
(>! local-changes-chan (->FileChangeEvent type dir path stat checksum))))))))))
|
||||
|
||||
|
@ -1853,7 +1856,7 @@
|
|||
(let [{:keys [private-key public-key]} (get @pwd-map graph-uuid)]
|
||||
(assert (and private-key public-key) (pr-str :private-key private-key :public-key public-key
|
||||
:pwd-map @pwd-map))
|
||||
(<set-env rsapi prod? private-key public-key graph-uuid)))
|
||||
(<set-env rsapi graph-uuid prod? private-key public-key)))
|
||||
|
||||
(defn- <ensure-set-env&keys
|
||||
[graph-uuid *stopped?]
|
||||
|
@ -2037,6 +2040,13 @@
|
|||
{:pre [(s/valid? ::sync-state sync-state)]}
|
||||
(= ::stop (:state sync-state)))
|
||||
|
||||
(defn sync-state--valid-to-accept-filewatcher-event?
|
||||
[sync-state]
|
||||
{:pre [(s/valid? ::sync-state sync-state)]}
|
||||
(contains? #{::idle ::local->remote ::remote->local ::local->remote-full-sync ::remote->local-full-sync}
|
||||
(:state sync-state)))
|
||||
|
||||
|
||||
;;; ### remote->local syncer & local->remote syncer
|
||||
|
||||
(defprotocol IRemote->LocalSync
|
||||
|
@ -2159,18 +2169,18 @@
|
|||
(case (.-type e)
|
||||
"unlink"
|
||||
;; keep this e when it's not found
|
||||
(<! (<local-file-not-exist? rsapi basepath r-path))
|
||||
(<! (<local-file-not-exist? graph-uuid rsapi basepath r-path))
|
||||
|
||||
("add" "change")
|
||||
;; 1. local file exists
|
||||
;; 2. compare with remote file, and changed
|
||||
(and (not (<! (<local-file-not-exist? rsapi basepath r-path)))
|
||||
(and (not (<! (<local-file-not-exist? graph-uuid rsapi basepath r-path)))
|
||||
(<! (<file-changed? graph-uuid r-path basepath)))))))
|
||||
|
||||
(defn- <filter-checksum-not-consistent
|
||||
"filter out FileChangeEvents checksum changed,
|
||||
compare checksum in FileChangeEvent and checksum calculated now"
|
||||
[es]
|
||||
[graph-uuid es]
|
||||
{:pre [(or (nil? es) (coll? es))
|
||||
(every? #(instance? FileChangeEvent %) es)]}
|
||||
(go
|
||||
|
@ -2178,7 +2188,8 @@
|
|||
(if (= "unlink" (.-type ^FileChangeEvent (first es)))
|
||||
es
|
||||
(let [base-path (.-dir (first es))
|
||||
files-meta (<! (<get-local-files-meta rsapi "" base-path (mapv relative-path es)))
|
||||
files-meta (<! (<get-local-files-meta
|
||||
rsapi graph-uuid base-path (mapv relative-path es)))
|
||||
current-checksum-map (when (coll? files-meta) (into {} (mapv (juxt :path :etag) files-meta)))
|
||||
origin-checksum-map (into {} (mapv (juxt relative-path #(.-checksum ^FileChangeEvent %)) es))
|
||||
origin-map (into {} (mapv (juxt relative-path identity) es))]
|
||||
|
@ -2243,7 +2254,8 @@
|
|||
(not (ignored? e)) ;not ignored
|
||||
;; download files will also trigger file-change-events, ignore them
|
||||
(let [r (not (contains? (:recent-remote->local-files @*sync-state)
|
||||
(<! (<file-change-event=>recent-remote->local-file-item e))))]
|
||||
(<! (<file-change-event=>recent-remote->local-file-item
|
||||
graph-uuid e))))]
|
||||
(when (and (true? r)
|
||||
(seq (:recent-remote->local-files @*sync-state)))
|
||||
(println :debug (:recent-remote->local-files @*sync-state) e))
|
||||
|
@ -2291,7 +2303,7 @@
|
|||
(map #(relative-path %))
|
||||
(remove ignored?))]
|
||||
(go
|
||||
(let [es* (<! (<filter-checksum-not-consistent es))
|
||||
(let [es* (<! (<filter-checksum-not-consistent graph-uuid es))
|
||||
_ (when (not= (count es*) (count es))
|
||||
(println :debug :filter-checksum-changed
|
||||
(mapv relative-path (set/difference (set es) (set es*)))))
|
||||
|
@ -2385,7 +2397,7 @@
|
|||
(loop [[f & fs] delete-local-files]
|
||||
(when f
|
||||
(let [relative-p (relative-path f)]
|
||||
(when-not (<! (<local-file-not-exist? rsapi base-path relative-p))
|
||||
(when-not (<! (<local-file-not-exist? graph-uuid rsapi base-path relative-p))
|
||||
(let [fake-recent-remote->local-file-item {:remote->local-type :delete
|
||||
:checksum nil
|
||||
:path relative-p}]
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
(:require [datascript.core :as d]
|
||||
[frontend.db.model :as model]
|
||||
[frontend.db.utils :as db-utils]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.modules.outliner.core :as outliner]
|
||||
[frontend.modules.outliner.file :as outliner-file]
|
||||
|
@ -30,17 +29,6 @@
|
|||
;; (and (.-classList el) (.. el -classList (contains "whiteboard"))) true
|
||||
;; :else (recur (.-parentElement el)))))
|
||||
|
||||
(defn get-tldr-app
|
||||
[]
|
||||
js/window.tln)
|
||||
|
||||
(defn tldraw-idle?
|
||||
"return true when tldraw is active and idle. nil when tldraw is
|
||||
not active."
|
||||
[]
|
||||
(when-let [^js app (get-tldr-app)]
|
||||
(.. app -selectedTool (isIn "idle"))))
|
||||
|
||||
(defn- block->shape [block]
|
||||
(:block/properties block))
|
||||
|
||||
|
|
|
@ -40,12 +40,14 @@
|
|||
[repo page-db-id]
|
||||
(let [page-block (db/pull repo '[*] page-db-id)
|
||||
page-db-id (:db/id page-block)
|
||||
whiteboard? (:block/whiteboard? page-block)
|
||||
blocks-count (model/get-page-blocks-count repo page-db-id)]
|
||||
(if (and (> blocks-count 500)
|
||||
(not (state/input-idle? repo :diff 3000))) ; long page
|
||||
(if (or (and (> blocks-count 500)
|
||||
(not (state/input-idle? repo :diff 3000))) ;; long page
|
||||
;; when this whiteboard page is just being updated
|
||||
(and whiteboard? (state/whiteboard-page-idle? repo page-block 3000)))
|
||||
(async/put! (state/get-file-write-chan) [repo page-db-id])
|
||||
(let [whiteboard? (:block/whiteboard? page-block)
|
||||
pull-keys (if whiteboard? whiteboard-blocks-pull-keys-with-persisted-ids '[*])
|
||||
(let [pull-keys (if whiteboard? whiteboard-blocks-pull-keys-with-persisted-ids '[*])
|
||||
blocks (model/get-page-blocks-no-cache repo (:block/name page-block) {:pull-keys pull-keys})
|
||||
blocks (if whiteboard? (map cleanup-whiteboard-block blocks) blocks)]
|
||||
(when-not (and (= 1 (count blocks))
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
(ns frontend.modules.shortcut.before
|
||||
(:require [frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.handler.whiteboard :as whiteboard]))
|
||||
(:require [frontend.mobile.util :as mobile-util]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]))
|
||||
|
||||
;; before function
|
||||
(defn prevent-default-behavior
|
||||
|
@ -35,6 +34,7 @@
|
|||
(fn [e]
|
||||
(when (and (or (contains? #{:srs :page-histories} (state/get-modal-id))
|
||||
(not (state/block-component-editing?)))
|
||||
(not (and (whiteboard/tldraw-idle?)
|
||||
(not (state/editing?)))))
|
||||
;; should not enable when in whiteboard mode, but not editing a logseq block
|
||||
(not (and (state/active-tldraw-app)
|
||||
(not (state/tldraw-editing-logseq-block?)))))
|
||||
(f e))))
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
(ns frontend.state
|
||||
(:require [cljs-bean.core :as bean]
|
||||
[cljs.core.async :as async]
|
||||
[clojure.string :as string]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.string :as string]
|
||||
[dommy.core :as dom]
|
||||
[medley.core :as medley]
|
||||
[electron.ipc :as ipc]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.storage :as storage]
|
||||
[frontend.util :as util]
|
||||
[frontend.util.cursor :as cursor]
|
||||
[goog.dom :as gdom]
|
||||
[goog.object :as gobj]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[frontend.mobile.util :as mobile-util]))
|
||||
[medley.core :as medley]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]))
|
||||
|
||||
;; Stores main application state
|
||||
(defonce ^:large-vars/data-var state
|
||||
|
@ -1396,6 +1396,15 @@ Similar to re-frame subscriptions"
|
|||
(set-state! [:plugin/installed-hooks hook-or-all] (disj coll pid))))
|
||||
true))
|
||||
|
||||
(defn active-tldraw-app
|
||||
[]
|
||||
^js js/window.tln)
|
||||
|
||||
(defn tldraw-editing-logseq-block?
|
||||
[]
|
||||
(when-let [app (active-tldraw-app)]
|
||||
(and (= 1 (.. app -selectedShapesArray -length))
|
||||
(= (.. app -editingShape) (.. app -selectedShapesArray (at 0))))))
|
||||
|
||||
(defn set-graph-syncing?
|
||||
[value]
|
||||
|
@ -1445,11 +1454,24 @@ Similar to re-frame subscriptions"
|
|||
:or {diff 1000}}]
|
||||
(when repo
|
||||
(or
|
||||
(when-let [last-time (get-in @state [:editor/last-input-time repo])]
|
||||
(let [now (util/time-ms)]
|
||||
(>= (- now last-time) diff)))
|
||||
;; not in editing mode
|
||||
(not (get-edit-input-id)))))
|
||||
(when-let [last-time (get-in @state [:editor/last-input-time repo])]
|
||||
(let [now (util/time-ms)]
|
||||
(>= (- now last-time) diff)))
|
||||
;; not in editing mode
|
||||
;; Is this a good idea to put whiteboard check here?
|
||||
(not (get-edit-input-id)))))
|
||||
|
||||
(defn whiteboard-page-idle?
|
||||
[repo whiteboard-page & {:keys [diff]
|
||||
:or {diff 1000}}]
|
||||
(when repo
|
||||
(or
|
||||
(when-let [last-time (:block/updated-at whiteboard-page)]
|
||||
(let [now (util/time-ms)]
|
||||
(>= (- now last-time) diff)))
|
||||
;; not in idle mode
|
||||
(not (when-let [tldraw-app (active-tldraw-app)]
|
||||
(.. tldraw-app -selectedTool (isIn "idle")))))))
|
||||
|
||||
(defn set-nfs-refreshing!
|
||||
[value]
|
||||
|
|
|
@ -30,6 +30,7 @@ export interface LogseqPortalShapeProps extends TLBoxShapeProps, CustomStyleProp
|
|||
blockType?: 'P' | 'B'
|
||||
collapsed?: boolean
|
||||
compact?: boolean
|
||||
borderRadius?: number
|
||||
collapsedHeight?: number
|
||||
scaleLevel?: SizeLevel
|
||||
}
|
||||
|
@ -186,6 +187,7 @@ export class LogseqPortalShape extends TLBoxShape<LogseqPortalShapeProps> {
|
|||
stroke: 'var(--ls-primary-text-color)',
|
||||
fill: 'var(--ls-secondary-background-color)',
|
||||
noFill: false,
|
||||
borderRadius: 8,
|
||||
strokeWidth: 2,
|
||||
strokeType: 'line',
|
||||
opacity: 1,
|
||||
|
|
|
@ -3,13 +3,56 @@ import { SvgPathUtils, TLDrawShape, TLDrawShapeProps } from '@tldraw/core'
|
|||
import { SVGContainer, TLComponentProps } from '@tldraw/react'
|
||||
import { computed, makeObservable } from 'mobx'
|
||||
import { observer } from 'mobx-react-lite'
|
||||
import getStroke from 'perfect-freehand'
|
||||
import getStroke, { getStrokeOutlinePoints, getStrokePoints, StrokeOptions } from 'perfect-freehand'
|
||||
import { CustomStyleProps, withClampedStyles } from './style-props'
|
||||
|
||||
export interface PencilShapeProps extends TLDrawShapeProps, CustomStyleProps {
|
||||
type: 'pencil'
|
||||
}
|
||||
|
||||
const simulatePressureSettings: StrokeOptions = {
|
||||
easing: t => Math.sin((t * Math.PI) / 2),
|
||||
simulatePressure: true,
|
||||
}
|
||||
|
||||
const realPressureSettings: StrokeOptions = {
|
||||
easing: t => t * t,
|
||||
simulatePressure: false,
|
||||
}
|
||||
|
||||
function getFreehandOptions(shape: PencilShapeProps) {
|
||||
const options: StrokeOptions = {
|
||||
size: 1 + shape.strokeWidth * 1.5,
|
||||
thinning: 0.65,
|
||||
streamline: 0.65,
|
||||
smoothing: 0.65,
|
||||
...(shape.points[1][2] === 0.5 ? simulatePressureSettings : realPressureSettings),
|
||||
last: shape.isComplete,
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
function getFillPath(shape: PencilShapeProps) {
|
||||
if (shape.points.length < 2) return ''
|
||||
|
||||
return SvgPathUtils.getSvgPathFromStroke(
|
||||
getStrokePoints(shape.points, getFreehandOptions(shape)).map(pt => pt.point)
|
||||
)
|
||||
}
|
||||
|
||||
function getDrawStrokePoints(shape: PencilShapeProps, options: StrokeOptions) {
|
||||
return getStrokePoints(shape.points, options)
|
||||
}
|
||||
|
||||
function getDrawStrokePathTDSnapshot(shape: PencilShapeProps) {
|
||||
if (shape.points.length < 2) return ''
|
||||
const options = getFreehandOptions(shape)
|
||||
const strokePoints = getDrawStrokePoints(shape, options)
|
||||
const path = SvgPathUtils.getSvgPathFromStroke(getStrokeOutlinePoints(strokePoints, options))
|
||||
return path
|
||||
}
|
||||
|
||||
export class PencilShape extends TLDrawShape<PencilShapeProps> {
|
||||
constructor(props = {} as Partial<PencilShapeProps>) {
|
||||
super(props)
|
||||
|
@ -34,16 +77,7 @@ export class PencilShape extends TLDrawShape<PencilShapeProps> {
|
|||
}
|
||||
|
||||
@computed get pointsPath() {
|
||||
const {
|
||||
props: { points, isComplete, strokeWidth },
|
||||
} = this
|
||||
if (points.length < 2) {
|
||||
return `M -4, 0
|
||||
a 4,4 0 1,0 8,0
|
||||
a 4,4 0 1,0 -8,0`
|
||||
}
|
||||
const stroke = getStroke(points, { size: 4 + strokeWidth * 2, last: isComplete })
|
||||
return SvgPathUtils.getCurvedPathForPolygon(stroke)
|
||||
return getDrawStrokePathTDSnapshot(this.props)
|
||||
}
|
||||
|
||||
ReactComponent = observer(({ events, isErasing }: TLComponentProps) => {
|
||||
|
|
|
@ -540,7 +540,6 @@ button.tl-select-input-trigger {
|
|||
}
|
||||
|
||||
&[data-recently-changed=true] {
|
||||
transition-delay: 0.5s;
|
||||
i.tie {
|
||||
transition-delay: 0.5s;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Vec from '@tldraw/vec'
|
||||
|
||||
export class SvgPathUtils {
|
||||
static getCurvedPathForPolygon(points: number[][]) {
|
||||
|
@ -45,23 +44,34 @@ export class SvgPathUtils {
|
|||
* @param stroke ;
|
||||
*/
|
||||
static getSvgPathFromStroke(points: number[][], closed = true): string {
|
||||
if (!points.length) {
|
||||
return ''
|
||||
const len = points.length
|
||||
|
||||
if (len < 4) {
|
||||
return ``
|
||||
}
|
||||
|
||||
const max = points.length - 1
|
||||
let a = points[0]
|
||||
let b = points[1]
|
||||
const c = points[2]
|
||||
|
||||
return points
|
||||
.reduce(
|
||||
(acc, point, i, arr) => {
|
||||
if (i === max) {
|
||||
if (closed) acc.push('Z')
|
||||
} else acc.push(point, Vec.med(point, arr[i + 1]))
|
||||
return acc
|
||||
},
|
||||
['M', points[0], 'Q']
|
||||
)
|
||||
.join(' ')
|
||||
.replaceAll(this.TRIM_NUMBERS, '$1')
|
||||
let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed(2)},${b[1].toFixed(
|
||||
2
|
||||
)} ${average(b[0], c[0]).toFixed(2)},${average(b[1], c[1]).toFixed(2)} T`
|
||||
|
||||
for (let i = 2, max = len - 1; i < max; i++) {
|
||||
a = points[i]
|
||||
b = points[i + 1]
|
||||
result += `${average(a[0], b[0]).toFixed(2)},${average(a[1], b[1]).toFixed(2)} `
|
||||
}
|
||||
|
||||
if (closed) {
|
||||
result += 'Z'
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
function average(a: number, b: number): number {
|
||||
return (a + b) / 2
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ export const SelectionForeground = observer(function SelectionForeground<S exten
|
|||
|
||||
const canResize = shapes.length === 1 ? shapes[0].canResize : [true, true]
|
||||
|
||||
const editing = !!app.editingShape
|
||||
// @ts-expect-error ???
|
||||
const borderRadius = app.editingShape?.props['borderRadius'] ?? 0
|
||||
|
||||
return (
|
||||
<SVGContainer>
|
||||
|
@ -30,8 +31,8 @@ export const SelectionForeground = observer(function SelectionForeground<S exten
|
|||
className="tl-bounds-fg"
|
||||
width={Math.max(width, 1)}
|
||||
height={Math.max(height, 1)}
|
||||
rx={editing ? 8 : 0}
|
||||
ry={editing ? 8 : 0}
|
||||
rx={borderRadius}
|
||||
ry={borderRadius}
|
||||
pointerEvents="none"
|
||||
/>
|
||||
<EdgeHandle
|
||||
|
|
|
@ -32,19 +32,29 @@ export function usePreventNavigation(rCanvas: React.RefObject<HTMLDivElement>):
|
|||
|
||||
if (!elm) return () => void null
|
||||
|
||||
elm.addEventListener('touchstart', preventGestureNavigation)
|
||||
elm.addEventListener('touchstart', preventGestureNavigation, {
|
||||
passive: true,
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
elm.addEventListener('gestureend', preventGestureNavigation)
|
||||
elm.addEventListener('gestureend', preventGestureNavigation, {
|
||||
passive: true,
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
elm.addEventListener('gesturechange', preventGestureNavigation)
|
||||
elm.addEventListener('gesturechange', preventGestureNavigation, {
|
||||
passive: true,
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
elm.addEventListener('gesturestart', preventGestureNavigation)
|
||||
elm.addEventListener('gesturestart', preventGestureNavigation, {
|
||||
passive: true,
|
||||
})
|
||||
|
||||
// @ts-ignore
|
||||
elm.addEventListener('touchstart', preventNavigation)
|
||||
elm.addEventListener('touchstart', preventNavigation, {
|
||||
passive: true,
|
||||
})
|
||||
|
||||
return () => {
|
||||
if (elm) {
|
||||
|
|
Loading…
Reference in New Issue