Ver Fonte

UX polish and refactor
- Visual polish for the settings page, keyboard shortcuts page, and outdenting tooltip
- Tweak copy throughout the app to make it a little simpler
- Add the ability to fetch the name of the meta key (i.e. Cmd or Ctrl) so it can be displayed to the user
- Make it possible to Cancel out of editing a keyboard shortcut, rather than the only option being to Save

Devon Zuegel há 4 anos atrás
pai
commit
592d731732

+ 307 - 1
resources/css/common.css

@@ -341,7 +341,294 @@ video {
   max-width: 100%;
 }
 
-/* TODO: add all tailwind supported bg colors */
+.bg-black {
+  background-color: rgba(0, 0, 0);
+}
+
+.bg-white {
+  background-color: rgba(255, 255, 255);
+}
+
+.bg-gray-50 {
+  background-color: rgba(249, 250, 251);
+}
+
+.bg-gray-100 {
+  background-color: rgba(243, 244, 246);
+}
+
+.bg-gray-200 {
+  background-color: rgba(229, 231, 235);
+}
+
+.bg-gray-300 {
+  background-color: rgba(209, 213, 219);
+}
+
+.bg-gray-400 {
+  background-color: rgba(156, 163, 175);
+}
+
+.bg-gray-500 {
+  background-color: rgba(107, 114, 128);
+}
+
+.bg-gray-600 {
+  background-color: rgba(75, 85, 99);
+}
+
+.bg-gray-700 {
+  background-color: rgba(55, 65, 81);
+}
+
+.bg-gray-800 {
+  background-color: rgba(31, 41, 55);
+}
+
+.bg-gray-900 {
+  background-color: rgba(17, 24, 39);
+}
+
+.bg-red-50 {
+  background-color: rgba(254, 242, 242);
+}
+
+.bg-red-100 {
+  background-color: rgba(254, 226, 226);
+}
+
+.bg-red-200 {
+  background-color: rgba(254, 202, 202);
+}
+
+.bg-red-300 {
+  background-color: rgba(252, 165, 165);
+}
+
+.bg-red-400 {
+  background-color: rgba(248, 113, 113);
+}
+
+.bg-red-500 {
+  background-color: rgba(239, 68, 68);
+}
+
+.bg-red-600 {
+  background-color: rgba(220, 38, 38);
+}
+
+.bg-red-700 {
+  background-color: rgba(185, 28, 28);
+}
+
+.bg-red-800 {
+  background-color: rgba(153, 27, 27);
+}
+
+.bg-red-900 {
+  background-color: rgba(127, 29, 29);
+}
+
+.bg-yellow-50 {
+  background-color: rgba(255, 251, 235);
+}
+
+.bg-yellow-100 {
+  background-color: rgba(254, 243, 199);
+}
+
+.bg-yellow-200 {
+  background-color: rgba(253, 230, 138);
+}
+
+.bg-yellow-300 {
+  background-color: rgba(252, 211, 77);
+}
+
+.bg-yellow-400 {
+  background-color: rgba(251, 191, 36);
+}
+
+.bg-yellow-500 {
+  background-color: rgba(245, 158, 11);
+}
+
+.bg-yellow-600 {
+  background-color: rgba(217, 119, 6);
+}
+
+.bg-yellow-700 {
+  background-color: rgba(180, 83, 9);
+}
+
+.bg-yellow-800 {
+  background-color: rgba(146, 64, 14);
+}
+
+.bg-yellow-900 {
+  background-color: rgba(120, 53, 15);
+}
+
+.bg-green-50 {
+  background-color: rgba(236, 253, 245);
+}
+
+.bg-green-100 {
+  background-color: rgba(209, 250, 229);
+}
+
+.bg-green-200 {
+  background-color: rgba(167, 243, 208);
+}
+
+.bg-green-300 {
+  background-color: rgba(110, 231, 183);
+}
+
+.bg-green-400 {
+  background-color: rgba(52, 211, 153);
+}
+
+.bg-green-500 {
+  background-color: rgba(16, 185, 129);
+}
+
+.bg-green-600 {
+  background-color: rgba(5, 150, 105);
+}
+
+.bg-green-700 {
+  background-color: rgba(4, 120, 87);
+}
+
+.bg-green-800 {
+  background-color: rgba(6, 95, 70);
+}
+
+.bg-green-900 {
+  background-color: rgba(6, 78, 59);
+}
+
+.bg-blue-50 {
+  background-color: rgba(239, 246, 255);
+}
+
+.bg-blue-100 {
+  background-color: rgba(219, 234, 254);
+}
+
+.bg-blue-200 {
+  background-color: rgba(191, 219, 254);
+}
+
+.bg-blue-300 {
+  background-color: rgba(147, 197, 253);
+}
+
+.bg-blue-400 {
+  background-color: rgba(96, 165, 250);
+}
+
+.bg-blue-500 {
+  background-color: rgba(59, 130, 246);
+}
+
+.bg-blue-600 {
+  background-color: rgba(37, 99, 235);
+}
+
+.bg-blue-700 {
+  background-color: rgba(29, 78, 216);
+}
+
+.bg-blue-800 {
+  background-color: rgba(30, 64, 175);
+}
+
+.bg-blue-900 {
+  background-color: rgba(30, 58, 138);
+}
+
+.bg-indigo-50 {
+  background-color: rgba(238, 242, 255);
+}
+
+.bg-indigo-100 {
+  background-color: rgba(224, 231, 255);
+}
+
+.bg-indigo-200 {
+  background-color: rgba(199, 210, 254);
+}
+
+.bg-indigo-300 {
+  background-color: rgba(165, 180, 252);
+}
+
+.bg-indigo-400 {
+  background-color: rgba(129, 140, 248);
+}
+
+.bg-indigo-500 {
+  background-color: rgba(99, 102, 241);
+}
+
+.bg-indigo-600 {
+  background-color: rgba(79, 70, 229);
+}
+
+.bg-indigo-700 {
+  background-color: rgba(67, 56, 202);
+}
+
+.bg-indigo-800 {
+  background-color: rgba(55, 48, 163);
+}
+
+.bg-indigo-900 {
+  background-color: rgba(49, 46, 129);
+}
+
+.bg-purple-50 {
+  background-color: rgba(245, 243, 255);
+}
+
+.bg-purple-100 {
+  background-color: rgba(237, 233, 254);
+}
+
+.bg-purple-200 {
+  background-color: rgba(221, 214, 254);
+}
+
+.bg-purple-300 {
+  background-color: rgba(196, 181, 253);
+}
+
+.bg-purple-400 {
+  background-color: rgba(167, 139, 250);
+}
+
+.bg-purple-500 {
+  background-color: rgba(139, 92, 246);
+}
+
+.bg-purple-600 {
+  background-color: rgba(124, 58, 237);
+}
+
+.bg-purple-700 {
+  background-color: rgba(109, 40, 217);
+}
+
+.bg-purple-800 {
+  background-color: rgba(91, 33, 182);
+}
+
+.bg-purple-900	{
+  background-color: rgba(76, 29, 149);
+}
+
 .bg-pink-100 {
   background-color: #fff5f7;
 }
