options widget work

This commit is contained in:
zadam 2022-12-07 15:37:53 +01:00
parent 2ad19ddd36
commit 5f6a10ef22
30 changed files with 370 additions and 760 deletions

View File

@ -84,7 +84,7 @@ export default class RootCommandExecutor extends Component {
await appContext.tabManager.openContextWithNote('hidden', true, null, 'hidden'); await appContext.tabManager.openContextWithNote('hidden', true, null, 'hidden');
} }
async showOptionsInHiddenCommand() { async showOptionsCommand() {
await appContext.tabManager.openContextWithNote('opt_root', true, null, 'opt_root'); await appContext.tabManager.openContextWithNote('options', true, null, 'opt_root')
} }
} }

View File

@ -68,7 +68,6 @@ import DeleteNotesDialog from "../widgets/dialogs/delete_notes.js";
import InfoDialog from "../widgets/dialogs/info.js"; import InfoDialog from "../widgets/dialogs/info.js";
import ConfirmDialog from "../widgets/dialogs/confirm.js"; import ConfirmDialog from "../widgets/dialogs/confirm.js";
import PromptDialog from "../widgets/dialogs/prompt.js"; import PromptDialog from "../widgets/dialogs/prompt.js";
import OptionsDialog from "../widgets/dialogs/options.js";
import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js"; import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js"; import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import MermaidExportButton from "../widgets/floating_buttons/mermaid_export_button.js"; import MermaidExportButton from "../widgets/floating_buttons/mermaid_export_button.js";
@ -77,18 +76,6 @@ import NoteRevisionsButton from "../widgets/buttons/note_revisions_button.js";
import EditableCodeButtonsWidget from "../widgets/type_widgets/editable_code_buttons.js"; import EditableCodeButtonsWidget from "../widgets/type_widgets/editable_code_buttons.js";
import ApiLogWidget from "../widgets/api_log.js"; import ApiLogWidget from "../widgets/api_log.js";
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js"; import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
import AppearanceOptions from "../widgets/dialogs/options/appearance.js";
import KeyboardShortcutsOptions from "../widgets/dialogs/options/shortcuts.js";
import TextNotesOptions from "../widgets/dialogs/options/text_notes.js";
import CodeNotesOptions from "../widgets/dialogs/options/code_notes.js";
import ImageOptions from "../widgets/dialogs/options/images.js";
import SpellcheckOptions from "../widgets/dialogs/options/spellcheck.js";
import PasswordOptions from "../widgets/dialogs/options/password.js";
import EtapiOptions from "../widgets/dialogs/options/etapi.js";
import BackupOptions from "../widgets/dialogs/options/backup.js";
import SyncOptions from "../widgets/dialogs/options/sync.js";
import OtherOptions from "../widgets/dialogs/options/other.js";
import AdvancedOptions from "../widgets/dialogs/options/advanced.js";
export default class DesktopLayout { export default class DesktopLayout {
constructor(customWidgets) { constructor(customWidgets) {
@ -219,19 +206,5 @@ export default class DesktopLayout {
.child(new InfoDialog()) .child(new InfoDialog())
.child(new ConfirmDialog()) .child(new ConfirmDialog())
.child(new PromptDialog()); .child(new PromptDialog());
// .child(new OptionsDialog()
// .child(new AppearanceOptions())
// .child(new KeyboardShortcutsOptions())
// .child(new TextNotesOptions())
// .child(new CodeNotesOptions())
// .child(new ImageOptions())
// .child(new SpellcheckOptions())
// .child(new PasswordOptions())
// .child(new EtapiOptions())
// .child(new BackupOptions())
// .child(new SyncOptions())
// .child(new OtherOptions())
// .child(new AdvancedOptions())
// );
} }
} }

View File

@ -87,11 +87,6 @@ const TPL = `
<span class="bx bx-slider"></span> <span class="bx bx-slider"></span>
Options Options
</li> </li>
<li class="dropdown-item" data-trigger-command="showOptionsInHidden">
<span class="bx bx-slider"></span>
Options in hidden
</li>
<li class="dropdown-item" data-trigger-command="openNewWindow"> <li class="dropdown-item" data-trigger-command="openNewWindow">
<span class="bx bx-window-open"></span> <span class="bx bx-window-open"></span>

View File

@ -1,90 +0,0 @@
"use strict";
import server from '../../services/server.js';
import utils from "../../services/utils.js";
import BasicWidget from "../basic_widget.js";
const TPL = `
<div class="options-dialog modal fade mx-auto" tabindex="-1" role="dialog">
<style>
.options-dialog .nav {
margin-right: 20px;
}
.options-dialog .tab-content {
overflow-y: auto;
max-height: 85vh;
}
</style>
<div class="modal-dialog modal-lg" style="min-width: 1000px;" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Options</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div style="display: flex">
<ul class="nav nav-tabs flex-column"></ul>
<br/>
<div class="tab-content"></div>
</div>
</div>
</div>
</div>
</div>`;
export default class OptionsDialog extends BasicWidget {
doRender() {
this.$widget = $(TPL);
this.$navTabs = this.$widget.find(".nav-tabs");
this.$tabContent = this.$widget.find(".tab-content");
for (const child of this.children) {
this.$navTabs.append(
$('<li class="nav-item">')
.append(
$('<a class="nav-link" data-toggle="tab">')
.attr("href", '#options-' + child.constructor.name)
.text(child.tabTitle)
)
);
this.$tabContent.append(
$('<div class="tab-pane">')
.attr("id", "options-" + child.constructor.name)
);
}
}
async showOptionsEvent({openTab}) {
const optionPromise = server.get('options');
for (const child of this.children) {
child.lazyRender();
this.$widget.find("#options-" + child.constructor.name)
.empty()
.append(child.$widget);
}
const options = await optionPromise;
for (const child of this.children) {
if (child.optionsLoaded) {
child.optionsLoaded(options)
}
}
await utils.openDialog(this.$widget);
if (!openTab) {
openTab = "AppearanceOptions";
}
this.$widget.find(`.nav-link[href='#options-${openTab}']`).trigger("click");
}
}

View File

@ -1,325 +0,0 @@
import server from "../../../services/server.js";
import utils from "../../../services/utils.js";
import appContext from "../../../components/app_context.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js";
const FONT_FAMILIES = [
{ value: "theme", label: "Theme defined" },
{ value: "serif", label: "Serif" },
{ value: "sans-serif", label: "Sans Serif" },
{ value: "monospace", label: "Monospace" },
{ value: "Arial", label: "Arial" },
{ value: "Verdana", label: "Verdana" },
{ value: "Helvetica", label: "Helvetica" },
{ value: "Tahoma", label: "Tahoma" },
{ value: "Trebuchet MS", label: "Trebuchet MS" },
{ value: "Times New Roman", label: "Times New Roman" },
{ value: "Georgia", label: "Georgia" },
{ value: "Garamond", label: "Garamond" },
{ value: "Courier New", label: "Courier New" },
{ value: "Brush Script MT", label: "Brush Script MT" },
{ value: "Impact", label: "Impact" },
{ value: "American Typewriter", label: "American Typewriter" },
{ value: "Andalé Mono", label: "Andalé Mono" },
{ value: "Lucida Console", label: "Lucida Console" },
{ value: "Monaco", label: "Monaco" },
{ value: "Bradley Hand", label: "Bradley Hand" },
{ value: "Luminari", label: "Luminari" },
{ value: "Comic Sans MS", label: "Comic Sans MS" },
];
const TPL = `
<p><strong>Settings on this options tab are saved automatically after each change.</strong></p>
<style>
.options-section .row {
/* rows otherwise overflow horizontally and force a scrollbar */
margin-left: auto;
margin-right: auto;
}
</style>
<div class="options-section">
<div class="form-group row">
<div class="col-6">
<label for="zoom-factor-select">Zoom factor (desktop build only)</label>
<input type="number" class="form-control" id="zoom-factor-select" min="0.3" max="2.0" step="0.1"/>
</div>
<div class="col-6">
<label for="native-title-bar-select">Native title bar (requires app restart)</label>
<select class="form-control" id="native-title-bar-select">
<option value="show">enabled</option>
<option value="hide">disabled</option>
</select>
</div>
</div>
<p>Zooming can be controlled with CTRL+- and CTRL+= shortcuts as well.</p>
</div>
<div class="options-section">
<h4>Theme</h4>
<div class="form-group row">
<div class="col-6">
<label for="theme-select">Theme</label>
<select class="form-control" id="theme-select"></select>
</div>
<div class="col-6">
<label for="override-theme-fonts">Override theme fonts</label>
<input type="checkbox" class="form-control" id="override-theme-fonts">
</div>
</div>
<div class="options-section">
<div id="overriden-font-settings" class="options-section">
<h4>Fonts</h4>
<h5>Main font</h5>
<div class="form-group row">
<div class="col-6">
<label for="main-font-family">Font family</label>
<select class="form-control" id="main-font-family"></select>
</div>
<div class="col-6">
<label for="main-font-size">Size</label>
<div class="input-group">
<input type="number" class="form-control" id="main-font-size" min="50" max="200" step="10"/>
<div class="input-group-append">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<h5>Note tree font</h5>
<div class="form-group row">
<div class="col-4">
<label for="tree-font-family">Font family</label>
<select class="form-control" id="tree-font-family"></select>
</div>
<div class="col-4">
<label for="tree-font-size">Size</label>
<div class="input-group">
<input type="number" class="form-control" id="tree-font-size" min="50" max="200" step="10"/>
<div class="input-group-append">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<h5>Note detail font</h5>
<div class="form-group row">
<div class="col-4">
<label for="detail-font-family">Font family</label>
<select class="form-control" id="detail-font-family"></select>
</div>
<div class="col-4">
<label for="detail-font-size">Size</label>
<div class="input-group">
<input type="number" class="form-control" id="detail-font-size" min="50" max="200" step="10"/>
<div class="input-group-append">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<h5>Monospace (code) font</h5>
<div class="form-group row">
<div class="col-4">
<label for="monospace-font-family">Font family</label>
<select class="form-control" id="monospace-font-family"></select>
</div>
<div class="col-4">
<label for="monospace-font-size">Size</label>
<div class="input-group">
<input type="number" class="form-control" id="monospace-font-size" min="50" max="200" step="10"/>
<div class="input-group-append">
<span class="input-group-text">%</span>
</div>
</div>
</div>
</div>
<p>Note that tree and detail font sizing is relative to the main font size setting.</p>
<p>Not all listed fonts may be available on your system.</p>
</div>
<p>
To apply font changes, click on
<button class="btn btn-micro reload-frontend-button">reload frontend</button>
</p>
<div class="options-section">
<h4>Content width</h4>
<p>Trilium by default limits max content width to improve readability for maximized screens on wide screens.</p>
<div class="form-group row">
<div class="col-4">
<label for="max-content-width">Max content width in pixels</label>
<input type="number" min="200" step="10" class="form-control" id="max-content-width">
</div>
</div>
<p>
To content width changes, click on
<button class="btn btn-micro reload-frontend-button">reload frontend</button>
</p>
</div>
`;
export default class AppearanceOptions extends OptionsWidget {
get tabTitle() { return "Appearance" }
lazyRender() {
this.$widget = $(TPL);
this.$zoomFactorSelect = this.$widget.find("#zoom-factor-select");
this.$nativeTitleBarSelect = this.$widget.find("#native-title-bar-select");
this.$themeSelect = this.$widget.find("#theme-select");
this.$overrideThemeFonts = this.$widget.find("#override-theme-fonts");
this.$overridenFontSettings = this.$widget.find("#overriden-font-settings");
this.$mainFontSize = this.$widget.find("#main-font-size");
this.$mainFontFamily = this.$widget.find("#main-font-family");
this.$treeFontSize = this.$widget.find("#tree-font-size");
this.$treeFontFamily = this.$widget.find("#tree-font-family");
this.$detailFontSize = this.$widget.find("#detail-font-size");
this.$detailFontFamily = this.$widget.find("#detail-font-family");
this.$monospaceFontSize = this.$widget.find("#monospace-font-size");
this.$monospaceFontFamily = this.$widget.find("#monospace-font-family");
this.$widget.find(".reload-frontend-button").on("click", () => utils.reloadFrontendApp("changes from appearance options"));
this.$body = this.$widget.find("body");
this.$themeSelect.on('change', async () => {
const newTheme = this.$themeSelect.val();
await server.put('options/theme/' + newTheme);
utils.reloadFrontendApp("theme change");
});
this.$overrideThemeFonts.on('change', async () => {
this.updateCheckboxOption('overrideThemeFonts', this.$overrideThemeFonts);
this.$overridenFontSettings.toggle(this.$overrideThemeFonts.is(":checked"));
});
this.$zoomFactorSelect.on('change', () => { appContext.triggerCommand('setZoomFactorAndSave', {zoomFactor: this.$zoomFactorSelect.val()}); });
this.$nativeTitleBarSelect.on('change', () => {
const nativeTitleBarVisible = this.$nativeTitleBarSelect.val() === 'show' ? 'true' : 'false';
this.updateOption('nativeTitleBarVisible', nativeTitleBarVisible);
});
const optionsToSave = [
'mainFontFamily', 'mainFontSize',
'treeFontFamily', 'treeFontSize',
'detailFontFamily', 'detailFontSize',
'monospaceFontFamily', 'monospaceFontSize'
];
for (const optionName of optionsToSave) {
this['$' + optionName].on('change', () =>
this.updateOption(optionName, this['$' + optionName].val()));
}
this.$maxContentWidth = this.$widget.find("#max-content-width");
this.$maxContentWidth.on('change', async () =>
this.updateOption('maxContentWidth', this.$maxContentWidth.val()))
}
toggleBodyClass(prefix, value) {
for (const clazz of Array.from(this.$body[0].classList)) { // create copy to safely iterate over while removing classes
if (clazz.startsWith(prefix)) {
this.$body.removeClass(clazz);
}
}
this.$body.addClass(prefix + value);
}
async optionsLoaded(options) {
if (utils.isElectron()) {
this.$zoomFactorSelect.val(options.zoomFactor);
}
else {
this.$zoomFactorSelect.prop('disabled', true);
}
this.$nativeTitleBarSelect.val(options.nativeTitleBarVisible === 'true' ? 'show' : 'hide');
const themes = [
{ val: 'light', title: 'Light' },
{ val: 'dark', title: 'Dark' }
].concat(await server.get('options/user-themes'));
this.$themeSelect.empty();
for (const theme of themes) {
this.$themeSelect.append($("<option>")
.attr("value", theme.val)
.attr("data-note-id", theme.noteId)
.text(theme.title));
}
this.$themeSelect.val(options.theme);
this.setCheckboxState(this.$overrideThemeFonts, options.overrideThemeFonts);
this.$overridenFontSettings.toggle(options.overrideThemeFonts === 'true');
this.$mainFontSize.val(options.mainFontSize);
this.fillFontFamilyOptions(this.$mainFontFamily, options.mainFontFamily);
this.$treeFontSize.val(options.treeFontSize);
this.fillFontFamilyOptions(this.$treeFontFamily, options.treeFontFamily);
this.$detailFontSize.val(options.detailFontSize);
this.fillFontFamilyOptions(this.$detailFontFamily, options.detailFontFamily);
this.$monospaceFontSize.val(options.monospaceFontSize);
this.fillFontFamilyOptions(this.$monospaceFontFamily, options.monospaceFontFamily);
this.$maxContentWidth.val(options.maxContentWidth);
}
fillFontFamilyOptions($select, currentValue) {
$select.empty();
for (const {value, label} of FONT_FAMILIES) {
$select.append($("<option>")
.attr("value", value)
.prop("selected", value === currentValue)
.text(label));
}
}
}

View File

@ -1,97 +0,0 @@
import mimeTypesService from "../../../services/mime_types.js";
import options from "../../../services/options.js";
import server from "../../../services/server.js";
import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js";
const TPL = `
<div class="options-section">
<h4>Use vim keybindings in code notes (no ex mode)</h4>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="vim-keymap-enabled">
<label class="custom-control-label" for="vim-keymap-enabled">Enable Vim Keybindings</label>
</div>
</div>
<div class="options-section">
<h4>Wrap lines in code notes</h4>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="line-wrap-enabled">
<label class="custom-control-label" for="line-wrap-enabled">Enable Line Wrap (change might need a frontend reload to take effect)</label>
</div>
</div>
<div class="options-section">
<h4>Automatic readonly size</h4>
<p>Automatic readonly note size is the size after which notes will be displayed in a readonly mode (for performance reasons).</p>
<div class="form-group">
<label for="auto-readonly-size-code">Automatic readonly size (code notes)</label>
<input class="form-control" id="auto-readonly-size-code" type="number" min="0">
</div>
</div>
<div class="options-section">
<h4>Available MIME types in the dropdown</h4>
<ul id="options-mime-types" style="max-height: 500px; overflow: auto; list-style-type: none;"></ul>
</div>`;
export default class CodeNotesOptions extends OptionsWidget {
get tabTitle() { return "Code notes" }
lazyRender() {
this.$widget = $(TPL);
this.$vimKeymapEnabled = this.$widget.find("#vim-keymap-enabled");
this.$vimKeymapEnabled.on('change', () =>
this.updateCheckboxOption('vimKeymapEnabled', this.$vimKeymapEnabled));
this.$codeLineWrapEnabled = this.$widget.find("#line-wrap-enabled");
this.$codeLineWrapEnabled.on('change', () =>
this.updateCheckboxOption('codeLineWrapEnabled', this.$codeLineWrapEnabled));
this.$mimeTypes = this.$widget.find("#options-mime-types");
this.$autoReadonlySizeCode = this.$widget.find("#auto-readonly-size-code");
this.$autoReadonlySizeCode.on('change', () =>
this.updateOption('autoReadonlySizeCode', this.$autoReadonlySizeCode.val()));
}
async optionsLoaded(options) {
this.$mimeTypes.empty();
this.setCheckboxState(this.$vimKeymapEnabled, options.vimKeymapEnabled);
this.setCheckboxState(this.$codeLineWrapEnabled, options.codeLineWrapEnabled);
this.$autoReadonlySizeCode.val(options.autoReadonlySizeCode);
let idCtr = 1;
for (const mimeType of await mimeTypesService.getMimeTypes()) {
const id = "code-mime-type-" + (idCtr++);
this.$mimeTypes.append($("<li>")
.append($('<input type="checkbox">')
.attr("id", id)
.attr("data-mime-type", mimeType.mime)
.prop("checked", mimeType.enabled))
.on('change', () => this.save())
.append(" &nbsp; ")
.append($('<label>')
.attr("for", id)
.text(mimeType.title))
);
}
}
async save() {
const enabledMimeTypes = [];
this.$mimeTypes.find("input:checked").each(
(i, el) => enabledMimeTypes.push(this.$widget.find(el).attr("data-mime-type")));
await this.updateOption('codeNotesMimeTypes', JSON.stringify(enabledMimeTypes));
mimeTypesService.loadMimeTypes();
}
}

View File

@ -1,80 +0,0 @@
import server from "../../../services/server.js";
import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js";
const TPL = `
<p><strong>Settings on this options tab are saved automatically after each change.</strong></p>
<div class="options-section">
<h4>Heading style</h4>
<select class="form-control" id="heading-style">
<option value="plain">Plain</option>
<option value="underline">Underline</option>
<option value="markdown">Markdown-style</option>
</select>
</div>
<div class="options-section">
<h4>Table of contents</h4>
Table of contents will appear in text notes when the note has more than a defined number of headings. You can customize this number:
<div class="form-group">
<input type="number" class="form-control" id="min-toc-headings" min="0" max="9999999999999999" step="1" style="text-align: right;"/>
</div>
<p>You can also use this option to effectively disable TOC by setting a very high number.</p>
</div>
<div class="options-section">
<h4>Automatic readonly size</h4>
<p>Automatic readonly note size is the size after which notes will be displayed in a readonly mode (for performance reasons).</p>
<div class="form-group">
<label for="auto-readonly-size-text">Automatic readonly size (text notes)</label>
<input class="form-control" id="auto-readonly-size-text" type="number" min="0" style="text-align: right;">
</div>
</div>`;
export default class TextNotesOptions extends OptionsWidget {
get tabTitle() { return "Text notes" }
lazyRender() {
this.$widget = $(TPL);
this.$body = $("body");
this.$headingStyle = this.$widget.find("#heading-style");
this.$headingStyle.on('change', () => {
const newHeadingStyle = this.$headingStyle.val();
this.toggleBodyClass("heading-style-", newHeadingStyle);
this.updateOption('headingStyle', newHeadingStyle);
});
this.$minTocHeadings = this.$widget.find("#min-toc-headings");
this.$minTocHeadings.on('change', () =>
this.updateOption('minTocHeadings', this.$minTocHeadings.val()));
this.$autoReadonlySizeText = this.$widget.find("#auto-readonly-size-text");
this.$autoReadonlySizeText.on('change', () =>
this.updateOption('autoReadonlySizeText', this.$autoReadonlySizeText.val()));
}
toggleBodyClass(prefix, value) {
for (const clazz of Array.from(this.$body[0].classList)) { // create copy to safely iterate over while removing classes
if (clazz.startsWith(prefix)) {
this.$body.removeClass(clazz);
}
}
this.$body.addClass(prefix + value);
}
optionsLoaded(options) {
this.$headingStyle.val(options.headingStyle);
this.$minTocHeadings.val(options.minTocHeadings);
this.$autoReadonlySizeText.val(options.autoReadonlySizeText);
}
}

View File

@ -4,6 +4,14 @@ import NativeTitleBarOptions from "./options/appearance/native_title_bar.js";
import ThemeOptions from "./options/appearance/theme.js"; import ThemeOptions from "./options/appearance/theme.js";
import FontsOptions from "./options/appearance/fonts.js"; import FontsOptions from "./options/appearance/fonts.js";
import MaxContentWidthOptions from "./options/appearance/max_content_width.js"; import MaxContentWidthOptions from "./options/appearance/max_content_width.js";
import KeyboardShortcutsOptions from "./options/shortcuts.js";
import HeadingStyleOptions from "./options/text_notes/heading_style.js";
import TableOfContentsOptions from "./options/text_notes/table_of_contents.js";
import TextAutoReadOnlySizeOptions from "./options/text_notes/text_auto_read_only_size.js";
import VimKeyBindingsOptions from "./options/code_notes/vim_key_bindings.js";
import WrapLinesOptions from "./options/code_notes/wrap_lines.js";
import CodeAutoReadOnlySizeOptions from "./options/code_notes/code_auto_read_only_size.js";
import CodeMimeTypesOptions from "./options/code_notes/code_mime_types.js";
const TPL = `<div class="note-detail-content-widget note-detail-printable"> const TPL = `<div class="note-detail-content-widget note-detail-printable">
<style> <style>
@ -22,6 +30,18 @@ const CONTENT_WIDGETS = {
ThemeOptions, ThemeOptions,
FontsOptions, FontsOptions,
MaxContentWidthOptions MaxContentWidthOptions
],
optionsShortcuts: [ KeyboardShortcutsOptions ],
optionsTextNotes: [
HeadingStyleOptions,
TableOfContentsOptions,
TextAutoReadOnlySizeOptions
],
optionsCodeNotes: [
VimKeyBindingsOptions,
WrapLinesOptions,
CodeAutoReadOnlySizeOptions,
CodeMimeTypesOptions
] ]
}; };
@ -45,7 +65,7 @@ export default class ContentWidgetTypeWidget extends TypeWidget {
for (const clazz of contentWidgets) { for (const clazz of contentWidgets) {
const widget = new clazz(); const widget = new clazz();
await widget.handleEvent('setNoteContext', {noteContext: this.noteContext}); await widget.handleEvent('setNoteContext', { noteContext: this.noteContext });
this.child(widget); this.child(widget);
this.$content.append(widget.render()); this.$content.append(widget.render());

View File

@ -1,13 +1,13 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4>Sync</h4> <h4>Sync</h4>
<button id="force-full-sync-button" class="btn">Force full sync</button> <button class="force-full-sync-button btn">Force full sync</button>
<button id="fill-entity-changes-button" class="btn">Fill entity changes records</button> <button class="fill-entity-changes-button btn">Fill entity changes records</button>
</div> </div>
<div class="options-section"> <div class="options-section">
@ -15,13 +15,13 @@ const TPL = `
<p>This will check that the database is not corrupted on the SQLite level. It might take some time, depending on the DB size.</p> <p>This will check that the database is not corrupted on the SQLite level. It might take some time, depending on the DB size.</p>
<button id="check-integrity-button" class="btn">Check database integrity</button> <button class="check-integrity-button btn">Check database integrity</button>
</div> </div>
<div class="options-section"> <div class="options-section">
<h4>Consistency checks</h4> <h4>Consistency checks</h4>
<button id="find-and-fix-consistency-issues-button" class="btn">Find and fix consistency issues</button> <button class="find-and-fix-consistency-issues-button btn">Find and fix consistency issues</button>
</div> </div>
<div class="options-section"> <div class="options-section">
@ -32,7 +32,7 @@ const TPL = `
<p>This action will create a new copy of the database and anonymize it (remove all note content and leave only structure and some non-sensitive metadata) <p>This action will create a new copy of the database and anonymize it (remove all note content and leave only structure and some non-sensitive metadata)
for sharing online for debugging purposes without fear of leaking your personal data.</p> for sharing online for debugging purposes without fear of leaking your personal data.</p>
<button id="anonymize-full-button" class="btn">Save fully anonymized database</button> <button class="anonymize-full-button btn">Save fully anonymized database</button>
<h5>Light anonymization</h5> <h5>Light anonymization</h5>
@ -40,7 +40,7 @@ const TPL = `
<p>You can decide yourself if you want to provide fully or lightly anonymized database. Even fully anonymized DB is very useful, however in some cases lightly anonymized database can speed up the process of bug identification and fixing.</p> <p>You can decide yourself if you want to provide fully or lightly anonymized database. Even fully anonymized DB is very useful, however in some cases lightly anonymized database can speed up the process of bug identification and fixing.</p>
<button id="anonymize-light-button" class="btn">Save lightly anonymized database</button> <button class="anonymize-light-button btn">Save lightly anonymized database</button>
</div> </div>
<div class="options-section"> <div class="options-section">
@ -48,7 +48,7 @@ const TPL = `
<p>This will rebuild the database which will typically result in a smaller database file. No data will be actually changed.</p> <p>This will rebuild the database which will typically result in a smaller database file. No data will be actually changed.</p>
<button id="vacuum-database-button" class="btn">Vacuum database</button> <button class="vacuum-database-button btn">Vacuum database</button>
</div>`; </div>`;
export default class AdvancedOptions extends OptionsWidget { export default class AdvancedOptions extends OptionsWidget {
@ -57,13 +57,13 @@ export default class AdvancedOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$forceFullSyncButton = this.$widget.find("#force-full-sync-button"); this.$forceFullSyncButton = this.$widget.find(".force-full-sync-button");
this.$fillEntityChangesButton = this.$widget.find("#fill-entity-changes-button"); this.$fillEntityChangesButton = this.$widget.find(".fill-entity-changes-button");
this.$anonymizeFullButton = this.$widget.find("#anonymize-full-button"); this.$anonymizeFullButton = this.$widget.find(".anonymize-full-button");
this.$anonymizeLightButton = this.$widget.find("#anonymize-light-button"); this.$anonymizeLightButton = this.$widget.find(".anonymize-light-button");
this.$vacuumDatabaseButton = this.$widget.find("#vacuum-database-button"); this.$vacuumDatabaseButton = this.$widget.find(".vacuum-database-button");
this.$findAndFixConsistencyIssuesButton = this.$widget.find("#find-and-fix-consistency-issues-button"); this.$findAndFixConsistencyIssuesButton = this.$widget.find(".find-and-fix-consistency-issues-button");
this.$checkIntegrityButton = this.$widget.find("#check-integrity-button"); this.$checkIntegrityButton = this.$widget.find(".check-integrity-button");
this.$forceFullSyncButton.on('click', async () => { this.$forceFullSyncButton.on('click', async () => {
await server.post('sync/force-full-sync'); await server.post('sync/force-full-sync');

View File

@ -1,4 +1,4 @@
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "../options_widget.js";
import utils from "../../../../services/utils.js"; import utils from "../../../../services/utils.js";
const FONT_FAMILIES = [ const FONT_FAMILIES = [

View File

@ -1,4 +1,4 @@
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "../options_widget.js";
import utils from "../../../../services/utils.js"; import utils from "../../../../services/utils.js";
const TPL = ` const TPL = `

View File

@ -1,4 +1,4 @@
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "../options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">

View File

@ -1,4 +1,4 @@
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "../options_widget.js";
import server from "../../../../services/server.js"; import server from "../../../../services/server.js";
import utils from "../../../../services/utils.js"; import utils from "../../../../services/utils.js";

View File

@ -1,5 +1,5 @@
import appContext from "../../../../components/app_context.js"; import appContext from "../../../../components/app_context.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "../options_widget.js";
import utils from "../../../../services/utils.js"; import utils from "../../../../services/utils.js";
const TPL = ` const TPL = `

View File

@ -1,6 +1,6 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
@ -9,18 +9,18 @@ const TPL = `
<p>Trilium can back up the database automatically:</p> <p>Trilium can back up the database automatically:</p>
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="daily-backup-enabled"> <input type="checkbox" class="daily-backup-enabled custom-control-input">
<label class="custom-control-label" for="daily-backup-enabled">Enable daily backup</label> <label class="custom-control-label">Enable daily backup</label>
</div> </div>
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="weekly-backup-enabled"> <input type="checkbox" class="weekly-backup-enabled custom-control-input">
<label class="custom-control-label" for="weekly-backup-enabled">Enable weekly backup</label> <label class="custom-control-label">Enable weekly backup</label>
</div> </div>
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="monthly-backup-enabled"> <input type="checkbox" class="monthly-backup-enabled custom-control-input">
<label class="custom-control-label" for="monthly-backup-enabled">Enable monthly backup</label> <label class="custom-control-label">Enable monthly backup</label>
</div> </div>
<br/> <br/>
@ -31,7 +31,7 @@ const TPL = `
<div class="options-section"> <div class="options-section">
<h4>Backup now</h4> <h4>Backup now</h4>
<button id="backup-database-button" class="btn">Backup database now</button> <button class="backup-database-button btn">Backup database now</button>
</div> </div>
`; `;
@ -41,7 +41,7 @@ export default class BackupOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$backupDatabaseButton = this.$widget.find("#backup-database-button"); this.$backupDatabaseButton = this.$widget.find(".backup-database-button");
this.$backupDatabaseButton.on('click', async () => { this.$backupDatabaseButton.on('click', async () => {
const {backupFile} = await server.post('database/backup-database'); const {backupFile} = await server.post('database/backup-database');
@ -49,9 +49,9 @@ export default class BackupOptions extends OptionsWidget {
toastService.showMessage("Database has been backed up to " + backupFile, 10000); toastService.showMessage("Database has been backed up to " + backupFile, 10000);
}); });
this.$dailyBackupEnabled = this.$widget.find("#daily-backup-enabled"); this.$dailyBackupEnabled = this.$widget.find(".daily-backup-enabled");
this.$weeklyBackupEnabled = this.$widget.find("#weekly-backup-enabled"); this.$weeklyBackupEnabled = this.$widget.find(".weekly-backup-enabled");
this.$monthlyBackupEnabled = this.$widget.find("#monthly-backup-enabled"); this.$monthlyBackupEnabled = this.$widget.find(".monthly-backup-enabled");
this.$dailyBackupEnabled.on('change', () => this.$dailyBackupEnabled.on('change', () =>
this.updateCheckboxOption('dailyBackupEnabled', this.$dailyBackupEnabled)); this.updateCheckboxOption('dailyBackupEnabled', this.$dailyBackupEnabled));

View File

@ -0,0 +1,26 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Automatic read-only size</h4>
<p>Automatic read-only note size is the size after which notes will be displayed in a read-only mode (for performance reasons).</p>
<div class="form-group">
<label>Automatic read-only size (code notes)</label>
<input class="auto-readonly-size-code form-control" type="number" min="0">
</div>
</div>`;
export default class CodeAutoReadOnlySizeOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$autoReadonlySizeCode = this.$widget.find(".auto-readonly-size-code");
this.$autoReadonlySizeCode.on('change', () =>
this.updateOption('autoReadonlySizeCode', this.$autoReadonlySizeCode.val()));
}
async optionsLoaded(options) {
this.$autoReadonlySizeCode.val(options.autoReadonlySizeCode);
}
}

