Forráskód Böngészése

cmdk initial implementation

Ben Yorke 2 éve
szülő
commit
8bff0f2139

+ 19 - 6
deps/shui/src/logseq/shui/button/v2.cljs

@@ -3,16 +3,26 @@
     [clojure.string :as str]
     [logseq.shui.util :as util]
     [rum.core :as rum]
-    [logseq.shui.icon.v2 :as icon]))
+    [logseq.shui.icon.v2 :as icon]
+    [clojure.string :as string]))
+
 
 (rum/defc root 
-  [{:keys [theme text depth size icon shortcut] :or {theme :color depth 1 size :md}} context]
+  [{:keys [theme text depth size icon interactive shortcut tiled on-click] :or {theme :color depth 1 size :md interactive true}} context]
   (let [theme-class (str "shui__button-theme-" (name theme))
         depth-class (str "shui__button-depth-" depth) 
         color-class (str "shui__button-color-" (some-> context :state deref :ui/radix-color name))
-        size-class  (str "shui__button-size-" (name size))]
-    [:button.shui__button {:class (str theme-class " " depth-class " " color-class " " size-class)} 
-     text
+        size-class  (str "shui__button-size-" (name size))
+        tiled-class (when tiled "shui__button-tiled")]
+    [:button.shui__button {:class (str theme-class " " depth-class " " color-class " " size-class " " tiled-class) 
+                           :on-click (when on-click on-click)}
+     (if-not tiled text
+       (for [[index tile] (map-indexed vector (rest (string/split text #"")))]
+         [:<> 
+          (when (< 0 index) 
+            [:div.shui__button__tile-separator])
+          [:div.shui__button__tile {:class ""} tile]]))
+        
      (when icon 
        (icon/root icon))
      (when (not-empty shortcut)
@@ -20,8 +30,11 @@
          [:div.shui__button-shortcut-key
           (case key 
             "cmd" (icon/root "command") 
-            "shift" (icon/root "arrow-big-up-filled")
+            "shift" (icon/root "arrow-big-top")
             "return" (icon/root "arrow-back")
+            "esc" [:div.tracking-tightest {:style {:transform "scaleX(0.8) scaleY(1.2) " 
+                                                   :font-size "0.5rem" 
+                                                   :font-weight "500"}} "ESC"]
             key)]))]))
           
      

+ 101 - 27
deps/shui/src/logseq/shui/cmdk/v2.cljs

@@ -8,7 +8,34 @@
 
 (def state (atom {:current-engine "All"
                   :highlight-index 0 
-                  :button {:text "Open" :theme :gray :shortcut ["return"]}}))
+                  :button {:text "Open" :theme :gray :shortcut ["return"]}
+                  :query ""
+                  :results {:blocks [] :pages [] :files []}}))
+
+; (let [entity (-> @state :context :entity)
+;       block (-> @state :results :blocks first)
+;       preview-uuid (-> @state :preview-uuid)
+;       {:keys [blocks-container get-block-and-children get-current-repo]} (:context @state)]
+;   ; (keys (entity (:block/page block)))
+;   ; blockgc)
+;   ; (blocks-container
+;   ;   (get-block-and-children (get-current-repo) preview-uuid) 
+;   ;   {:id preview-uuid :preview? true}))
+;   ; puuid)
+;   (get-block-and-children (get-current-repo) (keyword preview-uuid)))
+;   ; preview-uuid)
+;   ; (get-current-repo))
+
+; "6492d9e2-7554-4015-a28b-d6acbb6c1a19"
+; "logseq_local_shui-graph"
+  
+
+(defn handle-query-change [e context]
+  (let [query (.. e -target -value)]
+    (swap! state assoc :query query)
+    (when (and (seq query) (seq (str/trim query)))
+      (-> ((:search context) query)
+          (.then #(swap! state assoc :results %))))))
 
 (defn get-results []
   [])
@@ -60,9 +87,16 @@
   (fn []
     (swap! state assoc :button {:text text :theme theme :shortcut (map name shortcut)})))
 
+(defn button-updater-with-preview [preview-uuid text theme & shortcut]
+  (fn [] 
+    (apply button-updater text theme shortcut)
+    (if (string? preview-uuid)
+      (swap! state assoc :preview-uuid (uuid preview-uuid))
+      (swap! state assoc :preview-uuid preview-uuid))))
+
 (rum/defc results < rum/reactive []
   (let [state-value (rum/react state)
-        {:keys [current-engine highlight-index]} state-value
+        {:keys [current-engine highlight-index results]} state-value
         filtered-actions (when (#{"All" "Actions"} current-engine) 
                            [{:icon-theme :color :icon "plus" :text "Quick capture" :info "Add a block to todays journal page" :on-highlight (button-updater "Quick capture" :color :return)}
                             {:icon-theme :gradient :icon "question-mark" :text "Generate short answer" :on-highlight (button-updater "Generate" :gradient :return)} 
@@ -78,12 +112,16 @@
                                {:icon-theme :gradient :icon "messages" :text "Chat" :info "Chat with an AI about any topic" :on-highlight (button-updater "Start chat" :gradient :return)} 
                                {:icon-theme :gradient :icon "question-mark" :text "Generate short answer" :on-highlight (button-updater "Generate" :gradient :return)}])
         filtered-blocks  (when (#{"All" "Blocks"} current-engine) 
-                           [{:icon-theme :gray :icon "block" :title "Not a real document" :on-highlight (button-updater "Open" :gray :return) :text "When working on cmdk, we want to display blocks that appear from search. These can have quite a long body of text, and that body of text should potentially be truncated"} 
-                            {:icon-theme :gray :icon "block" :title "Not a real document" :on-highlight (button-updater "Open" :gray :return) :text "Of course, that truncated text should be truncated in a way that makes sense, and doesn't cut off in the middle of a word, and contains the search query if there is one"} 
-                            {:icon-theme :gray :icon "block" :title "Not a real document" :on-highlight (button-updater "Open" :gray :return) :text "We should play around with displaying the blocks hierarchy, currently it's very noisy, and I'm not sure if it's adding much value. It's possible that the preview will be a sufficient replacement"}])
+                           (->> (:blocks results) 
+                                (map #(hash-map :title (:block/content %) :on-highlight (button-updater-with-preview (:block/uuid %) "Open" :gray :return) :icon-theme :gray :icon "block"))))
+                           ; [{:icon-theme :gray :icon "block" :title "Not a real document" :on-highlight (button-updater "Open" :gray :return) :text "When working on cmdk, we want to display blocks that appear from search. These can have quite a long body of text, and that body of text should potentially be truncated"} 
+                           ;  {:icon-theme :gray :icon "block" :title "Not a real document" :on-highlight (button-updater "Open" :gray :return) :text "Of course, that truncated text should be truncated in a way that makes sense, and doesn't cut off in the middle of a word, and contains the search query if there is one"} 
+                           ;  {:icon-theme :gray :icon "block" :title "Not a real document" :on-highlight (button-updater "Open" :gray :return) :text "We should play around with displaying the blocks hierarchy, currently it's very noisy, and I'm not sure if it's adding much value. It's possible that the preview will be a sufficient replacement"}])
         filtered-pages   (when (#{"All" "Pages"} current-engine)
-                           [{:icon-theme :gray :icon "page" :text "Memo/CMDK" :on-highlight (button-updater "Open" :gray :return)} 
-                            {:icon-theme :gray :icon "page" :text "Logseq Logo Community Contest" :on-highlight (button-updater "Open" :gray :return)}]) 
+                           (->> (:pages results) 
+                                (map #(hash-map :title % :on-highlight (button-updater "Open" :gray :return) :icon-theme :gray :icon "page"))))
+                           ; [{:icon-theme :gray :icon "page" :text "Memo/CMDK" :on-highlight (button-updater "Open" :gray :return)} 
+                           ;  {:icon-theme :gray :icon "page" :text "Logseq Logo Community Contest" :on-highlight (button-updater "Open" :gray :return)}]) 
         grouped-items    (->> [["Actions" filtered-actions] ["Actions" filtered-qc-actions] ["Actions" filtered-ai-actions] ["Blocks" filtered-blocks] ["Pages" filtered-pages]]
                               (filter #(not-empty (second %))))
         item-count       (count (mapcat second grouped-items))
@@ -105,8 +143,34 @@
         (for [item items]
           (result-item (assoc item :highlighted (= item highlight-item) :on-highlight-dep current-engine)))])]))
 
-(rum/defc preview []
-  [:div "Preview"])
+(rum/defc preview < rum/reactive 
+  [_ {:keys [blocks-container get-block-and-children get-current-repo entity get-page page-cp get-page-blocks-no-cache get-block-children page] :as context}]
+  (let [state-value (rum/react state)
+        preview-uuid (:preview-uuid state-value)
+        preview-entity (entity [:block/uuid preview-uuid])
+        preview-page (:block/page preview-entity)
+        preview-page-uuid (:block/uuid preview-page)
+        preview-page-name (:block/name preview-page)]
+    (-> {:preview-uuid preview-uuid :preview-page-uuid preview-page-uuid :preview-page-name preview-page-name} 
+        pr-str 
+        js/console.log)
+    [:div 
+     ;; page 
+     (page {:page-name preview-page-name})]))
+     ; (page-cp {:preview? true
+     ;           :contents-page? (= "contents" (str/lower-case (str preview-page-name))) 
+     ;           :children (get-block-children (get-current-repo) preview-page-uuid)}
+     ;          preview-page)]))
+     ;; block
+     ; (blocks-container
+     ;   (get-block-and-children (get-current-repo) preview-uuid) 
+     ;   ; (get-block-and-children (get-current-repo) preview-page-uuid) 
+     ;   ; (get-page preview-page-uuid) 
+     ;   {:id preview-uuid :preview? true})]))
+
+; (let [preview-uuid (:preview-uuid @state)
+;       entity (-> @state :context :entity)]
+;   (keys (:block/page (entity [:block/uuid preview-uuid]))))
 
 (rum/defc actions < rum/reactive []
   (let [state-value (rum/react state)
@@ -129,10 +193,14 @@
     [:input {:class "w-full border-0 px-6 bg-transparent"
              :type "text"}]]])
 
-(rum/defc search []
-  [:input {:class "w-full border-0 px-6"
-           :type "text" 
-           :placeholder "Search"}])
+(rum/defc search < rum/reactive [context]
+  (let [state-value (rum/react state)
+        query (:query state-value)]
+    [:input {:class "w-full border-0 px-6"
+             :type "text" 
+             :placeholder "Search"
+             :value query
+             :on-change #(handle-query-change % context)}]))
 
 (rum/defc header < rum/reactive 
   [context]
@@ -145,7 +213,7 @@
      (engines context)
      (if (= current-engine "Quick capture")
        (quick-capture)
-       (search))]))
+       (search context))]))
 
 (defn prev-engine [current-engine]
   (->> ["All" "Pages" "Blocks" "Quick capture" "AI" "All"]
@@ -179,25 +247,31 @@
       #(js/window.removeEventListener "keydown" keydown-handler))
     []))
 
+(rum/defc body < rum/reactive [context]
+  (let [state-value (rum/react state)
+        preview-uuid (:preview-uuid state-value)]
+    (if preview-uuid
+      [:div.grid.grid-cols-2
+       (results)
+       (preview preview-uuid context)]
+      [:div.grid.grid-cols-1 
+       (results)])))
+  
+
 (rum/defc root < rum/reactive
   {:did-mount (fn [_] 
                 (js/window.removeEventListener "keydown" keydown-handler)
                 (js/window.addEventListener "keydown" keydown-handler))
    :will-unmount (fn [_] (js/window.removeEventListener "keydown" keydown-handler))}
   [props context]
+  (swap! state assoc :context context)
   ; (use-cmdk-keyboard-bindings!)
-  (let [preview-data (get-preview)] 
-    [:div.-m-8 {:style {:background-color "var(--lx-gray-02)"
-                        :width "75vw" 
-                        :max-width 800}}
-     (header context)
-     (if preview-data
-       [:div.grid.grid-cols-2
-        (results)
-        (preview)]
-       [:div.grid.grid-cols-1 
-        (results)])
-     [:div
-      (actions)]]))
+  [:div.-m-8 {:style {:background-color "var(--lx-gray-02)"
+                      :width "75vw" 
+                      :max-width 800}}
+   (header context)
+   (body context)
+   [:div
+    (actions)]])
    
 

