mirror of
https://github.com/zadam/trilium.git
synced 2025-11-10 00:19:04 +01:00
Merge branch 'main' of https://github.com/TriliumNext/Trilium into feat/ui-improvements
This commit is contained in:
commit
40b08e1828
@ -2432,4 +2432,8 @@ iframe.print-iframe {
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.excalidraw.theme--dark canvas {
|
||||
--theme-filter: invert(100%) hue-rotate(180deg);
|
||||
}
|
||||
@ -47,8 +47,9 @@ export default class RightDropdownButtonWidget extends BasicWidget {
|
||||
}
|
||||
});
|
||||
|
||||
this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title);
|
||||
this.tooltip = new Tooltip(this.$tooltip[0], {
|
||||
this.$widget.attr("title", this.title);
|
||||
this.tooltip = Tooltip.getOrCreateInstance(this.$widget[0], {
|
||||
trigger: "hover",
|
||||
placement: handleRightToLeftPlacement(this.settings.titlePlacement),
|
||||
fallbackPlacements: [ handleRightToLeftPlacement(this.settings.titlePlacement) ]
|
||||
});
|
||||
@ -56,9 +57,7 @@ export default class RightDropdownButtonWidget extends BasicWidget {
|
||||
this.$widget
|
||||
.find(".right-dropdown-button")
|
||||
.addClass(this.iconClass)
|
||||
.on("click", () => this.tooltip.hide())
|
||||
.on("mouseenter", () => this.tooltip.show())
|
||||
.on("mouseleave", () => this.tooltip.hide());
|
||||
.on("click", () => this.tooltip.hide());
|
||||
|
||||
this.$widget.on("show.bs.dropdown", async () => {
|
||||
await this.dropdownShown();
|
||||
|
||||
@ -141,7 +141,11 @@ function NoteContent({ note, trim, noChildrenList, highlightedTokens }: { note:
|
||||
})
|
||||
.then(({ $renderedContent, type }) => {
|
||||
if (!contentRef.current) return;
|
||||
contentRef.current.replaceChildren(...$renderedContent);
|
||||
if ($renderedContent[0].innerHTML) {
|
||||
contentRef.current.replaceChildren(...$renderedContent);
|
||||
} else {
|
||||
contentRef.current.replaceChildren();
|
||||
}
|
||||
contentRef.current.classList.add(`type-${type}`);
|
||||
highlightSearch(contentRef.current);
|
||||
})
|
||||
|
||||
@ -46,7 +46,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
|
||||
const parentComponent = useContext(ParentComponent);
|
||||
const canBeConvertedToAttachment = note?.isEligibleForConversionToAttachment();
|
||||
const isSearchable = ["text", "code", "book", "mindMap", "doc"].includes(note.type);
|
||||
const isInOptions = note.noteId.startsWith("_options");
|
||||
const isInOptionsOrHelp = note?.noteId.startsWith("_options") || note?.noteId.startsWith("_help");
|
||||
const isPrintable = ["text", "code"].includes(note.type) || (note.type === "book" && note.getLabelValue("viewType") === "presentation");
|
||||
const isElectron = getIsElectron();
|
||||
const isMac = getIsMac();
|
||||
@ -69,10 +69,10 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
|
||||
<FormDropdownDivider />
|
||||
|
||||
<CommandItem icon="bx bx-import" text={t("note_actions.import_files")}
|
||||
disabled={isInOptions || note.type === "search"}
|
||||
disabled={isInOptionsOrHelp || note.type === "search"}
|
||||
command={() => parentComponent?.triggerCommand("showImportDialog", { noteId: note.noteId })} />
|
||||
<CommandItem icon="bx bx-export" text={t("note_actions.export_note")}
|
||||
disabled={isInOptions || note.noteId === "_backendLog"}
|
||||
disabled={isInOptionsOrHelp || note.noteId === "_backendLog"}
|
||||
command={() => noteContext?.notePath && parentComponent?.triggerCommand("showExportDialog", {
|
||||
notePath: noteContext.notePath,
|
||||
defaultType: "single"
|
||||
@ -84,14 +84,14 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not
|
||||
<CommandItem command="showNoteSource" icon="bx bx-code" disabled={!hasSource} text={t("note_actions.note_source")} />
|
||||
<FormDropdownDivider />
|
||||
|
||||
<CommandItem command="forceSaveRevision" icon="bx bx-save" disabled={isInOptions} text={t("note_actions.save_revision")} />
|
||||
<CommandItem command="forceSaveRevision" icon="bx bx-save" disabled={isInOptionsOrHelp} text={t("note_actions.save_revision")} />
|
||||
<CommandItem icon="bx bx-trash destructive-action-icon" text={t("note_actions.delete_note")} destructive
|
||||
disabled={isInOptions}
|
||||
disabled={isInOptionsOrHelp}
|
||||
command={() => branches.deleteNotes([note.getParentBranches()[0].branchId])}
|
||||
/>
|
||||
<FormDropdownDivider />
|
||||
|
||||
<CommandItem command="showAttachments" icon="bx bx-paperclip" disabled={isInOptions} text={t("note_actions.note_attachments")} />
|
||||
<CommandItem command="showAttachments" icon="bx bx-paperclip" disabled={isInOptionsOrHelp} text={t("note_actions.note_attachments")} />
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
@ -152,14 +152,14 @@ function restoreRevision(req: Request) {
|
||||
}
|
||||
|
||||
function getEditedNotesOnDate(req: Request) {
|
||||
const noteIds = sql.getColumn<string>(
|
||||
`
|
||||
const noteIds = sql.getColumn<string>(/*sql*/`\
|
||||
SELECT notes.*
|
||||
FROM notes
|
||||
WHERE noteId IN (
|
||||
SELECT noteId FROM notes
|
||||
WHERE notes.dateCreated LIKE :date
|
||||
OR notes.dateModified LIKE :date
|
||||
WHERE
|
||||
(notes.dateCreated LIKE :date OR notes.dateModified LIKE :date)
|
||||
AND (noteId NOT LIKE '_%')
|
||||
UNION ALL
|
||||
SELECT noteId FROM revisions
|
||||
WHERE revisions.dateLastEdited LIKE :date
|
||||
|
||||
@ -681,3 +681,34 @@ describe("#normalizeCustomHandlerPattern", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#slugify", () => {
|
||||
it("should return a slugified string", () => {
|
||||
const testString = "This is a Test String! With unicode & Special #Chars.";
|
||||
const expectedSlug = "this-is-a-test-string-with-unicode-special-chars";
|
||||
const result = utils.slugify(testString);
|
||||
expect(result).toBe(expectedSlug);
|
||||
});
|
||||
|
||||
it("supports CJK characters without alteration", () => {
|
||||
const testString = "测试中文字符";
|
||||
const expectedSlug = "测试中文字符";
|
||||
const result = utils.slugify(testString);
|
||||
expect(result).toBe(expectedSlug);
|
||||
});
|
||||
|
||||
it("supports Cyrillic characters without alteration", () => {
|
||||
const testString = "Тестирование кириллических символов";
|
||||
const expectedSlug = "тестирование-кириллических-символов";
|
||||
const result = utils.slugify(testString);
|
||||
expect(result).toBe(expectedSlug);
|
||||
});
|
||||
|
||||
// preserves diacritic marks
|
||||
it("preserves diacritic marks", () => {
|
||||
const testString = "Café naïve façade jalapeño";
|
||||
const expectedSlug = "café-naïve-façade-jalapeño";
|
||||
const result = utils.slugify(testString);
|
||||
expect(result).toBe(expectedSlug);
|
||||
});
|
||||
});
|
||||
|
||||
@ -497,6 +497,14 @@ export function formatSize(size: number | null | undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
function slugify(text: string) {
|
||||
return text
|
||||
.normalize("NFC") // keep composed form, preserves accents
|
||||
.toLowerCase()
|
||||
.replace(/[^\p{Letter}\p{Number}]+/gu, "-") // replace non-letter/number with "-"
|
||||
.replace(/(^-|-$)+/g, ""); // trim dashes
|
||||
}
|
||||
|
||||
export default {
|
||||
compareVersions,
|
||||
crash,
|
||||
@ -532,6 +540,7 @@ export default {
|
||||
safeExtractMessageAndStackFromError,
|
||||
sanitizeSqlIdentifier,
|
||||
stripTags,
|
||||
slugify,
|
||||
timeLimit,
|
||||
toBase64,
|
||||
toMap,
|
||||
|
||||
@ -175,7 +175,8 @@ function register(router: Router) {
|
||||
appPath: isDev ? appPath : `../${appPath}`,
|
||||
showLoginInShareTheme,
|
||||
t,
|
||||
isDev
|
||||
isDev,
|
||||
utils
|
||||
};
|
||||
let useDefaultView = true;
|
||||
|
||||
|
||||
@ -90,9 +90,9 @@ const currentTheme = note.getLabel("shareTheme") === "light" ? "light" : "dark";
|
||||
const themeClass = currentTheme === "light" ? " theme-light" : " theme-dark";
|
||||
const headingRe = /(<h[1-6]>)(.+?)(<\/h[1-6]>)/g;
|
||||
const headingMatches = [...content.matchAll(headingRe)];
|
||||
const slugify = (text) => text.toLowerCase().replace(/[^\w]/g, "-");
|
||||
content = content.replaceAll(headingRe, (...match) => {
|
||||
match[0] = match[0].replace(match[3], `<a id="${slugify(match[2])}" class="toc-anchor" name="${slugify(match[2])}" href="#${slugify(match[2])}">#</a>${match[3]}`);
|
||||
const slug = utils.slugify(utils.stripTags(match[2]));
|
||||
match[0] = match[0].replace(match[3], `<a id="${slug}" class="toc-anchor" name="${slug}" href="#${slug}">#</a>${match[3]}`);
|
||||
return match[0];
|
||||
});
|
||||
%>
|
||||
|
||||
@ -1,12 +1,11 @@
|
||||
<%
|
||||
const slugify = (text) => text.toLowerCase().replace(/[^\w]/g, "-");
|
||||
const slug = slugify(entry.name);
|
||||
const strippedName = utils.stripTags(entry.name);
|
||||
const slug = utils.slugify(strippedName);
|
||||
%>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="#<%= slug %>">
|
||||
<span><%= entry.name %></span>
|
||||
<span><%= strippedName %></span>
|
||||
</a>
|
||||
|
||||
<% if (entry.children.length) { %>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user