mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
WIP
This commit is contained in:
parent
055db32510
commit
0ecb2f3662
12
package-lock.json
generated
12
package-lock.json
generated
@ -1337,9 +1337,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"async-mutex": {
|
"async-mutex": {
|
||||||
"version": "0.2.3",
|
"version": "0.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.2.4.tgz",
|
||||||
"integrity": "sha512-766xaN3BZJyNa7rxdsN1XV34/XFKiyuqJ7VBc8wrCS3wetLdCEgvarIiv7U+Mojly8TVxZxAXZcmFBEdmvDVCA==",
|
"integrity": "sha512-fcQKOXUKMQc57JlmjBCHtkKNrfGpHyR7vu18RfuLfeTAf4hK9PgOadPR5cDrBQ682zasrLUhJFe7EKAHJOduDg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tslib": "^2.0.0"
|
"tslib": "^2.0.0"
|
||||||
},
|
},
|
||||||
@ -2841,9 +2841,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"electron": {
|
"electron": {
|
||||||
"version": "10.0.0-beta.4",
|
"version": "10.0.0-beta.9",
|
||||||
"resolved": "https://registry.npmjs.org/electron/-/electron-10.0.0-beta.4.tgz",
|
"resolved": "https://registry.npmjs.org/electron/-/electron-10.0.0-beta.9.tgz",
|
||||||
"integrity": "sha512-/Jp9i0yiuM/WUdiKFjf7+5gZQJITGhijl++Zp31m94MY+QNMLEnFhaKLSqzrmPA2FPrXn2KlUPNpQs+4Wjcvpg==",
|
"integrity": "sha512-trK7AsHIr8+f5kfCVLM/IAiHhr0CVIpf6GEokvkUMqoi/6q3q2PAgeaPnQhb102AREwKruCiE/wVEXCZiY7spQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@electron/get": "^1.0.1",
|
"@electron/get": "^1.0.1",
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"electron": "10.0.0-beta.4",
|
"electron": "10.0.0-beta.9",
|
||||||
"electron-builder": "22.7.0",
|
"electron-builder": "22.7.0",
|
||||||
"electron-packager": "15.0.0",
|
"electron-packager": "15.0.0",
|
||||||
"electron-rebuild": "1.11.0",
|
"electron-rebuild": "1.11.0",
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
import attributeParser from '../src/public/app/services/attribute_parser.js';
|
import attributeParser from '../src/public/app/services/attribute_parser.js';
|
||||||
import {describe, it, expect, execute} from './mini_test.js';
|
import {describe, it, expect, execute} from './mini_test.js';
|
||||||
|
|
||||||
|
describe("Preprocessor", () => {
|
||||||
|
it("relation with value", () => {
|
||||||
|
expect(attributeParser.preprocess('<p>~relation = <a class="reference-link" href="#root/RclIpMauTOKS/NFi2gL4xtPxM" some-attr="abc" data-note-path="root/RclIpMauTOKS/NFi2gL4xtPxM">note</a> </p>'))
|
||||||
|
.toEqual("~relation = #root/RclIpMauTOKS/NFi2gL4xtPxM ");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("Lexer", () => {
|
describe("Lexer", () => {
|
||||||
it("simple label", () => {
|
it("simple label", () => {
|
||||||
expect(attributeParser.lexer("#label").map(t => t.text))
|
expect(attributeParser.lexer("#label").map(t => t.text))
|
||||||
@ -19,11 +26,8 @@ describe("Lexer", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("relation with value", () => {
|
it("relation with value", () => {
|
||||||
expect(attributeParser.lexer('~relation=<a class="reference-link" href="#root/RclIpMauTOKS/NFi2gL4xtPxM" data-note-path="root/RclIpMauTOKS/NFi2gL4xtPxM">note</a>').map(t => t.text))
|
expect(attributeParser.lexer('~relation=#root/RclIpMauTOKS/NFi2gL4xtPxM').map(t => t.text))
|
||||||
.toEqual(["~relation", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
|
.toEqual(["~relation", "=", "#root/RclIpMauTOKS/NFi2gL4xtPxM"]);
|
||||||
|
|
||||||
expect(attributeParser.lexer('~relation=<a class="reference-link" id="abc" href="#NFi2gL4xtPxM">note</a>').map(t => t.text))
|
|
||||||
.toEqual(["~relation", "=", "#NFi2gL4xtPxM"]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("use quotes to define value", () => {
|
it("use quotes to define value", () => {
|
||||||
@ -74,7 +78,7 @@ describe("Parser", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("error cases", () => {
|
it("error cases", () => {
|
||||||
expect(() => attributeParser.parser(["~token"].map(t => ({text: t}))))
|
expect(() => attributeParser.parser(["~token"].map(t => ({text: t})), "~token"))
|
||||||
.toThrow('Relation "~token" should point to a note.');
|
.toThrow('Relation "~token" should point to a note.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -44,6 +44,7 @@ export function expect(val) {
|
|||||||
console.trace("toThrow caught exception, but messages differ");
|
console.trace("toThrow caught exception, but messages differ");
|
||||||
console.error(`expected: ${errorMessage}`);
|
console.error(`expected: ${errorMessage}`);
|
||||||
console.error(`got: ${e.message}`);
|
console.error(`got: ${e.message}`);
|
||||||
|
console.error(`${e.stack}`);
|
||||||
|
|
||||||
errorCount++;
|
errorCount++;
|
||||||
}
|
}
|
||||||
|
@ -195,6 +195,7 @@ function lexAndParse(str, allowEmptyRelations = false) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
preprocess,
|
||||||
lexer,
|
lexer,
|
||||||
parser,
|
parser,
|
||||||
lexAndParse
|
lexAndParse
|
||||||
|
@ -115,13 +115,16 @@ export default class AttributeDetailWidget extends BasicWidget {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async showAttributeDetail({attribute, isOwned, x, y}) {
|
async showAttributeDetail({allAttributes, attribute, isOwned, x, y}) {
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
this.hide();
|
this.hide();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.allAttributes = allAttributes;
|
||||||
|
this.attribute = attribute;
|
||||||
|
|
||||||
this.toggleInt(true);
|
this.toggleInt(true);
|
||||||
|
|
||||||
let {results, count} = await server.post('search-related', attribute);
|
let {results, count} = await server.post('search-related', attribute);
|
||||||
@ -173,11 +176,13 @@ export default class AttributeDetailWidget extends BasicWidget {
|
|||||||
|
|
||||||
this.$attrEditName
|
this.$attrEditName
|
||||||
.val(attribute.name)
|
.val(attribute.name)
|
||||||
.attr('readonly', () => !isOwned);
|
.attr('readonly', () => !isOwned)
|
||||||
|
.on('keyup', () => this.updateParent());
|
||||||
|
|
||||||
this.$attrEditValue
|
this.$attrEditValue
|
||||||
.val(attribute.value)
|
.val(attribute.value)
|
||||||
.attr('readonly', () => !isOwned);
|
.attr('readonly', () => !isOwned)
|
||||||
|
.on('keyup', () => this.updateParent());
|
||||||
|
|
||||||
this.$attrEditButtonRow.toggle(!!isOwned);
|
this.$attrEditButtonRow.toggle(!!isOwned);
|
||||||
|
|
||||||
@ -186,6 +191,13 @@ export default class AttributeDetailWidget extends BasicWidget {
|
|||||||
this.$widget.show();
|
this.$widget.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateParent() {
|
||||||
|
this.attribute.name = this.$attrEditName.val();
|
||||||
|
this.attribute.value = this.$attrEditValue.val();
|
||||||
|
|
||||||
|
this.triggerCommand('updateAttributeList', { attributes: this.allAttributes });
|
||||||
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.toggleInt(false);
|
this.toggleInt(false);
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,59 @@ const mentionSetup = {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const editorConfig = {
|
||||||
|
removePlugins: [
|
||||||
|
'Enter',
|
||||||
|
'ShiftEnter',
|
||||||
|
'Heading',
|
||||||
|
'Link',
|
||||||
|
'Autoformat',
|
||||||
|
'Bold',
|
||||||
|
'Italic',
|
||||||
|
'Underline',
|
||||||
|
'Strikethrough',
|
||||||
|
'Code',
|
||||||
|
'Superscript',
|
||||||
|
'Subscript',
|
||||||
|
'BlockQuote',
|
||||||
|
'Image',
|
||||||
|
'ImageCaption',
|
||||||
|
'ImageStyle',
|
||||||
|
'ImageToolbar',
|
||||||
|
'ImageUpload',
|
||||||
|
'ImageResize',
|
||||||
|
'List',
|
||||||
|
'TodoList',
|
||||||
|
'PasteFromOffice',
|
||||||
|
'Table',
|
||||||
|
'TableToolbar',
|
||||||
|
'TableProperties',
|
||||||
|
'TableCellProperties',
|
||||||
|
'Indent',
|
||||||
|
'IndentBlock',
|
||||||
|
'BlockToolbar',
|
||||||
|
'ParagraphButtonUI',
|
||||||
|
'HeadingButtonsUI',
|
||||||
|
'UploadimagePlugin',
|
||||||
|
'InternalLinkPlugin',
|
||||||
|
'MarkdownImportPlugin',
|
||||||
|
'CuttonotePlugin',
|
||||||
|
'TextTransformation',
|
||||||
|
'Font',
|
||||||
|
'FontColor',
|
||||||
|
'FontBackgroundColor',
|
||||||
|
'CodeBlock',
|
||||||
|
'SelectAll',
|
||||||
|
'IncludeNote',
|
||||||
|
'CutToNote'
|
||||||
|
],
|
||||||
|
toolbar: {
|
||||||
|
items: []
|
||||||
|
},
|
||||||
|
placeholder: "Type the labels and relations here ...",
|
||||||
|
mention: mentionSetup
|
||||||
|
};
|
||||||
|
|
||||||
const TPL = `
|
const TPL = `
|
||||||
<div class="note-attributes">
|
<div class="note-attributes">
|
||||||
<style>
|
<style>
|
||||||
@ -321,102 +374,48 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
async initEditor() {
|
async initEditor() {
|
||||||
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
|
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
|
||||||
|
|
||||||
// CKEditor since version 12 needs the element to be visible before initialization. At the same time
|
|
||||||
// we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
|
|
||||||
// display of $widget in both branches.
|
|
||||||
this.$widget.show();
|
this.$widget.show();
|
||||||
|
|
||||||
this.$editor.on("click", async e => {
|
this.$editor.on("click", e => this.handleEditorClick(e));
|
||||||
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
|
||||||
|
|
||||||
if (pos && pos.textNode && pos.textNode.data) {
|
|
||||||
const attrText = pos.textNode.data;
|
|
||||||
const clickIndex = pos.offset - pos.textNode.startOffset;
|
|
||||||
|
|
||||||
const parsedAttrs = attributesParser.lexAndParse(attrText, true);
|
|
||||||
|
|
||||||
let matchedAttr = null;
|
|
||||||
|
|
||||||
for (const attr of parsedAttrs) {
|
|
||||||
if (clickIndex >= attr.startIndex && clickIndex <= attr.endIndex) {
|
|
||||||
matchedAttr = attr;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.attributeDetailWidget.showAttributeDetail({
|
|
||||||
attribute: matchedAttr,
|
|
||||||
isOwned: true,
|
|
||||||
x: e.pageX,
|
|
||||||
y: e.pageY
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.textEditor = await BalloonEditor.create(this.$editor[0], {
|
|
||||||
removePlugins: [
|
|
||||||
'Enter',
|
|
||||||
'ShiftEnter',
|
|
||||||
'Heading',
|
|
||||||
'Link',
|
|
||||||
'Autoformat',
|
|
||||||
'Bold',
|
|
||||||
'Italic',
|
|
||||||
'Underline',
|
|
||||||
'Strikethrough',
|
|
||||||
'Code',
|
|
||||||
'Superscript',
|
|
||||||
'Subscript',
|
|
||||||
'BlockQuote',
|
|
||||||
'Image',
|
|
||||||
'ImageCaption',
|
|
||||||
'ImageStyle',
|
|
||||||
'ImageToolbar',
|
|
||||||
'ImageUpload',
|
|
||||||
'ImageResize',
|
|
||||||
'List',
|
|
||||||
'TodoList',
|
|
||||||
'PasteFromOffice',
|
|
||||||
'Table',
|
|
||||||
'TableToolbar',
|
|
||||||
'TableProperties',
|
|
||||||
'TableCellProperties',
|
|
||||||
'Indent',
|
|
||||||
'IndentBlock',
|
|
||||||
'BlockToolbar',
|
|
||||||
'ParagraphButtonUI',
|
|
||||||
'HeadingButtonsUI',
|
|
||||||
'UploadimagePlugin',
|
|
||||||
'InternalLinkPlugin',
|
|
||||||
'MarkdownImportPlugin',
|
|
||||||
'CuttonotePlugin',
|
|
||||||
'TextTransformation',
|
|
||||||
'Font',
|
|
||||||
'FontColor',
|
|
||||||
'FontBackgroundColor',
|
|
||||||
'CodeBlock',
|
|
||||||
'SelectAll',
|
|
||||||
'IncludeNote',
|
|
||||||
'CutToNote'
|
|
||||||
],
|
|
||||||
toolbar: {
|
|
||||||
items: []
|
|
||||||
},
|
|
||||||
placeholder: "Type the labels and relations here ...",
|
|
||||||
mention: mentionSetup
|
|
||||||
});
|
|
||||||
|
|
||||||
|
this.textEditor = await BalloonEditor.create(this.$editor[0], editorConfig);
|
||||||
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
this.textEditor.model.document.on('change:data', () => this.spacedUpdate.scheduleUpdate());
|
||||||
|
|
||||||
// disable spellcheck for attribute editor
|
// disable spellcheck for attribute editor
|
||||||
this.textEditor.editing.view.change( writer => {
|
this.textEditor.editing.view.change(writer => writer.setAttribute('spellcheck', 'false', this.textEditor.editing.view.document.getRoot()));
|
||||||
writer.setAttribute( 'spellcheck', 'false', this.textEditor.editing.view.document.getRoot() );
|
|
||||||
} );
|
|
||||||
|
|
||||||
//await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
|
//await import(/* webpackIgnore: true */'../../libraries/ckeditor/inspector.js');
|
||||||
//CKEditorInspector.attach(this.textEditor);
|
//CKEditorInspector.attach(this.textEditor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleEditorClick(e) {
|
||||||
|
const pos = this.textEditor.model.document.selection.getFirstPosition();
|
||||||
|
|
||||||
|
if (pos && pos.textNode && pos.textNode.data) {
|
||||||
|
const attrText = pos.textNode.data;
|
||||||
|
const clickIndex = pos.offset - pos.textNode.startOffset;
|
||||||
|
|
||||||
|
const parsedAttrs = attributesParser.lexAndParse(attrText, true);
|
||||||
|
|
||||||
|
let matchedAttr = null;
|
||||||
|
|
||||||
|
for (const attr of parsedAttrs) {
|
||||||
|
if (clickIndex >= attr.startIndex && clickIndex <= attr.endIndex) {
|
||||||
|
matchedAttr = attr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.attributeDetailWidget.showAttributeDetail({
|
||||||
|
allAttributes: parsedAttrs,
|
||||||
|
attribute: matchedAttr,
|
||||||
|
isOwned: true,
|
||||||
|
x: e.pageX,
|
||||||
|
y: e.pageY
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async loadReferenceLinkTitle(noteId, $el) {
|
async loadReferenceLinkTitle(noteId, $el) {
|
||||||
const note = await treeCache.getNote(noteId, true);
|
const note = await treeCache.getNote(noteId, true);
|
||||||
|
|
||||||
@ -436,14 +435,7 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refreshWithNote(note) {
|
async refreshWithNote(note) {
|
||||||
const ownedAttributes = note.getOwnedAttributes();
|
await this.renderOwnedAttributes(note.getOwnedAttributes());
|
||||||
const $attributesContainer = $("<div>");
|
|
||||||
|
|
||||||
await this.renderAttributesIntoCKEditor(ownedAttributes, $attributesContainer);
|
|
||||||
|
|
||||||
await this.spacedUpdate.allowUpdateWithoutChange(() => {
|
|
||||||
this.textEditor.setData($attributesContainer.html());
|
|
||||||
});
|
|
||||||
|
|
||||||
const inheritedAttributes = note.getAttributes().filter(attr => attr.noteId !== this.noteId);
|
const inheritedAttributes = note.getAttributes().filter(attr => attr.noteId !== this.noteId);
|
||||||
|
|
||||||
@ -465,6 +457,16 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
this.parseAttributes();
|
this.parseAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async renderOwnedAttributes(ownedAttributes) {
|
||||||
|
const $attributesContainer = $("<div>");
|
||||||
|
|
||||||
|
await this.renderAttributesIntoCKEditor(ownedAttributes, $attributesContainer);
|
||||||
|
|
||||||
|
await this.spacedUpdate.allowUpdateWithoutChange(() => {
|
||||||
|
this.textEditor.setData($attributesContainer.html());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
attrPlural(number) {
|
attrPlural(number) {
|
||||||
return 'attribute' + (number === 1 ? '' : 's');
|
return 'attribute' + (number === 1 ? '' : 's');
|
||||||
}
|
}
|
||||||
@ -554,4 +556,8 @@ export default class NoteAttributesWidget extends TabAwareWidget {
|
|||||||
this.$editor.trigger('focus');
|
this.$editor.trigger('focus');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateAttributeListCommand({attributes}) {
|
||||||
|
this.renderOwnedAttributes(attributes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +168,23 @@ function executeWithoutTransaction(query, params = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function executeMany(query, params) {
|
function executeMany(query, params) {
|
||||||
getManyRows(query, params);
|
while (params.length > 0) {
|
||||||
|
const curParams = params.slice(0, Math.min(params.length, PARAM_LIMIT));
|
||||||
|
params = params.slice(curParams.length);
|
||||||
|
|
||||||
|
const curParamsObj = {};
|
||||||
|
|
||||||
|
let j = 1;
|
||||||
|
for (const param of curParams) {
|
||||||
|
curParamsObj['param' + j++] = param;
|
||||||
|
}
|
||||||
|
|
||||||
|
let i = 1;
|
||||||
|
const questionMarks = curParams.map(() => ":param" + i++).join(",");
|
||||||
|
const curQuery = query.replace(/\?\?\?/g, questionMarks);
|
||||||
|
|
||||||
|
dbConnection.prepare(curQuery).run(curParamsObj);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeScript(query) {
|
function executeScript(query) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user