Merge branch 'develop' into open_in_file_manager_of_data

This commit is contained in:
SiriusXT
2024-09-09 14:50:01 +08:00
61 changed files with 2327 additions and 2204 deletions

View File

@@ -14,6 +14,7 @@ import custom from "./routes/custom.js";
import error_handlers from "./routes/error_handlers.js";
import { startScheduledCleanup } from "./services/erase.js";
import sql_init from "./services/sql_init.js";
import { t } from "i18next";
await import('./services/handlers.js');
await import('./becca/becca_loader.js');
@@ -29,6 +30,11 @@ sql_init.initializeDb();
app.set('views', path.join(scriptDir, 'views'));
app.set('view engine', 'ejs');
app.use((req, res, next) => {
res.locals.t = t;
return next();
});
if (!utils.isElectron()) {
app.use(compression()); // HTTP compression
}

View File

@@ -91,7 +91,7 @@ export interface BranchRow {
* end user. Those types should be used only for checking against, they are
* not for direct use.
*/
export const ALLOWED_NOTE_TYPES = [ "file", "image", "search", "noteMap", "launcher", "doc", "contentWidget", "text", "relationMap", "render", "canvas", "mermaid", "book", "webView", "code" ] as const;
export const ALLOWED_NOTE_TYPES = [ "file", "image", "search", "noteMap", "launcher", "doc", "contentWidget", "text", "relationMap", "render", "canvas", "mermaid", "book", "webView", "code", "mindMap" ] as const;
export type NoteType = typeof ALLOWED_NOTE_TYPES[number];
export interface NoteRow {

13
src/main.ts Normal file
View File

@@ -0,0 +1,13 @@
/*
* Make sure not to import any modules that depend on localized messages via i18next here, as the initializations
* are loaded later and will result in an empty string.
*/
import { initializeTranslations } from "./services/i18n.js";
async function startApplication() {
await import("./www.js");
}
await initializeTranslations();
await startApplication();

View File

@@ -8,6 +8,7 @@ import noteTypesService from "../services/note_types.js";
import server from "../services/server.js";
import toastService from "../services/toast.js";
import dialogService from "../services/dialog.js";
import { t } from "../services/i18n.js";
export default class TreeContextMenu {
/**
@@ -48,55 +49,55 @@ export default class TreeContextMenu {
const insertNoteAfterEnabled = isNotRoot && !isHoisted && parentNotSearch;
return [
{ title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "bx bx-empty", enabled: noSelectedNotes },
{ title: 'Open in a new split', command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "bx bx-plus",
{ title: `${t("tree-context-menu.open-in-a-new-tab")} <kbd>Ctrl+Click</kbd>`, command: "openInTab", uiIcon: "bx bx-empty", enabled: noSelectedNotes },
{ title: t("tree-context-menu.open-in-a-new-split"), command: "openNoteInSplit", uiIcon: "bx bx-dock-right", enabled: noSelectedNotes },
{ title: `${t("tree-context-menu.insert-note-after")} <kbd data-command="createNoteAfter"></kbd>`, command: "insertNoteAfter", uiIcon: "bx bx-plus",
items: insertNoteAfterEnabled ? await noteTypesService.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes && notOptions },
{ title: 'Insert child note <kbd data-command="createNoteInto"></kbd>', command: "insertChildNote", uiIcon: "bx bx-plus",
{ title: `${t("tree-context-menu.insert-child-note")} <kbd data-command="createNoteInto"></kbd>`, command: "insertChildNote", uiIcon: "bx bx-plus",
items: notSearch ? await noteTypesService.getNoteTypeItems("insertChildNote") : null,
enabled: notSearch && noSelectedNotes && notOptions },
{ title: 'Delete <kbd data-command="deleteNotes"></kbd>', command: "deleteNotes", uiIcon: "bx bx-trash",
{ title: `${t("tree-context-menu.delete")} <kbd data-command="deleteNotes"></kbd>`, command: "deleteNotes", uiIcon: "bx bx-trash",
enabled: isNotRoot && !isHoisted && parentNotSearch && notOptions },
{ title: "----" },
{ title: 'Search in subtree <kbd data-command="searchInSubtree"></kbd>', command: "searchInSubtree", uiIcon: "bx bx-search",
{ title: `${t("tree-context-menu.search-in-subtree")} <kbd data-command="searchInSubtree"></kbd>`, command: "searchInSubtree", uiIcon: "bx bx-search",
enabled: notSearch && noSelectedNotes },
isHoisted ? null : { title: 'Hoist note <kbd data-command="toggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch },
!isHoisted || !isNotRoot ? null : { title: 'Unhoist note <kbd data-command="toggleNoteHoisting"></kbd>', command: "toggleNoteHoisting", uiIcon: "bx bx-door-open" },
{ title: 'Edit branch prefix <kbd data-command="editBranchPrefix"></kbd>', command: "editBranchPrefix", uiIcon: "bx bx-empty",
{ title: `${t("tree-context-menu.edit-branch-prefix")} <kbd data-command="editBranchPrefix"></kbd>`, command: "editBranchPrefix", uiIcon: "bx bx-empty",
enabled: isNotRoot && parentNotSearch && noSelectedNotes && notOptions },
{ title: "Advanced", uiIcon: "bx bx-empty", enabled: true, items: [
{ title: 'Expand subtree <kbd data-command="expandSubtree"></kbd>', command: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
{ title: 'Sort by ... <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch },
{ title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes && notOptions },
{ title: 'Convert to attachment', command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted && notOptions },
{ title: 'Copy note path to clipboard', command: "copyNotePathToClipboard", uiIcon: "bx bx-empty", enabled: true }
{ title: t("tree-context-menu.advanced"), uiIcon: "bx bx-empty", enabled: true, items: [
{ title: `${t("tree-context-menu.expand-subtree")} <kbd data-command="expandSubtree"></kbd>`, command: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: `${t("tree-context-menu.collapse-subtree")} <kbd data-command="collapseSubtree"></kbd>`, command: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
{ title: `${t("tree-context-menu.sort-by")} <kbd data-command="sortChildNotes"></kbd>`, command: "sortChildNotes", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch },
{ title: t("tree-context-menu.recent-changes-in-subtree"), command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes && notOptions },
{ title: t("tree-context-menu.convert-to-attachment"), command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted && notOptions },
{ title: t("tree-context-menu.copy-note-path-to-clipboard"), command: "copyNotePathToClipboard", uiIcon: "bx bx-empty", enabled: true }
] },
{ title: "----" },
{ title: "Protect subtree", command: "protectSubtree", uiIcon: "bx bx-check-shield", enabled: noSelectedNotes },
{ title: "Unprotect subtree", command: "unprotectSubtree", uiIcon: "bx bx-shield", enabled: noSelectedNotes },
{ title: t("tree-context-menu.protect-subtree"), command: "protectSubtree", uiIcon: "bx bx-check-shield", enabled: noSelectedNotes },
{ title: t("tree-context-menu.unprotect-subtree"), command: "unprotectSubtree", uiIcon: "bx bx-shield", enabled: noSelectedNotes },
{ title: "----" },
{ title: 'Copy / clone <kbd data-command="copyNotesToClipboard"></kbd>', command: "copyNotesToClipboard", uiIcon: "bx bx-copy",
{ title: `${t("tree-context-menu.copy-clone")} <kbd data-command="copyNotesToClipboard"></kbd>`, command: "copyNotesToClipboard", uiIcon: "bx bx-copy",
enabled: isNotRoot && !isHoisted },
{ title: 'Clone to ... <kbd data-command="cloneNotesTo"></kbd>', command: "cloneNotesTo", uiIcon: "bx bx-empty",
{ title: `${t("tree-context-menu.clone-to")} <kbd data-command="cloneNotesTo"></kbd>`, command: "cloneNotesTo", uiIcon: "bx bx-empty",
enabled: isNotRoot && !isHoisted },
{ title: 'Cut <kbd data-command="cutNotesToClipboard"></kbd>', command: "cutNotesToClipboard", uiIcon: "bx bx-cut",
{ title: `${t("tree-context-menu.cut")} <kbd data-command="cutNotesToClipboard"></kbd>`, command: "cutNotesToClipboard", uiIcon: "bx bx-cut",
enabled: isNotRoot && !isHoisted && parentNotSearch },
{ title: 'Move to ... <kbd data-command="moveNotesTo"></kbd>', command: "moveNotesTo", uiIcon: "bx bx-empty",
{ title: `${t("tree-context-menu.move-to")} <kbd data-command="moveNotesTo"></kbd>`, command: "moveNotesTo", uiIcon: "bx bx-empty",
enabled: isNotRoot && !isHoisted && parentNotSearch },
{ title: 'Paste into <kbd data-command="pasteNotesFromClipboard"></kbd>', command: "pasteNotesFromClipboard", uiIcon: "bx bx-paste",
{ title: `${t("tree-context-menu.paste-into")} <kbd data-command="pasteNotesFromClipboard"></kbd>`, command: "pasteNotesFromClipboard", uiIcon: "bx bx-paste",
enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes },
{ title: 'Paste after', command: "pasteNotesAfterFromClipboard", uiIcon: "bx bx-paste",
{ title: t("tree-context-menu.paste-after"), command: "pasteNotesAfterFromClipboard", uiIcon: "bx bx-paste",
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes },
{ title: `Duplicate subtree <kbd data-command="duplicateSubtree">`, command: "duplicateSubtree", uiIcon: "bx bx-empty",
{ title: `${t("tree-context-menu.duplicate-subtree")} <kbd data-command="duplicateSubtree">`, command: "duplicateSubtree", uiIcon: "bx bx-empty",
enabled: parentNotSearch && isNotRoot && !isHoisted && notOptions },
{ title: "----" },
{ title: "Export", command: "exportNote", uiIcon: "bx bx-empty",
{ title: t("tree-context-menu.export"), command: "exportNote", uiIcon: "bx bx-empty",
enabled: notSearch && noSelectedNotes && notOptions },
{ title: "Import into note", command: "importIntoNote", uiIcon: "bx bx-empty",
{ title: t("tree-context-menu.import-into-note"), command: "importIntoNote", uiIcon: "bx bx-empty",
enabled: notSearch && noSelectedNotes && notOptions },
{ title: "Apply bulk actions", command: "openBulkActionsDialog", uiIcon: "bx bx-list-plus",
{ title: t("tree-context-menu.apply-bulk-actions"), command: "openBulkActionsDialog", uiIcon: "bx bx-list-plus",
enabled: true }
].filter(row => row !== null);
}

View File

@@ -1,19 +1,20 @@
import server from "./server.js";
import froca from "./froca.js";
import { t } from "./i18n.js";
async function getNoteTypeItems(command) {
const items = [
{ title: "Text", command: command, type: "text", uiIcon: "bx bx-note" },
{ title: "Code", command: command, type: "code", uiIcon: "bx bx-code" },
{ title: "Saved Search", command: command, type: "search", uiIcon: "bx bx-file-find" },
{ title: "Relation Map", command: command, type: "relationMap", uiIcon: "bx bx-map-alt" },
{ title: "Note Map", command: command, type: "noteMap", uiIcon: "bx bx-map-alt" },
{ title: "Render Note", command: command, type: "render", uiIcon: "bx bx-extension" },
{ title: "Book", command: command, type: "book", uiIcon: "bx bx-book" },
{ title: "Mermaid Diagram", command: command, type: "mermaid", uiIcon: "bx bx-selection" },
{ title: "Canvas", command: command, type: "canvas", uiIcon: "bx bx-pen" },
{ title: "Web View", command: command, type: "webView", uiIcon: "bx bx-globe-alt" },
{ title: "Mind Map", command, type: "mindMap", uiIcon: "bx bx-sitemap" }
{ title: t("note_types.text"), command: command, type: "text", uiIcon: "bx bx-note" },
{ title: t("note_types.code"), command: command, type: "code", uiIcon: "bx bx-code" },
{ title: t("note_types.saved-search"), command: command, type: "search", uiIcon: "bx bx-file-find" },
{ title: t("note_types.relation-map"), command: command, type: "relationMap", uiIcon: "bx bx-map-alt" },
{ title: t("note_types.note-map"), command: command, type: "noteMap", uiIcon: "bx bx-map-alt" },
{ title: t("note_types.render-note"), command: command, type: "render", uiIcon: "bx bx-extension" },
{ title: t("note_types.book"), command: command, type: "book", uiIcon: "bx bx-book" },
{ title: t("note_types.mermaid-diagram"), command: command, type: "mermaid", uiIcon: "bx bx-selection" },
{ title: t("note_types.canvas"), command: command, type: "canvas", uiIcon: "bx bx-pen" },
{ title: t("note_types.web-view"), command: command, type: "webView", uiIcon: "bx bx-globe-alt" },
{ title: t("note_types.mind-map"), command, type: "mindMap", uiIcon: "bx bx-sitemap" }
];
const templateNoteIds = await server.get("search-templates");

View File

@@ -128,7 +128,10 @@ export default class CalendarWidget extends RightDropdownButtonWidget {
else {
toastService.showError(t("calendar.cannot_find_day_note"));
}
});
});
// Prevent dismissing the calendar popup by clicking on an empty space inside it.
this.$dropdownContent.on("click", (e) => e.stopPropagation());
}
manageFirstDayOfWeek() {

View File

@@ -17,7 +17,7 @@ const TPL = `
}
.note-actions .dropdown-menu {
width: 15em;
min-width: 15em;
}
.note-actions .dropdown-item[disabled], .note-actions .dropdown-item[disabled]:hover {

View File

@@ -7,7 +7,6 @@ export default class OnClickButtonWidget extends AbstractButtonWidget {
if (this.settings.onClick) {
this.$widget.on("click", e => {
e.stopPropagation();
this.$widget.tooltip("hide");
this.settings.onClick(this, e);

View File

@@ -33,7 +33,7 @@ const TPL = `
${t("note_type_chooser.modal_body")}
<div class="dropdown">
<button class="note-type-dropdown-trigger" type="button" style="display: none;" data-toggle="dropdown">${t("note_type_chooser.dropdown_trigger")}</button>
<button class="note-type-dropdown-trigger" type="button" style="display: none;" data-toggle="dropdown"></button>
<div class="note-type-dropdown dropdown-menu"></div>
</div>

View File

@@ -54,7 +54,7 @@ const TPL = `
</div>
<div class="modal-body" style="display: flex; height: 80vh;">
<div class="dropdown">
<button class="revision-list-dropdown" type="button" style="display: none;" data-toggle="dropdown">${t("revisions.dropdown_trigger")}</button>
<button class="revision-list-dropdown" type="button" style="display: none;" data-toggle="dropdown"></button>
<div class="revision-list dropdown-menu" style="position: static; height: 100%; overflow: auto;"></div>
</div>
@@ -274,7 +274,7 @@ export default class RevisionsDialog extends BasicWidget {
}
this.$content.html($table);
} else if (revisionItem.type === 'canvas') {
} else if ([ "canvas", "mindMap" ].includes(revisionItem.type)) {
const encodedTitle = encodeURIComponent(revisionItem.title);
this.$content.html($("<img>")

View File

@@ -6,6 +6,7 @@ import appContext from "../components/app_context.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import linkContextMenuService from "../menus/link_context_menu.js";
import utils from "../services/utils.js";
import { t } from "../services/i18n.js";
const esc = utils.escapeHtml;
@@ -30,8 +31,8 @@ const TPL = `<div class="note-map-widget" style="position: relative;">
</style>
<div class="btn-group btn-group-sm map-type-switcher" role="group">
<button type="button" class="btn bx bx-network-chart" title="Link Map" data-type="link"></button>
<button type="button" class="btn bx bx-sitemap" title="Tree map" data-type="tree"></button>
<button type="button" class="btn bx bx-network-chart" title="${t("note-map.button-link-map")}" data-type="link"></button>
<button type="button" class="btn bx bx-sitemap" title="${t("note-map.button-tree-map")}" data-type="tree"></button>
</div>
<div class="style-resolver"></div>

View File

@@ -2,25 +2,26 @@ import server from '../services/server.js';
import mimeTypesService from '../services/mime_types.js';
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import dialogService from "../services/dialog.js";
import { t } from '../services/i18n.js';
const NOTE_TYPES = [
{ type: "file", title: "File", selectable: false },
{ type: "image", title: "Image", selectable: false },
{ type: "search", title: "Saved Search", selectable: false },
{ type: "noteMap", mime: '', title: "Note Map", selectable: false },
{ type: "launcher", mime: '', title: "Launcher", selectable: false },
{ type: "doc", mime: '', title: "Doc", selectable: false },
{ type: "contentWidget", mime: '', title: "Widget", selectable: false },
{ type: "file", title: t("note_types.file"), selectable: false },
{ type: "image", title: t("note_types.image"), selectable: false },
{ type: "search", title: t("note_types.saved-search"), selectable: false },
{ type: "noteMap", mime: '', title: t("note_types.note-map"), selectable: false },
{ type: "launcher", mime: '', title: t("note_types.launcher"), selectable: false },
{ type: "doc", mime: '', title: t("note_types.doc"), selectable: false },
{ type: "contentWidget", mime: '', title: t("note_types.widget"), selectable: false },
{ type: "text", mime: "text/html", title: "Text", selectable: true },
{ type: "relationMap", mime: "application/json", title: "Relation Map", selectable: true },
{ type: "mindMap", mime: "application/json", "title": "Mind Map", selectable: true },
{ type: "render", mime: '', title: "Render Note", selectable: true },
{ type: "canvas", mime: 'application/json', title: "Canvas", selectable: true },
{ type: "mermaid", mime: 'text/mermaid', title: "Mermaid Diagram", selectable: true },
{ type: "book", mime: '', title: "Book", selectable: true },
{ type: "webView", mime: '', title: "Web View", selectable: true },
{ type: "code", mime: 'text/plain', title: "Code", selectable: true }
{ type: "text", mime: "text/html", title: t("note_types.text"), selectable: true },
{ type: "relationMap", mime: "application/json", title: t("note_types.relation-map"), selectable: true },
{ type: "mindMap", mime: "application/json", "title": t("note_types.mind-map"), selectable: true },
{ type: "render", mime: '', title: t("note_types.render-note"), selectable: true },
{ type: "canvas", mime: 'application/json', title: t("note_types.canvas"), selectable: true },
{ type: "mermaid", mime: 'text/mermaid', title: t("note_types.mermaid-diagram"), selectable: true },
{ type: "book", mime: '', title: t("note_types.book"), selectable: true },
{ type: "webView", mime: '', title: t("note_types.web-view"), selectable: true },
{ type: "code", mime: 'text/plain', title: t("note_types.code"), selectable: true }
];
const NOT_SELECTABLE_NOTE_TYPES = NOTE_TYPES.filter(nt => !nt.selectable).map(nt => nt.type);

View File

@@ -1,3 +1,4 @@
import { t } from "../services/i18n.js";
import protectedSessionService from "../services/protected_session.js";
import SwitchWidget from "./switch.js";
@@ -5,11 +6,11 @@ export default class ProtectedNoteSwitchWidget extends SwitchWidget {
doRender() {
super.doRender();
this.$switchOnName.text("Protect the note");
this.$switchOnButton.attr("title", "Note is not protected, click to make it protected");
this.$switchOnName.text(t("protect_note.toggle-on"));
this.$switchOnButton.attr("title", t("protect_note.toggle-on-hint"));
this.$switchOffName.text("Unprotect the note");
this.$switchOffButton.attr("title", "Note is protected, click to make it unprotected");
this.$switchOffName.text(t("protect_note.toggle-off"));
this.$switchOffButton.attr("title", t("protect_note.toggle-off-hint"));
}
switchOn() {

View File

@@ -1,6 +1,7 @@
import NoteContextAwareWidget from "./note_context_aware_widget.js";
import options from "../services/options.js";
import attributeService from "../services/attributes.js";
import { t } from "../services/i18n.js";
const TPL = `
<div class="shared-info-widget alert alert-warning">
@@ -13,7 +14,7 @@ const TPL = `
}
</style>
<span class="shared-text"></span> <a class="shared-link external"></a>. For help visit <a href="https://triliumnext.github.io/Docs/Wiki/sharing.html">wiki</a>.
<span class="shared-text"></span> <a class="shared-link external"></a>. ${t("shared_info.help_link")}
</div>`;
export default class SharedInfoWidget extends NoteContextAwareWidget {
@@ -36,7 +37,7 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
if (syncServerHost) {
link = `${syncServerHost}/share/${shareId}`;
this.$sharedText.text("This note is shared publicly on");
this.$sharedText.text(t("shared_info.shared_publicly"));
}
else {
let host = location.host;
@@ -47,7 +48,7 @@ export default class SharedInfoWidget extends NoteContextAwareWidget {
}
link = `${location.protocol}//${host}${location.pathname}share/${shareId}`;
this.$sharedText.text("This note is shared locally on");
this.$sharedText.text(t("shared_info.shared_locally"));
}
this.$sharedLink.attr("href", link).text(link);

View File

@@ -4,6 +4,7 @@ import server from "../services/server.js";
import utils from "../services/utils.js";
import syncService from "../services/sync.js";
import dialogService from "../services/dialog.js";
import { t } from "../services/i18n.js";
export default class SharedSwitchWidget extends SwitchWidget {
isEnabled() {
@@ -15,11 +16,11 @@ export default class SharedSwitchWidget extends SwitchWidget {
doRender() {
super.doRender();
this.$switchOnName.text("Shared");
this.$switchOnButton.attr("title", "Share the note");
this.$switchOnName.text(t("shared_switch.shared"));
this.$switchOnButton.attr("title", t("shared_switch.toggle-on-title"));
this.$switchOffName.text("Shared");
this.$switchOffButton.attr("title", "Unshare the note");
this.$switchOffName.text(t("shared_switch.shared"));
this.$switchOffButton.attr("title", t("shared_switch.toggle-off-title"));
this.$helpButton.attr("data-help-page", "sharing.html").show();
this.$helpButton.on('click', e => utils.openHelp($(e.target)));
@@ -39,9 +40,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
}
if (this.note.getParentBranches().length === 1) {
const text = "This note exists only as a shared note, unsharing would delete it. Do you want to continue and thus delete this note?";
if (!await dialogService.confirm(text)) {
if (!await dialogService.confirm(t("shared_switch.shared-branch"))) {
return;
}
}
@@ -60,7 +59,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
this.$switchOff.toggle(!!isShared);
if (switchDisabled) {
this.$widget.attr("title", "Note cannot be unshared here because it is shared through inheritance from an ancestor.");
this.$widget.attr("title", t("shared_switch.inherited"));
this.$switchOff.addClass("switch-disabled");
}
else {

View File

@@ -1,3 +1,4 @@
import { t } from "../services/i18n.js";
import NoteContextAwareWidget from "./note_context_aware_widget.js";
const TPL = `
@@ -90,7 +91,7 @@ const TPL = `
</span>
</div>
<button class="switch-help-button" type="button" data-help-page="" title="Open help page" style="display: none;">?</button>
<button class="switch-help-button" type="button" data-help-page="" title="${t("open-help-page")}" style="display: none;">?</button>
</div>`;
export default class SwitchWidget extends NoteContextAwareWidget {

View File

@@ -1,5 +1,6 @@
import SwitchWidget from "./switch.js";
import attributeService from "../services/attributes.js";
import { t } from "../services/i18n.js";
/**
* Switch for the basic properties widget which allows the user to select whether the note is a template or not, which toggles the `#template` attribute.
@@ -14,11 +15,11 @@ export default class TemplateSwitchWidget extends SwitchWidget {
doRender() {
super.doRender();
this.$switchOnName.text("Template");
this.$switchOnButton.attr("title", "Make the note a template");
this.$switchOnName.text(t("template_switch.template"));
this.$switchOnButton.attr("title", t("template_switch.toggle-on-hint"));
this.$switchOffName.text("Template");
this.$switchOffButton.attr("title", "Remove the note as a template");
this.$switchOffButton.attr("title", t("template_switch.toggle-off-hint"));
this.$helpButton.attr("data-help-page", "template.html").show();
this.$helpButton.on('click', e => utils.openHelp($(e.target)));

View File

@@ -26,6 +26,15 @@ export default class MindMapWidget extends TypeWidget {
doRender() {
this.$widget = $(TPL);
this.$content = this.$widget.find(".mind-map-container");
this.$content.on("keydown", (e) => {
/*
* Some global shortcuts interfere with the default shortcuts of the mind map,
* as defined here: https://mind-elixir.com/docs/guides/shortcuts
*/
if (e.key === "F1") {
e.stopPropagation();
}
});
super.doRender();
}

