mirror of https://github.com/keeweb/keeweb.git
fix #1243: auto-type any field
This commit is contained in:
parent
9e5d6403fa
commit
79dd95facd
|
@ -9,7 +9,6 @@ const Logger = require('../util/logger');
|
|||
const Locale = require('../util/locale');
|
||||
const Timeouts = require('../const/timeouts');
|
||||
const AppSettingsModel = require('../models/app-settings-model');
|
||||
const AutoTypeSequenceType = require('../const/autotype-sequencetype');
|
||||
|
||||
const logger = new Logger('auto-type');
|
||||
const clearTextAutoTypeLog = localStorage.autoTypeDebug;
|
||||
|
@ -73,14 +72,7 @@ const AutoType = {
|
|||
|
||||
run(result, callback) {
|
||||
this.running = true;
|
||||
let sequence;
|
||||
if (result.sequenceType === AutoTypeSequenceType.PASSWORD) {
|
||||
sequence = '{PASSWORD}';
|
||||
} else if (result.sequenceType === AutoTypeSequenceType.USERNAME) {
|
||||
sequence = '{USERNAME}';
|
||||
} else {
|
||||
sequence = result.entry.getEffectiveAutoTypeSeq();
|
||||
}
|
||||
const sequence = result.sequence || result.entry.getEffectiveAutoTypeSeq();
|
||||
logger.debug('Start', sequence);
|
||||
const ts = logger.ts();
|
||||
try {
|
||||
|
|
|
@ -7,6 +7,7 @@ const shortcutKeyProp = navigator.platform.indexOf('Mac') >= 0 ? 'metaKey' : 'ct
|
|||
const KeyHandler = {
|
||||
SHORTCUT_ACTION: 1,
|
||||
SHORTCUT_OPT: 2,
|
||||
SHORTCUT_SHIFT: 3,
|
||||
|
||||
shortcuts: {},
|
||||
modal: false,
|
||||
|
@ -57,7 +58,7 @@ const KeyHandler = {
|
|||
const keyShortcuts = this.shortcuts[code];
|
||||
if (keyShortcuts && keyShortcuts.length) {
|
||||
for (const sh of keyShortcuts) {
|
||||
if (this.modal && !sh.modal) {
|
||||
if (this.modal && sh.modal !== this.modal) {
|
||||
e.stopPropagation();
|
||||
continue;
|
||||
}
|
||||
|
@ -73,13 +74,18 @@ const KeyHandler = {
|
|||
continue;
|
||||
}
|
||||
break;
|
||||
case this.SHORTCUT_SHIFT:
|
||||
if (!e.shiftKey) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case this.SHORTCUT_ACTION + this.SHORTCUT_OPT:
|
||||
if (!e.altKey || !isActionKey) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (e.metaKey || e.ctrlKey || e.altKey) {
|
||||
if (e.metaKey || e.ctrlKey || e.altKey || e.shiftKey) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
const AutoTypeSequenceType = {
|
||||
DEFAULT: 0,
|
||||
USERNAME: 1,
|
||||
PASSWORD: 2
|
||||
};
|
||||
|
||||
module.exports = AutoTypeSequenceType;
|
|
@ -318,6 +318,8 @@
|
|||
"autoTypeSelectionHint": "Type the autotype sequence",
|
||||
"autoTypeSelectionHintAction": "Only type the password",
|
||||
"autoTypeSelectionHintOpt": "Only type the username",
|
||||
"autoTypeSelectionHintShift": "Other fields",
|
||||
"autoTypeSelectionOtp": "One-time password",
|
||||
|
||||
"appSecWarn": "Not Secure!",
|
||||
"appSecWarnBody1": "You have loaded this app with an insecure connection. Someone may be watching you and stealing your passwords. We strongly advise you to stop, unless you clearly understand what you're doing.",
|
||||
|
|
|
@ -23,6 +23,9 @@ const FeatureDetector = {
|
|||
altShortcutSymbol(formatting) {
|
||||
return this.isMac ? '⌥' : formatting ? '<span class="thin">alt + </span>' : 'alt-';
|
||||
},
|
||||
shiftShortcutSymbol(formatting) {
|
||||
return this.isMac ? '⇧' : formatting ? '<span class="thin">shift + </span>' : 'shift-';
|
||||
},
|
||||
globalShortcutSymbol(formatting) {
|
||||
return this.isMac
|
||||
? '⌃⌥'
|
||||
|
|
|
@ -5,8 +5,9 @@ const Locale = require('../../util/locale');
|
|||
const AppSettingsModel = require('../../models/app-settings-model');
|
||||
const EntryPresenter = require('../../presenters/entry-presenter');
|
||||
const Scrollable = require('../../mixins/scrollable');
|
||||
const AutoTypeSequenceType = require('../../const/autotype-sequencetype');
|
||||
const FeatureDetector = require('../../util/feature-detector');
|
||||
const DropdownView = require('../dropdown-view');
|
||||
const Format = require('../../util/format');
|
||||
|
||||
const AutoTypePopupView = Backbone.View.extend({
|
||||
el: 'body',
|
||||
|
@ -16,13 +17,15 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
|
||||
events: {
|
||||
'click .at-select__header-filter-clear': 'clearFilterText',
|
||||
'click .at-select__item': 'itemClicked'
|
||||
'click .at-select__item': 'itemClicked',
|
||||
'contextmenu .at-select__item': 'itemRightClicked'
|
||||
},
|
||||
|
||||
result: null,
|
||||
entries: null,
|
||||
|
||||
initialize() {
|
||||
this.views = {};
|
||||
this.initScroll();
|
||||
this.listenTo(Backbone, 'main-window-blur', this.mainWindowBlur);
|
||||
this.listenTo(Backbone, 'main-window-will-close', this.mainWindowWillClose);
|
||||
|
@ -30,31 +33,38 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
setupKeys() {
|
||||
KeyHandler.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, this, false, true);
|
||||
KeyHandler.onKey(Keys.DOM_VK_RETURN, this.enterPressed, this, false, true);
|
||||
KeyHandler.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, this, false, 'auto-type');
|
||||
KeyHandler.onKey(Keys.DOM_VK_RETURN, this.enterPressed, this, false, 'auto-type');
|
||||
KeyHandler.onKey(
|
||||
Keys.DOM_VK_RETURN,
|
||||
this.actionEnterPressed,
|
||||
this,
|
||||
KeyHandler.SHORTCUT_ACTION,
|
||||
true
|
||||
'auto-type'
|
||||
);
|
||||
KeyHandler.onKey(
|
||||
Keys.DOM_VK_RETURN,
|
||||
this.optEnterPressed,
|
||||
this,
|
||||
KeyHandler.SHORTCUT_OPT,
|
||||
true
|
||||
'auto-type'
|
||||
);
|
||||
KeyHandler.onKey(Keys.DOM_VK_UP, this.upPressed, this, false, true);
|
||||
KeyHandler.onKey(Keys.DOM_VK_DOWN, this.downPressed, this, false, true);
|
||||
KeyHandler.onKey(Keys.DOM_VK_BACK_SPACE, this.backSpacePressed, this, false, true);
|
||||
KeyHandler.onKey(
|
||||
Keys.DOM_VK_RETURN,
|
||||
this.shiftEnterPressed,
|
||||
this,
|
||||
KeyHandler.SHORTCUT_SHIFT,
|
||||
'auto-type'
|
||||
);
|
||||
KeyHandler.onKey(Keys.DOM_VK_UP, this.upPressed, this, false, 'auto-type');
|
||||
KeyHandler.onKey(Keys.DOM_VK_DOWN, this.downPressed, this, false, 'auto-type');
|
||||
KeyHandler.onKey(Keys.DOM_VK_BACK_SPACE, this.backSpacePressed, this, false, 'auto-type');
|
||||
KeyHandler.onKey(
|
||||
Keys.DOM_VK_O,
|
||||
this.openKeyPressed,
|
||||
this,
|
||||
KeyHandler.SHORTCUT_ACTION,
|
||||
true
|
||||
'auto-type'
|
||||
);
|
||||
KeyHandler.on('keypress:auto-type', this.keyPressed.bind(this));
|
||||
KeyHandler.setModal('auto-type');
|
||||
|
@ -65,6 +75,7 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
KeyHandler.offKey(Keys.DOM_VK_RETURN, this.enterPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_RETURN, this.actionEnterPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_RETURN, this.optEnterPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_RETURN, this.shiftEnterPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_UP, this.upPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_DOWN, this.downPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_BACK_SPACE, this.backSpacePressed, this);
|
||||
|
@ -96,12 +107,10 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
this.renderTemplate({
|
||||
filterText: this.model.filter.text,
|
||||
topMessage,
|
||||
selectionHintDefault: Locale.autoTypeSelectionHint,
|
||||
selectionHintAction: Locale.autoTypeSelectionHintAction,
|
||||
selectionHintOpt: Locale.autoTypeSelectionHintOpt,
|
||||
itemsHtml,
|
||||
actionSymbol: FeatureDetector.actionShortcutSymbol(true),
|
||||
altSymbol: FeatureDetector.altShortcutSymbol(true),
|
||||
shiftSymbol: FeatureDetector.shiftShortcutSymbol(true),
|
||||
keyEnter: Locale.keyEnter
|
||||
});
|
||||
document.activeElement.blur();
|
||||
|
@ -123,13 +132,10 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
this.trigger('result', this.result);
|
||||
},
|
||||
|
||||
closeWithResult(sequenceType) {
|
||||
if (!sequenceType) {
|
||||
sequenceType = AutoTypeSequenceType.DEFAULT;
|
||||
}
|
||||
closeWithResult(sequence) {
|
||||
this.trigger('result', {
|
||||
entry: this.result,
|
||||
sequenceType
|
||||
sequence
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -146,11 +152,11 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
actionEnterPressed() {
|
||||
this.closeWithResult(AutoTypeSequenceType.PASSWORD);
|
||||
this.closeWithResult('{PASSWORD}');
|
||||
},
|
||||
|
||||
optEnterPressed() {
|
||||
this.closeWithResult(AutoTypeSequenceType.USERNAME);
|
||||
this.closeWithResult('{USERNAME}');
|
||||
},
|
||||
|
||||
openKeyPressed() {
|
||||
|
@ -158,6 +164,11 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
this.trigger('show-open-files');
|
||||
},
|
||||
|
||||
shiftEnterPressed(e) {
|
||||
const activeItem = this.$el.find('.at-select__item[data-id="' + this.result.id + '"]');
|
||||
this.showItemOptions(activeItem, e);
|
||||
},
|
||||
|
||||
upPressed(e) {
|
||||
e.preventDefault();
|
||||
const activeIndex = this.entries.indexOf(this.result) - 1;
|
||||
|
@ -190,7 +201,7 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
keyPressed(e) {
|
||||
if (e.which) {
|
||||
if (e.which && e.which !== Keys.DOM_VK_RETURN) {
|
||||
this.model.filter.text += String.fromCharCode(e.which);
|
||||
this.render();
|
||||
}
|
||||
|
@ -213,9 +224,20 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
|
||||
itemClicked(e) {
|
||||
const itemEl = $(e.target).closest('.at-select__item');
|
||||
const id = itemEl.data('id');
|
||||
this.result = this.entries.get(id);
|
||||
this.closeWithResult();
|
||||
const optionsClicked = $(e.target).closest('.at-select__item-options');
|
||||
|
||||
if (optionsClicked) {
|
||||
this.showItemOptions(itemEl, e);
|
||||
} else {
|
||||
const id = itemEl.data('id');
|
||||
this.result = this.entries.get(id);
|
||||
this.closeWithResult();
|
||||
}
|
||||
},
|
||||
|
||||
itemRightClicked(e) {
|
||||
const itemEl = $(e.target).closest('.at-select__item');
|
||||
this.showItemOptions(itemEl, e);
|
||||
},
|
||||
|
||||
mainWindowBlur() {
|
||||
|
@ -225,6 +247,99 @@ const AutoTypePopupView = Backbone.View.extend({
|
|||
mainWindowWillClose(e) {
|
||||
e.preventDefault();
|
||||
this.cancelAndClose();
|
||||
},
|
||||
|
||||
showItemOptions(itemEl, event) {
|
||||
if (event) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
const id = itemEl.data('id');
|
||||
const entry = this.entries.get(id);
|
||||
|
||||
if (this.views.optionsDropdown) {
|
||||
this.hideItemOptionsDropdown();
|
||||
if (this.result && this.result.id === entry.id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.result = entry;
|
||||
if (!itemEl.hasClass('at-select__item--active')) {
|
||||
this.highlightActive();
|
||||
}
|
||||
|
||||
const view = new DropdownView();
|
||||
this.listenTo(view, 'cancel', this.hideItemOptionsDropdown);
|
||||
this.listenTo(view, 'select', this.itemOptionsDropdownSelect);
|
||||
|
||||
const options = [];
|
||||
|
||||
if (entry.fields.otp) {
|
||||
options.push({
|
||||
value: '{TOTP}',
|
||||
icon: 'clock-o',
|
||||
text: Locale.autoTypeSelectionOtp
|
||||
});
|
||||
}
|
||||
if (entry.user) {
|
||||
options.push({
|
||||
value: '{USERNAME}',
|
||||
icon: 'user',
|
||||
text: Format.capFirst(Locale.user)
|
||||
});
|
||||
}
|
||||
if (entry.password) {
|
||||
options.push({
|
||||
value: '{PASSWORD}',
|
||||
icon: 'key',
|
||||
text: Format.capFirst(Locale.password)
|
||||
});
|
||||
}
|
||||
|
||||
for (const field of Object.keys(entry.fields)) {
|
||||
if (field !== 'otp') {
|
||||
options.push({
|
||||
value: `{S:${field}}`,
|
||||
icon: 'th-list',
|
||||
text: field
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let position;
|
||||
if (event && event.button === 2) {
|
||||
position = {
|
||||
top: event.pageY,
|
||||
left: event.pageX
|
||||
};
|
||||
} else {
|
||||
const targetElRect = itemEl[0].getBoundingClientRect();
|
||||
position = {
|
||||
top: targetElRect.bottom,
|
||||
right: targetElRect.right
|
||||
};
|
||||
}
|
||||
|
||||
view.render({
|
||||
position,
|
||||
options
|
||||
});
|
||||
|
||||
this.views.optionsDropdown = view;
|
||||
},
|
||||
|
||||
hideItemOptionsDropdown() {
|
||||
if (this.views.optionsDropdown) {
|
||||
this.views.optionsDropdown.remove();
|
||||
delete this.views.optionsDropdown;
|
||||
}
|
||||
},
|
||||
|
||||
itemOptionsDropdownSelect(e) {
|
||||
this.hideItemOptionsDropdown();
|
||||
const sequence = e.item;
|
||||
this.closeWithResult(sequence);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
const Backbone = require('backbone');
|
||||
const Keys = require('../const/keys');
|
||||
const KeyHandler = require('../comp/key-handler');
|
||||
|
||||
const DropdownView = Backbone.View.extend({
|
||||
template: require('templates/dropdown.hbs'),
|
||||
|
@ -8,9 +10,19 @@ const DropdownView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
initialize() {
|
||||
Backbone.trigger('dropdown-shown');
|
||||
this.bodyClick = this.bodyClick.bind(this);
|
||||
this.listenTo(Backbone, 'show-context-menu', this.bodyClick);
|
||||
$('body').on('click contextmenu keyup', this.bodyClick);
|
||||
|
||||
this.listenTo(Backbone, 'show-context-menu dropdown-shown', this.bodyClick);
|
||||
$('body').on('click contextmenu keydown', this.bodyClick);
|
||||
|
||||
KeyHandler.onKey(Keys.DOM_VK_UP, this.upPressed, this, false, 'dropdown');
|
||||
KeyHandler.onKey(Keys.DOM_VK_DOWN, this.downPressed, this, false, 'dropdown');
|
||||
KeyHandler.onKey(Keys.DOM_VK_RETURN, this.enterPressed, this, false, 'dropdown');
|
||||
KeyHandler.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, this, false, 'dropdown');
|
||||
|
||||
this.prevModal = KeyHandler.modal === 'dropdown' ? undefined : KeyHandler.modal;
|
||||
KeyHandler.setModal('dropdown');
|
||||
},
|
||||
|
||||
render(config) {
|
||||
|
@ -33,11 +45,27 @@ const DropdownView = Backbone.View.extend({
|
|||
|
||||
remove() {
|
||||
this.viewRemoved = true;
|
||||
$('body').off('click contextmenu keyup', this.bodyClick);
|
||||
|
||||
$('body').off('click contextmenu keydown', this.bodyClick);
|
||||
|
||||
KeyHandler.offKey(Keys.DOM_VK_UP, this.upPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_DOWN, this.downPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_RETURN, this.enterPressed, this);
|
||||
KeyHandler.offKey(Keys.DOM_VK_ESCAPE, this.escPressed, this);
|
||||
|
||||
KeyHandler.setModal(this.prevModal);
|
||||
|
||||
Backbone.View.prototype.remove.apply(this);
|
||||
},
|
||||
|
||||
bodyClick() {
|
||||
bodyClick(e) {
|
||||
if (
|
||||
[Keys.DOM_VK_UP, Keys.DOM_VK_DOWN, Keys.DOM_VK_RETURN, Keys.DOM_VK_ESCAPE].includes(
|
||||
e.which
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (!this.viewRemoved) {
|
||||
this.trigger('cancel');
|
||||
}
|
||||
|
@ -48,6 +76,48 @@ const DropdownView = Backbone.View.extend({
|
|||
const el = $(e.target).closest('.dropdown__item');
|
||||
const selected = el.data('value');
|
||||
this.trigger('select', { item: selected, el });
|
||||
},
|
||||
|
||||
upPressed(e) {
|
||||
e.preventDefault();
|
||||
if (!this.selectedOption) {
|
||||
this.selectedOption = this.options.length - 1;
|
||||
} else {
|
||||
this.selectedOption--;
|
||||
}
|
||||
this.renderSelectedOption();
|
||||
},
|
||||
|
||||
downPressed(e) {
|
||||
e.preventDefault();
|
||||
if (this.selectedOption === undefined || this.selectedOption === this.options.length - 1) {
|
||||
this.selectedOption = 0;
|
||||
} else {
|
||||
this.selectedOption++;
|
||||
}
|
||||
this.renderSelectedOption();
|
||||
},
|
||||
|
||||
renderSelectedOption() {
|
||||
this.$el.find('.dropdown__item').removeClass('dropdown__item--active');
|
||||
this.$el
|
||||
.find(`.dropdown__item:nth(${this.selectedOption})`)
|
||||
.addClass('dropdown__item--active');
|
||||
},
|
||||
|
||||
enterPressed() {
|
||||
if (!this.viewRemoved && this.selectedOption !== undefined) {
|
||||
const el = this.$el.find(`.dropdown__item:nth(${this.selectedOption})`);
|
||||
const selected = el.data('value');
|
||||
this.trigger('select', { item: selected, el });
|
||||
}
|
||||
},
|
||||
|
||||
escPressed(e) {
|
||||
e.stopImmediatePropagation();
|
||||
if (!this.viewRemoved) {
|
||||
this.trigger('cancel');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@ const ModalView = Backbone.View.extend({
|
|||
|
||||
initialize() {
|
||||
if (typeof this.model.esc === 'string') {
|
||||
KeyHandler.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, this, false, true);
|
||||
KeyHandler.onKey(Keys.DOM_VK_ESCAPE, this.escPressed, this, false, 'alert');
|
||||
}
|
||||
if (typeof this.model.enter === 'string') {
|
||||
KeyHandler.onKey(Keys.DOM_VK_RETURN, this.enterPressed, this, false, true);
|
||||
KeyHandler.onKey(Keys.DOM_VK_RETURN, this.enterPressed, this, false, 'alert');
|
||||
}
|
||||
KeyHandler.setModal('alert');
|
||||
},
|
||||
|
|
|
@ -93,18 +93,18 @@
|
|||
text-overflow: ellipsis;
|
||||
word-wrap: break-word;
|
||||
&:first-of-type {
|
||||
width: 1em;
|
||||
width: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
width: 50%;
|
||||
}
|
||||
&:nth-of-type(3) {
|
||||
width: 25%;
|
||||
}
|
||||
&:nth-of-type(4) {
|
||||
width: 25%;
|
||||
}
|
||||
&:nth-of-type(5) {
|
||||
width: 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__item {
|
||||
|
@ -114,6 +114,17 @@
|
|||
@include area-selected(right);
|
||||
cursor: pointer;
|
||||
}
|
||||
&--active {
|
||||
}
|
||||
&-options {
|
||||
text-align: center;
|
||||
&:hover {
|
||||
background: var(--background-color);
|
||||
.at-select__item--active & {
|
||||
background: var(--action-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&__empty-title {
|
||||
align-self: center;
|
||||
|
|
|
@ -51,6 +51,12 @@ body.th-macdark {
|
|||
.at-select__table .at-select__item.at-select__item--active {
|
||||
background-color: var(--selected-item-color);
|
||||
}
|
||||
.at-select__item--active .at-select__item-options:hover {
|
||||
background: var(--secondary-background-color);
|
||||
}
|
||||
.dropdown__item--active, .dropdown__item--active:hover {
|
||||
background-color: var(--selected-item-color);
|
||||
}
|
||||
@include nomobile {
|
||||
.list__item--active,
|
||||
.list__item--active:hover {
|
||||
|
|
|
@ -9,4 +9,7 @@
|
|||
<td>{{#if title}}{{title}}{{else}}({{res 'noTitle'}}){{/if}}</td>
|
||||
<td>{{user}}</td>
|
||||
<td>{{url}}</td>
|
||||
<td class="at-select__item-options">
|
||||
<i class="fa fa-ellipsis-h"></i>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
<div class="at-select__header">
|
||||
<h1 class="at-select__header-text">{{res 'autoTypeHeader'}}</h1>
|
||||
<div class="at-select__hint">
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{keyEnter}}</span>: {{selectionHintDefault}}</div>
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{{actionSymbol}}} {{keyEnter}}</span>: {{selectionHintAction}}</div>
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{{altSymbol}}} {{keyEnter}}</span>: {{selectionHintOpt}}</div>
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{keyEnter}}</span>: {{res 'autoTypeSelectionHint'}}</div>
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{{actionSymbol}}} {{keyEnter}}</span>: {{res 'autoTypeSelectionHintAction'}}</div>
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{{altSymbol}}} {{keyEnter}}</span>: {{res 'autoTypeSelectionHintOpt'}}</div>
|
||||
<div class="at-select__hint-text"><span class="shortcut">{{{shiftSymbol}}} {{keyEnter}}</span>: {{res 'autoTypeSelectionHintShift'}}</div>
|
||||
</div>
|
||||
{{#if filterText}}
|
||||
<div class="at-select__header-filter">
|
||||
|
|
|
@ -3,6 +3,7 @@ Release notes
|
|||
##### v1.11.0 (TBD)
|
||||
`+` #1226: 7-digit Authy OTP support
|
||||
`+` #107: multiline custom fields support
|
||||
`+` #1243: auto-type any field
|
||||
`-` fix #764: multiple attachments display
|
||||
`-` fix multi-line fields display in history
|
||||
|
||||
|
|
Loading…
Reference in New Issue