Browse Source

enhance(plugin): support custom routes from the plugin sdk

charlie 1 year ago
parent
commit
53125d6315

+ 1 - 1
deps/shui/src/logseq/shui/ui.cljs

@@ -12,7 +12,7 @@
 (def button base-core/button)
 (def button base-core/button)
 (def link base-core/link)
 (def link base-core/link)
 (def trigger-as base-core/trigger-as)
 (def trigger-as base-core/trigger-as)
-(def tabler-icon icon-v2/root)
+(def ^:export tabler-icon icon-v2/root)
 
 
 (def alert (util/lsui-wrap "Alert"))
 (def alert (util/lsui-wrap "Alert"))
 (def alert-title (util/lsui-wrap "AlertTitle"))
 (def alert-title (util/lsui-wrap "AlertTitle"))

+ 20 - 3
libs/src/modules/LSPlugin.Experiments.ts

@@ -44,7 +44,7 @@ export class LSPluginExperiments {
   }
   }
 
 
   registerFencedCodeRenderer(
   registerFencedCodeRenderer(
-    type: string,
+    lang: string,
     opts: {
     opts: {
       edit?: boolean
       edit?: boolean
       before?: () => Promise<void>
       before?: () => Promise<void>
@@ -54,7 +54,23 @@ export class LSPluginExperiments {
   ) {
   ) {
     return this.ensureHostScope().logseq.api.exper_register_fenced_code_renderer(
     return this.ensureHostScope().logseq.api.exper_register_fenced_code_renderer(
       this.ctx.baseInfo.id,
       this.ctx.baseInfo.id,
-      type,
+      lang,
+      opts
+    )
+  }
+
+  registerRouteRenderer(
+    key: string,
+    opts: {
+      name?: string,
+      path: string,
+      render: (props: { content: string }) => any
+      subs?: Array<string>
+    }
+  ) {
+    return this.ensureHostScope().logseq.api.exper_register_route_renderer(
+      this.ctx.baseInfo.id,
+      key,
       opts
       opts
     )
     )
   }
   }
@@ -83,7 +99,8 @@ export class LSPluginExperiments {
 
 
   ensureHostScope(): any {
   ensureHostScope(): any {
     if (window === top) {
     if (window === top) {
-      throw new Error('Can not access host scope!')
+      console.error('Can not access host scope!')
+      return {}
     }
     }
 
 
     return top
     return top

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

@@ -3536,7 +3536,7 @@
       (let [lang (util/safe-lower-case (:language options))]
       (let [lang (util/safe-lower-case (:language options))]
         [:div.cp__fenced-code-block
         [:div.cp__fenced-code-block
          {:data-lang lang}
          {:data-lang lang}
-         (if-let [opts (plugin-handler/hook-fenced-code-by-type lang)]
+         (if-let [opts (plugin-handler/hook-fenced-code-by-lang lang)]
            [:div.ui-fenced-code-wrap
            [:div.ui-fenced-code-wrap
             (src-cp config options html-export?)
             (src-cp config options html-export?)
             (plugins/hook-ui-fenced-code (:block config) (string/join "" (:lines options)) opts)]
             (plugins/hook-ui-fenced-code (:block config) (string/join "" (:lines options)) opts)]

+ 2 - 2
src/main/frontend/components/lazy_editor.cljs

@@ -5,7 +5,7 @@
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.state :as state]
             [frontend.state :as state]
-            [frontend.handler.plugin :refer [hook-extensions-enhancer-by-type]]
+            [frontend.handler.plugin :refer [hook-extensions-enhancer-by-key]]
             [promesa.core :as p]))
             [promesa.core :as p]))
 
 
 ;; TODO: Why does shadow fail when code is required
 ;; TODO: Why does shadow fail when code is required
@@ -23,7 +23,7 @@
                   (if-not @loaded?
                   (if-not @loaded?
                     (p/finally
                     (p/finally
                      (p/all (when-let [enhancers (and config/lsp-enabled?
                      (p/all (when-let [enhancers (and config/lsp-enabled?
-                                                      (seq (hook-extensions-enhancer-by-type :codemirror)))]
+                                                      (seq (hook-extensions-enhancer-by-key :codemirror)))]
                               (for [{f :enhancer} enhancers]
                               (for [{f :enhancer} enhancers]
                                 (when (fn? f) (f (. js/window -CodeMirror))))))
                                 (when (fn? f) (f (. js/window -CodeMirror))))))
                      (fn []
                      (fn []

+ 11 - 0
src/main/frontend/components/plugins.cljs

@@ -12,6 +12,7 @@
             [frontend.search :as search]
             [frontend.search :as search]
             [frontend.util :as util]
             [frontend.util :as util]
             [frontend.mixins :as mixins]
             [frontend.mixins :as mixins]
+            [frontend.config :as config]
             [logseq.shui.ui :as shui]
             [logseq.shui.ui :as shui]
             [electron.ipc :as ipc]
             [electron.ipc :as ipc]
             [promesa.core :as p]
             [promesa.core :as p]
@@ -1426,3 +1427,13 @@
        (focused-settings-content title)])
        (focused-settings-content title)])
     {:label   "plugin-settings-modal"
     {:label   "plugin-settings-modal"
      :id      "ls-focused-settings-modal"}))
      :id      "ls-focused-settings-modal"}))
+
+(defn hook-custom-routes
+  [routes]
+  (cond-> routes
+    config/lsp-enabled?
+    (concat (some->> (plugin-handler/hook-routes-renderer)
+              (mapv #(when-let [{:keys [name path render]} %]
+                       (when (not (string/blank? path))
+                         [path {:name name :view (fn [r] (render r %))}])))
+              (remove nil?)))))

+ 8 - 3
src/main/frontend/core.cljs

@@ -5,6 +5,7 @@
             [frontend.handler :as handler]
             [frontend.handler :as handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.plugin :as plugin-handler]
             [frontend.handler.route :as route-handler]
             [frontend.handler.route :as route-handler]
+            [frontend.components.plugins :as plugins]
             [frontend.page :as page]
             [frontend.page :as page]
             [frontend.routes :as routes]
             [frontend.routes :as routes]
             [frontend.spec]
             [frontend.spec]
@@ -19,7 +20,7 @@
 (defn set-router!
 (defn set-router!
   []
   []
   (rfe/start!
   (rfe/start!
-   (rf/router routes/routes nil)
+   (rf/router (plugins/hook-custom-routes routes/routes) nil)
    (fn [route]
    (fn [route]
      (route-handler/set-route-match! route)
      (route-handler/set-route-match! route)
      (plugin-handler/hook-plugin-app
      (plugin-handler/hook-plugin-app
@@ -43,7 +44,7 @@
             \\/    /_____/     \\/     \\/   |__|
             \\/    /_____/     \\/     \\/   |__|
      "))
      "))
 
 
-(defn start []
+(defn ^:export start []
   (when config/dev?
   (when config/dev?
     (md/start!))
     (md/start!))
   (when-let [node (.getElementById js/document "root")]
   (when-let [node (.getElementById js/document "root")]
@@ -62,10 +63,14 @@
   (plugin-handler/setup!
   (plugin-handler/setup!
    #(handler/start! start)))
    #(handler/start! start)))
 
 
-(defn stop []
+(defn ^:export stop []
   ;; stop is called before any code is reloaded
   ;; stop is called before any code is reloaded
   ;; this is controlled by :before-load in the config
   ;; this is controlled by :before-load in the config
   (handler/stop!)
   (handler/stop!)
   (when config/dev?
   (when config/dev?
     (sync/<sync-stop))
     (sync/<sync-stop))
   (js/console.log "stop"))
   (js/console.log "stop"))
+
+(defn ^:export delay-remount
+  [delay]
+  (js/setTimeout #(start) delay))

+ 2 - 2
src/main/frontend/extensions/latex.cljs

@@ -4,7 +4,7 @@
             [frontend.ui :as ui]
             [frontend.ui :as ui]
             [frontend.config :as config]
             [frontend.config :as config]
             [frontend.util :as util]
             [frontend.util :as util]
-            [frontend.handler.plugin :refer [hook-extensions-enhancer-by-type] :as plugin-handler]
+            [frontend.handler.plugin :refer [hook-extensions-enhancer-by-key] :as plugin-handler]
             [promesa.core :as p]
             [promesa.core :as p]
             [goog.dom :as gdom]))
             [goog.dom :as gdom]))
 
 
@@ -42,7 +42,7 @@
           (config/asset-uri "/static/js/mhchem.min.js")
           (config/asset-uri "/static/js/mhchem.min.js")
           (fn []
           (fn []
             (p/finally
             (p/finally
-              (p/all (when-let [enhancers (and config/lsp-enabled? (seq (hook-extensions-enhancer-by-type :katex)))]
+              (p/all (when-let [enhancers (and config/lsp-enabled? (seq (hook-extensions-enhancer-by-key :katex)))]
                        (for [{f :enhancer} enhancers]
                        (for [{f :enhancer} enhancers]
                          (when (fn? f) (f js/window.katex)))))
                          (when (fn? f) (f js/window.katex)))))
               (fn []
               (fn []

+ 53 - 35
src/main/frontend/handler/plugin.cljs

@@ -340,10 +340,11 @@
   (when-let [pid (keyword pid)]
   (when-let [pid (keyword pid)]
     (when-let [type (and key (keyword type))]
     (when-let [type (and key (keyword type))]
       (let [path [:plugin/installed-resources pid type]]
       (let [path [:plugin/installed-resources pid type]]
-        (when (contains? #{:error nil} (get-in @state/state (conj path key)))
-          (swap! state/state update-in path
-                 (fnil assoc {}) key (merge opts {:pid pid}))
-          true)))))
+        ;; TODO: conditions
+        ;; (when (contains? #{:error nil} (get-in @state/state (conj path key))))
+        (swap! state/state update-in path
+          (fnil assoc {}) key (merge opts {:pid pid}))
+        true))))
 
 
 (defn unregister-plugin-resources
 (defn unregister-plugin-resources
   [pid]
   [pid]
@@ -380,39 +381,55 @@
   (when-let [hook (and uuid (str "hook:db:block_" (string/replace (str uuid) "-" "_")))]
   (when-let [hook (and uuid (str "hook:db:block_" (string/replace (str uuid) "-" "_")))]
     (boolean (seq (get (get-installed-hooks) hook)))))
     (boolean (seq (get (get-installed-hooks) hook)))))
 
 
-(defonce *fenced-code-providers (atom #{}))
+(defn- create-local-renderer-register
+  [type *providers]
+  (fn [pid key {:keys [subs render] :as _opts}]
+    (when-let [key (and key (keyword key))]
+      (register-plugin-resources pid type
+        (merge _opts {:key key :subs subs :render render}))
+      (swap! *providers conj pid)
+      #(swap! *providers disj pid))))
+
+(defn- create-local-renderer-getter
+  ([type *providers] (create-local-renderer-getter type *providers false))
+  ([type *providers many?]
+   (fn [key]
+     (when-let [key (or many? (and (seq @*providers) key (keyword key)))]
+       (when-let [rs (->> @*providers
+                       (map #(if many?
+                               (some-> (state/get-plugin-resources-with-type % type)
+                                 (vals))
+                               (state/get-plugin-resource % type key)))
+                       (remove nil?)
+                       (flatten)
+                       (seq))]
+         (if many? rs (first rs)))))))
 
 
-(defn register-fenced-code-renderer
-  [pid type {:keys [before subs render edit] :as _opts}]
-  (when-let [key (and type (keyword type))]
-    (register-plugin-resources pid :fenced-code-renderers
-                               {:key key :edit edit :before before :subs subs :render render})
-    (swap! *fenced-code-providers conj pid)
-    #(swap! *fenced-code-providers disj pid)))
-
-(defn hook-fenced-code-by-type
-  [type]
-  (when-let [key (and (seq @*fenced-code-providers) type (keyword type))]
-    (->> @*fenced-code-providers
-         (map #(state/get-plugin-resource % :fenced-code-renderers key))
-         (remove nil?)
-         (first))))
+(defonce *fenced-code-providers (atom #{}))
+(def register-fenced-code-renderer
+  ;; [pid key payload]
+  (create-local-renderer-register
+    :fenced-code-renderers *fenced-code-providers))
+(def hook-fenced-code-by-lang
+  ;; [key]
+  (create-local-renderer-getter
+    :fenced-code-renderers *fenced-code-providers))
 
 
 (def *extensions-enhancer-providers (atom #{}))
 (def *extensions-enhancer-providers (atom #{}))
-
-(defn register-extensions-enhancer
-  [pid type {:keys [enhancer] :as _opts}]
-  (when-let [key (and type (keyword type))]
-    (register-plugin-resources pid :extensions-enhancers
-                               {:key key :enhancer enhancer})
-    (swap! *extensions-enhancer-providers conj pid)
-    #(swap! *extensions-enhancer-providers disj pid)))
-
-(defn hook-extensions-enhancer-by-type
-  [type]
-  (when-let [key (and type (keyword type))]
-    (map #(state/get-plugin-resource % :extensions-enhancers key)
-         @*extensions-enhancer-providers)))
+(def register-extensions-enhancer
+  (create-local-renderer-register
+    :extensions-enhancers *extensions-enhancer-providers))
+(def hook-extensions-enhancer-by-key
+  (create-local-renderer-getter
+    :extensions-enhancers *extensions-enhancer-providers))
+
+(def *route-renderer-providers (atom #{}))
+(def register-route-renderer
+  (create-local-renderer-register
+    :route-renderers *route-renderer-providers))
+(def hook-routes-renderer
+  (create-local-renderer-getter
+    :route-renderers *route-renderer-providers true))
 
 
 (defn select-a-plugin-theme
 (defn select-a-plugin-theme
   [pid]
   [pid]
@@ -788,7 +805,8 @@
       (p/then
       (p/then
         (fn []
         (fn []
           (state/set-state! :plugin/indicator-text "END")
           (state/set-state! :plugin/indicator-text "END")
-          (callback)))
+          ;; wait for the plugin register async messages
+          (js/setTimeout #(callback) 64)))
       (p/catch
       (p/catch
         (fn [^js e]
         (fn [^js e]
           (log/error :setup-plugin-system-error e)
           (log/error :setup-plugin-system-error e)

+ 6 - 1
src/main/frontend/util.cljc

@@ -69,7 +69,12 @@
 #?(:cljs (def string-join-path common-util/string-join-path))
 #?(:cljs (def string-join-path common-util/string-join-path))
 
 
 #?(:cljs
 #?(:cljs
-   (def safe-re-find common-util/safe-re-find))
+   (do
+     (def safe-re-find common-util/safe-re-find)
+     (defn safe-keyword
+       [s]
+       (when (string? s)
+         (keyword (string/replace s " " "_"))))))
 
 
 #?(:cljs
 #?(:cljs
    (do
    (do

+ 14 - 1
src/main/logseq/api.cljs

@@ -982,12 +982,25 @@
       (keyword pid) type (reduce #(assoc %1 %2 (aget opts (name %2))) {}
       (keyword pid) type (reduce #(assoc %1 %2 (aget opts (name %2))) {}
                                  [:edit :before :subs :render]))))
                                  [:edit :before :subs :render]))))
 
 
+(defn ^:export exper_register_route_renderer
+  [pid key ^js opts]
+  (when-let [^js _pl (plugin-handler/get-plugin-inst pid)]
+    (let [key (util/safe-keyword key)]
+      (plugin-handler/register-route-renderer
+        (keyword pid) key
+        (reduce (fn [r k]
+                  (assoc r k (cond-> (aget opts (name k))
+                               (= :name k)
+                               (#(if % (util/safe-keyword %) key)))))
+          {} [:v :name :path :subs :render])))))
+
 (defn ^:export exper_register_extensions_enhancer
 (defn ^:export exper_register_extensions_enhancer
   [pid type enhancer]
   [pid type enhancer]
   (when-let [^js _pl (and (fn? enhancer) (plugin-handler/get-plugin-inst pid))]
   (when-let [^js _pl (and (fn? enhancer) (plugin-handler/get-plugin-inst pid))]
     (plugin-handler/register-extensions-enhancer
     (plugin-handler/register-extensions-enhancer
       (keyword pid) type {:enhancer enhancer})))
       (keyword pid) type {:enhancer enhancer})))
 
 
+;; http request
 (defonce *request-k (volatile! 0))
 (defonce *request-k (volatile! 0))
 
 
 (defn ^:export exper_request
 (defn ^:export exper_request
@@ -1060,4 +1073,4 @@
   []
   []
   true)
   true)
 
 
-(def ^:export set_blocks_id #(editor-handler/set-blocks-id! (map uuid %)))
+(def ^:export set_blocks_id #(editor-handler/set-blocks-id! (map uuid %)))

+ 7 - 2
src/main/logseq/sdk/utils.cljs

@@ -1,7 +1,8 @@
 (ns logseq.sdk.utils
 (ns logseq.sdk.utils
   (:require [clojure.walk :as walk]
   (:require [clojure.walk :as walk]
             [camel-snake-kebab.core :as csk]
             [camel-snake-kebab.core :as csk]
-            [frontend.util :as util]))
+            [frontend.util :as util]
+            [cljs-bean.core :as bean]))
 
 
 (defn normalize-keyword-for-json
 (defn normalize-keyword-for-json
   ([input] (normalize-keyword-for-json input true))
   ([input] (normalize-keyword-for-json input true))
@@ -28,4 +29,8 @@
     (uuid s)
     (uuid s)
 
 
     :else
     :else
-    (throw (js/Error. (str s " is not a valid UUID string.")))))
+    (throw (js/Error. (str s " is not a valid UUID string.")))))
+
+(def ^:export to-clj bean/->clj)
+(def ^:export to-keyword keyword)
+(def ^:export to-symbol symbol)