View File

@@ -9,7 +9,7 @@ const TPL = `
<p>${t("max_content_width.default_description")}</p>
<div class="form-group row">
<div class="col-4">
<div class="col-6">
<label>${t("max_content_width.max_width_label")}</label>
<input type="number" min="200" step="10" class="max-content-width form-control options-number-input">
</div>

View File

@@ -14,7 +14,7 @@ const TPL_ELECTRON = `
<div class="options-section">
<h4>${t('spellcheck.title')}</h4>
<p>App restart is required after change.</p>
<p>${t("spellcheck.restart-required")}</p>
<label>
<input type="checkbox" class="spell-check-enabled">

View File

@@ -924,6 +924,10 @@ li.dropdown-submenu:hover > ul.dropdown-menu {
margin: 10px;
}
.help-dropdown code {
font-variant-ligatures: none;
}
#launcher-pane .launcher-button {
font-size: 150%;
display: inline-block;

View File

@@ -198,14 +198,13 @@
},
"markdown_import": {
"dialog_title": "Markdown 导入",
"modal_body_text": "由于浏览器沙箱的限制,无法直接从 JavaScript 读取剪贴板内容。请将要导入的 Markdown 文本粘贴到下面的文本框中,然后点击导入按钮",
"modal_body_text": "由于浏览器沙箱的限制,无法直接从 JavaScript 读取剪贴板内容。请将要导入的 Markdown 文本粘贴到下面的文本框中,然后点击导入按钮",
"import_button": "导入 Ctrl+回车",
"import_success": "Markdown 内容已成功导入文档。"
},
"note_type_chooser": {
"modal_title": "选择笔记类型",
"modal_body": "选择新笔记的类型或模板:",
"dropdown_trigger": "下拉触发",
"templates": "模板:"
},
"password_not_set": {
@@ -238,7 +237,6 @@
"delete_all_revisions": "删除此笔记的所有历史版本",
"delete_all_button": "删除所有历史版本",
"help_title": "关于笔记历史版本的帮助",
"dropdown_trigger": "下拉触发器",
"revision_last_edited": "此历史版本上次编辑于 {{date}}",
"confirm_delete_all": "您是否要删除此笔记的所有历史版本?此操作将擦除历史版本的标题和内容,但仍将保留历史版本的元数据。",
"no_revisions": "此笔记暂无历史版本...",
@@ -470,8 +468,8 @@
"to": "到",
"target_parent_note": "目标父笔记",
"on_all_matched_notes": "对于所有匹配的笔记",
"move_note_new_parent": "如果笔记只有一个父笔记,则将其移动到新父笔记(即旧位置被移除,并创建到新父笔记的位置)",
"clone_note_new_parent": "如果笔记有多个克隆/位置,则将其克隆到新父笔记(不清楚应删除哪个位置)",
"move_note_new_parent": "",
"clone_note_new_parent": "",
"nothing_will_happen": "如果笔记无法移动到目标笔记(即这会创建一个树循环),则不会发生任何事情"
},
"rename_note": {
@@ -1025,8 +1023,8 @@
"title": "下拉菜单可用的MIME文件类型"
},
"vim_key_bindings": {
"use_vim_keybindings_in_code_notes": "在代码笔记中使用Vim键绑定无ex模式",
"enable_vim_keybindings": "启用Vim键绑定"
"use_vim_keybindings_in_code_notes": "",
"enable_vim_keybindings": ""
},
"wrap_lines": {
"wrap_lines_in_code_notes": "代码笔记自动换行",
@@ -1194,12 +1192,13 @@
},
"spellcheck": {
"title": "拼写检查",
"description": "这些选项仅适用于桌面版本,浏览器将使用其原生的拼写检查功能。更改后需要重启应用。",
"description": "这些选项仅适用于桌面版本,浏览器将使用其原生的拼写检查功能。",
"enable": "启用拼写检查",
"language_code_label": "语言代码",
"language_code_placeholder": "例如 \"en-US\", \"de-AT\"",
"multiple_languages_info": "多种语言可以用逗号分隔,例如 \"en-US, de-DE, cs\"。拼写检查选项的更改将在应用重启后生效。",
"available_language_codes_label": "可用的语言代码:"
"multiple_languages_info": "多种语言可以用逗号分隔,例如 \"en-US, de-DE, cs\"。",
"available_language_codes_label": "可用的语言代码:",
"restart-required": "拼写检查选项的更改将在应用重启后生效。"
},
"sync_2": {
"config_title": "同步配置",

View File

@@ -205,13 +205,12 @@
"note_type_chooser": {
"modal_title": "Choose note type",
"modal_body": "Choose note type / template of the new note:",
"dropdown_trigger": "Dropdown trigger",
"templates": "Templates:"
},
"password_not_set": {
"title": "Password is not set",
"body1": "Protected notes are encrypted using a user password, but password has not been set yet.",
"body2": "To be able to protect notes, click <a class=\"open-password-options-button\" href=\"javascript:\">here</a> to open the Options dialog and set your password.</a>"
"body2": "To be able to protect notes, click <a class=\"open-password-options-button\" href=\"javascript:\">here</a> to open the Options dialog and set your password."
},
"prompt": {
"title": "Prompt",
@@ -229,7 +228,7 @@
"title": "Recent changes",
"erase_notes_button": "Erase deleted notes now",
"deleted_notes_message": "Deleted notes have been erased.",
"no_changes_message": "No changes yet ...",
"no_changes_message": "No changes yet...",
"undelete_link": "undelete",
"confirm_undelete": "Do you want to undelete this note and its sub-notes?"
},
@@ -238,7 +237,6 @@
"delete_all_revisions": "Delete all revisions of this note",
"delete_all_button": "Delete all revisions",
"help_title": "Help on Note revisions",
"dropdown_trigger": "Dropdown trigger",
"revision_last_edited": "This revision was last edited on {{date}}",
"confirm_delete_all": "Do you want to delete all revisions of this note? This action will erase revision title and content, but still preserve revision metadata.",
"no_revisions": "No revisions for this note yet...",
@@ -256,7 +254,7 @@
"preview_not_available": "Preview isn't available for this note type."
},
"sort_child_notes": {
"sort_children_by": "Sort children by ...",
"sort_children_by": "Sort children by...",
"sorting_criteria": "Sorting criteria",
"title": "title",
"date_created": "date created",
@@ -470,8 +468,8 @@
"to": "to",
"target_parent_note": "target parent note",
"on_all_matched_notes": "On all matched notes",
"move_note_new_parent": "move note to the new parent if note has only one parent (i.e. the old placement is removed and new placement into the new parent is created)",
"clone_note_new_parent": "clone note to the new parent if note has multiple clones/placements (it's not clear which placement should be removed)",
"move_note_new_parent": "move note to the new parent if note has only one parent (i.e. the old branch is removed and new branch into the new parent is created)",
"clone_note_new_parent": "clone note to the new parent if note has multiple clones/branches (it's not clear which branch should be removed)",
"nothing_will_happen": "nothing will happen if note cannot be moved to the target note (i.e. this would create a tree cycle)"
},
"rename_note": {
@@ -752,7 +750,7 @@
"promoted_attributes": "Promoted Attributes",
"url_placeholder": "http://website...",
"open_external_link": "Open external link",
"unknown_label_type": "Unknown labelType '{{type}}'",
"unknown_label_type": "Unknown label type '{{type}}'",
"unknown_attribute_type": "Unknown attribute type '{{type}}'",
"add_new_attribute": "Add new attribute",
"remove_this_attribute": "Remove this attribute"
@@ -851,7 +849,7 @@
},
"search_string": {
"title_column": "Search string:",
"placeholder": "fulltext keywords, #tag = value ...",
"placeholder": "fulltext keywords, #tag = value...",
"search_syntax": "Search syntax",
"also_see": "also see",
"complete_help": "complete help on search syntax",
@@ -1028,8 +1026,8 @@
"title": "Available MIME types in the dropdown"
},
"vim_key_bindings": {
"use_vim_keybindings_in_code_notes": "Use vim keybindings in code notes (no ex mode)",
"enable_vim_keybindings": "Enable Vim Keybindings"
"use_vim_keybindings_in_code_notes": "Vim keybindings",
"enable_vim_keybindings": "Enable Vim keybindings in code notes (no ex mode)"
},
"wrap_lines": {
"wrap_lines_in_code_notes": "Wrap lines in code notes",
@@ -1083,7 +1081,7 @@
"save_button": "Save"
},
"tray": {
"title": "Tray",
"title": "System Tray",
"enable_tray": "Enable tray (Trilium needs to be restarted for this change to take effect)"
},
"heading_style": {
@@ -1200,12 +1198,13 @@
},
"spellcheck": {
"title": "Spell Check",
"description": "These options apply only for desktop builds, browsers will use their own native spell check. App restart is required after change.",
"description": "These options apply only for desktop builds, browsers will use their own native spell check.",
"enable": "Enable spellcheck",
"language_code_label": "Language code(s)",
"language_code_placeholder": "for example \"en-US\", \"de-AT\"",
"multiple_languages_info": "Multiple languages can be separated by comma, e.g. \"en-US, de-DE, cs\". Changes to the spell check options will take effect after application restart.",
"available_language_codes_label": "Available language codes:"
"multiple_languages_info": "Multiple languages can be separated by comma, e.g. \"en-US, de-DE, cs\". ",
"available_language_codes_label": "Available language codes:",
"restart-required": "Changes to the spell check options will take effect after application restart."
},
"sync_2": {
"config_title": "Sync Configuration",
@@ -1245,5 +1244,79 @@
"note_is_editable": "Note is editable if it's not too long.",
"note_is_read_only": "Note is read-only, but can be edited with a button click.",
"note_is_always_editable": "Note is always editable, regardless of its length."
}
},
"note-map": {
"button-link-map": "Link Map",
"button-tree-map": "Tree map"
},
"tree-context-menu": {
"open-in-a-new-tab": "Open in a new tab",
"open-in-a-new-split": "Open in a new split",
"insert-note-after": "Insert note after",
"insert-child-note": "Insert child note",
"delete": "Delete",
"search-in-subtree": "Search in subtree",
"edit-branch-prefix": "Edit branch prefix",
"advanced": "Advanced",
"expand-subtree": "Expand subtree",
"collapse-subtree": "Collapse subtree",
"sort-by": "Sort by...",
"recent-changes-in-subtree": "Recent changes in subtree",
"convert-to-attachment": "Convert to attachment",
"copy-note-path-to-clipboard": "Copy note path to clipboard",
"protect-subtree": "Protect subtree",
"unprotect-subtree": "Unprotect subtree",
"copy-clone": "Copy / clone",
"clone-to": "Clone to...",
"cut": "Cut",
"move-to": "Move to...",
"paste-into": "Paste into",
"paste-after": "Paste after",
"duplicate-subtree": "Duplicate subtree",
"export": "Export",
"import-into-note": "Import into note",
"apply-bulk-actions": "Apply bulk actions"
},
"shared_info": {
"shared_publicly": "This note is shared publicly on",
"shared_locally": "This note is shared locally on",
"help_link": "For help visit <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a>."
},
"note_types": {
"text": "Text",
"code": "Code",
"saved-search": "Saved Search",
"relation-map": "Relation Map",
"note-map": "Note Map",
"render-note": "Render Note",
"book": "Book",
"mermaid-diagram": "Mermaid Diagram",
"canvas": "Canvas",
"web-view": "Web View",
"mind-map": "Mind Map",
"file": "File",
"image": "Image",
"launcher": "Launcher",
"doc": "Doc",
"widget": "Widget"
},
"protect_note": {
"toggle-on": "Protect the note",
"toggle-off": "Unprotect the note",
"toggle-on-hint": "Note is not protected, click to make it protected",
"toggle-off-hint": "Note is protected, click to make it unprotected"
},
"shared_switch": {
"shared": "Shared",
"toggle-on-title": "Share the note",
"toggle-off-title": "Unshare the note",
"shared-branch": "This note exists only as a shared note, unsharing would delete it. Do you want to continue and thus delete this note?",
"inherited": "Note cannot be unshared here because it is shared through inheritance from an ancestor."
},
"template_switch": {
"template": "Template",
"toggle-on-hint": "Make the note a template",
"toggle-off-hint": "Remove the note as a template"
},
"open-help-page": "Open help page"
}

