add/edit groups

This commit is contained in:
Antelle 2015-10-31 22:09:32 +03:00
parent b1620be716
commit 4f4c984620
27 changed files with 408 additions and 73 deletions

View File

@ -1,8 +1,4 @@
# MVP
- [ ] add/edit groups
# FUTURE
# TODO
- [ ] trash: groups/empty/untrash
- [ ] move groups/entries

View File

@ -22,6 +22,10 @@ _.extend(Backbone.View.prototype, {
return this;
},
isHidden: function() {
return this._hidden;
},
afterPaint: function(callback) {
this.requestAnimationFrame(function() {
this.requestAnimationFrame(callback);

View File

@ -4,6 +4,7 @@ var Backbone = require('backbone'),
AppSettingsModel = require('./app-settings-model'),
MenuModel = require('./menu/menu-model'),
EntryModel = require('./entry-model'),
GroupModel = require('./group-model'),
FileCollection = require('../collections/file-collection'),
EntryCollection = require('../collections/entry-collection');
@ -139,7 +140,7 @@ var AppModel = Backbone.Model.extend({
return filter;
},
createNewEntry: function() {
getFirstSelectedGroup: function() {
var selGroupId = this.filter.group;
var file, group;
if (selGroupId) {
@ -155,7 +156,17 @@ var AppModel = Backbone.Model.extend({
file = this.files.first();
group = file.get('groups').first();
}
return EntryModel.newEntry(group, file);
return { group: group, file: file };
},
createNewEntry: function() {
var sel = this.getFirstSelectedGroup();
return EntryModel.newEntry(sel.group, sel.file);
},
createNewGroup: function() {
var sel = this.getFirstSelectedGroup();
return GroupModel.newGroup(sel.group, sel.file);
}
});

View File

@ -245,6 +245,7 @@ var FileModel = Backbone.Model.extend({
this.db.meta.name = name;
this.db.meta.nameChanged = new Date();
this.set('name', name);
this.get('groups').first().setName(name);
this.setModified();
},

View File

@ -10,7 +10,9 @@ var GroupModel = MenuItemModel.extend({
defaults: _.extend({}, MenuItemModel.prototype.defaults, {
iconId: 0,
entries: null,
filterKey: 'group'
filterKey: 'group',
editable: true,
top: false
}),
initialize: function() {
@ -23,9 +25,6 @@ var GroupModel = MenuItemModel.extend({
var isRecycleBin = file.db.meta.recycleBinUuid && file.db.meta.recycleBinUuid.id === group.uuid.id;
this.set({
id: group.uuid.id,
title: group.name,
iconId: group.icon,
icon: this._iconFromId(group.icon),
expanded: true,
visible: !isRecycleBin,
items: new GroupCollection(),
@ -33,16 +32,25 @@ var GroupModel = MenuItemModel.extend({
}, { silent: true });
this.group = group;
this.file = file;
this._fillByGroup(true);
var items = this.get('items'),
entries = this.get('entries');
group.groups.forEach(function(subGroup) {
items.add(GroupModel.fromGroup(subGroup, file));
});
items.add(GroupModel.fromGroup(subGroup, file, this));
}, this);
group.entries.forEach(function(entry) {
entries.add(EntryModel.fromEntry(entry, this, file));
}, this);
},
_fillByGroup: function(silent) {
this.set({
title: this.group.name,
iconId: this.group.icon,
icon: this._iconFromId(this.group.icon)
}, { silent: silent });
},
_iconFromId: function(id) {
if (id === KdbxIcons.Folder || id === KdbxIcons.FolderOpen) {
return undefined;
@ -50,6 +58,14 @@ var GroupModel = MenuItemModel.extend({
return IconMap[id];
},
_groupModified: function() {
this.file.setModified();
if (this.isJustCreated) {
this.isJustCreated = false;
}
this.group.times.update();
},
forEachGroup: function(callback, includeDisabled) {
var result = true;
this.get('items').forEach(function(group) {
@ -74,12 +90,72 @@ var GroupModel = MenuItemModel.extend({
addEntry: function(entry) {
this.get('entries').add(entry);
},
removeGroup: function(group) {
this.get('items').remove(group);
},
addGroup: function(group) {
this.get('items').add(group);
this.trigger('insert', group);
},
setName: function(name) {
this._groupModified();
this.group.name = name;
this._fillByGroup();
},
setIcon: function(iconId) {
this._groupModified();
this.group.icon = iconId;
this._fillByGroup();
},
moveToTrash: function() {
this.file.setModified();
this.file.db.remove(this.group, this.parentGroup.group);
this.parentGroup.removeGroup(this);
var trashGroup = this.file.getTrashGroup();
if (trashGroup) {
//trashGroup.addGroup(this); // TODO: groups in trash are currently not displayed
this.parentGroup = trashGroup;
this.deleted = true;
}
this.trigger('delete');
},
removeWithoutHistory: function() {
var ix = this.parentGroup.group.groups.indexOf(this.group);
if (ix >= 0) {
this.parentGroup.group.groups.splice(ix, 1);
}
this.parentGroup.removeGroup(this);
}
});
GroupModel.fromGroup = function(group, file) {
GroupModel.fromGroup = function(group, file, parentGroup) {
var model = new GroupModel();
model.setFromGroup(group, file);
if (parentGroup) {
model.parentGroup = parentGroup;
} else {
model.set({ top: true }, { silent: true });
}
return model;
};
GroupModel.newGroup = function(group, file) {
var model = new GroupModel();
var grp = file.db.createGroup(group.group);
model.setFromGroup(grp, file);
model.group.times.update();
model.parentGroup = group;
model.unsaved = true;
model.isJustCreated = true;
group.addGroup(model);
file.setModified();
return model;
};

View File

@ -6,6 +6,7 @@ var Backbone = require('backbone'),
FooterView = require('../views/footer-view'),
ListView = require('../views/list-view'),
DetailsView = require('../views/details/details-view'),
GrpView = require('../views/grp-view'),
OpenView = require('../views/open-view'),
SettingsView = require('../views/settings/settings-view'),
Alerts = require('../comp/alerts'),
@ -37,6 +38,7 @@ var AppView = Backbone.View.extend({
this.views.listDrag = new DragView('x');
this.views.details = new DetailsView();
this.views.details.appModel = this.model;
this.views.grp = new GrpView();
this.views.menu.listenDrag(this.views.menuDrag);
this.views.list.listenDrag(this.views.listDrag);
@ -44,6 +46,7 @@ var AppView = Backbone.View.extend({
this.listenTo(this.model.settings, 'change:theme', this.setTheme);
this.listenTo(this.model.files, 'update reset', this.fileListUpdated);
this.listenTo(Backbone, 'select-all', this.selectAll);
this.listenTo(Backbone, 'menu-select', this.menuSelect);
this.listenTo(Backbone, 'lock-workspace', this.lockWorkspace);
this.listenTo(Backbone, 'show-file', this.showFileSettings);
@ -53,6 +56,7 @@ var AppView = Backbone.View.extend({
this.listenTo(Backbone, 'toggle-settings', this.toggleSettings);
this.listenTo(Backbone, 'toggle-menu', this.toggleMenu);
this.listenTo(Backbone, 'toggle-details', this.toggleDetails);
this.listenTo(Backbone, 'edit-group', this.editGroup);
this.listenTo(Backbone, 'launcher-open-file', this.launcherOpenFile);
window.onbeforeunload = this.beforeUnload.bind(this);
@ -71,6 +75,7 @@ var AppView = Backbone.View.extend({
this.views.list.setElement(this.$el.find('.app__list')).render();
this.views.listDrag.setElement(this.$el.find('.app__list-drag')).render();
this.views.details.setElement(this.$el.find('.app__details')).render();
this.views.grp.setElement(this.$el.find('.app__grp')).render().hide();
return this;
},
@ -80,6 +85,7 @@ var AppView = Backbone.View.extend({
this.views.list.hide();
this.views.listDrag.hide();
this.views.details.hide();
this.views.grp.hide();
this.views.footer.toggle(this.model.files.hasOpenFiles());
this.hideSettings();
this.hideOpenFile();
@ -103,6 +109,7 @@ var AppView = Backbone.View.extend({
this.views.list.show();
this.views.listDrag.show();
this.views.details.show();
this.views.grp.hide();
this.views.footer.show();
this.hideOpenFile();
this.hideSettings();
@ -130,6 +137,7 @@ var AppView = Backbone.View.extend({
this.views.list.hide();
this.views.listDrag.hide();
this.views.details.hide();
this.views.grp.hide();
this.hideOpenFile();
this.views.settings = new SettingsView();
this.views.settings.setElement(this.$el.find('.app__body')).render();
@ -140,6 +148,13 @@ var AppView = Backbone.View.extend({
this.views.menu.switchVisibility(false);
},
showEditGroup: function() {
this.views.list.hide();
this.views.listDrag.hide();
this.views.details.hide();
this.views.grp.show();
},
fileListUpdated: function() {
if (this.model.files.hasOpenFiles()) {
this.showEntries();
@ -193,8 +208,15 @@ var AppView = Backbone.View.extend({
}
},
menuSelect: function(item) {
this.model.menu.select(item);
selectAll: function() {
this.menuSelect({ item: this.model.menu.allItemsSection.get('items').first() });
},
menuSelect: function(opt) {
this.model.menu.select(opt);
if (!this.views.grp.isHidden()) {
this.showEntries();
}
},
lockWorkspace: function() {
@ -261,6 +283,15 @@ var AppView = Backbone.View.extend({
this.views.menu.switchVisibility(false);
},
editGroup: function(group) {
if (group && this.views.grp.isHidden()) {
this.showEditGroup();
this.views.grp.showGroup(group);
} else {
this.showEntries();
}
},
contextmenu: function(e) {
if (['input', 'textarea'].indexOf(e.target.tagName.toLowerCase()) < 0) {
e.preventDefault();

View File

@ -9,7 +9,7 @@ var Backbone = require('backbone'),
FieldViewReadOnly = require('../fields/field-view-read-only'),
FieldViewHistory = require('../fields/field-view-history'),
FieldViewCustom = require('../fields/field-view-custom'),
DetailsIconView = require('./details-icon-view'),
IconSelectView = require('../icon-select-view'),
DetailsHistoryView = require('./details-history-view'),
DetailsAttachmentView = require('./details-attachment-view'),
Keys = require('../../const/keys'),
@ -175,12 +175,12 @@ var DetailsView = Backbone.View.extend({
},
toggleIcons: function() {
if (this.views.sub && this.views.sub instanceof DetailsIconView) {
if (this.views.sub && this.views.sub instanceof IconSelectView) {
this.render();
return;
}
this.removeSubView();
var subView = new DetailsIconView({ el: this.scroller, model: this.model });
var subView = new IconSelectView({ el: this.scroller, model: this.model });
this.listenTo(subView, 'select', this.iconSelected);
subView.render();
this.pageResized();

View File

@ -0,0 +1,98 @@
'use strict';
var Backbone = require('backbone'),
Scrollable = require('../mixins/scrollable'),
IconSelectView = require('./icon-select-view'),
baron = require('baron');
var GrpView = Backbone.View.extend({
template: require('templates/grp.html'),
events: {
'click .grp__icon': 'showIconsSelect',
'click .grp__buttons-trash': 'moveToTrash',
'blur #grp__field-title': 'titleBlur'
},
initialize: function() {
this.views = {};
},
render: function() {
this.removeSubView();
if (this.model) {
this.$el.html(this.template({
title: this.model.get('title'),
icon: this.model.get('icon') || 'folder',
readonly: this.model.get('top')
}));
}
this.scroll = baron({
root: this.$el.find('.details__body')[0],
scroller: this.$el.find('.scroller')[0],
bar: this.$el.find('.scroller__bar')[0],
$: Backbone.$
});
this.scroller = this.$el.find('.scroller');
this.scrollerBar = this.$el.find('.scroller__bar');
this.scrollerBarWrapper = this.$el.find('.scroller__bar-wrapper');
this.pageResized();
return this;
},
removeSubView: function() {
if (this.views.sub) {
this.views.sub.remove();
delete this.views.sub;
}
},
showGroup: function(group) {
this.model = group;
this.render();
},
titleBlur: function(e) {
var title = $.trim(e.target.value);
if (title) {
if (!this.model.get('top') && e.target.value !== this.model.get('title')) {
this.model.setName(e.target.value);
}
} else {
if (this.model.isJustCreated) {
this.model.removeWithoutHistory();
Backbone.trigger('edit-group');
} else {
this.render();
}
}
},
showIconsSelect: function() {
if (this.views.sub) {
this.removeSubView();
} else {
var subView = new IconSelectView({el: this.$el.find('.grp__icons'), model: {iconId: this.model.get('iconId')}});
this.listenTo(subView, 'select', this.iconSelected);
subView.render();
this.views.sub = subView;
}
this.pageResized();
},
iconSelected: function(iconId) {
if (iconId !== this.model.get('iconId')) {
this.model.setIcon(iconId);
}
this.render();
},
moveToTrash: function() {
this.model.moveToTrash();
Backbone.trigger('select-all');
}
});
_.extend(GrpView.prototype, Scrollable);
module.exports = GrpView;

View File

@ -1,13 +1,13 @@
'use strict';
var Backbone = require('backbone'),
IconMap = require('../../const/icon-map');
IconMap = require('../const/icon-map');
var DetailsIconView = Backbone.View.extend({
template: require('templates/details/details-icon.html'),
var IconSelectView = Backbone.View.extend({
template: require('templates/icon-select.html'),
events: {
'click .details__icons-icon': 'iconClick'
'click .icon-select__icon': 'iconClick'
},
render: function() {
@ -26,4 +26,4 @@ var DetailsIconView = Backbone.View.extend({
}
});
module.exports = DetailsIconView;
module.exports = IconSelectView;

View File

@ -31,6 +31,7 @@ var ListView = Backbone.View.extend({
this.listenTo(this.views.search, 'select-prev', this.selectPrev);
this.listenTo(this.views.search, 'select-next', this.selectNext);
this.listenTo(this.views.search, 'create-entry', this.createEntry);
this.listenTo(this.views.search, 'create-group', this.createGroup);
this.listenTo(this, 'show', this.viewShown);
this.listenTo(this, 'hide', this.viewHidden);
this.listenTo(Backbone, 'filter', this.filterChanged);
@ -105,6 +106,11 @@ var ListView = Backbone.View.extend({
this.selectItem(newEntry);
},
createGroup: function() {
var newGroup = this.model.createNewGroup();
Backbone.trigger('edit-group', newGroup);
},
selectItem: function(item) {
this.items.setActive(item);
Backbone.trigger('select-entry', item);

View File

@ -13,7 +13,8 @@ var MenuItemView = Backbone.View.extend({
'mouseout': 'mouseout',
'click .menu__item-option': 'selectOption',
'click': 'selectItem',
'dblclick': 'expandItem'
'dblclick': 'expandItem',
'click .menu__item-edit': 'editItem'
},
iconEl: null,
@ -22,9 +23,12 @@ var MenuItemView = Backbone.View.extend({
initialize: function () {
this.itemViews = [];
this.listenTo(this.model, 'change:title', this.changeTitle);
this.listenTo(this.model, 'change:icon', this.changeIcon);
this.listenTo(this.model, 'change:active', this.changeActive);
this.listenTo(this.model, 'change:expanded', this.changeExpanded);
this.listenTo(this.model, 'change:cls', this.changeCls);
this.listenTo(this.model, 'delete', this.remove);
this.listenTo(this.model, 'insert', this.insertItem);
var shortcut = this.model.get('shortcut');
if (shortcut) {
KeyHandler.onKey(shortcut, this.selectItem, this, KeyHandler.SHORTCUT_OPT);
@ -42,15 +46,16 @@ var MenuItemView = Backbone.View.extend({
if (items && this.model.get('expanded')) {
items.forEach(function (item) {
if (item.get('visible')) {
var itemView = new MenuItemView({el: this.$el, model: item});
itemView.listenTo(itemView, 'select', this.itemSelected);
itemView.render();
this.itemViews.push(itemView);
this.insertItem(item);
}
}, this);
}
},
insertItem: function(item) {
this.itemViews.push(new MenuItemView({el: this.$el, model: item}).render());
},
remove : function() {
this.removeInnerViews();
var shortcut = this.model.get('shortcut');
@ -69,7 +74,11 @@ var MenuItemView = Backbone.View.extend({
},
changeTitle: function(model, title) {
this.$el.find('.menu__item-title').text(title);
this.$el.find('.menu__item-title').first().text(title);
},
changeIcon: function(model, icon) {
this.iconEl[0].className = 'menu__item-icon fa ' + (icon ? 'fa-' + icon : 'menu__item-icon--no-icon');
},
changeActive: function(model, active) {
@ -129,6 +138,13 @@ var MenuItemView = Backbone.View.extend({
this.model.toggleExpanded();
}
e.stopPropagation();
},
editItem: function(e) {
if (this.model.get('active') && this.model.get('editable')) {
e.stopPropagation();
Backbone.trigger('edit-group', this.model);
}
}
});

View File

@ -66,6 +66,17 @@
}
}
&__grp {
@include flex(1);
@include display(flex);
overflow: hidden;
padding: $base-spacing;
position: relative;
@include mobile {
padding: $base-padding;
}
}
&__footer {
@include flex(0 0 auto);
@include th { border-top: light-border(); }

View File

@ -365,25 +365,6 @@
}
}
&__icons {
@include display(flex);
@include align-items(flex-start);
@include flex-direction(row);
@include justify-content(flex-start);
@include flex-wrap(wrap);
@include user-select(none);
&-icon {
@include area-selectable(bottom);
width: 26px;
text-align: center;
font-size: 20px;
padding: 10px;
&.details__icons-icon--active {
@include area-selected(bottom);
}
}
}
&__history {
@include flex(1 0 auto);
@include display(flex);

View File

@ -0,0 +1,38 @@
.grp {
@include flex(1);
@include display(flex);
@include align-items(stretch);
@include flex-direction(column);
@include justify-content(flex-start);
@include scrollbar-on-hover;
width: 100%;
user-select: none;
>.scroller {
@include flex(1);
@include display(flex);
@include align-items(stretch);
@include flex-direction(column);
@include justify-content(flex-start);
overflow-x: hidden;
padding-top: 3px;
}
&__icon {
display: block;
font-size: $large-header-font-size;
padding: $base-padding-px;
@include align-self(flex-start);
@include area-selectable();
}
&__buttons {
@include display(flex);
@include flex-direction(row);
margin-top: $base-padding-v;
&-trash {
@include icon-btn($error:true);
}
}
}

View File

@ -44,6 +44,7 @@
&__item {
text-overflow: ellipsis;
overflow: hidden;
position: relative;
@include display(flex);
@include align-items(stretch);
@include flex-direction(column);
@ -121,6 +122,26 @@
}
}
&-edit {
display: none;
opacity: 0;
position: absolute;
right: .8em;
top: .6em;
cursor: pointer;
transition: opacity $base-duration $base-timing, color $base-duration $base-timing;
@include th {
color: muted-color();
&:hover { color: medium-color(); }
}
.menu__item--active>.menu__item-body>& {
display: block;
}
.menu__item--active>.menu__item-body:hover>& {
opacity: .5;
}
}
.fa {
margin-right: $base-padding-h / 2;
}

View File

@ -53,13 +53,6 @@
}
}
&__select, &__input, &__pre, &__file-master-pass-label {
width: 60%;
@include tablet {
width: calc(100% - 20px);
}
}
&__select, &__input {
height: 2em;
}

View File

@ -243,3 +243,12 @@ $thumb-size: 14px;
@include th { background: text-color(); }
}
}
.input-base {
width: 60%;
@include tablet {
width: calc(100% - 20px);
}
}

View File

@ -0,0 +1,18 @@
&.icon-select {
@include display(flex);
@include align-items(flex-start);
@include flex-direction(row);
@include justify-content(flex-start);
@include flex-wrap(wrap);
@include user-select(none);
&__icon {
@include area-selectable(bottom);
width: 26px;
text-align: center;
font-size: 20px;
padding: 10px;
&.icon-select__icon--active {
@include area-selected(bottom);
}
}
}

View File

@ -16,12 +16,14 @@
@import "common/empty";
@import "common/fx";
@import "common/help-tip";
@import "common/icon-select";
@import "common/modal";
@import "common/scroll";
@import "areas/app";
@import "areas/details";
@import "areas/footer";
@import "areas/grp";
@import "areas/generator";
@import "areas/list";
@import "areas/menu";

View File

@ -5,6 +5,7 @@
<div class="app__list"></div>
<div class="app__list-drag"></div>
<div class="app__details"></div>
<div class="app__grp"></div>
</div>
<div class="app__footer"></div>
</div>

View File

@ -1,5 +0,0 @@
<div class="details__icons">
<% icons.forEach(function(icon, ix) { %>
<i class="fa fa-<%= icon %> details__icons-icon <%= ix === sel ? 'details__icons-icon--active' : '' %>" data-val="<%= ix %>"></i>
<% }); %>
</div>

19
app/templates/grp.html Normal file
View File

@ -0,0 +1,19 @@
<div class="grp">
<div class="scroller">
<h1>Group</h1>
<div class="grp__field">
<label for="grp__field-title">Name:</label>
<input type="text" class="input-base" id="grp__field-title" value="<%- title %>" size="50" maxlength="1024"
required <%= readonly ? 'readonly' : '' %> />
</div>
<label>Icon:</label>
<i class="fa fa-<%- icon %> grp__icon"></i>
<div class="grp__icons"></div>
</div>
<div class="scroller__bar-wrapper"><div class="scroller__bar"></div></div>
<% if (!readonly) { %>
<div class="grp__buttons">
<i class="grp__buttons-trash fa fa-trash-o"></i>
</div>
<% } %>
</div>

View File

@ -0,0 +1,5 @@
<div class="icon-select">
<% icons.forEach(function(icon, ix) { %>
<i class="fa fa-<%= icon %> icon-select__icon <%= ix === sel ? 'icon-select__icon--active' : '' %>" data-val="<%= ix %>"></i>
<% }); %>
</div>

View File

@ -14,5 +14,8 @@
<% }); %>
</div>
<% } %>
</div>
<% if (typeof editable !== 'undefined') { %>
<i class="menu__item-edit fa fa-cog"></i>
<% } %>
</i>
</div>

View File

@ -24,34 +24,34 @@
</div>
<h2>Settings</h2>
<label for="settings__file-master-pass" class="settings__file-master-pass-label">Master password:
<label for="settings__file-master-pass" class="settings__file-master-pass-label input-base">Master password:
<span class="settings__file-master-pass-warning">
<i class="fa fa-warning"></i> password was changed; leave the field blank to use old password
</span>
</label>
<input type="password" class="settings__input" id="settings__file-master-pass" value="<%= password %>" />
<input type="password" class="settings__input input-base" id="settings__file-master-pass" value="<%= password %>" />
<label for="settings__file-key-file">Key file:</label>
<select class="settings__select settings__select-no-margin" id="settings__file-key-file"></select>
<select class="settings__select settings__select-no-margin input-base" id="settings__file-key-file"></select>
<a id="settings__file-file-select-link">Select a key file</a>
<input type="file" accept=".key" id="settings__file-file-select" class="hide" />
<h2>Names</h2>
<label for="settings__file-name">Name:</label>
<input type="text" class="settings__input" id="settings__file-name" value="<%- name %>" required />
<input type="text" class="settings__input input-base" id="settings__file-name" value="<%- name %>" required />
<label for="settings__file-def-user">Default username:</label>
<input type="text" class="settings__input" id="settings__file-def-user" value="<%- defaultUser %>" />
<input type="text" class="settings__input input-base" id="settings__file-def-user" value="<%- defaultUser %>" />
<h2>History</h2>
<div>
<input type="checkbox" class="settings__input" id="settings__file-trash" <%- recycleBinEnabled ? 'checked' : '' %> />
<input type="checkbox" class="settings__input input-base" id="settings__file-trash" <%- recycleBinEnabled ? 'checked' : '' %> />
<label for="settings__file-trash">Enable trash</label>
</div>
<label for="settings__file-hist-len">History length, keep last records per entry:</label>
<input type="text" pattern="\d+" required class="settings__input" id="settings__file-hist-len" value="<%= historyMaxItems %>" />
<input type="text" pattern="\d+" required class="settings__input input-base" id="settings__file-hist-len" value="<%= historyMaxItems %>" />
<label for="settings__file-hist-size">History size, total MB per file:</label>
<input type="text" pattern="\d+" required class="settings__input" id="settings__file-hist-size" value="<%= historyMaxSize %>" />
<input type="text" pattern="\d+" required class="settings__input input-base" id="settings__file-hist-size" value="<%= historyMaxSize %>" />
<h2>Advanced</h2>
<label for="settings__file-key-rounds">Key encryption rounds:</label>
<input type="text" pattern="\d+" required class="settings__input" id="settings__file-key-rounds" value="<%= keyEncryptionRounds %>" />
<input type="text" pattern="\d+" required class="settings__input input-base" id="settings__file-key-rounds" value="<%= keyEncryptionRounds %>" />
</div>

View File

@ -3,7 +3,7 @@
<h2>Appearance</h2>
<div>
<label for="settings__general-theme">Theme:</label>
<select class="settings__general-theme settings__select" id="settings__general-theme">
<select class="settings__general-theme settings__select input-base" id="settings__general-theme">
<% _.forEach(themes, function(name, key) { %>
<option value="<%= key %>" <%= key === activeTheme ? 'selected' : '' %>><%- name %></option>
<% }); %>

View File

@ -10,7 +10,7 @@
or <a href="http://antelle.net/" target="_blank">contact a developer</a> directly.
</p>
<p>App information:</p>
<pre class="settings__pre"><%= appInfo %></pre>
<pre class="settings__pre input-base"><%= appInfo %></pre>
<h2>Other platforms</h2>
<ul>
<li>