View File

@ -0,0 +1,49 @@
import OptionsWidget from "../options_widget.js";
import mimeTypesService from "../../../../services/mime_types.js";
const TPL = `
<div class="options-section">
<h4>Available MIME types in the dropdown</h4>
<ul class="options-mime-types" style="list-style-type: none;"></ul>
</div>`;
let idCtr = 1; // global, since this can be shown in multiple dialogs
export default class CodeMimeTypesOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$mimeTypes = this.$widget.find(".options-mime-types");
}
async optionsLoaded(options) {
this.$mimeTypes.empty();
for (const mimeType of await mimeTypesService.getMimeTypes()) {
const id = "code-mime-type-" + (idCtr++);
this.$mimeTypes.append($("<li>")
.append($('<input type="checkbox">')
.attr("id", id)
.attr("data-mime-type", mimeType.mime)
.prop("checked", mimeType.enabled))
.on('change', () => this.save())
.append(" &nbsp; ")
.append($('<label>')
.attr("for", id)
.text(mimeType.title))
);
}
}
async save() {
const enabledMimeTypes = [];
this.$mimeTypes.find("input:checked").each(
(i, el) => enabledMimeTypes.push(this.$widget.find(el).attr("data-mime-type")));
await this.updateOption('codeNotesMimeTypes', JSON.stringify(enabledMimeTypes));
mimeTypesService.loadMimeTypes();
}
}