@@ -833,3 +1120,22 @@ a.page-op {
 .search-more {
     background: var(--ls-a-chosen-bg);
 }
+
+.keyboard-shortcut > code {
+  margin: 2px;
+  background-color: var(--ls-quaternary-background-color);
+  padding: 2px 4px !important;
+  border-radius: 6px;
+}
+
+html[data-theme='light'] .keyboard-shortcut > code {
+  box-shadow: inset 0 -1px 0 #433f3855, 0 0 1px 1px #433f3822;
+}
+
+html[data-theme='dark'] .keyboard-shortcut > code {
+  box-shadow: inset 0 -1px 0 var(--ls-primary-background-color), 0 0 1px 1px rgba(255, 255, 255,.1);
+}
+
+.ui__modal-panel {
+  border-radius: 8px;
+}

+ 0 - 5
resources/css/table.css

@@ -9,10 +9,6 @@ table {
     margin: 1rem 0;
 }
 
-tr {
-    border-bottom: 1px solid var(--ls-border-color);
-}
-
 th {
     font-size: 14px;
     font-weight: 400;
@@ -22,7 +18,6 @@ th {
 }
 
 td {
-    border-bottom: 1px solid var(--ls-border-color);
     padding: 6px 8px;
     text-align: left;
 }

+ 4 - 0
resources/css/tooltip.css

@@ -658,3 +658,7 @@
 .tippy-wrapper {
   background-color: var(--ls-tertiary-background-color);
 }
+
+.tippy-hover {
+  cursor: pointer;
+}

+ 2 - 3
src/main/frontend/components/onboarding.cljs

@@ -226,10 +226,9 @@
          svg/discord]]]
       [:li
        (t :help/shortcuts)
-       [:br]
        (ui/button
-        "Full-list & Customize"
-        :class "text-sm p-1"
+        "Customize"
+        :class "text-sm p-1 ml-3"
         :on-click
         (fn []
           (route-handler/redirect! {:to :shortcut})))

+ 298 - 258
src/main/frontend/components/settings.cljs

@@ -70,27 +70,29 @@
       [:span.pl-1.opacity-70 "Git commit requires the cors address."]]]))
 
 (defn toggle
-  [label-for name state on-toggle]
+  [label-for name state on-toggle & [detail-text]]
   [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
    [:label.block.text-sm.font-medium.leading-5.opacity-70
     {:for label-for}
     name]
    [:div.mt-1.sm:mt-0.sm:col-span-2
-    [:div.rounded-md.sm:max-w-xs
-     (ui/toggle state on-toggle true)]]])
+    [:div.rounded-md
+     {:style {:display "flex" :gap "1rem" :align-items "center"}}
+     (ui/toggle state on-toggle true)
+     detail-text]]])
 
 (rum/defcs app-updater < rum/reactive
   [state]
   (let [update-pending? (state/sub :electron/updater-pending?)
         {:keys [type payload]} (state/sub :electron/updater)]
-    [:div.cp__settings-app-updater
+    [:span.cp__settings-app-updater
+
      (ui/button
       (if update-pending? "Checking ..." "Check for updates")
-
-      :intent "logseq"
-      :class "check-update"
+      :class "text-sm p-1 mr-3"
       :disabled update-pending?
       :on-click #(js/window.apis.checkForUpdates false))
+
      (when-not (or update-pending?
                    (string/blank? type))
        [:div.update-state
@@ -138,12 +140,272 @@
 
 (rum/defc outdenting-hint
   []
-  [:div
-   [:p "See more details at " [:a {:target "_blank" :href "https://discuss.logseq.com/t/whats-your-preferred-outdent-behavior-the-direct-one-or-the-logical-one/978"} "here"] "."]
-   [:p "default(left) vs logical(right)"]
-   [:img {:src "https://discuss.logseq.com/uploads/default/original/1X/e8ea82f63a5e01f6d21b5da827927f538f3277b9.gif"
-          :width 500
-          :height 500}]])
+  [:div.ui__modal-panel
+   {:style {:box-shadow "0 4px 20px 4px rgba(0, 20, 60, .1), 0 4px 80px -8px rgba(0, 20, 60, .2)"}}
+   [:div {:style {:margin "12px" :max-width "500px"}}
+    [:p.text-sm
+     "The left side shows outdenting with the default setting, and the right shows outdenting with logical outdenting enabled. "
+     [:a.text-sm
+      {:target "_blank" :href "https://discuss.logseq.com/t/whats-your-preferred-outdent-behavior-the-direct-one-or-the-logical-one/978"}
+      "→ Learn more"]]
+    [:img {:src "https://discuss.logseq.com/uploads/default/original/1X/e8ea82f63a5e01f6d21b5da827927f538f3277b9.gif"
+           :width 500
+           :height 500}]]])
+
+(defn edit-config-edn []
+  (rum/with-context [[t] i18n/*tongue-context*]
+    [:div.text-sm
+     [:a {:href     (rfe/href :file {:path (config/get-config-path)})
+          :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))}
+      (t :settings-page/edit-config-edn)]]))
+
+(defn show-brackets-row [t show-brackets?]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "show_brackets"}
+    (t :settings-page/show-brackets)]
+   [:div
+    [:div.rounded-md.sm:max-w-xs
+     (ui/toggle show-brackets?
+                config-handler/toggle-ui-show-brackets!
+                true)]]
+   [:div {:style {:text-align "right"}}
+    ;; TODO: Fetch this shortcut from config.cljs so there's one source of truth
+    (ui/keyboard-shortcut [:meta :c :meta :b])]])
+
+(defn language-row [t preferred-language]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "preferred_language"}
+    (t :language)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div.max-w-lg.rounded-md
+     [:select.form-select.is-small
+      {:value preferred-language
+       :on-change (fn [e]
+                    (let [lang-code (util/evalue e)]
+                      (state/set-preferred-language! lang-code)
+                      (ui-handler/re-render-root!)))}
+      (for [language dicts/languages]
+        (let [lang-code (name (:value language))
+              lang-label (:label language)]
+          [:option {:key lang-code :value lang-code} lang-label]))]]]])
+
+(defn theme-modes-row [t switch-theme system-theme? dark?]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "toggle_theme"}
+    (t :right-side-bar/switch-theme (string/capitalize switch-theme))]
+   [:div.flex.flex-row.mt-1.sm:mt-0.sm:col-span-1
+    [:div.rounded-md.sm:max-w-xs
+
+     [:ul.theme-modes-options
+      [:li {:on-click (partial state/use-theme-mode! "light")
+            :class    (classnames [{:active (and (not system-theme?) (not dark?))}])} [:i.mode-light] [:strong "light"]]
+      [:li {:on-click (partial state/use-theme-mode! "dark")
+            :class    (classnames [{:active (and (not system-theme?) dark?)}])} [:i.mode-dark] [:strong "dark"]]
+      [:li {:on-click (partial state/use-theme-mode! "system")
+            :class    (classnames [{:active system-theme?}])} [:i.mode-system] [:strong "system"]]]]]
+
+   [:div {:style {:text-align "right"}}
+    (ui/keyboard-shortcut [:t :t])]])
+
+(defn file-format-row [t preferred-format]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "preferred_format"}
+    (t :settings-page/preferred-file-format)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div.max-w-lg.rounded-md
+     [:select.form-select.is-small
+      {:value (name preferred-format)
+       :on-change (fn [e]
+                    (let [format (-> (util/evalue e)
+                                     (string/lower-case)
+                                     keyword)]
+                      (user-handler/set-preferred-format! format)))}
+      (for [format (map name [:org :markdown])]
+        [:option {:key format :value format} (string/capitalize format)])]]]])
+
+(defn date-format-row [t preferred-date-format]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "custom_date_format"}
+    (t :settings-page/custom-date-format)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div.max-w-lg.rounded-md
+     [:select.form-select.is-small
+      {:value preferred-date-format
+       :on-change (fn [e]
+                    (let [format (util/evalue e)]
+                      (when-not (string/blank? format)
+                        (config-handler/set-config! :date-formatter format)
+                        (notification/show!
+                         [:div "You need to re-index your graph to make the change works"]
+                         :success)
+                        (state/close-modal!)
+                        (route-handler/redirect! {:to :repos}))))}
+      (for [format (sort (date/journal-title-formatters))]
+        [:option {:key format} format])]]]])
+
+(defn workflow-row [t preferred-workflow]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "preferred_workflow"}
+    (t :settings-page/preferred-workflow)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div.max-w-lg.rounded-md
+     [:select.form-select.is-small
+      {:value (name preferred-workflow)
+       :on-change (fn [e]
+                    (-> (util/evalue e)
+                        string/lower-case
+                        keyword
+                        (#(if (= % :now) :now :todo))
+                        user-handler/set-preferred-workflow!))}
+      (for [workflow [:now :todo]]
+        [:option {:key (name workflow) :value (name workflow)}
+         (if (= workflow :now) "NOW/LATER" "TODO/DOING")])]]]])
+
+(defn outdenting-row [t logical-outdenting?]
+  (toggle "preferred_outdenting"
+          [(t :settings-page/preferred-outdenting)
+           (ui/tippy {:html (outdenting-hint)
+                      :class "tippy-hover ml-2"
+                      :interactive true
+                      :disabled false}
+                     (svg/info))]
+          logical-outdenting?
+          config-handler/toggle-logical-outdenting!))
+
+(defn tooltip-row [t enable-tooltip?]
+  (toggle "enable_tooltip"
+          (t :settings-page/enable-tooltip)
+          enable-tooltip?
+          (fn []
+            (config-handler/toggle-ui-enable-tooltip!))))
+
+(defn timetracking-row [t enable-timetracking?]
+  (toggle "enable_timetracking"
+          (t :settings-page/enable-timetracking)
+          enable-timetracking?
+          (fn []
+            (let [value (not enable-timetracking?)]
+              (config-handler/set-config! :feature/enable-timetracking? value)))))
+
+(defn update-home-page
+  [event]
+  (let [value (util/evalue event)]
+    (cond
+      (string/blank? value)
+      (let [home (get (state/get-config) :default-home {})
+            new-home (dissoc home :page)]
+        (config-handler/set-config! :default-home new-home)
+        (notification/show! "Home default page updated successfully!" :success))
+
+      (page-handler/page-exists? (string/lower-case value))
+      (let [home (get (state/get-config) :default-home {})
+            new-home (assoc home :page value)]
+        (config-handler/set-config! :default-home new-home)
+        (notification/show! "Home default page updated successfully!" :success))
+
+      :else
+      (notification/show! (str "The page \"" value "\" doesn't exist yet. Please create that page first, and then try again.") :warning))))
+
+(defn journal-row [t enable-journals?]
+  [(toggle "enable_journals"
+           (t :settings-page/enable-journals)
+           enable-journals?
+           (fn []
+             (let [value (not enable-journals?)]
+               (config-handler/set-config! :feature/enable-journals? value))))
+
+   (when (not enable-journals?)
+     [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+      [:label.block.text-sm.font-medium.leading-5.opacity-70
+       {:for "default page"}
+       (t :settings-page/home-default-page)]
+      [:div.mt-1.sm:mt-0.sm:col-span-2
+       [:div.max-w-lg.rounded-md.sm:max-w-xs
+        [:input#home-default-page.form-input.is-small.transition.duration-150.ease-in-out
+         {:default-value (state/sub-default-home-page)
+          :on-blur update-home-page
+          :on-key-press (fn [e]
+                          (when (= "Enter" (util/ekey e))
+                            (update-home-page e)))}]]]])])
+
+(defn encryption-row [t enable-encryption?]
+  (toggle "enable_encryption"
+          (t :settings-page/enable-encryption)
+          enable-encryption?
+          (fn []
+            (let [value (not enable-encryption?)]
+              (config-handler/set-config! :feature/enable-encryption? value)))))
+
+(defn keyboard-shortcuts-row [t]
+  [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "customize_shortcuts"}
+    (t :settings-page/customize-shortcuts)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div
+     (ui/button
+      (t :settings-page/shortcut-settings)
+      :class "text-sm p-1"
+      :style {:margin-top "0px"}
+      :on-click
+      (fn []
+        (state/close-settings!)
+        (route-handler/redirect! {:to :shortcut})))]]])
+
+(defn auto-push-row [t current-repo enable-git-auto-push?]
+  (when (string/starts-with? current-repo "https://")
+    (toggle "enable_git_auto_push"
+            "Enable Git auto push"
+            enable-git-auto-push?
+            (fn []
+              (let [value (not enable-git-auto-push?)]
+                (config-handler/set-config! :git-auto-push value))))))
+
+(defn usage-diagnostics-row [t instrument-disabled?]
+  (toggle "usage-diagnostics"
+          (t :settings-page/disable-sentry)
+          (not instrument-disabled?)
+          (fn [] (instrument/disable-instrument
+                  (not instrument-disabled?)))
+          [:span.text-sm.opacity-50 "Logseq will never collect your local graph database or sell your data."]))
+
+(defn clear-cache-row [t]
+  [:div.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center.sm:pt-5
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    {:for "clear_cache"}
+    (t :settings-page/clear-cache)]
+   [:div.mt-1.sm:mt-0.sm:col-span-2
+    [:div.max-w-lg.rounded-md.sm:max-w-xs
+     (ui/button
+      (t :settings-page/clear)
+      :class "text-sm p-1"
+      :on-click handler/clear-cache!)]]])
+
+(defn version-row [t version]
+  [:div.it.app-updater.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
+   [:label.block.text-sm.font-medium.leading-5.opacity-70
+    (t :settings-page/current-version)]
+   [:div.wrap.sm:mt-0.sm:col-span-2
+    (when (util/electron?) (app-updater))
+    [:span.ver version]]])
+
+(defn developer-mode-row [t developer-mode?]
+  (toggle "developer_mode"
+          (t :settings-page/developer-mode)
+          developer-mode?
+          (fn []
+            (let [mode (not developer-mode?)]
+              (state/set-developer-mode! mode)
+              (and mode (util/electron?)
+                   (if (js/confirm (t :developer-mode-alert))
+                     (js/logseq.api.relaunch)))))
+          [:div.text-sm.opacity-50 (t :settings-page/developer-mode-desc)]))
 
 (rum/defcs settings < rum/reactive
   []
@@ -171,259 +433,37 @@
         switch-theme (if dark? "white" "dark")]
     (rum/with-context [[t] i18n/*tongue-context*]
       [:div#settings.cp__settings-main
-       [:h1.title (t :settings)]
-
-       [:div.panel-wrap
-
-        ;;; theme modes
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "toggle_theme"}
-          (t :right-side-bar/switch-theme (string/capitalize switch-theme))]
-         [:div.flex.flex-row.mt-1.sm:mt-0.sm:col-span-1
-          [:div.rounded-md.sm:max-w-xs
-
-           [:ul.theme-modes-options
-            [:li {:on-click (partial state/use-theme-mode! "light")
-                  :class    (classnames [{:active (and (not system-theme?) (not dark?))}])} [:i.mode-light] [:strong "light"]]
-            [:li {:on-click (partial state/use-theme-mode! "dark")
-                  :class    (classnames [{:active (and (not system-theme?) dark?)}])} [:i.mode-dark] [:strong "dark"]]
-            [:li {:on-click (partial state/use-theme-mode! "system")
-                  :class    (classnames [{:active system-theme?}])} [:i.mode-system] [:strong "system"]]]]]
-
-         [:span.ml-4.opacity-50.text-sm.px-5 "t t"]]
-
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "show_brackets"}
-          (t :settings-page/show-brackets)]
-         [:div.flex.flex-row.mt-1.sm:mt-0.sm:col-span-2
-          [:div.rounded-md.sm:max-w-xs
-           (ui/toggle show-brackets?
-                      config-handler/toggle-ui-show-brackets!
-                      true)]
-          [:span.ml-4.opacity-50.text-sm "Ctrl-c Ctrl-b"]]]
-
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "preferred_language"}
-          (t :language)]
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.max-w-lg.rounded-md
-           [:select.form-select.is-small
-            {:value preferred-language
-             :on-change (fn [e]
-                          (let [lang-code (util/evalue e)                                ]
-                            (state/set-preferred-language! lang-code)
-                            (ui-handler/re-render-root!)))}
-            (for [language dicts/languages]
-              (let [lang-code (name (:value language))
-                    lang-label (:label language)]
-                [:option {:key lang-code :value lang-code} lang-label]))]]]]
-
-                        ;; config.edn
-        (when current-repo
-          [:div.mt-5.text-sm
-           [:a {:href     (rfe/href :file {:path (config/get-config-path)})
-                :on-click #(js/setTimeout (fn [] (ui-handler/toggle-settings-modal!)))}
-            (t :settings-page/edit-config-edn)]])]
-
-       [:hr]
-
        [:div.panel-wrap
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "preferred_format"}
-          (t :settings-page/preferred-file-format)]
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.max-w-lg.rounded-md
-           [:select.form-select.is-small
-            {:value (name preferred-format)
-             :on-change (fn [e]
-                          (let [format (-> (util/evalue e)
-                                           (string/lower-case)
-                                           keyword)]
-                            (user-handler/set-preferred-format! format)))}
-            (for [format (map name [:org :markdown])]
-              [:option {:key format :value format} (string/capitalize format)])]]]]
-
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "custom_date_format"}
-          (t :settings-page/custom-date-format)]
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.max-w-lg.rounded-md
-           [:select.form-select.is-small
-            {:value preferred-date-format
-             :on-change (fn [e]
-                          (let [format (util/evalue e)]
-                            (when-not (string/blank? format)
-                              (config-handler/set-config! :date-formatter format)
-                              (notification/show!
-                               [:div "You need to re-index your graph to make the change works"]
-                               :success)
-                              (state/close-modal!)
-                              (route-handler/redirect! {:to :repos}))))}
-            (for [format (sort (date/journal-title-formatters))]
-              [:option {:key format} format])]]]]
-
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "preferred_workflow"}
-          (t :settings-page/preferred-workflow)]
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.max-w-lg.rounded-md
-           [:select.form-select.is-small
-            {:value (name preferred-workflow)
-             :on-change (fn [e]
-                          (-> (util/evalue e)
-                              string/lower-case
-                              keyword
-                              (#(if (= % :now) :now :todo))
-                              user-handler/set-preferred-workflow!))}
-            (for [workflow [:now :todo]]
-              [:option {:key (name workflow) :value (name workflow)}
-               (if (= workflow :now) "NOW/LATER" "TODO/DOING")])]]]]
-        (toggle "preferred_outdenting"
-                (ui/tippy {:html (outdenting-hint)
-                           :interactive true
-                           :disabled false}
-                          (t :settings-page/preferred-outdenting))
-                logical-outdenting?
-                (fn []
-                  (config-handler/toggle-logical-outdenting!)))
-
-        (toggle "enable_tooltip"
-                (t :settings-page/enable-tooltip)
-                enable-tooltip?
-                (fn []
-                  (config-handler/toggle-ui-enable-tooltip!)))
-
-        (toggle "enable_timetracking"
-                (t :settings-page/enable-timetracking)
-                enable-timetracking?
-                (fn []
-                  (let [value (not enable-timetracking?)]
-                    (config-handler/set-config! :feature/enable-timetracking? value))))
-
-                        ;; (toggle "enable_block_time"
-                        ;;         (t :settings-page/enable-block-time)
-                        ;;         enable-block-time?
-                        ;;         (fn []
-                        ;;           (let [value (not enable-block-time?)]
-                        ;;             (config-handler/set-config! :feature/enable-block-time? value))))
-
-        (toggle "enable_journals"
-                (t :settings-page/enable-journals)
-                enable-journals?
-                (fn []
-                  (let [value (not enable-journals?)]
-                    (config-handler/set-config! :feature/enable-journals? value))))
-
-        (when (not enable-journals?)
-          [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-           [:label.block.text-sm.font-medium.leading-5.opacity-70
-            {:for "default page"}
-            (t :settings-page/home-default-page)]
-           [:div.mt-1.sm:mt-0.sm:col-span-2
-            [:div.max-w-lg.rounded-md.sm:max-w-xs
-             [:input#home-default-page.form-input.is-small.transition.duration-150.ease-in-out
-              {:default-value (state/sub-default-home-page)
-               :on-blur       (fn [event]
-                                (let [value (util/evalue event)]
-                                  (cond
-                                    (string/blank? value)
-                                    (let [home (get (state/get-config) :default-home {})
-                                          new-home (dissoc home :page)]
-                                      (config-handler/set-config! :default-home new-home)
-                                      (notification/show! "Home default page updated successfully!" :success))
-
-                                    (page-handler/page-exists? (string/lower-case value))
-                                    (let [home (get (state/get-config) :default-home {})
-                                          new-home (assoc home :page value)]
-                                      (config-handler/set-config! :default-home new-home)
-                                      (notification/show! "Home default page updated successfully!" :success))
-
-                                    :else
-                                    (notification/show! "Please make sure the page exists!" :warning))))}]]]])
-
-        (toggle "enable_encryption"
-                (t :settings-page/enable-encryption)
-                enable-encryption?
-                (fn []
-                  (let [value (not enable-encryption?)]
-                    (config-handler/set-config! :feature/enable-encryption? value))))
-
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "customize_shortcuts"}
-          (t :settings-page/customize-shortcuts)]
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.max-w-lg.rounded-md
-           (ui/button
-            (t :settings-page/shortcut-settings)
-            :class "text-sm p-0.5"
-            :on-click
-            (fn []
-              (state/close-settings!)
-              (route-handler/redirect! {:to :shortcut})))]]]
-
-        (when (string/starts-with? current-repo "https://")
-          (toggle "enable_git_auto_push"
-                  "Enable Git auto push"
-                  enable-git-auto-push?
-                  (fn []
-                    (let [value (not enable-git-auto-push?)]
-                      (config-handler/set-config! :git-auto-push value)))))]
-
-       [:hr]
+        [:h1.title (t :settings)]]
 
-       [:div.panel-wrap
-        [:p "Logseq will never collect your local graph database or sell your data."]
-        (toggle "usage-diagnostics"
-                (t :settings-page/disable-sentry)
-                (not instrument-disabled?)
-                (fn [] (instrument/disable-instrument
-                        (not instrument-disabled?))))]
+       (when current-repo
+         [[:div.panel-wrap
+           (edit-config-edn)]])
 
        [:hr]
 
        [:div.panel-wrap
-
-        [:div.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-center.sm:pt-5
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "clear_cache"}
-          (t :settings-page/clear-cache)]
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.max-w-lg.rounded-md.sm:max-w-xs
-           (ui/button (t :settings-page/clear)
-             :on-click handler/clear-cache!)]]]]
+        (theme-modes-row t switch-theme system-theme? dark?)
+        (language-row t preferred-language)
+        (file-format-row t preferred-format)
+        (date-format-row t preferred-date-format)
+        (workflow-row t preferred-workflow)
+        (show-brackets-row t show-brackets?)
+        (outdenting-row t logical-outdenting?)
+        (tooltip-row t enable-tooltip?)
+        (timetracking-row t enable-timetracking?)
+        (journal-row t enable-journals?)
+        (encryption-row t enable-encryption?)
+        (keyboard-shortcuts-row t)
+        (auto-push-row t current-repo enable-git-auto-push?)]
+
+       [:hr] ;; Outside of panel wrap so that it is wider
 
        [:div.panel-wrap
-        [:div.it.app-updater.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          (t :settings-page/current-version)]
-         [:div.wrap.sm:mt-0.sm:col-span-2
-          [:div.ver version]
-          (if (util/electron?) (app-updater))]]
-
-        [:div.it.sm:grid.sm:grid-cols-3.sm:gap-4.sm:items-start
-         [:label.block.text-sm.font-medium.leading-5.opacity-70
-          {:for "developer_mode"}
-          (t :settings-page/developer-mode)]
-
-         [:div.mt-1.sm:mt-0.sm:col-span-2
-          [:div.rounded-md.sm:max-w-xs
-           (ui/toggle developer-mode?
-                      (fn []
-                        (let [mode (not developer-mode?)]
-                          (state/set-developer-mode! mode)
-                          (and mode (util/electron?)
-                               (if (js/confirm (t :developer-mode-alert))
-                                 (js/logseq.api.relaunch)))))
-                      true)]]]
-        [:div.text-sm.opacity-50
-         (t :settings-page/developer-mode-desc)]
+        (clear-cache-row t)
+        (version-row t version)
+        (usage-diagnostics-row t instrument-disabled?)
+        (developer-mode-row t developer-mode?)
 
         (when logged?
           [:div

+ 9 - 9
src/main/frontend/components/settings.css

@@ -1,8 +1,8 @@
 .cp__settings {
+  > h1.title {
+    margin-bottom: 0;
+  }
   &-main {
-    > h1.title {
-      margin-bottom: 2rem;
-    }
 
     hr {
       margin: 1rem 0;
@@ -14,7 +14,7 @@
 
       > .it {
         margin-bottom: 0;
-        padding-bottom: 20px;
+        padding-bottom: 12px;
         align-items: center;
 
         label {
@@ -50,6 +50,10 @@
         .form-select, .form-input {
           width: 68%;
         }
+
+        &:first-of-type {
+          padding-top: 14px;
+        }
       }
     }
 
@@ -115,10 +119,6 @@
     margin-bottom: -5px;
 
     button.check-update {
-      position: absolute;
-      right: 0;
-      top: -42px;
-
       &:disabled {
         cursor: progress;
       }
@@ -145,7 +145,7 @@
           margin-right: 2px;
         }
 
-        &:hover {
+        &:hover { 
           text-decoration: underline;
         }
       }

+ 33 - 25
src/main/frontend/components/shortcut.cljs

@@ -2,6 +2,7 @@
   (:require [frontend.context.i18n :as i18n]
             [frontend.modules.shortcut.core :as shortcut]
             [frontend.modules.shortcut.data-helper :as dh]
+            [frontend.components.svg :as svg]
             [frontend.state :as state]
             [frontend.ui :as ui]
             [rum.core :as rum]))
@@ -10,42 +11,49 @@
 (rum/defcs customize-shortcut-dialog-inner <
   (rum/local "")
   (shortcut/record!)
-  [state _]
-  (let [keypress (:rum/local state)]
+  [state _ action-name current-binding]
+  (let [keypress (:rum/local state)
+        keyboard-shortcut (if (= "" @keypress) current-binding @keypress)]
     [:div.w-full.sm:max-w-lg.sm:w-96
-     [:div.sm:flex.sm:items-start
-      [:div.mt-3.text-center.sm:mt-0.sm:text-left
-       [:h3#modal-headline.text-lg.leading-6.font-medium
-        "Press any key and Esc to finish"]
-       [:h4.text-lg.leading-6.font-medium
-        @keypress]]]]))
+     [:div
+      [:p.mb-4 "Press any sequence of keys to set the shortcut for the " [:b action-name] " action."]
+      [:p.mb-4.mt-4
+       (map-indexed (fn [i key]
+                      [:span.keyboard-shortcut {:key i}
+                       [:code {:style {:font-size "1.5em" :margin-right "8px"}}
+                        (clojure.string/replace key "meta" "cmd")]])
+                    (-> keyboard-shortcut
+                        (clojure.string/trim)
+                        (clojure.string/split  #" |\+")))]]
+     [:div.cancel-save-buttons.text-right.mt-4
+      (ui/button "Save" :on-click state/close-modal!)
+      [:a.ml-4
+       {:on-click (fn []
+                    (reset! keypress current-binding)
+                    (state/close-modal!))} "Cancel"]]]))
 
-(defn customize-shortcut-dialog [k]
+(defn customize-shortcut-dialog [k action-name displayed-binding]
   (fn [_]
-    (customize-shortcut-dialog-inner k)))
+    (customize-shortcut-dialog-inner k action-name displayed-binding)))
 
-(rum/defc shortcut-col [k binding configurable?]
+(rum/defc shortcut-col [k binding configurable? action-name]
   (let [conflict?         (dh/potential-confilct? k)
         displayed-binding (dh/binding-for-display k binding)]
-    (if configurable?
+    (if (not configurable?)
+      [:td.text-right displayed-binding]
       [:td.text-right
        (ui/button
         displayed-binding
+        :class "text-sm p-1"
         :title (if conflict?
                  "Shortcut conflict!"
                  "Click to modify")
         :background (when conflict? "pink")
-        :on-click
-        #(state/set-modal! (customize-shortcut-dialog k)))
-       (ui/button "❌"
-                  :title "Reset to default"
-                  :class "transform motion-safe:hover:scale-125"
-                  :background "none"
-                  :on-click
-                  (fn [_]
-                    (dh/remove-shortcut k)
-                    (shortcut/refresh!)))]
-      [:td.text-right displayed-binding])))
+        :on-click #(state/set-modal! (customize-shortcut-dialog k action-name displayed-binding)))
+       [:a.text-sm
+        {:style {:margin-left "12px"}
+         :on-click (fn [] (dh/remove-shortcut k) (shortcut/refresh!))}
+        "Reset"]])))
 
 (rum/defc shortcut-table < rum/reactive
   ([name]
@@ -58,12 +66,12 @@
          [:thead
           [:tr
            [:th.text-left [:b (t name)]]
-           [:th.text-right [:b (t :help/shortcut)]]]]
+           [:th.text-right]]]
          [:tbody
           (map (fn [[k {:keys [binding]}]]
                  [:tr {:key k}
                   [:td.text-left (t (dh/decorate-namespace k))]
-                  (shortcut-col k binding configurable?)])
+                  (shortcut-col k binding configurable? (t (dh/decorate-namespace k)))])
                (dh/binding-by-category name))]]]))))
 
 (rum/defc trigger-table []

+ 6 - 1
src/main/frontend/components/svg.cljs

@@ -549,4 +549,9 @@
             :viewBox "0 0 1024 1024"
             :width   "20"
             :height  "20"} opts)
-    [:path {:d "M512 12.63616c-282.74688 0-512 229.21216-512 512 0 226.22208 146.69824 418.14016 350.12608 485.82656 25.57952 4.73088 35.00032-11.10016 35.00032-24.63744 0-12.20608-0.47104-52.55168-0.69632-95.31392-142.4384 30.96576-172.50304-60.416-172.50304-60.416-23.28576-59.16672-56.85248-74.91584-56.85248-74.91584-46.44864-31.78496 3.50208-31.1296 3.50208-31.1296 51.4048 3.60448 78.47936 52.75648 78.47936 52.75648 45.6704 78.27456 119.76704 55.64416 149.01248 42.55744 4.58752-33.09568 17.85856-55.68512 32.50176-68.46464-113.72544-12.94336-233.2672-56.85248-233.2672-253.0304 0-55.88992 20.00896-101.5808 52.75648-137.4208-5.3248-12.9024-22.85568-64.96256 4.95616-135.49568 0 0 43.008-13.74208 140.84096 52.49024 40.83712-11.34592 84.64384-17.03936 128.16384-17.24416 43.49952 0.2048 87.32672 5.87776 128.24576 17.24416 97.73056-66.2528 140.65664-52.49024 140.65664-52.49024 27.87328 70.53312 10.3424 122.59328 5.03808 135.49568 32.82944 35.86048 52.69504 81.53088 52.69504 137.4208 0 196.64896-119.78752 239.94368-233.79968 252.6208 18.37056 15.89248 34.73408 47.04256 34.73408 94.80192 0 68.5056-0.59392 123.63776-0.59392 140.51328 0 13.6192 9.216 29.5936 35.16416 24.576 203.32544-67.76832 349.83936-259.62496 349.83936-485.76512 0-282.78784-229.23264-512-512-512z"}]]))
+    [:path {:d "M512 12.63616c-282.74688 0-512 229.21216-512 512 0 226.22208 146.69824 418.14016 350.12608 485.82656 25.57952 4.73088 35.00032-11.10016 35.00032-24.63744 0-12.20608-0.47104-52.55168-0.69632-95.31392-142.4384 30.96576-172.50304-60.416-172.50304-60.416-23.28576-59.16672-56.85248-74.91584-56.85248-74.91584-46.44864-31.78496 3.50208-31.1296 3.50208-31.1296 51.4048 3.60448 78.47936 52.75648 78.47936 52.75648 45.6704 78.27456 119.76704 55.64416 149.01248 42.55744 4.58752-33.09568 17.85856-55.68512 32.50176-68.46464-113.72544-12.94336-233.2672-56.85248-233.2672-253.0304 0-55.88992 20.00896-101.5808 52.75648-137.4208-5.3248-12.9024-22.85568-64.96256 4.95616-135.49568 0 0 43.008-13.74208 140.84096 52.49024 40.83712-11.34592 84.64384-17.03936 128.16384-17.24416 43.49952 0.2048 87.32672 5.87776 128.24576 17.24416 97.73056-66.2528 140.65664-52.49024 140.65664-52.49024 27.87328 70.53312 10.3424 122.59328 5.03808 135.49568 32.82944 35.86048 52.69504 81.53088 52.69504 137.4208 0 196.64896-119.78752 239.94368-233.79968 252.6208 18.37056 15.89248 34.73408 47.04256 34.73408 94.80192 0 68.5056-0.59392 123.63776-0.59392 140.51328 0 13.6192 9.216 29.5936 35.16416 24.576 203.32544-67.76832 349.83936-259.62496 349.83936-485.76512 0-282.78784-229.23264-512-512-512z"}]]))
+
+
+(defn info []
+  [:svg {:class "info" :viewbox "0 0 16 16" :width "16px" :height "16px"}
+   [:g [:path {:style {:transform "scale(0.25)"} :d "m32 2c-16.568 0-30 13.432-30 30s13.432 30 30 30 30-13.432 30-30-13.432-30-30-30m5 49.75h-10v-24h10v24m-5-29.5c-2.761 0-5-2.238-5-5s2.239-5 5-5c2.762 0 5 2.238 5 5s-2.238 5-5 5"}]]])

+ 4 - 1
src/main/frontend/components/svg.css

@@ -17,4 +17,7 @@ svg.add-button {
   }
 }
 
-
+svg.info {
+  fill: var(--ls-primary-text-color);
+  transition: fill 200ms ease-in;
+}

+ 6 - 6
src/main/frontend/dicts.cljs

@@ -203,19 +203,19 @@
         :content/open-in-sidebar "Open in sidebar"
         :content/copy-as-json "Copy as JSON"
         :content/click-to-edit "Click to edit"
-        :settings-page/edit-config-edn "Edit config.edn (for current repo)"
+        :settings-page/edit-config-edn "Edit config.edn for current repo"
         :settings-page/show-brackets "Show brackets"
         :settings-page/disable-sentry "Send usage data and diagnostics to Logseq"
         :settings-page/preferred-outdenting "Enable logical outdenting"
-        :settings-page/custom-date-format "Preferred journal format"
+        :settings-page/custom-date-format "Preferred date format"
         :settings-page/preferred-file-format "Preferred file format"
         :settings-page/preferred-workflow "Preferred workflow"
         :settings-page/enable-timetracking "Enable timetracking"
-        :settings-page/enable-tooltip "Enable tooltip"
+        :settings-page/enable-tooltip "Enable tooltips"
         :settings-page/enable-journals "Enable journals"
         :settings-page/enable-encryption "Enable encryption feature"
-        :settings-page/customize-shortcuts "Customize shortcuts"
-        :settings-page/shortcut-settings "Shortcut Settings"
+        :settings-page/customize-shortcuts "Keyboard shortcuts"
+        :settings-page/shortcut-settings "Customize shortcuts"
         :settings-page/home-default-page "Set the default home page"
         :settings-page/enable-block-time "Enable block timestamps"
         :settings-page/dont-use-other-peoples-proxy-servers "Don't use other people's proxy servers. It's very dangerous, which could make your token and notes stolen. Logseq will not be responsible for this loss if you use other people's proxy servers. You can deploy it yourself, check "
@@ -293,7 +293,7 @@
         :user/delete-your-account "Delete your account"
         :user/delete-account-notice "All your published pages on logseq.com will be deleted."
 
-        :help/shortcut-page-title "Learn & Customize Shortcuts"}
+        :help/shortcut-page-title "Keyboard shortcuts"}
 
    :de {:help/about "Über Logseq"
         :help/bug "Fehlerbericht"

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

@@ -1,8 +1,7 @@
 (ns frontend.handler.config
   (:require [frontend.state :as state]
             [frontend.handler.file :as file-handler]
-            [frontend.config :as config]
-            [clojure.string :as string]))
+            [frontend.config :as config]))
 
 (defn set-config!
   [k v]
@@ -14,6 +13,7 @@
     (set-config! :ui/show-brackets? (not show-brackets?))))
 
 (defn toggle-logical-outdenting! []
+  (js/alert "toggle-logical-outdenting!")
   (let [logical-outdenting? (state/logical-outdenting?)]
     (set-config! :editor/logical-outdenting? (not logical-outdenting?))))
 

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

@@ -94,27 +94,28 @@
       (str/lower-case)))
 
 (defn binding-for-display [k binding]
-  (cond
-    (false? binding)
-    (cond
-      (and util/mac? (= k :editor/kill-line-after))
-      "disabled (system default: ctrl+k)"
-      (and util/mac? (= k :editor/beginning-of-block))
-      "disabled (system default: ctrl+a)"
-      (and util/mac? (= k :editor/end-of-block))
-      "disabled (system default: ctrl+e)"
-      (and util/mac? (= k :editor/backward-kill-word))
-      "disabled (system default: opt+delete)"
-      :else
-      "disabled")
-
-    (string? binding)
-    (decorate-binding binding)
-
-    :else
-    (->> binding
-         (map decorate-binding)
-         (str/join " | "))))
+  (let [tmp (cond
+              (false? binding)
+              (cond
+                (and util/mac? (= k :editor/kill-line-after))
+                "disabled (system default: ctrl+k)"
+                (and util/mac? (= k :editor/beginning-of-block))
+                "disabled (system default: ctrl+a)"
+                (and util/mac? (= k :editor/end-of-block))
+                "disabled (system default: ctrl+e)"
+                (and util/mac? (= k :editor/backward-kill-word))
+                "disabled (system default: opt+delete)"
+                :else
+                "disabled")
+
+              (string? binding)
+              (decorate-binding binding)
+
+              :else
+              (->> binding
+                   (map decorate-binding)
+                   (str/join " | ")))]
+    (clojure.string/replace tmp "meta" "cmd")))
 
 
 (defn remove-shortcut [k]

+ 11 - 1
src/main/frontend/ui.cljs

@@ -385,6 +385,16 @@
       {:class       (if on? (if small? "translate-x-4" "translate-x-5") "translate-x-0")
        :aria-hidden "true"}]]]))
 
+(defn keyboard-shortcut [sequence]
+  [:div.keyboard-shortcut
+   (map (fn [key]
+          [:code
+           (if (= :meta key)
+             (util/meta-key-name)
+             (name key))])
+        sequence)]
+  )
+
 (defonce modal-show? (atom false))
 (rum/defc modal-overlay
   [state close-fn]
@@ -624,5 +634,5 @@
                             (if (fn? html)
                               (html)
                               html))
-                          [:div ""])))
+                          [:div {:key "tippy"} ""])))
           child)))

+ 5 - 1
src/main/frontend/ui.css

@@ -85,7 +85,7 @@
 
     .panel-content {
       overflow-y: auto;
-      max-height: 80vh;
+      max-height: 85vh;
       max-width: 768px;
       padding: 2rem;
     }
@@ -134,6 +134,10 @@
       color: var(--ls-link-text-color);
     }
   }
+
+  &.p-1 {
+    padding: 0.25rem 0.5rem !important;
+  }
 }
 
 .dropdown-wrapper {

+ 9 - 0
src/main/frontend/util.cljc

@@ -84,6 +84,10 @@
      [event]
      (gobj/getValueByKeys event "target" "value")))
 
+#?(:cljs
+   (defn ekey [event]
+     (gobj/getValueByKeys event "key")))
+
 #?(:cljs
    (defn set-change-value
      "compatible change event for React"
@@ -1282,3 +1286,8 @@
 
 (comment
   (re-matches (re-pattern (regex-escape "$u^8(d)+w.*[dw]d?")) "$u^8(d)+w.*[dw]d?"))
+
+#?(:cljs
+   (defn meta-key-name []
+     (let [user-agent (.. js/navigator -userAgent)]
+       (if mac? "Cmd" "Ctrl"))))