Browse Source

Enhance: plugin APIs (#10399)

* enhance(plugin): call apis with the sdk ns

* enhance(plugin): types

* enhance(api): get value from the computed style

* enhance(api): types

* enhance(plugin): types

* enhance(plugin): types

* fix: lint

* fix(apis): incorrect shortcut command registion for block editing mode #10392

* fix(api): types

* enhance(apis): support register shortcuts with multi binding vals

* fix(plugins): normalize command key to make the internal keyword legal

* chore(plugin): build libs core

* chore(plugin): bump version

* enhance(apis): normalize apis cljs data

* chore(plugin): update libs user sdk

* chore(plugin): CHANGELOG.md

* fix: typo

* fix(ux): support querying plugins with right space chars
Charlie 2 years ago
parent
commit
036df25a17

+ 13 - 1
libs/CHANGELOG.md

@@ -4,8 +4,20 @@ All notable changes to this project will be documented in this file.
 
 ## [Unreleased]
 
-## [0.0.15]
+## [0.0.16]
+### Added
+- Support api of `logseq.UI.queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>`
+- Support api of `logseq.UI.queryElementById: (id: string) => Promise<string | boolean>`
+- Support api of `logseq.UI.checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>`
+- Support api of `logseq.UI.resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<any>`
+- Support api of `logseq.Assets.builtInOpen(path: string): Promise<boolean | undefined>`
 
+### Fixed
+- fix Plugin can't register command shortcut with editing mode [#10392](https://github.com/logseq/logseq/issues/10392)
+- fix [Plugin API] [Keymap] Command without keybinding can't be present in Keymap [#10466](https://github.com/logseq/logseq/issues/10466)
+- fix [Possible DATA LOSS] [Plugin API] [Keymap] Any plugin could break the global config.edn [#10465](https://github.com/logseq/logseq/issues/10465)
+ 
+## [0.0.15]
 ### Added
 - Support a plug-in flag for the plugin slash commands item
 - Support api of `logseq.App.setCurrentGraphConfigs: (configs: {}) => Promise<void>`

+ 1 - 1
libs/package.json

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

+ 38 - 36
libs/src/LSPlugin.core.ts

@@ -54,6 +54,7 @@ const DIR_PLUGINS = 'plugins'
 declare global {
   interface Window {
     LSPluginCore: LSPluginCore
+    DOMPurify: typeof DOMPurify
   }
 }
 
@@ -308,23 +309,25 @@ function initProviderHandlers(pluginLocal: PluginLocal) {
   // provider:ui
   pluginLocal.on(_('ui'), (ui: UIOptions) => {
     pluginLocal._onHostMounted(() => {
-      pluginLocal._dispose(
-        setupInjectedUI.call(
-          pluginLocal,
-          ui,
-          Object.assign(
-            {
-              'data-ref': pluginLocal.id,
-            },
-            ui.attrs || {}
-          ),
-          ({ el, float }) => {
-            if (!float) return
-            const identity = el.dataset.identity
-            pluginLocal.layoutCore.move_container_to_top(identity)
-          }
-        )
+      const ret = setupInjectedUI.call(
+        pluginLocal,
+        ui,
+        Object.assign(
+          {
+            'data-ref': pluginLocal.id,
+          },
+          ui.attrs || {}
+        ),
+        ({ el, float }) => {
+          if (!float) return
+          const identity = el.dataset.identity
+          pluginLocal.layoutCore.move_container_to_top(identity)
+        }
       )
+
+      if (typeof ret === 'function') {
+        pluginLocal._dispose(ret)
+      }
     })
   })
 }
@@ -346,8 +349,6 @@ function initApiProxyHandlers(pluginLocal: PluginLocal) {
       }
     }
 
-    const { _sync } = payload
-
     if (pluginLocal.shadow) {
       if (payload.actor) {
         payload.actor.resolve(ret)
@@ -355,6 +356,8 @@ function initApiProxyHandlers(pluginLocal: PluginLocal) {
       return
     }
 
+    const { _sync } = payload
+
     if (_sync != null) {
       const reply = (result: any) => {
         pluginLocal.caller?.callUserModel(LSPMSG_SYNC, {
@@ -430,7 +433,7 @@ class PluginLocal extends EventEmitter<
 
   async _setupUserSettings(reload?: boolean) {
     const { _options } = this
-    const logger = (this._logger = new PluginLogger('Loader'))
+    const logger = (this._logger = new PluginLogger(`Loader:${this.debugTag}`))
 
     if (_options.settings && !reload) {
       return
@@ -532,7 +535,7 @@ class PluginLocal extends EventEmitter<
     const localRoot = (this._localRoot = safetyPathNormalize(url))
     const logseq: Partial<LSPluginPkgConfig> = pkg.logseq || {}
 
-    // Pick legal attrs
+      // Pick legal attrs
     ;[
       'name',
       'author',
@@ -642,10 +645,10 @@ class PluginLocal extends EventEmitter<
     <meta charset="UTF-8">
     <title>logseq plugin entry</title>
     ${
-      IS_DEV
-        ? `<script src="${sdkPathRoot}/lsplugin.user.js?v=${tag}"></script>`
-        : `<script src="https://cdn.jsdelivr.net/npm/@logseq/libs/dist/lsplugin.user.min.js?v=${tag}"></script>`
-    }
+        IS_DEV
+          ? `<script src="${sdkPathRoot}/lsplugin.user.js?v=${tag}"></script>`
+          : `<script src="https://cdn.jsdelivr.net/npm/@logseq/libs/dist/lsplugin.user.min.js?v=${tag}"></script>`
+      }
     
   </head>
   <body>
@@ -866,7 +869,7 @@ class PluginLocal extends EventEmitter<
 
       this._dispose(cleanInjectedScripts.bind(this))
     } catch (e) {
-      this.logger?.error('[Load Plugin]', e, true)
+      this.logger.error('load', e, true)
 
       this.dispose().catch(null)
       this._status = PluginLocalLoadStatus.ERROR
@@ -924,7 +927,7 @@ class PluginLocal extends EventEmitter<
           )
           this.emit('beforeunload', eventBeforeUnload)
         } catch (e) {
-          this.logger.error('[beforeunload]', e)
+          this.logger.error('beforeunload', e)
         }
 
         await this.dispose()
@@ -932,7 +935,7 @@ class PluginLocal extends EventEmitter<
 
       this.emit('unloaded')
     } catch (e) {
-      this.logger.error('[unload Error]', e)
+      this.logger.error('unload', e)
     } finally {
       this._status = PluginLocalLoadStatus.UNLOADED
     }
@@ -1038,7 +1041,7 @@ class PluginLocal extends EventEmitter<
 
   get debugTag() {
     const name = this._options?.name
-    return `#${this._id} ${name ?? ''}`
+    return `#${this._id} - ${name ?? ''}`
   }
 
   get localRoot(): string {
@@ -1103,8 +1106,7 @@ class LSPluginCore
     | 'beforereload'
     | 'reloaded'
   >
-  implements ILSPluginThemeManager
-{
+  implements ILSPluginThemeManager {
   private _isRegistering = false
   private _readyIndicator?: DeferredActor
   private readonly _hostMountedActor: DeferredActor = deferred()
@@ -1566,12 +1568,12 @@ class LSPluginCore
       await this.saveUserPreferences(
         theme.mode
           ? {
-              themes: {
-                ...this._userPreferences.themes,
-                mode: theme.mode,
-                [theme.mode]: theme,
-              },
-            }
+            themes: {
+              ...this._userPreferences.themes,
+              mode: theme.mode,
+              [theme.mode]: theme,
+            },
+          }
           : { theme: theme }
       )
     }

+ 14 - 29
libs/src/LSPlugin.ts

@@ -192,7 +192,7 @@ export interface BlockEntity {
   level?: number
   meta?: { timestamps: any; properties: any; startPos: number; endPos: number }
   title?: Array<any>
-        marker?: string
+  marker?: string
 }
 
 /**
@@ -235,9 +235,10 @@ export type BlockCursorPosition = {
   rect: DOMRect
 }
 
+export type Keybinding = string | Array<string>
 export type SimpleCommandKeybinding = {
   mode?: 'global' | 'non-editing' | 'editing'
-  binding: string
+  binding: Keybinding
   mac?: string // special for Mac OS
 }
 
@@ -469,25 +470,6 @@ export interface IAppProxy {
   removeTemplate: (name: string) => Promise<any>
   insertTemplate: (target: BlockUUID, name: string) => Promise<any>
 
-  // ui
-  queryElementById: (id: string) => Promise<string | boolean>
-
-  /**
-   * @added 0.0.5
-   * @param selector
-   */
-  queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
-
-  /**
-   * @deprecated Use `logseq.UI.showMsg` instead
-   * @param content
-   * @param status
-   */
-  showMsg: (
-    content: string,
-    status?: 'success' | 'warning' | 'error' | string
-  ) => void
-
   setZoomFactor: (factor: number) => void
   setFullScreen: (flag: boolean | 'toggle') => void
   setLeftSidebarVisible: (flag: boolean | 'toggle') => void
@@ -891,20 +873,16 @@ export type UIMsgOptions = {
 export type UIMsgKey = UIMsgOptions['key']
 
 export interface IUIProxy {
-  /**
-   * @added 0.0.2
-   *
-   * @param content
-   * @param status
-   * @param opts
-   */
   showMsg: (
     content: string,
     status?: 'success' | 'warning' | 'error' | string,
     opts?: Partial<UIMsgOptions>
   ) => Promise<UIMsgKey>
-
   closeMsg: (key: UIMsgKey) => void
+  queryElementRect: (selector: string) => Promise<DOMRectReadOnly | null>
+  queryElementById: (id: string) => Promise<string | boolean>
+  checkSlotValid: (slot: UISlotIdentity['slot']) => Promise<boolean>
+  resolveThemeCssPropsVals: (props: string | Array<string>) => Promise<Record<string, string | undefined> | null>
 }
 
 /**
@@ -938,6 +916,13 @@ export interface IAssetsProxy {
    * @param path
    */
   makeUrl(path: string): Promise<string>
+
+  /**
+   * try to open asset type file in Logseq app
+   * @added 0.0.16
+   * @param path
+   */
+  builtInOpen(path: string): Promise<boolean | undefined>
 }
 
 export interface ILSPluginThemeManager {

+ 14 - 5
libs/src/LSPlugin.user.ts

@@ -4,7 +4,7 @@ import {
   mergeSettingsWithSchema,
   PluginLogger,
   safeSnakeCase,
-  safetyPathJoin,
+  safetyPathJoin, normalizeKeyStr,
 } from './helpers'
 import { LSPluginCaller } from './LSPlugin.caller'
 import * as callableAPIs from './callable.apis'
@@ -77,12 +77,21 @@ function registerSimpleCommand (
   },
   action: SimpleCommandCallback
 ) {
+  const { key, label, desc, palette, keybinding, extras } = opts
+
   if (typeof action !== 'function') {
+    this.logger.error(`${key || label}: command action should be function.`)
     return false
   }
 
-  const { key, label, desc, palette, keybinding, extras } = opts
-  const eventKey = `SimpleCommandHook${key}${++registeredCmdUid}`
+  const normalizedKey = normalizeKeyStr(key)
+
+  if (!normalizedKey) {
+    this.logger.error(`${label}: command key is required.`)
+    return false
+  }
+
+  const eventKey = `SimpleCommandHook${normalizedKey}${++registeredCmdUid}`
 
   this.Editor['on' + eventKey](action)
 
@@ -92,7 +101,7 @@ function registerSimpleCommand (
       this.baseInfo.id,
       // [cmd, action]
       [
-        { key, label, type, desc, keybinding, extras },
+        { key: normalizedKey, label, type, desc, keybinding, extras },
         ['editor/hook', eventKey],
       ],
       palette,
@@ -171,7 +180,7 @@ const app: Partial<IAppProxy> = {
 
     const { binding } = keybinding
     const group = '$shortcut$'
-    const key = group + safeSnakeCase(binding)
+    const key = opts.key || (group + safeSnakeCase(binding?.toString()))
 
     return registerSimpleCommand.call(
       this,

+ 48 - 32
libs/src/helpers.ts

@@ -2,7 +2,7 @@ import { SettingSchemaDesc, StyleString, UIOptions } from './LSPlugin'
 import { PluginLocal } from './LSPlugin.core'
 import * as nodePath from 'path'
 import DOMPurify from 'dompurify'
-import merge from 'deepmerge';
+import merge from 'deepmerge'
 import { snakeCase } from 'snake-case'
 import * as callables from './callable.apis'
 import EventEmitter from 'eventemitter3'
@@ -211,12 +211,23 @@ export function deferred<T = any>(timeout?: number, tag?: string) {
 
 export function invokeHostExportedApi(method: string, ...args: Array<any>) {
   method = method?.startsWith('_call') ? method : method?.replace(/^[_$]+/, '')
-  const method1 = safeSnakeCase(method)
+  let method1 = safeSnakeCase(method)
+
+  // @ts-ignore
+  const nsSDK = window.logseq?.sdk
+  const supportedNS = nsSDK && Object.keys(nsSDK)
+  let nsTarget = {}
+  const ns0 = method1?.split('_')?.[0]
+
+  if (ns0 && supportedNS.includes(ns0)) {
+    method1 = method1.replace(new RegExp(`^${ns0}_`), '')
+    nsTarget = nsSDK?.[ns0]
+  }
 
   const logseqHostExportedApi = Object.assign(
     // @ts-ignore
-    window.logseq?.api || {},
-    callables
+    {}, window.logseq?.api,
+    nsTarget, callables
   )
 
   const fn =
@@ -266,9 +277,9 @@ export function setupInjectedStyle(
   el.textContent = style
 
   attrs &&
-    Object.entries(attrs).forEach(([k, v]) => {
-      el.setAttribute(k, v)
-    })
+  Object.entries(attrs).forEach(([k, v]) => {
+    el.setAttribute(k, v)
+  })
 
   document.head.append(el)
 
@@ -313,7 +324,7 @@ export function setupInjectedUI(
     console.error(
       `${this.debugTag} can not resolve selector target ${selector}`
     )
-    return
+    return false
   }
 
   if (ui.template) {
@@ -344,22 +355,22 @@ export function setupInjectedUI(
 
     // update attributes
     attrs &&
-      Object.entries(attrs).forEach(([k, v]) => {
-        el.setAttribute(k, v)
-      })
+    Object.entries(attrs).forEach(([k, v]) => {
+      el.setAttribute(k, v)
+    })
 
     let positionDirty = el.dataset.dx != null
     ui.style &&
-      Object.entries(ui.style).forEach(([k, v]) => {
-        if (
-          positionDirty &&
-          ['left', 'top', 'bottom', 'right', 'width', 'height'].includes(k)
-        ) {
-          return
-        }
-
-        el.style[k] = v
-      })
+    Object.entries(ui.style).forEach(([k, v]) => {
+      if (
+        positionDirty &&
+        ['left', 'top', 'bottom', 'right', 'width', 'height'].includes(k)
+      ) {
+        return
+      }
+
+      el.style[k] = v
+    })
     return
   }
 
@@ -379,14 +390,14 @@ export function setupInjectedUI(
   content.innerHTML = ui.template
 
   attrs &&
-    Object.entries(attrs).forEach(([k, v]) => {
-      el.setAttribute(k, v)
-    })
+  Object.entries(attrs).forEach(([k, v]) => {
+    el.setAttribute(k, v)
+  })
 
   ui.style &&
-    Object.entries(ui.style).forEach(([k, v]) => {
-      el.style[k] = v
-    })
+  Object.entries(ui.style).forEach(([k, v]) => {
+    el.style[k] = v
+  })
 
   let teardownUI: () => void
   let disposeFloat: () => void
@@ -399,11 +410,11 @@ export function setupInjectedUI(
     el.classList.add('lsp-ui-float-container', 'visible')
     disposeFloat =
       (pl._setupResizableContainer(el, key),
-      pl._setupDraggableContainer(el, {
-        key,
-        close: () => teardownUI(),
-        title: attrs?.title,
-      }))
+        pl._setupDraggableContainer(el, {
+          key,
+          close: () => teardownUI(),
+          title: attrs?.title,
+        }))
   }
 
   if (!!slot && ui.reset) {
@@ -542,3 +553,8 @@ export function mergeSettingsWithSchema(
   // shadow copy
   return Object.assign(defaults, settings)
 }
+
+export function normalizeKeyStr(s: string) {
+  if (typeof s !== 'string') return
+  return s.trim().replace(/\s/g, '_').toLowerCase()
+}

File diff suppressed because it is too large
+ 0 - 0
resources/js/lsplugin.core.js


File diff suppressed because it is too large
+ 0 - 0
resources/js/lsplugin.user.js


+ 1 - 0
resources/js/lsplugin.user.js.LICENSE.txt

@@ -0,0 +1 @@
+/*! @license DOMPurify 2.3.8 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.8/LICENSE */

+ 1 - 1
src/electron/electron/git.cljs

@@ -54,7 +54,7 @@
   (try
     (let [graph-path (state/get-graph-path)
           _ (when (string/blank? graph-path)
-              (utils/send-to-renderer "getCurrentGraph" {})
+              (utils/send-to-renderer :setCurrentGraph {})
               (throw (js/Error. "Empty graph path")))
           p (.join node-path graph-path ".git")]
       (when (and (fs/existsSync p)

+ 2 - 2
src/main/electron/listener.cljs

@@ -83,10 +83,10 @@
                  (fn []
                    (state/pub-event! [:modal/set-git-username-and-email])))
 
-  (safe-api-call "getCurrentGraph"
+  (safe-api-call "setCurrentGraph"
                  (fn []
                    (when-let [graph (state/get-current-repo)]
-                     (ipc/ipc "setCurrentGraph" graph))))
+                     (ipc/ipc :setCurrentGraph graph))))
 
   (safe-api-call "redirect"
                  (fn [data]

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

@@ -373,7 +373,7 @@
                         (some-> (js/document.querySelector ".cp__plugins-page") (.focus))
                         (reset! *search-key nil))))
      :on-change   #(let [^js target (.-target %)]
-                     (reset! *search-key (util/trim-safe (.-value target))))
+                     (reset! *search-key (some-> (.-value target) (string/triml))))
      :value       (or search-key "")}]])
 
 (rum/defc panel-tab-developer

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

@@ -35,7 +35,10 @@
           (get @state/state :command-palette/commands)))
 
 (defn history
-  ([] (or (storage/get "commands-history") []))
+  ([] (or (try (storage/get "commands-history")
+               (catch js/Error e
+                 (log/error :commands-history e)))
+          []))
   ([vals] (storage/set "commands-history" vals)))
 
 (defn- assoc-invokes [cmds]

+ 10 - 8
src/main/frontend/handler/plugin.cljs

@@ -25,12 +25,13 @@
 (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)))
+    (let [f (fn [[k v]] (if (keyword? k) [(csk/->camelCase (name k)) v] [k v]))]
+      (walk/postwalk
+        (fn [x]
+          (cond
+            (map? x) (into {} (map f x))
+            (uuid? x) (str x)
+            :else x)) input))))
 
 (defn invoke-exported-api
   [type & args]
@@ -297,7 +298,8 @@
   [pid key keybinding]
   (let [id      (keyword (str "plugin." pid "/" key))
         binding (:binding keybinding)
-        binding (some->> (if (string? binding) [binding] (seq binding))
+        binding (some->> (if (string? binding) [binding] (vec binding))
+                         (remove string/blank?)
                          (map shortcut-utils/undecorate-binding))
         binding (if util/mac?
                   (or (:mac keybinding) binding) binding)
@@ -495,7 +497,7 @@
                    payload)
                  (if (keyword? plugin-id) (name plugin-id) plugin-id))
       (catch :default e
-        (js/console.error "[Hook Plugin Err]" e)))))
+        (log/error :invoke-hook-exception e)))))
 
 (defn hook-plugin-app
   ([type payload] (hook-plugin-app type payload nil))

+ 11 - 9
src/main/frontend/modules/shortcut/config.cljs

@@ -925,15 +925,17 @@
 (def *shortcut-cmds (atom {}))
 
 (defn add-shortcut!
-  [handler-id id shortcut-map]
-  (swap! *config assoc-in [handler-id id] shortcut-map)
-  (swap! *shortcut-cmds assoc id (:cmd shortcut-map))
-  (let [plugin? (str/starts-with? (str id) ":plugin.")
-        category (or (:category shortcut-map)
-                     (if plugin?
-                       :shortcut.category/plugins
-                       :shortcut.category/others))]
-    (swap! *category update category #(conj % id))))
+  ([handler-id id shortcut-map] (add-shortcut! handler-id id shortcut-map false))
+  ([handler-id id shortcut-map config-only?]
+   (swap! *config assoc-in [handler-id id] shortcut-map)
+   (when-not config-only?
+     (swap! *shortcut-cmds assoc id (:cmd shortcut-map))
+     (let [plugin? (str/starts-with? (str id) ":plugin.")
+           category (or (:category shortcut-map)
+                        (if plugin?
+                          :shortcut.category/plugins
+                          :shortcut.category/others))]
+       (swap! *category update category #(conj % id))))))
 
 (defn remove-shortcut!
   [handler-id id]

+ 2 - 1
src/main/frontend/modules/shortcut/core.cljs

@@ -93,7 +93,8 @@
   (when-let [handler (get-handler-by-id handler-id)]
     (when-let [ks (dh/shortcut-binding shortcut-id)]
       (doseq [k ks]
-        (.unregisterShortcut ^js handler (shortcut-utils/undecorate-binding k))))
+        (.unregisterShortcut ^js handler (shortcut-utils/undecorate-binding k)))))
+  (when shortcut-id
     (shortcut-config/remove-shortcut! handler-id shortcut-id)))
 
 (defn uninstall-shortcut-handler!

+ 16 - 27
src/main/logseq/api.cljs

@@ -4,7 +4,6 @@
             [logseq.sdk.core]
             [logseq.sdk.utils :as sdk-utils]
             [logseq.sdk.ui :as sdk-ui]
-            [logseq.sdk.git :as sdk-git]
             [logseq.sdk.assets :as sdk-assets]
             [clojure.string :as string]
             [datascript.core :as d]
@@ -31,6 +30,7 @@
             [frontend.modules.outliner.tree :as outliner-tree]
             [frontend.handler.command-palette :as palette-handler]
             [frontend.modules.shortcut.core :as st]
+            [frontend.modules.shortcut.config :as shortcut-config]
             [electron.listener :as el]
             [frontend.state :as state]
             [frontend.util :as util]
@@ -352,7 +352,7 @@
             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))
+            palette-cmd (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
@@ -369,8 +369,16 @@
                                  (palette-handler/invoke-command palette-cmd)
                                  (action')))
                 [mode-id id shortcut-map] (update shortcut-args 2 merge cmd {:fn dispatch-cmd :cmd palette-cmd})]
-            (println :shortcut/register-shortcut [mode-id id shortcut-map])
-            (st/register-shortcut! mode-id id shortcut-map)))))))
+
+            (cond
+              ;; FIX ME: move to register logic
+              (= mode-id :shortcut.handler/block-editing-only)
+              (shortcut-config/add-shortcut! mode-id id shortcut-map)
+
+              :else
+              (do
+                (println :shortcut/register-shortcut [mode-id id shortcut-map])
+                (st/register-shortcut! mode-id id shortcut-map)))))))))
 
 (defn ^:export unregister_plugin_simple_command
   [pid]
@@ -378,10 +386,10 @@
   (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]
+  (let [cmds-matched (->> (vals @shortcut-config/*shortcut-cmds)
+                          (filter #(string/includes? (str (:id %)) (str "plugin." pid))))]
+    (when (seq cmds-matched)
+      (doseq [cmd cmds-matched]
         (palette-handler/unregister (:id cmd))
         ;; remove keybinding commands
         (when (seq (:shortcut cmd))
@@ -897,19 +905,10 @@
   (when-let [args (and args (seq (bean/->clj args)))]
     (shell/run-git-command! args)))
 
-;; git
-(def ^:export git_exec_command sdk-git/exec_command)
-(def ^:export git_load_ignore_file sdk-git/load_ignore_file)
-(def ^:export git_save_ignore_file sdk-git/save_ignore_file)
-
 ;; ui
 (def ^:export show_msg sdk-ui/-show_msg)
-(def ^:export ui_show_msg sdk-ui/show_msg)
-(def ^:export ui_close_msg sdk-ui/close_msg)
 
 ;; assets
-(def ^:export assets_list_files_of_current_graph sdk-assets/list_files_of_current_graph)
-(def ^:export assets_make_url sdk-assets/make_url)
 (def ^:export make_asset_url sdk-assets/make_url)
 
 ;; experiments
@@ -998,16 +997,6 @@
       (p/then #(bean/->js %))))
 
 ;; 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)]

+ 13 - 1
src/main/logseq/sdk/assets.cljs

@@ -2,7 +2,10 @@
   (:require [electron.ipc :as ipc]
             [cljs-bean.core :as bean]
             [promesa.core :as p]
-            [frontend.handler.editor :as editor-handler]))
+            [frontend.handler.editor :as editor-handler]
+            [frontend.extensions.pdf.assets :as pdf-assets]
+            [frontend.state :as state]
+            [frontend.util :as util]))
 
 (def ^:export make_url editor-handler/make-asset-url)
 
@@ -10,3 +13,12 @@
   [^js exts]
   (p/let [files (ipc/ipc :getAssetsFiles {:exts exts})]
     (bean/->js files)))
+
+(defn ^:export built_in_open
+  [asset-file]
+  (when-let [ext (util/trim-safe (util/get-file-ext asset-file))]
+    (cond
+      (contains? #{"pdf"} ext)
+      (state/set-current-pdf! (pdf-assets/inflate-asset asset-file))
+
+      :else false)))

+ 32 - 4
src/main/logseq/sdk/ui.cljs

@@ -1,7 +1,9 @@
 (ns logseq.sdk.ui
   (:require [frontend.handler.notification :as notification]
             [cljs-bean.core :as bean]
+            [goog.dom :as gdom]
             [sci.core :as sci]
+            [frontend.util :as util]
             [clojure.string :as string]))
 
 (defn- parse-hiccup-ui
@@ -19,9 +21,9 @@
    (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 nil)]
+         uid (when (string? key) (keyword key))
+         clear? (not= timeout 0)
+         key' (notification/show! content (keyword status) clear? uid timeout nil)]
      (name key'))))
 
 (defn ^:export show_msg
@@ -31,4 +33,30 @@
 (defn ^:export close_msg
   [key]
   (when (string? key)
-    (notification/clear! (keyword key)) nil))
+    (notification/clear! (keyword key)) nil))
+
+(defn ^:export query_element_rect
+  [selector]
+  (when-let [^js el (js/document.querySelector selector)]
+    (bean/->js (.toJSON (.getBoundingClientRect el)))))
+
+(defn ^:export query_element_by_id
+  [id]
+  (when-let [^js el (gdom/getElement id)]
+    (if el (str (.-tagName el) "#" id) false)))
+
+(defn ^:export check_slot_valid
+  [slot]
+  (when (string? slot)
+    (boolean (query_element_by_id slot))))
+
+(defn ^:export resolve_theme_css_props_vals
+  [props]
+  (when-let [props (if (string? props) [props] (bean/->clj props))]
+    (let [^js s (js/window.getComputedStyle js/document.body)]
+      (some->> (for [prop props]
+                 (when (string? prop)
+                   [prop (util/trim-safe (.getPropertyValue s prop))]))
+               (remove empty?)
+               (into {})
+               (bean/->js)))))

Some files were not shown because too many files changed in this diff