mirror of https://github.com/keeweb/keeweb
fix #713: markdown notes
parent
f7ef9a7b4a
commit
dbc73a73e2
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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/>');
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'),
|
||||
|
|
Loading…
Reference in New Issue