浏览代码

Enhance/plugin apis (#3355)

* improve(plugin): support autoFocus option for main ui frame

* improve(plugin): make single selected block as current block

* improve(api): get selected blocks

* improve(plugin): support call built-in command from api

* fix(plugin): sanitize key of shortcut id

* improve(plugin): add invoke built-in command api &

* fix(editor): overwritten class of collapsed block

* improve(plugin): add `getStateFromStore` api

* chore: build libs core

Co-authored-by: Tienson Qin <[email protected]>
Charlie 3 年之前
父节点
当前提交
9029c632ef

+ 1 - 1
libs/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@logseq/libs",
   "name": "@logseq/libs",
-  "version": "0.0.1-alpha.32",
+  "version": "0.0.1-alpha.33",
   "description": "Logseq SDK libraries",
   "description": "Logseq SDK libraries",
   "main": "dist/lsplugin.user.js",
   "main": "dist/lsplugin.user.js",
   "typings": "index.d.ts",
   "typings": "index.d.ts",

+ 2 - 2
libs/src/LSPlugin.core.ts

@@ -173,13 +173,13 @@ function initUserSettingsHandlers (pluginLocal: PluginLocal) {
 function initMainUIHandlers (pluginLocal: PluginLocal) {
 function initMainUIHandlers (pluginLocal: PluginLocal) {
   const _ = (label: string): any => `main-ui:${label}`
   const _ = (label: string): any => `main-ui:${label}`
 
 
-  pluginLocal.on(_('visible'), ({ visible, toggle, cursor }) => {
+  pluginLocal.on(_('visible'), ({ visible, toggle, cursor, autoFocus }) => {
     const el = pluginLocal.getMainUIContainer()
     const el = pluginLocal.getMainUIContainer()
     el?.classList[toggle ? 'toggle' : (visible ? 'add' : 'remove')]('visible')
     el?.classList[toggle ? 'toggle' : (visible ? 'add' : 'remove')]('visible')
     // pluginLocal.caller!.callUserModel(LSPMSG, { type: _('visible'), payload: visible })
     // pluginLocal.caller!.callUserModel(LSPMSG, { type: _('visible'), payload: visible })
     // auto focus frame
     // auto focus frame
     if (visible) {
     if (visible) {
-      if (!pluginLocal.shadow && el) {
+      if (!pluginLocal.shadow && el && (autoFocus !== false)) {
         (el.querySelector('iframe') as HTMLIFrameElement)?.contentWindow?.focus()
         (el.querySelector('iframe') as HTMLIFrameElement)?.contentWindow?.focus()
       }
       }
     }
     }

+ 64 - 1
libs/src/LSPlugin.ts

@@ -185,6 +185,44 @@ export type SimpleCommandKeybinding = {
   mac?: string // special for Mac OS
   mac?: string // special for Mac OS
 }
 }
 
 
+export type ExternalCommandType =
+  'logseq.command/run' |
+  'logseq.editor/cycle-todo' |
+  'logseq.editor/down' |
+  'logseq.editor/up' |
+  'logseq.editor/expand-block-children' |
+  'logseq.editor/collapse-block-children' |
+  'logseq.editor/open-file-in-default-app' |
+  'logseq.editor/open-file-in-directory' |
+  'logseq.editor/select-all-blocks' |
+  'logseq.editor/toggle-open-blocks' |
+  'logseq.editor/zoom-in' |
+  'logseq.editor/zoom-out' |
+  'logseq.go/home' |
+  'logseq.go/journals' |
+  'logseq.go/keyboard-shortcuts' |
+  'logseq.go/next-journal' |
+  'logseq.go/prev-journal' |
+  'logseq.go/search' |
+  'logseq.go/search-in-page' |
+  'logseq.go/tomorrow' |
+  'logseq.search/re-index' |
+  'logseq.sidebar/clear' |
+  'logseq.sidebar/open-today-page' |
+  'logseq.ui/goto-plugins' |
+  'logseq.ui/select-theme-color' |
+  'logseq.ui/toggle-brackets' |
+  'logseq.ui/toggle-cards' |
+  'logseq.ui/toggle-contents' |
+  'logseq.ui/toggle-document-mode' |
+  'logseq.ui/toggle-help' |
+  'logseq.ui/toggle-left-sidebar' |
+  'logseq.ui/toggle-right-sidebar' |
+  'logseq.ui/toggle-settings' |
+  'logseq.ui/toggle-theme' |
+  'logseq.ui/toggle-wide-mode' |
+  'logseq.command-palette/toggle'
+
 /**
 /**
  * App level APIs
  * App level APIs
  */
  */
@@ -213,6 +251,24 @@ export interface IAppProxy {
     },
     },
     action: SimpleCommandCallback) => void
     action: SimpleCommandCallback) => void
 
 
+  invokeExternalCommand: (
+    type: ExternalCommandType,
+    ...args: Array<any>) => Promise<void>
+
+  /**
+   * Get state from app store
+   * valid state is here
+   * https://github.com/logseq/logseq/blob/master/src/main/frontend/state.cljs#L27
+   *
+   * @example
+   * ```ts
+   * const isDocMode = await logseq.App.getStateFromStore('document/mode?')
+   * ```
+   * @param path
+   */
+  getStateFromStore:
+    <T = any>(path: string | Array<string>) => Promise<T>
+
   // native
   // native
   relaunch: () => Promise<void>
   relaunch: () => Promise<void>
   quit: () => Promise<void>
   quit: () => Promise<void>
@@ -335,6 +391,8 @@ export interface IEditorProxy extends Record<string, any> {
 
 
   getCurrentBlock: () => Promise<BlockEntity | null>
   getCurrentBlock: () => Promise<BlockEntity | null>
 
 
+  getSelectedBlocks: () => Promise<Array<BlockEntity> | null>
+
   /**
   /**
    * get all blocks of the current page as a tree structure
    * get all blocks of the current page as a tree structure
    *
    *
@@ -380,6 +438,11 @@ export interface IEditorProxy extends Record<string, any> {
     opts?: Partial<{ includeChildren: boolean }>
     opts?: Partial<{ includeChildren: boolean }>
   ) => Promise<BlockEntity | null>
   ) => Promise<BlockEntity | null>
 
 
+  setBlockCollapsed: (
+    uuid: BlockUUID,
+    opts?: { flag: boolean | 'toggle' }
+  ) => Promise<void>
+
   getPage: (
   getPage: (
     srcPage: PageIdentity | EntityID,
     srcPage: PageIdentity | EntityID,
     opts?: Partial<{ includeChildren: boolean }>
     opts?: Partial<{ includeChildren: boolean }>
@@ -580,7 +643,7 @@ export interface ILSPluginUser extends EventEmitter<LSPluginUserEvents> {
   /**
   /**
    * show the plugin's UI
    * show the plugin's UI
    */
    */
-  showMainUI (): void
+  showMainUI (opts?: { autoFocus: boolean }): void
 
 
   /**
   /**
    * hide the plugin's UI
    * hide the plugin's UI

+ 2 - 2
libs/src/LSPlugin.user.ts

@@ -358,8 +358,8 @@ export class LSPluginUser extends EventEmitter<LSPluginUserEvents> implements IL
     this._ui.set(payload.key, payload)
     this._ui.set(payload.key, payload)
   }
   }
 
 
-  showMainUI (): void {
-    const payload = { key: KEY_MAIN_UI, visible: true }
+  showMainUI (opts?: { autoFocus: boolean }): void {
+    const payload = { key: KEY_MAIN_UI, visible: true, autoFocus: opts?.autoFocus }
     this.caller.call('main-ui:visible', payload)
     this.caller.call('main-ui:visible', payload)
     this.emit('ui:visible:changed', payload)
     this.emit('ui:visible:changed', payload)
     this._ui.set(payload.key, payload)
     this._ui.set(payload.key, payload)

+ 34 - 26
libs/webpack.config.core.js

@@ -2,31 +2,39 @@ const webpack = require('webpack')
 const path = require('path')
 const path = require('path')
 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
 const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
 
 
-module.exports = {
-  entry: './src/LSPlugin.core.ts',
-  devtool: 'inline-source-map',
-  module: {
-    rules: [
-      {
-        test: /\.tsx?$/,
-        use: 'ts-loader',
-        exclude: /node_modules/,
-      },
+module.exports = (env, argv) => {
+  const config = {
+    entry: './src/LSPlugin.core.ts',
+    devtool: 'eval',
+    module: {
+      rules: [
+        {
+          test: /\.tsx?$/,
+          use: 'ts-loader',
+          exclude: /node_modules/,
+        },
+      ],
+    },
+    resolve: {
+      extensions: ['.tsx', '.ts', '.js'],
+    },
+    plugins: [
+      new webpack.ProvidePlugin({
+        process: 'process/browser',
+      }),
     ],
     ],
-  },
-  resolve: {
-    extensions: ['.tsx', '.ts', '.js'],
-  },
-  plugins: [
-    new webpack.ProvidePlugin({
-      process: 'process/browser',
-    }),
-    // new BundleAnalyzerPlugin()
-  ],
-  output: {
-    library: 'LSPlugin',
-    libraryTarget: 'umd',
-    filename: 'lsplugin.core.js',
-    path: path.resolve(__dirname, '../resources/js'),
-  },
+    output: {
+      library: 'LSPlugin',
+      libraryTarget: 'umd',
+      filename: 'lsplugin.core.js',
+      path: path.resolve(__dirname, '../resources/js'),
+    },
+  }
+
+  if (argv.mode === 'production') {
+    delete config.devtool
+    config.plugins.push(new BundleAnalyzerPlugin())
+  }
+
+  return config
 }
 }

文件差异内容过多而无法显示
+ 0 - 0
resources/js/lsplugin.core.js


+ 1 - 1
src/main/frontend/components/block.cljs

@@ -2197,8 +2197,8 @@
       {:id block-id
       {:id block-id
        :data-refs data-refs
        :data-refs data-refs
        :data-refs-self data-refs-self
        :data-refs-self data-refs-self
+       :data-collapsed (and collapsed? has-child?)
        :class (str uuid
        :class (str uuid
-                   (when (and collapsed? has-child?) " collapsed")
                    (when pre-block? " pre-block")
                    (when pre-block? " pre-block")
                    (when (and card? (not review-cards?)) " shadow-xl"))
                    (when (and card? (not review-cards?)) " shadow-xl"))
        :blockid (str uuid)
        :blockid (str uuid)

+ 1 - 1
src/main/frontend/db/model.cljs

@@ -900,7 +900,7 @@
    (has-children? (state/get-current-repo) block-id))
    (has-children? (state/get-current-repo) block-id))
   ([repo block-id]
   ([repo block-id]
    (let [db (conn/get-conn repo)]
    (let [db (conn/get-conn repo)]
-     (when-let [block (db-utils/entity [:block/uuid block-id])]
+     (when-let [block (get-block-by-uuid block-id)]
        ;; perf: early stop
        ;; perf: early stop
        (let [result (d/datoms db :avet :block/parent (:db/id block))]
        (let [result (d/datoms db :avet :block/parent (:db/id block))]
          (boolean (seq result)))))))
          (boolean (seq result)))))))

+ 0 - 1
src/main/frontend/handler/command_palette.cljs

@@ -22,7 +22,6 @@
         :shortcut.handler/global-non-editing-only]
         :shortcut.handler/global-non-editing-only]
        (mapcat shortcut-helper/shortcuts->commands)))
        (mapcat shortcut-helper/shortcuts->commands)))
 
 
-
 (defn get-commands []
 (defn get-commands []
   (->> (get @state/state :command-palette/commands)
   (->> (get @state/state :command-palette/commands)
        (sort-by :id)))
        (sort-by :id)))

+ 67 - 65
src/main/frontend/handler/editor.cljs

@@ -3308,73 +3308,75 @@
   (remove-block-property! block-id :collapsed))
   (remove-block-property! block-id :collapsed))
 
 
 (defn expand!
 (defn expand!
-  [e]
-  (util/stop e)
-  (cond
-    (state/editing?)
-    (when-let [block-id (:block/uuid (state/get-edit-block))]
-      (expand-block! block-id))
-
-    (state/selection?)
-    (do
-      (->> (get-selected-blocks-with-children)
-           (map (fn [dom]
-                  (-> (dom/attr dom "blockid")
-                      medley/uuid
-                      expand-block!)))
-           doall)
-      (clear-selection!))
-
-    :else
-    ;; expand one level
-    (let [blocks-with-level (all-blocks-with-level {})
-          max-level (or (apply max (map :block/level blocks-with-level)) 99)]
-      (loop [level 1]
-        (if (> level max-level)
-          nil
-          (let [blocks-to-expand (->> blocks-with-level
-                                      (filter (fn [b] (= (:block/level b) level)))
-                                      (filter (fn [{:block/keys [properties]}]
-                                                (contains? properties :collapsed))))]
-            (if (empty? blocks-to-expand)
-              (recur (inc level))
-              (doseq [{:block/keys [uuid]} blocks-to-expand]
-                (expand-block! uuid)))))))))
+  ([e] (expand! e false))
+  ([e clear-selection?]
+   (util/stop e)
+   (cond
+     (state/editing?)
+     (when-let [block-id (:block/uuid (state/get-edit-block))]
+       (expand-block! block-id))
+
+     (state/selection?)
+     (do
+       (->> (get-selected-blocks-with-children)
+            (map (fn [dom]
+                   (-> (dom/attr dom "blockid")
+                       medley/uuid
+                       expand-block!)))
+            doall)
+       (and clear-selection? (clear-selection!)))
+
+     :else
+     ;; expand one level
+     (let [blocks-with-level (all-blocks-with-level {})
+           max-level (or (apply max (map :block/level blocks-with-level)) 99)]
+       (loop [level 1]
+         (if (> level max-level)
+           nil
+           (let [blocks-to-expand (->> blocks-with-level
+                                       (filter (fn [b] (= (:block/level b) level)))
+                                       (filter (fn [{:block/keys [properties]}]
+                                                 (contains? properties :collapsed))))]
+             (if (empty? blocks-to-expand)
+               (recur (inc level))
+               (doseq [{:block/keys [uuid]} blocks-to-expand]
+                 (expand-block! uuid))))))))))
 
 
 (defn collapse!
 (defn collapse!
-  [e]
-  (util/stop e)
-  (cond
-    (state/editing?)
-    (when-let [block-id (:block/uuid (state/get-edit-block))]
-      (collapse-block! block-id))
-
-    (state/selection?)
-    (do
-      (->> (get-selected-blocks-with-children)
-           (map (fn [dom]
-                  (-> (dom/attr dom "blockid")
-                      medley/uuid
-                      collapse-block!)))
-           doall)
-      (clear-selection!))
-
-    :else
-    ;; collapse by one level from outside
-    (let [blocks-with-level
-          (all-blocks-with-level {:collapse? true})
-          max-level (or (apply max (map :block/level blocks-with-level)) 99)]
-      (loop [level max-level]
-        (if (zero? level)
-          nil
-          (let [blocks-to-collapse
-                (->> blocks-with-level
-                     (filter (fn [b] (= (:block/level b) level)))
-                     (filter (fn [b] (collapsable? (:block/uuid b)))))]
-            (if (empty? blocks-to-collapse)
-              (recur (dec level))
-              (doseq [{:block/keys [uuid]} blocks-to-collapse]
-                (collapse-block! uuid)))))))))
+  ([e] (collapse! e false))
+  ([e clear-selection?]
+   (util/stop e)
+   (cond
+     (state/editing?)
+     (when-let [block-id (:block/uuid (state/get-edit-block))]
+       (collapse-block! block-id))
+
+     (state/selection?)
+     (do
+       (->> (get-selected-blocks-with-children)
+            (map (fn [dom]
+                   (-> (dom/attr dom "blockid")
+                       medley/uuid
+                       collapse-block!)))
+            doall)
+       (and clear-selection? (clear-selection!)))
+
+     :else
+     ;; collapse by one level from outside
+     (let [blocks-with-level
+           (all-blocks-with-level {:collapse? true})
+           max-level (or (apply max (map :block/level blocks-with-level)) 99)]
+       (loop [level max-level]
+         (if (zero? level)
+           nil
+           (let [blocks-to-collapse
+                 (->> blocks-with-level
+                      (filter (fn [b] (= (:block/level b) level)))
+                      (filter (fn [b] (collapsable? (:block/uuid b)))))]
+             (if (empty? blocks-to-collapse)
+               (recur (dec level))
+               (doseq [{:block/keys [uuid]} blocks-to-collapse]
+                 (collapse-block! uuid))))))))))
 
 
 (defn- collapse-all!
 (defn- collapse-all!
   []
   []

+ 14 - 8
src/main/frontend/modules/shortcut/config.cljs

@@ -182,11 +182,13 @@
 
 
    :editor/up                      {:desc    "Move cursor up / Select up"
    :editor/up                      {:desc    "Move cursor up / Select up"
                                     :binding "up"
                                     :binding "up"
-                                    :fn      (editor-handler/shortcut-up-down :up)}
+                                    :fn      (editor-handler/shortcut-up-down :up)
+                                    :force?  true}
 
 
    :editor/down                    {:desc    "Move cursor down / Select down"
    :editor/down                    {:desc    "Move cursor down / Select down"
                                     :binding "down"
                                     :binding "down"
-                                    :fn      (editor-handler/shortcut-up-down :down)}
+                                    :fn      (editor-handler/shortcut-up-down :down)
+                                    :force?  true}
 
 
    :editor/left                    {:desc    "Move cursor left / Open selected block at beginning"
    :editor/left                    {:desc    "Move cursor left / Open selected block at beginning"
                                     :binding "left"
                                     :binding "left"
@@ -223,11 +225,13 @@
 
 
    :editor/expand-block-children   {:desc    "Expand"
    :editor/expand-block-children   {:desc    "Expand"
                                     :binding "mod+down"
                                     :binding "mod+down"
-                                    :fn      editor-handler/expand!}
+                                    :fn      editor-handler/expand!
+                                    :force?  true}
 
 
    :editor/collapse-block-children {:desc    "Collapse"
    :editor/collapse-block-children {:desc    "Collapse"
                                     :binding "mod+up"
                                     :binding "mod+up"
-                                    :fn      editor-handler/collapse!}
+                                    :fn      editor-handler/collapse!
+                                    :force?  true}
 
 
    :editor/indent                  {:desc    "Indent block"
    :editor/indent                  {:desc    "Indent block"
                                     :binding "tab"
                                     :binding "tab"
@@ -312,7 +316,8 @@
 
 
    :command-palette/toggle         {:desc    "Toggle command palette"
    :command-palette/toggle         {:desc    "Toggle command palette"
                                     :binding "mod+shift+p"
                                     :binding "mod+shift+p"
-                                    :fn      (fn [] (state/toggle! :ui/command-palette-open?))}
+                                    :fn      (fn [] (state/toggle! :ui/command-palette-open?))
+                                    :force?   true}
 
 
    :command/run                    {:desc    "Run git command"
    :command/run                    {:desc    "Run git command"
                                     :binding "mod+shift+1"
                                     :binding "mod+shift+1"
@@ -371,11 +376,11 @@
                                      :fn      page-handler/toggle-favorite!}
                                      :fn      page-handler/toggle-favorite!}
 
 
    :editor/open-file-in-default-app {:desc    "Open file in default app"
    :editor/open-file-in-default-app {:desc    "Open file in default app"
-                                     :binding "o f"
+                                     :binding nil
                                      :fn      page-handler/open-file-in-default-app}
                                      :fn      page-handler/open-file-in-default-app}
 
 
    :editor/open-file-in-directory   {:desc    "Open file in parent directory"
    :editor/open-file-in-directory   {:desc    "Open file in parent directory"
-                                     :binding "o d"
+                                     :binding nil
                                      :fn      page-handler/open-file-in-directory}
                                      :fn      page-handler/open-file-in-directory}
 
 
    :ui/toggle-wide-mode             {:desc    "Toggle wide mode"
    :ui/toggle-wide-mode             {:desc    "Toggle wide mode"
@@ -463,7 +468,8 @@
 
 
     :shortcut.handler/editor-global
     :shortcut.handler/editor-global
     (->
     (->
-     (build-category-map [:editor/cycle-todo
+     (build-category-map [:command-palette/toggle
+                          :editor/cycle-todo
                           :editor/up
                           :editor/up
                           :editor/down
                           :editor/down
                           :editor/left
                           :editor/left

+ 6 - 6
src/main/frontend/modules/shortcut/core.cljs

@@ -18,10 +18,10 @@
 (def *pending (atom []))
 (def *pending (atom []))
 
 
 (def global-keys #js
 (def global-keys #js
-  [KeyCodes/TAB
-   KeyCodes/ENTER
-   KeyCodes/BACKSPACE KeyCodes/DELETE
-   KeyCodes/UP KeyCodes/LEFT KeyCodes/DOWN KeyCodes/RIGHT])
+                  [KeyCodes/TAB
+                   KeyCodes/ENTER
+                   KeyCodes/BACKSPACE KeyCodes/DELETE
+                   KeyCodes/UP KeyCodes/LEFT KeyCodes/DOWN KeyCodes/RIGHT])
 
 
 (def key-names (js->clj KeyNames))
 (def key-names (js->clj KeyNames))
 
 
@@ -109,7 +109,7 @@
 
 
     ;; register shortcuts
     ;; register shortcuts
     (doseq [[id _] shortcut-map]
     (doseq [[id _] shortcut-map]
-      ;; (log/info :shortcut/install-shortcut {:id id :shortcut (dh/shortcut-binding id)})
+      ;;(log/info :shortcut/install-shortcut {:id id :shortcut (str (dh/shortcut-binding id))})
       (register-shortcut! handler id))
       (register-shortcut! handler id))
 
 
     (let [f (fn [e]
     (let [f (fn [e]
@@ -123,7 +123,7 @@
                        :dispatch-fn f
                        :dispatch-fn f
                        :handler    handler}}]
                        :handler    handler}}]
 
 
-      (events/listen handler EventType/SHORTCUT_TRIGGERED f)
+      (.listen handler EventType/SHORTCUT_TRIGGERED f)
 
 
       (when-not skip-installed?
       (when-not skip-installed?
         (swap! *installed merge data))
         (swap! *installed merge data))

+ 5 - 6
src/main/frontend/modules/shortcut/data_helper.cljs

@@ -30,7 +30,7 @@
                       (get (get-bindings) id))]
                       (get (get-bindings) id))]
     (cond
     (cond
       (nil? shortcut)
       (nil? shortcut)
-      (log/error :shortcut/binding-not-found {:id id})
+      (log/warn :shortcut/binding-not-found {:id id})
 
 
       (false? shortcut)
       (false? shortcut)
       (do
       (do
@@ -191,11 +191,10 @@
         data    (->> (vals @config/config)
         data    (->> (vals @config/config)
                      (into  {})
                      (into  {})
                      id)]
                      id)]
-    (when binding
-      (assoc
-       data
-       :binding
-       (binding-for-display id binding)))))
+    (assoc
+      data
+      :binding
+      (binding-for-display id binding))))
 
 
 (defn shortcuts->commands [handler-id]
 (defn shortcuts->commands [handler-id]
   (let [m (get @config/config handler-id)]
   (let [m (get @config/config handler-id)]

+ 48 - 0
src/main/logseq/api.cljs

@@ -57,6 +57,15 @@
         (js/console.error "[parse hiccup error]" e) input))))
         (js/console.error "[parse hiccup error]" e) input))))
 
 
 ;; base
 ;; 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))))
+
 (def ^:export get_user_configs
 (def ^:export get_user_configs
   (fn []
   (fn []
     (bean/->js
     (bean/->js
@@ -233,6 +242,7 @@
   (fn [pid ^js cmd-action palette?]
   (fn [pid ^js cmd-action palette?]
     (when-let [[cmd action] (bean/->clj cmd-action)]
     (when-let [[cmd action] (bean/->clj cmd-action)]
       (let [action (assoc action 0 (keyword (first action)))
       (let [action (assoc action 0 (keyword (first action)))
+            cmd (assoc cmd :key (string/replace (:key cmd) ":" "-"))
             key (:key cmd)
             key (:key cmd)
             keybinding (:keybinding cmd)
             keybinding (:keybinding cmd)
             palette-cmd (and palette? (plugin-handler/simple-cmd->palette-cmd pid cmd action))]
             palette-cmd (and palette? (plugin-handler/simple-cmd->palette-cmd pid cmd action))]
@@ -288,6 +298,15 @@
     (when (re-find #"https?://" url)
     (when (re-find #"https?://" url)
       (js/apis.openExternal 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'
 ;; flag - boolean | 'toggle'
 (def ^:export set_left_sidebar_visible
 (def ^:export set_left_sidebar_visible
   (fn [flag]
   (fn [flag]
@@ -352,10 +371,22 @@
 (def ^:export get_current_block
 (def ^:export get_current_block
   (fn []
   (fn []
     (let [block (state/get-edit-block)
     (let [block (state/get-edit-block)
+          block (or block (some-> (first (:selection/blocks @state/state))
+                                  (.getAttribute "blockid")
+                                  (db-model/get-block-by-uuid)))
           block (or block (state/get-last-edit-block))
           block (or block (state/get-last-edit-block))
           block (and block (db-utils/pull (:db/id block)))]
           block (and block (db-utils/pull (:db/id block)))]
       (bean/->js (normalize-keyword-for-json block)))))
       (bean/->js (normalize-keyword-for-json block)))))
 
 
+(def ^:export get_selected_blocks
+  (fn []
+    (when-let [blocks (and (state/in-selection-mode?)
+                           (seq (:selection/blocks @state/state)))]
+      (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
 (def ^:export get_current_page
   (fn []
   (fn []
     (when-let [page (state/get-current-page)]
     (when-let [page (state/get-current-page)]
@@ -496,6 +527,16 @@
       (when-let [right-siblings (outliner/get-right-siblings (outliner/->Block block))]
       (when-let [right-siblings (outliner/get-right-siblings (outliner/->Block block))]
         (bean/->js (normalize-keyword-for-json (:data (first right-siblings))))))))
         (bean/->js (normalize-keyword-for-json (:data (first right-siblings))))))))
 
 
+(def ^:export set_block_collapsed
+  (fn [uuid ^js opts]
+    (when-let [block (db-model/get-block-by-uuid uuid)]
+      (let [{:keys [flag]} (bean/->clj opts)
+            flag (if (= "toggle" flag)
+                   (not (-> block :block/properties :collapsed))
+                   (boolean flag))]
+        (if flag (editor-handler/collapse-block! uuid)
+                 (editor-handler/expand-block! uuid))))))
+
 (def ^:export upsert_block_property
 (def ^:export upsert_block_property
   (fn [block-uuid key value]
   (fn [block-uuid key value]
     (editor-handler/set-block-property! (medley/uuid block-uuid) key value)))
     (editor-handler/set-block-property! (medley/uuid block-uuid) key value)))
@@ -589,3 +630,10 @@
   []
   []
   (p/let [_ (el/persist-dbs!)
   (p/let [_ (el/persist-dbs!)
           _ (reset! handler/triggered? true)]))
           _ (reset! handler/triggered? true)]))
+
+(defn ^:export __debug_state
+  [path]
+  (-> (if (string? path)
+        (get @state/state (keyword path))
+        @state/state)
+      (bean/->js)))

部分文件因为文件数量过多而无法显示