View File

@@ -132,7 +132,7 @@
"pasteNotes": "pegar notas como subnotas en la nota activa (que se puede mover o clonar dependiendo de si se copió o cortó en el portapapeles)",
"deleteNotes": "eliminar nota/subárbol",
"editingNotes": "Editando notas",
"editNoteTitle": "",
"editNoteTitle": "en el panel de árbol cambiará del panel de árbol al título de la nota. Ingresar desde el título de la nota cambiará el foco al editor de texto. <kbd>Ctrl+.</kbd> cambiará de nuevo del editor al panel de árbol",
"createEditLink": "crear/editar enlace externo",
"createInternalLink": "crear enlace interno",
"followLink": "siga el enlace debajo del cursor",
@@ -205,13 +205,12 @@
"note_type_chooser": {
"modal_title": "Elija el tipo de nota",
"modal_body": "Elija el tipo de nota/plantilla de la nueva nota:",
"dropdown_trigger": "Activador desplegable",
"templates": "Plantillas:"
},
"password_not_set": {
"title": "La contraseña no está establecida",
"body1": "Las notas protegidas se cifran mediante una contraseña de usuario, pero la contraseña aún no se ha establecido.",
"body2": "Para poder proteger notas, dé clic <a class=\"open-password-options-button\" href=\"javascript:\">aquí</a> para abrir el diálogo de Opciones y establecer tu contraseña.</a>"
"body2": "Para poder proteger notas, dé clic <a class=\"open-password-options-button\" href=\"javascript:\">aquí</a> para abrir el diálogo de Opciones y establecer tu contraseña."
},
"prompt": {
"title": "Aviso",
@@ -238,7 +237,6 @@
"delete_all_revisions": "Eliminar todas las revisiones de esta nota.",
"delete_all_button": "Eliminar todas las revisiones",
"help_title": "Ayuda sobre revisiones de notas",
"dropdown_trigger": "Activador desplegable",
"revision_last_edited": "Esta revisión se editó por última vez en {{date}}",
"confirm_delete_all": "¿Quiere eliminar todas las revisiones de esta nota? Esta acción borrará el título y el contenido de la revisión, pero aún conservará los metadatos de la revisión.",
"no_revisions": "Aún no hay revisiones para esta nota...",
@@ -470,15 +468,15 @@
"to": "a",
"target_parent_note": "nota padre objetivo",
"on_all_matched_notes": "En todas las notas coincidentes",
"move_note_new_parent": "mover la nota al nuevo padre si la nota tiene solo un padre (es decir, se elimina la ubicación anterior y se crea una nueva ubicación en el nuevo padre)",
"clone_note_new_parent": "clonar nota al nuevo padre si la nota tiene múltiples clones/ubicaciones (no está claro qué ubicación se debe eliminar)",
"move_note_new_parent": "",
"clone_note_new_parent": "",
"nothing_will_happen": "No pasará nada si la nota no se puede mover a la nota de destino (es decir, esto crearía un ciclo de árbol)"
},
"rename_note": {
"rename_note": "Renombrar nota",
"rename_note_title_to": "Renombrar título de la nota a",
"new_note_title": "nuevo título de nota",
"click_help_icon": "Haga clic en el icono de ayuda a la derecha para ver todas las opciones.",
"click_help_icon": "Haga clic en el icono de ayuda a la derecha para ver todas las opciones",
"evaluated_as_js_string": "El valor dado se evalúa como una cadena de JavaScript y, por lo tanto, se puede enriquecer con contenido dinámico a través de la variable <code>note</code> inyectada (se cambia el nombre de la nota). Ejemplos:",
"example_note": "<code>Nota</code>: todas las notas coincidentes son renombradas a 'Nota'",
"example_new_title": "<code>NUEVO: ${note.title}</code> - los títulos de las notas coincidentes tienen el prefijo 'NUEVO: '",
@@ -647,6 +645,9 @@
"hide_floating_buttons_button": {
"button_title": "Ocultar botones"
},
"svg_export_button": {
"button_title": "Exportar diagrama como SVG"
},
"relation_map_buttons": {
"create_child_note_title": "Crear una nueva nota hijo y agregarla a este mapa de relaciones",
"reset_pan_zoom_title": "Restablecer la panorámica y el zoom a las coordenadas y ampliación iniciales",
@@ -655,13 +656,13 @@
},
"zpetne_odkazy": {
"backlink": "{{count}} Vínculo de retroceso",
"backlinks": "{{count}} Vínculos de retroceso",
"backlinks": "{{count}} vínculos de retroceso",
"relation": "relación"
},
"mobile_detail_menu": {
"insert_child_note": "Insertar nota hijo",
"delete_this_note": "Eliminar esta nota",
"error_cannot_get_branch_id": "No se puede obtener el branchID del notePath '{{notePad}}'",
"error_cannot_get_branch_id": "No se puede obtener el branchID del notePath '{{notePath}}'",
"error_unrecognized_command": "Comando no reconocido {{command}}"
},
"basic_properties": {
@@ -1025,8 +1026,8 @@
"title": "Tipos MIME disponibles en el menú desplegable"
},
"vim_key_bindings": {
"use_vim_keybindings_in_code_notes": "Utilizar combinaciones de teclas Vim en notas de código (no modo ex)",
"enable_vim_keybindings": "Habilitar combinaciones de teclas de Vim"
"use_vim_keybindings_in_code_notes": "",
"enable_vim_keybindings": ""
},
"wrap_lines": {
"wrap_lines_in_code_notes": "Ajustar líneas en notas de código",
@@ -1045,7 +1046,7 @@
"attachment_auto_deletion_description": "Los archivos adjuntos se eliminan (y borran) automáticamente si ya no se hace referencia a ellos en su nota después de un tiempo de espera definido.",
"erase_attachments_after_x_seconds": "Borrar archivos adjuntos después de X segundos de no usarse en su nota",
"manual_erasing_description": "También puede activar el borrado manualmente (sin considerar el tiempo de espera definido anteriormente):",
"erase_unused_attachments_now": "Borrar ahora los archivos adjuntos no utilizados en la nota ",
"erase_unused_attachments_now": "Borrar ahora los archivos adjuntos no utilizados en la nota",
"unused_attachments_erased": "Los archivos adjuntos no utilizados se han eliminado."
},
"network_connections": {
@@ -1080,7 +1081,7 @@
"save_button": "Guardar"
},
"tray": {
"title": "Bandeja",
"title": "Bandeja de sistema",
"enable_tray": "Habilitar bandeja (es necesario reiniciar Trilium para que este cambio surta efecto)"
},
"heading_style": {
@@ -1092,11 +1093,11 @@
"highlights_list": {
"title": "Lista de aspectos destacados",
"description": "Puede personalizar la lista de aspectos destacados que se muestra en el panel derecho:",
"bold": "",
"italic": "",
"underline": "",
"color": "",
"bg_color": "",
"bold": "Texto en negrita",
"italic": "Texto en cursiva",
"underline": "Texto subrayado",
"color": "Texto con color",
"bg_color": "Texto con color de fondo",
"visibility_title": "Visibilidad de la lista de aspectos destacados",
"visibility_description": "Puede ocultar el widget de aspectos destacados por nota agregando una etiqueta #hideHighlightWidget.",
"shortcut_info": "Puede configurar un método abreviado de teclado para alternar rápidamente el panel derecho (incluidos los aspectos destacados) en Opciones -> Atajos (nombre 'toggleRightPane')."
@@ -1197,12 +1198,13 @@
},
"spellcheck": {
"title": "Revisión ortográfica",
"description": "Estas opciones se aplican sólo para compilaciones de escritorio; los navegadores utilizarán su corrector ortográfico nativo. Es necesario reiniciar la aplicación después del cambio.",
"description": "Estas opciones se aplican sólo para compilaciones de escritorio; los navegadores utilizarán su corrector ortográfico nativo.",
"enable": "Habilitar corrector ortográfico",
"language_code_label": "Código(s) de idioma",
"language_code_placeholder": "por ejemplo \"en-US\", \"de-AT\"",
"multiple_languages_info": "Múltiples idiomas se pueden separar por coma, por ejemplo \"en-US, de-DE, cs \". Los cambios en las opciones de corrección ortográfica entrarán en vigor después del reinicio de la aplicación.",
"available_language_codes_label": "Códigos de idioma disponibles:"
"multiple_languages_info": "Múltiples idiomas se pueden separar por coma, por ejemplo \"en-US, de-DE, cs\".",
"available_language_codes_label": "Códigos de idioma disponibles:",
"restart-required": "Los cambios en las opciones de corrección ortográfica entrarán en vigor después del reinicio de la aplicación."
},
"sync_2": {
"config_title": "Configuración de sincronización",

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
export default {
buildDate: "2024-08-09T22:05:59Z",
buildRevision: "2a5c444eff3eb99389339716ea8bfc989be90ecd"
buildDate: "2024-09-07T18:36:34Z",
buildRevision: "7c0d6930fa8f20d269dcfbcbc8f636a25f6bb9a7"
};

31
src/services/i18n.ts Normal file
View File

@@ -0,0 +1,31 @@
import i18next from "i18next";
import Backend from "i18next-fs-backend";
import options from "./options.js";
import sql_init from "./sql_init.js";
export async function initializeTranslations() {
// Initialize translations
await i18next.use(Backend).init({
lng: await getCurrentLanguage(),
fallbackLng: "en",
ns: "server",
backend: {
loadPath: "translations/{{lng}}/{{ns}}.json"
},
debug: true
});
}
function getCurrentLanguage() {
let language;
if (sql_init.isDbInitialized()) {
language = options.getOption("locale");
}
if (!language) {
console.info("Language option not found, falling back to en.");
language = "en";
}
return language;
}

View File

@@ -4,6 +4,7 @@ import optionService from "./options.js";
import log from "./log.js";
import utils from "./utils.js";
import { KeyboardShortcut } from './keyboard_actions_interface.js';
import { t } from "i18next";
const isMac = process.platform === "darwin";
const isElectron = utils.isElectron();
@@ -19,7 +20,7 @@ const isElectron = utils.isElectron();
const DEFAULT_KEYBOARD_ACTIONS: KeyboardShortcut[] = [
{
separator: "Note navigation"
separator: t("keyboard_actions.note-navigation")
},
{
actionName: "backInNoteHistory",
@@ -36,7 +37,7 @@ const DEFAULT_KEYBOARD_ACTIONS: KeyboardShortcut[] = [
{
actionName: "jumpToNote",
defaultShortcuts: ["CommandOrControl+J"],
description: 'Open "Jump to note" dialog',
description: t("keyboard_actions.open-jump-to-note-dialog"),
scope: "window"
},
{
@@ -52,37 +53,37 @@ const DEFAULT_KEYBOARD_ACTIONS: KeyboardShortcut[] = [
{
actionName: "searchInSubtree",
defaultShortcuts: ["CommandOrControl+Shift+S"],
description: "Search for notes in the active note's subtree",
description: t("keyboard_actions.search-in-subtree"),
scope: "note-tree"
},
{
actionName: "expandSubtree",
defaultShortcuts: [],
description: "Expand subtree of current note",
description: t("keyboard_actions.expand-subtree"),
scope: "note-tree"
},
{
actionName: "collapseTree",
defaultShortcuts: ["Alt+C"],
description: "Collapses the complete note tree",
description: t("keyboard_actions.collapse-tree"),
scope: "window"
},
{
actionName: "collapseSubtree",
defaultShortcuts: ["Alt+-"],
description: "Collapses subtree of current note",
description: t("keyboard_actions.collapse-subtree"),
scope: "note-tree"
},
{
actionName: "sortChildNotes",
defaultShortcuts: ["Alt+S"],
description: "Sort child notes",
description: t("keyboard_actions.sort-child-notes"),
scope: "note-tree"
},
{
separator: "Creating and moving notes"
separator: t("keyboard_actions.creating-and-moving-notes")
},
{
actionName: "createNoteAfter",
@@ -97,50 +98,50 @@ const DEFAULT_KEYBOARD_ACTIONS: KeyboardShortcut[] = [
{
actionName: "createNoteIntoInbox",
defaultShortcuts: ["global:CommandOrControl+Alt+P"],
description: "Create and open in the inbox (if defined) or day note",
description: t("keyboard_actions.create-note-into-inbox"),
scope: "window"
},
{
actionName: "deleteNotes",
defaultShortcuts: ["Delete"],
description: "Delete note",
description: t("keyboard_actions.delete-note"),
scope: "note-tree"
},
{
actionName: "moveNoteUp",
defaultShortcuts: isMac ? ["Alt+Up"] : ["CommandOrControl+Up"],
description: "Move note up",
description: t("keyboard_actions.move-note-up"),
scope: "note-tree"
},
{
actionName: "moveNoteDown",
defaultShortcuts: isMac ? ["Alt+Down"] : ["CommandOrControl+Down"],
description: "Move note down",
description: t("keyboard_actions.move-note-down"),
scope: "note-tree"
},
{
actionName: "moveNoteUpInHierarchy",
defaultShortcuts: isMac ? ["Alt+Left"] : ["CommandOrControl+Left"],
description: "Move note up in hierarchy",
description: t("keyboard_actions.move-note-up-in-hierarchy"),
scope: "note-tree"
},
{
actionName: "moveNoteDownInHierarchy",
defaultShortcuts: isMac ? ["Alt+Right"] : ["CommandOrControl+Right"],
description: "Move note down in hierarchy",
description: t("keyboard_actions.move-note-down-in-hierarchy"),
scope: "note-tree"
},
{
actionName: "editNoteTitle",
defaultShortcuts: ["Enter"],
description: "Jump from tree to the note detail and edit title",
description: t("keyboard_actions.edit-note-title"),
scope: "note-tree"
},
{
actionName: "editBranchPrefix",
defaultShortcuts: ["F2"],
description: "Show Edit branch prefix dialog",
scope: "window"
description: t("keyboard_actions.edit-branch-prefix"),
scope: "note-tree"
},
{
actionName: "cloneNotesTo",
@@ -154,399 +155,398 @@ const DEFAULT_KEYBOARD_ACTIONS: KeyboardShortcut[] = [
},
{
separator: "Note clipboard"
separator: t("keyboard_actions.note-clipboard")
},
{
actionName: "copyNotesToClipboard",
defaultShortcuts: ["CommandOrControl+C"],
description: "Copy selected notes to the clipboard",
description: t("keyboard_actions.copy-notes-to-clipboard"),
scope: "note-tree"
},
{
actionName: "pasteNotesFromClipboard",
defaultShortcuts: ["CommandOrControl+V"],
description: "Paste notes from the clipboard into active note",
description: t("keyboard_actions.paste-notes-from-clipboard"),
scope: "note-tree"
},
{
actionName: "cutNotesToClipboard",
defaultShortcuts: ["CommandOrControl+X"],
description: "Cut selected notes to the clipboard",
description: t("keyboard_actions.cut-notes-to-clipboard"),
scope: "note-tree"
},
{
actionName: "selectAllNotesInParent",
defaultShortcuts: ["CommandOrControl+A"],
description: "Select all notes from the current note level",
description: t("keyboard_actions.select-all-notes-in-parent"),
scope: "note-tree"
},
{
actionName: "addNoteAboveToSelection",
defaultShortcuts: ["Shift+Up"],
description: "Add note above to the selection",
description: t("keyboard_actions.add-note-above-to-the-selection"),
scope: "note-tree"
},
{
actionName: "addNoteBelowToSelection",
defaultShortcuts: ["Shift+Down"],
description: "Add note above to the selection",
description: t("keyboard_actions.add-note-below-to-selection"),
scope: "note-tree"
},
{
actionName: "duplicateSubtree",
defaultShortcuts: [],
description: "Duplicate subtree",
description: t("keyboard_actions.duplicate-subtree"),
scope: "note-tree"
},
{
separator: "Tabs & Windows"
separator: t("keyboard_actions.tabs-and-windows")
},
{
actionName: "openNewTab",
defaultShortcuts: isElectron ? ["CommandOrControl+T"] : [],
description: "Opens new tab",
description: t("keyboard_actions.open-new-tab"),
scope: "window"
},
{
actionName: "closeActiveTab",
defaultShortcuts: isElectron ? ["CommandOrControl+W"] : [],
description: "Closes active tab",
description: t("keyboard_actions.close-active-tab"),
scope: "window"
},
{
actionName: "reopenLastTab",
defaultShortcuts: isElectron ? ["CommandOrControl+Shift+T"] : [],
description: "Reopens the last closed tab",
description: t("keyboard_actions.reopen-last-tab"),
scope: "window"
},
{
actionName: "activateNextTab",
defaultShortcuts: isElectron ? ["CommandOrControl+Tab", "CommandOrControl+PageDown"] : [],
description: "Activates tab on the right",
description: t("keyboard_actions.activate-next-tab"),
scope: "window"
},
{
actionName: "activatePreviousTab",
defaultShortcuts: isElectron ? ["CommandOrControl+Shift+Tab", "CommandOrControl+PageUp"] : [],
description: "Activates tab on the left",
description: t("keyboard_actions.activate-previous-tab"),
scope: "window"
},
{
actionName: "openNewWindow",
defaultShortcuts: [],
description: "Open new empty window",
description: t("keyboard_actions.open-new-window"),
scope: "window"
},
{
actionName: "toggleTray",
defaultShortcuts: [],
description: "Shows/hides the application from the system tray",
description: t("keyboard_actions.toggle-tray"),
scope: "window"
},
{
actionName: "firstTab",
defaultShortcuts: ["CommandOrControl+1"],
description: "Activates the first tab in the list",
description: t("keyboard_actions.first-tab"),
scope: "window"
},
{
actionName: "secondTab",
defaultShortcuts: ["CommandOrControl+2"],
description: "Activates the second tab in the list",
description: t("keyboard_actions.second-tab"),
scope: "window"
},
{
actionName: "thirdTab",
defaultShortcuts: ["CommandOrControl+3"],
description: "Activates the third tab in the list",
description: t("keyboard_actions.third-tab"),
scope: "window"
},
{
actionName: "fourthTab",
defaultShortcuts: ["CommandOrControl+4"],
description: "Activates the fourth tab in the list",
description: t("keyboard_actions.fourth-tab"),
scope: "window"
},
{
actionName: "fifthTab",
defaultShortcuts: ["CommandOrControl+5"],
description: "Activates the fifth tab in the list",
description: t("keyboard_actions.fifth-tab"),
scope: "window"
},
{
actionName: "sixthTab",
defaultShortcuts: ["CommandOrControl+6"],
description: "Activates the sixth tab in the list",
description: t("keyboard_actions.sixth-tab"),
scope: "window"
},
{
actionName: "seventhTab",
defaultShortcuts: ["CommandOrControl+7"],
description: "Activates the seventh tab in the list",
description: t("keyboard_actions.seventh-tab"),
scope: "window"
},
{
actionName: "eigthTab",
defaultShortcuts: ["CommandOrControl+8"],
description: "Activates the eighth tab in the list",
description: t("keyboard_actions.eight-tab"),
scope: "window"
},
{
actionName: "ninthTab",
defaultShortcuts: ["CommandOrControl+9"],
description: "Activates the ninth tab in the list",
description: t("keyboard_actions.ninth-tab"),
scope: "window"
},
{
actionName: "lastTab",
defaultShortcuts: [],
description: "Activates the last tab in the list",
description: t("keyboard_actions.last-tab"),
scope: "window"
},
{
separator: "Dialogs"
separator: t("keyboard_actions.dialogs")
},
{
actionName: "showNoteSource",
defaultShortcuts: [],
description: "Shows Note Source dialog",
description: t("keyboard_actions.show-note-source"),
scope: "window"
},
{
actionName: "showOptions",
defaultShortcuts: [],
description: "Shows Options dialog",
description: t("keyboard_actions.show-options"),
scope: "window"
},
{
actionName: "showRevisions",
defaultShortcuts: [],
description: "Shows Note Revisions dialog",
description: t("keyboard_actions.show-revisions"),
scope: "window"
},
{
actionName: "showRecentChanges",
defaultShortcuts: [],
description: "Shows Recent Changes dialog",
description: t("keyboard_actions.show-recent-changes"),
scope: "window"
},
{
actionName: "showSQLConsole",
defaultShortcuts: ["Alt+O"],
description: "Shows SQL Console dialog",
description: t("keyboard_actions.show-sql-console"),
scope: "window"
},
{
actionName: "showBackendLog",
defaultShortcuts: [],
description: "Shows Backend Log dialog",
description: t("keyboard_actions.show-backend-log"),
scope: "window"
},
{
actionName: "showHelp",
defaultShortcuts: ["F1"],
description: "Shows built-in Help / cheatsheet",
description: t("keyboard_actions.show-help"),
scope: "window"
},
{
separator: "Text note operations"
separator: t("keyboard_actions.text-note-operations")
},
{
actionName: "addLinkToText",
defaultShortcuts: ["CommandOrControl+L"],
description: "Open dialog to add link to the text",
description: t("keyboard_actions.add-link-to-text"),
scope: "text-detail"
},
{
actionName: "followLinkUnderCursor",
defaultShortcuts: ["CommandOrControl+Enter"],
description: "Follow link within which the caret is placed",
description: t("keyboard_actions.follow-link-under-cursor"),
scope: "text-detail"
},
{
actionName: "insertDateTimeToText",
defaultShortcuts: ["Alt+T"],
description: "Insert current date & time into text",
description: t("keyboard_actions.insert-date-and-time-to-text"),
scope: "text-detail"
},
{
actionName: "pasteMarkdownIntoText",
defaultShortcuts: [],
description: "Pastes Markdown from clipboard into text note",
description: t("keyboard_actions.paste-markdown-into-text"),
scope: "text-detail"
},
{
actionName: "cutIntoNote",
defaultShortcuts: [],
description: "Cuts the selection from the current note and creates subnote with the selected text",
description: t("keyboard_actions.cut-into-note"),
scope: "text-detail"
},
{
actionName: "addIncludeNoteToText",
defaultShortcuts: [],
description: "Opens the dialog to include a note",
description: t("keyboard_actions.add-include-note-to-text"),
scope: "text-detail"
},
{
actionName: "editReadOnlyNote",
defaultShortcuts: [],
description: "Edit a read-only note",
description: t("keyboard_actions.edit-readonly-note"),
scope: "window"
},
{
separator: "Attributes (labels & relations)"
separator: t("keyboard_actions.attributes-labels-and-relations")
},
{
actionName: "addNewLabel",
defaultShortcuts: ["Alt+L"],
description: "Create new label",
description: t("keyboard_actions.add-new-label"),
scope: "window"
},
{
actionName: "addNewRelation",
defaultShortcuts: ["Alt+R"],
description: "Create new relation",
description: t("keyboard_actions.create-new-relation"),
scope: "window"
},
{
separator: "Ribbon tabs"
separator: t("keyboard_actions.ribbon-tabs")
},
{
actionName: "toggleRibbonTabBasicProperties",
defaultShortcuts: [],
description: "Toggle Basic Properties",
description: t("keyboard_actions.toggle-basic-properties"),
scope: "window"
},
{
actionName: "toggleRibbonTabBookProperties",
defaultShortcuts: [],
description: "Toggle Book Properties",
description: t("keyboard_actions.toggle-book-properties"),
scope: "window"
},
{
actionName: "toggleRibbonTabFileProperties",
defaultShortcuts: [],
description: "Toggle File Properties",
description: t("keyboard_actions.toggle-file-properties"),
scope: "window"
},
{
actionName: "toggleRibbonTabImageProperties",
defaultShortcuts: [],
description: "Toggle Image Properties",
description: t("keyboard_actions.toggle-image-properties"),
scope: "window"
},
{
actionName: "toggleRibbonTabOwnedAttributes",
defaultShortcuts: ["Alt+A"],
description: "Toggle Owned Attributes",
description: t("keyboard_actions.toggle-owned-attributes"),
scope: "window"
},
{
actionName: "toggleRibbonTabInheritedAttributes",
defaultShortcuts: [],
description: "Toggle Inherited Attributes",
description: t("keyboard_actions.toggle-inherited-attributes"),
scope: "window"
},
{
actionName: "toggleRibbonTabPromotedAttributes",
defaultShortcuts: [],
description: "Toggle Promoted Attributes",
description: t("keyboard_actions.toggle-promoted-attributes"),
scope: "window"
},
{
actionName: "toggleRibbonTabNoteMap",
defaultShortcuts: [],
description: "Toggle Link Map",
description: t("keyboard_actions.toggle-link-map"),
scope: "window"
},
{
actionName: "toggleRibbonTabNoteInfo",
defaultShortcuts: [],
description: "Toggle Note Info",
description: t("keyboard_actions.toggle-note-info"),
scope: "window"
},
{
actionName: "toggleRibbonTabNotePaths",
defaultShortcuts: [],
description: "Toggle Note Paths",
description: t("keyboard_actions.toggle-note-paths"),
scope: "window"
},
{
actionName: "toggleRibbonTabSimilarNotes",
defaultShortcuts: [],
description: "Toggle Similar Notes",
description: t("keyboard_actions.toggle-similar-notes"),
scope: "window"
},
{
separator: "Other"
separator: t("keyboard_actions.other")
},
{
actionName: "toggleRightPane",
defaultShortcuts: [],
description: "Toggle the display of the right pane, which includes Table of Contents and Highlights",
description: t("keyboard_actions.toggle-right-pane"),
scope: "window"
},
{
actionName: "printActiveNote",
defaultShortcuts: [],
description: "Print active note",
description: t("keyboard_actions.print-active-note"),
scope: "window"
},
{
actionName: "openNoteExternally",
defaultShortcuts: [],
description: "Open note as a file with default application",
description: t("keyboard_actions.open-note-externally"),
scope: "window"
},
{
actionName: "renderActiveNote",
defaultShortcuts: [],
description: "Render (re-render) active note",
description: t("keyboard_actions.render-active-note"),
scope: "window"
},
{
actionName: "runActiveNote",
defaultShortcuts: ["CommandOrControl+Enter"],
description: "Run active JavaScript (frontend/backend) code note",
description: t("keyboard_actions.run-active-note"),
scope: "code-detail"
},
{
actionName: "toggleNoteHoisting",
defaultShortcuts: ["Alt+H"],
description: "Toggles note hoisting of active note",
description: t("keyboard_actions.toggle-note-hoisting"),
scope: "window"
},
{
actionName: "unhoist",
defaultShortcuts: ["Alt+U"],
description: "Unhoist from anywhere",
description: t("keyboard_actions.unhoist"),
scope: "window"
},
{
actionName: "reloadFrontendApp",
defaultShortcuts: ["F5", "CommandOrControl+R"],
description: "Reload frontend App",
description: t("keyboard_actions.reload-frontend-app"),
scope: "window"
},
{
actionName: "openDevTools",
defaultShortcuts: isElectron ? ["CommandOrControl+Shift+I"] : [],
description: "Open dev tools",
description: t("keyboard_actions.open-dev-tools"),
scope: "window"
},
{
@@ -557,43 +557,43 @@ const DEFAULT_KEYBOARD_ACTIONS: KeyboardShortcut[] = [
{
actionName: "toggleLeftPane",
defaultShortcuts: [],
description: "Toggle left (note tree) panel",
description: t("keyboard_actions.toggle-left-note-tree-panel"),
scope: "window"
},
{
actionName: "toggleFullscreen",
defaultShortcuts: ["F11"],
description: "Toggle full screen",
description: t("keyboard_actions.toggle-full-screen"),
scope: "window"
},
{
actionName: "zoomOut",
defaultShortcuts: isElectron ? ["CommandOrControl+-"] : [],
description: "Zoom Out",
description: t("keyboard_actions.zoom-out"),
scope: "window"
},
{
actionName: "zoomIn",
description: "Zoom In",
description: t("keyboard_actions.zoom-in"),
defaultShortcuts: isElectron ? ["CommandOrControl+="] : [],
scope: "window"
},
{
actionName: "zoomReset",
description: "Reset zoom level",
description: t("keyboard_actions.reset-zoom-level"),
defaultShortcuts: isElectron ? ["CommandOrControl+0"] : [],
scope: "window"
},
{
actionName: "copyWithoutFormatting",
defaultShortcuts: ["CommandOrControl+Alt+C"],
description: "Copy selected text without formatting",
description: t("keyboard_actions.copy-without-formatting"),
scope: "text-detail"
},
{
actionName: "forceSaveRevision",
defaultShortcuts: [],
description: "Force creating / saving new note revision of the active note",
description: t("keyboard_actions.force-save-revision"),
scope: "window"
}
];

View File

@@ -7,7 +7,7 @@
<title>TriliumNext Notes</title>
</head>
<body class="desktop heading-style-<%= headingStyle %>">
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<noscript><%= t("javascript-required") %></noscript>
<script>
// hide body to reduce flickering on the startup. This is done through JS and not CSS to not hide <noscript>

View File

@@ -3,24 +3,24 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Login</title>
<title><%= t("login.title") %></title>
<link rel="apple-touch-icon" sizes="180x180" href="<%= assetPath %>/images/app-icons/ios/apple-touch-icon.png">
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<div class="container">
<div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto" style="padding-top: 25px;">
<h1>Trilium login</h1>
<h1><%= t("login.heading") %></h1>
<% if (failedAuth) { %>
<div class="alert alert-warning">
Password is incorrect. Please try again.
<%= t("login.incorrect-password") %>
</div>
<% } %>
<form action="login" method="POST">
<div class="form-group">
<label for="password">Password</label>
<label for="password"><%= t("login.password") %></label>
<div class="controls">
<input id="password" name="password" placeholder="" class="form-control" type="password">
</div>
@@ -28,12 +28,12 @@
<div class="form-group">
<div class="checkbox">
<label>
<input id="remember-me" name="rememberMe" value="1" type="checkbox"> Remember me
<input id="remember-me" name="rememberMe" value="1" type="checkbox"> <%= t("login.remember-me") %>
</label>
</div>
</div>
<div class="form-group">
<button class="btn btn-success">Login</button>
<button class="btn btn-success"><%= t("login.button") %></button>
</div>
</form>
</div>

View File

@@ -96,7 +96,7 @@
</style>
</head>
<body class="mobile heading-style-<%= headingStyle %>">
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<noscript><%= t("javascript-required") %></noscript>
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>

View File

@@ -3,14 +3,14 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Login</title>
<title><%= t("login.title") %></title>
<link rel="apple-touch-icon" sizes="180x180" href="<%= assetPath %>/images/app-icons/ios/apple-touch-icon.png">
<link rel="shortcut icon" href="favicon.ico">
</head>
<body>
<div class="container">
<div class="col-xs-12 col-sm-10 col-md-6 col-lg-4 col-xl-4 mx-auto" style="padding-top: 25px;">
<h1>Set password</h1>
<h1><%= t("set_password.heading") %></h1>
<% if (error) { %>
<div class="alert alert-warning">
@@ -18,24 +18,24 @@
</div>
<% } %>
<p>Before you can start using Trilium from web, you need to set a password first. You will then use this password to login.</p>
<p><%= t("set_password.description") %></p>
<form action="set-password" method="POST">
<div class="form-group">
<label for="password">Password</label>
<label for="password"><%= t("set_password.password") %></label>
<div class="controls">
<input id="password" name="password1" placeholder="" class="form-control" type="password">
</div>
</div>
<div class="form-group">
<label for="password">Password confirmation</label>
<label for="password"><%= t("set_password.password-confirmation") %></label>
<div class="controls">
<input id="password" name="password2" placeholder="" class="form-control" type="password">
</div>
</div>
<div class="form-group">
<button class="btn btn-success">Set password</button>
<button class="btn btn-success"><%= t("set_password.button") %></button>
</div>
</form>
</div>

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Setup</title>
<title><%= t("setup.title") %></title>
<style>
.lds-ring {
@@ -44,10 +44,10 @@
</style>
</head>
<body>
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<noscript><%= t("javascript-required") %></noscript>
<div class="container">
<div id="setup-dialog" class="col-md-12 col-lg-8 col-xl-6 mx-auto" style="padding-top: 25px; font-size: larger; display: none;">
<h1>TriliumNext Notes setup</h1>
<h1><%= t("setup.heading") %></h1>
<div class="alert alert-warning" id="alert" style="display: none;">
</div>
@@ -55,22 +55,22 @@
<div id="setup-type" data-bind="visible: step() == 'setup-type'" style="margin-top: 20px;">
<div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="new-document" data-bind="checked: setupType">
I'm a new user, and I want to create a new Trilium document for my notes</label>
<%= t("setup.new-document") %></label>
</div>
<div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="sync-from-desktop" data-bind="checked: setupType">
I have a desktop instance already, and I want to set up sync with it</label>
<%= t("setup.sync-from-desktop") %></label>
</div>
<div class="radio" style="margin-bottom: 15px;">
<label><input type="radio" name="setup-type" value="sync-from-server" data-bind="checked: setupType">
I have a server instance already, and I want to set up sync with it</label>
<%= t("setup.sync-from-server") %></label>
</div>
<button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary">Next</button>
<button type="button" data-bind="disable: !setupTypeSelected(), click: selectSetupType" class="btn btn-primary"><%= t("setup.next") %></button>
</div>
<div data-bind="visible: step() == 'new-document-in-progress'">
<h2>Document initialization in progress</h2>
<h2><%= t("setup.init-in-progress") %></h2>
<div style="display: flex; justify-content: flex-start; margin-top: 20px;">
<div class="lds-ring" style="margin-right: 20px;">
@@ -81,61 +81,61 @@
</div>
<div style="line-height: 60px;">
<p>You will be shortly redirected to the application.</p>
<p><%= t("setup.redirecting") %></p>
</div>
</div>
</div>
<div data-bind="visible: step() == 'sync-from-desktop'">
<h2>Sync from Desktop</h2>
<h2><%= t("setup_sync-from-desktop.heading") %></h2>
<p>This setup needs to be initiated from the desktop instance:</p>
<p><%= t("setup_sync-from-desktop.description") %></p>
<ol>
<li>Open your desktop instance of TriliumNext Notes.</li>
<li>From the Trilium Menu, click Options.</li>
<li>Click on Sync tab.</li>
<li>Change server instance address to: <span id="current-host"></span> and click save.</li>
<li>Click "Test sync" button to verify connection is successful.</li>
<li>Once you've completed these steps, click <a href="/">here</a>.</li>
<li><%= t("setup_sync-from-desktop.step1") %></li>
<li><%= t("setup_sync-from-desktop.step2") %></li>
<li><%= t("setup_sync-from-desktop.step3") %></li>
<li><%- t("setup_sync-from-desktop.step4", { host: '<span id="current-host"></span>'}) %></li>
<li><%= t("setup_sync-from-desktop.step5") %></li>
<li><%- t("setup_sync-from-desktop.step6", { link: `<a href="/">${t("setup_sync-from-desktop.step6-here")}</a>` }) %></li>
</ol>
<button type="button" data-bind="click: back" class="btn btn-secondary">Back</button>
</div>
<div data-bind="visible: step() == 'sync-from-server'">
<h2>Sync from Server</h2>
<h2><%= t("setup_sync-from-server.heading") %></h2>
<p>Please enter Trilium server address and credentials below. This will download the whole Trilium document from server and setup sync to it. Depending on the document size and your connection speed, this may take a while.</p>
<p><%= t("setup_sync-from-server.instructions") %></p>
<div class="form-group">
<label for="sync-server-host">Trilium server address</label>
<input type="text" id="syncServerHost" class="form-control" data-bind="value: syncServerHost" placeholder="https://<hostname>:<port>">
<label for="sync-server-host"><%= t("setup_sync-from-server.server-host") %></label>
<input type="text" id="syncServerHost" class="form-control" data-bind="value: syncServerHost" placeholder="<%= t("setup_sync-from-server.server-host-placeholder") %>">
</div>
<div class="form-group">
<label for="sync-proxy">Proxy server (optional)</label>
<input type="text" id="sync-proxy" class="form-control" data-bind="value: syncProxy" placeholder="https://<hostname>:<port>">
<label for="sync-proxy"><%= t("setup_sync-from-server.proxy-server") %></label>
<input type="text" id="sync-proxy" class="form-control" data-bind="value: syncProxy" placeholder="<%= t("setup_sync-from-server.proxy-server-placeholder") %>">
<p><strong>Note:</strong> If you leave proxy setting blank, system proxy will be used (applies to desktop/electron build only)</p>
<p><strong><%= t("setup_sync-from-server.note") %></strong> <%= t("setup_sync-from-server.proxy-instruction") %></p>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" class="form-control" data-bind="value: password" placeholder="Password">
<label for="password"><%= t("setup_sync-from-server.password") %></label>
<input type="password" id="password" class="form-control" data-bind="value: password" placeholder="<%= t("setup_sync-from-server.password-placeholder") %>">
</div>
<button type="button" data-bind="click: back" class="btn btn-secondary">Back</button>
<button type="button" data-bind="click: back" class="btn btn-secondary"><%= t("setup_sync-from-server.back") %></button>
&nbsp;
<button type="button" data-bind="click: finish" class="btn btn-primary">Finish setup</button>
<button type="button" data-bind="click: finish" class="btn btn-primary"><%= t("setup_sync-from-server.finish-setup") %></button>
</div>
<div data-bind="visible: step() == 'sync-in-progress'">
<h2>Sync in progress</h2>
<h2><%= t("setup_sync-in-progress.heading") %></h2>
<div class="alert alert-success">Sync has been correctly set up. It will take some time for the initial sync to finish. Once it's done, you'll be redirected to the login page.</div>
<div class="alert alert-success"><%= t("setup_sync-in-progress.successful") %></div>
<div>Outstanding sync items: <strong id="outstanding-syncs">N/A</strong></div>
<div><%= t("setup_sync-in-progress.outstanding-items") %> <strong id="outstanding-syncs"><%= t("setup_sync-in-progress.outstanding-items-default") %></strong></div>
</div>
</div>
</div>

View File

@@ -3,9 +3,9 @@
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="../favicon.ico">
<title>Not found</title>
<title><%= t("share_404.title") %></title>
</head>
<body>
<h1>Not found</h1>
<h1><%= t("share_404.heading") %></h1>
</body>
</html>

View File

@@ -37,15 +37,15 @@
<div id="main">
<% if (note.parents[0].noteId !== '_share' && note.parents.length !== 0) { %>
<nav id="parentLink">
parent: <a href="<%= note.parents[0].shareId %>"
class="type-<%= note.parents[0].type %>"><%= note.parents[0].title %></a>
<%= t("share_page.parent") %> <a href="<%= note.parents[0].shareId %>"
class="type-<%= note.parents[0].type %>"><%= note.parents[0].title %></a>
</nav>
<% } %>
<h1 id="title"><%= note.title %></h1>
<% if (note.hasLabel("pageUrl")) { %>
<div id="noteClippedFrom">This note was originally clipped from <a href="<%= note.getLabelValue("pageUrl") %>"><%= note.getLabelValue("pageUrl") %></a></div>
<div id="noteClippedFrom"><%- t("share_page.clipped-from", { url: `<a href="${note.getLabelValue("pageUrl")}">${note.getLabelValue("pageUrl")}</a>` }) %></div>
<% } %>
<% if (!isEmpty) { %>
@@ -58,7 +58,7 @@
<nav id="childLinks" class="<% if (isEmpty) { %>grid<% } else { %>list<% } %>">
<% if (!isEmpty) { %>
<hr>
<span>Child notes: </span>
<span><%= t("share_page.child-notes") %> </span>
<% } %>
<ul>
@@ -76,7 +76,7 @@
</ul>
</nav>
<% } else if (isEmpty) { %>
<p>This note has no content.</p>
<p><%= t("share_page.no-content") %></p>
<% } %>
</div>

View File

@@ -1,4 +1,5 @@
#!/usr/bin/env node
import app from "./app.js";
import sessionParser from "./routes/session_parser.js";
import fs from "fs";
@@ -55,13 +56,15 @@ async function startTrilium() {
*/
if (utils.isElectron()) {
(await import('electron')).app.requestSingleInstanceLock();
}
}
log.info(JSON.stringify(appInfo, null, 2));
// for perf. issues it's good to know the rough configuration
const cpuInfos = (await import('os')).cpus();
if (cpuInfos && cpuInfos[0] !== undefined) { // https://github.com/zadam/trilium/pull/3957
log.info(`CPU model: ${cpuInfos[0].model}, logical cores: ${cpuInfos.length} freq: ${cpuInfos[0].speed} Mhz`); // for perf. issues it's good to know the rough configuration
const cpuModel = (cpuInfos[0].model || "").trimEnd();
log.info(`CPU model: ${cpuModel}, logical cores: ${cpuInfos.length}, freq: ${cpuInfos[0].speed} Mhz`);
}
const httpServer = startHttpServer();
@@ -144,7 +147,16 @@ function startHttpServer() {
}
if (utils.isElectron()) {
import("electron").then(({ dialog }) => {
import("electron").then(({ app, dialog }) => {
// Not all situations require showing an error dialog. When Trilium is already open,
// clicking the shortcut, the software icon, or the taskbar icon, or when creating a new window,
// should simply focus on the existing window or open a new one, without displaying an error message.
if ("code" in error && error.code == 'EADDRINUSE') {
if (process.argv.includes('--new-window') || !app.requestSingleInstanceLock()) {
console.error(message);
process.exit(1);
}
}
dialog.showErrorBox("Error while initializing the server", message);
process.exit(1);
});