+ 18 - 4
deps/shui/src/logseq/shui/context.cljs

@@ -1,7 +1,10 @@
 (ns logseq.shui.context
   (:require 
-    [frontend.state :as state]
-    [frontend.colors :as colors]))
+    [frontend.colors :as colors]
+    [frontend.db :as db]
+    [frontend.db.utils :as db-utils]
+    [frontend.handler.search :as search-handler]
+    [frontend.state :as state]))
 
 (defn inline->inline-block [inline block-config]
   (fn [_context item]
@@ -12,7 +15,7 @@
     (fn [context col]
       (map #(inline* context %) col))))
 
-(defn make-context [{:keys [block-config app-config inline int->local-time-2]}]
+(defn make-context [{:keys [block-config app-config inline int->local-time-2 blocks-container page-cp page]}]
   {;; Shui needs access to the global configuration of the application
    :config app-config
    ;; Until components are converted over, they need to fallback to the old inline function 
@@ -43,4 +46,15 @@
    :sub-color-gradient-bg-styles state/sub-color-gradient-bg-styles 
    :sub-color-gradient-text-styles state/sub-color-gradient-text-styles
    :linear-gradient colors/linear-gradient
-   :state state/state})
+   :state state/state
+   ;; Add search to context 
+   :search search-handler/search
+   :entity db-utils/entity
+   :blocks-container blocks-container
+   :get-block-and-children db/get-block-and-children
+   :get-block-children db/get-block-children
+   :get-current-repo state/get-current-repo
+   :get-page-blocks-no-cache db/get-page-blocks-no-cache
+   :get-page db/get-page
+   :page-cp page-cp
+   :page page})

+ 10 - 0
deps/shui/src/logseq/shui/core.cljs

@@ -3,6 +3,8 @@
     [logseq.shui.button.v2 :as shui.button.v2]
     [logseq.shui.cmdk.v2 :as shui.cmdk.v2]
     [logseq.shui.context :as shui.context]
+    [logseq.shui.icon.v2 :as shui.icon.v2]
+    [logseq.shui.list-item.v1 :as shui.list-item.v1]
     [logseq.shui.table.v2 :as shui.table.v2]))
 
 ;; table component
@@ -17,5 +19,13 @@
 (def cmdk shui.cmdk.v2/root)
 (def cmdk-v2 shui.cmdk.v2/root)
 
+;; icon 
+(def icon shui.icon.v2/root)
+(def icon-v2 shui.icon.v2/root)
+
+;; list-item 
+(def list-item shui.list-item.v1/root)
+(def list-item-v1 shui.list-item.v1/root)
+
 ;; context
 (def make-context shui.context/make-context)

+ 78 - 0
deps/shui/src/logseq/shui/list_item/v1.cljs

@@ -0,0 +1,78 @@
+(ns logseq.shui.list-item.v1
+  (:require 
+    [rum.core :as rum]
+    [clojure.string :as string]
+    [logseq.shui.icon.v2 :as icon]
+    [logseq.shui.button.v2 :as button]))
+
+(defn print-shortcut-key [key]
+  (case key
+    ("cmd" "command" "mod" "⌘") "⌘"
+    ("return" "enter" "⏎") "⏎"
+    ("shift" "⇧") "⇧"
+    ("alt" "option" "opt" "⌥") "⌥"
+    ("ctrl" "control" "⌃") "⌃"
+    ("space" " ") " "
+    ("up" "↑") "↑"
+    ("down" "↓") "↓"
+    ("left" "←") "←"
+    ("right" "→") "→"
+    ("disabled") ""
+    ("backspace" "delete") ""
+    ("tab") ""
+    (nil) ""
+    (name key)))
+
+;; result-item
+(rum/defc root [{:keys [icon icon-theme text info shortcut value-label value title highlighted on-highlight on-highlight-dep header]}]
+  (let [ref (rum/create-ref)]
+    (rum/use-effect! 
+      (fn [] 
+        (when (and highlighted on-highlight) 
+          (on-highlight ref)))
+      [highlighted on-highlight-dep])
+    [:div.flex.flex-col.px-6.gap-1.py-4 {:style {:background (if highlighted "var(--lx-gray-04-alpha)" "var(--lx-gray-02)")
+                                                 :opacity (if highlighted 1 0.8)
+                                                 :mix-blend-mode (if highlighted :normal :luminosity)}
+                                         :ref ref}
+     ;; header
+     (when header
+      [:div.text-xs.pl-8.font-light {:class "-mt-1"
+                                     :style {:color "var(--lx-gray-11)"}}
+                                    header])
+     ;; main row
+     [:div.flex.items-center.gap-3
+      [:div.w-5.h-5.rounded.flex.items-center.justify-center 
+       {:style {:background (case icon-theme :color "var(--lx-accent-09)" :gray "var(--lx-gray-09)" :gradient "linear-gradient(-65deg, #8AE8FF, #5373E7, #369EFF, #00B1CC)" "var(--lx-gray-09)")
+                :box-shadow (when (#{:color :gradient} icon-theme) "inset 0 0 0 1px rgba(255,255,255,0.3) ")}}
+       (icon/root icon {:size "14"})]
+      [:div.flex.flex-1.flex-col
+       (when title
+         [:div.text-sm.pb-2.font-bold {:style {:color "var(--lx-gray-11)"}} title])
+       [:div {:class "text-sm font-medium"
+              :style {:color "var(--lx-gray-12)"}} text
+        (when info 
+          [:span {:style {:color "var(--lx-gray-11)"}} (str " — " info)])]]
+      (when (or value-label value)
+        [:div {:class "text-xs"}
+         (when (and value-label value)
+           [:span {:style {:color "var(--lx-gray-11)"}} (str value-label ": ")])
+         (when (and value-label (not value))
+           [:span {:style {:color "var(--lx-gray-11)"}} (str value-label)])
+         (when value
+           [:span {:style {:color "var(--lx-gray-12)"}} value])])
+      (when shortcut 
+        [:div {:class "flex gap-1"}
+         (for [[index option] (map-indexed vector (string/split shortcut #" \| "))]
+           [:<>
+             (when (< 0 index)
+               [:div {:style {:color "var(--lx-gray-11)"}} "|"])
+             (for [sequence (string/split option #" ")
+                   :let [text (->> (string/split sequence #"\+")
+                                   (map print-shortcut-key)
+                                   (apply str))]]
+               (button/root {:theme :gray 
+                             :interactive false 
+                             :text text
+                             :tiled true}))])])]]))
+        ; [:span {:style} (str key)])])])

+ 150 - 42
resources/css/shui.css

@@ -17,66 +17,174 @@
   @apply text-sm py-1 px-3;
 }
 
+.shui__button-tiled {
+  padding: 0 !important;
+  gap: 0 !important;
+}
+
+.shui__button-tiled .shui__button__tile {
+  @apply flex items-center justify-center text-center;
+}
+
+.shui__button-tiled.shui__button-size-md .shui__button__tile {
+  @apply h-6 w-6;       
+}
+
+.shui__button__tile-separator {
+  @apply w-px h-full bg-white/10;
+}
+
 .shui__button-theme-color {
-  background: or(--lx-accent-09, --ls-active-primary-color);
+  background: or(--lx-accent-09, --rx-blue-09);
+}
+
+.shui__button-theme-color:hover {
+  background: or(--lx-accent-10, --rx-blue-10);
+}
+
+.shui__button-theme-color:active {
+  background: or(--lx-accent-08, --rx-blue-08);
 }
 
 .shui__button-theme-gray {
   background: or(--lx-gray-05, --ls-quaternary-background-color);
 }
 
+.shui__button-theme-gray:hover {
+  background: or(--lx-gray-06, --ls-quaternary-background-color);
+}
+
+.shui__button-theme-gray:active {
+  background: or(--lx-gray-04, --ls-quaternary-background-color);
+}
+
 .shui__button-theme-gradient {
-  background: linear-gradient(-65deg, #8AE8FF, #5373E7, #369EFF, #00B1CC);
+  --depth-shadow-from: rgba(2, 23, 53, 0.70);
+  --depth-shadow-to: rgba(2, 23, 53, 0.00);
 }
 
-.shui__button-depth-1:before {
-  @apply absolute inset-0;
-  border-radius: 0.25rem;
-  content: "";
-  padding: 1px;
-  background: linear-gradient(to bottom, rgba(255,255,255,0.3), transparent);
-  -webkit-mask: linear-gradient(#fff 0 0) content-box,
-                linear-gradient(#fff 0 0);
-  -webkit-mask-composite: xor;
-  mask-composite: exclude;
+.shui__button-theme-gradient:hover {
+  --depth-shadow-from: rgba(2, 23, 53, 0.30);
+  --depth-shadow-to: rgba(2, 23, 53, 0.00);
 }
 
-.shui__button-depth-1:after {
-  @apply absolute inset-0;
-  border-radius: 0.25rem;
-  content: "";
-  padding: 1px;
-  background: linear-gradient(to top, rgba(0,0,0,0.2), transparent);
-  -webkit-mask: linear-gradient(#fff 0 0) content-box,
-                linear-gradient(#fff 0 0);
-  -webkit-mask-composite: xor;
-  mask-composite: exclude;
+.shui__button-theme-gradient:active {
+  --depth-shadow-from: rgba(0, 0, 0, 0);
+  --depth-shadow-to: rgba(0, 0, 0, 0);
 }
 
-.shui__button-depth-2:before {
-  @apply absolute inset-0;
-  border-radius: 0.25rem;
-  content: "";
-  padding: 1px;
-  background: linear-gradient(to bottom, rgba(255,255,255,0.6), transparent);
-  -webkit-mask: linear-gradient(#fff 0 0) content-box,
-                linear-gradient(#fff 0 0);
-  -webkit-mask-composite: xor;
-  mask-composite: exclude;
+.shui__button-theme-gradient.shui__button-color-,
+.shui__button-theme-gradient.shui__button-color-indigo,
+.shui__button-theme-gradient.shui__button-color-blue,
+.shui__button-theme-gradient.shui__button-color-sky,
+.shui__button-theme-gradient.shui__button-color-cyan {
+  background: linear-gradient(37deg, var(--depth-shadow-from) 0%, var(--depth-shadow-to) 100%),
+              linear-gradient(135deg, var(--rx-indigo-09) 0%, var(--rx-blue-09) 33.85%, var(--rx-sky-09) 64.06%, var(--rx-cyan-09) 100%);
 }
 
-.shui__button-depth-2:after {
-  @apply absolute inset-0;
-  border-radius: 0.25rem;
-  content: "";
-  padding: 1px;
-  background: linear-gradient(to top, rgba(0,0,0,0.4), transparent);
-  -webkit-mask: linear-gradient(#fff 0 0) content-box,
-                linear-gradient(#fff 0 0);
-  -webkit-mask-composite: xor;
-  mask-composite: exclude;
+.shui__button-theme-gradient.shui__button-color-tomato,
+.shui__button-theme-gradient.shui__button-color-red,
+.shui__button-theme-gradient.shui__button-color-crimson,
+.shui__button-theme-gradient.shui__button-color-pink,
+.shui__button-theme-gradient.shui__button-color-plum,
+.shui__button-theme-gradient.shui__button-color-purple,
+.shui__button-theme-gradient.shui__button-color-violet {
+  background: linear-gradient(37deg, var(--depth-shadow-from) 0%, var(--depth-shadow-to) 100%),
+              linear-gradient(135deg, var(--rx-tomato-09) 0%, var(--rx-red-09) 16.66%, var(--rx-crimson-09) 33.33%, var(--rx-pink-09) 50%, var(--rx-plum-09) 66.66%, var(--rx-purple-09) 83.33%, var(--rx-violet-09) 100%);
+}
+
+.shui__button-theme-gradient.shui__button-color-green,
+.shui__button-theme-gradient.shui__button-color-mint,
+.shui__button-theme-gradient.shui__button-color-teal {
+  background: linear-gradient(37deg, var(--depth-shadow-from) 0%, var(--depth-shadow-to) 100%),
+              linear-gradient(135deg, var(--rx-teal-09) 0%, var(--rx-mint-09) 50%, var(--rx-green-09) 100%);
 }
 
+.shui__button-theme-gradient.shui__button-color-grass,
+.shui__button-theme-gradient.shui__button-color-lime {
+  background: linear-gradient(37deg, var(--depth-shadow-from) 0%, var(--depth-shadow-to) 100%),
+              linear-gradient(135deg, var(--rx-grass-09) 0%, var(--rx-lime-09) 100%);
+}
+
+.shui__button-theme-gradient.shui__button-color-yellow,
+.shui__button-theme-gradient.shui__button-color-amber,
+.shui__button-theme-gradient.shui__button-color-orange,
+.shui__button-theme-gradient.shui__button-color-brown {
+  background: linear-gradient(37deg, var(--depth-shadow-from) 0%, var(--depth-shadow-to) 100%),
+              linear-gradient(135deg, var(--rx-yellow-09) 0%, var(--rx-amber-09) 33.33%, var(--rx-orange-09) 66.66%, var(--rx-brown-09) 100%);
+}
+
+/* .shui__button-theme-gradient:hover { */
+  /* background: linear-gradient(37deg, rgba(2, 23, 53, 0.30) 0%, rgba(2, 23, 53, 0.00) 100%), linear-gradient(135deg, #8AE8FF 0%, #5373E7 33.85%, #369EFF 64.06%, #00B1CC 100%); */
+/* } */
+
+/* Shadow/xs */
+/* box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05); */
+/* } */
+
+.shui__button-depth-1 {
+  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.15), 
+              inset 0 -1px 0 0 rgba(0, 0, 0, 0.15);
+}
+
+.shui__button-depth-1:hover {
+  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.15), 
+              inset 0 -2px 0 0 rgba(0, 0, 0, 0.15);
+}
+
+.shui__button-depth-2 {
+  box-shadow: inset 0 1px 0 0 rgba(255, 255, 255, 0.2), 
+              inset 0 -1px 0 0 rgba(0, 0, 0, 0.2);
+}
+
+/* .shui__button-depth-1:before { */
+/*   @apply absolute inset-0; */
+/*   border-radius: 0.25rem; */
+/*   content: ""; */
+/*   padding: 1px; */
+/*   background: linear-gradient(to bottom, rgba(255,255,255,0.3), transparent); */
+/*   -webkit-mask: linear-gradient(#fff 0 0) content-box, */
+/*                 linear-gradient(#fff 0 0); */
+/*   -webkit-mask-composite: xor; */
+/*   mask-composite: exclude; */
+/* } */
+
+/* .shui__button-depth-1:after { */
+/*   @apply absolute inset-0; */
+/*   border-radius: 0.25rem; */
+/*   content: ""; */
+/*   padding: 1px; */
+/*   background: linear-gradient(to top, rgba(0,0,0,0.2), transparent); */
+/*   -webkit-mask: linear-gradient(#fff 0 0) content-box, */
+/*                 linear-gradient(#fff 0 0); */
+/*   -webkit-mask-composite: xor; */
+/*   mask-composite: exclude; */
+/* } */
+
+/* .shui__button-depth-2:before { */
+/*   @apply absolute inset-0; */
+/*   border-radius: 0.25rem; */
+/*   content: ""; */
+/*   padding: 1px; */
+/*   background: linear-gradient(to bottom, rgba(255,255,255,0.6), transparent); */
+/*   -webkit-mask: linear-gradient(#fff 0 0) content-box, */
+/*                 linear-gradient(#fff 0 0); */
+/*   -webkit-mask-composite: xor; */
+/*   mask-composite: exclude; */
+/* } */
+
+/* .shui__button-depth-2:after { */
+/*   @apply absolute inset-0; */
+/*   border-radius: 0.25rem; */
+/*   content: ""; */
+/*   padding: 1px; */
+/*   background: linear-gradient(to top, rgba(0,0,0,0.4), transparent); */
+/*   -webkit-mask: linear-gradient(#fff 0 0) content-box, */
+/*                 linear-gradient(#fff 0 0); */
+/*   -webkit-mask-composite: xor; */
+/*   mask-composite: exclude; */
+/* } */
+
 .shui__button-shortcut-key:first-of-type {
   @apply ml-2;
 }

+ 20 - 22
src/main/frontend/components/block.cljs

@@ -541,15 +541,16 @@
   (let [*mouse-down? (::mouse-down? state)
         tag? (:tag? config)
         config (assoc config :whiteboard-page? whiteboard-page?)
-        untitled? (model/untitled-page? page-name)
-        gradient-styles (state/sub-color-gradient-text-styles :09)]
+        untitled? (model/untitled-page? page-name)]
+        ; gradient-styles (state/sub-color-gradient-text-styles :09)]
+        
     [:a
      {:tabIndex "0"
       :class (cond-> (if tag? "tag" "page-ref")
                (:property? config)
                (str " page-property-key block-property")
                untitled? (str " opacity-50"))
-      :style gradient-styles
+      :style {:color "var(--lx-accent-10)"}
       :data-ref page-name
       :draggable true
       :on-drag-start (fn [e] (editor-handler/block->data-transfer! page-name-in-block e))
@@ -758,10 +759,7 @@
        (excalidraw s block-uuid)]
       [:span.page-reference
        {:data-ref s
-        :style {:background-image (colors/linear-gradient :grass "09" 5)
-                :background-clip "text"
-                "-webkit-background-clip" "text"
-                :color "transparent"}}
+        :style {:color "var(--lx-accent-09)"}}
        (when (and (or show-brackets? nested-link?)
                   (not html-export?)
                   (not contents-page?))
@@ -2286,12 +2284,12 @@
                 :data-type (name block-type)
                 :style {:width "100%" :pointer-events (when stop-events? "none")}}
 
-                (not (string/blank? (:hl-color properties)))
-                (assoc :data-hl-color (:hl-color properties))
+               (not (string/blank? (:hl-color properties)))
+               (assoc :data-hl-color (:hl-color properties))
 
-                (not block-ref?)
-                (assoc mouse-down-key (fn [e]
-                                        (block-content-on-mouse-down e block block-id content edit-input-id))))]
+               (not block-ref?)
+               (assoc mouse-down-key (fn [e]
+                                       (block-content-on-mouse-down e block block-id content edit-input-id))))]
     [:div.block-content.inline
      (cond-> {:id (str "block-content-" uuid)
               :class (when selected? "select-none")
@@ -2809,20 +2807,20 @@
        :blockid (str uuid)
        :haschild (str (boolean has-child?))}
 
-       level
-       (assoc :level level)
+      level
+      (assoc :level level)
 
-       (not slide?)
-       (merge attrs)
+      (not slide?)
+      (merge attrs)
 
-       (or reference? embed?)
-       (assoc :data-transclude true)
+      (or reference? embed?)
+      (assoc :data-transclude true)
 
-       embed?
-       (assoc :data-embed true)
+      embed?
+      (assoc :data-embed true)
 
-       custom-query?
-       (assoc :data-query true))
+      custom-query?
+      (assoc :data-query true))
 
      (when (and ref? breadcrumb-show?)
        (breadcrumb config repo uuid {:show-page? false

+ 346 - 0
src/main/frontend/components/cmdk.cljs

@@ -0,0 +1,346 @@
+(ns frontend.components.cmdk
+  (:require 
+    [clojure.string :as string]
+    [frontend.components.block :as block]
+    [frontend.components.command-palette :as cp]
+    [frontend.components.page :as page]
+    [frontend.context.i18n :refer [t]]
+    [frontend.db :as db]
+    [frontend.db.model :as model]
+    [frontend.handler.command-palette :as cp-handler]
+    [frontend.handler.editor :as editor-handler]
+    [frontend.handler.route :as route-handler]
+    [frontend.handler.search :as search-handler]
+    [frontend.modules.shortcut.core :as shortcut]
+    [frontend.modules.shortcut.data-helper :as shortcut-helper]
+    [frontend.search :as search]
+    [frontend.state :as state]
+    [frontend.ui :as ui]
+    [frontend.util :as util]
+    [goog.functions :as gfun]
+    [logseq.shui.context :refer [make-context]]
+    [logseq.shui.core :as shui]
+    [promesa.core :as p]
+    [rum.core :as rum]))
+
+;; When CMDK opens, we have some default search actions we make avaialbe for quick access
+(def default-search-actions 
+  [{:text "Search only pages"     :info "Add filter to search"}
+   {:text "Search only blocks"    :info "Add filter to search"}
+   {:text "Create block"          :info "Add a block to today's journal page" :icon "block"         :icon-theme :color}
+   {:text "Generate short answer" :info "Ask a language model"                :icon "question-mark" :icon-theme :gradient}
+   {:text "Open settings"                                                     :icon "settings"      :icon-theme :gray}])
+
+;; The results are separated into groups, and loaded/fetched/queried separately
+(def default-results 
+  {:search-actions {:status :success :show-more false :items default-search-actions} 
+   :commands       {:status :success :show-more false :items nil}
+   :history        {:status :success :show-more false :items nil}
+   :current-page   {:status :success :show-more false :items nil}
+   :pages          {:status :success :show-more false :items nil}
+   :blocks         {:status :success :show-more false :items nil} 
+   :files          {:status :success :show-more false :items nil}})        
+                    
+;; Each result gorup has it's own load-results function
+(defmulti load-results (fn [group state] group))
+
+;; The search-actions are only loaded when there is no query present. It's like a quick access to filters
+(defmethod load-results :search-actions [group state]
+  (let [!input (::input state)
+        !results (::results state)]
+    (if (empty? @!input)
+      (swap! !results assoc group {:status :success :items default-search-actions})
+      (swap! !results assoc group {:status :success :items nil}))))
+
+;; The commands search uses the command-palette hander
+(defmethod load-results :commands [group state]
+  (let [!input (::input state)
+        !results (::results state)]
+    (swap! !results assoc-in [group :status] :loading)
+    (->> (vals (cp-handler/get-commands-unique))
+         (filter #(string/includes? (string/lower-case (pr-str %)) (string/lower-case @!input)))
+         (map #(hash-map :icon "command" 
+                         :icon-theme :gray 
+                         :text (cp/translate t %)
+                         :value-label (pr-str (:id %))
+                         ; :info (pr-str (:id %))
+                         ; :info (:desc %) 
+                         :shortcut (:shortcut %)
+                         :source-command %))
+         (hash-map :status :success :items)
+         (swap! !results assoc group)) 
+    (js/console.log "commands" (clj->js (get-in @!results [:commands :items])))))
+
+;; The pages search action uses an existing handler
+(defmethod load-results :pages [group state]
+  (let [!input (::input state)
+        !results (::results state)]
+    (swap! !results assoc-in [group :status] :loading)
+    (p/let [pages (search/page-search @!input)
+            items (map #(hash-map :icon "page" 
+                                  :icon-theme :gray 
+                                  :text % 
+                                  :source-page %) pages)]
+      (js/console.log "pages" (pr-str pages) (clj->js items))
+      (swap! !results assoc group {:status :success :items items}))))
+
+;; The blocks search action uses an existing handler
+(defmethod load-results :blocks [group state]
+  (let [!input (::input state)
+        !results (::results state)
+        repo (state/get-current-repo)
+        opts {:limit 100}]
+    (swap! !results assoc-in [group :status] :loading)
+    (p/let [blocks (search/block-search repo @!input opts)
+            items (map #(hash-map :icon "block" 
+                                  :icon-theme :gray 
+                                  :text (:block/content %) 
+                                  :header (some-> % :block/page db/entity :block/name)
+                                  :source-block %) blocks)]
+                              
+      ; (js/console.log "blocks" (clj->js items) (map (comp pr-str :block/page) blocks))
+      ; (js/console.log "blocks" (clj->js items) 
+      ;                 (pr-str (map (comp pr-str :block/page) blocks)) 
+      ;                 (pr-str (map (comp :block/name :block/page) blocks)) 
+      ;                 (pr-str (map (comp :block/name db/entity :block/page) blocks)))
+      (swap! !results assoc group {:status :success :items items}))))
+
+;; The default load-results function triggers all the other load-results function
+(defmethod load-results :default [_ state]
+  (js/console.log "load-results/default" @(::input state))
+  (load-results :search-actions state)
+  (load-results :commands state)
+  (load-results :blocks state)
+  (load-results :pages state))
+
+; (def search [query]
+;   (load-results :search-actions state))
+  ; (let [repo (state/get-current-repo)
+  ;       limit 5
+  ;       current-page-db-id nil 
+  ;       opts {:limit limit}]
+  ;   (p/let [blocks (search/block-search repo q opts)
+  ;           pages (search/page-search q)
+  ;           pages-content (when current-page-db-id (search/page-content-search repo q opts))
+  ;           files (search/file-search q)
+  ;           commands])))
+
+(defmulti handle-action (fn [action _state _item _event] action))
+
+(defmethod handle-action :cancel [_ state item event]
+  (js/console.log :handle-action/cancel)
+  (state/close-modal!))
+
+(defmethod handle-action :copy-page-ref [_ state item event]
+  (when-let [page-name (:source-page item)]
+    (util/copy-to-clipboard! page-name)
+    (state/close-modal!)))
+    
+(defmethod handle-action :copy-block-ref [_ state item event]
+  (when-let [block-uuid (some-> item :source-block :block/uuid uuid)]
+    (editor-handler/copy-block-ref! block-uuid)
+    (state/close-modal!)))
+
+(defmethod handle-action :open-page [_ state item event]
+  (when-let [page-name (:source-page item)]
+    (route-handler/redirect-to-page! page-name)
+    (state/close-modal!)))
+
+(defmethod handle-action :open-block [_ state item event]
+  (let [get-block-page (partial model/get-block-page (state/get-current-repo))]
+    (when-let [page (some-> item :source-block :block/uuid uuid get-block-page :block/name model/get-redirect-page-name)]
+      (route-handler/redirect-to-page! page)
+      (state/close-modal!))))
+
+(defmethod handle-action :open-page-right [_ state item event]
+  (when-let [page-uuid (some-> item :source-page model/get-page :block/uuid uuid)]
+    (js/console.log "oepn-page-right" page-uuid) 
+    (editor-handler/open-block-in-sidebar! page-uuid)))
+
+(defmethod handle-action :open-block-right [_ state item event]
+  (when-let [block-uuid (some-> item :source-block :block/uuid uuid)]
+    (js/console.log "oepn-block-right" block-uuid) 
+    (editor-handler/open-block-in-sidebar! block-uuid)))
+
+(defmethod handle-action :trigger [_ state item event])
+(defmethod handle-action :return [_ state item event])
+
+(rum/defc result-group < rum/reactive 
+  [state title group visible-items highlighted-result]
+  (let [{:keys [show-more items]} (some-> state ::results deref group)] 
+    [:div {:class ""}
+     [:div {:class "text-xs py-1.5 px-6 flex justify-between items-center gap-2" 
+            :style {:color "var(--lx-gray-11)" 
+                    :background "var(--lx-gray-02)"}} 
+      [:div {:class "font-bold" 
+             :style {:color "var(--lx-gray-11)"}} title]
+      [:div {:class "bg-white/20 px-1.5 py-px text-white rounded-full"
+             :style {:font-size "0.6rem"}}
+       (if (<= 100 (count items))
+         (str "99+")
+         (count items))]
+      [:div {:class "flex-1"}]
+      (if show-more
+        [:div {:on-click #(swap! (::results state) update-in [group :show-more] not)} "Show less"]
+        [:div {:on-click #(swap! (::results state) update-in [group :show-more] not)} "Show more"])]
+
+     [:div {:class ""}
+      (for [result visible-items
+            :let [highlighted? (= result highlighted-result)]]
+       (shui/list-item (assoc result 
+                              :highlighted highlighted?
+                              :on-highlight (fn [ref]  
+                                              (.. ref -current (scrollIntoView #js {:block "center" 
+                                                                                    :inline "nearest"
+                                                                                    :behavior "smooth"})) 
+                                              (case group 
+                                                :search-actions (reset! (::actions state) [:cancel :return])
+                                                :commands       (reset! (::actions state) [:cancel :trigger])
+                                                :pages          (reset! (::actions state) [:cancel :copy-page-ref :open-page-right :open-page])
+                                                :blocks         (reset! (::actions state) [:cancel :copy-block-ref :open-block-right :open-block])
+                                                nil)))))]]))
+                                                
+                                                  
+
+(defonce keydown-handler
+  (fn [state e]
+    (when (#{"ArrowDown" "ArrowUp"} (.-key e))
+      (.preventDefault e))
+    (case (.-key e)
+      ; "Escape" (rum/dispatch! :close)
+      "ArrowDown" (swap! (::highlight-index state) inc)
+      "ArrowUp"   (swap! (::highlight-index state) dec)
+      ; "j" (when (.-metaKey e) 
+      ;       (if (.-shiftKey e)
+      ;         (swap! state update :current-engine prev-engine)
+      ;         (swap! state update :current-engine next-engine)))
+      ; "ArrowUp" (rum/dispatch! :highlight-prev)
+      ; "Enter" (rum/dispatch! :select)
+      (println (.-key e)))))
+
+(defn handle-input-change [state e]
+  (let [input (.. e -target -value)
+        !input (::input state)
+        !load-results-throttled (::load-results-throttled state)]
+    ;; update the input value in the UI
+    (reset! !input input) 
+
+    ;; ensure that there is a throttled version of the load-results function
+    (when-not @!load-results-throttled
+      (reset! !load-results-throttled (gfun/throttle load-results 1000)))
+
+    ;; retreive the laod-results function and update all the results
+    (when-let [load-results-throttled @!load-results-throttled]
+      (load-results-throttled :all state))))
+
+(rum/defc page-preview [state highlighted]
+  (let [page-name (:source-page highlighted)]
+    (page/page {:page-name (model/get-redirect-page-name page-name) :whiteboard? true})))
+
+(rum/defc block-preview [state highlighted]
+  (let [block (:source-block highlighted)
+        block-uuid-str (str (:block/uuid block))]
+    ; ((state/get-component :block/single-block) (uuid (:block/uuid block)))))
+    ; ((state/get-component :block/container) block)
+    ; ((state/get-component :block/embed) (uuid (:block/uuid block)))))
+    ; (block/block-container {} block)))
+    (page/page {:parameters {:path {:name block-uuid-str}} 
+                :sidebar? true 
+                :repo (state/get-current-repo)})))
+
+(rum/defcs cmdk < 
+  shortcut/disable-all-shortcuts 
+  (rum/local "" ::input)
+  (rum/local 0 ::highlight-index)
+  (rum/local nil ::keydown-handler)
+  (rum/local default-results ::results)
+  (rum/local nil ::load-results-throttled)
+  (rum/local [:cancel :return] ::actions)
+  {:did-mount (fn [state] 
+                (let [next-keydown-handler (partial keydown-handler state)]
+                  (when-let [prev-keydown-handler @(::keydown-handler state)]
+                    (js/window.removeEventListener "keydown" prev-keydown-handler))
+                  (js/window.addEventListener "keydown" next-keydown-handler)
+                  (reset! (::keydown-handler state) next-keydown-handler))
+                state)
+   :will-unmount (fn [state] 
+                   (when-let [current-keydown-handler (some-> state ::keydown-handler deref)] 
+                     (js/window.removeEventListener "keydown" current-keydown-handler))
+                   (reset! (::keydown-handler state) nil)
+                   state)}
+  [state {:keys []}]
+  (let [input @(::input state)
+        actions @(::actions state)
+        highlight-index @(::highlight-index state)
+        results @(::results state)
+        visible-items-for-group (fn [group] 
+                                  (let [{:keys [items show-more]} (get results group)]
+                                    (if show-more items (take 5 items))))
+        results-ordered [["Search actions" :search-actions (visible-items-for-group :search-actions)]
+                         ["Commands"       :commands       (visible-items-for-group :commands)]
+                         ["Pages"          :pages          (visible-items-for-group :pages)]
+                         ["Blocks"         :blocks         (visible-items-for-group :blocks)]]
+        results (mapcat last results-ordered)
+        result-count (count results)
+        highlighted-result-index (cond 
+                                   (zero? result-count) nil 
+                                   (<= 0 (mod highlight-index result-count)) (mod highlight-index result-count) 
+                                   :else (- result-count (mod highlight-index result-count)))
+        highlighted-result (when highlighted-result-index 
+                             (nth results highlighted-result-index nil))
+        preview? (or (:source-page highlighted-result) (:source-block highlighted-result))]
+    [:div.cp__cmdk {:class "-m-8 max-w-[90dvw] max-h-[90dvh] w-[60rem] h-[30.7rem] "}
+     [:div {:class ""
+            :style {:background "var(--lx-gray-02)"
+                    :border-bottom "1px solid var(--lx-gray-07)"}}
+      [:input {:class "text-xl bg-transparent border-none w-full outline-none px-4 py-3" 
+               :placeholder "What are you looking for?"
+               :ref #(when % (.focus %))
+               :on-change (partial handle-input-change state)
+               :value input}]]
+
+     [:div {:class (str "grid" (if preview? " grid-cols-2" " grid-cols-1"))}
+      [:div {:class "pt-1 overflow-y-auto h-96"
+             :style {:background "var(--lx-gray-02)"}}
+       (for [[group-name group-key group-items] results-ordered
+             :when (not-empty group-items)]
+         (result-group state group-name group-key group-items highlighted-result))]
+      (when preview?
+       [:div {:class "h-96 overflow-y-auto"} 
+        (cond 
+         (:source-page highlighted-result)
+         (page-preview state highlighted-result)
+         (:source-block highlighted-result)
+         (block-preview state highlighted-result))])]
+
+     [:div {:class "flex justify-between w-full px-4"
+            :style {:background "var(--lx-gray-03)"
+                    :border-top "1px solid var(--lx-gray-07)"}}
+      [:div {:class "flex items-stretch gap-2"}
+       (for [[tab-name tab-icon] [["Search" "search"] 
+                                  ["Capture" "square-plus"]]
+             :let [active? (= tab-name "Search")]]
+        [:div {:class "flex items-center px-1.5 gap-1 relative"}
+         (when active? 
+           [:div {:class "absolute inset-x-0 top-0 h-0.5 bg-gray-500"}])
+         (when active?
+          (shui/icon tab-icon {:size "16"}))
+         [:div {:class ""} tab-name]])]
+      [:div {:class "flex items-center py-3 gap-4"}
+       (for [action actions
+             :let [on-click (partial handle-action action state highlighted-result)]]
+         (case action
+           :cancel           (shui/button {:text "Cancel"           :theme :gray  :on-click on-click :shortcut ["esc"]}) 
+           :copy-page-ref    (shui/button {:text "Copy"             :theme :gray  :on-click on-click :shortcut ["cmd" "c"]}) 
+           :copy-block-ref   (shui/button {:text "Copy"             :theme :gray  :on-click on-click :shortcut ["cmd" "c"]}) 
+           :open-page-right  (shui/button {:text "Open in sidebar"  :theme :gray  :on-click on-click :shortcut ["shift" "return"]})
+           :open-page        (shui/button {:text "Open"             :theme :color :on-click on-click :shortcut ["return"]})  
+           :open-block-right (shui/button {:text "Open in sidebar"  :theme :gray  :on-click on-click :shortcut ["shift" "return"]})
+           :open-block       (shui/button {:text "Open page"        :theme :color :on-click on-click :shortcut ["return"]})  
+           :trigger          (shui/button {:text "Trigger"          :theme :color :on-click on-click :shortcut ["return"]})  
+           :return           (shui/button {:text "Return"           :theme :color :on-click on-click :shortcut ["return"]})))]]]))  
+       ; (shui/button {:text "AI" :theme :gradient} (make-context {}))]]]))
+
+(->> (cp-handler/get-commands-unique)
+     vals
+     (map (juxt :shortcut :id)))

+ 7 - 7
src/main/frontend/components/right_sidebar.cljs

@@ -319,21 +319,21 @@
        [:div.cp__right-sidebar-settings.hide-scrollbar.gap-1 {:key "right-sidebar-settings"}
         [:div.text-sm
          [:button.button.cp__right-sidebar-settings-btn {:on-click (fn [_e]
-                                                         (state/sidebar-add-block! repo "contents" :contents))}
+                                                                    (state/sidebar-add-block! repo "contents" :contents))}
           (t :right-side-bar/contents)]]
 
         [:div.text-sm
          [:button.button.cp__right-sidebar-settings-btn {:on-click (fn []
-                                                         (when-let [page (get-current-page)]
-                                                           (state/sidebar-add-block!
-                                                            repo
-                                                            page
-                                                            :page-graph)))}
+                                                                    (when-let [page (get-current-page)]
+                                                                      (state/sidebar-add-block!
+                                                                       repo
+                                                                       page
+                                                                       :page-graph)))}
           (t :right-side-bar/page-graph)]]
 
         [:div.text-sm
          [:button.button.cp__right-sidebar-settings-btn {:on-click (fn [_e]
-                                                         (state/sidebar-add-block! repo "help" :help))}
+                                                                    (state/sidebar-add-block! repo "help" :help))}
           (t :right-side-bar/help)]]
 
         (when config/dev? [:div.text-sm

+ 3 - 2
src/main/frontend/db/model.cljs

@@ -190,9 +190,9 @@
           '[:find ?path
              ;; ?modified-at
             :where
-            [?file :file/path ?path]
+            [?file :file/path ?path]]
              ;; [?file :file/last-modified-at ?modified-at]
-            ]
+            
           db)
          (seq)
          ;; (sort-by last)
