22
Gruntfile.js
@@ -70,7 +70,7 @@ module.exports = function(grunt) {
|
|||||||
},
|
},
|
||||||
app: {
|
app: {
|
||||||
src: [
|
src: [
|
||||||
'node_modules/sn-models/dist/sn-models.js',
|
'node_modules/snjs/dist/snjs.js',
|
||||||
'app/assets/javascripts/app/*.js',
|
'app/assets/javascripts/app/*.js',
|
||||||
'app/assets/javascripts/app/models/**/*.js',
|
'app/assets/javascripts/app/models/**/*.js',
|
||||||
'app/assets/javascripts/app/controllers/**/*.js',
|
'app/assets/javascripts/app/controllers/**/*.js',
|
||||||
@@ -136,7 +136,7 @@ module.exports = function(grunt) {
|
|||||||
singleQuotes: true,
|
singleQuotes: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
neeto: {
|
sn: {
|
||||||
files: {
|
files: {
|
||||||
'dist/javascripts/compiled.js': 'dist/javascripts/compiled.js',
|
'dist/javascripts/compiled.js': 'dist/javascripts/compiled.js',
|
||||||
},
|
},
|
||||||
@@ -149,6 +149,19 @@ module.exports = function(grunt) {
|
|||||||
dest: 'dist/javascripts/compiled.min.js'
|
dest: 'dist/javascripts/compiled.min.js'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ngconstant: {
|
||||||
|
options: {
|
||||||
|
name: 'app',
|
||||||
|
dest: 'app/assets/javascripts/app/constants.js',
|
||||||
|
deps: false,
|
||||||
|
constants: {
|
||||||
|
appVersion: grunt.file.readJSON('package.json').version
|
||||||
|
}
|
||||||
|
},
|
||||||
|
build: {
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
grunt.loadNpmTasks('grunt-newer');
|
grunt.loadNpmTasks('grunt-newer');
|
||||||
@@ -162,10 +175,13 @@ module.exports = function(grunt) {
|
|||||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||||
grunt.loadNpmTasks('grunt-babel');
|
grunt.loadNpmTasks('grunt-babel');
|
||||||
grunt.loadNpmTasks('grunt-browserify');
|
grunt.loadNpmTasks('grunt-browserify');
|
||||||
|
grunt.loadNpmTasks('grunt-ng-constant');
|
||||||
|
|
||||||
grunt.registerTask('default', ['haml', 'ngtemplates', 'sass', 'concat:app', 'babel', 'browserify',
|
grunt.registerTask('default', ['haml', 'ngtemplates', 'sass', 'concat:app', 'babel', 'browserify',
|
||||||
'concat:lib', 'concat:dist', 'ngAnnotate', 'concat:css', 'uglify']);
|
'concat:lib', 'concat:dist', 'ngAnnotate', 'concat:css', 'uglify', 'ngconstant:build']);
|
||||||
|
|
||||||
grunt.registerTask('vendor', ['concat:app', 'sass', 'babel', 'browserify',
|
grunt.registerTask('vendor', ['concat:app', 'sass', 'babel', 'browserify',
|
||||||
'concat:lib', 'concat:dist', 'ngAnnotate', 'concat:css', 'uglify']);
|
'concat:lib', 'concat:dist', 'ngAnnotate', 'concat:css', 'uglify']);
|
||||||
|
|
||||||
|
grunt.registerTask('constants', ['ngconstant:build'])
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -55,11 +55,6 @@ Array.prototype.containsPrimitiveSubset = function(array) {
|
|||||||
return !array.some(val => this.indexOf(val) === -1);
|
return !array.some(val => this.indexOf(val) === -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use with numbers and strings, not objects */
|
|
||||||
Array.prototype.containsObjectSubset = function(array) {
|
|
||||||
return !array.some(val => !_.find(this, val));
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
|
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
|
||||||
if (!Array.prototype.includes) {
|
if (!Array.prototype.includes) {
|
||||||
Object.defineProperty(Array.prototype, 'includes', {
|
Object.defineProperty(Array.prototype, 'includes', {
|
||||||
|
|||||||
5
app/assets/javascripts/app/constants.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
angular.module('app')
|
||||||
|
|
||||||
|
.constant('appVersion', '3.0.9')
|
||||||
|
|
||||||
|
;
|
||||||
@@ -23,7 +23,8 @@ angular.module('app')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager,
|
.controller('EditorCtrl', function ($sce, $timeout, authManager, $rootScope, actionsManager,
|
||||||
syncManager, modelManager, themeManager, componentManager, storageManager, sessionHistory, privilegesManager) {
|
syncManager, modelManager, themeManager, componentManager, storageManager, sessionHistory,
|
||||||
|
privilegesManager, keyboardManager) {
|
||||||
|
|
||||||
this.spellcheck = true;
|
this.spellcheck = true;
|
||||||
this.componentManager = componentManager;
|
this.componentManager = componentManager;
|
||||||
@@ -121,6 +122,9 @@ angular.module('app')
|
|||||||
this.showExtensions = false;
|
this.showExtensions = false;
|
||||||
this.showMenu = false;
|
this.showMenu = false;
|
||||||
this.noteStatus = null;
|
this.noteStatus = null;
|
||||||
|
// When setting alt key down and deleting note, an alert will come up and block the key up event when alt is released.
|
||||||
|
// We reset it on set note so that the alt menu restores to default.
|
||||||
|
this.altKeyDown = false;
|
||||||
this.loadTagsString();
|
this.loadTagsString();
|
||||||
|
|
||||||
let onReady = () => {
|
let onReady = () => {
|
||||||
@@ -796,10 +800,41 @@ angular.module('app')
|
|||||||
syncManager.sync();
|
syncManager.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.altKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
modifiers: [KeyboardManager.KeyModifierAlt],
|
||||||
|
onKeyDown: () => {
|
||||||
|
$timeout(() => {
|
||||||
|
this.altKeyDown = true;
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onKeyUp: () => {
|
||||||
|
$timeout(() => {
|
||||||
|
this.altKeyDown = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.trashKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
key: KeyboardManager.KeyBackspace,
|
||||||
|
notElementIds: ["note-text-editor", "note-title-editor"],
|
||||||
|
modifiers: [KeyboardManager.KeyModifierMeta],
|
||||||
|
onKeyDown: () => {
|
||||||
|
$timeout(() => {
|
||||||
|
this.deleteNote();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
this.deleteKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
key: KeyboardManager.KeyBackspace,
|
||||||
|
modifiers: [KeyboardManager.KeyModifierMeta, KeyboardManager.KeyModifierShift, KeyboardManager.KeyModifierAlt],
|
||||||
|
onKeyDown: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
$timeout(() => {
|
||||||
|
this.deleteNote(true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Editor Customization
|
Editor Customization
|
||||||
@@ -810,49 +845,59 @@ angular.module('app')
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.loadedTabListener = true;
|
this.loadedTabListener = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert 4 spaces when a tab key is pressed,
|
* Insert 4 spaces when a tab key is pressed,
|
||||||
* only used when inside of the text editor.
|
* only used when inside of the text editor.
|
||||||
* If the shift key is pressed first, this event is
|
* If the shift key is pressed first, this event is
|
||||||
* not fired.
|
* not fired.
|
||||||
*/
|
*/
|
||||||
var parent = this;
|
|
||||||
var handleTab = function (event) {
|
const editor = document.getElementById("note-text-editor");
|
||||||
if (!event.shiftKey && event.which == 9) {
|
this.tabObserver = keyboardManager.addKeyObserver({
|
||||||
if(parent.note.locked) {
|
element: editor,
|
||||||
|
key: KeyboardManager.KeyTab,
|
||||||
|
onKeyDown: (event) => {
|
||||||
|
if(event.shiftKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(this.note.locked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Using document.execCommand gives us undo support
|
// Using document.execCommand gives us undo support
|
||||||
if(!document.execCommand("insertText", false, "\t")) {
|
let insertSuccessful = document.execCommand("insertText", false, "\t");
|
||||||
|
if(!insertSuccessful) {
|
||||||
// document.execCommand works great on Chrome/Safari but not Firefox
|
// document.execCommand works great on Chrome/Safari but not Firefox
|
||||||
var start = this.selectionStart;
|
var start = editor.selectionStart;
|
||||||
var end = this.selectionEnd;
|
var end = editor.selectionEnd;
|
||||||
var spaces = " ";
|
var spaces = " ";
|
||||||
|
|
||||||
// Insert 4 spaces
|
// Insert 4 spaces
|
||||||
this.value = this.value.substring(0, start)
|
editor.value = editor.value.substring(0, start)
|
||||||
+ spaces + this.value.substring(end);
|
+ spaces + editor.value.substring(end);
|
||||||
|
|
||||||
// Place cursor 4 spaces away from where
|
// Place cursor 4 spaces away from where
|
||||||
// the tab key was pressed
|
// the tab key was pressed
|
||||||
this.selectionStart = this.selectionEnd = start + 4;
|
editor.selectionStart = editor.selectionEnd = start + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.note.text = this.value;
|
$timeout(() => {
|
||||||
parent.changesMade();
|
this.note.text = editor.value;
|
||||||
}
|
this.changesMade({bypassDebouncer: true});
|
||||||
}
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
var element = document.getElementById("note-text-editor");
|
// This handles when the editor itself is destroyed, and not when our controller is destroyed.
|
||||||
element.addEventListener('keydown', handleTab);
|
angular.element(editor).on('$destroy', function(){
|
||||||
|
if(this.tabObserver) {
|
||||||
angular.element(element).on('$destroy', function(){
|
keyboardManager.removeKeyObserver(this.tabObserver);
|
||||||
window.removeEventListener('keydown', handleTab);
|
|
||||||
this.loadedTabListener = false;
|
this.loadedTabListener = false;
|
||||||
}.bind(this))
|
|
||||||
}
|
}
|
||||||
|
}.bind(this));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -23,13 +23,14 @@ angular.module('app')
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager,
|
.controller('NotesCtrl', function (authManager, $timeout, $rootScope, modelManager,
|
||||||
syncManager, storageManager, desktopManager, privilegesManager) {
|
syncManager, storageManager, desktopManager, privilegesManager, keyboardManager) {
|
||||||
|
|
||||||
this.panelController = {};
|
this.panelController = {};
|
||||||
this.searchSubmitted = false;
|
this.searchSubmitted = false;
|
||||||
|
|
||||||
$rootScope.$on("user-preferences-changed", () => {
|
$rootScope.$on("user-preferences-changed", () => {
|
||||||
this.loadPreferences();
|
this.loadPreferences();
|
||||||
|
this.reloadNotes();
|
||||||
});
|
});
|
||||||
|
|
||||||
authManager.addEventHandler((event) => {
|
authManager.addEventHandler((event) => {
|
||||||
@@ -335,6 +336,25 @@ angular.module('app')
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.selectNextNote = function() {
|
||||||
|
var visibleNotes = this.visibleNotes();
|
||||||
|
let currentIndex = visibleNotes.indexOf(this.selectedNote);
|
||||||
|
if(currentIndex + 1 < visibleNotes.length) {
|
||||||
|
this.selectNote(visibleNotes[currentIndex + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.selectPreviousNote = function() {
|
||||||
|
var visibleNotes = this.visibleNotes();
|
||||||
|
let currentIndex = visibleNotes.indexOf(this.selectedNote);
|
||||||
|
if(currentIndex - 1 >= 0) {
|
||||||
|
this.selectNote(visibleNotes[currentIndex - 1]);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.selectNote = async function(note, viaClick = false) {
|
this.selectNote = async function(note, viaClick = false) {
|
||||||
if(!note) {
|
if(!note) {
|
||||||
return;
|
return;
|
||||||
@@ -398,7 +418,6 @@ angular.module('app')
|
|||||||
|
|
||||||
this.noteFilter = {text : ''};
|
this.noteFilter = {text : ''};
|
||||||
|
|
||||||
|
|
||||||
this.onFilterEnter = function() {
|
this.onFilterEnter = function() {
|
||||||
// For Desktop, performing a search right away causes input to lose focus.
|
// For Desktop, performing a search right away causes input to lose focus.
|
||||||
// We wait until user explicity hits enter before highlighting desktop search results.
|
// We wait until user explicity hits enter before highlighting desktop search results.
|
||||||
@@ -566,4 +585,59 @@ angular.module('app')
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Keyboard Shortcuts
|
||||||
|
*/
|
||||||
|
|
||||||
|
// In the browser we're not allowed to override cmd/ctrl + n, so we have to use Control modifier as well.
|
||||||
|
// These rules don't apply to desktop, but probably better to be consistent.
|
||||||
|
this.newNoteKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
key: "n",
|
||||||
|
modifiers: [KeyboardManager.KeyModifierMeta, KeyboardManager.KeyModifierCtrl],
|
||||||
|
onKeyDown: (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
$timeout(() => {
|
||||||
|
this.createNewNote();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.getSearchBar = function() {
|
||||||
|
return document.getElementById("search-bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nextNoteKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
key: KeyboardManager.KeyDown,
|
||||||
|
elements: [document.body, this.getSearchBar()],
|
||||||
|
onKeyDown: (event) => {
|
||||||
|
let searchBar = this.getSearchBar();
|
||||||
|
if(searchBar == document.activeElement) {
|
||||||
|
searchBar.blur()
|
||||||
|
}
|
||||||
|
$timeout(() => {
|
||||||
|
this.selectNextNote();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.nextNoteKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
key: KeyboardManager.KeyUp,
|
||||||
|
element: document.body,
|
||||||
|
onKeyDown: (event) => {
|
||||||
|
$timeout(() => {
|
||||||
|
this.selectPreviousNote();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.searchKeyObserver = keyboardManager.addKeyObserver({
|
||||||
|
key: "f",
|
||||||
|
modifiers: [KeyboardManager.KeyModifierMeta, KeyboardManager.KeyModifierShift],
|
||||||
|
onKeyDown: (event) => {
|
||||||
|
let searchBar = this.getSearchBar();
|
||||||
|
if(searchBar) {searchBar.focus()};
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ class AccountMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
controller($scope, $rootScope, authManager, modelManager, syncManager, storageManager, dbManager, passcodeManager,
|
controller($scope, $rootScope, authManager, modelManager, syncManager, storageManager, dbManager, passcodeManager,
|
||||||
$timeout, $compile, archiveManager, privilegesManager) {
|
$timeout, $compile, archiveManager, privilegesManager, appVersion) {
|
||||||
'ngInject';
|
'ngInject';
|
||||||
|
|
||||||
|
$scope.appVersion = "v" + appVersion;
|
||||||
$scope.formData = {mergeLocal: true, ephemeral: false};
|
$scope.formData = {mergeLocal: true, ephemeral: false};
|
||||||
|
|
||||||
$scope.user = authManager.user;
|
$scope.user = authManager.user;
|
||||||
|
|||||||
@@ -111,8 +111,7 @@ class ComponentView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$timeout.cancel($scope.loadTimeout);
|
$timeout.cancel($scope.loadTimeout);
|
||||||
componentManager.registerComponentWindow(component, iframe.contentWindow);
|
componentManager.registerComponentWindow(component, iframe.contentWindow).then(() => {
|
||||||
|
|
||||||
// Add small timeout to, as $scope.loading controls loading overlay,
|
// Add small timeout to, as $scope.loading controls loading overlay,
|
||||||
// which is used to avoid flicker when enabling extensions while having an enabled theme
|
// which is used to avoid flicker when enabling extensions while having an enabled theme
|
||||||
// we don't use ng-show because it causes problems with rendering iframes after timeout, for some reason.
|
// we don't use ng-show because it causes problems with rendering iframes after timeout, for some reason.
|
||||||
@@ -120,6 +119,8 @@ class ComponentView {
|
|||||||
$scope.loading = false;
|
$scope.loading = false;
|
||||||
$scope.issueLoading = desktopError; /* Typically we'd just set this to false at this point, but we now account for desktopError */
|
$scope.issueLoading = desktopError; /* Typically we'd just set this to false at this point, but we now account for desktopError */
|
||||||
}, 7)
|
}, 7)
|
||||||
|
})
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,7 +161,7 @@ class ComponentView {
|
|||||||
$scope.componentValid = false;
|
$scope.componentValid = false;
|
||||||
componentManager.reloadComponent($scope.component).then(() => {
|
componentManager.reloadComponent($scope.component).then(() => {
|
||||||
$scope.reloadStatus();
|
$scope.reloadStatus();
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.reloadStatus = function(doManualReload = true) {
|
$scope.reloadStatus = function(doManualReload = true) {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ class PermissionsModal {
|
|||||||
this.scope = {
|
this.scope = {
|
||||||
show: "=",
|
show: "=",
|
||||||
component: "=",
|
component: "=",
|
||||||
permissions: "=",
|
permissionsString: "=",
|
||||||
callback: "="
|
callback: "="
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -31,68 +31,6 @@ class PermissionsModal {
|
|||||||
controller($scope, modelManager) {
|
controller($scope, modelManager) {
|
||||||
'ngInject';
|
'ngInject';
|
||||||
|
|
||||||
$scope.permissionsString = function() {
|
|
||||||
var finalString = "";
|
|
||||||
let permissionsCount = $scope.permissions.length;
|
|
||||||
|
|
||||||
let addSeparator = (index, length) => {
|
|
||||||
if(index > 0) {
|
|
||||||
if(index == length - 1) {
|
|
||||||
if(length == 2) {
|
|
||||||
return " and ";
|
|
||||||
} else {
|
|
||||||
return ", and "
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return ", ";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
$scope.permissions.forEach((permission, index) => {
|
|
||||||
|
|
||||||
if(permission.name === "stream-items") {
|
|
||||||
var types = permission.content_types.map(function(type){
|
|
||||||
var desc = modelManager.humanReadableDisplayForContentType(type);
|
|
||||||
if(desc) {
|
|
||||||
return desc + "s";
|
|
||||||
} else {
|
|
||||||
return "items of type " + type;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
var typesString = "";
|
|
||||||
|
|
||||||
for(var i = 0;i < types.length;i++) {
|
|
||||||
var type = types[i];
|
|
||||||
typesString += addSeparator(i, types.length + permissionsCount - index - 1);
|
|
||||||
typesString += type;
|
|
||||||
}
|
|
||||||
|
|
||||||
finalString += addSeparator(index, permissionsCount);
|
|
||||||
|
|
||||||
finalString += typesString;
|
|
||||||
|
|
||||||
if(types.length >= 2 && index < permissionsCount - 1) {
|
|
||||||
// If you have a list of types, and still an additional root-level permission coming up, add a comma
|
|
||||||
finalString += ", ";
|
|
||||||
}
|
|
||||||
} else if(permission.name === "stream-context-item") {
|
|
||||||
var mapping = {
|
|
||||||
"editor-stack" : "working note",
|
|
||||||
"note-tags" : "working note",
|
|
||||||
"editor-editor": "working note"
|
|
||||||
}
|
|
||||||
|
|
||||||
finalString += addSeparator(index, permissionsCount, true);
|
|
||||||
|
|
||||||
finalString += mapping[$scope.component.area];
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return finalString + ".";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
117
app/assets/javascripts/app/services/keyboardManager.js
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
class KeyboardManager {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.observers = [];
|
||||||
|
|
||||||
|
KeyboardManager.KeyTab = "Tab";
|
||||||
|
KeyboardManager.KeyBackspace = "Backspace";
|
||||||
|
KeyboardManager.KeyUp = "ArrowUp";
|
||||||
|
KeyboardManager.KeyDown = "ArrowDown";
|
||||||
|
|
||||||
|
KeyboardManager.KeyModifierShift = "Shift";
|
||||||
|
KeyboardManager.KeyModifierCtrl = "Control";
|
||||||
|
// ⌘ key on Mac, ⊞ key on Windows
|
||||||
|
KeyboardManager.KeyModifierMeta = "Meta";
|
||||||
|
KeyboardManager.KeyModifierAlt = "Alt";
|
||||||
|
|
||||||
|
KeyboardManager.KeyEventDown = "KeyEventDown";
|
||||||
|
KeyboardManager.KeyEventUp = "KeyEventUp";
|
||||||
|
|
||||||
|
KeyboardManager.AllModifiers = [
|
||||||
|
KeyboardManager.KeyModifierShift,
|
||||||
|
KeyboardManager.KeyModifierCtrl,
|
||||||
|
KeyboardManager.KeyModifierMeta,
|
||||||
|
KeyboardManager.KeyModifierAlt
|
||||||
|
]
|
||||||
|
|
||||||
|
window.addEventListener('keydown', this.handleKeyDown.bind(this));
|
||||||
|
window.addEventListener('keyup', this.handleKeyUp.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiersForEvent(event) {
|
||||||
|
let eventModifiers = KeyboardManager.AllModifiers.filter((modifier) => {
|
||||||
|
// For a modifier like ctrlKey, must check both event.ctrlKey and event.key.
|
||||||
|
// That's because on keyup, event.ctrlKey would be false, but event.key == Control would be true.
|
||||||
|
let matches = (
|
||||||
|
((event.ctrlKey || event.key == KeyboardManager.KeyModifierCtrl) && modifier === KeyboardManager.KeyModifierCtrl) ||
|
||||||
|
((event.metaKey || event.key == KeyboardManager.KeyModifierMeta) && modifier === KeyboardManager.KeyModifierMeta) ||
|
||||||
|
((event.altKey || event.key == KeyboardManager.KeyModifierAlt) && modifier === KeyboardManager.KeyModifierAlt) ||
|
||||||
|
((event.shiftKey || event.key == KeyboardManager.KeyModifierShift) && modifier === KeyboardManager.KeyModifierShift)
|
||||||
|
)
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
})
|
||||||
|
|
||||||
|
return eventModifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
eventMatchesKeyAndModifiers(event, key, modifiers = []) {
|
||||||
|
let eventModifiers = this.modifiersForEvent(event);
|
||||||
|
|
||||||
|
if(eventModifiers.length != modifiers.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let modifier of modifiers) {
|
||||||
|
if(!eventModifiers.includes(modifier)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifers match, check key
|
||||||
|
if(!key) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the browser, shift + f results in key 'f', but in Electron, shift + f results in 'F'
|
||||||
|
// In our case we don't differentiate between the two.
|
||||||
|
return key.toLowerCase() == event.key.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyObserver(event, keyEventType) {
|
||||||
|
for(let observer of this.observers) {
|
||||||
|
if(observer.element && event.target != observer.element) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(observer.elements && !observer.elements.includes(event.target)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(observer.notElement && observer.notElement == event.target) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(observer.notElementIds && observer.notElementIds.includes(event.target.id)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.eventMatchesKeyAndModifiers(event, observer.key, observer.modifiers)) {
|
||||||
|
let callback = keyEventType == KeyboardManager.KeyEventDown ? observer.onKeyDown : observer.onKeyUp;
|
||||||
|
if(callback) {
|
||||||
|
callback(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown(event) {
|
||||||
|
this.notifyObserver(event, KeyboardManager.KeyEventDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyUp(event) {
|
||||||
|
this.notifyObserver(event, KeyboardManager.KeyEventUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
addKeyObserver({key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds}) {
|
||||||
|
let observer = {key, modifiers, onKeyDown, onKeyUp, element, elements, notElement, notElementIds};
|
||||||
|
this.observers.push(observer);
|
||||||
|
return observer;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeKeyObserver(observer) {
|
||||||
|
this.observers.splice(this.observers.indexOf(observer), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module('app').service('keyboardManager', KeyboardManager);
|
||||||
@@ -41,6 +41,13 @@ class NativeExtManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle addition of SN|ExtensionRepo permission
|
||||||
|
let permission = resolvedSingleton.content.permissions.find((p) => p.name == "stream-items");
|
||||||
|
if(!permission.content_types.includes("SN|ExtensionRepo")) {
|
||||||
|
permission.content_types.push("SN|ExtensionRepo");
|
||||||
|
needsSync = true;
|
||||||
|
}
|
||||||
|
|
||||||
if(needsSync) {
|
if(needsSync) {
|
||||||
resolvedSingleton.setDirty(true);
|
resolvedSingleton.setDirty(true);
|
||||||
this.syncManager.sync();
|
this.syncManager.sync();
|
||||||
@@ -68,7 +75,10 @@ class NativeExtManager {
|
|||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
name: "stream-items",
|
name: "stream-items",
|
||||||
content_types: ["SN|Component", "SN|Theme", "SF|Extension", "Extension", "SF|MFA", "SN|Editor"]
|
content_types: [
|
||||||
|
"SN|Component", "SN|Theme", "SF|Extension",
|
||||||
|
"Extension", "SF|MFA", "SN|Editor", "SN|ExtensionRepo"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -141,7 +151,12 @@ class NativeExtManager {
|
|||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
name: "stream-items",
|
name: "stream-items",
|
||||||
content_types: ["Note", "Tag", "SN|Component", "SN|Theme", "SF|Extension", "Extension", "SF|MFA", "SN|Editor", "SN|UserPreferences"]
|
content_types: [
|
||||||
|
"Note", "Tag", "SN|SmartTag",
|
||||||
|
"SN|Component", "SN|Theme", "SN|UserPreferences",
|
||||||
|
"SF|Extension", "Extension", "SF|MFA", "SN|Editor",
|
||||||
|
"SN|FileSafe|Credentials", "SN|FileSafe|FileMetadata", "SN|FileSafe|Integration"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ $heading-height: 75px;
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
tab-size: 2;
|
||||||
background-color: var(--sn-stylekit-background-color);
|
background-color: var(--sn-stylekit-background-color);
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -135,6 +136,8 @@ $heading-height: 75px;
|
|||||||
|
|
||||||
#editor-pane-component-stack {
|
#editor-pane-component-stack {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
// To keep glued to bottom when editor is in loading state and not visible.
|
||||||
|
margin-top: auto;
|
||||||
|
|
||||||
// When two component stack items are expired and eat up full screen, this is required to scroll them.
|
// When two component stack items are expired and eat up full screen, this is required to scroll them.
|
||||||
// overflow: auto;
|
// overflow: auto;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
#notes-title-bar {
|
#notes-title-bar {
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 18px;
|
font-size: var(--sn-stylekit-font-size-h1);
|
||||||
|
|
||||||
.section-title-bar-header .title {
|
.section-title-bar-header .title {
|
||||||
color: var(--sn-stylekit-neutral-color);
|
color: var(--sn-stylekit-neutral-color);
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
|
|
||||||
.filter-section {
|
.filter-section {
|
||||||
clear: left;
|
clear: left;
|
||||||
height: 32px;
|
height: 29px;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
color: #909090;
|
color: #909090;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 16px;
|
font-size: var(--sn-stylekit-font-size-h3);
|
||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
|
|
||||||
border: none;
|
border: none;
|
||||||
@@ -120,8 +120,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.note-preview {
|
.note-preview {
|
||||||
font-size: 15px;
|
font-size: var(--sn-stylekit-font-size-h3);
|
||||||
margin-top: 1px;
|
margin-top: 2px;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
@@ -140,12 +141,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.preview-hidden {
|
|
||||||
font-size: 14px;
|
|
||||||
margin-top: 2px;
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.note-flag {
|
.note-flag {
|
||||||
color: var(--sn-stylekit-info-color);
|
color: var(--sn-stylekit-info-color);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,12 +53,22 @@
|
|||||||
|
|
||||||
> .tag-info {
|
> .tag-info {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
> .tag-icon {
|
||||||
|
width: 10px;
|
||||||
|
opacity: 0.2;
|
||||||
|
font-size: var(--sn-stylekit-font-size-h2);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
> .title {
|
> .title {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
float: left;
|
|
||||||
color: var(--sn-stylekit-secondary-foreground-color);
|
color: var(--sn-stylekit-secondary-foreground-color);
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -194,6 +194,8 @@
|
|||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
.sk-spinner.small.info{"ng-if" => "importData.loading"}
|
.sk-spinner.small.info{"ng-if" => "importData.loading"}
|
||||||
.sk-panel-footer
|
.sk-panel-footer
|
||||||
|
.sk-panel-row
|
||||||
|
.sk-p.left.neutral.faded {{appVersion}}
|
||||||
%a.sk-a.right{"ng-if" => "formData.showLogin || formData.showRegister", "ng-click" => "formData.showLogin = false; formData.showRegister = false;"}
|
%a.sk-a.right{"ng-if" => "formData.showLogin || formData.showRegister", "ng-click" => "formData.showLogin = false; formData.showRegister = false;"}
|
||||||
Cancel
|
Cancel
|
||||||
%a.sk-a.right.danger{"ng-if" => "!formData.showLogin && !formData.showRegister", "ng-click" => "destroyLocalData()"}
|
%a.sk-a.right.danger{"ng-if" => "!formData.showLogin && !formData.showRegister", "ng-click" => "destroyLocalData()"}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
.sk-h2
|
.sk-h2
|
||||||
%strong {{component.name}}
|
%strong {{component.name}}
|
||||||
would like to interact with your
|
would like to interact with your
|
||||||
{{permissionsString()}}
|
{{permissionsString}}
|
||||||
|
|
||||||
.sk-panel-row
|
.sk-panel-row
|
||||||
%p.sk-p
|
%p.sk-p
|
||||||
|
|||||||
@@ -38,13 +38,13 @@
|
|||||||
%menu-row{"label" => "ctrl.note.locked ? 'Unlock' : 'Lock'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleLockNote()", "desc" => "'Locking notes prevents unintentional editing'"}
|
%menu-row{"label" => "ctrl.note.locked ? 'Unlock' : 'Lock'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleLockNote()", "desc" => "'Locking notes prevents unintentional editing'"}
|
||||||
%menu-row{"label" => "ctrl.note.content.protected ? 'Unprotect' : 'Protect'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleProtectNote()", "desc" => "'Protecting a note will require credentials to view it (Manage Privileges via Account menu)'"}
|
%menu-row{"label" => "ctrl.note.content.protected ? 'Unprotect' : 'Protect'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleProtectNote()", "desc" => "'Protecting a note will require credentials to view it (Manage Privileges via Account menu)'"}
|
||||||
%menu-row{"label" => "'Preview'", "circle" => "ctrl.note.content.hidePreview ? 'danger' : 'success'", "circle-align" => "'right'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleNotePreview()", "desc" => "'Hide or unhide the note preview from the list of notes'"}
|
%menu-row{"label" => "'Preview'", "circle" => "ctrl.note.content.hidePreview ? 'danger' : 'success'", "circle-align" => "'right'", "action" => "ctrl.selectedMenuItem(true); ctrl.toggleNotePreview()", "desc" => "'Hide or unhide the note preview from the list of notes'"}
|
||||||
%menu-row{"ng-show" => "!ctrl.note.content.trashed && !ctrl.note.errorDecrypting", "label" => "'Move to Trash'", "action" => "ctrl.selectedMenuItem(); ctrl.deleteNote()", "stylekit-class" => "'warning'", "desc" => "'Send this note to the trash'"}
|
%menu-row{"ng-show" => "!ctrl.altKeyDown && !ctrl.note.content.trashed && !ctrl.note.errorDecrypting", "label" => "'Move to Trash'", "action" => "ctrl.selectedMenuItem(); ctrl.deleteNote()", "stylekit-class" => "'warning'", "desc" => "'Send this note to the trash'"}
|
||||||
%menu-row{"ng-show" => "!ctrl.note.content.trashed && ctrl.note.errorDecrypting", "label" => "'Delete Permanently'", "action" => "ctrl.selectedMenuItem(); ctrl.deleteNotePermanantely()", "stylekit-class" => "'danger'", "desc" => "'Delete this note permanently from all your devices'"}
|
%menu-row{"ng-show" => "!ctrl.note.content.trashed && ctrl.note.errorDecrypting", "label" => "'Delete Permanently'", "action" => "ctrl.selectedMenuItem(); ctrl.deleteNotePermanantely()", "stylekit-class" => "'danger'", "desc" => "'Delete this note permanently from all your devices'"}
|
||||||
|
|
||||||
%div{"ng-if" => "ctrl.note.content.trashed"}
|
%div{"ng-if" => "ctrl.note.content.trashed || ctrl.altKeyDown"}
|
||||||
%menu-row{"label" => "'Restore'", "action" => "ctrl.selectedMenuItem(true); ctrl.restoreTrashedNote()", "stylekit-class" => "'info'", "desc" => "'Undelete this note and restore it back into your notes'"}
|
%menu-row{"label" => "'Restore'", "ng-show" => "ctrl.note.content.trashed", "action" => "ctrl.selectedMenuItem(true); ctrl.restoreTrashedNote()", "stylekit-class" => "'info'", "desc" => "'Undelete this note and restore it back into your notes'"}
|
||||||
%menu-row{"label" => "'Delete Permanently'", "action" => "ctrl.selectedMenuItem(true); ctrl.deleteNotePermanantely()", "stylekit-class" => "'danger'", "desc" => "'Delete this note permanently from all your devices'"}
|
%menu-row{"label" => "'Delete Permanently'", "action" => "ctrl.selectedMenuItem(true); ctrl.deleteNotePermanantely()", "stylekit-class" => "'danger'", "desc" => "'Delete this note permanently from all your devices'"}
|
||||||
%menu-row{"label" => "'Empty Trash'", "subtitle" => "ctrl.getTrashCount() + ' notes in trash'", "action" => "ctrl.selectedMenuItem(true); ctrl.emptyTrash()", "stylekit-class" => "'danger'", "desc" => "'Permanently delete all notes in the trash'"}
|
%menu-row{"label" => "'Empty Trash'", "ng-show" => "ctrl.note.content.trashed || !ctrl.altKeyDown", "subtitle" => "ctrl.getTrashCount() + ' notes in trash'", "action" => "ctrl.selectedMenuItem(true); ctrl.emptyTrash()", "stylekit-class" => "'danger'", "desc" => "'Permanently delete all notes in the trash'"}
|
||||||
|
|
||||||
.sk-menu-panel-section
|
.sk-menu-panel-section
|
||||||
.sk-menu-panel-header
|
.sk-menu-panel-header
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
.sk-label
|
.sk-label
|
||||||
%i.icon.ion-plus.add-button
|
%i.icon.ion-plus.add-button
|
||||||
.filter-section{"role" => "search"}
|
.filter-section{"role" => "search"}
|
||||||
%input.filter-bar#search-bar.mousetrap{"select-on-click" => "true", "ng-model" => "ctrl.noteFilter.text", "placeholder" => "Search",
|
%input.filter-bar#search-bar{"select-on-click" => "true", "ng-model" => "ctrl.noteFilter.text", "placeholder" => "Search",
|
||||||
"ng-change" => "ctrl.filterTextChanged()", "lowercase" => "true", "ng-blur" => "ctrl.onFilterEnter()", "ng-keyup" => "$event.keyCode == 13 && ctrl.onFilterEnter();",
|
"ng-change" => "ctrl.filterTextChanged()", "lowercase" => "true", "ng-blur" => "ctrl.onFilterEnter()", "ng-keyup" => "$event.keyCode == 13 && ctrl.onFilterEnter();",
|
||||||
"title" => "Searches notes in the currently selected tag"}
|
"title" => "Searches notes in the currently selected tag"}
|
||||||
#search-clear-button{"ng-show" => "ctrl.noteFilter.text", "ng-click" => "ctrl.clearFilterText();"} ✕
|
#search-clear-button{"ng-show" => "ctrl.noteFilter.text", "ng-click" => "ctrl.clearFilterText();"} ✕
|
||||||
@@ -56,7 +56,8 @@
|
|||||||
.name{"ng-show" => "note.title"}
|
.name{"ng-show" => "note.title"}
|
||||||
{{note.title}}
|
{{note.title}}
|
||||||
|
|
||||||
.note-preview{"ng-show" => "!ctrl.hideNotePreview && !note.content.hidePreview && !note.content.protected"}
|
-# Use ng-if and not ng-show here. We don't want previews in the dom at all if protected.
|
||||||
|
.note-preview{"ng-if" => "!ctrl.hideNotePreview && !note.content.hidePreview && !note.content.protected"}
|
||||||
.html-preview{"ng-show" => "note.content.preview_html", "ng-bind-html" => "note.content.preview_html"}
|
.html-preview{"ng-show" => "note.content.preview_html", "ng-bind-html" => "note.content.preview_html"}
|
||||||
.plain-preview{"ng-show" => "!note.content.preview_html && note.content.preview_plain"} {{note.content.preview_plain}}
|
.plain-preview{"ng-show" => "!note.content.preview_html && note.content.preview_plain"} {{note.content.preview_plain}}
|
||||||
.default-preview{"ng-show" => "!note.content.preview_html && !note.content.preview_plain"} {{note.text}}
|
.default-preview{"ng-show" => "!note.content.preview_html && !note.content.preview_plain"} {{note.text}}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
%span.sk-bold Tags
|
%span.sk-bold Tags
|
||||||
.tag{"ng-repeat" => "tag in ctrl.tags track by tag.uuid", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}"}
|
.tag{"ng-repeat" => "tag in ctrl.tags track by tag.uuid", "ng-click" => "ctrl.selectTag(tag)", "ng-class" => "{'selected' : ctrl.selectedTag == tag}"}
|
||||||
.tag-info
|
.tag-info
|
||||||
|
.tag-icon #
|
||||||
%input.title{"ng-class" => "{'editing' : ctrl.editingTag == tag}", "ng-attr-id" => "tag-{{tag.uuid}}", "ng-click" => "ctrl.selectTag(tag)", "ng-model" => "tag.title",
|
%input.title{"ng-class" => "{'editing' : ctrl.editingTag == tag}", "ng-attr-id" => "tag-{{tag.uuid}}", "ng-click" => "ctrl.selectTag(tag)", "ng-model" => "tag.title",
|
||||||
"ng-keyup" => "$event.keyCode == 13 && $event.target.blur()", "sn-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag",
|
"ng-keyup" => "$event.keyCode == 13 && $event.target.blur()", "sn-autofocus" => "true", "should-focus" => "ctrl.newTag || ctrl.editingTag == tag",
|
||||||
"ng-change" => "ctrl.tagTitleDidChange(tag)", "ng-blur" => "ctrl.saveTag($event, tag)", "spellcheck" => "false"}
|
"ng-change" => "ctrl.tagTitleDidChange(tag)", "ng-blur" => "ctrl.saveTag($event, tag)", "spellcheck" => "false"}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<link href="favicon/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180"></link>
|
<link href="favicon/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180"></link>
|
||||||
<link href="favicon/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"></link>
|
<link href="favicon/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png"></link>
|
||||||
<link href="favicon/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"></link>
|
<link href="favicon/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png"></link>
|
||||||
<link href="favicon/manifest.json" rel="manifest"></link>
|
<link href="favicon/site.webmanifest" rel="manifest"></link>
|
||||||
|
|
||||||
<link color="#5bbad5" href="favicon/safari-pinned-tab.svg" rel="mask-icon"></link>
|
<link color="#5bbad5" href="favicon/safari-pinned-tab.svg" rel="mask-icon"></link>
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<meta ng-bind="title" content="Standard Notes" name="application-name"/>
|
<meta ng-bind="title" content="Standard Notes" name="application-name"/>
|
||||||
<base href="/"></base>
|
<base href="/"></base>
|
||||||
|
|
||||||
<title>Notes — Standard Notes</title>
|
<title>Notes · Standard Notes</title>
|
||||||
<meta name="description" content="Standard Notes is a basic notes app that delivers only the essentials in note taking. Because of its simplicity and resistance to growth, users can count on durability and convenience."/>
|
<meta name="description" content="Standard Notes is a basic notes app that delivers only the essentials in note taking. Because of its simplicity and resistance to growth, users can count on durability and convenience."/>
|
||||||
|
|
||||||
<meta name="twitter:title" content="Standard Notes, a private and secure notes app."/>
|
<meta name="twitter:title" content="Standard Notes, a private and secure notes app."/>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ require "rails/test_unit/railtie"
|
|||||||
# you've limited to :test, :development, or :production.
|
# you've limited to :test, :development, or :production.
|
||||||
Bundler.require(*Rails.groups)
|
Bundler.require(*Rails.groups)
|
||||||
|
|
||||||
module Neeto
|
module StandardNotes
|
||||||
class Application < Rails::Application
|
class Application < Rails::Application
|
||||||
# Cross-Origin Resource Sharing (CORS) for Rack compatible web applications.
|
# Cross-Origin Resource Sharing (CORS) for Rack compatible web applications.
|
||||||
config.middleware.insert_before 0, Rack::Cors do
|
config.middleware.insert_before 0, Rack::Cors do
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
default: &default
|
default: &default
|
||||||
key_path: /path/to/key.pem
|
key_path: /path/to/key.pem
|
||||||
repo_url: https://github.com/neeto-project/neeto-web-client.git
|
repo_url: https://github.com/sn-project/sn-web-client.git
|
||||||
user: ssh_username
|
user: ssh_username
|
||||||
|
|
||||||
staging:
|
staging:
|
||||||
<<: *default
|
<<: *default
|
||||||
server: staging.yourdomain.com
|
server: staging.yourdomain.com
|
||||||
branch: staging
|
branch: staging
|
||||||
deploy_to: ~/neeto-staging-client
|
deploy_to: ~/sn-staging-client
|
||||||
|
|
||||||
production:
|
production:
|
||||||
<<: *default
|
<<: *default
|
||||||
server: yourdomain.com
|
server: yourdomain.com
|
||||||
deploy_to: ~/neeto-prod-client
|
deploy_to: ~/sn-prod-client
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
CAP_CONFIG = YAML.load_file("config/cap.yml")
|
CAP_CONFIG = YAML.load_file("config/cap.yml")
|
||||||
|
|
||||||
set :application, 'neeto'
|
set :application, 'standard-notes'
|
||||||
set :repo_url, CAP_CONFIG["default"]["repo_url"]
|
set :repo_url, CAP_CONFIG["default"]["repo_url"]
|
||||||
|
|
||||||
# Default branch is :master
|
# Default branch is :master
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
# Be sure to restart your server when you modify this file.
|
# Be sure to restart your server when you modify this file.
|
||||||
|
|
||||||
Rails.application.config.session_store :cookie_store, key: '_neeto_session'
|
Rails.application.config.session_store :cookie_store, key: '_sn_session'
|
||||||
|
|||||||
2802
package-lock.json
generated
11
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "standard-notes-web",
|
"name": "standard-notes-web",
|
||||||
"version": "3.0.8",
|
"version": "3.0.9",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -34,11 +34,12 @@
|
|||||||
"grunt-haml2html": "^0.3.1",
|
"grunt-haml2html": "^0.3.1",
|
||||||
"grunt-newer": "^1.2.0",
|
"grunt-newer": "^1.2.0",
|
||||||
"grunt-ng-annotate": "^3.0.0",
|
"grunt-ng-annotate": "^3.0.0",
|
||||||
|
"grunt-ng-constant": "^2.0.2",
|
||||||
|
"grunt-shell": "^2.1.0",
|
||||||
"mocha": "^5.2.0",
|
"mocha": "^5.2.0",
|
||||||
"serve-static": "^1.13.2",
|
"serve-static": "^1.13.2",
|
||||||
"sn-models": "0.1.14",
|
"sn-stylekit": "2.0.14",
|
||||||
"sn-stylekit": "2.0.13",
|
"snjs": "0.2.1",
|
||||||
"standard-file-js": "0.3.57",
|
"standard-file-js": "0.3.58"
|
||||||
"grunt-shell": "^2.1.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
BIN
public/favicon/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -2,7 +2,7 @@
|
|||||||
<browserconfig>
|
<browserconfig>
|
||||||
<msapplication>
|
<msapplication>
|
||||||
<tile>
|
<tile>
|
||||||
<square150x150logo src="/mstile-150x150.png"/>
|
<square150x150logo src="favicon/mstile-150x150.png"/>
|
||||||
<TileColor>#da532c</TileColor>
|
<TileColor>#da532c</TileColor>
|
||||||
</tile>
|
</tile>
|
||||||
</msapplication>
|
</msapplication>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 556 B After Width: | Height: | Size: 557 B |
|
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 595 B |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -1,20 +0,0 @@
|
|||||||
<?xml version="1.0" standalone="no"?>
|
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
|
||||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
|
||||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
|
|
||||||
preserveAspectRatio="xMidYMid meet">
|
|
||||||
<metadata>
|
|
||||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
|
||||||
</metadata>
|
|
||||||
<g transform="translate(0.000000,16.000000) scale(0.003200,-0.003200)"
|
|
||||||
fill="#000000" stroke="none">
|
|
||||||
<path d="M230 2490 l0 -2270 2270 0 2270 0 0 2270 0 2270 -2270 0 -2270 0 0
|
|
||||||
-2270z m4340 0 l0 -2070 -2070 0 -2070 0 0 2070 0 2070 2070 0 2070 0 0 -2070z"/>
|
|
||||||
<path d="M2368 3475 c-2 -1 -23 -5 -48 -8 -298 -37 -515 -205 -603 -467 -14
|
|
||||||
-41 -29 -91 -33 -110 -4 -19 -9 -328 -11 -687 l-4 -651 168 1 168 2 2 606 c2
|
|
||||||
648 4 683 54 782 82 162 231 240 449 234 255 -7 412 -140 454 -387 3 -14 5
|
|
||||||
-298 5 -632 l1 -606 168 1 167 2 0 645 c-1 634 -1 646 -23 730 -81 306 -261
|
|
||||||
468 -597 535 -31 7 -311 15 -317 10z"/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1005 B |
@@ -1,14 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "Standard Notes",
|
"name": "",
|
||||||
|
"short_name": "",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/favicon/android-chrome-192x192.png",
|
"src": "favicon/android-chrome-192x192.png",
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/favicon/android-chrome-384x384.png",
|
"src": "favicon/android-chrome-512x512.png",
|
||||||
"sizes": "384x384",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||