View File

@ -0,0 +1,23 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Use vim keybindings in code notes (no ex mode)</h4>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="vim-keymap-enabled custom-control-input">
<label class="custom-control-label">Enable Vim Keybindings</label>
</div>
</div>`;
export default class VimKeyBindingsOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$vimKeymapEnabled = this.$widget.find(".vim-keymap-enabled");
this.$vimKeymapEnabled.on('change', () =>
this.updateCheckboxOption('vimKeymapEnabled', this.$vimKeymapEnabled));
}
async optionsLoaded(options) {
this.setCheckboxState(this.$vimKeymapEnabled, options.vimKeymapEnabled);
}
}

View File

@ -0,0 +1,23 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Wrap lines in code notes</h4>
<div class="custom-control custom-checkbox">
<input type="checkbox" class="line-wrap-enabled custom-control-input">
<label class="custom-control-label">Enable Line Wrap (change might need a frontend reload to take effect)</label>
</div>
</div>`;
export default class WrapLinesOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$codeLineWrapEnabled = this.$widget.find(".line-wrap-enabled");
this.$codeLineWrapEnabled.on('change', () =>
this.updateCheckboxOption('codeLineWrapEnabled', this.$codeLineWrapEnabled));
}
async optionsLoaded(options) {
this.setCheckboxState(this.$codeLineWrapEnabled, options.codeLineWrapEnabled);
}
}

View File

@ -1,7 +1,7 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import dialogService from "../../../services/dialog.js"; import dialogService from "../../../services/dialog.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
@ -10,14 +10,14 @@ const TPL = `
<p>ETAPI is a REST API used to access Trilium instance programmatically, without UI. <br/> <p>ETAPI is a REST API used to access Trilium instance programmatically, without UI. <br/>
See more details on <a href="https://github.com/zadam/trilium/wiki/ETAPI">wiki</a> and <a onclick="window.open('etapi/etapi.openapi.yaml')" href="etapi/etapi.openapi.yaml">ETAPI OpenAPI spec</a>.</p> See more details on <a href="https://github.com/zadam/trilium/wiki/ETAPI">wiki</a> and <a onclick="window.open('etapi/etapi.openapi.yaml')" href="etapi/etapi.openapi.yaml">ETAPI OpenAPI spec</a>.</p>
<button type="button" class="btn btn-sm" id="create-etapi-token">Create new ETAPI token</button> <button type="button" class="create-etapi-token btn btn-sm">Create new ETAPI token</button>
<h5>Existing tokens</h5> <h5>Existing tokens</h5>
<div id="no-tokens-yet">There are no tokens yet. Click on the button above to create one.</div> <div class="no-tokens-yet">There are no tokens yet. Click on the button above to create one.</div>
<div style="overflow: auto; height: 500px;"> <div style="overflow: auto; height: 500px;">
<table id="tokens-table" class="table table-stripped"> <table class="tokens-table table table-stripped">
<thead> <thead>
<tr> <tr>
<th>Token name</th> <th>Token name</th>
@ -52,7 +52,7 @@ export default class EtapiOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$widget.find("#create-etapi-token").on("click", async () => { this.$widget.find(".create-etapi-token").on("click", async () => {
const tokenName = await dialogService.prompt({ const tokenName = await dialogService.prompt({
title: "New ETAPI token", title: "New ETAPI token",
message: "Please enter new token's name", message: "Please enter new token's name",
@ -79,8 +79,8 @@ export default class EtapiOptions extends OptionsWidget {
} }
async refreshTokens() { async refreshTokens() {
const $noTokensYet = this.$widget.find("#no-tokens-yet"); const $noTokensYet = this.$widget.find(".no-tokens-yet");
const $tokensTable = this.$widget.find("#tokens-table"); const $tokensTable = this.$widget.find(".tokens-table");
const tokens = await server.get('etapi-tokens'); const tokens = await server.get('etapi-tokens');

View File

@ -1,4 +1,4 @@
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<style> <style>
@ -12,25 +12,25 @@ const TPL = `
<h4>Images</h4> <h4>Images</h4>
<div class="form-group"> <div class="form-group">
<input id="download-images-automatically" type="checkbox" name="download-images-automatically"> <input class="download-images-automatically" type="checkbox" name="download-images-automatically">
<label for="download-images-automatically">Download images automatically for offline use.</label> <label>Download images automatically for offline use.</label>
<p>(pasted HTML can contain references to online images, Trilium will find those references and download the images so that they are available offline)</p> <p>(pasted HTML can contain references to online images, Trilium will find those references and download the images so that they are available offline)</p>
</div> </div>
<div class="form-group"> <div class="form-group">
<input id="image-compresion-enabled" type="checkbox" name="image-compression-enabled"> <input class="image-compresion-enabled" type="checkbox" name="image-compression-enabled">
<label for="image-compresion-enabled">Enable image compression</label> <label>Enable image compression</label>
</div> </div>
<div id="image-compression-enabled-wraper"> <div class="image-compression-enabled-wraper">
<div class="form-group"> <div class="form-group">
<label for="image-max-width-height">Max width / height of an image in pixels (image will be resized if it exceeds this setting).</label> <label>Max width / height of an image in pixels (image will be resized if it exceeds this setting).</label>
<input class="form-control" id="image-max-width-height" type="number" min="1"> <input class="image-max-width-height form-control" type="number" min="1">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="image-jpeg-quality">JPEG quality (10 - worst quality, 100 best quality, 50 - 85 is recommended)</label> <label>JPEG quality (10 - worst quality, 100 best quality, 50 - 85 is recommended)</label>
<input class="form-control" id="image-jpeg-quality" min="10" max="100" type="number"> <input class="image-jpeg-quality form-control" min="10" max="100" type="number">
</div> </div>
</div> </div>
</div> </div>
@ -42,8 +42,8 @@ export default class ImageOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$imageMaxWidthHeight = this.$widget.find("#image-max-width-height"); this.$imageMaxWidthHeight = this.$widget.find(".image-max-width-height");
this.$imageJpegQuality = this.$widget.find("#image-jpeg-quality"); this.$imageJpegQuality = this.$widget.find(".image-jpeg-quality");
this.$imageMaxWidthHeight.on('change', () => this.$imageMaxWidthHeight.on('change', () =>
this.updateOption('imageMaxWidthHeight', this.$imageMaxWidthHeight.val())); this.updateOption('imageMaxWidthHeight', this.$imageMaxWidthHeight.val()));
@ -51,13 +51,13 @@ export default class ImageOptions extends OptionsWidget {
this.$imageJpegQuality.on('change', () => this.$imageJpegQuality.on('change', () =>
this.updateOption('imageJpegQuality', this.$imageJpegQuality.val())); this.updateOption('imageJpegQuality', this.$imageJpegQuality.val()));
this.$downloadImagesAutomatically = this.$widget.find("#download-images-automatically"); this.$downloadImagesAutomatically = this.$widget.find(".download-images-automatically");
this.$downloadImagesAutomatically.on("change", () => this.$downloadImagesAutomatically.on("change", () =>
this.updateCheckboxOption('downloadImagesAutomatically', this.$downloadImagesAutomatically)); this.updateCheckboxOption('downloadImagesAutomatically', this.$downloadImagesAutomatically));
this.$enableImageCompression = this.$widget.find("#image-compresion-enabled"); this.$enableImageCompression = this.$widget.find(".image-compresion-enabled");
this.$imageCompressionWrapper = this.$widget.find("#image-compression-enabled-wraper"); this.$imageCompressionWrapper = this.$widget.find(".image-compression-enabled-wraper");
this.$enableImageCompression.on("change", () => { this.$enableImageCompression.on("change", () => {
this.updateCheckboxOption('compressImages', this.$enableImageCompression); this.updateCheckboxOption('compressImages', this.$enableImageCompression);

View File

@ -1,7 +1,7 @@
import server from "../../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../../services/toast.js"; import toastService from "../../../services/toast.js";
import NoteContextAwareWidget from "../../../note_context_aware_widget.js"; import NoteContextAwareWidget from "../../note_context_aware_widget.js";
import attributeService from "../../../../services/attributes.js"; import attributeService from "../../../services/attributes.js";
export default class OptionsWidget extends NoteContextAwareWidget { export default class OptionsWidget extends NoteContextAwareWidget {
constructor() { constructor() {
@ -42,6 +42,8 @@ export default class OptionsWidget extends NoteContextAwareWidget {
$checkbox.prop('checked', optionValue === 'true'); $checkbox.prop('checked', optionValue === 'true');
} }
optionsLoaded(options) {}
async refreshWithNote(note) { async refreshWithNote(note) {
const options = await server.get('options'); const options = await server.get('options');

View File

@ -1,14 +1,14 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4>Tray</h4> <h4>Tray</h4>
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="tray-enabled"> <input type="checkbox" class="tray-enabled custom-control-input">
<label class="custom-control-label" for="tray-enabled">Enable tray (Trilium needs to be restarted for this change to take effect)</label> <label class="custom-control-label">Enable tray (Trilium needs to be restarted for this change to take effect)</label>
</div> </div>
</div> </div>
@ -21,13 +21,13 @@ const TPL = `
of the period between deleting and erasing the note.</p> of the period between deleting and erasing the note.</p>
<div class="form-group"> <div class="form-group">
<label for="erase-entities-after-time-in-seconds">Erase notes after X seconds</label> <label>Erase notes after X seconds</label>
<input class="form-control" id="erase-entities-after-time-in-seconds" type="number" min="0"> <input class="erase-entities-after-time-in-seconds form-control" type="number" min="0">
</div> </div>
<p>You can also trigger erasing manually:</p> <p>You can also trigger erasing manually:</p>
<button id="erase-deleted-notes-now-button" class="btn">Erase deleted notes now</button> <button class="erase-deleted-notes-now-button btn">Erase deleted notes now</button>
</div> </div>
<div class="options-section"> <div class="options-section">
@ -36,8 +36,8 @@ const TPL = `
<p>Note revision snapshot time interval is time in seconds after which a new note revision will be created for the note. See <a href="https://github.com/zadam/trilium/wiki/Note-revisions" class="external">wiki</a> for more info.</p> <p>Note revision snapshot time interval is time in seconds after which a new note revision will be created for the note. See <a href="https://github.com/zadam/trilium/wiki/Note-revisions" class="external">wiki</a> for more info.</p>
<div class="form-group"> <div class="form-group">
<label for="note-revision-snapshot-time-interval-in-seconds">Note revision snapshot time interval (in seconds)</label> <label>Note revision snapshot time interval (in seconds)</label>
<input class="form-control" id="note-revision-snapshot-time-interval-in-seconds" type="number" min="10"> <input class="note-revision-snapshot-time-interval-in-seconds form-control" type="number" min="10">
</div> </div>
</div> </div>
@ -45,8 +45,8 @@ const TPL = `
<h4>Network connections</h4> <h4>Network connections</h4>
<div class="form-group"> <div class="form-group">
<input id="check-for-updates" type="checkbox" name="check-for-updates"> <input class="check-for-updates" type="checkbox" name="check-for-updates">
<label for="check-for-updates">Check for updates automatically</label> <label>Check for updates automatically</label>
</div> </div>
</div>`; </div>`;
@ -56,26 +56,26 @@ export default class OtherOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$trayEnabled = this.$widget.find("#tray-enabled"); this.$trayEnabled = this.$widget.find(".tray-enabled");
this.$trayEnabled.on('change', () => this.$trayEnabled.on('change', () =>
this.updateOption('disableTray', !this.$trayEnabled.is(":checked") ? "true" : "false")); this.updateOption('disableTray', !this.$trayEnabled.is(":checked") ? "true" : "false"));
this.$eraseEntitiesAfterTimeInSeconds = this.$widget.find("#erase-entities-after-time-in-seconds"); this.$eraseEntitiesAfterTimeInSeconds = this.$widget.find(".erase-entities-after-time-in-seconds");
this.$eraseEntitiesAfterTimeInSeconds.on('change', () => this.updateOption('eraseEntitiesAfterTimeInSeconds', this.$eraseEntitiesAfterTimeInSeconds.val())); this.$eraseEntitiesAfterTimeInSeconds.on('change', () => this.updateOption('eraseEntitiesAfterTimeInSeconds', this.$eraseEntitiesAfterTimeInSeconds.val()));
this.$eraseDeletedNotesButton = this.$widget.find("#erase-deleted-notes-now-button"); this.$eraseDeletedNotesButton = this.$widget.find(".erase-deleted-notes-now-button");
this.$eraseDeletedNotesButton.on('click', () => { this.$eraseDeletedNotesButton.on('click', () => {
server.post('notes/erase-deleted-notes-now').then(() => { server.post('notes/erase-deleted-notes-now').then(() => {
toastService.showMessage("Deleted notes have been erased."); toastService.showMessage("Deleted notes have been erased.");
}); });
}); });
this.$noteRevisionsTimeInterval = this.$widget.find("#note-revision-snapshot-time-interval-in-seconds"); this.$noteRevisionsTimeInterval = this.$widget.find(".note-revision-snapshot-time-interval-in-seconds");
this.$noteRevisionsTimeInterval.on('change', () => this.$noteRevisionsTimeInterval.on('change', () =>
this.updateOption('noteRevisionSnapshotTimeInterval', this.$noteRevisionsTimeInterval.val())); this.updateOption('noteRevisionSnapshotTimeInterval', this.$noteRevisionsTimeInterval.val()));
this.$checkForUpdates = this.$widget.find("#check-for-updates"); this.$checkForUpdates = this.$widget.find(".check-for-updates");
this.$checkForUpdates.on("change", () => this.$checkForUpdates.on("change", () =>
this.updateCheckboxOption('checkForUpdates', this.$checkForUpdates)); this.updateCheckboxOption('checkForUpdates', this.$checkForUpdates));
} }

View File

@ -1,35 +1,35 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import protectedSessionHolder from "../../../services/protected_session_holder.js"; import protectedSessionHolder from "../../../services/protected_session_holder.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4 id="password-heading"></h4> <h4 class="password-heading"></h4>
<div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;"> <div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;">
Please take care to remember your new password. Password is used for logging into the web interface and Please take care to remember your new password. Password is used for logging into the web interface and
to encrypt protected notes. If you forget your password, then all your protected notes are forever lost. to encrypt protected notes. If you forget your password, then all your protected notes are forever lost.
In case you did forget your password, <a id="reset-password-button" href="javascript:">click here to reset it</a>. In case you did forget your password, <a class="reset-password-button" href="javascript:">click here to reset it</a>.
</div> </div>
<form id="change-password-form"> <form class="change-password-form">
<div class="form-group" id="old-password-form-group"> <div class="old-password-form-group form-group">
<label for="old-password">Old password</label> <label>Old password</label>
<input class="form-control" id="old-password" type="password"> <input class="old-password form-control" type="password">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="new-password1">New password</label> <label>New password</label>
<input class="form-control" id="new-password1" type="password"> <input class="new-password1 form-control" type="password">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="new-password2">New password Confirmation</label> <label>New password Confirmation</label>
<input class="form-control" id="new-password2" type="password"> <input class="new-password2 form-control" type="password">
</div> </div>
<button class="btn btn-primary" id="save-password-button">Change password</button> <button class="save-password-button btn btn-primary">Change password</button>
</form> </form>
</div> </div>
@ -40,8 +40,8 @@ const TPL = `
the browser's memory. This is measured from the last interaction with protected notes. See <a href="https://github.com/zadam/trilium/wiki/Protected-notes" class="external">wiki</a> for more info.</p> the browser's memory. This is measured from the last interaction with protected notes. See <a href="https://github.com/zadam/trilium/wiki/Protected-notes" class="external">wiki</a> for more info.</p>
<div class="form-group"> <div class="form-group">
<label for="protected-session-timeout-in-seconds">Protected session timeout (in seconds)</label> <label>Protected session timeout (in seconds)</label>
<input class="form-control" id="protected-session-timeout-in-seconds" type="number" min="60"> <input class="protected-session-timeout-in-seconds form-control" type="number" min="60">
</div> </div>
</div>`; </div>`;
@ -51,13 +51,13 @@ export default class PasswordOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$passwordHeading = this.$widget.find("#password-heading"); this.$passwordHeading = this.$widget.find(".password-heading");
this.$changePasswordForm = this.$widget.find("#change-password-form"); this.$changePasswordForm = this.$widget.find(".change-password-form");
this.$oldPassword = this.$widget.find("#old-password"); this.$oldPassword = this.$widget.find(".old-password");
this.$newPassword1 = this.$widget.find("#new-password1"); this.$newPassword1 = this.$widget.find(".new-password1");
this.$newPassword2 = this.$widget.find("#new-password2"); this.$newPassword2 = this.$widget.find(".new-password2");
this.$savePasswordButton = this.$widget.find("#save-password-button"); this.$savePasswordButton = this.$widget.find(".save-password-button");
this.$resetPasswordButton = this.$widget.find("#reset-password-button"); this.$resetPasswordButton = this.$widget.find(".reset-password-button");
this.$resetPasswordButton.on("click", async () => { this.$resetPasswordButton.on("click", async () => {
if (confirm("By resetting the password you will forever lose access to all your existing protected notes. Do you really want to reset the password?")) { if (confirm("By resetting the password you will forever lose access to all your existing protected notes. Do you really want to reset the password?")) {
@ -72,7 +72,7 @@ export default class PasswordOptions extends OptionsWidget {
this.$changePasswordForm.on('submit', () => this.save()); this.$changePasswordForm.on('submit', () => this.save());
this.$protectedSessionTimeout = this.$widget.find("#protected-session-timeout-in-seconds"); this.$protectedSessionTimeout = this.$widget.find(".protected-session-timeout-in-seconds");
this.$protectedSessionTimeout.on('change', () => this.$protectedSessionTimeout.on('change', () =>
this.updateOption('protectedSessionTimeout', this.$protectedSessionTimeout.val())); this.updateOption('protectedSessionTimeout', this.$protectedSessionTimeout.val()));
} }
@ -80,7 +80,7 @@ export default class PasswordOptions extends OptionsWidget {
optionsLoaded(options) { optionsLoaded(options) {
const isPasswordSet = options.isPasswordSet === 'true'; const isPasswordSet = options.isPasswordSet === 'true';
this.$widget.find("#old-password-form-group").toggle(isPasswordSet); this.$widget.find(".old-password-form-group").toggle(isPasswordSet);
this.$passwordHeading.text(isPasswordSet ? 'Change password' : 'Set password'); this.$passwordHeading.text(isPasswordSet ? 'Change password' : 'Set password');
this.$savePasswordButton.text(isPasswordSet ? 'Change password' : 'Set password'); this.$savePasswordButton.text(isPasswordSet ? 'Change password' : 'Set password');
this.$protectedSessionTimeout.val(options.protectedSessionTimeout); this.$protectedSessionTimeout.val(options.protectedSessionTimeout);

View File

@ -1,10 +1,10 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import utils from "../../../services/utils.js"; import utils from "../../../services/utils.js";
import dialogService from "../../../services/dialog.js"; import dialogService from "../../../services/dialog.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section" style="display: flex; flex-direction: column; height: 100%;">
<h4>Keyboard shortcuts</h4> <h4>Keyboard shortcuts</h4>
<p> <p>
@ -13,11 +13,11 @@ const TPL = `
</p> </p>
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control" id="keyboard-shortcut-filter" placeholder="Type text to filter shortcuts..."> <input type="text" class="keyboard-shortcut-filter form-control" placeholder="Type text to filter shortcuts...">
</div> </div>
<div style="overflow: auto; height: 500px;"> <div style="overflow: auto; flex-grow: 1; flex-shrink: 1;">
<table id="keyboard-shortcut-table" cellpadding="10"> <table class="keyboard-shortcut-table" cellpadding="10">
<thead> <thead>
<tr> <tr>
<th>Action name</th> <th>Action name</th>
@ -31,23 +31,21 @@ const TPL = `
</div> </div>
<div style="display: flex; justify-content: space-between"> <div style="display: flex; justify-content: space-between">
<button class="btn btn-primary" id="options-keyboard-shortcuts-reload-app">Reload app to apply changes</button> <button class="options-keyboard-shortcuts-reload-app btn btn-primary">Reload app to apply changes</button>
<button class="btn" id="options-keyboard-shortcuts-set-all-to-default">Set all shortcuts to the default</button> <button class="options-keyboard-shortcuts-set-all-to-default btn">Set all shortcuts to the default</button>
</div> </div>
</div>`; </div>`;
let globActions; let globActions;
export default class KeyboardShortcutsOptions extends OptionsWidget { export default class KeyboardShortcutsOptions extends OptionsWidget {
get tabTitle() { return "Shortcuts" } doRender() {
lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$widget.find("#options-keyboard-shortcuts-reload-app").on("click", () => utils.reloadFrontendApp()); this.$widget.find(".options-keyboard-shortcuts-reload-app").on("click", () => utils.reloadFrontendApp());
const $table = this.$widget.find("#keyboard-shortcut-table tbody"); const $table = this.$widget.find(".keyboard-shortcut-table tbody");
server.get('keyboard-actions').then(actions => { server.get('keyboard-actions').then(actions => {
globActions = actions; globActions = actions;
@ -93,7 +91,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
this.updateOption(optionName, JSON.stringify(shortcuts)); this.updateOption(optionName, JSON.stringify(shortcuts));
}); });
this.$widget.find("#options-keyboard-shortcuts-set-all-to-default").on('click', async () => { this.$widget.find(".options-keyboard-shortcuts-set-all-to-default").on('click', async () => {
if (!await dialogService.confirm("Do you really want to reset all keyboard shortcuts to the default?")) { if (!await dialogService.confirm("Do you really want to reset all keyboard shortcuts to the default?")) {
return; return;
} }
@ -109,7 +107,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
}); });
}); });
const $filter = this.$widget.find("#keyboard-shortcut-filter"); const $filter = this.$widget.find(".keyboard-shortcut-filter");
$filter.on('keyup', () => { $filter.on('keyup', () => {
const filter = $filter.val().trim().toLowerCase(); const filter = $filter.val().trim().toLowerCase();

View File

@ -1,5 +1,5 @@
import utils from "../../../services/utils.js"; import utils from "../../../services/utils.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
@ -8,20 +8,20 @@ const TPL = `
<p>These options apply only for desktop builds, browsers will use their own native spell check. App restart is required after change.</p> <p>These options apply only for desktop builds, browsers will use their own native spell check. App restart is required after change.</p>
<div class="custom-control custom-checkbox"> <div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="spell-check-enabled"> <input type="checkbox" class="spell-check-enabled custom-control-input">
<label class="custom-control-label" for="spell-check-enabled">Enable spellcheck</label> <label class="custom-control-label">Enable spellcheck</label>
</div> </div>
<br/> <br/>
<div class="form-group"> <div class="form-group">
<label for="spell-check-language-code">Language code(s)</label> <label>Language code(s)</label>
<input type="text" class="form-control" id="spell-check-language-code" placeholder="for example &quot;en-US&quot;, &quot;de-AT&quot;"> <input type="text" class="spell-check-language-code form-control" placeholder="for example &quot;en-US&quot;, &quot;de-AT&quot;">
</div> </div>
<p>Multiple languages can be separated by comma, e.g. <code>en-US, de-DE, cs</code>. Changes to the spell check options will take effect after application restart.</p> <p>Multiple languages can be separated by comma, e.g. <code>en-US, de-DE, cs</code>. Changes to the spell check options will take effect after application restart.</p>
<p><strong>Available language codes: </strong> <span id="available-language-codes"></span></p> <p><strong>Available language codes: </strong> <span class="available-language-codes"></span></p>
</div>`; </div>`;
export default class SpellcheckOptions extends OptionsWidget { export default class SpellcheckOptions extends OptionsWidget {
@ -30,8 +30,8 @@ export default class SpellcheckOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$spellCheckEnabled = this.$widget.find("#spell-check-enabled"); this.$spellCheckEnabled = this.$widget.find(".spell-check-enabled");
this.$spellCheckLanguageCode = this.$widget.find("#spell-check-language-code"); this.$spellCheckLanguageCode = this.$widget.find(".spell-check-language-code");
this.$spellCheckEnabled.on('change', () => this.$spellCheckEnabled.on('change', () =>
this.updateCheckboxOption('spellCheckEnabled', this.$spellCheckEnabled)); this.updateCheckboxOption('spellCheckEnabled', this.$spellCheckEnabled));
@ -39,7 +39,7 @@ export default class SpellcheckOptions extends OptionsWidget {
this.$spellCheckLanguageCode.on('change', () => this.$spellCheckLanguageCode.on('change', () =>
this.updateOption('spellCheckLanguageCode', this.$spellCheckLanguageCode.val())); this.updateOption('spellCheckLanguageCode', this.$spellCheckLanguageCode.val()));
this.$availableLanguageCodes = this.$widget.find("#available-language-codes"); this.$availableLanguageCodes = this.$widget.find(".available-language-codes");
if (utils.isElectron()) { if (utils.isElectron()) {
const { webContents } = utils.dynamicRequire('@electron/remote').getCurrentWindow(); const { webContents } = utils.dynamicRequire('@electron/remote').getCurrentWindow();

View File

@ -1,25 +1,25 @@
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "../../type_widgets/options/appearance/options_widget.js"; import OptionsWidget from "./options_widget.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4 style="margin-top: 0px;">Sync configuration</h4> <h4 style="margin-top: 0px;">Sync configuration</h4>
<form id="sync-setup-form"> <form class="sync-setup-form">
<div class="form-group"> <div class="form-group">
<label for="sync-server-host">Server instance address</label> <label>Server instance address</label>
<input class="form-control" id="sync-server-host" placeholder="https://<host>:<port>"> <input class="sync-server-host form-control" placeholder="https://<host>:<port>">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="sync-server-timeout">Sync timeout (milliseconds)</label> <label>Sync timeout (milliseconds)</label>
<input class="form-control" id="sync-server-timeout" min="1" max="10000000" type="number" style="text-align: left;"> <input class="sync-server-timeout form-control" min="1" max="10000000" type="number" style="text-align: left;">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="sync-proxy">Sync proxy server (optional)</label> <label>Sync proxy server (optional)</label>
<input class="form-control" id="sync-proxy" placeholder="https://<host>:<port>"> <input class="sync-proxy form-control" placeholder="https://<host>:<port>">
<p><strong>Note:</strong> If you leave the proxy setting blank, the system proxy will be used (applies to desktop/electron build only)</p> <p><strong>Note:</strong> If you leave the proxy setting blank, the system proxy will be used (applies to desktop/electron build only)</p>
</div> </div>
@ -37,7 +37,7 @@ const TPL = `
<p>This will test the connection and handshake to the sync server. If the sync server isn't initialized, this will set it up to sync with the local document.</p> <p>This will test the connection and handshake to the sync server. If the sync server isn't initialized, this will set it up to sync with the local document.</p>
<button id="test-sync-button" class="btn">Test sync</button> <button class="test-sync-button btn">Test sync</button>
</div>`; </div>`;
export default class SyncOptions extends OptionsWidget { export default class SyncOptions extends OptionsWidget {
@ -46,11 +46,11 @@ export default class SyncOptions extends OptionsWidget {
lazyRender() { lazyRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$form = this.$widget.find("#sync-setup-form"); this.$form = this.$widget.find(".sync-setup-form");
this.$syncServerHost = this.$widget.find("#sync-server-host"); this.$syncServerHost = this.$widget.find(".sync-server-host");
this.$syncServerTimeout = this.$widget.find("#sync-server-timeout"); this.$syncServerTimeout = this.$widget.find(".sync-server-timeout");
this.$syncProxy = this.$widget.find("#sync-proxy"); this.$syncProxy = this.$widget.find(".sync-proxy");
this.$testSyncButton = this.$widget.find("#test-sync-button"); this.$testSyncButton = this.$widget.find(".test-sync-button");
this.$form.on('submit', () => this.save()); this.$form.on('submit', () => this.save());

View File

@ -0,0 +1,40 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Heading style</h4>
<select class="heading-style form-control">
<option value="plain">Plain</option>
<option value="underline">Underline</option>
<option value="markdown">Markdown-style</option>
</select>
</div>`;
export default class HeadingStyleOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$body = $("body");
this.$headingStyle = this.$widget.find(".heading-style");
this.$headingStyle.on('change', () => {
const newHeadingStyle = this.$headingStyle.val();
this.toggleBodyClass("heading-style-", newHeadingStyle);
this.updateOption('headingStyle', newHeadingStyle);
});
}
async optionsLoaded(options) {
this.$headingStyle.val(options.headingStyle);
}
toggleBodyClass(prefix, value) {
for (const clazz of Array.from(this.$body[0].classList)) { // create copy to safely iterate over while removing classes
if (clazz.startsWith(prefix)) {
this.$body.removeClass(clazz);
}
}
this.$body.addClass(prefix + value);
}
}

View File

@ -0,0 +1,27 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Table of contents</h4>
Table of contents will appear in text notes when the note has more than a defined number of headings. You can customize this number:
<div class="form-group">
<input type="number" class="min-toc-headings form-control" min="0" max="9999999999999999" step="1" style="text-align: right;"/>
</div>
<p>You can also use this option to effectively disable TOC by setting a very high number.</p>
</div>`;
export default class TableOfContentsOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$minTocHeadings = this.$widget.find(".min-toc-headings");
this.$minTocHeadings.on('change', () =>
this.updateOption('minTocHeadings', this.$minTocHeadings.val()));
}
async optionsLoaded(options) {
this.$minTocHeadings.val(options.minTocHeadings);
}
}

View File

@ -0,0 +1,26 @@
import OptionsWidget from "../options_widget.js";
const TPL = `
<div class="options-section">
<h4>Automatic read-only size</h4>
<p>Automatic read-only note size is the size after which notes will be displayed in a read-only mode (for performance reasons).</p>
<div class="form-group">
<label>Automatic read-only size (text notes)</label>
<input class="auto-readonly-size-text form-control" type="number" min="0" style="text-align: right;">
</div>
</div>`;
export default class TextAutoReadOnlySizeOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$autoReadonlySizeText = this.$widget.find(".auto-readonly-size-text");
this.$autoReadonlySizeText.on('change', () =>
this.updateOption('autoReadonlySizeText', this.$autoReadonlySizeText.val()));
}
async optionsLoaded(options) {
this.$autoReadonlySizeText.val(options.autoReadonlySizeText);
}
}