Files
logseq/src/main/logseq/api.cljs
Tienson Qin 31fee024f6 fix: typo
2022-08-11 20:44:16 +08:00

866 lines
32 KiB
Clojure

(ns ^:no-doc logseq.api
(:require [camel-snake-kebab.core :as csk]
[cljs-bean.core :as bean]
[cljs.reader]
[clojure.string :as string]
[clojure.walk :as walk]
[datascript.core :as d]
[electron.ipc :as ipc]
[frontend.commands :as commands]
[frontend.components.plugins :as plugins]
[frontend.config :as config]
[frontend.db :as db]
[frontend.db.model :as db-model]
[frontend.db.query-dsl :as query-dsl]
[frontend.db.utils :as db-utils]
[frontend.db.query-react :as query-react]
[frontend.fs :as fs]
[frontend.handler.dnd :as editor-dnd-handler]
[frontend.handler.editor :as editor-handler]
[frontend.handler.export :as export-handler]
[frontend.handler.notification :as notification]
[frontend.handler.page :as page-handler]
[frontend.handler.plugin :as plugin-handler]
[frontend.modules.outliner.core :as outliner]
[frontend.modules.outliner.tree :as outliner-tree]
[frontend.handler.command-palette :as palette-handler]
[frontend.modules.shortcut.core :as st]
[electron.listener :as el]
[frontend.state :as state]
[frontend.util :as util]
[frontend.util.cursor :as cursor]
[frontend.loader :as loader]
[goog.dom :as gdom]
[lambdaisland.glogi :as log]
[promesa.core :as p]
[reitit.frontend.easy :as rfe]
[sci.core :as sci]
[frontend.version :as fv]
[frontend.handler.shell :as shell]
[frontend.modules.layout.core]))
;; helpers
(defn- normalize-keyword-for-json
[input]
(when input
(walk/postwalk
(fn [a]
(cond
(keyword? a) (csk/->camelCase (name a))
(uuid? a) (str a)
:else a)) input)))
(defn- parse-hiccup-ui
[input]
(when (string? input)
(try
(sci/eval-string input {:preset :termination-safe})
(catch js/Error e
(js/console.error "[parse hiccup error]" e) input))))
(defn ^:export install-plugin-hook
[pid hook]
(state/install-plugin-hook pid hook))
(defn ^:export uninstall-plugin-hook
[pid hook-or-all]
(state/uninstall-plugin-hook pid hook-or-all))
(defn ^:export should-exec-plugin-hook
[pid hook]
(let [hooks (:plugin/installed-hooks @state/state)]
(or (nil? (seq hooks))
(contains? (get hooks hook) (keyword pid)))))
;; base
(defn ^:export get_state_from_store
[^js path]
(when-let [path (if (string? path) [path] (bean/->clj path))]
(->> path
(map #(if (string/starts-with? % "@")
(subs % 1)
(keyword %)))
(get-in @state/state))))
(defn ^:export get_app_info
;; get app base info
[]
(bean/->js
(normalize-keyword-for-json
{:version fv/version})))
(def ^:export get_user_configs
(fn []
(bean/->js
(normalize-keyword-for-json
{:preferred-language (:preferred-language @state/state)
:preferred-theme-mode (:ui/theme @state/state)
:preferred-format (state/get-preferred-format)
:preferred-workflow (state/get-preferred-workflow)
:preferred-todo (state/get-preferred-todo)
:preferred-date-format (state/get-date-formatter)
:preferred-start-of-week (state/get-start-of-week)
:current-graph (state/get-current-repo)
:show-brackets (state/show-brackets?)
:me (state/get-me)}))))
(def ^:export get_current_graph
(fn []
(when-let [repo (state/get-current-repo)]
(when-not (= config/local-repo repo)
(bean/->js {:url repo
:name (util/node-path.basename repo)
:path (config/get-repo-dir repo)})))))
(def ^:export show_themes
(fn []
(plugins/open-select-theme!)))
(def ^:export set_theme_mode
(fn [mode]
(state/set-theme-mode! mode)))
(def ^:export load_plugin_config
(fn [path]
(fs/read-file "" (util/node-path.join path "package.json"))))
(def ^:export load_plugin_readme
(fn [path]
(fs/read-file "" (util/node-path.join path "readme.md"))))
(def ^:export save_plugin_config
(fn [path ^js data]
(let [repo ""
path (util/node-path.join path "package.json")]
(fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true}))))
(defn ^:private write_dotdir_file
[file content sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
exist? (fs/file-exists? path "")
_ (when-not exist? (fs/mkdir-recur! path))
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (when-not sub-dir?
(log/info :debug user-path)
(throw "write file denied"))
user-path-root (util/node-path.dirname user-path)
exist? (fs/file-exists? user-path-root "")
_ (when-not exist? (fs/mkdir-recur! user-path-root))
_ (fs/write-file! repo "" user-path content {:skip-compare? true})]
user-path))
(defn ^:private read_dotdir_file
[file sub-root]
(p/let [path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (when-not sub-dir? (log/info :debug user-path) (throw "read file denied"))
exist? (fs/file-exists? "" user-path)
_ (when-not exist? (log/info :debug user-path) (throw "file not existed"))
content (fs/read-file "" user-path)]
content))
(defn ^:private unlink_dotdir_file!
[file sub-root]
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path sub-root)
user-path (util/node-path.join path file)
sub-dir? (string/starts-with? user-path path)
_ (when-not sub-dir? (log/info :debug user-path) (throw "access file denied"))
exist? (fs/file-exists? "" user-path)
_ (when-not exist? (log/info :debug user-path) (throw "file not existed"))
_ (fs/unlink! repo user-path {})]))
(def ^:export write_user_tmp_file
(fn [file content]
(write_dotdir_file file content "tmp")))
(def ^:export write_plugin_storage_file
(fn [plugin-id file content]
(write_dotdir_file
file content
(let [plugin-id (util/node-path.basename plugin-id)]
(util/node-path.join "storages" plugin-id)))))
(def ^:export read_plugin_storage_file
(fn [plugin-id file]
(let [plugin-id (util/node-path.basename plugin-id)]
(read_dotdir_file
file (util/node-path.join "storages" plugin-id)))))
(def ^:export unlink_plugin_storage_file
(fn [plugin-id file]
(let [plugin-id (util/node-path.basename plugin-id)]
(unlink_dotdir_file!
file (util/node-path.join "storages" plugin-id)))))
(def ^:export exist_plugin_storage_file
(fn [plugin-id file]
(p/let [root (plugin-handler/get-ls-dotdir-root)
plugin-id (util/node-path.basename plugin-id)
exist? (fs/file-exists?
(util/node-path.join root "storages" plugin-id)
file)]
exist?)))
(def ^:export clear_plugin_storage_files
(fn [plugin-id]
(p/let [root (plugin-handler/get-ls-dotdir-root)
plugin-id (util/node-path.basename plugin-id)]
(fs/rmdir! (util/node-path.join root "storages" plugin-id)))))
(def ^:export load_user_preferences
(fn []
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path "preferences.json")
_ (fs/create-if-not-exists repo "" path)
json (fs/read-file "" path)
json (if (string/blank? json) "{}" json)]
(js/JSON.parse json))))
(def ^:export save_user_preferences
(fn [^js data]
(when data
(p/let [repo ""
path (plugin-handler/get-ls-dotdir-root)
path (util/node-path.join path "preferences.json")]
(fs/write-file! repo "" path (js/JSON.stringify data nil 2) {:skip-compare? true})))))
(def ^:export load_plugin_user_settings
;; results [path data]
(plugin-handler/make-fn-to-load-dotdir-json "settings" "{}"))
(def ^:export save_plugin_user_settings
(fn [key ^js data]
((plugin-handler/make-fn-to-save-dotdir-json "settings")
key (js/JSON.stringify data nil 2))))
(def ^:export unlink_plugin_user_settings
(plugin-handler/make-fn-to-unlink-dotdir-json "settings"))
(def ^:export register_plugin_slash_command
(fn [pid ^js cmd-actions]
(when-let [[cmd actions] (bean/->clj cmd-actions)]
(plugin-handler/register-plugin-slash-command
pid [cmd (mapv #(into [(keyword (first %))]
(rest %)) actions)]))))
(def ^:export register_plugin_simple_command
(fn [pid ^js cmd-action palette?]
(when-let [[cmd action] (bean/->clj cmd-action)]
(let [action (assoc action 0 (keyword (first action)))
cmd (assoc cmd :key (string/replace (:key cmd) ":" "-"))
key (:key cmd)
keybinding (:keybinding cmd)
palette-cmd (and palette? (plugin-handler/simple-cmd->palette-cmd pid cmd action))
action' #(state/pub-event! [:exec-plugin-cmd {:type type :key key :pid pid :cmd cmd :action action}])]
;; handle simple commands
(plugin-handler/register-plugin-simple-command pid cmd action)
;; handle palette commands
(when palette?
(palette-handler/register palette-cmd))
;; handle keybinding commands
(when-let [shortcut-args (and keybinding (plugin-handler/simple-cmd-keybinding->shortcut-args pid key keybinding))]
(let [dispatch-cmd (fn [_e]
(if palette?
(palette-handler/invoke-command palette-cmd)
(action')))
[handler-id id shortcut-map] (update shortcut-args 2 assoc :fn dispatch-cmd)]
(js/console.debug :shortcut/register-shortcut [handler-id id shortcut-map])
(st/register-shortcut! handler-id id shortcut-map)))))))
(defn ^:export unregister_plugin_simple_command
[pid]
;; remove simple commands
(plugin-handler/unregister-plugin-simple-command pid)
;; remove palette commands
(let [palette-matched (->> (palette-handler/get-commands)
(filter #(string/includes? (str (:id %)) (str "plugin." pid))))]
(when (seq palette-matched)
(doseq [cmd palette-matched]
(palette-handler/unregister (:id cmd))
;; remove keybinding commands
(when (seq (:shortcut cmd))
(js/console.debug :shortcut/unregister-shortcut cmd)
(st/unregister-shortcut! (:handler-id cmd) (:id cmd)))))))
(def ^:export register_plugin_ui_item
(fn [pid type ^js opts]
(when-let [opts (bean/->clj opts)]
(plugin-handler/register-plugin-ui-item
pid (assoc opts :type type)))))
;; app
(def ^:export relaunch
(fn []
(ipc/ipc "relaunchApp")))
(def ^:export quit
(fn []
(ipc/ipc "quitApp")))
(def ^:export open_external_link
(fn [url]
(when (re-find #"https?://" url)
(js/apis.openExternal url))))
(def ^:export invoke_external_command
(fn [type & args]
(when-let [id (and (string/starts-with? type "logseq.")
(-> (string/replace type #"^logseq." "")
(util/safe-lower-case)
(keyword)))]
(when-let [action (get-in (palette-handler/get-commands-unique) [id :action])]
(apply action args)))))
;; flag - boolean | 'toggle'
(def ^:export set_left_sidebar_visible
(fn [flag]
(if (= flag "toggle")
(state/toggle-left-sidebar!)
(state/set-state! :ui/left-sidebar-open? (boolean flag)))
nil))
;; flag - boolean | 'toggle'
(def ^:export set_right_sidebar_visible
(fn [flag]
(if (= flag "toggle")
(state/toggle-sidebar-open?!)
(state/set-state! :ui/sidebar-open? (boolean flag)))
nil))
(def ^:export push_state
(fn [^js k ^js params ^js query]
(rfe/push-state
(keyword k)
(bean/->clj params)
(bean/->clj query))))
(def ^:export replace_state
(fn [^js k ^js params ^js query]
(rfe/replace-state
(keyword k)
(bean/->clj params)
(bean/->clj query))))
;; editor
(def ^:export check_editing
(fn []
(if (state/get-edit-input-id)
(str (:block/uuid (state/get-edit-block))) false)))
(def ^:export exit_editing_mode
(fn [select?]
(editor-handler/escape-editing select?)
nil))
(def ^:export insert_at_editing_cursor
(fn [content]
(when-let [input-id (state/get-edit-input-id)]
(commands/simple-insert! input-id content {})
(.focus (gdom/getElement input-id)))))
(def ^:export restore_editing_cursor
(fn []
(when-let [input-id (state/get-edit-input-id)]
(.focus (gdom/getElement input-id)))))
(def ^:export get_editing_cursor_position
(fn []
(when-let [input-id (state/get-edit-input-id)]
(bean/->js (normalize-keyword-for-json (cursor/get-caret-pos (gdom/getElement input-id)))))))
(def ^:export get_editing_block_content
(fn []
(state/get-edit-content)))
(def ^:export get_selected_blocks
(fn []
(when-let [blocks (and (state/in-selection-mode?)
(seq (state/get-selection-blocks)))]
(let [blocks (->> blocks
(map (fn [^js el] (some-> (.getAttribute el "blockid")
(db-model/query-block-by-uuid)))))]
(bean/->js (normalize-keyword-for-json blocks))))))
(def ^:export get_current_page
(fn []
(when-let [page (state/get-current-page)]
(when-let [page (db-model/get-page page)]
(bean/->js (normalize-keyword-for-json (db-utils/pull (:db/id page))))))))
(def ^:export get_page
(fn [id-or-page-name]
(when-let [page (cond
(number? id-or-page-name) (db-utils/pull id-or-page-name)
(string? id-or-page-name) (db-model/get-page id-or-page-name))]
(when-not (contains? page :block/left)
(bean/->js (normalize-keyword-for-json (db-utils/pull (:db/id page))))))))
(def ^:export get_all_pages
(fn [repo]
(let [pages (page-handler/get-all-pages repo)]
(bean/->js (normalize-keyword-for-json pages)))))
(def ^:export create_page
(fn [name ^js properties ^js opts]
(some-> (if-let [page (db-model/get-page name)]
page
(let [properties (bean/->clj properties)
{:keys [redirect createFirstBlock format journal]} (bean/->clj opts)
name (page-handler/create!
name
{:redirect? (if (boolean? redirect) redirect true)
:journal? journal
:create-first-block? (if (boolean? createFirstBlock) createFirstBlock true)
:format format
:properties properties})]
(db-model/get-page name)))
(:db/id)
(db-utils/pull)
(normalize-keyword-for-json)
(bean/->js))))
(def ^:export delete_page
(fn [name]
(p/create (fn [ok] (page-handler/delete! name ok)))))
(def ^:export rename_page
page-handler/rename!)
(defn ^:export open_in_right_sidebar
[block-uuid]
(editor-handler/open-block-in-sidebar! (uuid block-uuid)))
(def ^:export select_block
(fn [block-uuid]
(when-let [block (db-model/get-block-by-uuid block-uuid)]
(editor-handler/select-block! (:block/uuid block)) nil)))
(def ^:export edit_block
(fn [block-uuid ^js opts]
(when-let [block-uuid (and block-uuid (uuid block-uuid))]
(when-let [block (db-model/query-block-by-uuid block-uuid)]
(let [{:keys [pos] :or {pos :max}} (bean/->clj opts)]
(editor-handler/edit-block! block pos block-uuid))))))
(def ^:export insert_block
(fn [block-uuid-or-page-name content ^js opts]
(let [{:keys [before sibling isPageBlock properties]} (bean/->clj opts)
page-name (and isPageBlock block-uuid-or-page-name)
block-uuid (if isPageBlock nil (uuid block-uuid-or-page-name))
block-uuid' (if (and (not sibling) before block-uuid)
(let [block (db/entity [:block/uuid block-uuid])
first-child (db-model/get-by-parent-&-left (db/get-db)
(:db/id block)
(:db/id block))]
(if first-child
(:block/uuid first-child)
block-uuid))
block-uuid)
insert-at-first-child? (not= block-uuid' block-uuid)
[sibling? before?] (if insert-at-first-child?
[true true]
[sibling before])
before? (if (and (false? sibling?) before? (not insert-at-first-child?))
false
before?)
new-block (editor-handler/api-insert-new-block!
content
{:block-uuid block-uuid'
:sibling? sibling?
:before? before?
:page page-name
:properties properties})]
(bean/->js (normalize-keyword-for-json new-block)))))
(def ^:export insert_batch_block
(fn [block-uuid ^js batch-blocks ^js opts]
(when-let [block (db-model/query-block-by-uuid block-uuid)]
(when-let [bb (bean/->clj batch-blocks)]
(let [bb (if-not (vector? bb) (vector bb) bb)
{:keys [sibling]} (bean/->clj opts)
_ (editor-handler/insert-block-tree-after-target
(:db/id block) sibling bb (:block/format block))]
nil)))))
(def ^:export remove_block
(fn [block-uuid ^js _opts]
(let [includeChildren true
repo (state/get-current-repo)]
(editor-handler/delete-block-aux!
{:block/uuid (uuid block-uuid) :repo repo} includeChildren)
nil)))
(def ^:export update_block
(fn [block-uuid content ^js _opts]
(let [repo (state/get-current-repo)
edit-input (state/get-edit-input-id)
editing? (and edit-input (string/ends-with? edit-input block-uuid))]
(if editing?
(state/set-edit-content! edit-input content)
(editor-handler/save-block! repo (uuid block-uuid) content))
nil)))
(def ^:export move_block
(fn [src-block-uuid target-block-uuid ^js opts]
(let [{:keys [before children]} (bean/->clj opts)
move-to (cond
(boolean before)
:top
(boolean children)
:nested
:else
nil)
src-block (db-model/query-block-by-uuid (uuid src-block-uuid))
target-block (db-model/query-block-by-uuid (uuid target-block-uuid))]
(editor-dnd-handler/move-blocks nil [src-block] target-block move-to) nil)))
(def ^:export get_block
(fn [id-or-uuid ^js opts]
(when-let [block (cond
(number? id-or-uuid) (db-utils/pull id-or-uuid)
(string? id-or-uuid) (db-model/query-block-by-uuid id-or-uuid))]
(when-not (contains? block :block/name)
(when-let [uuid (:block/uuid block)]
(let [{:keys [includeChildren]} (bean/->clj opts)
repo (state/get-current-repo)
block (if includeChildren
;; nested children results
(first (outliner-tree/blocks->vec-tree
(db-model/get-block-and-children repo uuid) uuid))
;; attached shallow children
(assoc block :block/children
(map #(list :uuid (get-in % [:data :block/uuid]))
(db/get-block-immediate-children repo uuid))))]
(bean/->js (normalize-keyword-for-json block))))))))
(def ^:export get_current_block
(fn [^js opts]
(let [block (state/get-edit-block)
block (or block
(some-> (or (first (state/get-selection-blocks))
(gdom/getElement (state/get-editing-block-dom-id)))
(.getAttribute "blockid")
(db-model/get-block-by-uuid)))]
(get_block (:db/id block) opts))))
(def ^:export get_previous_sibling_block
(fn [block-uuid]
(when-let [block (db-model/query-block-by-uuid block-uuid)]
(let [{:block/keys [parent left]} block
block (when-not (= parent left) (db-utils/pull (:db/id left)))]
(and block (bean/->js (normalize-keyword-for-json block)))))))
(def ^:export get_next_sibling_block
(fn [block-uuid]
(when-let [block (db-model/query-block-by-uuid block-uuid)]
(when-let [right-siblings (outliner/get-right-siblings (outliner/->Block block))]
(bean/->js (normalize-keyword-for-json (:data (first right-siblings))))))))
(def ^:export set_block_collapsed
(fn [block-uuid ^js opts]
(when-let [block (db-model/get-block-by-uuid block-uuid)]
(let [opts (bean/->clj opts)
opts (if (or (string? opts) (boolean? opts)) {:flag opts} opts)
{:keys [flag]} opts
block-uuid (uuid block-uuid)
flag (if (= "toggle" flag)
(not (util/collapsed? block))
(boolean flag))]
(if flag (editor-handler/collapse-block! block-uuid)
(editor-handler/expand-block! block-uuid))
nil))))
(def ^:export upsert_block_property
(fn [block-uuid key value]
(editor-handler/set-block-property! (uuid block-uuid) key value)))
(def ^:export remove_block_property
(fn [block-uuid key]
(editor-handler/remove-block-property! (uuid block-uuid) key)))
(def ^:export get_block_property
(fn [block-uuid key]
(when-let [block (db-model/query-block-by-uuid block-uuid)]
(get (:block/properties block) (keyword key)))))
(def ^:export get_block_properties
(fn [block-uuid]
(when-let [block (db-model/query-block-by-uuid block-uuid)]
(bean/->js (normalize-keyword-for-json (:block/properties block))))))
(def ^:export get_current_page_blocks_tree
(fn []
(when-let [page (state/get-current-page)]
(let [blocks (db-model/get-page-blocks-no-cache page)
blocks (outliner-tree/blocks->vec-tree blocks page)
;; clean key
blocks (normalize-keyword-for-json blocks)]
(bean/->js blocks)))))
(def ^:export get_page_blocks_tree
(fn [page-name]
(when-let [_ (db-model/get-page page-name)]
(let [blocks (db-model/get-page-blocks-no-cache page-name)
blocks (outliner-tree/blocks->vec-tree blocks page-name)
blocks (normalize-keyword-for-json blocks)]
(bean/->js blocks)))))
(defn ^:export get_page_linked_references
[page-name-or-uuid]
(when-let [page (and page-name-or-uuid (db-model/get-page page-name-or-uuid))]
(let [page-name (:block/name page)
ref-blocks (if page-name
(db-model/get-page-referenced-blocks-full page-name)
(db-model/get-block-referenced-blocks (:block/uuid page)))
ref-blocks (and (seq ref-blocks) (into [] ref-blocks))]
(bean/->js (normalize-keyword-for-json ref-blocks)))))
(defn ^:export get_pages_from_namespace
[ns]
(when-let [repo (and ns (state/get-current-repo))]
(when-let [pages (db-model/get-namespace-pages repo ns)]
(bean/->js (normalize-keyword-for-json pages)))))
(defn ^:export get_pages_tree_from_namespace
[ns]
(when-let [repo (and ns (state/get-current-repo))]
(when-let [pages (db-model/get-namespace-hierarchy repo ns)]
(bean/->js (normalize-keyword-for-json pages)))))
(defn first-child-of-block
[block]
(when-let [children (:block/_parent block)]
(first (db-model/sort-by-left children block))))
(defn second-child-of-block
[block]
(when-let [children (:block/_parent block)]
(second (db-model/sort-by-left children block))))
(defn last-child-of-block
[block]
(when-let [children (:block/_parent block)]
(last (db-model/sort-by-left children block))))
(defn ^:export prepend_block_in_page
[uuid-or-page-name content ^js opts]
(let [page? (not (util/uuid-string? uuid-or-page-name))
page-not-exist? (and page? (nil? (db-model/get-page uuid-or-page-name)))
_ (and page-not-exist? (page-handler/create! uuid-or-page-name
{:redirect? false
:create-first-block? true
:format (state/get-preferred-format)}))]
(when-let [block (db-model/get-page uuid-or-page-name)]
(let [block' (if page? (second-child-of-block block) (first-child-of-block block))
sibling? (and page? (not (nil? block')))
opts (bean/->clj opts)
opts (merge opts {:isPageBlock (and page? (not sibling?)) :sibling sibling? :before sibling?})
src (if sibling? (str (:block/uuid block')) uuid-or-page-name)]
(insert_block src content (bean/->js opts))))))
(defn ^:export append_block_in_page
[uuid-or-page-name content ^js opts]
(let [page? (not (util/uuid-string? uuid-or-page-name))
page-not-exist? (and page? (nil? (db-model/get-page uuid-or-page-name)))
_ (and page-not-exist? (page-handler/create! uuid-or-page-name
{:redirect? false
:create-first-block? true
:format (state/get-preferred-format)}))]
(when-let [block (db-model/get-page uuid-or-page-name)]
(let [block' (last-child-of-block block)
sibling? (not (nil? block'))
opts (bean/->clj opts)
opts (merge opts {:isPageBlock (and page? (not sibling?)) :sibling sibling?}
(when sibling? {:before false}))
src (if sibling? (str (:block/uuid block')) uuid-or-page-name)]
(insert_block src content (bean/->js opts))))))
;; plugins
(def ^:export __install_plugin
(fn [^js manifest]
(when-let [{:keys [repo id] :as mft} (bean/->clj manifest)]
(if-not (and repo id)
(throw (js/Error. "[required] :repo :id"))
(plugin-handler/install-marketplace-plugin mft)))))
;; db
(defn ^:export q
[query-string]
(when-let [repo (state/get-current-repo)]
(when-let [result (query-dsl/query repo query-string)]
(bean/->js (normalize-keyword-for-json (flatten @result))))))
(defn ^:export datascript_query
[query & inputs]
(when-let [repo (state/get-current-repo)]
(when-let [db (db/get-db repo)]
(let [query (cljs.reader/read-string query)
resolved-inputs (map (comp query-react/resolve-input cljs.reader/read-string) inputs)
result (apply d/q query db resolved-inputs)]
(clj->js result)))))
(defn ^:export custom_query
[query-string]
(let [result (let [query (cljs.reader/read-string query-string)]
(db/custom-query {:query query}))]
(bean/->js (normalize-keyword-for-json (flatten @result)))))
(defn ^:export download_graph_db
[]
(when-let [repo (state/get-current-repo)]
(when-let [db (db/get-db repo)]
(let [db-str (if db (db/db->string db) "")
data-str (str "data:text/edn;charset=utf-8," (js/encodeURIComponent db-str))]
(when-let [anchor (gdom/getElement "download")]
(.setAttribute anchor "href" data-str)
(.setAttribute anchor "download" (str (string/replace repo "/" " ") ".transit"))
(.click anchor))))))
(defn ^:export download_graph_pages
[]
(when-let [repo (state/get-current-repo)]
(export-handler/export-repo-as-zip! repo)))
(defn ^:export exec_git_command
[^js args]
(when-let [args (and args (seq (bean/->clj args)))]
(shell/run-git-command! args)))
;; git
(defn ^:export git_exec_command
[^js args]
(when-let [args (and args (seq (bean/->clj args)))]
(shell/run-git-command2! args)))
(defn ^:export git_load_ignore_file
[]
(when-let [repo (state/get-current-repo)]
(p/let [file ".gitignore"
dir (config/get-repo-dir repo)
_ (fs/create-if-not-exists repo dir file)
content (fs/read-file dir file)]
content)))
(defn ^:export git_save_ignore_file
[content]
(when-let [repo (and (string? content) (state/get-current-repo))]
(p/let [file ".gitignore"
dir (config/get-repo-dir repo)
_ (fs/write-file! repo dir file content {:skip-compare? true})])))
;; ui
(defn ^:export show_msg
([content] (show_msg content :success nil))
([content status] (show_msg content status nil))
([content status ^js opts]
(let [{:keys [key timeout]} (bean/->clj opts)
hiccup? (and (string? content) (string/starts-with? (string/triml content) "[:"))
content (if hiccup? (parse-hiccup-ui content) content)
uid (when (string? key) (keyword key))
clear? (not= timeout 0)
key' (notification/show! content (keyword status) clear? uid timeout)]
(name key'))))
(defn ^:export ui_show_msg
[& args]
(apply show_msg args))
(defn ^:export ui_close_msg
[key]
(when (string? key)
(notification/clear! (keyword key)) nil))
;; assets
(defn ^:export assets_list_files_of_current_graph
[^js exts]
(p/let [files (ipc/ipc :getAssetsFiles {:exts exts})]
(bean/->js files)))
;; experiments
(defn ^:export exper_load_scripts
[pid & scripts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(doseq [s scripts
:let [upt-status #(state/upt-plugin-resource pid :scripts s :status %)
init? (plugin-handler/register-plugin-resources pid :scripts {:key s :src s})]]
(when init?
(p/catch
(p/then
(do
(upt-status :pending)
(loader/load s nil {:attributes {:data-ref (name pid)}}))
#(upt-status :done))
#(upt-status :error))))))
(defn ^:export exper_register_fenced_code_renderer
[pid type ^js opts]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(plugin-handler/register-fenced-code-renderer
(keyword pid) type (reduce #(assoc %1 %2 (aget opts (name %2))) {}
[:edit :before :subs :render]))))
(defn ^:export exper_register_extensions_enhancer
[pid type enhancer]
(when-let [^js _pl (and (fn? enhancer) (plugin-handler/get-plugin-inst pid))]
(plugin-handler/register-extensions-enhancer
(keyword pid) type {:enhancer enhancer})))
(defonce *request-k (volatile! 0))
(defn ^:export exper_request
[pid ^js options]
(when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
(let [req-id (vreset! *request-k (inc @*request-k))
req-cb #(plugin-handler/request-callback _pl req-id %)]
(-> (ipc/ipc :httpRequest req-id options)
(p/then #(req-cb %))
(p/catch #(req-cb %)))
req-id)))
(defn ^:export http_request_abort
[req-id]
(ipc/ipc :httpRequestAbort req-id))
;; helpers
(defn ^:export query_element_by_id
[id]
(when-let [^js el (gdom/getElement id)]
(if el (str (.-tagName el) "#" id) false)))
(defn ^:export query_element_rect
[selector]
(when-let [^js el (js/document.querySelector selector)]
(bean/->js (.toJSON (.getBoundingClientRect el)))))
(defn ^:export set_focused_settings
[pid]
(when-let [plugin (state/get-plugin-by-id pid)]
(state/set-state! :plugin/focused-settings pid)
(state/pub-event! [:go/plugins-settings pid false (or (:name plugin) (:title plugin))])))
(defn ^:export force_save_graph
[]
(p/let [_ (el/persist-dbs!)]
true))
(defn ^:export __debug_state
[path]
(-> (if (string? path)
(get @state/state (keyword path))
@state/state)
(bean/->js)))