Просмотр исходного кода

enhance: copy code block

1. search and store code lang instead of mode
2. select uses frontend.components.select instead of shui input + auto-complete
Tienson Qin 1 год назад
Родитель
Сommit
deb3507bb0

+ 1 - 1
deps/db/src/logseq/db/frontend/property.cljs

@@ -43,7 +43,7 @@
                                                 :public? false
                                                 :hide? true
                                                 :view-context :block}}
-   :logseq.property.code/mode {:title "Code mode"
+   :logseq.property.code/lang {:title "Code mode"
                                :schema {:type :string
                                         :public? false
                                         :hide? true

+ 1 - 1
deps/db/src/logseq/db/frontend/schema.cljs

@@ -2,7 +2,7 @@
   "Main datascript schemas for the Logseq app"
   (:require [clojure.set :as set]))
 
-(def version 28)
+(def version 29)
 ;; A page is a special block, a page can corresponds to multiple files with the same ":block/name".
 (def ^:large-vars/data-var schema
   {:db/ident        {:db/unique :db.unique/identity}

+ 1 - 1
package.json

@@ -125,7 +125,7 @@
         "check-password-strength": "2.0.7",
         "chokidar": "3.5.1",
         "chrono-node": "2.2.4",
-        "codemirror": "5.65.13",
+        "codemirror": "5.65.18",
         "comlink": "^4.4.1",
         "d3-force": "3.0.0",
         "diff": "5.0.0",

+ 1 - 1
src/main/frontend/commands.cljs

@@ -189,7 +189,7 @@
      [:editor/set-property :block/tags :logseq.class/Query]
      [:editor/set-property :logseq.property/query ""]
      [:editor/set-property-on-block-property :logseq.property/query :logseq.property.node/display-type :code]
-     [:editor/set-property-on-block-property :logseq.property/query :logseq.property.code/mode "clojure"]]
+     [:editor/set-property-on-block-property :logseq.property/query :logseq.property.code/lang "clojure"]]
     (->block "query")))
 
 (defn db-based-code-block

+ 91 - 70
src/main/frontend/components/block.cljs

@@ -9,7 +9,6 @@
             [datascript.impl.entity :as e]
             [dommy.core :as dom]
             [electron.ipc :as ipc]
-            [frontend.search :refer [fuzzy-search]]
             [frontend.commands :as commands]
             [frontend.components.block.macros :as block-macros]
             [frontend.components.datetime :as datetime-comp]
@@ -24,6 +23,7 @@
             [frontend.components.query.builder :as query-builder-component]
             [frontend.components.svg :as svg]
             [frontend.components.title :as title]
+            [frontend.components.select :as select]
             [frontend.config :as config]
             [frontend.context.i18n :refer [t]]
             [frontend.date :as date]
@@ -2155,7 +2155,7 @@
     (cond
       (= :code node-type)
       [:div.flex.flex-1.w-full
-       (src-cp (assoc config :block block) {:language (:logseq.property.code/mode block)})]
+       (src-cp (assoc config :block block) {:language (:logseq.property.code/lang block)})]
 
       query-block?
       (query-builder-component/builder (:block/title block')
@@ -3341,7 +3341,7 @@
              (and advanced-query? (not collapsed?))
              [:div.flex.flex-1.my-1 {:style {:margin-left 42}}
               (src-cp (assoc config :block query)
-                      {:language (:logseq.property.code/mode query)})]
+                      {:language (:logseq.property.code/lang query)})]
 
              (and (not advanced-query?) (not collapsed?) (not (string/blank? (:block/title query))))
              [:div.flex.flex-1.my-1 {:style {:margin-left 42}}
@@ -3589,42 +3589,41 @@
 
 (defn get-cm-instance
   [^js target]
-  (some-> target (.closest ".ls-code-editor-wrap")
-    (.querySelector ".CodeMirror") (.-CodeMirror)))
+  (when target
+    (when-let [node (util/rec-get-node target "ls-code-editor-wrap")]
+      (some-> node (.querySelector ".CodeMirror") (.-CodeMirror)))))
+
+(defn- get-code-mode-by-lang
+  [lang]
+  (some (fn [m] (when (= (.-name m) lang) (.-mode m))) js/window.CodeMirror.modeInfo))
 
 (rum/defc src-lang-picker
   [block on-select!]
-  (let [[q, set-q!] (rum/use-state "")]
-    (when-let [modes (some->> js/window.CodeMirror (.-modes) (js/Object.keys) (js->clj) (remove #(= "null" %)))]
-      (let [matched (seq (fuzzy-search modes q))
-            matched (or matched (if (string/blank? q) modes [q]))]
-        [:div.overflow-hidden
-         [:div.p-1
-          (shui/input {:auto-focus true
-                       :on-change (fn [^js e] (set-q! (util/evalue e)))
-                       :class "px-1.5 h-7 text-xs mb-0.5"})]
-         [:div.max-h-72.overflow-auto
-          (ui/auto-complete matched
-            {:on-chosen (fn [chosen e]
+  (when-let [langs (map (fn [m] (.-name m)) js/window.CodeMirror.modeInfo)]
+    (let [options (map (fn [lang] {:label lang :value lang}) langs)]
+      (select/select {:items options
+                      :input-default-placeholder "Choose language"
+                      :on-chosen
+                      (fn [chosen _ _ e]
+                        (let [lang (:value chosen)]
                           (when (and (= :code (:logseq.property.node/display-type block))
-                                  (not= chosen (:logseq.property.code/mode block)))
-                            (on-select! chosen e))
-                          (shui/popup-hide!))
-             :item-render (fn [mode]
-                            [:strong mode])})]]))))
+                                     (not= lang (:logseq.property.code/lang block)))
+                            (on-select! lang e)))
+                        (shui/popup-hide!))}))))
 
 (rum/defc src-cp < rum/static
   [config options]
   (let [block (:block config)
         container-id (:container-id config)
-        *mode-ref (rum/use-ref nil)]
+        *mode-ref (rum/use-ref nil)
+        *actions-ref (rum/use-ref nil)]
 
     (rum/use-effect!
-      (fn []
-        (when (= (some-> (state/sub :editor/pending-type-block) :block :block/uuid) (:block/uuid block))
-          (util/schedule #(some-> (rum/deref *mode-ref) (.click)))
-          (state/set-state! :editor/pending-type-block nil)))
-      [])
+     (fn []
+       (when (= (some-> (state/sub :editor/pending-type-block) :block :block/uuid) (:block/uuid block))
+         (util/schedule #(some-> (rum/deref *mode-ref) (.click)))
+         (state/set-state! :editor/pending-type-block nil)))
+     [])
 
     (when options
       (let [html-export? (:html-export? config)
@@ -3641,37 +3640,59 @@
           (let [language (if (contains? #{"edn" "clj" "cljc" "cljs"} language) "clojure" language)]
             [:div.ui-fenced-code-editor.flex.flex-1
              {:ref (fn [el]
-                     (set-inside-portal? (and el (whiteboard-handler/inside-portal? el))))}
+                     (set-inside-portal? (and el (whiteboard-handler/inside-portal? el))))
+              :on-mouse-over #(dom/add-class! (rum/deref *actions-ref) "opacity-100")
+              :on-mouse-leave (fn [e]
+                                (when (dom/has-class? (.-target e) "code-editor")
+                                  (dom/remove-class! (rum/deref *actions-ref) "opacity-100")))}
              (cond
                (nil? inside-portal?) nil
 
                (or (:slide? config) inside-portal?)
                (highlight/highlight (str (random-uuid))
-                 {:class (str "language-" language)
-                  :data-lang language}
-                 code)
+                                    {:class (str "language-" language)
+                                     :data-lang language}
+                                    code)
 
                :else
                [:div.ls-code-editor-wrap
-                [:a.select-language
-                 {:ref *mode-ref
-                  :containerid (str container-id)
-                  :blockid (str (:block/uuid block))
-                  :on-click (fn [^js e]
-                              (let [target (.-target e)]
-                                (shui/popup-show! target
-                                  #(src-lang-picker block
-                                     (fn [mode ^js e]
-                                       (when-let [^js cm (get-cm-instance target)]
-                                         (.setOption cm "mode" mode)
-                                         (when (or (string/blank? (util/trim-safe code))
-                                                 (not= (some-> e (.-type)) "click"))
-                                           (.focus cm)
-                                           (.setCursor cm (.lineCount cm) 0))
-                                         (db-property-handler/set-block-property!
-                                           (:db/id block) :logseq.property.code/mode mode))))
-                                  {:align :end})))}
-                 (or language "Select a language")]
+                [:div.code-block-actions.flex.flex-row.gap-1.opacity-0.transition-opacity.ease-in.duration-300
+                 {:ref *actions-ref}
+                 (shui/button
+                  {:variant :text
+                   :size :sm
+                   :class "select-language"
+                   :ref *mode-ref
+                   :containerid (str container-id)
+                   :blockid (str (:block/uuid block))
+                   :on-click (fn [^js e]
+                               (util/stop-propagation e)
+                               (let [target (.-target e)]
+                                 (shui/popup-show! target
+                                                   #(src-lang-picker block
+                                                                     (fn [lang ^js e]
+                                                                       (when-let [^js cm (get-cm-instance target)]
+                                                                         (if-let [mode (get-code-mode-by-lang lang)]
+                                                                           (.setOption cm "mode" mode)
+                                                                           (throw (ex-info "code mode not found"
+                                                                                           {:lang lang})))
+                                                                         (when (or (string/blank? (util/trim-safe code))
+                                                                                   (not= (some-> e (.-type)) "click"))
+                                                                           (.focus cm)
+                                                                           (.setCursor cm (.lineCount cm) 0))
+                                                                         (db-property-handler/set-block-property!
+                                                                          (:db/id block) :logseq.property.code/lang lang))))
+                                                   {:align :end})))}
+                  (or language "Choose language")
+                  (ui/icon "chevron-down"))
+                 (shui/button
+                  {:variant :text
+                   :size :sm
+                   :on-click (fn [e]
+                               (util/stop-propagation e)
+                               (editor-handler/copy-block-content! block))}
+                  (ui/icon "copy")
+                  "Copy")]
                 (lazy-editor/editor config (str (d/squuid)) attr code options)
                 (let [options (:options options) block (:block config)]
                   (when (and (= language "clojure") (contains? (set options) ":results"))
@@ -4029,26 +4050,26 @@
   (let [container-id (some-> (:editor/container-id @state/state) (deref))
         uuid' (:block/uuid editing-block)]
     (rum/use-effect!
-      (fn []
-        (case (:logseq.property.node/display-type editing-block)
-          :code
-          (let [_cursor-pos (some-> (:editor/cursor-range @state/state) (deref) (count))
-                direction (:block.editing/direction editing-block)
-                pos (:block.editing/pos editing-block)
-                target (js/document.querySelector
-                         (util/format "a.select-language[blockid=\"%s\"][containerid=\"%s\"]" uuid' container-id))]
-            (when-let [cm (get-cm-instance target)]
-              (let [to-line (case direction
-                              :up (.lastLine cm)
-                              (case pos
-                                :max (.lastLine cm)
-                                0))]
+     (fn []
+       (case (:logseq.property.node/display-type editing-block)
+         :code
+         (let [_cursor-pos (some-> (:editor/cursor-range @state/state) (deref) (count))
+               direction (:block.editing/direction editing-block)
+               pos (:block.editing/pos editing-block)
+               target (js/document.querySelector
+                       (util/format ".select-language[blockid=\"%s\"][containerid=\"%s\"]" uuid' container-id))]
+           (when-let [cm (get-cm-instance target)]
+             (let [to-line (case direction
+                             :up (.lastLine cm)
+                             (case pos
+                               :max (.lastLine cm)
+                               0))]
                 ;; move to friendly cursor
-                (doto cm
-                  (.focus)
-                  (.setCursor to-line (or _cursor-pos 0))))))
-          :dune))
-      [editing-block]))
+               (doto cm
+                 (.focus)
+                 (.setCursor to-line (or _cursor-pos 0))))))
+         :dune))
+     [editing-block]))
   nil)
 
 ;; headers to hiccup

+ 10 - 4
src/main/frontend/components/block.css

@@ -951,8 +951,14 @@ html.is-mac {
     @apply hidden;
   }
 
-  > a.select-language {
-    @apply absolute right-1 top-1 bg-gray-02-alpha border rounded text-gray-11
-    select-none opacity-70 hover:opacity-90 z-[1] text-xs px-1.5;
+  > .code-block-actions {
+    @apply absolute right-1 top-1 select-none z-[1] text-xs;
+    button {
+        @apply !py-0 h-4 text-muted-foreground hover:text-foreground text-xs px-1;
+    }
+    svg {
+      width: 14px;
+      height: 14px;
+    }
   }
-}
+}

+ 12 - 12
src/main/frontend/components/select.cljs

@@ -133,22 +133,22 @@
                              {:item-render       (or item-cp (fn [result chosen?]
                                                                (render-item result chosen? multiple-choices? *selected-choices)))
                               :class             "cp__select-results"
-                              :on-chosen         (fn [raw-chosen]
+                              :on-chosen         (fn [raw-chosen e]
                                                    (reset! input "")
                                                    (let [chosen (extract-chosen-fn raw-chosen)]
                                                      (if multiple-choices?
                                                        (if (selected-choices chosen)
                                                          (do
                                                            (swap! *selected-choices disj chosen)
-                                                           (when on-chosen (on-chosen chosen false @*selected-choices)))
+                                                           (when on-chosen (on-chosen chosen false @*selected-choices e)))
                                                          (do
                                                            (swap! *selected-choices conj chosen)
-                                                           (when on-chosen (on-chosen chosen true @*selected-choices))))
+                                                           (when on-chosen (on-chosen chosen true @*selected-choices e))))
                                                        (do
                                                          (when (and close-modal? (not multiple-choices?))
                                                            (state/close-modal!))
                                                          (when on-chosen
-                                                           (on-chosen chosen true @*selected-choices))))))
+                                                           (on-chosen chosen true @*selected-choices e))))))
                               :empty-placeholder (empty-placeholder t)})]
 
                            (when (and multiple-choices? (fn? on-apply))
@@ -206,7 +206,7 @@
                           [:div.mb-2 (t :select.graph/empty-placeholder-description)]
                           (ui/button
                            (t :select.graph/add-graph)
-                            :href (rfe/href :graphs)
+                           :href (rfe/href :graphs)
                            :on-click state/close-modal!)])}
    :graph-remove
    {:items-fn (fn []
@@ -223,14 +223,14 @@
    {:items-fn (fn []
                 (let [current-repo (state/get-current-repo)]
                   (->> (state/get-repos)
-                      (remove (fn [{:keys [url]}]
+                       (remove (fn [{:keys [url]}]
                                 ;; Can't replace current graph as ui wouldn't reload properly
-                                (or (= url current-repo) (not (config/db-based-graph? url)))))
-                      (map (fn [{:keys [url] :as original-graph}]
-                             {:value (text-util/get-graph-name-from-path url)
-                              :id (config/get-repo-dir url)
-                              :graph url
-                              :original-graph original-graph})))))
+                                 (or (= url current-repo) (not (config/db-based-graph? url)))))
+                       (map (fn [{:keys [url] :as original-graph}]
+                              {:value (text-util/get-graph-name-from-path url)
+                               :id (config/get-repo-dir url)
+                               :graph url
+                               :original-graph original-graph})))))
     :on-chosen #(dev-common-handler/import-chosen-graph (:graph %))}})
 
 (rum/defc select-modal < rum/reactive

+ 7 - 2
src/main/frontend/handler/editor.cljs

@@ -940,6 +940,11 @@
     (set-blocks-id! [block-id])
     (util/copy-to-clipboard! (tap-clipboard block-id)))))
 