@@ -919,6 +919,7 @@ independent of format as format specific heading characters are stripped"
 
 (defn get-block-page
   [repo block-uuid]
+  (assert (uuid? block-uuid) "get-block-page requires block-uuid to be of type uuid")
   (when-let [block (db-utils/entity repo [:block/uuid block-uuid])]
     (db-utils/entity repo (:db/id (:block/page block)))))
 

+ 2 - 0
src/main/frontend/handler.cljs

@@ -190,6 +190,8 @@
   (state/set-component! :block/linked-references reference/block-linked-references)
   (state/set-component! :whiteboard/tldraw-preview whiteboard/tldraw-preview)
   (state/set-component! :block/single-block block/single-block-cp)
+  (state/set-component! :block/container block/block-container)
+  (state/set-component! :block/embed block/block-embed)
   (state/set-component! :editor/box editor/box)
   (command-palette/register-global-shortcut-commands))
 

+ 12 - 4
src/main/frontend/handler/editor.cljs

@@ -193,8 +193,16 @@
        (state/set-edit-content! edit-id new-value)
        (cursor/move-cursor-to input (+ cur-pos forward-pos))))))
 
+(-> (random-uuid) str)
+
 (defn open-block-in-sidebar!
   [block-id]
+  ; (assert (uuid? block-id) "frontend.handler.editor/open-block-in-sidebar! expects block-id to be of type uuid")
+  (js/console.log "db-entity/block" block-id)
+  (js/console.log "db-entity/entity" (db/entity [:block/uuid block-id]))
+  ; (js/console.log "db-entity/types" (str block-id) (uuid block-id))
+  ; (js/console.log "db-entity/string" (db/entity [:block/uuid (str block-id)]))
+  ; (js/console.log "db-entity/uuid" (db/entity [:block/uuid (uuid block-id)]))
   (when block-id
     (when-let [block (db/entity [:block/uuid block-id])]
       (let [page? (nil? (:block/page block))]
@@ -831,9 +839,9 @@
                        concat-prev-block? (boolean (and prev-block new-content))
                        transact-opts (cond->
                                       {:outliner-op :delete-blocks}
-                                       concat-prev-block?
-                                       (assoc :concat-data
-                                              {:last-edit-block (:block/uuid block)}))]
+                                      concat-prev-block?
+                                      (assoc :concat-data
+                                             {:last-edit-block (:block/uuid block)}))]
                    (outliner-tx/transact! transact-opts
                                           (if concat-prev-block?
                                             (let [prev-block' (if (seq (:block/_refs block-e))
@@ -1267,7 +1275,7 @@
 
 (defn save-block!
   ([repo block-or-uuid content]
-    (save-block! repo block-or-uuid content {}))
+   (save-block! repo block-or-uuid content {}))
   ([repo block-or-uuid content {:keys [properties] :as opts}]
    (let [block (if (or (uuid? block-or-uuid)
                        (string? block-or-uuid))

+ 7 - 3
src/main/frontend/handler/events.cljs

@@ -12,12 +12,15 @@
             [clojure.string :as string]
             [datascript.core :as d]
             [frontend.commands :as commands]
+            [frontend.components.block :as block]
+            [frontend.components.cmdk :as cmdk]
             [frontend.components.command-palette :as command-palette]
             [frontend.components.conversion :as conversion-component]
             [frontend.components.diff :as diff]
             [frontend.components.encryption :as encryption]
             [frontend.components.file-sync :as file-sync]
             [frontend.components.git :as git-component]
+            [frontend.components.page :as page]
             [frontend.components.plugins :as plugin]
             [frontend.components.search :as component-search]
             [frontend.components.shell :as shell]
@@ -69,7 +72,6 @@
             [goog.dom :as gdom]
             [logseq.db.schema :as db-schema]
             [logseq.graph-parser.config :as gp-config]
-            [logseq.shui.core :refer [cmdk]]
             [promesa.core :as p]
             [rum.core :as rum]))
 
@@ -412,8 +414,10 @@
                      :label "ls-modal-search"}))
 
 (defmethod handle :go/cmdk [_]
-  (when-not (= cmdk (:modal/panel-content @state/state))
-    (state/set-modal! cmdk 
+  (when-not (= cmdk/cmdk (:modal/panel-content @state/state))
+    (state/set-modal! ; (partial cmdk {} (make-context {:blocks-container block/blocks-container :page-cp block/page-cp :page page/page})) 
+                      ; cmdk
+                      cmdk/cmdk
                       {:fullscreen? false 
                        :close-btn?  false 
                        :label "ls-modal-cmdk" 

+ 4 - 2
src/main/frontend/shui.cljs

@@ -3,7 +3,8 @@
   (:require 
     [frontend.date :refer [int->local-time-2]]
     [frontend.state :as state]
-    [logseq.shui.context :refer [make-context]]))
+    [logseq.shui.context :refer [make-context]]
+    [frontend.handler.search :as search-handler]))
 
 (def default-versions {:logseq.table.version 1})
 
@@ -23,4 +24,5 @@
                  :app-config (state/get-config) 
                  :inline inline 
                  :int->local-time-2 int->local-time-2 
-                 :state state/state}))
+                 :state state/state 
+                 :search search-handler/search}))