mirror of
https://github.com/logseq/logseq.git
synced 2026-06-01 19:01:22 +00:00
Merge remote-tracking branch 'upstream/master' into whiteboards
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
["chokidar" :as watcher]
|
||||
[electron.utils :as utils]
|
||||
["electron" :refer [app]]
|
||||
[frontend.util.fs :as util-fs]
|
||||
[electron.window :as window]))
|
||||
|
||||
;; TODO: explore different solutions for different platforms
|
||||
@@ -30,10 +29,15 @@
|
||||
|
||||
(defn- publish-file-event!
|
||||
[dir path event]
|
||||
(send-file-watcher! dir event {:dir (utils/fix-win-path! dir)
|
||||
:path (utils/fix-win-path! path)
|
||||
:content (utils/read-file path)
|
||||
:stat (fs/statSync path)}))
|
||||
(let [content (when (and (not= event "unlink")
|
||||
(utils/should-read-content? path))
|
||||
(utils/read-file path))
|
||||
stat (when (not= event "unlink")
|
||||
(fs/statSync path))]
|
||||
(send-file-watcher! dir event {:dir (utils/fix-win-path! dir)
|
||||
:path (utils/fix-win-path! path)
|
||||
:content content
|
||||
:stat stat})))
|
||||
|
||||
(defn watch-dir!
|
||||
"Watch a directory if no such file watcher exists"
|
||||
@@ -43,7 +47,7 @@
|
||||
(let [watcher (.watch watcher dir
|
||||
(clj->js
|
||||
{:ignored (fn [path]
|
||||
(util-fs/ignored-path? dir path))
|
||||
(utils/ignored-path? dir path))
|
||||
:ignoreInitial false
|
||||
:ignorePermissionErrors true
|
||||
:interval polling-interval
|
||||
@@ -63,9 +67,7 @@
|
||||
(publish-file-event! dir path "change")))
|
||||
(.on watcher "unlink"
|
||||
(fn [path]
|
||||
(send-file-watcher! dir "unlink"
|
||||
{:dir (utils/fix-win-path! dir)
|
||||
:path (utils/fix-win-path! path)})))
|
||||
(publish-file-event! dir path "unlink")))
|
||||
(.on watcher "error"
|
||||
(fn [path]
|
||||
(println "Watch error happened: "
|
||||
|
||||
@@ -62,27 +62,27 @@
|
||||
(when-let [agent (cfgs/get-item :settings/agent)]
|
||||
(set-fetch-agent agent)))
|
||||
|
||||
;; keep same as ignored-path? in src/main/frontend/util/fs.cljs
|
||||
;; TODO: merge them
|
||||
(defn ignored-path?
|
||||
"Ignore given path from file-watcher notification"
|
||||
[dir path]
|
||||
(when (string? path)
|
||||
(or
|
||||
(some #(string/starts-with? path (str dir "/" %))
|
||||
["." ".recycle" "assets" "node_modules" "logseq/bak"])
|
||||
["." ".recycle" "node_modules" "logseq/bak" "version-files"])
|
||||
(some #(string/includes? path (str "/" % "/"))
|
||||
["." ".recycle" "assets" "node_modules" "logseq/bak"])
|
||||
(string/ends-with? path ".DS_Store")
|
||||
["." ".recycle" "node_modules" "logseq/bak" "version-files"])
|
||||
(some #(string/ends-with? path %)
|
||||
[".DS_Store" "logseq/graphs-txid.edn" "logseq/broken-config.edn"])
|
||||
;; hidden directory or file
|
||||
(let [relpath (path/relative dir path)]
|
||||
(or (re-find #"/\.[^.]+" relpath)
|
||||
(re-find #"^\.[^.]+" relpath)))
|
||||
(let [path (string/lower-case path)]
|
||||
(and
|
||||
(not (string/blank? (path/extname path)))
|
||||
(not
|
||||
(some #(string/ends-with? path %)
|
||||
[".md" ".markdown" ".org" ".js" ".edn" ".css"])))))))
|
||||
(re-find #"^\.[^.]+" relpath))))))
|
||||
|
||||
(defn should-read-content?
|
||||
"Skip reading content of file while using file-watcher"
|
||||
[path]
|
||||
(let [ext (string/lower-case (path/extname path))]
|
||||
(contains? #{".md" ".markdown" ".org" ".js" ".edn" ".css"} ext)))
|
||||
|
||||
(defn fix-win-path!
|
||||
[path]
|
||||
@@ -131,4 +131,4 @@
|
||||
|
||||
(defn normalize-lc
|
||||
[s]
|
||||
(normalize (string/lower-case s)))
|
||||
(normalize (string/lower-case s)))
|
||||
|
||||
@@ -273,21 +273,16 @@
|
||||
["Query table function" [[:editor/input "{{function }}" {:backward-pos 2}]] "Create a query table function"]
|
||||
["Calculator" [[:editor/input "```calc\n\n```" {:backward-pos 4}]
|
||||
[:codemirror/focus]] "Insert a calculator"]
|
||||
|
||||
["draw" (draw-handler/initialize-excalidarw-file) "Draw a graph with Excalidraw"]
|
||||
|
||||
(when (util/zh-CN-supported?)
|
||||
["Embed Bilibili video" [[:editor/input "{{bilibili }}" {:last-pattern (state/get-editor-command-trigger)
|
||||
:backward-pos 2}]]])
|
||||
|
||||
["Embed HTML " (->inline "html")]
|
||||
|
||||
["Embed Youtube video" [[:editor/input "{{youtube }}" {:last-pattern (state/get-editor-command-trigger)
|
||||
:backward-pos 2}]]]
|
||||
["Embed Video URL" [[:editor/input "{{video }}" {:last-pattern (state/get-editor-command-trigger)
|
||||
:backward-pos 2}]]]
|
||||
|
||||
["Embed Youtube timestamp" [[:youtube/insert-timestamp]]]
|
||||
|
||||
["Embed Vimeo video" [[:editor/input "{{vimeo }}" {:last-pattern (state/get-editor-command-trigger)
|
||||
:backward-pos 2}]]]
|
||||
|
||||
["Embed Twitter tweet" [[:editor/input "{{tweet }}" {:last-pattern (state/get-editor-command-trigger)
|
||||
:backward-pos 2}]]]]
|
||||
|
||||
|
||||
@@ -264,14 +264,14 @@
|
||||
(let [src (::src state)
|
||||
granted? (state/sub [:nfs/user-granted? (state/get-current-repo)])
|
||||
href (config/get-local-asset-absolute-path href)]
|
||||
(when (or granted? (util/electron?) (mobile-util/is-native-platform?))
|
||||
(when (or granted? (util/electron?) (mobile-util/native-platform?))
|
||||
(p/then (editor-handler/make-asset-url href) #(reset! src %)))
|
||||
|
||||
(when @src
|
||||
(let [ext (keyword (util/get-file-ext @src))
|
||||
share-fn (fn [event]
|
||||
(util/stop event)
|
||||
(when (mobile-util/is-native-platform?)
|
||||
(when (mobile-util/native-platform?)
|
||||
(p/let [url (str (config/get-repo-dir (state/get-current-repo)) href)]
|
||||
(.share Share #js {:url url
|
||||
:title "Open file with your favorite app"}))))]
|
||||
@@ -884,7 +884,7 @@
|
||||
(state/set-state! :pdf/current current)))}
|
||||
label-text]
|
||||
|
||||
(mobile-util/is-native-platform?)
|
||||
(mobile-util/native-platform?)
|
||||
(asset-link config label-text s metadata full_text))
|
||||
|
||||
(contains? (config/doc-formats) ext)
|
||||
@@ -1113,42 +1113,80 @@
|
||||
(defn- macro-vimeo-cp
|
||||
[_config arguments]
|
||||
(when-let [url (first arguments)]
|
||||
(let [Vimeo-regex #"^((?:https?:)?//)?((?:www).)?((?:player.vimeo.com|vimeo.com)?)((?:/video/)?)([\w-]+)(\S+)?$"]
|
||||
(when-let [vimeo-id (nth (util/safe-re-find Vimeo-regex url) 5)]
|
||||
(when-not (string/blank? vimeo-id)
|
||||
(let [width (min (- (util/get-width) 96)
|
||||
560)
|
||||
height (int (* width (/ 315 560)))]
|
||||
[:iframe
|
||||
{:allow-full-screen "allowfullscreen"
|
||||
:allow
|
||||
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
|
||||
:frame-border "0"
|
||||
:src (str "https://player.vimeo.com/video/" vimeo-id)
|
||||
:height height
|
||||
:width width}]))))))
|
||||
(when-let [vimeo-id (nth (util/safe-re-find text/vimeo-regex url) 5)]
|
||||
(when-not (string/blank? vimeo-id)
|
||||
(let [width (min (- (util/get-width) 96)
|
||||
560)
|
||||
height (int (* width (/ 315 560)))]
|
||||
[:iframe
|
||||
{:allow-full-screen "allowfullscreen"
|
||||
:allow
|
||||
"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
|
||||
:frame-border "0"
|
||||
:src (str "https://player.vimeo.com/video/" vimeo-id)
|
||||
:height height
|
||||
:width width}])))))
|
||||
|
||||
(defn- macro-bilibili-cp
|
||||
[_config arguments]
|
||||
(when-let [url (first arguments)]
|
||||
(let [id-regex #"https?://www\.bilibili\.com/video/([^? ]+)"]
|
||||
(when-let [id (cond
|
||||
(<= (count url) 15) url
|
||||
:else
|
||||
(last (util/safe-re-find id-regex url)))]
|
||||
(when-not (string/blank? id)
|
||||
(let [width (min (- (util/get-width) 96)
|
||||
560)
|
||||
height (int (* width (/ 315 560)))]
|
||||
[:iframe
|
||||
{:allowfullscreen true
|
||||
:framespacing "0"
|
||||
:frameborder "no"
|
||||
:border "0"
|
||||
:scrolling "no"
|
||||
:src (str "https://player.bilibili.com/player.html?bvid=" id "&high_quality=1")
|
||||
:width width
|
||||
:height (max 500 height)}]))))))
|
||||
(when-let [id (cond
|
||||
(<= (count url) 15) url
|
||||
:else
|
||||
(nth (util/safe-re-find text/bilibili-regex url) 5))]
|
||||
(when-not (string/blank? id)
|
||||
(let [width (min (- (util/get-width) 96)
|
||||
560)
|
||||
height (int (* width (/ 315 560)))]
|
||||
[:iframe
|
||||
{:allowfullscreen true
|
||||
:framespacing "0"
|
||||
:frameborder "no"
|
||||
:border "0"
|
||||
:scrolling "no"
|
||||
:src (str "https://player.bilibili.com/player.html?bvid=" id "&high_quality=1")
|
||||
:width width
|
||||
:height (max 500 height)}])))))
|
||||
|
||||
(defn- macro-video-cp
|
||||
[_config arguments]
|
||||
(when-let [url (first arguments)]
|
||||
(let [width (min (- (util/get-width) 96)
|
||||
560)
|
||||
height (int (* width (/ 315 560)))
|
||||
results (text/get-matched-video url)
|
||||
src (match results
|
||||
[_ _ _ (:or "youtube.com" "youtu.be" "y2u.be") _ id _]
|
||||
(if (= (count id) 11) ["youtube-player" id] url)
|
||||
|
||||
[_ _ _ "youtube-nocookie.com" _ id _]
|
||||
(str "https://www.youtube-nocookie.com/embed/" id)
|
||||
|
||||
[_ _ _ "loom.com" _ id _]
|
||||
(str "https://www.loom.com/embed/" id)
|
||||
|
||||
[_ _ _ (_ :guard #(string/ends-with? % "vimeo.com")) _ id _]
|
||||
(str "https://player.vimeo.com/video/" id)
|
||||
|
||||
[_ _ _ "bilibili.com" _ id _]
|
||||
(str "https://player.bilibili.com/player.html?bvid=" id "&high_quality=1")
|
||||
|
||||
:else
|
||||
url)]
|
||||
(if (and (coll? src)
|
||||
(= (first src) "youtube-player"))
|
||||
(youtube/youtube-video (last src))
|
||||
(when src
|
||||
[:iframe
|
||||
{:allowfullscreen true
|
||||
:allow "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
|
||||
:framespacing "0"
|
||||
:frameborder "no"
|
||||
:border "0"
|
||||
:scrolling "no"
|
||||
:src src
|
||||
:width width
|
||||
:height height}])))))
|
||||
|
||||
(defn- macro-else-cp
|
||||
[name config arguments]
|
||||
@@ -1261,13 +1299,12 @@
|
||||
|
||||
(= name "youtube")
|
||||
(when-let [url (first arguments)]
|
||||
(let [YouTube-regex #"^((?:https?:)?//)?((?:www|m).)?((?:youtube.com|youtu.be))(/(?:[\w-]+\?v=|embed/|v/)?)([\w-]+)(\S+)?$"]
|
||||
(when-let [youtube-id (cond
|
||||
(== 11 (count url)) url
|
||||
:else
|
||||
(nth (util/safe-re-find YouTube-regex url) 5))]
|
||||
(when-not (string/blank? youtube-id)
|
||||
(youtube/youtube-video youtube-id)))))
|
||||
(when-let [youtube-id (cond
|
||||
(== 11 (count url)) url
|
||||
:else
|
||||
(nth (util/safe-re-find text/youtube-regex url) 5))]
|
||||
(when-not (string/blank? youtube-id)
|
||||
(youtube/youtube-video youtube-id))))
|
||||
|
||||
(= name "youtube-timestamp")
|
||||
(when-let [timestamp (first arguments)]
|
||||
@@ -1290,6 +1327,9 @@
|
||||
(= name "bilibili")
|
||||
(macro-bilibili-cp config arguments)
|
||||
|
||||
(= name "video")
|
||||
(macro-video-cp config arguments)
|
||||
|
||||
(contains? #{"tweet" "twitter"} name)
|
||||
(when-let [url (first arguments)]
|
||||
(let [id-regex #"/status/(\d+)"]
|
||||
@@ -1535,7 +1575,7 @@
|
||||
"hide-inner-bullet"))}
|
||||
[:span.bullet {:blockid (str uuid)}]]]]
|
||||
(cond
|
||||
(and (or (mobile-util/is-native-platform?)
|
||||
(and (or (mobile-util/native-platform?)
|
||||
(:ui/show-empty-bullets? (state/get-config)))
|
||||
(not doc-mode?))
|
||||
bullet
|
||||
@@ -1869,35 +1909,40 @@
|
||||
(.stopPropagation e)
|
||||
(let [target (gobj/get e "target")
|
||||
button (gobj/get e "buttons")
|
||||
shift? (gobj/get e "shiftKey")]
|
||||
(when (contains? #{1 0} button)
|
||||
(when-not (target-forbidden-edit? target)
|
||||
(if (and shift? (state/get-selection-start-block))
|
||||
(editor-handler/highlight-selection-area! block-id)
|
||||
(do
|
||||
(editor-handler/clear-selection!)
|
||||
(editor-handler/unhighlight-blocks!)
|
||||
(let [f #(let [block (or (db/pull [:block/uuid (:block/uuid block)]) block)
|
||||
cursor-range (util/caret-range (gdom/getElement block-id))
|
||||
{:block/keys [content format]} block
|
||||
content (->> content
|
||||
(property/remove-built-in-properties format)
|
||||
(drawer/remove-logbook))]
|
||||
;; save current editing block
|
||||
(let [{:keys [value] :as state} (editor-handler/get-state)]
|
||||
(editor-handler/save-block! state value))
|
||||
(state/set-editing!
|
||||
edit-input-id
|
||||
content
|
||||
block
|
||||
cursor-range
|
||||
false))]
|
||||
;; wait a while for the value of the caret range
|
||||
(if (util/ios?)
|
||||
(f)
|
||||
(js/setTimeout f 5))
|
||||
shift? (gobj/get e "shiftKey")
|
||||
meta? (util/meta-key? e)]
|
||||
(if (and meta? (not (state/get-edit-input-id)))
|
||||
(do
|
||||
(util/stop e)
|
||||
(state/conj-selection-block! (gdom/getElement block-id) :down))
|
||||
(when (contains? #{1 0} button)
|
||||
(when-not (target-forbidden-edit? target)
|
||||
(if (and shift? (state/get-selection-start-block))
|
||||
(editor-handler/highlight-selection-area! block-id)
|
||||
(do
|
||||
(editor-handler/clear-selection!)
|
||||
(editor-handler/unhighlight-blocks!)
|
||||
(let [f #(let [block (or (db/pull [:block/uuid (:block/uuid block)]) block)
|
||||
cursor-range (util/caret-range (gdom/getElement block-id))
|
||||
{:block/keys [content format]} block
|
||||
content (->> content
|
||||
(property/remove-built-in-properties format)
|
||||
(drawer/remove-logbook))]
|
||||
;; save current editing block
|
||||
(let [{:keys [value] :as state} (editor-handler/get-state)]
|
||||
(editor-handler/save-block! state value))
|
||||
(state/set-editing!
|
||||
edit-input-id
|
||||
content
|
||||
block
|
||||
cursor-range
|
||||
false))]
|
||||
;; wait a while for the value of the caret range
|
||||
(if (util/ios?)
|
||||
(f)
|
||||
(js/setTimeout f 5))
|
||||
|
||||
(when block-id (state/set-selection-start-block! block-id)))))))))
|
||||
(when block-id (state/set-selection-start-block! block-id))))))))))
|
||||
|
||||
(rum/defc dnd-separator-wrapper < rum/reactive
|
||||
[block block-id slide? top? block-content?]
|
||||
@@ -1970,7 +2015,8 @@
|
||||
(when (and
|
||||
(state/in-selection-mode?)
|
||||
(not (string/includes? content "```"))
|
||||
(not (gobj/get e "shiftKey")))
|
||||
(not (gobj/get e "shiftKey"))
|
||||
(not (util/meta-key? e)))
|
||||
;; clear highlighted text
|
||||
(util/clear-selection!)))}
|
||||
(not slide?)
|
||||
@@ -2227,20 +2273,21 @@
|
||||
|
||||
(defn- block-mouse-over
|
||||
[uuid e *control-show? block-id doc-mode?]
|
||||
(util/stop e)
|
||||
(when (or
|
||||
(model/block-collapsed? uuid)
|
||||
(editor-handler/collapsable? uuid {:semantic? true}))
|
||||
(reset! *control-show? true))
|
||||
(when-let [parent (gdom/getElement block-id)]
|
||||
(let [node (.querySelector parent ".bullet-container")]
|
||||
(when doc-mode?
|
||||
(dom/remove-class! node "hide-inner-bullet"))))
|
||||
(when (and
|
||||
(state/in-selection-mode?)
|
||||
(non-dragging? e))
|
||||
(when-not @*dragging?
|
||||
(util/stop e)
|
||||
(editor-handler/highlight-selection-area! block-id)))
|
||||
(when (or
|
||||
(model/block-collapsed? uuid)
|
||||
(editor-handler/collapsable? uuid {:semantic? true}))
|
||||
(reset! *control-show? true))
|
||||
(when-let [parent (gdom/getElement block-id)]
|
||||
(let [node (.querySelector parent ".bullet-container")]
|
||||
(when doc-mode?
|
||||
(dom/remove-class! node "hide-inner-bullet"))))
|
||||
(when (and
|
||||
(state/in-selection-mode?)
|
||||
(non-dragging? e))
|
||||
(util/stop e)
|
||||
(editor-handler/highlight-selection-area! block-id))))
|
||||
|
||||
(defn- block-mouse-leave
|
||||
[e *control-show? block-id doc-mode?]
|
||||
|
||||
@@ -185,14 +185,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
html.is-mobile,
|
||||
html.is-native-iphone,
|
||||
html.is-native-android {
|
||||
.references .block-control {
|
||||
margin-left: -20px;
|
||||
}
|
||||
}
|
||||
|
||||
.block-ref {
|
||||
border-bottom: 0.5px solid;
|
||||
border-bottom-color: var(--ls-block-ref-link-text-color);
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
[frontend.components.datetime :as datetime-comp]
|
||||
[frontend.components.search :as search]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.mobile.camera :as mobile-camera]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.config :as config]
|
||||
[frontend.db :as db]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.extensions.zotero :as zotero]
|
||||
@@ -27,9 +24,7 @@
|
||||
[goog.dom :as gdom]
|
||||
[promesa.core :as p]
|
||||
[rum.core :as rum]
|
||||
[frontend.handler.history :as history]
|
||||
[frontend.mobile.footer :as footer]
|
||||
[frontend.handler.config :as config-handler]))
|
||||
[frontend.mobile.footer :as footer]))
|
||||
|
||||
(rum/defc commands < rum/reactive
|
||||
[id format]
|
||||
@@ -232,88 +227,6 @@
|
||||
template)
|
||||
:class "black"}))))))
|
||||
|
||||
(rum/defc mobile-bar-indent-outdent [indent? icon]
|
||||
[:div
|
||||
[:button.bottom-action
|
||||
{:on-mouse-down (fn [e]
|
||||
(util/stop e)
|
||||
(editor-handler/indent-outdent indent?))}
|
||||
(ui/icon icon {:style {:fontSize ui/icon-size}})]])
|
||||
|
||||
(def ^:private mobile-bar-icons-keywords
|
||||
[:checkbox :brackets :parentheses :command :tag :a-b :list :camera
|
||||
:brand-youtube :link :rotate :rotate-clockwise :code :bold :italic :strikethrough :paint])
|
||||
|
||||
(def ^:private mobile-bar-commands-stats
|
||||
(atom (into {}
|
||||
(mapv (fn [name] [name {:counts 0}])
|
||||
mobile-bar-icons-keywords))))
|
||||
|
||||
(defn set-command-stats [icon]
|
||||
(let [key (keyword icon)
|
||||
counts (get-in @mobile-bar-commands-stats [key :counts])]
|
||||
(swap! mobile-bar-commands-stats
|
||||
assoc-in [key :counts] (inc counts))
|
||||
(config-handler/set-config!
|
||||
:mobile/toolbar-stats @mobile-bar-commands-stats)))
|
||||
|
||||
(rum/defc mobile-bar-command
|
||||
[command-handler icon & [count? event?]]
|
||||
[:div
|
||||
[:button.bottom-action
|
||||
{:on-mouse-down (fn [e]
|
||||
(util/stop e)
|
||||
(when count?
|
||||
(set-command-stats icon))
|
||||
(if event?
|
||||
(command-handler e)
|
||||
(command-handler)))}
|
||||
(ui/icon icon {:style {:fontSize ui/icon-size}})]])
|
||||
|
||||
(defn mobile-bar-commands
|
||||
[_parent-state parent-id]
|
||||
(let [viewport-fn (fn [] (when-let [input (gdom/getElement parent-id)]
|
||||
(util/make-el-cursor-position-into-center-viewport input)
|
||||
(.focus input)))]
|
||||
(zipmap mobile-bar-icons-keywords
|
||||
[(mobile-bar-command editor-handler/cycle-todo! "checkbox" true)
|
||||
(mobile-bar-command #(editor-handler/toggle-page-reference-embed parent-id) "brackets" true)
|
||||
(mobile-bar-command #(editor-handler/toggle-block-reference-embed parent-id) "parentheses" true)
|
||||
(mobile-bar-command #(do (viewport-fn) (commands/simple-insert! parent-id "/" {})) "command" true)
|
||||
(mobile-bar-command #(do (viewport-fn) (commands/simple-insert! parent-id "#" {})) "tag" true)
|
||||
(mobile-bar-command editor-handler/cycle-priority! "a-b" true)
|
||||
(mobile-bar-command editor-handler/toggle-list! "list" true)
|
||||
(mobile-bar-command #(mobile-camera/embed-photo parent-id) "camera" true)
|
||||
(mobile-bar-command commands/insert-youtube-timestamp "brand-youtube" true)
|
||||
(mobile-bar-command editor-handler/html-link-format! "link" true)
|
||||
(mobile-bar-command history/undo! "rotate" true true)
|
||||
(mobile-bar-command history/redo! "rotate-clockwise" true true)
|
||||
(mobile-bar-command #(do (viewport-fn) (commands/simple-insert! parent-id "<" {})) "code" true)
|
||||
(mobile-bar-command editor-handler/bold-format! "bold" true)
|
||||
(mobile-bar-command editor-handler/italics-format! "italic" true)
|
||||
(mobile-bar-command editor-handler/strike-through-format! "strikethrough" true)
|
||||
(mobile-bar-command editor-handler/highlight-format! "paint" true)])))
|
||||
|
||||
(rum/defc mobile-bar < rum/reactive
|
||||
[parent-state parent-id]
|
||||
(when-let [config-toolbar-stats (:mobile/toolbar-stats (state/get-config))]
|
||||
(reset! mobile-bar-commands-stats config-toolbar-stats))
|
||||
(let [commands (mobile-bar-commands parent-state parent-id)
|
||||
sorted-commands (sort-by (comp :counts second) > @mobile-bar-commands-stats)]
|
||||
[:div#mobile-editor-toolbar.bg-base-2
|
||||
[:div.toolbar-commands
|
||||
(mobile-bar-indent-outdent false "arrow-bar-left")
|
||||
(mobile-bar-indent-outdent true "arrow-bar-right")
|
||||
(mobile-bar-command (editor-handler/move-up-down true) "arrow-bar-to-up")
|
||||
(mobile-bar-command (editor-handler/move-up-down false) "arrow-bar-to-down")
|
||||
(mobile-bar-command #(if (state/sub :document/mode?)
|
||||
(editor-handler/insert-new-block! nil)
|
||||
(commands/simple-insert! parent-id "\n" {})) "arrow-back")
|
||||
(for [command sorted-commands]
|
||||
((first command) commands))]
|
||||
[:div.toolbar-hide-keyboard
|
||||
(mobile-bar-command #(state/clear-edit!) "keyboard-show")]]))
|
||||
|
||||
(rum/defcs input < rum/reactive
|
||||
(rum/local {} ::input-value)
|
||||
(mixins/event-mixin
|
||||
@@ -607,7 +520,7 @@
|
||||
(mixins/event-mixin setup-key-listener!)
|
||||
(shortcut/mixin :shortcut.handler/block-editing-only)
|
||||
lifecycle/lifecycle
|
||||
[state {:keys [format block]} id config]
|
||||
[state {:keys [format block]} id _config]
|
||||
(let [content (state/sub-edit-content)
|
||||
heading-class (get-editor-style-class content format)]
|
||||
[:div.editor-inner {:class (if block "block-editor" "non-block-editor")}
|
||||
@@ -616,11 +529,6 @@
|
||||
[:div#audio-record-toolbar
|
||||
(footer/audio-record-cp)])
|
||||
|
||||
(when (and (or (mobile-util/is-native-platform?)
|
||||
config/mobile?)
|
||||
(not (:review-cards? config)))
|
||||
(mobile-bar state id))
|
||||
|
||||
(ui/ls-textarea
|
||||
{:id id
|
||||
:cacheMeasurements (editor-row-height-unchanged?) ;; check when content updated (as the content variable is binded)
|
||||
@@ -629,7 +537,6 @@
|
||||
:on-click (editor-handler/editor-on-click! id)
|
||||
:on-change (editor-handler/editor-on-change! block id search-timeout)
|
||||
:on-paste (editor-handler/editor-on-paste! id)
|
||||
:on-height-change (editor-handler/editor-on-height-change! id)
|
||||
:auto-focus false
|
||||
:class heading-class})
|
||||
|
||||
|
||||
@@ -1,33 +1,3 @@
|
||||
#mobile-editor-toolbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
/* height: 2.5rem; */
|
||||
z-index: 9999;
|
||||
transition: none;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
padding: 7px 10px;
|
||||
}
|
||||
|
||||
.toolbar-commands {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow-x: overlay;
|
||||
overflow-y: hidden;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.toolbar-hide-keyboard {
|
||||
border-left: 1px solid;
|
||||
border-color: var(--ls-quaternary-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
#audio-record-toolbar {
|
||||
position: fixed;
|
||||
background-color: var(--ls-secondary-background-color);
|
||||
|
||||
@@ -224,11 +224,10 @@
|
||||
(let [repos (->> (state/sub [:me :repos])
|
||||
(remove #(= (:url %) config/local-repo)))
|
||||
electron-mac? (and util/mac? (util/electron?))
|
||||
vw-state (state/sub :ui/visual-viewport-state)
|
||||
show-open-folder? (and (nfs/supported?)
|
||||
(or (empty? repos)
|
||||
(nil? (state/sub :git/current-repo)))
|
||||
(not (mobile-util/is-native-platform?))
|
||||
(not (mobile-util/native-platform?))
|
||||
(not config/publishing?))]
|
||||
[:div.cp__header#head
|
||||
{:class (util/classnames [{:electron-mac electron-mac?
|
||||
@@ -239,8 +238,7 @@
|
||||
(when (and (util/electron?)
|
||||
(.. target -classList (contains "cp__header")))
|
||||
(js/window.apis.toggleMaxOrMinActiveWindow))))
|
||||
:style {:fontSize 50
|
||||
:transform (str "translateY(" (or (:offset-top vw-state) 0) "px)")}}
|
||||
:style {:fontSize 50}}
|
||||
[:div.l.flex
|
||||
(left-menu-button {:on-click (fn []
|
||||
(open-fn)
|
||||
@@ -271,7 +269,7 @@
|
||||
(mobile-util/native-ios?))
|
||||
(back-and-forward))
|
||||
|
||||
(when-not (mobile-util/is-native-platform?)
|
||||
(when-not (mobile-util/native-platform?)
|
||||
(new-block-mode))
|
||||
|
||||
(when show-open-folder?
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
user-select: none;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
background-color: var(--ls-primary-background-color);
|
||||
|
||||
> .l {
|
||||
width: var(--ls-left-sidebar-width);
|
||||
@@ -221,7 +222,9 @@ html.is-native-iphone-without-notch,
|
||||
html.is-native-ipad {
|
||||
|
||||
#main-container {
|
||||
padding-top: 0px;
|
||||
padding-top: 0px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#main-content-container {
|
||||
|
||||
@@ -71,10 +71,10 @@
|
||||
[:section.a
|
||||
[:strong "Let’s get you set up."]
|
||||
[:small (str "Where on your " DEVICE " do you want to save your work?")
|
||||
(when (mobile-util/is-native-platform?)
|
||||
(when (mobile-util/native-platform?)
|
||||
(mobile-intro))]
|
||||
|
||||
(if (or (nfs/supported?) (mobile-util/is-native-platform?))
|
||||
(if (or (nfs/supported?) (mobile-util/native-platform?))
|
||||
[:div.choose.flex.flex-col.items-center
|
||||
{:on-click #(page-handler/ls-dir-files!
|
||||
(fn []
|
||||
|
||||
@@ -183,7 +183,7 @@
|
||||
(ui/foldable
|
||||
[:h2.font-bold.opacity-50 (util/format "Pages tagged with \"%s\"" tag)]
|
||||
[:ul.mt-2
|
||||
(for [[original-name name] (sort pages)]
|
||||
(for [[original-name name] (sort-by last pages)]
|
||||
[:li {:key (str "tagged-page-" name)}
|
||||
[:a {:href (rfe/href :page {:name name})}
|
||||
original-name]])]
|
||||
@@ -231,7 +231,6 @@
|
||||
(when (gp-util/wrapped-by-quotes? @*title-value)
|
||||
(swap! *title-value gp-util/unquote-string)
|
||||
(gobj/set (rum/deref input-ref) "value" @*title-value))
|
||||
(state/set-state! :editor/editing-page-title? false)
|
||||
(cond
|
||||
(= old-name @*title-value)
|
||||
(reset! *edit? false)
|
||||
@@ -266,7 +265,6 @@
|
||||
(reset! *title-value old-name)
|
||||
(reset! *edit? false)))}]]
|
||||
[:a.page-title {:on-mouse-down (fn [e]
|
||||
(state/set-state! :editor/editing-page-title? true)
|
||||
(when (util/right-click? e)
|
||||
(state/set-state! :page-title/context {:page page-name})))
|
||||
:on-click (fn [e]
|
||||
@@ -364,7 +362,7 @@
|
||||
[:div.relative
|
||||
(when (and (not sidebar?) (not block?))
|
||||
[:div.flex.flex-row.space-between
|
||||
(when (or (mobile-util/is-native-platform?) (util/mobile?))
|
||||
(when (or (mobile-util/native-platform?) (util/mobile?))
|
||||
[:div.flex.flex-row.pr-2
|
||||
{:style {:margin-left -15}
|
||||
:on-mouse-over (fn [e]
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
(page-handler/unfavorite-page! page-original-name)
|
||||
(page-handler/favorite-page! page-original-name)))}}
|
||||
|
||||
(when-not (mobile-util/is-native-platform?)
|
||||
(when-not (mobile-util/native-platform?)
|
||||
{:title (t :page/presentation-mode)
|
||||
:options {:on-click (fn []
|
||||
(state/sidebar-add-block!
|
||||
@@ -103,7 +103,7 @@
|
||||
:options {:on-click #(js/window.apis.openPath file-path)}}])
|
||||
|
||||
(when (or (util/electron?)
|
||||
(mobile-util/is-native-platform?))
|
||||
(mobile-util/native-platform?))
|
||||
{:title (t :page/copy-page-url)
|
||||
:options {:on-click #(util/copy-to-clipboard!
|
||||
(url-util/get-logseq-graph-page-url nil repo page-original-name))}})
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
[:div.pl-1.content.mt-3
|
||||
[:div.flex.flex-row.my-4
|
||||
(when (or (nfs-handler/supported?)
|
||||
(mobile-util/is-native-platform?))
|
||||
(mobile-util/native-platform?))
|
||||
[:div.mr-8
|
||||
(ui/button
|
||||
(t :open-a-directory)
|
||||
@@ -102,7 +102,7 @@
|
||||
(when (and nfs-repo?
|
||||
(not= current-repo config/local-repo)
|
||||
(or (nfs-handler/supported?)
|
||||
(mobile-util/is-native-platform?)))
|
||||
(mobile-util/native-platform?)))
|
||||
{:title (t :sync-from-local-files)
|
||||
:hover-detail (t :sync-from-local-files-detail)
|
||||
:options {:on-click
|
||||
|
||||
@@ -132,7 +132,7 @@
|
||||
:href href
|
||||
:on-click on-click))]
|
||||
(when-not (or (util/mobile?)
|
||||
(mobile-util/is-native-platform?))
|
||||
(mobile-util/native-platform?))
|
||||
[:div.text-sm desc])]])
|
||||
|
||||
(defn edit-config-edn []
|
||||
@@ -161,7 +161,7 @@
|
||||
(ui/toggle show-brackets?
|
||||
config-handler/toggle-ui-show-brackets!
|
||||
true)]]
|
||||
(when (not (or (util/mobile?) (mobile-util/is-native-platform?)))
|
||||
(when (not (or (util/mobile?) (mobile-util/native-platform?)))
|
||||
[:div {:style {:text-align "right"}}
|
||||
(ui/render-keyboard-shortcut (shortcut-helper/gen-shortcut-seq :ui/toggle-brackets))])])
|
||||
|
||||
@@ -543,9 +543,9 @@
|
||||
(show-brackets-row t show-brackets?)
|
||||
(when (util/electron?) (switch-spell-check-row t))
|
||||
(outdenting-row t logical-outdenting?)
|
||||
(when-not (or (util/mobile?) (mobile-util/is-native-platform?))
|
||||
(when-not (or (util/mobile?) (mobile-util/native-platform?))
|
||||
(shortcut-tooltip-row t enable-shortcut-tooltip?))
|
||||
(when-not (or (util/mobile?) (mobile-util/is-native-platform?))
|
||||
(when-not (or (util/mobile?) (mobile-util/native-platform?))
|
||||
(tooltip-row t enable-tooltip?))
|
||||
(timetracking-row t enable-timetracking?)
|
||||
(journal-row t enable-journals?)
|
||||
@@ -595,7 +595,7 @@
|
||||
[:div.panel-wrap.is-advanced
|
||||
(when (and util/mac? (util/electron?)) (app-auto-update-row t))
|
||||
(usage-diagnostics-row t instrument-disabled?)
|
||||
(when-not (mobile-util/is-native-platform?) (developer-mode-row t developer-mode?))
|
||||
(when-not (mobile-util/native-platform?) (developer-mode-row t developer-mode?))
|
||||
(when (util/electron?) (plugin-system-switcher-row))
|
||||
(when (util/electron?) (https-user-agent-row https-agent-opts))
|
||||
(clear-cache-row t)
|
||||
@@ -633,7 +633,7 @@
|
||||
(for [[label text icon]
|
||||
[[:general (t :settings-page/tab-general) (ui/icon "adjustments" {:style {:font-size 20}})]
|
||||
[:editor (t :settings-page/tab-editor) (ui/icon "writing" {:style {:font-size 20}})]
|
||||
(when-not (mobile-util/is-native-platform?)
|
||||
(when-not (mobile-util/native-platform?)
|
||||
[:git (t :settings-page/tab-version-control) (ui/icon "history" {:style {:font-size 20}})])
|
||||
[:advanced (t :settings-page/tab-advanced) (ui/icon "bulb" {:style {:font-size 20}})]
|
||||
(when plugins-of-settings
|
||||
|
||||
@@ -4,37 +4,38 @@
|
||||
[frontend.components.command-palette :as command-palette]
|
||||
[frontend.components.header :as header]
|
||||
[frontend.components.journal :as journal]
|
||||
[frontend.components.onboarding :as onboarding]
|
||||
[frontend.components.plugins :as plugins]
|
||||
[frontend.components.repo :as repo]
|
||||
[frontend.components.right-sidebar :as right-sidebar]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.components.theme :as theme]
|
||||
[frontend.components.widgets :as widgets]
|
||||
[frontend.components.plugins :as plugins]
|
||||
[frontend.components.select :as select]
|
||||
[frontend.config :as config]
|
||||
[frontend.context.i18n :refer [t]]
|
||||
[frontend.db :as db]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.components.svg :as svg]
|
||||
[frontend.db-mixins :as db-mixins]
|
||||
[frontend.db.model :as db-model]
|
||||
[frontend.extensions.pdf.assets :as pdf-assets]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.handler.mobile.swipe :as swipe]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.handler.user :as user-handler]
|
||||
[frontend.mixins :as mixins]
|
||||
[frontend.mobile.footer :as footer]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.mobile.mobile-bar :refer [mobile-bar]]
|
||||
[frontend.modules.shortcut.data-helper :as shortcut-dh]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[goog.dom :as gdom]
|
||||
[goog.object :as gobj]
|
||||
[rum.core :as rum]
|
||||
[frontend.extensions.srs :as srs]
|
||||
[frontend.extensions.pdf.assets :as pdf-assets]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.handler.mobile.swipe :as swipe]
|
||||
[frontend.components.onboarding :as onboarding]
|
||||
[frontend.mobile.footer :as footer]))
|
||||
[reitit.frontend.easy :as rfe]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defc nav-content-item
|
||||
[name {:keys [class]} child]
|
||||
@@ -305,7 +306,10 @@
|
||||
:data-is-full-width (or margin-less-pages?
|
||||
(contains? #{:all-files :all-pages :my-publishing} route-name))}
|
||||
|
||||
(when (and (not (mobile-util/is-native-platform?))
|
||||
(mobile-bar)
|
||||
(footer/footer)
|
||||
|
||||
(when (and (not (mobile-util/native-platform?))
|
||||
(contains? #{:page :home} route-name))
|
||||
(widgets/demo-graph-alert))
|
||||
|
||||
@@ -378,7 +382,10 @@
|
||||
[page :page])]
|
||||
(state/sidebar-add-block! current-repo db-id block-type)))
|
||||
(reset! sidebar-inited? true))))
|
||||
state)}
|
||||
state)
|
||||
:did-mount (fn [state]
|
||||
(state/set-state! :mobile/show-tabbar? true)
|
||||
state)}
|
||||
[]
|
||||
(let [default-home (get-default-home-if-valid)
|
||||
current-repo (state/sub :git/current-repo)
|
||||
@@ -465,7 +472,8 @@
|
||||
(defn- hide-context-menu-and-clear-selection
|
||||
[e]
|
||||
(state/hide-custom-context-menu!)
|
||||
(when-not (gobj/get e "shiftKey")
|
||||
(when-not (or (gobj/get e "shiftKey")
|
||||
(util/meta-key? e))
|
||||
(editor-handler/clear-selection!)))
|
||||
|
||||
(rum/defcs ^:large-vars/cleanup-todo sidebar <
|
||||
@@ -550,12 +558,7 @@
|
||||
:indexeddb-support? indexeddb-support?
|
||||
:light? light?
|
||||
:db-restoring? db-restoring?
|
||||
:main-content main-content})
|
||||
|
||||
(when (and (mobile-util/is-native-platform?)
|
||||
current-repo
|
||||
(not (state/sub :modal/show?)))
|
||||
(footer/footer))]
|
||||
:main-content main-content})]
|
||||
|
||||
(right-sidebar/sidebar)
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
[]
|
||||
[:div.flex.flex-col
|
||||
[:h1.title (t :on-boarding/add-graph)]
|
||||
(let [nfs-supported? (or (nfs/supported?) (mobile-util/is-native-platform?))]
|
||||
(if (mobile-util/is-native-platform?)
|
||||
(let [nfs-supported? (or (nfs/supported?) (mobile-util/native-platform?))]
|
||||
(if (mobile-util/native-platform?)
|
||||
[:div.text-sm
|
||||
(ui/button "Open a local directory"
|
||||
:on-click #(page-handler/ls-dir-files! shortcut/refresh!))
|
||||
|
||||
@@ -315,7 +315,7 @@
|
||||
(and (util/electron?) (local-db? repo-url))
|
||||
(get-local-dir repo-url)
|
||||
|
||||
(and (mobile-util/is-native-platform?) (local-db? repo-url))
|
||||
(and (mobile-util/native-platform?) (local-db? repo-url))
|
||||
(let [dir (get-local-dir repo-url)]
|
||||
(if (string/starts-with? dir "file:")
|
||||
dir
|
||||
@@ -328,7 +328,7 @@
|
||||
|
||||
(defn get-repo-path
|
||||
[repo-url path]
|
||||
(if (and (or (util/electron?) (mobile-util/is-native-platform?))
|
||||
(if (and (or (util/electron?) (mobile-util/native-platform?))
|
||||
(local-db? repo-url))
|
||||
path
|
||||
(util/node-path.join (get-repo-dir repo-url) path)))
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
(defn get-repo-name
|
||||
[repo]
|
||||
(cond
|
||||
(mobile-util/is-native-platform?)
|
||||
(mobile-util/native-platform?)
|
||||
(text/get-graph-name-from-path repo)
|
||||
|
||||
(config/local-db? repo)
|
||||
@@ -36,7 +36,7 @@
|
||||
"repo-path: output of `get-repo-name`"
|
||||
[repo-path]
|
||||
(if (or (util/electron?)
|
||||
(mobile-util/is-native-platform?))
|
||||
(mobile-util/native-platform?))
|
||||
(text/get-file-basename repo-path)
|
||||
repo-path))
|
||||
|
||||
|
||||
@@ -409,6 +409,29 @@
|
||||
f))
|
||||
form))
|
||||
|
||||
(defn get-sorted-page-block-ids
|
||||
[page-id]
|
||||
(let [root (db-utils/entity page-id)]
|
||||
(loop [result []
|
||||
children (sort-by-left (:block/_parent root) root)]
|
||||
(if (seq children)
|
||||
(let [child (first children)]
|
||||
(recur (conj result (:db/id child))
|
||||
(concat
|
||||
(sort-by-left (:block/_parent child) child)
|
||||
(rest children))))
|
||||
result))))
|
||||
|
||||
(defn sort-page-random-blocks
|
||||
"Blocks could be non consecutive."
|
||||
[blocks]
|
||||
(assert (every? #(= (:block/page %) (:block/page (first blocks))) blocks) "Blocks must to be in a same page.")
|
||||
(let [page-id (:db/id (:block/page (first blocks)))
|
||||
;; TODO: there's no need to sort all the blocks
|
||||
sorted-ids (get-sorted-page-block-ids page-id)
|
||||
blocks-map (zipmap (map :db/id blocks) blocks)]
|
||||
(keep blocks-map sorted-ids)))
|
||||
|
||||
(defn has-children?
|
||||
([block-id]
|
||||
(has-children? (conn/get-db) block-id))
|
||||
@@ -584,6 +607,57 @@
|
||||
(recur parent)))
|
||||
false)))))
|
||||
|
||||
(defn get-prev-sibling
|
||||
[db id]
|
||||
(when-let [e (d/entity db id)]
|
||||
(let [left (:block/left e)]
|
||||
(when (not= (:db/id left) (:db/id (:block/parent e)))
|
||||
left))))
|
||||
|
||||
(defn get-right-sibling
|
||||
[db db-id]
|
||||
(when-let [block (d/entity db db-id)]
|
||||
(get-by-parent-&-left db
|
||||
(:db/id (:block/parent block))
|
||||
db-id)))
|
||||
|
||||
(defn last-child-block?
|
||||
"The child block could be collapsed."
|
||||
[db parent-id child-id]
|
||||
(when-let [child (d/entity db child-id)]
|
||||
(cond
|
||||
(= parent-id child-id)
|
||||
true
|
||||
|
||||
(get-right-sibling db child-id)
|
||||
false
|
||||
|
||||
:else
|
||||
(last-child-block? db parent-id (:db/id (:block/parent child))))))
|
||||
|
||||
(defn- consecutive-block?
|
||||
[block-1 block-2]
|
||||
(let [db (conn/get-db)
|
||||
aux-fn (fn [block-1 block-2]
|
||||
(and (= (:block/page block-1) (:block/page block-2))
|
||||
(or
|
||||
;; sibling or child
|
||||
(= (:db/id (:block/left block-2)) (:db/id block-1))
|
||||
(when-let [prev-sibling (get-prev-sibling db (:db/id block-2))]
|
||||
(last-child-block? db (:db/id prev-sibling) (:db/id block-1))))))]
|
||||
(or (aux-fn block-1 block-2) (aux-fn block-2 block-1))))
|
||||
|
||||
(defn get-non-consecutive-blocks
|
||||
[blocks]
|
||||
(vec
|
||||
(keep-indexed
|
||||
(fn [i _block]
|
||||
(when (< (inc i) (count blocks))
|
||||
(when-not (consecutive-block? (nth blocks i)
|
||||
(nth blocks (inc i)))
|
||||
(nth blocks i))))
|
||||
blocks)))
|
||||
|
||||
(defn- get-start-id-for-pagination-query
|
||||
[repo-url current-db {:keys [db-before tx-meta] :as tx-report}
|
||||
result outliner-op page-id block-id tx-block-ids]
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
(defn gen-youtube-ts-macro []
|
||||
(if-let [player (get-player (state/get-input))]
|
||||
(util/format "{{youtube-timestamp %s}}" (Math/floor (.getCurrentTime ^js player)))
|
||||
(when (mobile-util/is-native-platform?)
|
||||
(when (mobile-util/native-platform?)
|
||||
(notification/show!
|
||||
"Please embed a YouTube video at first, then use this icon.
|
||||
Remember: You can paste a raw YouTube url as embedded video on mobile."
|
||||
@@ -123,9 +123,9 @@ Remember: You can paste a raw YouTube url as embedded video on mobile."
|
||||
reg-number #"^\d+$"
|
||||
timestamp (str timestamp)
|
||||
total-seconds (-> (re-matches reg-number timestamp)
|
||||
parse-long)
|
||||
util/safe-parse-int)
|
||||
[_ hours minutes seconds] (re-matches reg timestamp)
|
||||
[hours minutes seconds] (map parse-long [hours minutes seconds])]
|
||||
[hours minutes seconds] (map util/safe-parse-int [hours minutes seconds])]
|
||||
(cond
|
||||
total-seconds
|
||||
total-seconds
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
(and (util/electron?) (not bfs-local?))
|
||||
node-record
|
||||
|
||||
(mobile-util/is-native-platform?)
|
||||
(mobile-util/native-platform?)
|
||||
mobile-record
|
||||
|
||||
(local-db? dir)
|
||||
@@ -109,7 +109,7 @@
|
||||
|
||||
:else
|
||||
(let [[old-path new-path]
|
||||
(map #(if (or (util/electron?) (mobile-util/is-native-platform?))
|
||||
(map #(if (or (util/electron?) (mobile-util/native-platform?))
|
||||
%
|
||||
(str (config/get-repo-dir repo) "/" %))
|
||||
[old-path new-path])]
|
||||
@@ -125,7 +125,7 @@
|
||||
(util/electron?)
|
||||
node-record
|
||||
|
||||
(mobile-util/is-native-platform?)
|
||||
(mobile-util/native-platform?)
|
||||
mobile-record
|
||||
|
||||
:else
|
||||
@@ -136,7 +136,7 @@
|
||||
(let [record (get-record)]
|
||||
(p/let [result (protocol/open-dir record ok-handler)]
|
||||
(if (or (util/electron?)
|
||||
(mobile-util/is-native-platform?))
|
||||
(mobile-util/native-platform?))
|
||||
(let [[dir & paths] (bean/->clj result)]
|
||||
[(:path dir) paths])
|
||||
result))))
|
||||
@@ -145,7 +145,7 @@
|
||||
[path-or-handle ok-handler]
|
||||
(let [record (get-record)
|
||||
electron? (util/electron?)
|
||||
mobile? (mobile-util/is-native-platform?)]
|
||||
mobile? (mobile-util/native-platform?)]
|
||||
(p/let [result (protocol/get-files record path-or-handle ok-handler)]
|
||||
(if (or electron? mobile?)
|
||||
(let [result (bean/->clj result)]
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
(and (not (seq (db/get-files config/local-repo)))
|
||||
;; Not native local directory
|
||||
(not (some config/local-db? (map :url repos)))
|
||||
(not (mobile-util/is-native-platform?)))
|
||||
(not (mobile-util/native-platform?)))
|
||||
;; will execute `(state/set-db-restoring! false)` inside
|
||||
(repo-handler/setup-local-repo-if-not-exists!)
|
||||
|
||||
@@ -193,7 +193,7 @@
|
||||
(p/let [repos (get-repos)]
|
||||
(state/set-repos! repos)
|
||||
(restore-and-setup! repos db-schema)
|
||||
(when (mobile-util/is-native-platform?)
|
||||
(when (mobile-util/native-platform?)
|
||||
(p/do! (mobile-util/hide-splash))))
|
||||
|
||||
(reset! db/*sync-search-indice-f search/sync-search-indice!)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
(ns frontend.handler.editor
|
||||
(:require ["/frontend/utils" :as utils]
|
||||
["path" :as path]
|
||||
[cljs.core.match :refer [match]]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[clojure.walk :as w]
|
||||
[dommy.core :as dom]
|
||||
[frontend.commands :as commands
|
||||
:refer [*angle-bracket-caret-pos *show-block-commands
|
||||
*show-commands *slash-caret-pos]]
|
||||
:refer [*angle-bracket-caret-pos
|
||||
*show-block-commands *show-commands
|
||||
*slash-caret-pos]]
|
||||
[frontend.config :as config]
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
@@ -25,12 +27,12 @@
|
||||
[frontend.handler.notification :as notification]
|
||||
[frontend.handler.repeated :as repeated]
|
||||
[frontend.handler.route :as route-handler]
|
||||
[frontend.image :as image]
|
||||
[frontend.idb :as idb]
|
||||
[frontend.image :as image]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.modules.outliner.core :as outliner-core]
|
||||
[frontend.modules.outliner.tree :as tree]
|
||||
[frontend.modules.outliner.transaction :as outliner-tx]
|
||||
[frontend.modules.outliner.tree :as tree]
|
||||
[frontend.search :as search]
|
||||
[frontend.state :as state]
|
||||
[frontend.template :as template]
|
||||
@@ -40,21 +42,20 @@
|
||||
[frontend.util.clock :as clock]
|
||||
[frontend.util.cursor :as cursor]
|
||||
[frontend.util.drawer :as drawer]
|
||||
[frontend.util.marker :as marker]
|
||||
[frontend.util.property :as property]
|
||||
[frontend.util.priority :as priority]
|
||||
[frontend.util.thingatpt :as thingatpt]
|
||||
[frontend.util.keycode :as keycode]
|
||||
[frontend.util.list :as list]
|
||||
[frontend.util.marker :as marker]
|
||||
[frontend.util.priority :as priority]
|
||||
[frontend.util.property :as property]
|
||||
[frontend.util.thingatpt :as thingatpt]
|
||||
[goog.dom :as gdom]
|
||||
[goog.dom.classes :as gdom-classes]
|
||||
[goog.object :as gobj]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[frontend.util.keycode :as keycode]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[logseq.graph-parser.block :as gp-block]
|
||||
["path" :as path]))
|
||||
[logseq.graph-parser.block :as gp-block]))
|
||||
|
||||
;; FIXME: should support multiple images concurrently uploading
|
||||
|
||||
@@ -1411,7 +1412,7 @@
|
||||
(util/electron?)
|
||||
(str "assets://" repo-dir path)
|
||||
|
||||
(mobile-util/is-native-platform?)
|
||||
(mobile-util/native-platform?)
|
||||
(mobile-util/convert-file-src (str repo-dir path))
|
||||
|
||||
:else
|
||||
@@ -1677,7 +1678,8 @@
|
||||
(move-nodes blocks))
|
||||
(when-let [input-id (state/get-edit-input-id)]
|
||||
(when-let [input (gdom/getElement input-id)]
|
||||
(.focus input))))
|
||||
(.focus input)
|
||||
(js/setTimeout #(util/scroll-editor-cursor input) 100))))
|
||||
(let [ids (state/get-selection-block-ids)]
|
||||
(when (seq ids)
|
||||
(let [lookup-refs (map (fn [id] [:block/uuid id]) ids)
|
||||
@@ -2630,7 +2632,7 @@
|
||||
;; FIXME: On mobile, a backspace click to call keydown-backspace-handler
|
||||
;; does not work sometimes in an empty block, hence the empty block
|
||||
;; can't be deleted. Need to figure out why and find a better solution.
|
||||
(and (mobile-util/is-native-platform?)
|
||||
(and (mobile-util/native-platform?)
|
||||
(= key "Backspace")
|
||||
(= value ""))
|
||||
(do
|
||||
@@ -2813,24 +2815,9 @@
|
||||
[id]
|
||||
(fn [_e]
|
||||
(let [input (gdom/getElement id)]
|
||||
(util/scroll-editor-cursor input)
|
||||
(close-autocomplete-if-outside input))))
|
||||
|
||||
(defonce mobile-toolbar-height 40)
|
||||
(defn editor-on-height-change!
|
||||
[id]
|
||||
(fn [box-height ^js row-height]
|
||||
(let [row-height (:rowHeight (js->clj row-height :keywordize-keys true))
|
||||
input (gdom/getElement id)
|
||||
caret (cursor/get-caret-pos input)
|
||||
cursor-bottom (if caret (+ row-height (:top caret)) box-height)
|
||||
box-top (gobj/get (.getBoundingClientRect input) "top")
|
||||
cursor-y (+ cursor-bottom box-top)
|
||||
vw-height (.-height js/window.visualViewport)]
|
||||
(when (< vw-height (+ cursor-y mobile-toolbar-height))
|
||||
(let [main-node (gdom/getElement "main-content-container")
|
||||
scroll-top (.-scrollTop main-node)]
|
||||
(set! (.-scrollTop main-node) (+ scroll-top row-height)))))))
|
||||
|
||||
(defn editor-on-change!
|
||||
[block id search-timeout]
|
||||
(fn [e]
|
||||
@@ -2842,7 +2829,9 @@
|
||||
(js/setTimeout
|
||||
#(edit-box-on-change! e block id)
|
||||
timeout)))
|
||||
(edit-box-on-change! e block id))))
|
||||
(let [input (gdom/getElement id)]
|
||||
(edit-box-on-change! e block id)
|
||||
(util/scroll-editor-cursor input)))))
|
||||
|
||||
(defn- paste-text-parseable
|
||||
[format text]
|
||||
@@ -2878,6 +2867,18 @@
|
||||
(recur (remove (set (map :block/uuid result)) (rest ids)) result))
|
||||
result)))
|
||||
|
||||
(defn wrap-macro-url
|
||||
[url]
|
||||
(cond
|
||||
(boolean (text/get-matched-video url))
|
||||
(util/format "{{video %s}}" url)
|
||||
|
||||
(string/includes? url "twitter.com")
|
||||
(util/format "{{twitter %s}}" url)
|
||||
|
||||
:else
|
||||
(notification/show! (util/format "No macro is available for %s" url) :warning)))
|
||||
|
||||
(defn- paste-copied-blocks-or-text
|
||||
[text e]
|
||||
(let [copied-blocks (state/get-copied-blocks)
|
||||
@@ -2905,18 +2906,7 @@
|
||||
(and (gp-util/url? text)
|
||||
(not (string/blank? (util/get-selected-text))))
|
||||
(html-link-format! text)
|
||||
|
||||
(and (gp-util/url? text)
|
||||
(or (string/includes? text "youtube.com")
|
||||
(string/includes? text "youtu.be"))
|
||||
(mobile-util/is-native-platform?))
|
||||
(commands/simple-insert! (state/get-edit-input-id) (util/format "{{youtube %s}}" text) nil)
|
||||
|
||||
(and (gp-util/url? text)
|
||||
(string/includes? text "twitter.com")
|
||||
(mobile-util/is-native-platform?))
|
||||
(commands/simple-insert! (state/get-edit-input-id) (util/format "{{twitter %s}}" text) nil)
|
||||
|
||||
|
||||
(and (text/block-ref? text)
|
||||
(wrapped-by? input "((" "))"))
|
||||
(commands/simple-insert! (state/get-edit-input-id) (text/get-block-ref text) nil)
|
||||
@@ -2953,7 +2943,10 @@
|
||||
(utils/getClipText
|
||||
(fn [clipboard-data]
|
||||
(when-let [_ (state/get-input)]
|
||||
(state/append-current-edit-content! clipboard-data)))
|
||||
(let [data (if (gp-util/url? clipboard-data)
|
||||
(wrap-macro-url clipboard-data)
|
||||
clipboard-data)]
|
||||
(state/append-current-edit-content! data))))
|
||||
(fn [error]
|
||||
(js/console.error error))))
|
||||
|
||||
@@ -2964,7 +2957,8 @@
|
||||
(let [text (.getData (gobj/get e "clipboardData") "text")
|
||||
input (state/get-input)]
|
||||
(if-not (string/blank? text)
|
||||
(if (thingatpt/org-admonition&src-at-point input)
|
||||
(if (or (thingatpt/markdown-src-at-point input)
|
||||
(thingatpt/org-admonition&src-at-point input))
|
||||
(when-not (mobile-util/native-ios?)
|
||||
(util/stop e)
|
||||
(paste-text-in-one-block-at-point))
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
[frontend.handler.editor.keyboards :as keyboards-handler]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[goog.dom :as gdom]))
|
||||
|
||||
(defn did-mount!
|
||||
@@ -21,9 +20,7 @@
|
||||
|
||||
(when-let [element (gdom/getElement id)]
|
||||
(.focus element)
|
||||
(when (or (mobile-util/is-native-platform?)
|
||||
(util/mobile?))
|
||||
(util/make-el-cursor-position-into-center-viewport element))))
|
||||
(js/setTimeout #(util/scroll-editor-cursor element) 50)))
|
||||
state)
|
||||
|
||||
(defn did-remount!
|
||||
|
||||
@@ -137,7 +137,7 @@
|
||||
[repo]
|
||||
(when
|
||||
(and (not (util/electron?))
|
||||
(not (mobile-util/is-native-platform?)))
|
||||
(not (mobile-util/native-platform?)))
|
||||
(fn [close-fn]
|
||||
[:div
|
||||
[:p
|
||||
@@ -288,15 +288,28 @@
|
||||
(reset! st/*inited? true)
|
||||
(st/consume-pending-shortcuts!)))
|
||||
|
||||
(defmethod handle :mobile/keyboard-will-show [[_ keyboard-height]]
|
||||
(let [main-node (util/app-scroll-container-node)]
|
||||
(state/set-state! :mobile/show-tabbar? false)
|
||||
(state/set-state! :mobile/show-toolbar? true)
|
||||
(when (mobile-util/native-ios?)
|
||||
(reset! util/keyboard-height keyboard-height)
|
||||
(set! (.. main-node -style -marginBottom) (str keyboard-height "px"))
|
||||
(when-let [card-preview-el (js/document.querySelector ".cards-review")]
|
||||
(set! (.. card-preview-el -style -marginBottom) (str keyboard-height "px")))
|
||||
(js/setTimeout (fn []
|
||||
(let [toolbar (.querySelector main-node "#mobile-editor-toolbar")]
|
||||
(set! (.. toolbar -style -bottom) (str keyboard-height "px"))))
|
||||
100))))
|
||||
|
||||
(defmethod handle :mobile/keyboard-will-show [[_]]
|
||||
(when (and (state/get-left-sidebar-open?)
|
||||
(state/editing?))
|
||||
(state/set-left-sidebar-open! false)))
|
||||
|
||||
(defmethod handle :mobile/keyboard-did-show [[_]]
|
||||
(when-let [input (state/get-input)]
|
||||
(util/make-el-cursor-position-into-center-viewport input)))
|
||||
(defmethod handle :mobile/keyboard-will-hide [[_]]
|
||||
(let [main-node (util/app-scroll-container-node)]
|
||||
(state/set-state! :mobile/show-toolbar? false)
|
||||
(state/set-state! :mobile/show-tabbar? true)
|
||||
(when (mobile-util/native-ios?)
|
||||
(when-let [card-preview-el (js/document.querySelector ".cards-review")]
|
||||
(set! (.. card-preview-el -style -marginBottom) "0px"))
|
||||
(set! (.. main-node -style -marginBottom) "0px"))))
|
||||
|
||||
(defmethod handle :plugin/consume-updates [[_ id pending? updated?]]
|
||||
(let [downloading? (:plugin/updates-downloading? @state/state)]
|
||||
|
||||
@@ -164,7 +164,7 @@
|
||||
(db/transact! [[:db.fn/retractEntity [:file/path file-path]]])
|
||||
(->
|
||||
(p/let [_ (and (config/local-db? repo)
|
||||
(mobile-util/is-native-platform?)
|
||||
(mobile-util/native-platform?)
|
||||
(fs/delete-file! repo file-path file-path {}))
|
||||
_ (fs/unlink! repo (config/get-repo-path repo file-path) nil)])
|
||||
(p/catch (fn [err]
|
||||
@@ -594,7 +594,7 @@
|
||||
page)
|
||||
(let [journal? (date/valid-journal-title? page)
|
||||
ref-file-path (str
|
||||
(if (or (util/electron?) (mobile-util/is-native-platform?))
|
||||
(if (or (util/electron?) (mobile-util/native-platform?))
|
||||
(-> (config/get-repo-dir (state/get-current-repo))
|
||||
js/decodeURI
|
||||
(string/replace #"/+$" "")
|
||||
@@ -728,7 +728,7 @@
|
||||
(not (state/loading-files? repo)))
|
||||
(state/set-today! (date/today))
|
||||
(when (or (config/local-db? repo)
|
||||
(and (= "local" repo) (not (mobile-util/is-native-platform?))))
|
||||
(and (= "local" repo) (not (mobile-util/native-platform?))))
|
||||
(let [title (date/today)
|
||||
today-page (util/page-name-sanity-lc title)
|
||||
format (state/get-preferred-format repo)
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
|
||||
(defn exec-js-if-exists-&-allowed!
|
||||
[t]
|
||||
(when-not (mobile/is-native-platform?)
|
||||
(when-not (mobile/native-platform?)
|
||||
(when-let [href (or
|
||||
(state/get-custom-js-link)
|
||||
(config/get-custom-js-path))]
|
||||
|
||||
@@ -26,12 +26,12 @@
|
||||
[frontend.encrypt :as encrypt]))
|
||||
|
||||
(defn remove-ignore-files
|
||||
[files]
|
||||
[files dir-name nfs?]
|
||||
(let [files (remove (fn [f]
|
||||
(let [path (:file/path f)]
|
||||
(or (string/starts-with? path ".git/")
|
||||
(string/includes? path ".git/")
|
||||
(and (util-fs/ignored-path? "" path)
|
||||
(and (util-fs/ignored-path? (if nfs? "" dir-name) path)
|
||||
(not= (:file/name f) ".gitignore")))))
|
||||
files)]
|
||||
(if-let [ignore-file (some #(when (= (:file/name %) ".gitignore")
|
||||
@@ -55,7 +55,7 @@
|
||||
:file/last-modified-at mtime
|
||||
:file/size size
|
||||
:file/content content})
|
||||
result)
|
||||
result)
|
||||
|
||||
electron?
|
||||
(map (fn [{:keys [path stat content]}]
|
||||
@@ -64,7 +64,7 @@
|
||||
:file/last-modified-at mtime
|
||||
:file/size size
|
||||
:file/content content}))
|
||||
result)
|
||||
result)
|
||||
|
||||
:else
|
||||
(let [result (flatten (bean/->clj result))]
|
||||
@@ -122,7 +122,7 @@
|
||||
[ok-handler]
|
||||
(let [path-handles (atom {})
|
||||
electron? (util/electron?)
|
||||
mobile-native? (mobile-util/is-native-platform?)
|
||||
mobile-native? (mobile-util/native-platform?)
|
||||
nfs? (and (not electron?)
|
||||
(not mobile-native?))
|
||||
*repo (atom nil)]
|
||||
@@ -147,7 +147,7 @@
|
||||
(nfs/add-nfs-file-handle! root-handle-path root-handle))
|
||||
result (nth result 1)
|
||||
files (-> (->db-files mobile-native? electron? dir-name result)
|
||||
remove-ignore-files)
|
||||
(remove-ignore-files dir-name nfs?))
|
||||
_ (when nfs?
|
||||
(let [file-paths (set (map :file/path files))]
|
||||
(swap! path-handles (fn [handles]
|
||||
@@ -282,7 +282,7 @@
|
||||
handle-path (str config/local-handle-prefix dir-name)
|
||||
path-handles (atom {})
|
||||
electron? (util/electron?)
|
||||
mobile-native? (mobile-util/is-native-platform?)
|
||||
mobile-native? (mobile-util/native-platform?)
|
||||
nfs? (and (not electron?)
|
||||
(not mobile-native?))]
|
||||
(when re-index?
|
||||
@@ -297,7 +297,7 @@
|
||||
(when nfs?
|
||||
(swap! path-handles assoc path handle))))
|
||||
new-files (-> (->db-files mobile-native? electron? dir-name files-result)
|
||||
remove-ignore-files)
|
||||
(remove-ignore-files dir-name nfs?))
|
||||
_ (when nfs?
|
||||
(let [file-paths (set (map :file/path new-files))]
|
||||
(swap! path-handles (fn [handles]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
(ns frontend.mobile.core
|
||||
(:require ["@capacitor/app" :refer [^js App]]
|
||||
["@capacitor/keyboard" :refer [^js Keyboard]]
|
||||
[clojure.string :as string]
|
||||
[frontend.fs.capacitor-fs :as fs]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
@@ -80,6 +81,15 @@
|
||||
(fn [event]
|
||||
(state/pub-event! [:file-watcher/changed event])))
|
||||
|
||||
(.addListener Keyboard "keyboardWillShow"
|
||||
(fn [^js info]
|
||||
(let [keyboard-height (.-keyboardHeight info)]
|
||||
(state/pub-event! [:mobile/keyboard-will-show keyboard-height]))))
|
||||
|
||||
(.addListener Keyboard "keyboardWillHide"
|
||||
(fn []
|
||||
(state/pub-event! [:mobile/keyboard-will-hide])))
|
||||
|
||||
(.addEventListener js/window "statusTap"
|
||||
#(util/scroll-to-top true))
|
||||
|
||||
@@ -96,6 +106,6 @@
|
||||
|
||||
(when (mobile-util/native-ios?)
|
||||
(ios-init))
|
||||
|
||||
(when (mobile-util/is-native-platform?)
|
||||
|
||||
(when (mobile-util/native-platform?)
|
||||
(general-init)))
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
(ns frontend.mobile.footer
|
||||
(:require [frontend.ui :as ui]
|
||||
[rum.core :as rum]
|
||||
[frontend.state :as state]
|
||||
[frontend.mobile.record :as record]
|
||||
[frontend.util :as util]
|
||||
(:require [clojure.string :as string]
|
||||
[frontend.date :as date]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[clojure.string :as string]
|
||||
[frontend.date :as date]))
|
||||
[frontend.mobile.record :as record]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(rum/defc mobile-bar-command [command-handler icon]
|
||||
[:div
|
||||
@@ -46,9 +46,8 @@
|
||||
|
||||
(rum/defc footer < rum/reactive
|
||||
[]
|
||||
(when-not (or (state/sub :editor/editing?)
|
||||
(state/sub :block/component-editing-mode?)
|
||||
(state/sub :editor/editing-page-title?))
|
||||
(when (and (state/sub :mobile/show-tabbar?)
|
||||
(state/get-current-repo))
|
||||
[:div.cp__footer.w-full.bottom-0.justify-between
|
||||
(audio-record-cp)
|
||||
(mobile-bar-command #(state/toggle-document-mode!) "notes")
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
.cp__footer {
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
padding: 10px 20px;
|
||||
background-color: var(--ls-primary-background-color);
|
||||
z-index: 1000;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
@@ -22,6 +23,56 @@
|
||||
}
|
||||
}
|
||||
|
||||
#mobile-editor-toolbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
transition: bottom 260ms;
|
||||
/* transition-timing-function: cubic-bezier(.29, 1.01, 1, -0.68); */
|
||||
/* transition-timing-function: steps(10, jump-end); */
|
||||
/* transition-timing-function: steps(5, end); */
|
||||
transition-timing-function: ease-out;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
button {
|
||||
padding: 7px 10px;
|
||||
|
||||
.submenu {
|
||||
background-color: red;
|
||||
z-index: 100;
|
||||
background-color: var(--ls-secondary-background-color);
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.02);
|
||||
overflow-x: overlay;
|
||||
overflow-y: hidden;
|
||||
left: 0px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.show-submenu {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.toolbar-commands {
|
||||
justify-content: space-between;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
overflow-x: overlay;
|
||||
overflow-y: hidden;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.toolbar-hide-keyboard {
|
||||
border-left: 1px solid;
|
||||
border-color: var(--ls-quaternary-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
html.is-native-ipad {
|
||||
.cp__footer {
|
||||
height: 55px;
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
(ns frontend.mobile.intent
|
||||
(:require ["@capacitor/filesystem" :refer [Filesystem]]
|
||||
["path" :as path]
|
||||
["send-intent" :refer [^js SendIntent]]
|
||||
[lambdaisland.glogi :as log]
|
||||
[promesa.core :as p]
|
||||
[clojure.pprint :as pprint]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as string]
|
||||
[frontend.config :as config]
|
||||
[frontend.date :as date]
|
||||
[frontend.db :as db]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.state :as state]
|
||||
[frontend.date :as date]
|
||||
[frontend.util :as util]
|
||||
[frontend.config :as config]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
["path" :as path]
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.handler.notification :as notification]
|
||||
[clojure.pprint :as pprint]
|
||||
[clojure.set :as set]))
|
||||
[frontend.mobile.util :as mobile-util]
|
||||
[frontend.state :as state]
|
||||
[frontend.util :as util]
|
||||
[lambdaisland.glogi :as log]
|
||||
[logseq.graph-parser.config :as gp-config]
|
||||
[logseq.graph-parser.mldoc :as gp-mldoc]
|
||||
[logseq.graph-parser.text :as text]
|
||||
[promesa.core :as p]))
|
||||
|
||||
(defn- handle-received-text [result]
|
||||
(let [{:keys [title url]} result
|
||||
@@ -33,9 +34,8 @@
|
||||
(string/split url "\"\n"))
|
||||
text (some-> text (string/replace #"^\"" ""))
|
||||
url (and url
|
||||
(cond (or (string/includes? url "youtube.com")
|
||||
(string/includes? url "youtu.be"))
|
||||
(util/format "{{youtube %s}}" url)
|
||||
(cond (boolean (text/get-matched-video url))
|
||||
(util/format "{{video %s}}" url)
|
||||
|
||||
(and (string/includes? url "twitter.com")
|
||||
(string/includes? url "status"))
|
||||
|
||||
141
src/main/frontend/mobile/mobile_bar.cljs
Normal file
141
src/main/frontend/mobile/mobile_bar.cljs
Normal file
@@ -0,0 +1,141 @@
|
||||
(ns frontend.mobile.mobile-bar
|
||||
(:require [dommy.core :as dom]
|
||||
[frontend.commands :as commands]
|
||||
[frontend.date :as date]
|
||||
[frontend.handler.config :as config-handler]
|
||||
[frontend.handler.editor :as editor-handler]
|
||||
[frontend.handler.history :as history]
|
||||
[frontend.handler.page :as page-handler]
|
||||
[frontend.mobile.camera :as mobile-camera]
|
||||
[frontend.state :as state]
|
||||
[frontend.ui :as ui]
|
||||
[frontend.util :as util]
|
||||
[goog.dom :as gdom]
|
||||
[rum.core :as rum]))
|
||||
|
||||
(def ^:private icons-keywords
|
||||
[:checkbox :brackets :parentheses :command :tag :a-b :list :camera
|
||||
:brand-youtube :link :rotate :rotate-clockwise :code :calendar :bold :italic :strikethrough :paint])
|
||||
|
||||
(def ^:private commands-stats
|
||||
(atom (into {}
|
||||
(mapv (fn [name] [name {:counts 0}])
|
||||
icons-keywords))))
|
||||
|
||||
(defn set-command-stats [icon]
|
||||
(let [key (keyword icon)
|
||||
counts (get-in @commands-stats [key :counts])]
|
||||
(swap! commands-stats
|
||||
assoc-in [key :counts] (inc counts))
|
||||
(config-handler/set-config!
|
||||
:mobile/toolbar-stats @commands-stats)))
|
||||
|
||||
(rum/defc command
|
||||
[command-handler icon & [count? event?]]
|
||||
[:div
|
||||
[:button.bottom-action
|
||||
{:on-mouse-down (fn [e]
|
||||
(util/stop e)
|
||||
(when count?
|
||||
(set-command-stats icon))
|
||||
(if event?
|
||||
(command-handler e)
|
||||
(command-handler))
|
||||
(state/set-state! :mobile/toolbar-update-observer (rand-int 1000000)))}
|
||||
(ui/icon icon {:style {:fontSize ui/icon-size}})]])
|
||||
|
||||
(rum/defc indent-outdent [indent? icon]
|
||||
[:div
|
||||
[:button.bottom-action
|
||||
{:on-mouse-down (fn [e]
|
||||
(util/stop e)
|
||||
(editor-handler/indent-outdent indent?))}
|
||||
(ui/icon icon {:style {:fontSize ui/icon-size}})]])
|
||||
|
||||
(rum/defc timestamp-submenu
|
||||
[parent-id]
|
||||
(let [callback (fn [event]
|
||||
(util/stop event)
|
||||
(let [target (.-parentNode (.-target event))]
|
||||
(dom/remove-class! target "show-submenu")))
|
||||
command-cp (fn [action description]
|
||||
[:button
|
||||
{:on-mouse-down (fn [e]
|
||||
(action)
|
||||
(callback e))}
|
||||
description])]
|
||||
[:div
|
||||
[:button.bottom-action
|
||||
{:on-mouse-down (fn [event]
|
||||
(util/stop event)
|
||||
(set-command-stats :calendar)
|
||||
(state/set-state! :mobile/toolbar-update-observer (rand-int 1000000))
|
||||
(let [target (gdom/getNextElementSibling (.-target event))]
|
||||
(dom/add-class! target "show-submenu")))}
|
||||
(ui/icon "calendar" {:style {:fontSize ui/icon-size}})
|
||||
[:div.submenu.fixed.hidden.flex.flex-col.w-full.justify-evenly
|
||||
{:style {:bottom @util/keyboard-height}}
|
||||
(command-cp #(let [today (page-handler/get-page-ref-text (date/today))]
|
||||
(commands/simple-insert! parent-id today {}))
|
||||
"Today")
|
||||
(command-cp #(let [tomorrow (page-handler/get-page-ref-text (date/tomorrow))]
|
||||
(commands/simple-insert! parent-id tomorrow {}))
|
||||
"Tomorrow")
|
||||
(command-cp #(let [yesterday (page-handler/get-page-ref-text (date/yesterday))]
|
||||
(commands/simple-insert! parent-id yesterday {}))
|
||||
"Yesterday")
|
||||
(command-cp #(let [timestamp (date/get-current-time)]
|
||||
(commands/simple-insert! parent-id timestamp {}))
|
||||
"Time")]]]))
|
||||
|
||||
(defn commands
|
||||
[parent-id]
|
||||
(let [viewport-fn (fn [] (when-let [input (gdom/getElement parent-id)]
|
||||
(util/scroll-editor-cursor input :to-vw-one-quarter? true)
|
||||
(.focus input)))]
|
||||
(zipmap icons-keywords
|
||||
[(command editor-handler/cycle-todo! "checkbox" true)
|
||||
(command #(do (viewport-fn) (editor-handler/toggle-page-reference-embed parent-id)) "brackets" true)
|
||||
(command #(do (viewport-fn) (editor-handler/toggle-block-reference-embed parent-id)) "parentheses" true)
|
||||
(command #(do (viewport-fn) (commands/simple-insert! parent-id "/" {})) "command" true)
|
||||
(command #(do (viewport-fn) (commands/simple-insert! parent-id "#" {})) "tag" true)
|
||||
(command editor-handler/cycle-priority! "a-b" true)
|
||||
(command editor-handler/toggle-list! "list" true)
|
||||
(command #(mobile-camera/embed-photo parent-id) "camera" true)
|
||||
(command commands/insert-youtube-timestamp "brand-youtube" true)
|
||||
(command editor-handler/html-link-format! "link" true)
|
||||
(command history/undo! "rotate" true true)
|
||||
(command history/redo! "rotate-clockwise" true true)
|
||||
(timestamp-submenu parent-id)
|
||||
(command #(commands/simple-insert! parent-id "<" {}) "code" true)
|
||||
(command editor-handler/bold-format! "bold" true)
|
||||
(command editor-handler/italics-format! "italic" true)
|
||||
(command editor-handler/strike-through-format! "strikethrough" true)
|
||||
(command editor-handler/highlight-format! "paint" true)])))
|
||||
|
||||
(rum/defc mobile-bar < rum/reactive
|
||||
[]
|
||||
(when (and (state/sub :mobile/toolbar-update-observer)
|
||||
(state/sub :mobile/show-toolbar?))
|
||||
(when-let [config-toolbar-stats (:mobile/toolbar-stats (state/get-config))]
|
||||
(prn :config-toolbar-stats config-toolbar-stats)
|
||||
(reset! commands-stats config-toolbar-stats))
|
||||
(let [parent-id (state/get-edit-input-id)
|
||||
commands (commands parent-id)
|
||||
sorted-commands (sort-by (comp :counts second) > @commands-stats)]
|
||||
(when (and (state/sub :mobile/show-toolbar?)
|
||||
(state/sub :editor/editing?))
|
||||
[:div#mobile-editor-toolbar.bg-base-2
|
||||
[:div.toolbar-commands
|
||||
(indent-outdent false "arrow-bar-left")
|
||||
(indent-outdent true "arrow-bar-right")
|
||||
(command (editor-handler/move-up-down true) "arrow-bar-to-up")
|
||||
(command (editor-handler/move-up-down false) "arrow-bar-to-down")
|
||||
(command #(if (state/sub :document/mode?)
|
||||
(editor-handler/insert-new-block! nil)
|
||||
(commands/simple-insert! parent-id "\n" {})) "arrow-back")
|
||||
(for [command sorted-commands]
|
||||
((first command) commands))]
|
||||
[:div.toolbar-hide-keyboard
|
||||
(command #(state/clear-edit!) "keyboard-show")]]))))
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
(defn platform []
|
||||
(.getPlatform Capacitor))
|
||||
|
||||
(defn is-native-platform? []
|
||||
(defn native-platform? []
|
||||
(.isNativePlatform Capacitor))
|
||||
|
||||
(defn native-ios? []
|
||||
(and (is-native-platform?)
|
||||
(and (native-platform?)
|
||||
(= (platform) "ios")))
|
||||
|
||||
(defn native-android? []
|
||||
(and (is-native-platform?)
|
||||
(and (native-platform?)
|
||||
(= (platform) "android")))
|
||||
|
||||
(defn convert-file-src [path-str]
|
||||
@@ -27,7 +27,7 @@
|
||||
(defonce file-sync (registerPlugin "FileSync")))
|
||||
|
||||
;; NOTE: both iOS and android share the same FsWatcher API
|
||||
(when (is-native-platform?)
|
||||
(when (native-platform?)
|
||||
(defonce fs-watcher (registerPlugin "FsWatcher")))
|
||||
|
||||
(defn sync-icloud-repo [repo-dir]
|
||||
@@ -41,45 +41,6 @@
|
||||
(defn hide-splash []
|
||||
(.hide SplashScreen))
|
||||
|
||||
(def idevice-info
|
||||
(atom
|
||||
{:iPadPro12.9 {:width 1024 :height 1366 :statusbar 40}
|
||||
:iPadPro11 {:width 834 :height 1194 :statusbar 40}
|
||||
:iPadPro10.5 {:width 834 :height 1112 :statusbar 40}
|
||||
:iPadAir10.5 {:width 834 :height 1112 :statusbar 40}
|
||||
:iPadAir10.9 {:width 820 :height 1180 :statusbar 40}
|
||||
:iPad10.2 {:width 810 :height 1080 :statusbar 40}
|
||||
:iPadPro9.7 {:width 768 :height 1024 :statusbar 40}
|
||||
:iPadmini9.7 {:width 768 :height 1024 :statusbar 40}
|
||||
:iPadAir9.7 {:width 768 :height 1024 :statusbar 40}
|
||||
:iPad9.7 {:width 768 :height 1024 :statusbar 40}
|
||||
:iPadmini8.3 {:width 744 :height 1133 :statusbar 40}
|
||||
:iPhone7Plus {:width 476 :height 847 :statusbar 20}
|
||||
:iPhone6sPlus {:width 476 :height 847 :statusbar 20}
|
||||
:iPhone6Plus {:width 476 :height 847 :statusbar 20}
|
||||
:iPhone13ProMax {:width 428 :height 926 :statusbar 47}
|
||||
:iPhone12ProMax {:width 428 :height 926 :statusbar 47}
|
||||
:iPhone11ProMax {:width 414 :height 896 :statusbar 44}
|
||||
:iPhone11 {:width 414 :height 896 :statusbar 48}
|
||||
:iPhoneXSMax {:width 414 :height 896 :statusbar 48}
|
||||
:iPhoneXR {:width 414 :height 896 :statusbar 48}
|
||||
:iPhone8Plus {:width 414 :height 736 :statusbar 20}
|
||||
:iPhone13Pro {:width 390 :height 844 :statusbar 47}
|
||||
:iPhone13 {:width 390 :height 844 :statusbar 47}
|
||||
:iPhone12 {:width 390 :height 844 :statusbar 47}
|
||||
:iPhone12Pro {:width 390 :height 844 :statusbar 47}
|
||||
:iPhone11Pro {:width 375 :height 812 :statusbar 44}
|
||||
:iPhoneXS {:width 375 :height 812 :statusbar 44}
|
||||
:iPhoneX {:width 375 :height 812 :statusbar 44}
|
||||
:iPhone8 {:width 375 :height 667 :statusbar 20}
|
||||
:iPhone7 {:width 375 :height 667 :statusbar 20}
|
||||
:iPhone6s {:width 375 :height 667 :statusbar 20}
|
||||
:iPhone6 {:width 375 :height 667 :statusbar 20}
|
||||
:iPhone13mini {:width 375 :height 812 :statusbar 44}
|
||||
:iPhone12mini {:width 375 :height 812 :statusbar 44}
|
||||
:iPhoneSE4 {:width 320 :height 568 :statusbar 20}
|
||||
:iPodtouch5 {:width 320 :height 568 :statusbar 20}}))
|
||||
|
||||
(defn get-idevice-model
|
||||
[]
|
||||
(when (native-ios?)
|
||||
@@ -121,12 +82,3 @@
|
||||
[]
|
||||
(when-let [model (get-idevice-model)]
|
||||
(string/starts-with? (first model) "iPad")))
|
||||
|
||||
(defn get-idevice-statusbar-height
|
||||
[]
|
||||
(let [[model landscape?] (get-idevice-model)
|
||||
model (when-not (= model "Not a known Apple device!")
|
||||
(keyword model))]
|
||||
(if (and model landscape?)
|
||||
20
|
||||
(:statusbar (model @idevice-info)))))
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
:initialScope {:tags
|
||||
{:platform (cond
|
||||
(util/electron?) "electron"
|
||||
(mobile-util/is-native-platform?) "mobile"
|
||||
(mobile-util/native-platform?) "mobile"
|
||||
:else "web")
|
||||
:publishing config/publishing?}}
|
||||
:integrations [(new posthog/SentryIntegration posthog "logseq" 5311485)
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
|
||||
(-get-parent-id [this]
|
||||
(-> (get-in this [:data :block/parent])
|
||||
(outliner-u/->block-id)))
|
||||
(outliner-u/->block-id)))
|
||||
|
||||
(-set-parent-id [this parent-id]
|
||||
(outliner-u/check-block-id parent-id)
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
(-get-left-id [this]
|
||||
(-> (get-in this [:data :block/left])
|
||||
(outliner-u/->block-id)))
|
||||
(outliner-u/->block-id)))
|
||||
|
||||
(-set-left-id [this left-id]
|
||||
(outliner-u/check-block-id left-id)
|
||||
@@ -170,7 +170,7 @@
|
||||
|
||||
(-del [this txs-state children?]
|
||||
(assert (ds/outliner-txs-state? txs-state)
|
||||
"db should be satisfied outliner-tx-state?")
|
||||
"db should be satisfied outliner-tx-state?")
|
||||
(let [block-id (tree/-get-id this)
|
||||
ids (set (if children?
|
||||
(let [children (db/get-block-children (state/get-current-repo) block-id)
|
||||
@@ -192,7 +192,7 @@
|
||||
(assoc :block/left parent))))
|
||||
immediate-children)))
|
||||
txs))
|
||||
txs)]
|
||||
txs)]
|
||||
(swap! txs-state concat txs)
|
||||
block-id))
|
||||
|
||||
@@ -209,12 +209,7 @@
|
||||
(defn get-right-sibling
|
||||
[db-id]
|
||||
(when db-id
|
||||
(when-let [block (db/entity db-id)]
|
||||
(db-model/get-by-parent-&-left (conn/get-db)
|
||||
(:db/id (:block/parent block))
|
||||
db-id))))
|
||||
|
||||
|
||||
(db-model/get-right-sibling (conn/get-db) db-id)))
|
||||
|
||||
(defn- assoc-level-aux
|
||||
[tree-vec children-key init-level]
|
||||
@@ -285,13 +280,13 @@
|
||||
(loop [node node
|
||||
limit limit
|
||||
result []]
|
||||
(if (zero? limit)
|
||||
result
|
||||
(if-let [left (tree/-get-left node)]
|
||||
(if-not (= left parent)
|
||||
(recur left (dec limit) (conj result (tree/-get-id left)))
|
||||
result)
|
||||
result)))))
|
||||
(if (zero? limit)
|
||||
result
|
||||
(if-let [left (tree/-get-left node)]
|
||||
(if-not (= left parent)
|
||||
(recur left (dec limit) (conj result (tree/-get-id left)))
|
||||
result)
|
||||
result)))))
|
||||
|
||||
(defn- page-first-child?
|
||||
[block]
|
||||
@@ -494,6 +489,48 @@
|
||||
{:tx-data full-tx
|
||||
:blocks tx}))))
|
||||
|
||||
(defn- build-move-blocks-next-tx
|
||||
[blocks]
|
||||
(let [id->blocks (zipmap (map :db/id blocks) blocks)
|
||||
top-level-blocks (get-top-level-blocks blocks)
|
||||
top-level-blocks-ids (set (map :db/id top-level-blocks))
|
||||
right-block (get-right-sibling (:db/id (last top-level-blocks)))]
|
||||
(when (and right-block
|
||||
(not (contains? top-level-blocks-ids (:db/id right-block))))
|
||||
{:db/id (:db/id right-block)
|
||||
:block/left (loop [block (:block/left right-block)]
|
||||
(if (contains? top-level-blocks-ids (:db/id block))
|
||||
(recur (:block/left (get id->blocks (:db/id block))))
|
||||
(:db/id block)))})))
|
||||
|
||||
(defn- find-new-left
|
||||
[block moved-ids target-block current-block sibling?]
|
||||
(if (= (:db/id target-block) (:db/id (:block/left current-block)))
|
||||
(if sibling?
|
||||
(db/entity (last moved-ids))
|
||||
target-block)
|
||||
(let [left (db/entity (:db/id (:block/left block)))]
|
||||
(if (contains? (set moved-ids) (:db/id left))
|
||||
(find-new-left left moved-ids target-block current-block sibling?)
|
||||
left))))
|
||||
|
||||
(defn- fix-non-consecutive-blocks
|
||||
[blocks target-block sibling?]
|
||||
(let [page-blocks (group-by :block/page blocks)]
|
||||
(->>
|
||||
(mapcat (fn [[_page blocks]]
|
||||
(let [blocks (db-model/sort-page-random-blocks blocks)
|
||||
non-consecutive-blocks (->> (conj (db-model/get-non-consecutive-blocks blocks) (last blocks))
|
||||
(util/distinct-by :db/id))]
|
||||
(when (seq non-consecutive-blocks)
|
||||
(mapv (fn [block]
|
||||
(when-let [right (get-right-sibling (:db/id block))]
|
||||
(when-let [new-left (find-new-left right (distinct (map :db/id blocks)) target-block block sibling?)]
|
||||
{:db/id (:db/id right)
|
||||
:block/left (:db/id new-left)})))
|
||||
non-consecutive-blocks)))) page-blocks)
|
||||
(remove nil?))))
|
||||
|
||||
(defn- delete-block
|
||||
"Delete block from the tree."
|
||||
[txs-state block' children?]
|
||||
@@ -556,23 +593,11 @@
|
||||
(tree/-save new-right-node txs-state))))
|
||||
(doseq [id block-ids]
|
||||
(let [node (block (db/pull id))]
|
||||
(tree/-del node txs-state true)))))
|
||||
(tree/-del node txs-state true)))
|
||||
(let [fix-non-consecutive-tx (fix-non-consecutive-blocks blocks nil false)]
|
||||
(swap! txs-state concat fix-non-consecutive-tx))))
|
||||
{:tx-data @txs-state}))
|
||||
|
||||
(defn- build-move-blocks-next-tx
|
||||
[blocks]
|
||||
(let [id->blocks (zipmap (map :db/id blocks) blocks)
|
||||
top-level-blocks (get-top-level-blocks blocks)
|
||||
top-level-blocks-ids (set (map :db/id top-level-blocks))
|
||||
right-block (get-right-sibling (:db/id (last top-level-blocks)))]
|
||||
(when (and right-block
|
||||
(not (contains? top-level-blocks-ids (:db/id right-block))))
|
||||
{:db/id (:db/id right-block)
|
||||
:block/left (loop [block (:block/left right-block)]
|
||||
(if (contains? top-level-blocks-ids (:db/id block))
|
||||
(recur (:block/left (get id->blocks (:db/id block))))
|
||||
(:db/id block)))})))
|
||||
|
||||
(defn move-blocks
|
||||
"Move `blocks` to `target-block` as siblings or children."
|
||||
[blocks target-block {:keys [sibling? outliner-op]}]
|
||||
@@ -598,7 +623,8 @@
|
||||
(let [children-ids (mapcat #(db/get-block-children-ids (state/get-current-repo) (:block/uuid %)) blocks)]
|
||||
(map (fn [uuid] {:block/uuid uuid
|
||||
:block/page target-page}) children-ids)))
|
||||
full-tx (util/concat-without-nil tx-data move-blocks-next-tx children-page-tx)
|
||||
fix-non-consecutive-tx (fix-non-consecutive-blocks blocks target-block sibling?)
|
||||
full-tx (util/concat-without-nil tx-data move-blocks-next-tx children-page-tx fix-non-consecutive-tx)
|
||||
tx-meta (cond-> {:move-blocks (mapv :db/id blocks)
|
||||
:target (:db/id target-block)}
|
||||
not-same-page?
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
[]
|
||||
(try
|
||||
(comp
|
||||
(ui/setup-active-keystroke!)
|
||||
(ui/setup-patch-ios-visual-viewport-state!))
|
||||
(ui/setup-active-keystroke!))
|
||||
(catch js/Error _e
|
||||
nil)))
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
:modal/close-btn? nil
|
||||
:modal/subsets []
|
||||
|
||||
|
||||
;; right sidebar
|
||||
:ui/fullscreen? false
|
||||
:ui/settings-open? false
|
||||
@@ -88,9 +89,6 @@
|
||||
:ui/shortcut-tooltip? (if (false? (storage/get :ui/shortcut-tooltip?))
|
||||
false
|
||||
true)
|
||||
:ui/visual-viewport-pending? false
|
||||
:ui/visual-viewport-state nil
|
||||
|
||||
:document/mode? document-mode?
|
||||
|
||||
:config {}
|
||||
@@ -114,7 +112,6 @@
|
||||
:editor/args nil
|
||||
:editor/on-paste? false
|
||||
:editor/last-key-code nil
|
||||
:editor/editing-page-title? false
|
||||
|
||||
;; for audio record
|
||||
:editor/record-status "NONE"
|
||||
@@ -146,6 +143,13 @@
|
||||
:electron/updater {}
|
||||
:electron/user-cfgs nil
|
||||
|
||||
;; mobile
|
||||
:mobile/show-toolbar? false
|
||||
;;; toolbar icon doesn't update correctly when clicking after separate it from box,
|
||||
;;; add a random in (<= 1000000) to observer its update
|
||||
:mobile/toolbar-update-observer 0
|
||||
:mobile/show-tabbar? false
|
||||
|
||||
;; plugin
|
||||
:plugin/enabled (and (util/electron?)
|
||||
;; true false :theme-only
|
||||
@@ -289,7 +293,7 @@
|
||||
(defn get-current-repo
|
||||
[]
|
||||
(or (:git/current-repo @state)
|
||||
(when-not (mobile-util/is-native-platform?)
|
||||
(when-not (mobile-util/native-platform?)
|
||||
"local")))
|
||||
|
||||
(defn get-config
|
||||
@@ -853,10 +857,7 @@
|
||||
(util/set-change-value input content))
|
||||
|
||||
(when move-cursor?
|
||||
(cursor/move-cursor-to input pos))
|
||||
|
||||
(when (or (util/mobile?) (mobile-util/is-native-platform?))
|
||||
(util/make-el-center-if-near-top input))))))))
|
||||
(cursor/move-cursor-to input pos))))))))
|
||||
|
||||
(defn clear-edit!
|
||||
[]
|
||||
@@ -1193,7 +1194,7 @@
|
||||
|
||||
(defn enable-tooltip?
|
||||
[]
|
||||
(if (or (util/mobile?) (mobile-util/is-native-platform?))
|
||||
(if (or (util/mobile?) (mobile-util/native-platform?))
|
||||
false
|
||||
(get (get (sub-config) (get-current-repo))
|
||||
:ui/enable-tooltip?
|
||||
@@ -1547,14 +1548,6 @@
|
||||
[]
|
||||
(:editor/last-key-code @state))
|
||||
|
||||
(defn set-visual-viewport-state
|
||||
[input]
|
||||
(set-state! :ui/visual-viewport-state input))
|
||||
|
||||
(defn get-visual-viewport-state
|
||||
[]
|
||||
(:ui/visual-viewport-state @state))
|
||||
|
||||
(defn get-plugin-by-id
|
||||
[id]
|
||||
(when-let [id (and id (keyword id))]
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
(util/safari?)
|
||||
(js/window.scrollTo 0 0)))
|
||||
|
||||
(defonce icon-size (if (mobile-util/is-native-platform?) 23 20))
|
||||
(defonce icon-size (if (mobile-util/native-platform?) 23 20))
|
||||
|
||||
(rum/defc ls-textarea
|
||||
< rum/reactive
|
||||
@@ -290,40 +290,6 @@
|
||||
(.appendChild js/document.head node))
|
||||
style)))
|
||||
|
||||
(defn setup-patch-ios-visual-viewport-state!
|
||||
[]
|
||||
(when-let [^js vp (and (or (and (util/mobile?) (util/safari?))
|
||||
(mobile-util/native-ios?))
|
||||
js/window.visualViewport)]
|
||||
(let [raf-pending? (atom false)
|
||||
set-raf-pending! #(reset! raf-pending? %)
|
||||
on-viewport-changed
|
||||
(fn []
|
||||
(let [update-vw-state
|
||||
(debounce
|
||||
(fn []
|
||||
(state/set-visual-viewport-state {:height (.-height vp)
|
||||
:page-top (.-pageTop vp)
|
||||
:offset-top (.-offsetTop vp)})
|
||||
(state/set-state! :ui/visual-viewport-pending? false))
|
||||
20)]
|
||||
(when-not @raf-pending?
|
||||
(let [f (fn []
|
||||
(set-raf-pending! false)
|
||||
(update-vw-state))]
|
||||
(set-raf-pending! true)
|
||||
(state/set-state! :ui/visual-viewport-pending? true)
|
||||
(js/window.requestAnimationFrame f)))))]
|
||||
|
||||
(.addEventListener vp "resize" on-viewport-changed)
|
||||
(.addEventListener vp "scroll" on-viewport-changed)
|
||||
|
||||
(fn []
|
||||
(.removeEventListener vp "resize" on-viewport-changed)
|
||||
(.removeEventListener vp "scroll" on-viewport-changed)
|
||||
(state/set-visual-viewport-state nil))))
|
||||
#())
|
||||
|
||||
(defn apply-custom-theme-effect! [theme]
|
||||
(when plugin-handler/lsp-enabled?
|
||||
(when-let [custom-theme (state/sub [:ui/custom-theme (keyword theme)])]
|
||||
@@ -684,7 +650,7 @@
|
||||
(assoc :on-mouse-down on-mouse-down
|
||||
:class "cursor"))
|
||||
[:div.flex.flex-row.items-center
|
||||
(when-not (mobile-util/is-native-platform?)
|
||||
(when-not (mobile-util/native-platform?)
|
||||
[:a.block-control.opacity-50.hover:opacity-100.mr-2
|
||||
(cond->
|
||||
{:style {:width 14
|
||||
@@ -953,7 +919,7 @@
|
||||
(rum/local true ::active?)
|
||||
[state content-fn sensor-opts {:keys [reset-height? once?]}]
|
||||
(let [*active? (::active? state)]
|
||||
(if (or (util/mobile?) (mobile-util/is-native-platform?))
|
||||
(if (or (util/mobile?) (mobile-util/native-platform?))
|
||||
(content-fn)
|
||||
(let [*visible? (::visible? state)]
|
||||
(visibility-sensor
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
[cljs-time.coerce :as tc]
|
||||
[cljs-time.core :as t]
|
||||
[dommy.core :as d]
|
||||
[frontend.mobile.util :refer [is-native-platform?]]
|
||||
[frontend.mobile.util :refer [native-platform?]]
|
||||
[logseq.graph-parser.util :as gp-util]
|
||||
[goog.dom :as gdom]
|
||||
[goog.object :as gobj]
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
#?(:cljs
|
||||
(def nfs? (and (not (electron?))
|
||||
(not (is-native-platform?)))))
|
||||
(not (native-platform?)))))
|
||||
|
||||
#?(:cljs
|
||||
(defn file-protocol?
|
||||
@@ -1040,29 +1040,6 @@
|
||||
(string/replace """ "\"")
|
||||
(string/replace "'" "'")))
|
||||
|
||||
#?(:cljs
|
||||
(defn system-locales
|
||||
[]
|
||||
(when-not node-test?
|
||||
(when-let [navigator (and js/window (.-navigator js/window))]
|
||||
;; https://zzz.buzz/2016/01/13/detect-browser-language-in-javascript/
|
||||
(when navigator
|
||||
(let [v (js->clj
|
||||
(or
|
||||
(.-languages navigator)
|
||||
(.-language navigator)
|
||||
(.-userLanguage navigator)
|
||||
(.-browserLanguage navigator)
|
||||
(.-systemLanguage navigator)))]
|
||||
(if (string? v) [v] v)))))))
|
||||
|
||||
#?(:cljs
|
||||
(defn zh-CN-supported?
|
||||
[]
|
||||
(let [system-locales (set (system-locales))]
|
||||
(or (contains? system-locales "zh-CN")
|
||||
(contains? system-locales "zh-Hans-CN")))))
|
||||
|
||||
(comment
|
||||
(= (get-relative-path "journals/2020_11_18.org" "pages/grant_ideas.org")
|
||||
"../pages/grant_ideas.org")
|
||||
@@ -1216,6 +1193,12 @@
|
||||
(defn meta-key-name []
|
||||
(if mac? "Cmd" "Ctrl")))
|
||||
|
||||
#?(:cljs
|
||||
(defn meta-key? [e]
|
||||
(if mac?
|
||||
(gobj/get e "metaKey")
|
||||
(gobj/get e "ctrlKey"))))
|
||||
|
||||
#?(:cljs
|
||||
(defn right-click?
|
||||
[e]
|
||||
@@ -1224,42 +1207,53 @@
|
||||
(or (= which 3)
|
||||
(= button 2)))))
|
||||
|
||||
(def keyboard-height (atom nil))
|
||||
#?(:cljs
|
||||
(defn make-el-into-center-viewport
|
||||
[^js/HTMLElement el]
|
||||
(when el
|
||||
(.scrollIntoView el #js {:block "center" :behavior "smooth"}))))
|
||||
(defn scroll-editor-cursor
|
||||
[^js/HTMLElement el & {:keys [to-vw-one-quarter?]}]
|
||||
(when (and el (or (native-platform?) mobile?))
|
||||
(let [box-rect (.getBoundingClientRect el)
|
||||
box-top (.-top box-rect)
|
||||
box-bottom (.-bottom box-rect)
|
||||
|
||||
#?(:cljs
|
||||
(defn make-el-cursor-position-into-center-viewport
|
||||
[^js/HTMLElement el]
|
||||
(when el
|
||||
(let [main-node (gdom/getElement "main-content-container")
|
||||
pos (get-selection-start el)
|
||||
cursor-top (some-> (gdom/getElement "mock-text")
|
||||
gdom/getChildren
|
||||
array-seq
|
||||
(nth-safe pos)
|
||||
.-offsetTop)
|
||||
box-caret (.getBoundingClientRect el)
|
||||
box-top (.-top box-caret)
|
||||
box-bottom (.-bottom box-caret)
|
||||
vw-height (or (.-height js/window.visualViewport)
|
||||
(.-clientHeight js/document.documentElement))
|
||||
scroll-top (.-scrollTop main-node)
|
||||
cursor-y (if cursor-top (+ cursor-top box-top) box-bottom)
|
||||
scroll (- cursor-y (/ vw-height 2))]
|
||||
(when (> scroll 0)
|
||||
(set! (.-scrollTop main-node) (+ scroll-top scroll)))))))
|
||||
header-height (-> (gdom/getElementByClass "cp__header")
|
||||
.-clientHeight)
|
||||
|
||||
#?(:cljs
|
||||
(defn make-el-center-if-near-top
|
||||
([^js/HTMLElement el]
|
||||
(make-el-center-if-near-top el 80))
|
||||
([^js/HTMLElement el offset]
|
||||
(let [target-top (.-top (.getBoundingClientRect el))]
|
||||
(when (<= target-top (or (safe-parse-int offset) 0))
|
||||
(make-el-into-center-viewport el))))))
|
||||
main-node (app-scroll-container-node)
|
||||
scroll-top (.-scrollTop main-node)
|
||||
|
||||
current-pos (get-selection-start el)
|
||||
mock-text (some-> (gdom/getElement "mock-text")
|
||||
gdom/getChildren
|
||||
array-seq
|
||||
(nth-safe current-pos))
|
||||
offset-top (and mock-text (.-offsetTop mock-text))
|
||||
offset-height (and mock-text (.-offsetHeight mock-text))
|
||||
|
||||
cursor-y (if offset-top (+ offset-top box-top offset-height 2) box-bottom)
|
||||
vw-height (or (.-height js/window.visualViewport)
|
||||
(.-clientHeight js/document.documentElement))
|
||||
;; mobile toolbar height: 40px
|
||||
scroll (- cursor-y (- vw-height (+ @keyboard-height 40)))]
|
||||
(cond
|
||||
(and to-vw-one-quarter? (> cursor-y (* vw-height 0.4)))
|
||||
(set! (.-scrollTop main-node) (+ scroll-top (- cursor-y (/ vw-height 4))))
|
||||
|
||||
(and (< cursor-y (+ header-height offset-height 4)) ;; 4 is top+bottom padding for per line
|
||||
(>= cursor-y header-height))
|
||||
(.scrollBy main-node (bean/->js {:top (- (+ offset-height 4))}))
|
||||
|
||||
(< cursor-y header-height)
|
||||
(let [_ (.scrollIntoView el true)
|
||||
main-node (app-scroll-container-node)
|
||||
scroll-top (.-scrollTop main-node)]
|
||||
(set! (.-scrollTop main-node) (- scroll-top (/ vw-height 4))))
|
||||
|
||||
(> scroll 0)
|
||||
(set! (.-scrollTop main-node) (+ scroll-top scroll))
|
||||
|
||||
:else
|
||||
nil)))))
|
||||
|
||||
#?(:cljs
|
||||
(defn sm-breakpoint?
|
||||
|
||||
@@ -4,17 +4,22 @@
|
||||
|
||||
;; TODO: move all file path related util functions to here
|
||||
|
||||
;; keep same as ignored-path? in src/electron/electron/utils.cljs
|
||||
;; TODO: merge them
|
||||
;; NOTE: This is not the same ignored-path? as src/electron/electron/utils.cljs.
|
||||
;; The assets directory is ignored.
|
||||
;;
|
||||
;; When in nfs-mode, dir is "", path is relative path to graph dir.
|
||||
;; When in native-mode, dir and path are absolute paths.
|
||||
(defn ignored-path?
|
||||
"Ignore path for ls-dir-files-with-handler! and reload-dir!"
|
||||
[dir path]
|
||||
(when (string? path)
|
||||
(or
|
||||
(some #(string/starts-with? path (str dir "/" %))
|
||||
["." ".recycle" "assets" "node_modules" "logseq/bak"])
|
||||
["." ".recycle" "assets" "node_modules" "logseq/bak" "version-files"])
|
||||
(some #(string/includes? path (str "/" % "/"))
|
||||
["." ".recycle" "assets" "node_modules" "logseq/bak"])
|
||||
(string/ends-with? path ".DS_Store")
|
||||
["." ".recycle" "assets" "node_modules" "logseq/bak" "version-files"])
|
||||
(some #(string/ends-with? path %)
|
||||
[".DS_Store" "logseq/graphs-txid.edn" "logseq/broken-config.edn"])
|
||||
;; hidden directory or file
|
||||
(let [relpath (path/relative dir path)]
|
||||
(or (re-find #"/\.[^.]+" relpath)
|
||||
@@ -24,4 +29,4 @@
|
||||
(not (string/blank? (path/extname path)))
|
||||
(not
|
||||
(some #(string/ends-with? path %)
|
||||
[".md" ".markdown" ".org" ".js" ".edn" ".css"])))))))
|
||||
[".md" ".markdown" ".org" ".js" ".edn" ".css"])))))))
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
:name name
|
||||
:end (+ (:end admonition&src) (count name))))))))
|
||||
|
||||
(defn- markdown-src-at-point [& [input]]
|
||||
(defn markdown-src-at-point [& [input]]
|
||||
(when-let [markdown-src (thing-at-point ["```" "```"] input)]
|
||||
(let [language (-> (:full-content markdown-src)
|
||||
string/split-lines
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
(ns frontend.version)
|
||||
|
||||
(defonce version "0.6.9")
|
||||
(defonce version "0.6.10")
|
||||
|
||||
@@ -122,6 +122,18 @@
|
||||
[s]
|
||||
(string/split s #"(\"[^\"]*\")"))
|
||||
|
||||
(def bilibili-regex #"^((?:https?:)?//)?((?:www).)?((?:bilibili.com))(/(?:video/)?)([\w-]+)(\S+)?$")
|
||||
(def loom-regex #"^((?:https?:)?//)?((?:www).)?((?:loom.com))(/(?:share/|embed/))([\w-]+)(\S+)?$")
|
||||
(def vimeo-regex #"^((?:https?:)?//)?((?:www).)?((?:player.vimeo.com|vimeo.com))(/(?:video/)?)([\w-]+)(\S+)?$")
|
||||
(def youtube-regex #"^((?:https?:)?//)?((?:www|m).)?((?:youtube.com|youtu.be|y2u.be|youtube-nocookie.com))(/(?:[\w-]+\?v=|embed/|v/)?)([\w-]+)([\S^\?]+)?$")
|
||||
|
||||
(defn get-matched-video
|
||||
[url]
|
||||
(or (re-find youtube-regex url)
|
||||
(re-find loom-regex url)
|
||||
(re-find vimeo-regex url)
|
||||
(re-find bilibili-regex url)))
|
||||
|
||||
(def markdown-link #"\[([^\[]+)\](\(.*\))")
|
||||
|
||||
(defn split-page-refs-without-brackets
|
||||
|
||||
Reference in New Issue
Block a user