|
|
@@ -1,65 +1,106 @@
|
|
|
(ns frontend.components.plugins
|
|
|
- (:require [cljs-bean.core :as bean]
|
|
|
- [clojure.string :as string]
|
|
|
- [frontend.components.svg :as svg]
|
|
|
- [frontend.handler.notification :as notification]
|
|
|
- [frontend.handler.plugin :as plugin-handler]
|
|
|
+ (:require [rum.core :as rum]
|
|
|
[frontend.state :as state]
|
|
|
+ [cljs-bean.core :as bean]
|
|
|
+ [frontend.context.i18n :as i18n]
|
|
|
[frontend.ui :as ui]
|
|
|
[frontend.util :as util]
|
|
|
+ [frontend.mixins :as mixins]
|
|
|
+ [electron.ipc :as ipc]
|
|
|
[promesa.core :as p]
|
|
|
- [rum.core :as rum]))
|
|
|
+ [frontend.components.svg :as svg]
|
|
|
+ [frontend.handler.notification :as notification]
|
|
|
+ [frontend.handler.plugin :as plugin-handler]
|
|
|
+ [clojure.string :as string]))
|
|
|
|
|
|
-(rum/defc installed-themes
|
|
|
+(rum/defcs installed-themes
|
|
|
< rum/reactive
|
|
|
- []
|
|
|
- (let [themes (state/sub :plugin/installed-themes)
|
|
|
+ (rum/local 0 ::cursor)
|
|
|
+ (rum/local 0 ::total)
|
|
|
+ (mixins/event-mixin
|
|
|
+ (fn [state]
|
|
|
+ (let [*cursor (::cursor state)
|
|
|
+ *total (::total state)
|
|
|
+ ^js target (rum/dom-node state)]
|
|
|
+ (.focus target)
|
|
|
+ (mixins/on-key-down
|
|
|
+ state {38 ;; up
|
|
|
+ (fn [^js e]
|
|
|
+ (reset! *cursor
|
|
|
+ (if (zero? @*cursor)
|
|
|
+ (dec @*total) (dec @*cursor))))
|
|
|
+ 40 ;; down
|
|
|
+ (fn [^js e]
|
|
|
+ (reset! *cursor
|
|
|
+ (if (= @*cursor (dec @*total))
|
|
|
+ 0 (inc @*cursor))))
|
|
|
+
|
|
|
+ 13 ;; enter
|
|
|
+ #(when-let [^js active (.querySelector target ".is-active")]
|
|
|
+ (.click active))
|
|
|
+ }))))
|
|
|
+ [state]
|
|
|
+ (let [*cursor (::cursor state)
|
|
|
+ *total (::total state)
|
|
|
+ themes (state/sub :plugin/installed-themes)
|
|
|
selected (state/sub :plugin/selected-theme)
|
|
|
- themes (cons {:name "Default Theme" :url nil :description "Logseq default light/dark theme."} themes)]
|
|
|
-
|
|
|
- [:div.cp__themes-installed
|
|
|
- [:h2.mb-4.text-xl "Installed Themes"]
|
|
|
- (for [opt themes]
|
|
|
- (let [current-selected (= selected (:url opt))]
|
|
|
- [:div.it.flex.px-3.py-2.mb-2.rounded-sm.justify-between
|
|
|
- {:key (:url opt)
|
|
|
- :class [(when current-selected "is-selected")]
|
|
|
- :on-click #(do (js/LSPluginCore.selectTheme (if current-selected nil (clj->js opt)))
|
|
|
- (state/set-modal! nil))}
|
|
|
- [:section
|
|
|
- [:strong.block (:name opt)]
|
|
|
- [:small.opacity-30 (:description opt)]]
|
|
|
- [:small.flex-shrink-0.flex.items-center.opacity-10
|
|
|
- (when current-selected "current")]]))]))
|
|
|
+ themes (cons {:name "Default Theme" :url nil :description "Logseq default light/dark theme."} themes)
|
|
|
+ themes (sort #(:selected %) (map #(assoc % :selected (= (:url %) selected)) themes))
|
|
|
+ _ (reset! *total (count themes))]
|
|
|
+
|
|
|
+ (rum/with-context
|
|
|
+ [[t] i18n/*tongue-context*]
|
|
|
+
|
|
|
+ [:div.cp__themes-installed
|
|
|
+ {:tab-index -1}
|
|
|
+ [:h1.mb-4.text-2xl.p-2 (t :themes)]
|
|
|
+ (map-indexed
|
|
|
+ (fn [idx opt]
|
|
|
+ (let [current-selected (:selected opt)
|
|
|
+ plg (get (:plugin/installed-plugins @state/state) (keyword (:pid opt)))]
|
|
|
+ [:div.it.flex.px-3.py-1.5.rounded-sm.justify-between
|
|
|
+ {:key (:url opt)
|
|
|
+ :title (if current-selected "Cancel selected theme")
|
|
|
+ :class (util/classnames
|
|
|
+ [{:is-selected current-selected
|
|
|
+ :is-active (= idx @*cursor)}])
|
|
|
+ :on-click #(do (js/LSPluginCore.selectTheme (if current-selected nil (clj->js opt)))
|
|
|
+ (state/set-modal! nil))}
|
|
|
+ [:section
|
|
|
+ [:strong.block (when plg (str (:name plg) " / ")) (:name opt)]
|
|
|
+ [:small.opacity-30.italic (:description opt)]]
|
|
|
+ [:small.flex-shrink-0.flex.items-center.opacity-10
|
|
|
+ (if current-selected (svg/check 28))]]))
|
|
|
+ themes)])))
|
|
|
|
|
|
(rum/defc unpacked-plugin-loader
|
|
|
[unpacked-pkg-path]
|
|
|
(rum/use-effect!
|
|
|
- (fn []
|
|
|
- (let [err-handle
|
|
|
- (fn [^js e]
|
|
|
- (case (keyword (aget e "name"))
|
|
|
- :IllegalPluginPackageError
|
|
|
- (notification/show! "Illegal Logseq plugin package." :error)
|
|
|
- :ExistedImportedPluginPackageError
|
|
|
- (notification/show! "Existed Imported plugin package." :error)
|
|
|
- :default)
|
|
|
- (plugin-handler/reset-unpacked-state))
|
|
|
- reg-handle #(plugin-handler/reset-unpacked-state)]
|
|
|
- (when unpacked-pkg-path
|
|
|
- (doto js/LSPluginCore
|
|
|
- (.once "error" err-handle)
|
|
|
- (.once "registered" reg-handle)
|
|
|
- (.register (bean/->js {:url unpacked-pkg-path}))))
|
|
|
- #(doto js/LSPluginCore
|
|
|
- (.off "error" err-handle)
|
|
|
- (.off "registered" reg-handle))))
|
|
|
- [unpacked-pkg-path])
|
|
|
+ (fn []
|
|
|
+ (let [err-handle
|
|
|
+ (fn [^js e]
|
|
|
+ (case (keyword (aget e "name"))
|
|
|
+ :IllegalPluginPackageError
|
|
|
+ (notification/show! "Illegal Logseq plugin package." :error)
|
|
|
+ :ExistedImportedPluginPackageError
|
|
|
+ (notification/show! "Existed Imported plugin package." :error)
|
|
|
+ :default)
|
|
|
+ (plugin-handler/reset-unpacked-state))
|
|
|
+ reg-handle #(plugin-handler/reset-unpacked-state)]
|
|
|
+ (when unpacked-pkg-path
|
|
|
+ (doto js/LSPluginCore
|
|
|
+ (.once "error" err-handle)
|
|
|
+ (.once "registered" reg-handle)
|
|
|
+ (.register (bean/->js {:url unpacked-pkg-path}))))
|
|
|
+ #(doto js/LSPluginCore
|
|
|
+ (.off "error" err-handle)
|
|
|
+ (.off "registered" reg-handle))))
|
|
|
+ [unpacked-pkg-path])
|
|
|
|
|
|
(when unpacked-pkg-path
|
|
|
[:strong.inline-flex.px-3 "Loading ..."]))
|
|
|
|
|
|
-(rum/defc simple-markdown-display
|
|
|
+(rum/defc local-markdown-display
|
|
|
< rum/reactive
|
|
|
[]
|
|
|
(let [[content item] (state/sub :plugin/active-readme)]
|
|
|
@@ -73,90 +114,240 @@
|
|
|
(when-let [repo (:repository item)]
|
|
|
(when-let [repo (if (string? repo) repo (:url repo))]
|
|
|
[:div.p-4.rounded-md.bg-base-3
|
|
|
- [:strong [:a.flex.items-center {:target "_blank" :href repo} [:span.mr-1 (svg/github {:width 25 :height 25})] repo]]]))
|
|
|
+ [:strong [:a.flex.items-center {:target "_blank" :href repo}
|
|
|
+ [:span.mr-1 (svg/github {:width 25 :height 25})] repo]]]))
|
|
|
[:div.p-1.bg-transparent.border-none.ls-block
|
|
|
{:style {:min-height "60vw"
|
|
|
:max-width 900}
|
|
|
:dangerouslySetInnerHTML {:__html content}}]]))
|
|
|
|
|
|
-(rum/defc plugin-item-card
|
|
|
- [{:keys [id name settings version url description author icon usf] :as item}]
|
|
|
- (let [disabled (:disabled settings)]
|
|
|
- [:div.cp__plugins-item-card
|
|
|
- [:div.l.link-block
|
|
|
- {:on-click #(plugin-handler/open-readme! url item simple-markdown-display)}
|
|
|
- (if icon
|
|
|
- [:img.icon {:src icon}]
|
|
|
- svg/folder)]
|
|
|
- [:div.r
|
|
|
- [:h3.head.text-xl.font-bold.pt-1.5
|
|
|
- {:on-click #(plugin-handler/open-readme! url item simple-markdown-display)}
|
|
|
- [:span name]
|
|
|
- [:sup.inline-block.px-1.text-xs.opacity-30 version]]
|
|
|
- [:div.desc.text-xs.opacity-60
|
|
|
- [:p description]
|
|
|
- ;;[:small (js/JSON.stringify (bean/->js settings))]
|
|
|
-]
|
|
|
- [:div.flag
|
|
|
- [:p.text-xs.text-gray-300.pr-2.flex.justify-between.dark:opacity-40
|
|
|
- [:small author]
|
|
|
- [:small (str "ID: " id)]]]
|
|
|
-
|
|
|
- [:div.ctl
|
|
|
- [:div.l
|
|
|
- [:div.de
|
|
|
- [:strong svg/settings-sm]
|
|
|
- [:ul.menu-list
|
|
|
- [:li {:on-click #(when usf (js/apis.openPath usf))} "Open settings"]
|
|
|
- [:li {:on-click #(js/apis.openPath url)} "Open plugin package"]
|
|
|
- [:li {:on-click
|
|
|
- #(let [confirm-fn
|
|
|
- (ui/make-confirm-modal
|
|
|
- {:title (str "Are you sure uninstall plugin [" name "] ?")
|
|
|
- :on-confirm (fn [_ {:keys [close-fn]}]
|
|
|
- (close-fn)
|
|
|
- (plugin-handler/unregister-plugin id))})]
|
|
|
- (state/set-modal! confirm-fn))}
|
|
|
- "Uninstall plugin"]]]]
|
|
|
-
|
|
|
- [:div.flex.items-center
|
|
|
- [:small.de (if disabled "Disabled" "Enabled")]
|
|
|
- (ui/toggle (not disabled)
|
|
|
- (fn []
|
|
|
- (js-invoke js/LSPluginCore (if disabled "enable" "disable") id))
|
|
|
- true)]]]]))
|
|
|
-
|
|
|
-(rum/defc installed-page
|
|
|
- < rum/reactive
|
|
|
+(rum/defc remote-readme-display
|
|
|
+ [repo content]
|
|
|
+
|
|
|
+ (let [src (str "lsp://logseq.com/marketplace.html?repo=" repo)]
|
|
|
+ [:iframe.lsp-frame-readme {:src src}]))
|
|
|
+
|
|
|
+(defn security-warning
|
|
|
[]
|
|
|
+ (ui/admonition
|
|
|
+ :warning
|
|
|
+ [:div.max-w-4xl
|
|
|
+ "Plugins can access your graph and your local files, issue network requests.
|
|
|
+ They can also cause data corruption or loss. We're working on proper access rules for your graphs.
|
|
|
+ Meanwhile, make sure you have regular backups of your graphs and only install the plugins when you can read and
|
|
|
+ understand the source code."]))
|
|
|
+
|
|
|
+(rum/defc plugin-item-card < rum/static
|
|
|
+ [{:keys [id name title settings version url description author icon usf iir repo] :as item}
|
|
|
+ installing-or-updating? installed? stat]
|
|
|
+
|
|
|
+ (let [market? (and (not (nil? repo)) (nil? usf))
|
|
|
+ disabled (:disabled settings)
|
|
|
+ name (or title name "Untitled")]
|
|
|
+ (rum/with-context
|
|
|
+ [[t] i18n/*tongue-context*]
|
|
|
+
|
|
|
+ [:div.cp__plugins-item-card
|
|
|
+ {:class (util/classnames [{:market market?}])}
|
|
|
+
|
|
|
+ [:div.l.link-block
|
|
|
+ {:on-click #(plugin-handler/open-readme!
|
|
|
+ url item (if repo remote-readme-display local-markdown-display))}
|
|
|
+ (if (and icon (not (string/blank? icon)))
|
|
|
+ [:img.icon {:src (if market? (plugin-handler/pkg-asset id icon) icon)}]
|
|
|
+ svg/folder)
|
|
|
+
|
|
|
+ (when-not (or market? iir)
|
|
|
+ [:span.flex.justify-center.text-xs.text-red-500.pt-2 "unpacked"])]
|
|
|
+
|
|
|
+ [:div.r
|
|
|
+ [:h3.head.text-xl.font-bold.pt-1.5
|
|
|
+
|
|
|
+ [:span name]
|
|
|
+ (if (not market?) [:sup.inline-block.px-1.text-xs.opacity-30 version])]
|
|
|
+
|
|
|
+ [:div.desc.text-xs.opacity-60
|
|
|
+ [:p description]
|
|
|
+ ;;[:small (js/JSON.stringify (bean/->js settings))]
|
|
|
+ ]
|
|
|
+
|
|
|
+ [:div.flag
|
|
|
+ [:p.text-xs.pr-2.flex.justify-between
|
|
|
+ [:small author]
|
|
|
+ [:small {:on-click #(do
|
|
|
+ (notification/show! "Copied!" :success)
|
|
|
+ (util/copy-to-clipboard! id))}
|
|
|
+ (str "ID: " id)]]]
|
|
|
+
|
|
|
+ [:div.flag.is-top.opacity-50
|
|
|
+ (if repo
|
|
|
+ [:a.flex {:target "_blank"
|
|
|
+ :href (plugin-handler/gh-repo-url repo)}
|
|
|
+ (svg/github {:width 16 :height 16})])]
|
|
|
+
|
|
|
+ (if market?
|
|
|
+ ;; market ctls
|
|
|
+ [:div.ctl
|
|
|
+ [:ul.l.flex.items-center
|
|
|
+ ;; downloads
|
|
|
+ [:li.flex.text-sm.items-center.pr-3 (svg/star 16) [:span.pl-1 (:stargazers_count stat)]]
|
|
|
+
|
|
|
+ ;; stars
|
|
|
+ (when-let [downloads (and stat (reduce (fn [a b] (+ a (get b 2))) 0 (:releases stat)))]
|
|
|
+ (if (and downloads (> downloads 0))
|
|
|
+ [:li.flex.text-sm.items-center.pr-3 (svg/cloud-down 16) [:span.pl-1 downloads]]))]
|
|
|
+
|
|
|
+ [:div.r.flex.items-center
|
|
|
+
|
|
|
+ [:a.btn
|
|
|
+ {:class (util/classnames [{:disabled (or installed? installing-or-updating?)
|
|
|
+ :installing installing-or-updating?}])
|
|
|
+ :on-click #(plugin-handler/install-marketplace-plugin item)}
|
|
|
+ (if installed?
|
|
|
+ (t :plugin/installed)
|
|
|
+ (if installing-or-updating?
|
|
|
+ [:span.flex.items-center [:small svg/loading]
|
|
|
+ (t :plugin/installing)]
|
|
|
+ (t :plugin/install)))]]]
|
|
|
+
|
|
|
+ ;; installed ctls
|
|
|
+ [:div.ctl
|
|
|
+ [:div.l
|
|
|
+ [:div.de
|
|
|
+ [:strong (svg/settings)]
|
|
|
+ [:ul.menu-list
|
|
|
+ [:li {:on-click #(if usf (js/apis.openPath usf))} (t :plugin/open-settings)]
|
|
|
+ [:li {:on-click #(js/apis.openPath url)} (t :plugin/open-package)]
|
|
|
+ [:li {:on-click
|
|
|
+ #(let [confirm-fn
|
|
|
+ (ui/make-confirm-modal
|
|
|
+ {:title (str "Are you sure uninstall plugin [" name "] ?")
|
|
|
+ :on-confirm (fn [_ {:keys [close-fn]}]
|
|
|
+ (close-fn)
|
|
|
+ (plugin-handler/unregister-plugin id))})]
|
|
|
+ (state/set-modal! confirm-fn))}
|
|
|
+ (t :plugin/uninstall)]]]]
|
|
|
+
|
|
|
+ [:div.r.flex.items-center
|
|
|
+ (if (not disabled)
|
|
|
+ [:a.btn
|
|
|
+ {:on-click #(js-invoke js/LSPluginCore "reload" id)}
|
|
|
+ (t :plugin/reload)])
|
|
|
+
|
|
|
+ (if iir
|
|
|
+ [:a.btn
|
|
|
+ {:class (util/classnames [{:disabled (or installing-or-updating?)
|
|
|
+ :updating installing-or-updating?}])
|
|
|
+ :on-click #(plugin-handler/update-marketplace-plugin
|
|
|
+ item (fn [e] (notification/show! e :error)))}
|
|
|
+
|
|
|
+ (if installing-or-updating?
|
|
|
+ (t :plugin/updating)
|
|
|
+ (t :plugin/update))])
|
|
|
+
|
|
|
+ (ui/toggle (not disabled)
|
|
|
+ (fn []
|
|
|
+ (js-invoke js/LSPluginCore (if disabled "enable" "disable") id))
|
|
|
+ true)]])]])))
|
|
|
+
|
|
|
+(rum/defcs marketplace-plugins
|
|
|
+ < rum/static rum/reactive
|
|
|
+ (rum/local false ::fetching)
|
|
|
+ (rum/local nil ::error)
|
|
|
+ {:did-mount (fn [s]
|
|
|
+ (reset! (::fetching s) true)
|
|
|
+ (reset! (::error s) nil)
|
|
|
+ (-> (plugin-handler/load-marketplace-plugins false)
|
|
|
+ (p/then #(plugin-handler/load-marketplace-stats false))
|
|
|
+ (p/catch #(do (js/console.error %) (reset! (::error s) %)))
|
|
|
+ (p/finally #(reset! (::fetching s) false)))
|
|
|
+ s)}
|
|
|
+ [state]
|
|
|
+ (let [pkgs (state/sub :plugin/marketplace-pkgs)
|
|
|
+ stats (state/sub :plugin/marketplace-stats)
|
|
|
+ installed-plugins (state/sub :plugin/installed-plugins)
|
|
|
+ installing (state/sub :plugin/installing)
|
|
|
+ online? (state/sub :network/online?)
|
|
|
+ *fetching (::fetching state)
|
|
|
+ *error (::error state)]
|
|
|
+
|
|
|
+ (cond
|
|
|
+ (not online?)
|
|
|
+ [:p.flex.justify-center.pt-20.opacity-50
|
|
|
+ (svg/offline 30)]
|
|
|
+
|
|
|
+ @*fetching
|
|
|
+ [:p.flex.justify-center.pt-20
|
|
|
+ svg/loading]
|
|
|
+
|
|
|
+ @*error
|
|
|
+ [:p.flex.justify-center.pt-20.opacity-50
|
|
|
+ "Remote error: " (.-message @*error)]
|
|
|
+
|
|
|
+ :else
|
|
|
+ [:div.cp__plugins-marketplace
|
|
|
+ {:class (util/classnames [{:has-installing (boolean installing)}])}
|
|
|
+ [:div.cp__plugins-item-lists.grid-cols-1.md:grid-cols-2.lg:grid-cols-3
|
|
|
+ (for [item pkgs]
|
|
|
+ (rum/with-key
|
|
|
+ (let [pid (keyword (:id item))]
|
|
|
+ (plugin-item-card
|
|
|
+ item (and installing (= (keyword (:id installing)) pid))
|
|
|
+ (contains? installed-plugins pid)
|
|
|
+ (get stats pid)))
|
|
|
+ (:id item)))]])))
|
|
|
+
|
|
|
+(rum/defcs installed-plugins
|
|
|
+ < rum/static rum/reactive
|
|
|
+ [state]
|
|
|
(let [installed-plugins (state/sub :plugin/installed-plugins)
|
|
|
+ updating (state/sub :plugin/installing)
|
|
|
selected-unpacked-pkg (state/sub :plugin/selected-unpacked-pkg)]
|
|
|
- [:div.cp__plugins-page-installed
|
|
|
- [:h1 "Installed Plugins"]
|
|
|
- (ui/admonition
|
|
|
- :warning
|
|
|
- [:div {:style {:max-width 700}}
|
|
|
- "Plugins can access your graph and your local files, issue network requests. They can also cause data corruption or loss. We're working on proper access rules for your graphs. Meanwhile, make sure you have regular backups of your graphs and only install the plugins when you can read and understand the source code."])
|
|
|
- [:hr]
|
|
|
- [:div.mb-6.flex.items-center.justify-between
|
|
|
- (ui/button
|
|
|
- "Load unpacked plugin"
|
|
|
- :intent "logseq"
|
|
|
- :on-click plugin-handler/load-unpacked-plugin)
|
|
|
- (unpacked-plugin-loader selected-unpacked-pkg)
|
|
|
- (when (util/electron?)
|
|
|
+ (rum/with-context
|
|
|
+ [[t] i18n/*tongue-context*]
|
|
|
+
|
|
|
+ [:div.cp__plugins-installed
|
|
|
+ [:div.mb-4.flex.items-center.justify-between
|
|
|
(ui/button
|
|
|
- [:span.flex.items-center
|
|
|
- ;;svg/settings-sm
|
|
|
- "Open plugin preferences file"]
|
|
|
- :intent "logseq"
|
|
|
- :on-click (fn []
|
|
|
- (p/let [root (plugin-handler/get-ls-dotdir-root)]
|
|
|
- (js/apis.openPath (str root "/preferences.json"))))))]
|
|
|
-
|
|
|
- [:div.cp__plugins-item-lists.grid-cols-1.md:grid-cols-2.lg:grid-cols-3
|
|
|
- (for [[_ item] installed-plugins]
|
|
|
- (rum/with-key (plugin-item-card item) (:id item)))]]))
|
|
|
+ (t :plugin/load-unpacked)
|
|
|
+ :intent "logseq"
|
|
|
+ :on-click plugin-handler/load-unpacked-plugin)
|
|
|
+ (unpacked-plugin-loader selected-unpacked-pkg)
|
|
|
+
|
|
|
+ (when (util/electron?)
|
|
|
+ [:div.flex.align-items
|
|
|
+ (ui/button
|
|
|
+ (t :plugin/open-preferences)
|
|
|
+ :intent "logseq"
|
|
|
+ :on-click (fn []
|
|
|
+ (p/let [root (plugin-handler/get-ls-dotdir-root)]
|
|
|
+ (js/apis.openPath (str root "/preferences.json")))))
|
|
|
+
|
|
|
+ (ui/button
|
|
|
+ [:span.flex.items-center
|
|
|
+ [:span.pr-1
|
|
|
+ (ui/Tippy
|
|
|
+ {:html [:small.inline-flex.py-2.pr-2
|
|
|
+ {:style {:max-width "180px" :text-align "left" :justify-content "flex-start"}}
|
|
|
+ (t :plugin/marketplace-tips)]
|
|
|
+ :arrow true
|
|
|
+ :distance 18
|
|
|
+ :offset -25
|
|
|
+ :theme "transparent"}
|
|
|
+ (svg/info))]
|
|
|
+
|
|
|
+ (t :plugin/restart)]
|
|
|
+ :class "ml-2"
|
|
|
+ :intent "logseq"
|
|
|
+ :on-click #(plugin-handler/invoke-exported-api "relaunch"))])]
|
|
|
+
|
|
|
+ [:div.cp__plugins-item-lists.grid-cols-1.md:grid-cols-2.lg:grid-cols-3
|
|
|
+ (for [[_ item] installed-plugins]
|
|
|
+ (rum/with-key
|
|
|
+ (let [pid (keyword (:id item))]
|
|
|
+ (plugin-item-card
|
|
|
+ item (and updating (= (keyword (:id updating)) pid))
|
|
|
+ true nil)) (:id item)))]])))
|
|
|
|
|
|
(defn open-select-theme!
|
|
|
[]
|
|
|
@@ -167,10 +358,10 @@
|
|
|
([type payload opts]
|
|
|
(let [id (str "slot__" (util/rand-str 8))]
|
|
|
(rum/use-effect!
|
|
|
- (fn []
|
|
|
- (plugin-handler/hook-plugin-app type {:slot id :payload payload} nil)
|
|
|
- #())
|
|
|
- [])
|
|
|
+ (fn []
|
|
|
+ (plugin-handler/hook-plugin-app type {:slot id :payload payload} nil)
|
|
|
+ #())
|
|
|
+ [])
|
|
|
[:div.lsp-hook-ui-slot
|
|
|
(merge opts {:id id})])))
|
|
|
|
|
|
@@ -181,11 +372,11 @@
|
|
|
^js pl (js/LSPluginCore.registeredPlugins.get (name pid))]
|
|
|
|
|
|
(rum/use-effect!
|
|
|
- (fn []
|
|
|
- (when-let [^js el (rum/deref *el)]
|
|
|
- (js/LSPlugin.pluginHelpers.setupInjectedUI.call
|
|
|
- pl #js {:slot (.-id el) :key key :template template} #js {})))
|
|
|
- [])
|
|
|
+ (fn []
|
|
|
+ (when-let [^js el (rum/deref *el)]
|
|
|
+ (js/LSPlugin.pluginHelpers.setupInjectedUI.call
|
|
|
+ pl #js {:slot (.-id el) :key key :template template} #js {})))
|
|
|
+ [])
|
|
|
|
|
|
(if-not (nil? pl)
|
|
|
[:div {:id (uni (str (name key) "-" (name pid)))
|
|
|
@@ -194,10 +385,10 @@
|
|
|
[:span])))
|
|
|
|
|
|
(rum/defcs hook-ui-items < rum/reactive
|
|
|
- "type
|
|
|
- - :toolbar
|
|
|
- - :pagebar
|
|
|
- "
|
|
|
+ "type
|
|
|
+ - :toolbar
|
|
|
+ - :pagebar
|
|
|
+ "
|
|
|
[state type]
|
|
|
(when (state/sub [:plugin/installed-ui-items])
|
|
|
(let [items (state/get-plugins-ui-items-with-type type)]
|
|
|
@@ -206,3 +397,32 @@
|
|
|
:data-type (name type)}
|
|
|
(for [[_ {:keys [key template] :as opts} pid] items]
|
|
|
(rum/with-key (ui-item-renderer pid type opts) key))]))))
|
|
|
+
|
|
|
+(rum/defc plugins-page
|
|
|
+ []
|
|
|
+
|
|
|
+ (let [[active set-active!] (rum/use-state :installed)
|
|
|
+ market? (= active :marketplace)]
|
|
|
+
|
|
|
+ (rum/with-context
|
|
|
+ [[t] i18n/*tongue-context*]
|
|
|
+
|
|
|
+ [:div.cp__plugins-page
|
|
|
+ [:h1 (t :plugins)]
|
|
|
+ (security-warning)
|
|
|
+ [:hr]
|
|
|
+
|
|
|
+ [:div.tabs.flex.items-center.justify-center
|
|
|
+ [:div.tabs-inner.flex.items-center
|
|
|
+ (ui/button [:span.it (t :plugin/installed)]
|
|
|
+ :on-click #(set-active! :installed)
|
|
|
+ :intent "logseq" :class (if-not market? "active" ""))
|
|
|
+
|
|
|
+ (ui/button [:span.mk (svg/apps 16) (t :plugin/marketplace)]
|
|
|
+ :on-click #(set-active! :marketplace)
|
|
|
+ :intent "logseq" :class (if market? "active" ""))]]
|
|
|
+
|
|
|
+ [:div.panels
|
|
|
+ (if market?
|
|
|
+ (marketplace-plugins)
|
|
|
+ (installed-plugins))]])))
|