1
0
Эх сурвалжийг харах

feat: wip, command palette

Weihua Lu 4 жил өмнө
parent
commit
2569611de1

+ 49 - 0
src/main/frontend/components/command_palette.cljs

@@ -0,0 +1,49 @@
+(ns frontend.components.command-palette
+  (:require [frontend.handler.command-palette :as cp]
+            [frontend.search :as search]
+            [frontend.state :as state]
+            [frontend.modules.shortcut.core :as shortcut]
+            [frontend.ui :as ui]
+            [frontend.util :as util]
+            [rum.core :as rum]))
+
+(defn get-matched-commands [commands input]
+  (search/fuzzy-search commands input :limit 7 :extract-fn :desc))
+
+(defn render-command [{:keys [id desc shortcut]} chosen?]
+  [:div
+   {:class (when chosen? "chosen")}
+   [:span (namespace id)]
+   [:span desc]
+   [:code shortcut]])
+
+(rum/defcs command-palette <
+  (shortcut/disable-all-shortcuts)
+  (rum/local "" ::input)
+  {:will-unmount (fn [state]
+                   (state/set-state! :ui/command-palette-open? false)
+                   state)}
+  [state {:keys [commands]}]
+  (let [input (::input state)]
+    [:div#command-palette
+     [:input.cp__command-palette-input
+      {:type        "text"
+       :placeholder "Type a command"
+       :auto-focus   true
+       :value       @input
+       :on-change   (fn [e] (reset! input (util/evalue e)))}]
+     (ui/auto-complete
+      (get-matched-commands commands @input)
+      {:item-render render-command
+       :class       "cp__command-palette-results"
+       :on-chosen   (fn [{:keys [action]}]
+                      (state/set-state! :ui/command-palette-open? false)
+                      (action))})]))
+
+
+(rum/defc command-palette-modal < rum/reactive
+  []
+  (let [open? (state/sub :ui/command-palette-open?)]
+    (when open?
+      (state/set-modal! #(command-palette {:commands (cp/get-commands)})))
+    nil))

+ 11 - 0
src/main/frontend/components/command_palette.css

@@ -0,0 +1,11 @@
+cp__command-palette {
+    &-input {
+    }
+
+    &-results {
+        @apply flex;
+    }
+    .chosen {
+        background: var(--ls-a-chosen-bg);
+    }
+}

+ 2 - 0
src/main/frontend/components/sidebar.cljs

@@ -8,6 +8,7 @@
             [frontend.components.settings :as settings]
             [frontend.components.theme :as theme]
             [frontend.components.widgets :as widgets]
+            [frontend.components.command-palette :as command-palette]
             [frontend.config :as config]
             [frontend.context.i18n :as i18n]
             [frontend.db :as db]
@@ -368,6 +369,7 @@
                          (ui/notification)
                          (ui/modal)
                          (settings-modal)
+                         (command-palette/command-palette-modal)
                          (custom-context-menu)
                          [:a#download.hidden]
                          (when

+ 3 - 1
src/main/frontend/handler.cljs

@@ -14,6 +14,7 @@
             [frontend.handler.page :as page-handler]
             [frontend.handler.repo :as repo-handler]
             [frontend.handler.ui :as ui-handler]
+            [frontend.handler.command-palette :as command-palette]
             [frontend.idb :as idb]
             [frontend.modules.instrumentation.core :as instrument]
             [frontend.modules.shortcut.core :as shortcut]
@@ -173,7 +174,8 @@
 (defn- register-components-fns!
   []
   (state/set-page-blocks-cp! page/page-blocks-cp)
-  (state/set-editor-cp! editor/box))
+  (state/set-editor-cp! editor/box)
+  (command-palette/register-global-shortcut-commands))
 
 (defn start!
   [render]

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

@@ -0,0 +1,43 @@
+(ns frontend.handler.command-palette
+  (:require [cljs.spec.alpha :as s]
+            [frontend.modules.shortcut.data-helper :as shortcut-helper]
+            [frontend.spec :as spec]
+            [frontend.state :as state]
+            [lambdaisland.glogi :as log]))
+
+(s/def :command/id keyword?)
+(s/def :command/desc string?)
+(s/def :command/action fn?)
+(s/def :command/shortcut string?)
+
+(s/def :command/command
+  (s/keys :req-un [:command/id :command/desc :command/action]
+          :opt-un [:command/shortcut]))
+
+(defn global-shortcut-commands []
+  (->> [:shortcut.handler/editor-global
+        :shortcut.handler/global-prevent-default
+        :shortcut.handler/global-non-editing-only]
+       (mapcat shortcut-helper/shortcuts->commands)))
+
+(defn get-commands []
+  (get @state/state :command-palette/commands))
+
+(defn register [{:keys [id] :as command}]
+  (spec/validate :command/command command)
+  (let [cmds (get-commands)]
+    (if (some (fn [existing-cmd] (= (:id existing-cmd) id)) cmds)
+      (log/error :command/register {:msg "Failed to register command. Command with same id already exist"
+                                    :id  id})
+      (state/set-state! :command-palette/commands (conj cmds command)))))
+
+(defn register-global-shortcut-commands []
+  (let [cmds (global-shortcut-commands)]
+    (doseq [cmd cmds] (register cmd))))
+
+(comment
+  ;; register custom command example
+  (register
+   {:id :document/open-logseq-doc
+    :desc "Document: open Logseq documents"
+    :action (fn [] (js/window.open "https://logseq.github.io/"))}))

+ 6 - 1
src/main/frontend/modules/shortcut/config.cljs

@@ -322,7 +322,12 @@
    ;; always overrides the copy due to "mod+c mod+s"
    {:misc/copy
     {:binding "mod+c"
-     :fn     (fn [] (js/document.execCommand "copy"))}}
+     :fn     (fn [] (js/document.execCommand "copy"))}
+
+    :command-palette/toggle
+    {:desc "Toggle command palette"
+     :binding "mod+shift+p"
+     :fn  (fn [] (state/toggle! :ui/command-palette-open?))}}
 
    :shortcut.handler/global-non-editing-only
    ^{:before m/enable-when-not-editing-mode!}

+ 21 - 0
src/main/frontend/modules/shortcut/data_helper.cljs

@@ -1,6 +1,7 @@
 (ns frontend.modules.shortcut.data-helper
   (:require [borkdude.rewrite-edn :as rewrite]
             [clojure.string :as str]
+            [clojure.set :refer [rename-keys]]
             [frontend.config :as cfg]
             [frontend.db :as db]
             [frontend.handler.file :as file]
@@ -10,6 +11,7 @@
             [lambdaisland.glogi :as log]
             [frontend.handler.common :as common-handler])
   (:import [goog.ui KeyboardShortcutHandler]))
+
 (defonce default-binding
   (->> (vals config/default-config)
        (into {})
@@ -174,3 +176,22 @@
                              (map js->clj))]
 
       (some? (some (fn [b] (some #{b} rest-bindings)) bindings)))))
+
+(defn shortcut-data-by-id [id]
+  (let [binding (shortcut-binding id)
+        data    (->> (vals config/default-config)
+                     (into  {})
+                     id)]
+    (when binding
+      (assoc
+       data
+       :binding
+       (binding-for-display id binding)))))
+
+(defn shortcuts->commands [handler-id]
+  (let [m (get config/default-config handler-id)]
+    (->> m
+         (map (fn [[id _]] (-> (shortcut-data-by-id id)
+                               (assoc :id id)
+                               (rename-keys {:binding :shortcut
+                                             :fn      :action})))))))

+ 4 - 0
src/main/frontend/state.cljs

@@ -72,6 +72,7 @@
       :ui/file-component nil
       :ui/custom-query-components {}
       :ui/show-recent? false
+      :ui/command-palette-open? false
       :ui/developer-mode? (or (= (storage/get "developer-mode") "true")
                               false)
       ;; remember scroll positions of visited paths
@@ -156,6 +157,9 @@
                                                  #{})
       :date-picker/date nil
 
+      ;; command palette
+      :command-palette/commands []
+
       :view/components {}})))