fix #713: markdown notes

This commit is contained in:
antelle 2019-09-14 22:12:02 +02:00
parent f7ef9a7b4a
commit dbc73a73e2
10 changed files with 91 additions and 7 deletions

View File

@ -1,8 +1,9 @@
const kdbxweb = require('kdbxweb');
const Format = require('../util/format');
const Locale = require('../util/locale');
const MdToHtml = require('../util/md-to-html');
const Links = require('../const/links');
const RuntimeInfo = require('./runtime-info');
const kdbxweb = require('kdbxweb');
const Templates = {
db: require('templates/export/db.hbs'),
@ -13,7 +14,7 @@ const FieldMapping = [
{ name: 'UserName', locStr: 'user' },
{ name: 'Password', locStr: 'password', protect: true },
{ name: 'URL', locStr: 'website' },
{ name: 'Notes', locStr: 'notes' }
{ name: 'Notes', locStr: 'notes', markdown: true }
];
const KnownFields = { 'Title': true };
@ -38,12 +39,21 @@ function walkEntry(db, entry, parents) {
const path = parents.map(group => group.name).join(' / ');
const fields = [];
for (const field of FieldMapping) {
const value = entryField(entry, field.name);
let value = entryField(entry, field.name);
if (value) {
let html = false;
if (field.markdown) {
const converted = MdToHtml.convert(value);
if (converted !== value) {
value = converted;
html = true;
}
}
fields.push({
title: Format.capFirst(Locale[field.locStr]),
value,
protect: field.protect
protect: field.protect,
html
});
}
}

View File

@ -0,0 +1,23 @@
const marked = require('marked');
const dompurify = require('dompurify');
const whiteSpaceRegex = /<\/?p>|<br>|\r|\n/g;
const MdToHtml = {
convert(md) {
if (!md) {
return '';
}
const html = marked(md);
const htmlWithoutLineBreaks = html.replace(whiteSpaceRegex, '');
const mdWithoutLineBreaks = md.replace(whiteSpaceRegex, '');
if (htmlWithoutLineBreaks === mdWithoutLineBreaks) {
return md;
} else {
const sanitized = dompurify.sanitize(html);
return `<div class="markdown">${sanitized}</div>`;
}
}
};
module.exports = MdToHtml;

View File

@ -233,6 +233,7 @@ const DetailsView = Backbone.View.extend({
name: '$Notes',
title: Format.capFirst(Locale.notes),
multiline: 'true',
markdown: true,
value() {
return model.notes;
}
@ -1016,6 +1017,13 @@ const DetailsView = Backbone.View.extend({
}
options.push({ value: 'det-add-new', icon: 'plus', text: Locale.detMenuAddNewField });
options.push({ value: 'det-clone', icon: 'clone', text: Locale.detClone });
if (canCopy) {
options.push({
value: 'copy-to-clipboard',
icon: 'copy',
text: Locale.detCopyEntryToClipboard
});
}
if (AutoType.enabled) {
options.push({ value: 'det-auto-type', icon: 'keyboard-o', text: Locale.detAutoType });
}
@ -1039,6 +1047,9 @@ const DetailsView = Backbone.View.extend({
case 'det-auto-type':
this.autoType();
break;
case 'copy-to-clipboard':
this.copyToClipboard();
break;
}
},

View File

@ -1,15 +1,22 @@
const Backbone = require('backbone');
const kdbxweb = require('kdbxweb');
const FieldView = require('./field-view');
const GeneratorView = require('../generator-view');
const KeyHandler = require('../../comp/key-handler');
const Keys = require('../../const/keys');
const PasswordGenerator = require('../../util/password-generator');
const FeatureDetector = require('../../util/feature-detector');
const kdbxweb = require('kdbxweb');
const Tip = require('../../util/tip');
const MdToHtml = require('../../util/md-to-html');
const FieldViewText = FieldView.extend({
renderValue(value) {
if (this.model.markdown) {
if (value && value.isProtected) {
value = value.getText();
}
return MdToHtml.convert(value);
}
return value && value.isProtected
? PasswordGenerator.presentValueWithLineBreaks(value)
: _.escape(value || '').replace(/\n/g, '<br/>');

View File

@ -385,6 +385,19 @@
width: 100%;
padding: 0 $base-padding-h;
}
.markdown {
white-space: normal;
h6 { font-size: 1rem; }
h5 { font-size: modular-scale(1, 1rem, 1.05); }
h4 { font-size: modular-scale(2, 1rem, 1.05); }
h3 { font-size: modular-scale(3, 1rem, 1.05); }
h2 { font-size: modular-scale(4, 1rem, 1.05); }
h1 { font-size: $small-header-font-size; }
ul, ol { margin-bottom: 1em; }
ul { list-style-type: initial; }
ol { list-style-type: decimal; }
li { margin-left: 2em; }
}
}
&--no-select {

View File

@ -9,7 +9,11 @@
{{#if field.protect}}
<code>{{field.value}}</code>
{{else}}
{{field.value}}
{{#if field.html}}
{{{field.value}}}
{{else}}
{{field.value}}
{{/if}}
{{/if}}
</td>
</tr>

10
package-lock.json generated
View File

@ -3628,6 +3628,11 @@
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
},
"dompurify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.0.tgz",
"integrity": "sha512-i8LWSIMDpGmv7AbOcQOyy54L4TrRhjs6yrSessoNeYspsAtgaKiiGeBAG5959qLfhGvyndkHeyZWxx0dd4iDxw=="
},
"domutils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@ -7897,6 +7902,11 @@
"object-visit": "^1.0.0"
}
},
"marked": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-0.7.0.tgz",
"integrity": "sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg=="
},
"math-random": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz",

View File

@ -22,6 +22,7 @@
"baron": "3.0.3",
"base64-loader": "1.0.0",
"bourbon": "^6.0.0",
"dompurify": "^2.0.0",
"electron": "^6.0.9",
"eslint": "^6.4.0",
"eslint-config-prettier": "^6.3.0",
@ -57,6 +58,7 @@
"jsqrcode": "github:antelle/jsqrcode#0.1.3",
"kdbxweb": "1.4.2",
"load-grunt-tasks": "5.1.0",
"marked": "^0.7.0",
"mini-css-extract-plugin": "^0.8.0",
"node-sass": "^4.12.0",
"node-stream-zip": "1.8.2",

View File

@ -8,6 +8,7 @@ Release notes
`*` #502: increased the default value of encryption rounds
`+` #348: configurable system-wide shortcuts
`+` #743: copying entry fields to clipboard
`+` #713: markdown notes
`*` devtools are now opened with alt-cmd-I
`-` fix #764: multiple attachments display
`-` fix multi-line fields display in history

View File

@ -30,7 +30,8 @@ function config(grunt, mode = 'production') {
'pikaday',
'jsqrcode',
'argon2-wasm',
'argon2'
'argon2',
'marked'
]
},
output: {
@ -64,6 +65,8 @@ function config(grunt, mode = 'production') {
baron: `baron/baron${devMode ? '.min' : ''}.js`,
qrcode: `jsqrcode/dist/qrcode${devMode ? '.min' : ''}.js`,
argon2: 'argon2-browser/dist/argon2.js',
marked: devMode ? 'marked/lib/marked.js' : 'marked/dist/marked.min.js',
dompurify: `dompurify/dist/purify${devMode ? '.min' : ''}.js`,
hbs: 'handlebars/runtime.js',
'argon2-wasm': 'argon2-browser/dist/argon2.wasm',
templates: path.join(__dirname, 'app/templates'),