mirror of
https://github.com/penpot/penpot.git
synced 2026-07-31 18:36:18 +00:00
WIP
This commit is contained in:
parent
392caa4ec1
commit
fac7e457bd
@ -59,7 +59,9 @@
|
||||
[:release-notes-viewed {:optional true}
|
||||
[::sm/text {:max 100}]]
|
||||
[:notifications {:optional true} schema:props-notifications]
|
||||
[:workspace-visited {:optional true} ::sm/boolean]])
|
||||
[:workspace-visited {:optional true} ::sm/boolean]
|
||||
[:custom-shortcuts {:optional true}
|
||||
[:map-of {:gen/max 10} :keyword :string]]])
|
||||
|
||||
(def schema:profile
|
||||
[:map {:title "Profile"}
|
||||
|
||||
@ -121,6 +121,60 @@
|
||||
[sc]
|
||||
(str/split sc #"\+| "))))
|
||||
|
||||
(defn command->tooltip
|
||||
"Converts a Mousetrap command string (e.g. \"command+shift+z\") to
|
||||
a human-readable display string in the same format used by :tooltip
|
||||
fields (e.g. \"⌘⇧Z\" on macOS or \"Ctrl+Shift+Z\" on Windows).
|
||||
Returns nil for empty or unbound commands."
|
||||
[command]
|
||||
(when (and command (not= command ""))
|
||||
(let [is-macos? (cf/check-platform? :macos)
|
||||
parts (str/split command #"\+")
|
||||
display-part (fn [p]
|
||||
(case p
|
||||
"ctrl" (if is-macos? mac-control "Ctrl+")
|
||||
"command" (if is-macos? mac-command "Ctrl+")
|
||||
"alt" (if is-macos? mac-option "Alt+")
|
||||
"shift" (if is-macos? mac-shift "Shift+")
|
||||
"up" up-arrow
|
||||
"down" down-arrow
|
||||
"left" left-arrow
|
||||
"right" right-arrow
|
||||
"del" (if is-macos? mac-delete "Del")
|
||||
"backspace" (if is-macos? mac-delete "Backspace")
|
||||
"escape" (if is-macos? mac-esc "Escape")
|
||||
"enter" (if is-macos? mac-enter "Enter")
|
||||
"space" "Space"
|
||||
"tab" tab
|
||||
(str/upper p)))]
|
||||
(str/join "" (map display-part parts)))))
|
||||
|
||||
(defn apply-custom-overrides
|
||||
[shortcuts custom-overrides]
|
||||
(if (empty? custom-overrides)
|
||||
shortcuts
|
||||
(reduce-kv
|
||||
(fn [acc sc-key new-command]
|
||||
(if (and (contains? acc sc-key)
|
||||
(not (:disabled (get acc sc-key))))
|
||||
(-> acc
|
||||
(assoc-in [sc-key :command] new-command)
|
||||
(update sc-key dissoc :show-command))
|
||||
acc))
|
||||
shortcuts
|
||||
custom-overrides)))
|
||||
|
||||
(defn build-command-index
|
||||
[shortcuts]
|
||||
(reduce-kv
|
||||
(fn [acc sc-key sc-def]
|
||||
(let [commands (:command sc-def)]
|
||||
(if (vector? commands)
|
||||
(reduce #(assoc %1 %2 sc-key) acc commands)
|
||||
(assoc acc commands sc-key))))
|
||||
{}
|
||||
shortcuts))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Events
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -180,8 +234,10 @@
|
||||
(update state :shortcuts conj* [key shortcuts]))
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ _ _]
|
||||
(reset! shortcuts)))))
|
||||
(effect [_ state _]
|
||||
(let [custom-overrides (get-in state [:profile :props :custom-shortcuts])
|
||||
effective (apply-custom-overrides shortcuts custom-overrides)]
|
||||
(reset! effective))))))
|
||||
|
||||
(defn pop-shortcuts
|
||||
[key]
|
||||
@ -193,5 +249,18 @@
|
||||
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(let [[_key shortcuts] (last (:shortcuts state))]
|
||||
(reset! shortcuts)))))
|
||||
(let [[_key shortcuts] (last (:shortcuts state))
|
||||
custom-overrides (get-in state [:profile :props :custom-shortcuts])
|
||||
effective (apply-custom-overrides shortcuts custom-overrides)]
|
||||
(reset! effective)))))
|
||||
|
||||
(defn rebind-shortcuts
|
||||
[]
|
||||
(ptk/reify ::rebind-shortcuts
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(let [[_key shortcuts] (last (:shortcuts state))
|
||||
custom-overrides (get-in state [:profile :props :custom-shortcuts])
|
||||
effective (apply-custom-overrides shortcuts custom-overrides)]
|
||||
(when shortcuts
|
||||
(reset! effective))))))
|
||||
|
||||
@ -634,6 +634,24 @@
|
||||
*assert*
|
||||
(merge debug-shortcuts)))
|
||||
|
||||
(defn get-tooltip [shortcut]
|
||||
(defn get-tooltip
|
||||
"Returns the tooltip string for a shortcut, using any custom binding
|
||||
from the user's profile props if one exists, falling back to the
|
||||
default :tooltip field."
|
||||
[shortcut]
|
||||
(assert (contains? shortcuts shortcut) (str shortcut))
|
||||
(get-in shortcuts [shortcut :tooltip]))
|
||||
(let [custom-shortcuts (get-in @st/state [:profile :props :custom-shortcuts])
|
||||
custom-command (get custom-shortcuts shortcut)]
|
||||
(if (and custom-command (not= custom-command ""))
|
||||
(ds/command->tooltip custom-command)
|
||||
(get-in shortcuts [shortcut :tooltip]))))
|
||||
|
||||
(defn get-effective-tooltip
|
||||
"Returns the tooltip string for a shortcut given an already-resolved
|
||||
custom-shortcuts map. Use this when you already have the custom
|
||||
shortcuts derefed for the current render cycle."
|
||||
[shortcut custom-shortcuts]
|
||||
(let [custom-command (get custom-shortcuts shortcut)]
|
||||
(if (and custom-command (not= custom-command ""))
|
||||
(ds/command->tooltip custom-command)
|
||||
(get-in shortcuts [shortcut :tooltip]))))
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.data.workspace.shortcuts.customize
|
||||
(:require
|
||||
[app.main.data.profile :as du]
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[beicon.v2.core :as rx]
|
||||
[potok.v2.core :as ptk]))
|
||||
|
||||
(defn set-custom-shortcut
|
||||
[shortcut-key new-command conflicting-key]
|
||||
(ptk/reify ::set-custom-shortcut
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [current-customs (get-in state [:profile :props :custom-shortcuts] {})
|
||||
new-customs (-> current-customs
|
||||
(assoc shortcut-key new-command)
|
||||
(cond-> conflicting-key (assoc conflicting-key "")))]
|
||||
(rx/of (du/update-profile-props {:custom-shortcuts new-customs})
|
||||
(ds/rebind-shortcuts))))))
|
||||
|
||||
(defn reset-custom-shortcut
|
||||
[shortcut-key]
|
||||
(ptk/reify ::reset-custom-shortcut
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [current-customs (get-in state [:profile :props :custom-shortcuts] {})
|
||||
new-customs (not-empty (dissoc current-customs shortcut-key))]
|
||||
(rx/of (du/update-profile-props {:custom-shortcuts new-customs})
|
||||
(ds/rebind-shortcuts))))))
|
||||
|
||||
(defn reset-all-custom-shortcuts
|
||||
[]
|
||||
(ptk/reify ::reset-all-custom-shortcuts
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (du/update-profile-props {:custom-shortcuts nil})
|
||||
(ds/rebind-shortcuts)))))
|
||||
@ -33,6 +33,9 @@
|
||||
(def profile
|
||||
(l/derived (l/key :profile) st/state))
|
||||
|
||||
(def custom-shortcuts
|
||||
(l/derived (fn [state] (get-in state [:profile :props :custom-shortcuts])) st/state))
|
||||
|
||||
(def current-page-id
|
||||
(l/derived (l/key :current-page-id) st/state))
|
||||
|
||||
|
||||
@ -226,7 +226,7 @@
|
||||
(mf/defc preferences-menu*
|
||||
{::mf/private true
|
||||
::mf/wrap [mf/memo]}
|
||||
[{:keys [layout profile toggle-flag on-close toggle-theme toggle-render]}]
|
||||
[{:keys [layout profile toggle-flag on-close toggle-theme toggle-render show-shortcuts]}]
|
||||
(let [renderer (or (-> profile :props :renderer) :svg)
|
||||
|
||||
show-nudge-options
|
||||
@ -333,7 +333,18 @@
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(if (= renderer :wasm)
|
||||
(tr "workspace.header.menu.disable-webgl")
|
||||
(tr "workspace.header.menu.enable-webgl"))]])]))
|
||||
(tr "workspace.header.menu.enable-webgl"))]])
|
||||
|
||||
[:> dropdown-menu-item* {:on-click show-shortcuts
|
||||
:class (stl/css :base-menu-item :submenu-item)
|
||||
:on-key-down (fn [event]
|
||||
(when (kbd/enter? event)
|
||||
(show-shortcuts event)))
|
||||
:data-testid "change-shortcuts"
|
||||
:id "file-menu-change-shortcuts"}
|
||||
[:span {:class (stl/css :item-name)}
|
||||
(tr "workspace.header.menu.change-shortcuts")]
|
||||
[:> shortcuts* {:id :show-shortcuts}]]]))
|
||||
|
||||
(mf/defc view-menu*
|
||||
{::mf/private true
|
||||
@ -967,6 +978,19 @@
|
||||
::ev/origin "workspace:menu"})
|
||||
(modal/show :plugin-management {}))))
|
||||
|
||||
show-shortcuts
|
||||
(mf/use-fn
|
||||
(mf/deps layout)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(reset! show-menu* false)
|
||||
(reset! selected-sub-menu* nil)
|
||||
(when (contains? layout :collapse-left-sidebar)
|
||||
(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))
|
||||
(st/emit!
|
||||
(-> (dw/toggle-layout-flag :shortcuts)
|
||||
(vary-meta assoc ::ev/origin "workspace-menu")))))
|
||||
|
||||
subscription (:subscription (:props profile))
|
||||
subscription-type (get-subscription-type subscription)]
|
||||
|
||||
@ -1133,6 +1157,7 @@
|
||||
:toggle-flag toggle-flag
|
||||
:toggle-theme toggle-theme
|
||||
:toggle-render toggle-render
|
||||
:show-shortcuts show-shortcuts
|
||||
:on-close close-sub-menu}]
|
||||
|
||||
:plugins
|
||||
|
||||
@ -51,9 +51,12 @@
|
||||
|
||||
(mf/defc palette*
|
||||
[{:keys [layout on-change-size]}]
|
||||
(let [color-palette? (:colorpalette layout)
|
||||
text-palette? (:textpalette layout)
|
||||
hide-palettes? (:hide-palettes layout)
|
||||
(let [color-palette? (:colorpalette layout)
|
||||
text-palette? (:textpalette layout)
|
||||
hide-palettes? (:hide-palettes layout)
|
||||
|
||||
custom-shortcuts (mf/deref refs/custom-shortcuts)
|
||||
get-tt #(sc/get-effective-tooltip % custom-shortcuts)
|
||||
|
||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
container (mf/use-ref nil)
|
||||
@ -179,16 +182,16 @@
|
||||
[:ul {:class (dm/str size-classname " " (stl/css-case :palette-btn-list true
|
||||
:hidden-bts hide-palettes?))}
|
||||
[:li {:class (stl/css :palette-item)}
|
||||
[:button {:title (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-colorpalette))
|
||||
:aria-label (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-colorpalette))
|
||||
[:button {:title (tr "workspace.toolbar.color-palette" (get-tt :toggle-colorpalette))
|
||||
:aria-label (tr "workspace.toolbar.color-palette" (get-tt :toggle-colorpalette))
|
||||
:class (stl/css-case :palette-btn true
|
||||
:selected color-palette?)
|
||||
:on-click on-select-color-palette}
|
||||
deprecated-icon/drop-icon]]
|
||||
|
||||
[:li {:class (stl/css :palette-item)}
|
||||
[:button {:title (tr "workspace.toolbar.text-palette" (sc/get-tooltip :toggle-textpalette))
|
||||
:aria-label (tr "workspace.toolbar.text-palette" (sc/get-tooltip :toggle-textpalette))
|
||||
[:button {:title (tr "workspace.toolbar.text-palette" (get-tt :toggle-textpalette))
|
||||
:aria-label (tr "workspace.toolbar.text-palette" (get-tt :toggle-textpalette))
|
||||
:class (stl/css-case :palette-btn true
|
||||
:selected text-palette?)
|
||||
:on-click on-select-text-palette}
|
||||
|
||||
@ -40,8 +40,10 @@
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/wrap-props false}
|
||||
[{:keys [zoom on-increase on-decrease on-zoom-reset on-zoom-fit on-zoom-selected]}]
|
||||
(let [open* (mf/use-state false)
|
||||
open? (deref open*)
|
||||
(let [open* (mf/use-state false)
|
||||
open? (deref open*)
|
||||
custom-shortcuts (mf/deref refs/custom-shortcuts)
|
||||
get-tt #(sc/get-effective-tooltip % custom-shortcuts)
|
||||
|
||||
open-dropdown
|
||||
(mf/use-fn
|
||||
@ -97,14 +99,14 @@
|
||||
:on-click on-zoom-fit}
|
||||
(tr "workspace.header.zoom-fit-all")
|
||||
[:span {:class (stl/css :shortcuts)}
|
||||
(for [sc (scd/split-sc (sc/get-tooltip :fit-all))]
|
||||
(for [sc (scd/split-sc (get-tt :fit-all))]
|
||||
[:span {:class (stl/css :shortcut-key)
|
||||
:key (str "zoom-fit-" sc)} sc])]]
|
||||
[:li {:class (stl/css :zoom-option)
|
||||
:on-click on-zoom-selected}
|
||||
(tr "workspace.header.zoom-selected")
|
||||
[:span {:class (stl/css :shortcuts)}
|
||||
(for [sc (scd/split-sc (sc/get-tooltip :zoom-selected))]
|
||||
(for [sc (scd/split-sc (get-tt :zoom-selected))]
|
||||
[:span {:class (stl/css :shortcut-key)
|
||||
:key (str "zoom-selected-" sc)} sc])]]]]]))
|
||||
|
||||
@ -118,6 +120,9 @@
|
||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
selected-drawtool (mf/deref refs/selected-drawing-tool)
|
||||
|
||||
custom-shortcuts (mf/deref refs/custom-shortcuts)
|
||||
get-tt #(sc/get-effective-tooltip % custom-shortcuts)
|
||||
|
||||
on-increase (mf/use-fn #(st/emit! (dw/increase-zoom nil)))
|
||||
on-decrease (mf/use-fn #(st/emit! (dw/decrease-zoom nil)))
|
||||
on-zoom-reset (mf/use-fn #(st/emit! dw/reset-zoom))
|
||||
@ -211,8 +216,8 @@
|
||||
:on-zoom-selected on-zoom-selected}]]
|
||||
|
||||
[:div {:class (stl/css :comments-section)}
|
||||
[:button {:title (tr "workspace.toolbar.comments" (sc/get-tooltip :add-comment))
|
||||
:aria-label (tr "workspace.toolbar.comments" (sc/get-tooltip :add-comment))
|
||||
[:button {:title (tr "workspace.toolbar.comments" (get-tt :add-comment))
|
||||
:aria-label (tr "workspace.toolbar.comments" (get-tt :add-comment))
|
||||
:class (stl/css-case :comments-btn true
|
||||
:selected (= selected-drawtool :comments))
|
||||
:on-click toggle-comments
|
||||
@ -239,7 +244,7 @@
|
||||
deprecated-icon/share])
|
||||
|
||||
[:a {:class (stl/css :viewer-btn)
|
||||
:title (tr "workspace.header.viewer" (sc/get-tooltip :open-viewer))
|
||||
:title (tr "workspace.header.viewer" (get-tt :open-viewer))
|
||||
:on-click nav-to-viewer}
|
||||
deprecated-icon/play]]))
|
||||
|
||||
|
||||
@ -11,15 +11,20 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.config :as cf]
|
||||
[app.main.data.dashboard.shortcuts]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[app.main.data.viewer.shortcuts]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.path.shortcuts]
|
||||
[app.main.data.workspace.shortcuts]
|
||||
[app.main.data.workspace.shortcuts.customize :as customize]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.search-bar :refer [search-bar*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i :refer [icon*]]
|
||||
[app.main.ui.ds.product.panel-title :refer [panel-title*]]
|
||||
[app.main.ui.workspace.sidebar.shortcuts.edit-modal]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.strings :refer [matches-search]]
|
||||
@ -262,7 +267,8 @@
|
||||
:command command}])])]))
|
||||
|
||||
(mf/defc shortcut-row*
|
||||
[{:keys [elements filter-term is-match-section is-match-subsection]}]
|
||||
[{:keys [elements filter-term is-match-section is-match-subsection
|
||||
editable? custom-shortcuts on-edit on-reset]}]
|
||||
(let [shortcut-name (keys elements)
|
||||
shortcut-translations (map #(translation-keyname :sc %) shortcut-name)
|
||||
match-shortcut? (some #(matches-search % @filter-term) shortcut-translations)
|
||||
@ -275,13 +281,33 @@
|
||||
(for [command-translate sorted-filtered]
|
||||
(let [sc-by-translate (first (filter #(= (:translation (second %)) command-translate) elements))
|
||||
[command comand-info] sc-by-translate
|
||||
content (or (:show-command comand-info) (:command comand-info))]
|
||||
[:li {:class (stl/css :shortcuts-name)
|
||||
content (or (:show-command comand-info) (:command comand-info))
|
||||
customized? (and editable? (contains? custom-shortcuts command))]
|
||||
[:li {:class (stl/css-case :shortcuts-name true
|
||||
:customized customized?)
|
||||
:key command-translate}
|
||||
[:span {:class (stl/css :command-name)}
|
||||
command-translate]
|
||||
[:> shortcuts-keys* {:content content
|
||||
:command command}]]))]))
|
||||
[:div {:class (stl/css :shortcut-actions)}
|
||||
[:> shortcuts-keys* {:content content
|
||||
:command command}]
|
||||
(when editable?
|
||||
[:div {:class (stl/css :edit-buttons)}
|
||||
(when customized?
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "shortcuts.reset")
|
||||
:on-click (fn [e]
|
||||
(dom/stop-propagation e)
|
||||
(on-reset command))
|
||||
:icon i/reload
|
||||
:icon-size "s"}])
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "shortcuts.edit")
|
||||
:on-click (fn [e]
|
||||
(dom/stop-propagation e)
|
||||
(on-edit command))
|
||||
:icon i/curve
|
||||
:icon-size "s"}]])]]))]))
|
||||
|
||||
(mf/defc section-title*
|
||||
[{:keys [name is-visible is-sub]}]
|
||||
@ -295,7 +321,8 @@
|
||||
(stl/css :section-name))} name]])
|
||||
|
||||
(mf/defc shortcut-subsection*
|
||||
[{:keys [subsections manage-sections filter-term is-match-section open-sections]}]
|
||||
[{:keys [subsections manage-sections filter-term is-match-section open-sections
|
||||
editable? custom-shortcuts on-edit on-reset]}]
|
||||
(let [subsections-names (keys subsections)
|
||||
subsection-translations (if (= :none (first subsections-names))
|
||||
(map #(translation-keyname :sc %) subsections-names)
|
||||
@ -307,7 +334,11 @@
|
||||
[:> shortcut-row* {:elements (:children basic-shortcuts)
|
||||
:filter-term filter-term
|
||||
:is-match-section is-match-section
|
||||
:is-match-subsection true}])
|
||||
:is-match-subsection true
|
||||
:editable? editable?
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:on-edit on-edit
|
||||
:on-reset on-reset}])
|
||||
|
||||
[:ul {:class (stl/css :subsection-menu)}
|
||||
(for [sub-translated sorted-translations]
|
||||
@ -328,10 +359,15 @@
|
||||
[:> shortcut-row* {:elements (:children sub-info)
|
||||
:filter-term filter-term
|
||||
:is-match-section is-match-section
|
||||
:is-match-subsection match-subsection?}]]])))])))
|
||||
:is-match-subsection match-subsection?
|
||||
:editable? editable?
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:on-edit on-edit
|
||||
:on-reset on-reset}]]])))])))
|
||||
|
||||
(mf/defc shortcut-section*
|
||||
[{:keys [section manage-sections open-sections filter-term]}]
|
||||
[{:keys [section manage-sections open-sections filter-term
|
||||
editable? custom-shortcuts on-edit on-reset]}]
|
||||
(let [[section-key section-info] section
|
||||
section-id (:id section-info)
|
||||
section-translation (translation-keyname :sec section-key)
|
||||
@ -359,13 +395,24 @@
|
||||
:open-sections open-sections
|
||||
:manage-sections manage-sections
|
||||
:is-match-section match-section?
|
||||
:filter-term filter-term}]]])))
|
||||
:filter-term filter-term
|
||||
:editable? editable?
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:on-edit on-edit
|
||||
:on-reset on-reset}]]])))
|
||||
|
||||
(def ^:private workspace-shortcuts-raw
|
||||
(d/deep-merge app.main.data.workspace.path.shortcuts/shortcuts
|
||||
app.main.data.workspace.shortcuts/shortcuts))
|
||||
|
||||
(mf/defc shortcuts-container*
|
||||
[{:keys [class]}]
|
||||
(let [workspace-shortcuts app.main.data.workspace.shortcuts/shortcuts
|
||||
path-shortcuts app.main.data.workspace.path.shortcuts/shortcuts
|
||||
all-workspace-shortcuts (->> (d/deep-merge path-shortcuts workspace-shortcuts)
|
||||
(let [profile (mf/deref refs/profile)
|
||||
custom-shortcuts (get-in profile [:props :custom-shortcuts])
|
||||
|
||||
workspace-shortcuts-custom (ds/apply-custom-overrides workspace-shortcuts-raw custom-shortcuts)
|
||||
|
||||
all-workspace-shortcuts (->> workspace-shortcuts-custom
|
||||
(add-translation :sc)
|
||||
(into {}))
|
||||
|
||||
@ -484,7 +531,30 @@
|
||||
(mf/use-callback
|
||||
(fn [_]
|
||||
(reset! open-sections [[1]])
|
||||
(reset! filter-term "")))]
|
||||
(reset! filter-term "")))
|
||||
|
||||
on-edit-shortcut
|
||||
(mf/use-callback
|
||||
(mf/deps custom-shortcuts workspace-shortcuts-custom)
|
||||
(fn [shortcut-key]
|
||||
(let [default-command (:command (get workspace-shortcuts-raw shortcut-key))
|
||||
current-command (or (get custom-shortcuts shortcut-key) default-command)]
|
||||
(st/emit! (modal/show :shortcut-edit
|
||||
{:shortcut-key shortcut-key
|
||||
:shortcut-name (translation-keyname :sc shortcut-key)
|
||||
:current-command current-command
|
||||
:default-command default-command
|
||||
:all-shortcuts workspace-shortcuts-custom})))))
|
||||
|
||||
on-reset-shortcut
|
||||
(mf/use-callback
|
||||
(fn [shortcut-key]
|
||||
(st/emit! (customize/reset-custom-shortcut shortcut-key))))
|
||||
|
||||
on-reset-all
|
||||
(mf/use-callback
|
||||
(fn [_]
|
||||
(st/emit! (customize/reset-all-custom-shortcuts))))]
|
||||
|
||||
[:div {:class (dm/str class " " (stl/css :shortcuts))}
|
||||
[:> panel-title* {:class (stl/css :shortcuts-title)
|
||||
@ -497,14 +567,26 @@
|
||||
:value @filter-term
|
||||
:placeholder (tr "shortcuts.title")
|
||||
:icon-id i/search
|
||||
:auto-focus true}]]
|
||||
:auto-focus true}]
|
||||
(when (seq custom-shortcuts)
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "shortcuts.reset-all")
|
||||
:on-click on-reset-all
|
||||
:icon i/reload
|
||||
:icon-size "s"}])]
|
||||
|
||||
(if match-any?
|
||||
[:div {:class (stl/css :shortcuts-list)}
|
||||
(for [section all-shortcuts]
|
||||
[:> shortcut-section* {:key (->> section second :id first)
|
||||
:section section
|
||||
:manage-sections manage-sections
|
||||
:open-sections open-sections
|
||||
:filter-term filter-term}])]
|
||||
(let [[section-key _] section
|
||||
ws-editable? (contains? #{:basics :workspace} section-key)]
|
||||
[:> shortcut-section* {:key (->> section second :id first)
|
||||
:section section
|
||||
:manage-sections manage-sections
|
||||
:open-sections open-sections
|
||||
:filter-term filter-term
|
||||
:editable? ws-editable?
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:on-edit on-edit-shortcut
|
||||
:on-reset on-reset-shortcut}]))]
|
||||
[:div {:class (stl/css :not-found)} (tr "shortcuts.not-found")])]))
|
||||
|
||||
@ -16,7 +16,16 @@
|
||||
}
|
||||
|
||||
.search-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: deprecated.$s-4;
|
||||
margin: deprecated.$s-16 deprecated.$s-12 deprecated.$s-4 deprecated.$s-12;
|
||||
|
||||
// The search bar should fill available space; the reset-all button sits at the end
|
||||
& > :first-child {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.shortcuts-title {
|
||||
@ -91,6 +100,33 @@
|
||||
|
||||
margin-left: deprecated.$s-2;
|
||||
color: var(--pill-foreground-color);
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.shortcut-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: deprecated.$s-2;
|
||||
}
|
||||
|
||||
.edit-buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
&:hover .edit-buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.customized {
|
||||
background-color: var(--pill-background-color-hover, var(--pill-background-color));
|
||||
|
||||
.edit-buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.keys {
|
||||
|
||||
@ -0,0 +1,190 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.workspace.sidebar.shortcuts.edit-modal
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.config :as cf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.shortcuts :as ds]
|
||||
[app.main.data.workspace.shortcuts.customize :as customize]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.ds.notifications.context-notification :refer [context-notification*]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as events]
|
||||
[rumext.v2 :as mf])
|
||||
(:import
|
||||
goog.events.EventType))
|
||||
|
||||
(def ^:private modifier-keys
|
||||
#{"Control" "Shift" "Alt" "Meta"})
|
||||
|
||||
(def ^:private key-name-map
|
||||
{"ArrowUp" "up"
|
||||
"ArrowDown" "down"
|
||||
"ArrowLeft" "left"
|
||||
"ArrowRight" "right"
|
||||
"Escape" "escape"
|
||||
"Enter" "enter"
|
||||
"Backspace" "backspace"
|
||||
"Delete" "del"
|
||||
"Tab" "tab"
|
||||
" " "space"})
|
||||
|
||||
(defn- shortcut-translation
|
||||
[keyname]
|
||||
(tr (str "shortcuts." (d/name keyname))))
|
||||
|
||||
(defn- keyboard-event->mousetrap
|
||||
[^js event]
|
||||
(let [parts (cond-> []
|
||||
(and (.-ctrlKey event)
|
||||
(not (cf/check-platform? :macos)))
|
||||
(conj "ctrl")
|
||||
|
||||
(and (.-metaKey event)
|
||||
(cf/check-platform? :macos))
|
||||
(conj "command")
|
||||
|
||||
(.-altKey event)
|
||||
(conj "alt")
|
||||
|
||||
(.-shiftKey event)
|
||||
(conj "shift"))
|
||||
key (.-key event)
|
||||
key (if (contains? modifier-keys key)
|
||||
nil
|
||||
(or (get key-name-map key)
|
||||
(.toLowerCase key)))]
|
||||
(when key
|
||||
(str/join "+" (conj parts key)))))
|
||||
|
||||
(defn- keyboard-event->display-parts
|
||||
[^js event]
|
||||
(let [parts (cond-> []
|
||||
(and (.-ctrlKey event)
|
||||
(not (cf/check-platform? :macos)))
|
||||
(conj "ctrl")
|
||||
|
||||
(and (.-metaKey event)
|
||||
(cf/check-platform? :macos))
|
||||
(conj "command")
|
||||
|
||||
(.-altKey event)
|
||||
(conj "alt")
|
||||
|
||||
(.-shiftKey event)
|
||||
(conj "shift"))
|
||||
key (.-key event)]
|
||||
(if (contains? modifier-keys key)
|
||||
{:modifiers parts :finalized? false}
|
||||
{:modifiers parts
|
||||
:final-key (or (get key-name-map key) (.toLowerCase key))
|
||||
:finalized? true})))
|
||||
|
||||
(defn- find-conflict
|
||||
[new-command all-shortcuts current-key]
|
||||
(let [command-index (ds/build-command-index all-shortcuts)]
|
||||
(when-let [conflicting-key (get command-index new-command)]
|
||||
(when (not= conflicting-key current-key)
|
||||
{:key conflicting-key
|
||||
:name (shortcut-translation conflicting-key)}))))
|
||||
|
||||
(mf/defc shortcut-edit-dialog
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :shortcut-edit}
|
||||
[{:keys [shortcut-key shortcut-name current-command default-command all-shortcuts]}]
|
||||
(let [recorded-command (mf/use-state nil)
|
||||
display-parts (mf/use-state nil)
|
||||
conflict (mf/use-state nil)
|
||||
|
||||
has-recording? (some? @recorded-command)
|
||||
|
||||
cancel-fn
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (modal/hide))))
|
||||
|
||||
save-fn
|
||||
(mf/use-callback
|
||||
(mf/deps @recorded-command @conflict shortcut-key)
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(when @recorded-command
|
||||
(st/emit! (modal/hide)
|
||||
(customize/set-custom-shortcut
|
||||
shortcut-key
|
||||
@recorded-command
|
||||
(:key @conflict))))))]
|
||||
|
||||
(mf/with-effect []
|
||||
(letfn [(on-keydown [^js event]
|
||||
(.preventDefault event)
|
||||
(.stopPropagation event)
|
||||
(let [command (keyboard-event->mousetrap event)
|
||||
parts (keyboard-event->display-parts event)]
|
||||
(reset! display-parts parts)
|
||||
(when command
|
||||
(reset! recorded-command command)
|
||||
(reset! conflict (find-conflict command all-shortcuts shortcut-key)))))]
|
||||
(->> (events/listen js/document EventType.KEYDOWN on-keydown)
|
||||
(partial events/unlistenByKey))))
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-container)}
|
||||
[:div {:class (stl/css :modal-header)}
|
||||
[:h2 {:class (stl/css :modal-title)} (tr "shortcuts.edit-modal.title")]
|
||||
[:div {:class (stl/css :modal-close-btn)}
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:aria-label (tr "labels.close")
|
||||
:on-click cancel-fn
|
||||
:icon i/close}]]]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div {:class (stl/css :shortcut-info)}
|
||||
[:span {:class (stl/css :shortcut-label)} shortcut-name]
|
||||
[:div {:class (stl/css :current-keys)}
|
||||
[:span {:class (stl/css :current-label)} (tr "shortcuts.edit-modal.current")]
|
||||
[:span {:class (stl/css :current-value)}
|
||||
(if (vector? current-command)
|
||||
(str/join " / " current-command)
|
||||
current-command)]]]
|
||||
|
||||
[:div {:class (stl/css :recording-area)}
|
||||
(if (nil? @display-parts)
|
||||
[:span {:class (stl/css :recording-hint)}
|
||||
(tr "shortcuts.edit-modal.press-keys")]
|
||||
|
||||
[:div {:class (stl/css :recorded-keys)}
|
||||
(for [mod (:modifiers @display-parts)]
|
||||
[:span {:class (stl/css :key) :key mod} mod])
|
||||
(when (:final-key @display-parts)
|
||||
[:span {:class (stl/css :key)} (:final-key @display-parts)])
|
||||
(when-not (:finalized? @display-parts)
|
||||
[:span {:class (stl/css :recording-ellipsis)}
|
||||
(tr "shortcuts.edit-modal.recording")])])]
|
||||
|
||||
(when @conflict
|
||||
[:> context-notification* {:level :warning
|
||||
:appearance :ghost}
|
||||
(tr "shortcuts.edit-modal.conflict" (:name @conflict))])]
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
[:> button* {:variant "secondary"
|
||||
:on-click cancel-fn}
|
||||
(tr "shortcuts.edit-modal.cancel")]
|
||||
[:> button* {:variant "primary"
|
||||
:disabled (not has-recording?)
|
||||
:on-click save-fn}
|
||||
(tr "shortcuts.edit-modal.save")]]]]]))
|
||||
@ -0,0 +1,125 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
|
||||
.modal-overlay {
|
||||
@extend %modal-overlay-base;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
@extend %modal-container-base;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-xl);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include deprecated.headline-medium-typography;
|
||||
|
||||
color: var(--modal-title-foreground-color);
|
||||
}
|
||||
|
||||
.modal-close-btn {
|
||||
position: absolute;
|
||||
top: var(--sp-m);
|
||||
right: var(--sp-m);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-l);
|
||||
}
|
||||
|
||||
.shortcut-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.shortcut-label {
|
||||
@include deprecated.body-large-typography;
|
||||
|
||||
color: var(--modal-title-foreground-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.current-keys {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.current-label {
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.current-value {
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.recording-area {
|
||||
@include deprecated.flex-center;
|
||||
|
||||
min-height: deprecated.$s-60;
|
||||
padding: var(--sp-l);
|
||||
border-radius: deprecated.$br-8;
|
||||
border: 2px dashed var(--color-foreground-secondary);
|
||||
background-color: var(--pill-background-color);
|
||||
}
|
||||
|
||||
.recording-hint {
|
||||
@include deprecated.body-large-typography;
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.recorded-keys {
|
||||
@include deprecated.flex-center;
|
||||
|
||||
gap: deprecated.$s-4;
|
||||
}
|
||||
|
||||
.key {
|
||||
@include deprecated.body-small-typography;
|
||||
@include deprecated.flex-center;
|
||||
|
||||
text-transform: capitalize;
|
||||
height: deprecated.$s-28;
|
||||
padding: deprecated.$s-4 deprecated.$s-8;
|
||||
border-radius: deprecated.$s-6;
|
||||
background-color: var(--menu-shortcut-background-color);
|
||||
color: var(--pill-foreground-color);
|
||||
}
|
||||
|
||||
.recording-ellipsis {
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
margin-left: var(--sp-s);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
@extend %modal-action-btns;
|
||||
}
|
||||
@ -87,8 +87,10 @@
|
||||
(mf/defc image-upload*
|
||||
{::mf/wrap [mf/memo]}
|
||||
[]
|
||||
(let [ref (mf/use-ref nil)
|
||||
file-id (mf/use-ctx ctx/current-file-id)
|
||||
(let [ref (mf/use-ref nil)
|
||||
file-id (mf/use-ctx ctx/current-file-id)
|
||||
custom-shortcuts (mf/deref refs/custom-shortcuts)
|
||||
get-tt #(sc/get-effective-tooltip % custom-shortcuts)
|
||||
|
||||
on-click
|
||||
(mf/use-fn
|
||||
@ -111,8 +113,8 @@
|
||||
(st/emit! (dwm/upload-media-workspace params)))))]
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.image" (sc/get-tooltip :insert-image))
|
||||
:aria-label (tr "workspace.toolbar.image" (sc/get-tooltip :insert-image))
|
||||
{:title (tr "workspace.toolbar.image" (get-tt :insert-image))
|
||||
:aria-label (tr "workspace.toolbar.image" (get-tt :insert-image))
|
||||
:on-click on-click
|
||||
:class (stl/css :main-toolbar-options-button)}
|
||||
deprecated-icon/img
|
||||
@ -142,8 +144,9 @@
|
||||
(let [drawtool (mf/deref refs/selected-drawing-tool)
|
||||
edition (mf/deref refs/selected-edition)
|
||||
|
||||
profile (mf/deref refs/profile)
|
||||
props (get profile :props)
|
||||
profile (mf/deref refs/profile)
|
||||
props (get profile :props)
|
||||
custom-shortcuts (mf/deref refs/custom-shortcuts)
|
||||
|
||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
rulers? (mf/deref refs/rulers?)
|
||||
@ -181,10 +184,12 @@
|
||||
(dom/blur! (dom/get-target event))
|
||||
(st/emit! (dwc/toggle-toolbar-visibility))))
|
||||
|
||||
get-tt #(sc/get-effective-tooltip % custom-shortcuts)
|
||||
|
||||
test-tooltip-board-text
|
||||
(if (not (:workspace-visited props))
|
||||
(tr "workspace.toolbar.frame-first-time" (sc/get-tooltip :draw-frame))
|
||||
(tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)))]
|
||||
(tr "workspace.toolbar.frame-first-time" (get-tt :draw-frame))
|
||||
(tr "workspace.toolbar.frame" (get-tt :draw-frame)))]
|
||||
|
||||
(when-not ^boolean read-only?
|
||||
[:aside {:class (stl/css-case :main-toolbar true
|
||||
@ -194,8 +199,8 @@
|
||||
:data-testid "toolbar-options"}
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.move" (sc/get-tooltip :move))
|
||||
:aria-label (tr "workspace.toolbar.move" (sc/get-tooltip :move))
|
||||
{:title (tr "workspace.toolbar.move" (get-tt :move))
|
||||
:aria-label (tr "workspace.toolbar.move" (get-tt :move))
|
||||
:class (stl/css-case :main-toolbar-options-button true
|
||||
:selected (and (nil? drawtool)
|
||||
(not edition)))
|
||||
@ -205,7 +210,7 @@
|
||||
[:li
|
||||
[:button
|
||||
{:title test-tooltip-board-text
|
||||
:aria-label (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame))
|
||||
:aria-label (tr "workspace.toolbar.frame" (get-tt :draw-frame))
|
||||
:class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :frame))
|
||||
:on-click select-drawtool
|
||||
:data-tool "frame"
|
||||
@ -213,8 +218,8 @@
|
||||
deprecated-icon/board]]
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect))
|
||||
:aria-label (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect))
|
||||
{:title (tr "workspace.toolbar.rect" (get-tt :draw-rect))
|
||||
:aria-label (tr "workspace.toolbar.rect" (get-tt :draw-rect))
|
||||
:class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :rect))
|
||||
:on-click select-drawtool
|
||||
:data-tool "rect"
|
||||
@ -222,8 +227,8 @@
|
||||
deprecated-icon/rectangle]]
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse))
|
||||
:aria-label (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse))
|
||||
{:title (tr "workspace.toolbar.ellipse" (get-tt :draw-ellipse))
|
||||
:aria-label (tr "workspace.toolbar.ellipse" (get-tt :draw-ellipse))
|
||||
:class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :circle))
|
||||
:on-click select-drawtool
|
||||
:data-tool "circle"
|
||||
@ -231,8 +236,8 @@
|
||||
deprecated-icon/ellipse]]
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text))
|
||||
:aria-label (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text))
|
||||
{:title (tr "workspace.toolbar.text" (get-tt :draw-text))
|
||||
:aria-label (tr "workspace.toolbar.text" (get-tt :draw-text))
|
||||
:class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :text))
|
||||
:on-click select-drawtool
|
||||
:data-tool "text"}
|
||||
@ -242,8 +247,8 @@
|
||||
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve))
|
||||
:aria-label (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve))
|
||||
{:title (tr "workspace.toolbar.curve" (get-tt :draw-curve))
|
||||
:aria-label (tr "workspace.toolbar.curve" (get-tt :draw-curve))
|
||||
:class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :curve))
|
||||
:on-click select-drawtool
|
||||
:data-tool "curve"
|
||||
@ -251,8 +256,8 @@
|
||||
deprecated-icon/curve]]
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path))
|
||||
:aria-label (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path))
|
||||
{:title (tr "workspace.toolbar.path" (get-tt :draw-path))
|
||||
:aria-label (tr "workspace.toolbar.path" (get-tt :draw-path))
|
||||
:class (stl/css-case :main-toolbar-options-button true :selected (= drawtool :path))
|
||||
:on-click select-drawtool
|
||||
:data-tool "path"
|
||||
@ -262,8 +267,8 @@
|
||||
(when (features/active-feature? @st/state "plugins/runtime")
|
||||
[:li
|
||||
[:button
|
||||
{:title (tr "workspace.toolbar.plugins" (sc/get-tooltip :plugins))
|
||||
:aria-label (tr "workspace.toolbar.plugins" (sc/get-tooltip :plugins))
|
||||
{:title (tr "workspace.toolbar.plugins" (get-tt :plugins))
|
||||
:aria-label (tr "workspace.toolbar.plugins" (get-tt :plugins))
|
||||
:class (stl/css :main-toolbar-options-button)
|
||||
:on-click #(st/emit!
|
||||
(ev/event {::ev/name "open-plugins-manager"
|
||||
|
||||
@ -5565,7 +5565,37 @@ msgstr "Set thumbnails"
|
||||
msgid "shortcuts.title"
|
||||
msgstr "Keyboard shortcuts"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:185
|
||||
msgid "shortcuts.edit"
|
||||
msgstr "Edit shortcut"
|
||||
|
||||
msgid "shortcuts.reset"
|
||||
msgstr "Reset to default"
|
||||
|
||||
msgid "shortcuts.reset-all"
|
||||
msgstr "Reset all shortcuts"
|
||||
|
||||
msgid "shortcuts.edit-modal.title"
|
||||
msgstr "Edit shortcut"
|
||||
|
||||
msgid "shortcuts.edit-modal.current"
|
||||
msgstr "Current shortcut"
|
||||
|
||||
msgid "shortcuts.edit-modal.press-keys"
|
||||
msgstr "Press the desired key combination"
|
||||
|
||||
msgid "shortcuts.edit-modal.recording"
|
||||
msgstr "Recording..."
|
||||
|
||||
msgid "shortcuts.edit-modal.conflict"
|
||||
msgstr "This shortcut is already assigned to \"%s\". The old shortcut will be unbound."
|
||||
|
||||
msgid "shortcuts.edit-modal.save"
|
||||
msgstr "Save"
|
||||
|
||||
msgid "shortcuts.edit-modal.cancel"
|
||||
msgstr "Cancel"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182
|
||||
msgid "shortcuts.toggle-alignment"
|
||||
msgstr "Toggle dynamic alignment"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user