mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
expose add link on UI, fixes #95
This commit is contained in:
parent
cb69914f09
commit
1a737f7d19
1
src/public/images/trilium.svg
Normal file
1
src/public/images/trilium.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path d="M63.966,45.043c0.008-0.009,0.021-0.021,0.027-0.029c0.938-1.156-0.823-13.453-5.063-20.125 c-1.389-2.186-2.239-3.423-3.219-4.719c-3.907-5.166-6-6.125-6-6.125S35.732,24.78,36.149,44.315 c-1.754,0.065-11.218,7.528-14.826,14.388c-1.206,2.291-1.856,3.645-2.493,5.141c-2.539,5.957-2.33,8.25-2.33,8.25 s16.271,6.79,33.014-3.294c0.007,0.021,0.013,0.046,0.02,0.063c0.537,1.389,12.08,5.979,19.976,5.621 c2.587-0.116,4.084-0.238,5.696-0.444c6.424-0.818,8.298-2.157,8.298-2.157S81.144,54.396,63.966,45.043z M50.787,65.343 c1.059-1.183,4.648-5.853,0.995-11.315c-0.253-0.377-0.496-0.236-0.496-0.236s0.063,10.822-5.162,12.359 c-5.225,1.537-13.886,4.4-20.427,0.455C25,66.186,26.924,53.606,38.544,47.229c0.546,1.599,2.836,6.854,9.292,6.409 c0.453-0.031,0.453-0.313,0.453-0.313s-9.422-5.328-8.156-10.625s3.089-14.236,9.766-17.948c0.714-0.397,10.746,7.593,10.417,20.94 c-1.606-0.319-7.377-1.004-10.226,4.864c-0.198,0.409,0.046,0.549,0.046,0.549s9.31-5.521,13.275-1.789 c3.965,3.733,10.813,9.763,10.71,17.4C74.111,67.533,62.197,72.258,50.787,65.343z M35.613,35.145c0,0-0.991,3.241-0.603,7.524 l-13.393-7.524C21.618,35.145,27.838,30.931,35.613,35.145z M21.193,36.03l13.344,7.612c-3.872,1.872-6.142,4.388-6.142,4.388 C20.78,43.531,21.193,36.03,21.193,36.03z M72.287,49.064c0,0-2.321-2.471-6.23-4.263l13.187-7.881 C79.243,36.92,79.808,44.413,72.287,49.064z M78.687,36.113l-13.237,7.794c0.3-4.291-0.754-7.511-0.754-7.511 C72.383,32.025,78.687,36.113,78.687,36.113z M42.076,73.778c0,0,3.309-0.737,6.845-3.185l0.056,15.361 C48.977,85.955,42.244,82.621,42.076,73.778z M49.956,85.888L50,70.526c3.539,2.445,6.846,3.181,6.846,3.181 C56.686,82.551,49.956,85.888,49.956,85.888z"></path></svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -2,7 +2,8 @@ import cloningService from '../services/cloning.js';
|
||||
import linkService from '../services/link.js';
|
||||
import noteDetailService from '../services/note_detail.js';
|
||||
import treeUtils from '../services/tree_utils.js';
|
||||
import autocompleteService from '../services/autocomplete.js';
|
||||
import server from "../services/server.js";
|
||||
import noteDetailText from "../services/note_detail_text.js";
|
||||
|
||||
const $dialog = $("#add-link-dialog");
|
||||
const $form = $("#add-link-form");
|
||||
@ -11,6 +12,7 @@ const $linkTitle = $("#link-title");
|
||||
const $clonePrefix = $("#clone-prefix");
|
||||
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
||||
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
||||
const $linkTypeDiv = $("#add-link-type-div");
|
||||
const $linkTypes = $("input[name='add-link-type']");
|
||||
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
||||
|
||||
@ -52,8 +54,12 @@ async function showDialog() {
|
||||
}
|
||||
|
||||
$autoComplete.autocomplete({
|
||||
source: await autocompleteService.getAutocompleteItems(),
|
||||
minLength: 0,
|
||||
source: async function(request, response) {
|
||||
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||
|
||||
response(result);
|
||||
},
|
||||
minLength: 2,
|
||||
change: async () => {
|
||||
const val = $autoComplete.val();
|
||||
const notePath = linkService.getNodePathFromLabel(val);
|
||||
@ -92,7 +98,16 @@ $form.submit(() => {
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
linkService.addLinkToEditor(linkTitle, '#' + notePath);
|
||||
const linkHref = '#' + notePath;
|
||||
|
||||
if (hasSelection()) {
|
||||
const editor = noteDetailText.getEditor();
|
||||
|
||||
editor.execute('link', linkHref);
|
||||
}
|
||||
else {
|
||||
linkService.addLinkToEditor(linkTitle, linkHref);
|
||||
}
|
||||
}
|
||||
else if (linkType === 'selected-to-current') {
|
||||
const prefix = $clonePrefix.val();
|
||||
@ -113,17 +128,21 @@ $form.submit(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
// returns true if user selected some text, false if there's no selection
|
||||
function hasSelection() {
|
||||
const model = noteDetailText.getEditor().model;
|
||||
const selection = model.document.selection;
|
||||
|
||||
return !selection.isCollapsed;
|
||||
}
|
||||
|
||||
function linkTypeChanged() {
|
||||
const value = $linkTypes.filter(":checked").val();
|
||||
|
||||
if (value === 'html') {
|
||||
$linkTitleFormGroup.show();
|
||||
$prefixFormGroup.hide();
|
||||
}
|
||||
else {
|
||||
$linkTitleFormGroup.hide();
|
||||
$prefixFormGroup.show();
|
||||
}
|
||||
$linkTitleFormGroup.toggle(!hasSelection() && value === 'html');
|
||||
$prefixFormGroup.toggle(!hasSelection() && value !== 'html');
|
||||
|
||||
$linkTypeDiv.toggle(!hasSelection());
|
||||
}
|
||||
|
||||
$linkTypes.change(linkTypeChanged);
|
||||
|
@ -1,104 +0,0 @@
|
||||
import treeCache from "./tree_cache.js";
|
||||
import treeUtils from "./tree_utils.js";
|
||||
import protectedSessionHolder from './protected_session_holder.js';
|
||||
|
||||
async function getAutocompleteItems(parentNoteId, notePath, titlePath) {
|
||||
if (!parentNoteId) {
|
||||
parentNoteId = 'root';
|
||||
}
|
||||
|
||||
const parentNote = await treeCache.getNote(parentNoteId);
|
||||
const childNotes = await parentNote.getChildNotes();
|
||||
|
||||
if (!childNotes.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!notePath) {
|
||||
notePath = '';
|
||||
}
|
||||
|
||||
if (!titlePath) {
|
||||
titlePath = '';
|
||||
}
|
||||
|
||||
const autocompleteItems = [];
|
||||
|
||||
for (const childNote of childNotes) {
|
||||
if (childNote.hideInAutocomplete) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const childNotePath = (notePath ? (notePath + '/') : '') + childNote.noteId;
|
||||
const childTitlePath = (titlePath ? (titlePath + ' / ') : '') + await treeUtils.getNoteTitle(childNote.noteId, parentNoteId);
|
||||
|
||||
if (!childNote.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) {
|
||||
autocompleteItems.push({
|
||||
value: childTitlePath + ' (' + childNotePath + ')',
|
||||
label: childTitlePath
|
||||
});
|
||||
}
|
||||
|
||||
const childItems = await getAutocompleteItems(childNote.noteId, childNotePath, childTitlePath);
|
||||
|
||||
for (const childItem of childItems) {
|
||||
autocompleteItems.push(childItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (parentNoteId === 'root') {
|
||||
console.log(`Generated ${autocompleteItems.length} autocomplete items`);
|
||||
}
|
||||
|
||||
return autocompleteItems;
|
||||
}
|
||||
|
||||
// Overrides the default autocomplete filter function to search for matched on atleast 1 word in each of the input term's words
|
||||
$.ui.autocomplete.filter = (array, terms) => {
|
||||
if (!terms) {
|
||||
return array;
|
||||
}
|
||||
|
||||
const startDate = new Date();
|
||||
|
||||
const results = [];
|
||||
const tokens = terms.toLowerCase().split(" ");
|
||||
|
||||
for (const item of array) {
|
||||
const lcLabel = item.label.toLowerCase();
|
||||
|
||||
const found = tokens.every(token => lcLabel.indexOf(token) !== -1);
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is not completely correct and might cause minor problems with note with names containing this " / "
|
||||
const lastSegmentIndex = lcLabel.lastIndexOf(" / ");
|
||||
|
||||
if (lastSegmentIndex !== -1) {
|
||||
const lastSegment = lcLabel.substr(lastSegmentIndex + 3);
|
||||
|
||||
// at least some token needs to be in the last segment (leaf note), otherwise this
|
||||
// particular note is not that interesting (query is satisfied by parent note)
|
||||
const foundInLastSegment = tokens.some(token => lastSegment.indexOf(token) !== -1);
|
||||
|
||||
if (!foundInLastSegment) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
results.push(item);
|
||||
|
||||
if (results.length > 100) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Search took " + (new Date().getTime() - startDate.getTime()) + "ms");
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
export default {
|
||||
getAutocompleteItems
|
||||
};
|
1
src/public/javascripts/services/bootstrap.js
vendored
1
src/public/javascripts/services/bootstrap.js
vendored
@ -35,6 +35,7 @@ import libraryLoader from "./library_loader.js";
|
||||
// required for CKEditor image upload plugin
|
||||
window.glob.getCurrentNode = treeService.getCurrentNode;
|
||||
window.glob.getHeaders = server.getHeaders;
|
||||
window.glob.showAddLinkDialog = addLinkDialog.showDialog;
|
||||
|
||||
// required for ESLint plugin
|
||||
window.glob.getCurrentNote = noteDetailService.getCurrentNote;
|
||||
|
2
src/public/libraries/ckeditor/ckeditor.js
vendored
2
src/public/libraries/ckeditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -56,7 +56,7 @@ async function checkContentHashes(otherHashes) {
|
||||
if (hashes[key] !== otherHashes[key]) {
|
||||
allChecksPassed = false;
|
||||
|
||||
await eventLogService.addEvent(`Content hash check for ${key} FAILED. Local is ${hashes[key]}, remote is ${resp.hashes[key]}`);
|
||||
await eventLogService.addEvent(`Content hash check for ${key} FAILED. Local is ${hashes[key]}, remote is ${otherHashes[key]}`);
|
||||
|
||||
if (key !== 'recent_notes') {
|
||||
// let's not get alarmed about recent notes which get updated often and can cause failures in race conditions
|
||||
|
@ -220,7 +220,7 @@
|
||||
|
||||
<div id="add-link-dialog" title="Add link" style="display: none;">
|
||||
<form id="add-link-form">
|
||||
<div class="radio">
|
||||
<div id="add-link-type-div" class="radio">
|
||||
<label title="Add HTML link to the selected note at cursor in current note">
|
||||
<input type="radio" name="add-link-type" value="html"/>
|
||||
add normal HTML link</label>
|
||||
|
Loading…
x
Reference in New Issue
Block a user