mirror of
https://github.com/zadam/trilium.git
synced 2025-11-16 18:25:51 +01:00
Merge remote-tracking branch 'origin/develop' into feature/extend-kept-html-tags
; Conflicts: ; src/routes/api/options.ts ; src/services/options_init.ts
This commit is contained in:
@@ -34,8 +34,17 @@ interface Item {
|
||||
baseSize?: string;
|
||||
growthFactor?: string;
|
||||
targetNoteId?: "_backendLog" | "_globalNoteMap";
|
||||
builtinWidget?: "bookmarks" | "spacer" | "backInHistoryButton" | "forwardInHistoryButton" | "syncStatus" | "protectedSession" | "todayInJournal" | "calendar";
|
||||
command?: "jumpToNote" | "searchNotes" | "createNoteIntoInbox" | "showRecentChanges";
|
||||
builtinWidget?: "bookmarks" | "spacer" | "backInHistoryButton" | "forwardInHistoryButton" | "syncStatus" | "protectedSession" | "todayInJournal" | "calendar" | "quickSearch";
|
||||
command?: keyof typeof Command;
|
||||
}
|
||||
|
||||
// TODO: Move this into a commons project once the client/server architecture is well split.
|
||||
enum Command {
|
||||
jumpToNote,
|
||||
searchNotes,
|
||||
createNoteIntoInbox,
|
||||
showRecentChanges,
|
||||
showOptions
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -94,7 +103,8 @@ const HIDDEN_SUBTREE_DEFINITION: Item = {
|
||||
type: 'contentWidget',
|
||||
icon: 'bx-terminal',
|
||||
attributes: [
|
||||
{ type: 'label', name: 'keepCurrentHoisting' }
|
||||
{ type: 'label', name: 'keepCurrentHoisting' },
|
||||
{ type: 'label', name: 'fullContentWidth' }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -231,8 +241,10 @@ const HIDDEN_SUBTREE_DEFINITION: Item = {
|
||||
{ id: '_lbBookmarks', title: 'Bookmarks', type: 'launcher', builtinWidget: 'bookmarks', icon: 'bx bx-bookmark' },
|
||||
{ id: '_lbToday', title: "Open Today's Journal Note", type: 'launcher', builtinWidget: 'todayInJournal', icon: 'bx bx-calendar-star' },
|
||||
{ id: '_lbSpacer2', title: 'Spacer', type: 'launcher', builtinWidget: 'spacer', baseSize: "0", growthFactor: "1" },
|
||||
{ id: '_lbQuickSearch', title: "Quick Search", type: "launcher", builtinWidget: "quickSearch", icon: "bx bx-rectangle" },
|
||||
{ id: '_lbProtectedSession', title: 'Protected Session', type: 'launcher', builtinWidget: 'protectedSession', icon: 'bx bx bx-shield-quarter' },
|
||||
{ id: '_lbSyncStatus', title: 'Sync Status', type: 'launcher', builtinWidget: 'syncStatus', icon: 'bx bx-wifi' }
|
||||
{ id: '_lbSyncStatus', title: 'Sync Status', type: 'launcher', builtinWidget: 'syncStatus', icon: 'bx bx-wifi' },
|
||||
{ id: '_lbSettings', title: 'Settings', type: 'launcher', command: 'showOptions', icon: 'bx bx-cog' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -60,7 +60,10 @@ function sanitize(dirtyHtml: string) {
|
||||
'mumble', 'nfs', 'onenote', 'pop', 'rmi', 's3', 'sftp', 'skype', 'sms', 'spotify', 'steam', 'svn', 'udp',
|
||||
'view-source', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack'
|
||||
],
|
||||
transformTags,
|
||||
nonTextTags: [
|
||||
'head'
|
||||
],
|
||||
transformTags
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -149,15 +149,20 @@ function importMarkdown(taskContext: TaskContext, file: File, parentNote: BNote)
|
||||
}
|
||||
|
||||
function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) {
|
||||
const title = utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
||||
let content = file.buffer.toString("utf-8");
|
||||
|
||||
if (taskContext?.data?.safeImport) {
|
||||
content = htmlSanitizer.sanitize(content);
|
||||
}
|
||||
// Try to get title from HTML first, fall back to filename
|
||||
// We do this before sanitization since that turns all <h1>s into <h2>
|
||||
const htmlTitle = importUtils.extractHtmlTitle(content);
|
||||
const title = htmlTitle || utils.getNoteTitle(file.originalname, !!taskContext.data?.replaceUnderscoresWithSpaces);
|
||||
|
||||
content = importUtils.handleH1(content, title);
|
||||
|
||||
if (taskContext?.data?.safeImport) {
|
||||
content = htmlSanitizer.sanitize(content);
|
||||
}
|
||||
|
||||
|
||||
const {note} = noteService.createNewNote({
|
||||
parentNoteId: parentNote.noteId,
|
||||
title,
|
||||
@@ -166,9 +171,9 @@ function importHtml(taskContext: TaskContext, file: File, parentNote: BNote) {
|
||||
mime: 'text/html',
|
||||
isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
|
||||
});
|
||||
|
||||
|
||||
taskContext.increaseProgressCount();
|
||||
|
||||
|
||||
return note;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
function handleH1(content: string, title: string) {
|
||||
content = content.replace(/<h1>([^<]*)<\/h1>/gi, (match, text) => {
|
||||
content = content.replace(/<h1[^>]*>([^<]*)<\/h1>/gi, (match, text) => {
|
||||
if (title.trim() === text.trim()) {
|
||||
return ""; // remove whole H1 tag
|
||||
} else {
|
||||
@@ -11,6 +11,12 @@ function handleH1(content: string, title: string) {
|
||||
return content;
|
||||
}
|
||||
|
||||
function extractHtmlTitle(content: string): string | null {
|
||||
const titleMatch = content.match(/<title[^>]*>([^<]+)<\/title>/i);
|
||||
return titleMatch ? titleMatch[1].trim() : null;
|
||||
}
|
||||
|
||||
export default {
|
||||
handleH1
|
||||
handleH1,
|
||||
extractHtmlTitle
|
||||
};
|
||||
|
||||
@@ -137,6 +137,7 @@ const defaultOptions: DefaultOption[] = [
|
||||
{ name: "textNoteEditorType", value: "ckeditor-balloon", isSynced: true },
|
||||
|
||||
// HTML import configuration
|
||||
{ name: "layoutOrientation", value: "vertical", isSynced: false },
|
||||
{ name: "allowedHtmlTags", value: JSON.stringify([
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
|
||||
'li', 'b', 'i', 'strong', 'em', 'strike', 's', 'del', 'abbr', 'code', 'hr', 'br', 'div',
|
||||
|
||||
@@ -8,7 +8,7 @@ function parse(value: string): DefinitionObject {
|
||||
if (token === 'promoted') {
|
||||
defObj.isPromoted = true;
|
||||
}
|
||||
else if (['text', 'number', 'boolean', 'date', 'datetime', 'url'].includes(token)) {
|
||||
else if (['text', 'number', 'boolean', 'date', 'datetime', 'time', 'url'].includes(token)) {
|
||||
defObj.labelType = token;
|
||||
}
|
||||
else if (['single', 'multi'].includes(token)) {
|
||||
|
||||
@@ -27,43 +27,52 @@ class SearchResult {
|
||||
this.score = 0;
|
||||
|
||||
const note = becca.notes[this.noteId];
|
||||
const normalizedQuery = fulltextQuery.toLowerCase();
|
||||
const normalizedTitle = note.title.toLowerCase();
|
||||
|
||||
// Note ID exact match, much higher score
|
||||
if (note.noteId.toLowerCase() === fulltextQuery) {
|
||||
this.score += 100;
|
||||
this.score += 1000;
|
||||
}
|
||||
|
||||
if (note.title.toLowerCase() === fulltextQuery) {
|
||||
this.score += 100; // high reward for exact match #3470
|
||||
// Title matching scores, make sure to always win
|
||||
if (normalizedTitle === normalizedQuery) {
|
||||
this.score += 2000; // Increased from 1000 to ensure exact matches always win
|
||||
}
|
||||
else if (normalizedTitle.startsWith(normalizedQuery)) {
|
||||
this.score += 500; // Increased to give more weight to prefix matches
|
||||
}
|
||||
else if (normalizedTitle.includes(` ${normalizedQuery} `) ||
|
||||
normalizedTitle.startsWith(`${normalizedQuery} `) ||
|
||||
normalizedTitle.endsWith(` ${normalizedQuery}`)) {
|
||||
this.score += 300; // Increased to better distinguish word matches
|
||||
}
|
||||
|
||||
// notes with matches on its own note title as opposed to ancestors or descendants
|
||||
this.addScoreForStrings(tokens, note.title, 1.5);
|
||||
|
||||
// matches in attributes don't get extra points and thus are implicitly valued less than note path matches
|
||||
|
||||
this.addScoreForStrings(tokens, this.notePathTitle, 1);
|
||||
// Add scores for partial matches with adjusted weights
|
||||
this.addScoreForStrings(tokens, note.title, 2.0); // Increased to give more weight to title matches
|
||||
this.addScoreForStrings(tokens, this.notePathTitle, 0.3); // Reduced to further de-emphasize path matches
|
||||
|
||||
if (note.isInHiddenSubtree()) {
|
||||
this.score = this.score / 2;
|
||||
this.score = this.score / 3; // Increased penalty for hidden notes
|
||||
}
|
||||
}
|
||||
|
||||
addScoreForStrings(tokens: string[], str: string, factor: number) {
|
||||
const chunks = str.toLowerCase().split(" ");
|
||||
|
||||
this.score = 0;
|
||||
|
||||
let tokenScore = 0;
|
||||
for (const chunk of chunks) {
|
||||
for (const token of tokens) {
|
||||
if (chunk === token) {
|
||||
this.score += 4 * token.length * factor;
|
||||
tokenScore += 4 * token.length * factor;
|
||||
} else if (chunk.startsWith(token)) {
|
||||
this.score += 2 * token.length * factor;
|
||||
tokenScore += 2 * token.length * factor;
|
||||
} else if (chunk.includes(token)) {
|
||||
this.score += token.length * factor;
|
||||
tokenScore += token.length * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.score += tokenScore;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -340,9 +340,9 @@ function findFirstNoteWithQuery(query: string, searchContext: SearchContext): BN
|
||||
return searchResults.length > 0 ? becca.notes[searchResults[0].noteId] : null;
|
||||
}
|
||||
|
||||
function searchNotesForAutocomplete(query: string) {
|
||||
function searchNotesForAutocomplete(query: string, fastSearch: boolean = true) {
|
||||
const searchContext = new SearchContext({
|
||||
fastSearch: true,
|
||||
fastSearch: fastSearch,
|
||||
includeArchivedNotes: false,
|
||||
includeHiddenNotes: true,
|
||||
fuzzyAttributeSearch: true,
|
||||
|
||||
Reference in New Issue
Block a user