+(defn copy-block-content!
+  [block]
+  (util/copy-to-clipboard! (:block/title block))
+  (notification/show! "Copied!" :success))
+
 (defn select-block!
   [block-uuid]
   (block-handler/select-block! block-uuid))
@@ -2571,8 +2576,8 @@
                 value (state/get-edit-content)]
             (p/do!
              (when (and
-                     (not (state/block-component-editing?))
-                     (not= (clean-content! repo format title)
+                    (not (state/block-component-editing?))
+                    (not= (clean-content! repo format title)
                           (string/trim value)))
                (save-block! repo uuid value))
 

+ 2 - 1
src/main/frontend/worker/db/migrate.cljs

@@ -300,7 +300,8 @@
         :fix add-query-property-to-query-tag}]
    [26 {:properties [:logseq.property.node/type]}]
    [27 {:properties [:logseq.property.code/mode]}]
-   [28 {:fix (rename-properties {:logseq.property.node/type :logseq.property.node/display-type})}]])
+   [28 {:fix (rename-properties {:logseq.property.node/type :logseq.property.node/display-type})}]
+   [29 {:properties [:logseq.property.code/lang]}]])
 
 (let [max-schema-version (apply max (map first schema-version->updates))]
   (assert (<= db-schema/version max-schema-version))

+ 15 - 15
yarn.lock

@@ -2134,10 +2134,10 @@ code-point-at@^1.0.0:
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
   integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==
 
[email protected]3:
-  version "5.65.13"
-  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.13.tgz#c098a6f409db8b5a7c5722788bd9fa3bb2367f2e"
-  integrity sha512-SVWEzKXmbHmTQQWaz03Shrh4nybG0wXx2MEu3FO4ezbPW8IbnZEd5iGHGEffSUaitKYa3i+pHpBsSvw8sPHtzg==
[email protected]8:
+  version "5.65.18"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.18.tgz#d7146e4271135a9b4adcd023a270185457c9c428"
+  integrity sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==
 
 collection-map@^1.0.0:
   version "1.0.0"
@@ -3130,7 +3130,7 @@ escape-html@~1.0.3:
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
   integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
 
-escape-string-regexp@^1.0.5:
+escape-string-regexp@^1.0.3, escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
@@ -3974,6 +3974,16 @@ gulp-cli@^2.2.0:
     v8flags "^3.2.0"
     yargs "^7.1.0"
 
+gulp-postcss@^10.0.0:
+  version "10.0.0"
+  resolved "https://registry.yarnpkg.com/gulp-postcss/-/gulp-postcss-10.0.0.tgz#a88d7c6602f8a8c94aaa9f28ac3a68def00c7ada"
+  integrity sha512-z1RF2RJEX/BvFsKN11PXai8lRmihZTiHnlJf7Zu8uHaA/Q7Om4IeN8z1NtMAW5OiLwUY02H0DIFl9tHl0CNSgA==
+  dependencies:
+    fancy-log "^2.0.0"
+    plugin-error "^2.0.1"
+    postcss-load-config "^5.0.0"
+    vinyl-sourcemaps-apply "^0.2.1"
+
 gulp-replace@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/gulp-replace/-/gulp-replace-1.1.4.tgz#06a0e9ee36f30e343c1e0a2dd760ec32c8a3d3b2"
@@ -3985,16 +3995,6 @@ gulp-replace@^1.1.4:
     replacestream "^4.0.3"
     yargs-parser ">=5.0.0-security.0"
 
-gulp-postcss@^10.0.0:
-  version "10.0.0"
-  resolved "https://registry.yarnpkg.com/gulp-postcss/-/gulp-postcss-10.0.0.tgz#a88d7c6602f8a8c94aaa9f28ac3a68def00c7ada"
-  integrity sha512-z1RF2RJEX/BvFsKN11PXai8lRmihZTiHnlJf7Zu8uHaA/Q7Om4IeN8z1NtMAW5OiLwUY02H0DIFl9tHl0CNSgA==
-  dependencies:
-    fancy-log "^2.0.0"
-    plugin-error "^2.0.1"
-    postcss-load-config "^5.0.0"
-    vinyl-sourcemaps-apply "^0.2.1"
-
 gulp@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa"