From fe85a5717e28e7c6f9362da10725a3ec885b673f Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Thu, 30 Jul 2026 16:56:10 +0200 Subject: [PATCH] :recycle: Fixes on shortcuts (#10906) * :tada: Improve reset to default flow * :tada: Add diff modal * :bug: Fix measurement shortcuts * :tada: Separate open-sections for each tab * :bug: Align button link icon * :tada: Reset only works after press save * :bug: Fix little things * :tada: Make the import export row to be fixed * :recycle: Fix CI * :bug: Cancel shortcut is not appearing on disabled tab * :bug: Fix tests * :bug: Fix loop on personalized * :bug: Fix show measurements shortcut --- frontend/playwright/ui/pages/ShortcutsPage.js | 7 + .../playwright/ui/specs/shortcuts.spec.js | 36 +++- frontend/src/app/main/data/shortcuts.cljs | 11 +- .../data/workspace/grid_layout/shortcuts.cljs | 10 +- .../main/data/workspace/path/shortcuts.cljs | 26 +++ .../app/main/data/workspace/shortcuts.cljs | 25 ++- .../app/main/ui/components/button_link.scss | 10 +- .../settings/import_shortcuts_diff_modal.cljs | 161 +++++++++++++++++ .../settings/import_shortcuts_diff_modal.scss | 167 ++++++++++++++++++ .../ui/settings/restore_shortcuts_modal.cljs | 16 +- .../ui/settings/restore_shortcuts_modal.scss | 7 + .../src/app/main/ui/settings/shortcuts.cljs | 154 +++++++++++----- .../src/app/main/ui/settings/shortcuts.scss | 32 ++-- frontend/src/app/main/ui/shortcuts.cljs | 52 ++++-- frontend/src/app/main/ui/shortcuts.scss | 5 + .../ui/settings_shortcuts_test.cljs | 43 +++++ frontend/translations/en.po | 28 ++- frontend/translations/es.po | 28 ++- 18 files changed, 695 insertions(+), 123 deletions(-) create mode 100644 frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.cljs create mode 100644 frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.scss diff --git a/frontend/playwright/ui/pages/ShortcutsPage.js b/frontend/playwright/ui/pages/ShortcutsPage.js index d434f9fb5b..578311570e 100644 --- a/frontend/playwright/ui/pages/ShortcutsPage.js +++ b/frontend/playwright/ui/pages/ShortcutsPage.js @@ -305,6 +305,13 @@ export class ShortcutsPage extends BaseWebSocketPage { }); } + async confirmImportApply() { + const applyButton = this.page.getByRole("button", { + name: /apply/i, + }); + await applyButton.click(); + } + async getExportedJson() { const [download] = await Promise.all([ this.page.waitForEvent("download"), diff --git a/frontend/playwright/ui/specs/shortcuts.spec.js b/frontend/playwright/ui/specs/shortcuts.spec.js index afb8fbbd7f..c0b487b883 100644 --- a/frontend/playwright/ui/specs/shortcuts.spec.js +++ b/frontend/playwright/ui/specs/shortcuts.spec.js @@ -77,10 +77,12 @@ test.describe("Shortcut Import", () => { workspace: { "align-bottom": "ctrl+y" }, }); + await shortcutsPage.confirmImportApply(); + await shortcutsPage.expectShortcutCustomized("Align bottom"); - await shortcutsPage.searchForShortcut("Align bottom"); await shortcutsPage.clickTab("Personalized"); + await shortcutsPage.searchForShortcut("Align bottom"); await shortcutsPage.expectShortcutVisible("Align bottom"); }); @@ -116,6 +118,8 @@ test.describe("Shortcut Import", () => { dashboard: { "toggle-theme": "alt+m" }, }); + await shortcutsPage.confirmImportApply(); + await expect( page.getByRole("alert").filter({ hasText: /Invalid data/i }), ).not.toBeVisible(); @@ -144,6 +148,8 @@ test.describe("Shortcut Import", () => { }, }); + await shortcutsPage.confirmImportApply(); + await shortcutsPage.expectShortcutCustomized("Align bottom"); await shortcutsPage.expandSubsection("Alignment"); @@ -183,6 +189,8 @@ test.describe("Shortcut Export", () => { workspace: { "align-bottom": "" }, }); + await shortcutsPage.confirmImportApply(); + await shortcutsPage.searchForShortcut("Align bottom"); await shortcutsPage.expectShortcutDisabled("Align bottom"); @@ -228,11 +236,36 @@ test.describe("Shortcut Reset", () => { await shortcutsPage.importShortcuts({ workspace: { "align-bottom": "ctrl+y" }, }); + await shortcutsPage.confirmImportApply(); await shortcutsPage.expectShortcutCustomized("Align bottom"); await shortcutsPage.restoreAllShortcuts(); await shortcutsPage.expectShortcutNotCustomized("Align bottom"); }); + + test("Reset button shows pending message and requires save to apply", async ({ + page, + }) => { + const shortcutsPage = new ShortcutsPage(page); + await shortcutsPage.goToShortcuts(); + + await shortcutsPage.expandSubsection("Alignment"); + await shortcutsPage.clickEditShortcut("Align bottom"); + await shortcutsPage.pressKey("Control+y"); + await shortcutsPage.saveShortcut(); + await shortcutsPage.expectShortcutCustomized("Align bottom"); + + await shortcutsPage.clickEditShortcut("Align bottom"); + await shortcutsPage.resetShortcut("Align bottom"); + + await expect( + page.getByText(/If you save, this shortcut will return/i), + ).toBeVisible(); + + await shortcutsPage.saveShortcut(); + + await shortcutsPage.expectShortcutNotCustomized("Align bottom"); + }); }); test.describe("Shortcut Persistence", () => { @@ -315,6 +348,7 @@ test.describe("Shortcut Round-Trip", () => { await shortcutsPage.expectShortcutNotCustomized("Align bottom"); await shortcutsPage.importShortcuts(exported); + await shortcutsPage.confirmImportApply(); await shortcutsPage.expectShortcutCustomized("Align bottom"); const reExported = await shortcutsPage.getExportedJson(); diff --git a/frontend/src/app/main/data/shortcuts.cljs b/frontend/src/app/main/data/shortcuts.cljs index 8325370e45..84595c2b7f 100644 --- a/frontend/src/app/main/data/shortcuts.cljs +++ b/frontend/src/app/main/data/shortcuts.cljs @@ -207,13 +207,16 @@ (remove #(:disabled (second %))) (run! (fn [[key {:keys [command fn type overwrite]}]] (let [callback (wrap-cb key fn) - undefined (js* "(void 0)") commands (if (vector? command) (into-array command) #js [command])] - (if type - (mousetrap/bind commands callback type overwrite) - (mousetrap/bind commands callback undefined overwrite))))))) + (if (vector? type) + (do (mousetrap/bind commands callback (nth type 0) overwrite) + (mousetrap/bind commands callback (nth type 1) overwrite)) + (let [undefined (js* "(void 0)")] + (if type + (mousetrap/bind commands callback type overwrite) + (mousetrap/bind commands callback undefined overwrite))))))))) (defn- reset! ([] diff --git a/frontend/src/app/main/data/workspace/grid_layout/shortcuts.cljs b/frontend/src/app/main/data/workspace/grid_layout/shortcuts.cljs index e1ca153f48..326ffe93c3 100644 --- a/frontend/src/app/main/data/workspace/grid_layout/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/grid_layout/shortcuts.cljs @@ -34,38 +34,46 @@ (def shortcuts {:escape {:tooltip (ds/esc) :command ["escape" "enter" "v"] + :section [:workspace] :fn #(st/emit! (esc-pressed))} :undo {:tooltip (ds/meta "Z") :command (ds/c-mod "z") + :section [:workspace] :fn #(st/emit! dwu/undo)} :redo {:tooltip (ds/meta "Y") :command [(ds/c-mod "shift+z") (ds/c-mod "y")] + :section [:workspace] :fn #(st/emit! dwu/redo)} ;; ZOOM :increase-zoom {:tooltip "+" :command "+" + :section [:workspace] :fn #(st/emit! (dw/increase-zoom nil))} :decrease-zoom {:tooltip "-" :command "-" + :section [:workspace] :fn #(st/emit! (dw/decrease-zoom nil))} :reset-zoom {:tooltip (ds/shift "0") :command "shift+0" + :section [:workspace] :fn #(st/emit! dw/reset-zoom)} :fit-all {:tooltip (ds/shift "1") :command "shift+1" + :section [:workspace] :fn #(st/emit! dw/zoom-to-fit-all)} :zoom-selected {:tooltip (ds/shift "2") :command "shift+2" + :section [:workspace] :fn #(st/emit! dw/zoom-to-selected-shape)}}) (defn get-tooltip [shortcut] (assert (contains? shortcuts shortcut) (str shortcut)) - (get-in shortcuts [shortcut :tooltip])) \ No newline at end of file + (get-in shortcuts [shortcut :tooltip])) diff --git a/frontend/src/app/main/data/workspace/path/shortcuts.cljs b/frontend/src/app/main/data/workspace/path/shortcuts.cljs index 24444d711d..fe35b33e40 100644 --- a/frontend/src/app/main/data/workspace/path/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/path/shortcuts.cljs @@ -30,120 +30,146 @@ {:move-nodes {:tooltip "M" :command "m" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/change-edit-mode :move))} :draw-nodes {:tooltip "P" :command "p" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/change-edit-mode :draw))} :add-node {:tooltip (ds/shift "+") :command "shift++" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/add-node))} :delete-node {:tooltip (ds/supr) :command ["del" "backspace"] :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/remove-node))} :merge-nodes {:tooltip (ds/meta "J") :command (ds/c-mod "j") :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/merge-nodes))} :join-nodes {:tooltip "J" :command "j" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/join-nodes))} :separate-nodes {:tooltip "K" :command "k" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/separate-nodes))} :make-corner {:tooltip "X" :command "x" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/make-corner))} :make-curve {:tooltip "C" :command "c" :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/make-curve))} :snap-nodes {:tooltip (ds/meta "'") ;;https://github.com/ccampbell/mousetrap/issues/85 :command [(ds/c-mod "'") (ds/c-mod "219")] :subsections [:path-editor] + :section [:workspace] :fn #(st/emit! (drp/toggle-snap))} :escape {:tooltip (ds/esc) :command ["escape" "enter" "v"] + :section [:workspace] :fn #(st/emit! (esc-pressed))} :undo {:tooltip (ds/meta "Z") :command (ds/c-mod "z") + :section [:workspace] :fn #(st/emit! (drp/undo-path))} :redo {:tooltip (ds/meta "Y") :command [(ds/c-mod "shift+z") (ds/c-mod "y")] + :section [:workspace] :fn #(st/emit! (drp/redo-path))} ;; ZOOM :increase-zoom {:tooltip "+" :command "+" + :section [:workspace] :fn #(st/emit! (dw/increase-zoom nil))} :decrease-zoom {:tooltip "-" :command "-" + :section [:workspace] :fn #(st/emit! (dw/decrease-zoom nil))} :reset-zoom {:tooltip (ds/shift "0") :command "shift+0" + :section [:workspace] :fn #(st/emit! dw/reset-zoom)} :fit-all {:tooltip (ds/shift "1") :command "shift+1" + :section [:workspace] :fn #(st/emit! dw/zoom-to-fit-all)} :zoom-selected {:tooltip (ds/shift "2") :command "shift+2" + :section [:workspace] :fn #(st/emit! dw/zoom-to-selected-shape)} ;; Arrow movement :move-fast-up {:tooltip (ds/shift ds/up-arrow) :command "shift+up" + :section [:workspace] :fn #(st/emit! (drp/move-selected :up true))} :move-fast-down {:tooltip (ds/shift ds/down-arrow) :command "shift+down" + :section [:workspace] :fn #(st/emit! (drp/move-selected :down true))} :move-fast-right {:tooltip (ds/shift ds/right-arrow) :command "shift+right" + :section [:workspace] :fn #(st/emit! (drp/move-selected :right true))} :move-fast-left {:tooltip (ds/shift ds/left-arrow) :command "shift+left" + :section [:workspace] :fn #(st/emit! (drp/move-selected :left true))} :move-unit-up {:tooltip ds/up-arrow :command "up" + :section [:workspace] :fn #(st/emit! (drp/move-selected :up false))} :move-unit-down {:tooltip ds/down-arrow :command "down" + :section [:workspace] :fn #(st/emit! (drp/move-selected :down false))} :move-unit-left {:tooltip ds/right-arrow :command "right" + :section [:workspace] :fn #(st/emit! (drp/move-selected :right false))} :move-unit-right {:tooltip ds/left-arrow :command "left" + :section [:workspace] :fn #(st/emit! (drp/move-selected :left false))}}) (defn get-tooltip [shortcut] diff --git a/frontend/src/app/main/data/workspace/shortcuts.cljs b/frontend/src/app/main/data/workspace/shortcuts.cljs index a75e47cc6d..192258f0d7 100644 --- a/frontend/src/app/main/data/workspace/shortcuts.cljs +++ b/frontend/src/app/main/data/workspace/shortcuts.cljs @@ -148,23 +148,19 @@ :section [:workspace] :fn #(emit-when-no-readonly (dw/start-editing-selected))} - :start-measure {:tooltip (ds/alt "") - :command ["alt" "."] - :type "keydown" - :subsections [:edit] - :section [:workspace] - :fn #(emit-when-no-readonly (dw/toggle-distances-display true))} - - :stop-measure {:tooltip (ds/alt "") - :command ["alt" "."] - :type "keyup" - :subsections [:edit] - :section [:workspace] - :fn #(emit-when-no-readonly (dw/toggle-distances-display false))} + :show-measure {:tooltip (ds/alt "") + :command ["alt" "."] + :type ["keydown" "keyup"] + :subsections [:tools] + :section [:workspace] + :fn #(emit-when-no-readonly + (let [type (.-type %)] + (dw/toggle-distances-display (if (= type "keydown") true false))))} :escape {:tooltip (ds/esc) :command "escape" :subsections [:edit] + :section [:workspace] :fn #(st/emit! :interrupt (dwdc/clear-drawing) (dw/deselect-all true))} :find {:tooltip (ds/meta "F") :command (ds/c-mod "f") :subsections [:edit] @@ -191,6 +187,7 @@ :ungroup {:tooltip (ds/shift "G") :command "shift+g" :subsections [:modify-layers] + :section [:workspace] :fn #(emit-when-no-readonly (dw/ungroup-selected))} :mask {:tooltip (ds/meta "M") @@ -363,11 +360,13 @@ :draw-line {:tooltip "L" :command "l" :subsections [:tools] + :section [:workspace] :fn #(emit-when-no-readonly (dwd/select-for-drawing :line))} :draw-arrow {:tooltip (ds/shift "L") :command "shift+l" :subsections [:tools] + :section [:workspace] :fn #(emit-when-no-readonly (dwd/select-for-drawing :arrow))} :draw-curve {:tooltip (ds/shift "C") diff --git a/frontend/src/app/main/ui/components/button_link.scss b/frontend/src/app/main/ui/components/button_link.scss index 872afd5af5..895ed0bc97 100644 --- a/frontend/src/app/main/ui/components/button_link.scss +++ b/frontend/src/app/main/ui/components/button_link.scss @@ -23,9 +23,9 @@ transition: all 0.4s; text-decoration: none !important; block-size: $sz-40; - - .logo { - block-size: px2rem(20); - inline-size: px2rem(20); - } +} + +.logo { + block-size: px2rem(20); + inline-size: px2rem(20); } diff --git a/frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.cljs b/frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.cljs new file mode 100644 index 0000000000..bb0c8a81af --- /dev/null +++ b/frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.cljs @@ -0,0 +1,161 @@ +;; 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 Sucursal en España SL + +(ns app.main.ui.settings.import-shortcuts-diff-modal + (:require-macros [app.main.style :as stl]) + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.i18n :refer [tr]] + [app.main.data.dashboard.shortcuts :as dsc] + [app.main.data.modal :as modal] + [app.main.data.viewer.shortcuts :as vsc] + [app.main.data.workspace.path.shortcuts :as psc] + [app.main.data.workspace.shortcuts :as wsc] + [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.shortcuts :as ss] + [app.util.dom :as dom] + [cuerdas.core :as str] + [rumext.v2 :as mf])) + +(def ^:private context->defaults + {:workspace (d/deep-merge psc/shortcuts wsc/shortcuts) + :dashboard dsc/shortcuts + :viewer vsc/shortcuts}) + +(defn compute-diff + [imported-shortcuts custom-shortcuts] + (let [contexts (filter #(contains? imported-shortcuts %) [:workspace :dashboard :viewer])] + (mapcat + (fn [ctx] + (let [imported-ctx (get imported-shortcuts ctx) + current-ctx (get custom-shortcuts ctx {}) + defaults-ctx (get context->defaults ctx)] + (keep + (fn [[shortcut-key imported-binding]] + (let [current-binding (get current-ctx shortcut-key) + default-cmd (:command (get defaults-ctx shortcut-key)) + effective-binding (or current-binding default-cmd) + customized? (and (some? current-binding) + (seq current-binding) + (not= current-binding default-cmd))] + (when (and (some? imported-binding) + (not= effective-binding imported-binding)) + {:context ctx + :key shortcut-key + :current effective-binding + :imported imported-binding + :customized? customized?}))) + imported-ctx))) + contexts))) + +(def ^:private context-order {:workspace 0 :dashboard 1 :viewer 2}) + +(def ^:private context-name + {:workspace "Workspace" + :dashboard "Dashboard" + :viewer "Viewer"}) + +(mf/defc import-shortcuts-diff-modal + {::mf/register modal/components + ::mf/register-as :import-shortcuts-diff-modal} + [{:keys [imported-shortcuts custom-shortcuts all-shortcuts-raw]}] + (let [diff-entries + (mf/with-memo [imported-shortcuts custom-shortcuts] + (->> (compute-diff imported-shortcuts custom-shortcuts) + (sort-by (fn [e] [(get context-order (:context e) 99) + (name (:key e))])))) + + handle-close-dialog + (mf/use-fn + (fn [event] + (dom/stop-propagation event) + (st/emit! (modal/hide)))) + + handle-apply + (mf/use-fn + (mf/deps imported-shortcuts all-shortcuts-raw) + (fn [event] + (dom/stop-propagation event) + (st/emit! (ss/import-custom-shortcuts imported-shortcuts all-shortcuts-raw)) + (st/emit! (modal/hide))))] + + [:div {:class (stl/css :modal-overlay)} + [:div {:class (stl/css :modal-dialog)} + [:> icon-button* {:class (stl/css :close-btn) + :variant "ghost" + :aria-label (tr "labels.close") + :on-click handle-close-dialog + :tooltip-class (stl/css :close-btn-tooltip) + :icon i/close}] + [:div {:class (stl/css :modal-title)} + (tr "import-shortcuts.diff-modal-title")] + + [:div {:class (stl/css :modal-content)} + [:div {:class (stl/css :modal-content-text)} + (tr "import-shortcuts.diff-modal-text")] + + (if (seq diff-entries) + [:table {:class (stl/css :shortcuts-table)} + [:thead + [:tr {:class (stl/css :shortcuts-list-header)} + [:th {:class (stl/css :shortcut-header-name)} + (tr "restore-shortcuts.acction")] + [:th {:class (stl/css :shortcut-header-command)} + (tr "labels.current")] + [:th {:class (stl/css :shortcut-header-command)} + (tr "labels.import")]]] + [:tbody {:class (stl/css :shortcuts-list-body)} + (let [last-ctx* (volatile! nil)] + (for [entry diff-entries] + (let [{:keys [context key current imported customized?]} entry + show-context-label? (not= @last-ctx* context)] + (vreset! last-ctx* context) + [:* {:key (dm/str (name context) "-" (name key) "-group")} + (when show-context-label? + [:tr {:key (dm/str "ctx-" (name context)) + :class (stl/css :context-separator)} + [:td {:colSpan 3 + :class (stl/css :context-label)} + (get context-name context)]]) + [:tr {:key (dm/str (name context) "-" (name key)) + :class (stl/css :shortcuts-list-item)} + [:td {:class (stl/css :shortcut-name)} + (ss/translation-keyname :sc key)] + [:td {:class (stl/css :shortcut-command)} + (if (str/blank? current) + [:span {:class (stl/css :shortcut-empty)} "-"] + [:> ss/shortcuts-keys* {:content current + :command key + :is-customized customized? + :light-shortcut true + :has-conflict? false}])] + [:td {:class (stl/css :shortcut-command)} + (if (str/blank? imported) + [:span {:class (stl/css :shortcut-empty)} "-"] + [:> ss/shortcuts-keys* {:content imported + :command key + :light-shortcut true + :is-customized true + :has-conflict? false}])]]])))]] + [:div {:class (stl/css :no-changes)} + (tr "import-shortcuts.no-changes")])] + + [:div {:class (stl/css :modal-footer)} + [:div {:class (stl/css :action-buttons)} + [:> button* {:class (stl/css :cancel-button) + :variant "secondary" + :type "button" + :on-click handle-close-dialog} + (tr "labels.cancel")] + [:> button* {:class (stl/css :cancel-button) + :variant "primary" + :type "button" + :on-click handle-apply} + (tr "import-shortcuts.apply")]]]]])) diff --git a/frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.scss b/frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.scss new file mode 100644 index 0000000000..0d42b9d1a6 --- /dev/null +++ b/frontend/src/app/main/ui/settings/import_shortcuts_diff_modal.scss @@ -0,0 +1,167 @@ +// 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 Sucursal en España SL + +@use "ds/typography.scss" as t; +@use "ds/_borders.scss" as *; +@use "ds/spacing.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; + +.modal-overlay { + display: flex; + justify-content: center; + align-items: center; + position: fixed; + inset-inline-start: 0; + inset-block-start: 0; + block-size: 100%; + inline-size: 100%; + z-index: var(--z-index-set); + background-color: var(--overlay-color); + padding-inline-start: px2rem(272); +} + +.modal-dialog { + position: relative; + padding: var(--sp-xxxl); + border-radius: $br-8; + background-color: var(--color-background-primary); + border: $b-2 solid var(--color-background-quaternary); + display: grid; + grid-template-rows: auto 1fr auto; + gap: var(--sp-xxxl); + min-inline-size: $sz-364; + min-block-size: $sz-192; + max-block-size: $sz-520; + inline-size: px2rem(584); + max-inline-size: $sz-712; +} + +.modal-title { + @include t.use-typography("headline-medium"); + + color: var(--color-foreground-primary); + block-size: px2rem(26); +} + +.close-btn { + position: absolute; + inset-block-start: px2rem(8); + inset-inline-end: px2rem(6); + block-size: $sz-32; + inline-size: $sz-28; +} + +.close-btn-tooltip { + position: absolute; + inset-block-start: px2rem(8); + inset-inline-end: px2rem(6); +} + +.modal-content { + display: flex; + flex-direction: column; + gap: var(--sp-l); + block-size: fit-content; + overflow-y: auto; +} + +.modal-content-text { + @include t.use-typography("body-medium"); + + color: var(--color-foreground-primary); +} + +.modal-footer { + justify-self: end; + display: flex; + flex-direction: row; + gap: var(--sp-s); +} + +.action-buttons { + display: flex; + justify-content: flex-end; + gap: var(--sp-s); +} + +.shortcuts-table { + inline-size: 100%; + table-layout: fixed; + border-collapse: collapse; +} + +.shortcuts-list-item, +.shortcuts-list-header { + @include t.use-typography("body-medium"); + + color: var(--color-foreground-secondary); + display: grid; + grid-template-columns: 35% 30% 35%; +} + +.shortcuts-list-header { + border-block-end: $b-1 solid var(--color-foreground-secondary); + block-size: $sz-32; + text-align: start; + padding-block-end: var(--sp-s); +} + +.shortcuts-list-body { + display: flex; + flex-direction: column; + gap: var(--sp-s); +} + +.shortcuts-list-item { + color: var(--color-foreground-primary); +} + +.shortcuts-list-item:first-child { + padding-block-start: var(--sp-s); +} + +.shortcut-header-name, +.shortcut-header-command { + text-align: start; +} + +.shortcut-name { + text-align: start; +} + +.shortcut-command { + display: flex; + gap: var(--sp-s); + align-items: center; + justify-content: start; + text-align: start; + flex-wrap: wrap; +} + +.shortcut-empty { + color: var(--color-foreground-secondary); +} + +.context-separator { + border-block-end: none; +} + +.context-label { + @include t.use-typography("title-small"); + + color: var(--color-foreground-secondary); + text-align: start; + padding-block: var(--sp-m) var(--sp-xxs); +} + +.no-changes { + @include t.use-typography("body-medium"); + + color: var(--color-foreground-secondary); + text-align: center; + padding-block: var(--sp-xl); +} diff --git a/frontend/src/app/main/ui/settings/restore_shortcuts_modal.cljs b/frontend/src/app/main/ui/settings/restore_shortcuts_modal.cljs index a24091f7b2..f200bdd052 100644 --- a/frontend/src/app/main/ui/settings/restore_shortcuts_modal.cljs +++ b/frontend/src/app/main/ui/settings/restore_shortcuts_modal.cljs @@ -46,7 +46,9 @@ (drop-last default-chars-list)) ctx-customs (get custom-shortcuts context {}) - current-command (or (get ctx-customs shortcut-key) default-command) + custom-val (get ctx-customs shortcut-key) + current-command (or custom-val default-command) + customized? (and (some? custom-val) (seq custom-val) (not= custom-val default-command)) current-managed-list (if (coll? current-command) current-command @@ -59,7 +61,7 @@ current-short-char-list (if (= 1 (count current-chars-list)) current-chars-list (drop-last current-chars-list))] - [default-last-element default-short-char-list current-last-element current-command current-short-char-list])) + [default-last-element default-short-char-list current-last-element current-command current-short-char-list customized?])) (mf/defc restore-all-modal {::mf/register modal/components @@ -107,9 +109,11 @@ default-short-char-list current-last-element current-command - current-short-char-list] (extract-shortcut-keys shortcut-key custom-shortcuts context) + current-short-char-list + customized?] (extract-shortcut-keys shortcut-key custom-shortcuts context) current-penultimate (last current-short-char-list) - default-penultimate (last default-short-char-list)] + default-penultimate (last default-short-char-list) + command-class (if customized? (stl/css :customized-command) (stl/css :default-command))] [:tr {:key (dm/str (name context) "-" (name shortcut-key)) :class (stl/css :shortcuts-list-item)} [:td {:class (stl/css :shortcut-name)} @@ -122,7 +126,7 @@ (for [char chars] [:> ss/converted-chars* {:key (dm/str char "-" (name shortcut-key)) :char char - :class (stl/css :default-command) + :class command-class :command shortcut-key}]) (when (not= chars current-penultimate) [:span {:class (stl/css :space)} ","])])) (when (not= current-last-element current-penultimate) @@ -131,7 +135,7 @@ (for [char current-last-element] [:> ss/converted-chars* {:key (dm/str char "-" (name shortcut-key)) :char char - :class (stl/css :default-command) + :class command-class :command shortcut-key}])])] [:td {:class (stl/css :shortcut-command)} diff --git a/frontend/src/app/main/ui/settings/restore_shortcuts_modal.scss b/frontend/src/app/main/ui/settings/restore_shortcuts_modal.scss index 95da5b5147..acfe0ee27f 100644 --- a/frontend/src/app/main/ui/settings/restore_shortcuts_modal.scss +++ b/frontend/src/app/main/ui/settings/restore_shortcuts_modal.scss @@ -21,6 +21,7 @@ inline-size: 100%; z-index: var(--z-index-set); background-color: var(--overlay-color); + padding-inline-start: px2rem(272); } .modal-dialog { @@ -142,6 +143,12 @@ border-radius: $br-6; } +.customized-command { + background-color: var(--color-background-info); + color: var(--color-foreground-primary); + border-radius: $br-6; +} + .shortcut-empty { color: var(--color-foreground-secondary); } diff --git a/frontend/src/app/main/ui/settings/shortcuts.cljs b/frontend/src/app/main/ui/settings/shortcuts.cljs index 24b49cba1d..7800403adc 100644 --- a/frontend/src/app/main/ui/settings/shortcuts.cljs +++ b/frontend/src/app/main/ui/settings/shortcuts.cljs @@ -24,6 +24,7 @@ [app.main.ui.ds.foundations.typography.text :refer [text*]] [app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]] [app.main.ui.ds.product.empty-state :refer [empty-state*]] + [app.main.ui.settings.import-shortcuts-diff-modal] [app.main.ui.settings.restore-shortcuts-modal] [app.main.ui.shortcuts :as ss] [app.util.dom :as dom] @@ -35,7 +36,7 @@ [rumext.v2 :as mf])) (mf/defc search-section* - [{:keys [filter-term on-search-term-change on-search-clear-click on-restore-all show-restore-all? has-custom-shortcuts]}] + [{:keys [filter-term on-search-term-change on-search-clear-click on-restore-all show-restore-all has-custom-shortcuts]}] [:div {:class (stl/css :shortcuts-search-section)} [:> search-bar* {:on-change on-search-term-change :on-clear on-search-clear-click @@ -43,7 +44,7 @@ :placeholder (tr "shortcuts.title") :icon-id i/search :auto-focus true}] - (when (and show-restore-all? has-custom-shortcuts) + (when (and show-restore-all has-custom-shortcuts) [:> button* {:variant "secondary" :on-click on-restore-all @@ -171,10 +172,9 @@ :conflicts conflicts}]))])) (mf/defc shortcuts-tab-section* - [{:keys [shortcut-filter show-restore-all? on-restore-all empty-str custom-shortcuts conflicts]}] + [{:keys [shortcut-filter show-restore-all on-restore-all empty-str custom-shortcuts conflicts expand-all-by-default + open-sections update-open-sections]}] (let [{:keys [all-shortcuts]} (mf/use-ctx ctx/shortcuts-ctx) - open-sections* (mf/use-state [[:workspace]]) - open-sections (deref open-sections*) filter-term* (mf/use-state "") filter-term (deref filter-term*) @@ -186,8 +186,15 @@ (seq (:children (:none children))) (seq children)))) - all-shortcuts (into {} (filter (fn [[_ v]] (section-has-content? v)) all-shortcuts)) - filtered-shortcuts (filter-shortcuts-tree all-shortcuts shortcut-filter filter-term) + all-shortcuts (mf/with-memo [all-shortcuts] + (into {} (filter (fn [[_ v]] (section-has-content? v)) all-shortcuts))) + filtered-shortcuts (mf/with-memo [all-shortcuts shortcut-filter filter-term] + (filter-shortcuts-tree all-shortcuts shortcut-filter filter-term)) + + expand-all-sections + (fn [] + (let [all-ids (collect-open-section-ids filtered-shortcuts)] + (update-open-sections (if (seq all-ids) (vec all-ids) [[:workspace]])))) on-search-term-change (mf/use-fn @@ -195,16 +202,20 @@ (fn [term] (reset! filter-term* term) (if (str/blank? term) - (reset! open-sections* [[:workspace]]) + (if expand-all-by-default + (expand-all-sections) + (update-open-sections [[:workspace]])) (let [filtered-tree (filter-shortcuts-tree all-shortcuts shortcut-filter term) open-ids (collect-open-section-ids filtered-tree)] - (reset! open-sections* open-ids))))) + (update-open-sections (vec open-ids)))))) on-search-clear-click (mf/use-fn (fn [_] - (reset! open-sections* [[:workspace]]) - (reset! filter-term* ""))) + (reset! filter-term* "") + (if expand-all-by-default + (expand-all-sections) + (update-open-sections [[:workspace]])))) manage-sections (fn [item] @@ -214,15 +225,28 @@ new-value (if is-present? (filterv (fn [element] (not= element item)) open-sections) (conj open-sections item))] - (reset! open-sections* new-value))))] + (update-open-sections new-value)))) + + has-custom-shortcuts (some #(seq (val %)) custom-shortcuts) + + empty-str + (if (and has-custom-shortcuts (not (str/blank? filter-term))) + (tr "shortcuts.no-shortcuts") + empty-str)] + + (mf/with-effect [filtered-shortcuts filter-term expand-all-by-default open-sections] + (when (and expand-all-by-default (str/blank? filter-term)) + (let [all-ids (vec (collect-open-section-ids filtered-shortcuts))] + (when (and (seq all-ids) (not= all-ids open-sections)) + (update-open-sections all-ids))))) [:div {:class (stl/css :shortcuts-section)} [:> search-section* {:filter-term filter-term :on-search-term-change on-search-term-change :on-search-clear-click on-search-clear-click :on-restore-all on-restore-all - :show-restore-all? show-restore-all? - :has-custom-shortcuts (some #(seq (val %)) custom-shortcuts)}] + :show-restore-all show-restore-all + :has-custom-shortcuts has-custom-shortcuts}] (if (seq filtered-shortcuts) [:> shortcuts-list* {:shortcuts filtered-shortcuts :open-sections open-sections @@ -273,6 +297,14 @@ (fn [new-section] (reset! section* (keyword new-section)))) + open-sections-by-tab* (mf/use-state {}) + open-sections-by-tab (deref open-sections-by-tab*) + + make-update-open-sections + (fn [tab-key] + (fn [new-val] + (swap! open-sections-by-tab* assoc tab-key new-val))) + custom-shortcuts (get-in profile [:props :custom-shortcuts]) has-custom-shortcuts (some #(seq (val %)) custom-shortcuts) path-shortcuts-custom (ds/apply-custom-overrides @@ -322,11 +354,12 @@ :viewer-orig vsc/shortcuts) shortcuts-ctx-value - {:workspace-sc-trans (d/deep-merge workspace-shortcuts-with-translation path-shortcuts-with-translation) - :dashboard-sc-trans dashboard-shortcuts-with-translation - :viewer-sc-trans viewer-shortcuts-with-translation - :all-shortcuts all-shortcuts - :all-sc-raw all-shortcuts-raw} + (mf/with-memo [custom-shortcuts] + {:workspace-sc-trans (d/deep-merge workspace-shortcuts-with-translation path-shortcuts-with-translation) + :dashboard-sc-trans dashboard-shortcuts-with-translation + :viewer-sc-trans viewer-shortcuts-with-translation + :all-shortcuts all-shortcuts + :all-sc-raw all-shortcuts-raw}) on-restore-all (mf/use-fn @@ -336,6 +369,38 @@ (st/emit! (modal/show {:type :restore-all-modal :custom-shortcuts custom-shortcuts})))) + filter-all + (mf/with-memo [] + (fn [_ shortcut search-term] + (or (str/blank? search-term) + (matches-search (:translation shortcut) search-term)))) + + filter-personalized + (mf/use-fn + (mf/deps custom-shortcuts) + (fn [shortcut-key shortcut search-term] + (let [shortcut-group (first (:section shortcut)) + group-map (get custom-shortcuts shortcut-group) + group-map (if (map? group-map) group-map {}) + customized? (and (contains? group-map shortcut-key) + (not (str/blank? (get group-map shortcut-key))))] + (and customized? + (or (str/blank? search-term) + (matches-search (:translation shortcut) search-term)))))) + + filter-disabled + (mf/use-fn + (mf/deps custom-shortcuts) + (fn [shortcut-key shortcut search-term] + (let [shortcut-group (first (:section shortcut)) + group-map (get custom-shortcuts shortcut-group) + group-map (if (map? group-map) group-map {}) + in-group? (contains? group-map shortcut-key) + blank? (str/blank? (get group-map shortcut-key))] + (and in-group? blank? + (or (str/blank? search-term) + (matches-search (:translation shortcut) search-term)))))) + on-import-file (mf/use-fn (fn [_] @@ -357,7 +422,7 @@ on-file-selected (mf/use-fn - (mf/deps all-shortcuts-raw) + (mf/deps all-shortcuts-raw custom-shortcuts) (fn [event] (let [file (-> (dom/get-target event) (dom/get-files) @@ -370,7 +435,10 @@ :keywordize-keys true) validation (validate-imported-shortcuts shortcuts)] (if (:valid? validation) - (st/emit! (ss/import-custom-shortcuts shortcuts all-shortcuts-raw)) + (st/emit! (modal/show {:type :import-shortcuts-diff-modal + :imported-shortcuts shortcuts + :custom-shortcuts custom-shortcuts + :all-shortcuts-raw all-shortcuts-raw})) (st/emit! (ntf/error (tr "errors.invalid-data"))))) (catch :default _ (st/emit! (ntf/error (tr "errors.invalid-data")))))))) @@ -400,43 +468,33 @@ :class (stl/css :shortcuts-switcher)} (case section :all - [:> shortcuts-tab-section* {:shortcut-filter (fn [_ shortcut search-term] - (or (str/blank? search-term) - (matches-search (:translation shortcut) search-term))) - :show-restore-all? true + [:> shortcuts-tab-section* {:shortcut-filter filter-all + :show-restore-all true :empty-str (tr "shortcuts.no-shortcuts") :on-restore-all on-restore-all - :custom-shortcuts custom-shortcuts}] + :custom-shortcuts custom-shortcuts + :open-sections (get open-sections-by-tab :all [[:workspace]]) + :update-open-sections (make-update-open-sections :all)}] :personalized - [:> shortcuts-tab-section* {:shortcut-filter (fn [shortcut-key shortcut search-term] - (let [shortcut-group (first (:section shortcut)) - group-map (get custom-shortcuts shortcut-group) - group-map (if (map? group-map) group-map {}) - customized? (and (contains? group-map shortcut-key) - (not (str/blank? (get group-map shortcut-key))))] - (and customized? - (or (str/blank? search-term) - (matches-search (:translation shortcut) search-term))))) - :show-restore-all? true + [:> shortcuts-tab-section* {:shortcut-filter filter-personalized + :show-restore-all true :empty-str (tr "shortcuts.no-personalized") :on-restore-all on-restore-all - :custom-shortcuts custom-shortcuts}] + :expand-all-by-default true + :custom-shortcuts custom-shortcuts + :open-sections (get open-sections-by-tab :personalized [[:workspace]]) + :update-open-sections (make-update-open-sections :personalized)}] :disabled - [:> shortcuts-tab-section* {:shortcut-filter (fn [shortcut-key shortcut search-term] - (let [shortcut-group (first (:section shortcut)) - group-map (get custom-shortcuts shortcut-group) - group-map (if (map? group-map) group-map {}) - in-group? (contains? group-map shortcut-key) - blank? (str/blank? (get group-map shortcut-key))] - (and in-group? blank? - (or (str/blank? search-term) - (matches-search (:translation shortcut) search-term))))) - :show-restore-all? true + [:> shortcuts-tab-section* {:shortcut-filter filter-disabled + :show-restore-all true :empty-str (tr "shortcuts.no-disabled") :on-restore-all on-restore-all - :custom-shortcuts custom-shortcuts}])]] + :expand-all-by-default true + :custom-shortcuts custom-shortcuts + :open-sections (get open-sections-by-tab :disabled [[:workspace]]) + :update-open-sections (make-update-open-sections :disabled)}])]] [:div {:class (stl/css :shortcuts-page-footer)} [:div {:class (stl/css :shortcuts-info)} diff --git a/frontend/src/app/main/ui/settings/shortcuts.scss b/frontend/src/app/main/ui/settings/shortcuts.scss index e9395e2fe5..a3bfa3df78 100644 --- a/frontend/src/app/main/ui/settings/shortcuts.scss +++ b/frontend/src/app/main/ui/settings/shortcuts.scss @@ -14,6 +14,9 @@ .shortcuts-page { display: flex; justify-content: center; + + // box-sizing: border-box; + // min-block-size: 100%; padding-block-end: px2rem(32); } @@ -194,26 +197,20 @@ align-items: center; justify-content: space-between; gap: var(--sp-s); - padding-block-start: var(--sp-m); -} - -.shortcuts-info { - display: flex; - gap: var(--sp-s); - align-items: center; -} - -.shortcuts-page-footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: var(--sp-s); - padding-block-start: var(--sp-m); + margin-block-start: auto; + padding-block: var(--sp-m) var(--sp-s); + position: fixed; + bottom: 0; + background-color: var(--app-background); + min-inline-size: px2rem(590); } .shortcuts-info { @include t.use-typography("body-small"); + display: flex; + gap: var(--sp-m); + align-items: center; color: var(--color-foreground-secondary); } @@ -229,11 +226,6 @@ background-color: var(--color-accent-error); } -.shortcuts-info { - display: flex; - gap: var(--sp-m); -} - .shortcuts-info-wrapper { display: flex; align-items: center; diff --git a/frontend/src/app/main/ui/shortcuts.cljs b/frontend/src/app/main/ui/shortcuts.cljs index 2a27c2742b..668d697e11 100644 --- a/frontend/src/app/main/ui/shortcuts.cljs +++ b/frontend/src/app/main/ui/shortcuts.cljs @@ -305,7 +305,7 @@ :key unique-key} char])) (mf/defc shortcuts-keys* - [{:keys [content command is-customized has-conflict?]}] + [{:keys [content command is-customized has-conflict? light-shortcut]}] (let [managed-list (if (coll? content) content (conj () content)) @@ -324,7 +324,8 @@ :command command :class (cond has-conflict? (stl/css :conflict-key) - is-customized (stl/css :customized-key))}]) + is-customized (stl/css :customized-key) + light-shortcut (stl/css :light-key))}]) (when (not= chars penultimate) [:span {:class (stl/css :space)} ","])]) (when (not= last-element penultimate) @@ -336,7 +337,8 @@ :command command :class (cond has-conflict? (stl/css :conflict-key) - is-customized (stl/css :customized-key))}])])])) + is-customized (stl/css :customized-key) + light-shortcut (stl/css :light-key))}])])])) (mf/defc shortcut-row-editable* [{:keys [elements custom-shortcuts command-translate conflicts section-key]}] @@ -371,21 +373,30 @@ conflict* (mf/use-state nil) conflict (deref conflict*) + reset-notification* (mf/use-state nil) + reset-pending* (mf/use-state false) + clean-editing-state (fn [] (reset! is-editing* false) (reset! display-parts* nil) (reset! recorded-command* nil) - (reset! conflict* nil)) + (reset! conflict* nil) + (reset! reset-notification* nil) + (reset! reset-pending* false)) effective-section-key (if (= section-key :basics) :workspace section-key) on-reset-shortcut (mf/use-fn (mf/deps effective-section-key command-info) - (fn [shortcut-key] - (st/emit! (customize/reset-custom-shortcut shortcut-key (:original-command command-info) effective-section-key)) - (clean-editing-state))) + (fn [_] + (let [original-cmd (:original-command command-info)] + (reset! reset-pending* true) + (reset! recorded-command* original-cmd) + (reset! reset-notification* original-cmd) + (reset! conflict* nil) + (reset! display-parts* nil)))) start-editing (mf/use-fn @@ -419,11 +430,13 @@ (fn [event] (dom/prevent-default event) (when recorded-command - (st/emit! (customize/set-custom-shortcut - command - recorded-command - (:key conflict) - effective-section-key))) + (if (deref reset-pending*) + (st/emit! (customize/reset-custom-shortcut command recorded-command effective-section-key)) + (st/emit! (customize/set-custom-shortcut + command + recorded-command + (:key conflict) + effective-section-key)))) (clean-editing-state))) on-editable-container-blur @@ -437,6 +450,11 @@ (when is-editing (some-> (mf/ref-val recording-ref) (dom/focus!)))) + (mf/with-effect [display-parts] + (when (some? display-parts) + (reset! reset-notification* nil) + (reset! reset-pending* false))) + (mf/with-effect [is-editing] (when is-editing (letfn [(on-keydown [^js event] @@ -506,7 +524,15 @@ :customized-key customized?)} (:final-key display-parts)]) (when-not (:finalized? display-parts) [:span {:class (stl/css :recording-ellipsis)} "..."])])] - (when recorded-command + (when-let [default-cmd @reset-notification*] + [:> context-notification* {:level :info :class (stl/css :modal-reset-msg)} + (tr "shortcuts.edit-modal.reset-pending") + [:> shortcuts-keys* {:content default-cmd + :command (keyword "default") + :is-customized false + :has-conflict? false}]]) + + (when (and recorded-command (not (deref reset-pending*))) (if conflict [:> context-notification* {:level :warning :class (stl/css :modal-error-msg)} (tr "shortcuts.edit-modal.conflict" (:name conflict))] diff --git a/frontend/src/app/main/ui/shortcuts.scss b/frontend/src/app/main/ui/shortcuts.scss index cc78a03214..7d582e4e71 100644 --- a/frontend/src/app/main/ui/shortcuts.scss +++ b/frontend/src/app/main/ui/shortcuts.scss @@ -187,6 +187,11 @@ color: var(--color-foreground-error); } +.light-key { + background-color: var(--color-background-tertiary); + color: var(--color-foreground-secondary); +} + .space { margin: 0 var(--sp-xxs); color: var(--color-foreground-secondary); diff --git a/frontend/test/frontend_tests/ui/settings_shortcuts_test.cljs b/frontend/test/frontend_tests/ui/settings_shortcuts_test.cljs index 0def5fb1c9..8bbf5af6b2 100644 --- a/frontend/test/frontend_tests/ui/settings_shortcuts_test.cljs +++ b/frontend/test/frontend_tests/ui/settings_shortcuts_test.cljs @@ -1,6 +1,7 @@ (ns frontend-tests.ui.settings-shortcuts-test (:require [app.main.data.profile :as du] + [app.main.ui.settings.import-shortcuts-diff-modal :as diff-modal] [app.main.ui.settings.restore-shortcuts-modal :as restore-modal] [app.main.ui.settings.shortcuts :as sut] [app.main.ui.shortcuts :as ui-shortcuts] @@ -150,6 +151,48 @@ (t/is (= "ctrl+up" (get customs :move-to-top)) "Last imported entry with duplicate command should survive"))))) +(t/deftest compute-diff-shows-new-shortcut-changes + (let [imported {:workspace {:add-comment "alt+c"}} + diff (diff-modal/compute-diff imported {})] + (t/is (= 1 (count diff))) + (let [entry (first diff)] + (t/is (= :workspace (:context entry))) + (t/is (= :add-comment (:key entry))) + (t/is (= "alt+c" (:imported entry))) + (t/is (not= "alt+c" (:current entry)))))) + +(t/deftest compute-diff-shows-conflict-as-disabled + (let [imported {:workspace {:select-all "ctrl+shift+a"}} + diff (diff-modal/compute-diff imported {})] + (t/is (pos? (count diff))) + (let [select-all-entry (first (filter #(= :select-all (:key %)) diff))] + (t/is (= :workspace (:context select-all-entry))) + (t/is (= "ctrl+shift+a" (:imported select-all-entry)))))) + +(t/deftest compute-diff-shows-empty-string-as-disabled-shortcut + (let [imported {:workspace {:escape ""}} + current {} + diff (diff-modal/compute-diff imported current)] + (t/is (pos? (count diff))) + (let [entry (first (filter #(= :escape (:key %)) diff))] + (t/is (= :workspace (:context entry))) + (t/is (= "" (:imported entry)))))) + +(t/deftest compute-diff-excludes-unchanged-shortcuts + (let [imported {:workspace {:escape ""}} + current {:workspace {:escape ""}} + diff (diff-modal/compute-diff imported current)] + (t/is (empty? diff)))) + +(t/deftest compute-diff-detects-multiple-contexts + (let [imported {:workspace {:add-comment "alt+c"} + :viewer {:next-frame "right"}} + diff (diff-modal/compute-diff imported {})] + (t/is (pos? (count diff))) + (let [contexts (set (map :context diff))] + (t/is (contains? contexts :workspace)) + (t/is (contains? contexts :viewer))))) + (t/deftest extract-shortcut-keys-uses-correct-context-override (let [customs {:workspace {:undo "shift+z" :move-nodes "shift+m"} diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 5b28fa12f6..70b168bb93 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -5631,12 +5631,8 @@ msgid "shortcuts.start-editing" msgstr "Start editing" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182 -msgid "shortcuts.start-measure" -msgstr "Start measurement" - -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:183 -msgid "shortcuts.stop-measure" -msgstr "Stop measurement" +msgid "shortcuts.show-measure" +msgstr "Show measurements" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:184 msgid "shortcuts.thumbnail-set" @@ -5710,6 +5706,10 @@ msgstr "Press the key combination" msgid "shortcuts.edit-modal.conflict" msgstr "Combination assigned to \"%s\". Saving will remove it there." +#: src/app/main/ui/shortcuts.cljs +msgid "shortcuts.edit-modal.reset-pending" +msgstr "If you save, this shortcut will return to the original value:" + #: src/app/main/ui/shortcuts.cljs msgid "shortcuts.edit-modal.success" msgstr "This key combination is available and can be assigned without conflicts." @@ -5846,6 +5846,22 @@ msgstr "Acction" msgid "restore-shortcuts.restore" msgstr "Restore" +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.diff-modal-title" +msgstr "Import shortcuts" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.diff-modal-text" +msgstr "The following shortcuts will be changed by the imported configuration." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.no-changes" +msgstr "No differences found between the current and imported shortcuts." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.apply" +msgstr "Apply" + #: src/app/main/ui/dashboard/subscription.cljs:191 msgid "subscription.banner.create-org-info" msgstr "" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 0660023ff4..28b9d2335d 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -5486,12 +5486,8 @@ msgid "shortcuts.start-editing" msgstr "Comenzar edición" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182 -msgid "shortcuts.start-measure" -msgstr "Comenzar medida" - -#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:183 -msgid "shortcuts.stop-measure" -msgstr "Terminar medida" +msgid "shortcuts.show-measure" +msgstr "Mostrar medidas" #: src/app/main/ui/workspace/sidebar/shortcuts.cljs:184 msgid "shortcuts.thumbnail-set" @@ -5565,6 +5561,10 @@ msgstr "Pulsa la combinación de teclas deseada" msgid "shortcuts.edit-modal.conflict" msgstr "Combinación usada en \"%s\". Guardar este atajo hará que se elimine allí." +#: src/app/main/ui/shortcuts.cljs +msgid "shortcuts.edit-modal.reset-pending" +msgstr "Si guardas, este atajo volvera a su valor original:" + #: src/app/main/ui/shortcuts.cljs msgid "shortcuts.edit-modal.success" msgstr "Esta combinación de teclas está disponible y puede asignarse sin conflictos." @@ -5701,6 +5701,22 @@ msgstr "Acción" msgid "restore-shortcuts.restore" msgstr "Restaurar" +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.diff-modal-title" +msgstr "Importar atajos" + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.diff-modal-text" +msgstr "Los siguientes atajos se modificarán con la configuración importada." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.no-changes" +msgstr "No se encontraron diferencias entre los atajos actuales y los importados." + +#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs +msgid "import-shortcuts.apply" +msgstr "Aplicar" + #: src/app/main/ui/dashboard/subscription.cljs:191 msgid "subscription.banner.create-org-info" msgstr ""