diff --git a/src/public/app/components/component.js b/src/public/app/components/component.js index 0f614bcbf..490b70b35 100644 --- a/src/public/app/components/component.js +++ b/src/public/app/components/component.js @@ -88,8 +88,11 @@ export default class Component { if (fun) { return this.callMethod(fun, data); - } - else { + } else { + if (!this.parent) { + throw new Error(`Component "${this.componentId}" does not have a parent attached to propagate a command.`); + } + return this.parent.triggerCommand(name, data); } } diff --git a/src/public/app/layouts/desktop_layout.js b/src/public/app/layouts/desktop_layout.js index 34fe4454b..612f3bfad 100644 --- a/src/public/app/layouts/desktop_layout.js +++ b/src/public/app/layouts/desktop_layout.js @@ -83,6 +83,7 @@ import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js"; import CopyImageReferenceButton from "../widgets/floating_buttons/copy_image_reference_button.js"; import ScrollPaddingWidget from "../widgets/scroll_padding.js"; import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js"; +import options from "../services/options.js"; export default class DesktopLayout { constructor(customWidgets) { @@ -92,112 +93,120 @@ export default class DesktopLayout { getRootWidget(appContext) { appContext.noteTreeWidget = new NoteTreeWidget(); - return new RootContainer() + const launcherPaneIsHorizontal = (options.get("layoutOrientation") === "horizontal"); + const launcherPane = this.#buildLauncherPane(launcherPaneIsHorizontal); + + return new RootContainer(launcherPaneIsHorizontal) .setParent(appContext) - .child(new FlexContainer("column") - .id("launcher-pane") - .css("width", "53px") - .child(new GlobalMenuWidget()) - .child(new LauncherContainer()) - .child(new LeftPaneToggleWidget()) + .optChild(launcherPaneIsHorizontal, new FlexContainer('row') + .child(new TabRowWidget().class("full-width")) + .child(new TitleBarButtonsWidget()) + .css('height', '40px') + .css('background-color', 'var(--launcher-pane-background-color)') + .setParent(appContext) ) - .child(new LeftPaneContainer() - .child(new QuickSearchWidget()) - .child(appContext.noteTreeWidget) - .child(...this.customWidgets.get('left-pane')) - ) - .child(new FlexContainer('column') - .id('rest-pane') + .optChild(launcherPaneIsHorizontal, launcherPane) + .child(new FlexContainer('row') .css("flex-grow", "1") - .child(new FlexContainer('row') - .child(new TabRowWidget()) - .child(new TitleBarButtonsWidget()) - .css('height', '40px') + .optChild(!launcherPaneIsHorizontal, launcherPane) + .child(new LeftPaneContainer() + .optChild(!launcherPaneIsHorizontal, new QuickSearchWidget()) + .child(appContext.noteTreeWidget) + .child(...this.customWidgets.get('left-pane')) ) - .child(new FlexContainer('row') - .filling() - .collapsible() - .child(new FlexContainer('column') + .child(new FlexContainer('column') + .id('rest-pane') + .css("flex-grow", "1") + .optChild(!launcherPaneIsHorizontal, new FlexContainer('row') + .child(new TabRowWidget()) + .child(new TitleBarButtonsWidget()) + .css('height', '40px') + ) + .child(new FlexContainer('row') .filling() .collapsible() - .id('center-pane') - .child(new SplitNoteContainer(() => - new NoteWrapperWidget() - .child(new FlexContainer('row').class('title-row') - .css("height", "50px") - .css("min-height", "50px") - .css('align-items', "center") - .cssBlock('.title-row > * { margin: 5px; }') - .child(new NoteIconWidget()) - .child(new NoteTitleWidget()) - .child(new SpacerWidget(0, 1)) - .child(new MovePaneButton(true)) - .child(new MovePaneButton(false)) - .child(new ClosePaneButton()) - .child(new CreatePaneButton()) - ) - .child( - new RibbonContainer() - // the order of the widgets matter. Some of these want to "activate" themselves - // when visible. When this happens to multiple of them, the first one "wins". - // promoted attributes should always win. - .ribbon(new ClassicEditorToolbar()) - .ribbon(new PromotedAttributesWidget()) - .ribbon(new ScriptExecutorWidget()) - .ribbon(new SearchDefinitionWidget()) - .ribbon(new EditedNotesWidget()) - .ribbon(new BookPropertiesWidget()) - .ribbon(new NotePropertiesWidget()) - .ribbon(new FilePropertiesWidget()) - .ribbon(new ImagePropertiesWidget()) - .ribbon(new BasicPropertiesWidget()) - .ribbon(new OwnedAttributeListWidget()) - .ribbon(new InheritedAttributesWidget()) - .ribbon(new NotePathsWidget()) - .ribbon(new NoteMapRibbonWidget()) - .ribbon(new SimilarNotesWidget()) - .ribbon(new NoteInfoWidget()) - .button(new RevisionsButton()) - .button(new NoteActionsWidget()) - ) - .child(new SharedInfoWidget()) - .child(new WatchedFileUpdateStatusWidget()) - .child(new FloatingButtons() - .child(new EditButton()) - .child(new ShowTocWidgetButton()) - .child(new ShowHighlightsListWidgetButton()) - .child(new CodeButtonsWidget()) - .child(new RelationMapButtons()) - .child(new CopyImageReferenceButton()) - .child(new SvgExportButton()) - .child(new BacklinksWidget()) - .child(new HideFloatingButtonsButton()) - ) - .child(new MermaidWidget()) - .child( - new ScrollingContainer() - .filling() - .child(new SqlTableSchemasWidget()) - .child(new NoteDetailWidget()) - .child(new NoteListWidget()) - .child(new SearchResultWidget()) - .child(new SqlResultWidget()) - .child(new ScrollPaddingWidget()) - ) - .child(new ApiLogWidget()) - .child(new FindWidget()) - .child( - ...this.customWidgets.get('node-detail-pane'), // typo, let's keep it for a while as BC - ...this.customWidgets.get('note-detail-pane') - ) + .child(new FlexContainer('column') + .filling() + .collapsible() + .id('center-pane') + .child(new SplitNoteContainer(() => + new NoteWrapperWidget() + .child(new FlexContainer('row').class('title-row') + .css("height", "50px") + .css("min-height", "50px") + .css('align-items', "center") + .cssBlock('.title-row > * { margin: 5px; }') + .child(new NoteIconWidget()) + .child(new NoteTitleWidget()) + .child(new SpacerWidget(0, 1)) + .child(new MovePaneButton(true)) + .child(new MovePaneButton(false)) + .child(new ClosePaneButton()) + .child(new CreatePaneButton()) + ) + .child( + new RibbonContainer() + // the order of the widgets matter. Some of these want to "activate" themselves + // when visible. When this happens to multiple of them, the first one "wins". + // promoted attributes should always win. + .ribbon(new ClassicEditorToolbar()) + .ribbon(new PromotedAttributesWidget()) + .ribbon(new ScriptExecutorWidget()) + .ribbon(new SearchDefinitionWidget()) + .ribbon(new EditedNotesWidget()) + .ribbon(new BookPropertiesWidget()) + .ribbon(new NotePropertiesWidget()) + .ribbon(new FilePropertiesWidget()) + .ribbon(new ImagePropertiesWidget()) + .ribbon(new BasicPropertiesWidget()) + .ribbon(new OwnedAttributeListWidget()) + .ribbon(new InheritedAttributesWidget()) + .ribbon(new NotePathsWidget()) + .ribbon(new NoteMapRibbonWidget()) + .ribbon(new SimilarNotesWidget()) + .ribbon(new NoteInfoWidget()) + .button(new RevisionsButton()) + .button(new NoteActionsWidget()) + ) + .child(new SharedInfoWidget()) + .child(new WatchedFileUpdateStatusWidget()) + .child(new FloatingButtons() + .child(new EditButton()) + .child(new ShowTocWidgetButton()) + .child(new ShowHighlightsListWidgetButton()) + .child(new CodeButtonsWidget()) + .child(new RelationMapButtons()) + .child(new CopyImageReferenceButton()) + .child(new SvgExportButton()) + .child(new BacklinksWidget()) + .child(new HideFloatingButtonsButton()) + ) + .child(new MermaidWidget()) + .child( + new ScrollingContainer() + .filling() + .child(new SqlTableSchemasWidget()) + .child(new NoteDetailWidget()) + .child(new NoteListWidget()) + .child(new SearchResultWidget()) + .child(new SqlResultWidget()) + .child(new ScrollPaddingWidget()) + ) + .child(new ApiLogWidget()) + .child(new FindWidget()) + .child( + ...this.customWidgets.get('node-detail-pane'), // typo, let's keep it for a while as BC + ...this.customWidgets.get('note-detail-pane') + ) + ) ) + .child(...this.customWidgets.get('center-pane')) + ) + .child(new RightPaneContainer() + .child(new TocWidget()) + .child(new HighlightsListWidget()) + .child(...this.customWidgets.get('right-pane')) ) - .child(...this.customWidgets.get('center-pane')) - ) - .child(new RightPaneContainer() - .child(new TocWidget()) - .child(new HighlightsListWidget()) - .child(...this.customWidgets.get('right-pane')) ) ) ) @@ -225,4 +234,27 @@ export default class DesktopLayout { .child(new ConfirmDialog()) .child(new PromptDialog()); } + + #buildLauncherPane(isHorizontal) { + let launcherPane; + + if (isHorizontal) { + launcherPane = new FlexContainer("row") + .css("height", "53px") + .class("horizontal") + .child(new LeftPaneToggleWidget(true)) + .child(new LauncherContainer(true)) + .child(new GlobalMenuWidget(true)) + } else { + launcherPane = new FlexContainer("column") + .css("width", "53px") + .class("vertical") + .child(new GlobalMenuWidget(false)) + .child(new LauncherContainer(false)) + .child(new LeftPaneToggleWidget(false)); + } + + launcherPane.id("launcher-pane"); + return launcherPane; + } } diff --git a/src/public/app/layouts/mobile_layout.js b/src/public/app/layouts/mobile_layout.js index dfe5a91f5..578e2d50c 100644 --- a/src/public/app/layouts/mobile_layout.js +++ b/src/public/app/layouts/mobile_layout.js @@ -24,6 +24,7 @@ import RootContainer from "../widgets/containers/root_container.js"; import SharedInfoWidget from "../widgets/shared_info.js"; import PromotedAttributesWidget from "../widgets/ribbon_widgets/promoted_attributes.js"; import ClassicEditorToolbar from "../widgets/ribbon_widgets/classic_editor_toolbar.js"; +import options from "../services/options.js"; const MOBILE_CSS = ` - - @@ -25,6 +19,10 @@ export default class RightDropdownButtonWidget extends BasicWidget { this.iconClass = iconClass; this.title = title; this.dropdownTpl = dropdownTpl; + + this.settings = { + titlePlacement: "right" + }; } doRender() { @@ -33,7 +31,10 @@ export default class RightDropdownButtonWidget extends BasicWidget { this.dropdown = bootstrap.Dropdown.getOrCreateInstance(this.$widget.find("[data-bs-toggle='dropdown']")); this.$tooltip = this.$widget.find(".tooltip-trigger").attr("title", this.title); - this.tooltip = new bootstrap.Tooltip(this.$tooltip); + this.tooltip = new bootstrap.Tooltip(this.$tooltip, { + placement: this.settings.titlePlacement, + fallbackPlacements: [ this.settings.titlePlacement ] + }); this.$widget.find(".right-dropdown-button") .addClass(this.iconClass) diff --git a/src/public/app/widgets/containers/launcher.js b/src/public/app/widgets/containers/launcher.js index a4697c76f..3751cbfce 100644 --- a/src/public/app/widgets/containers/launcher.js +++ b/src/public/app/widgets/containers/launcher.js @@ -10,12 +10,14 @@ import CommandButtonWidget from "../buttons/command_button.js"; import utils from "../../services/utils.js"; import TodayLauncher from "../buttons/launcher/today_launcher.js"; import HistoryNavigationButton from "../buttons/history_navigation.js"; +import QuickSearchLauncherWidget from "../quick_search_launcher.js"; export default class LauncherWidget extends BasicWidget { - constructor() { + constructor(isHorizontalLayout) { super(); this.innerWidget = null; + this.isHorizontalLayout = isHorizontalLayout; } isEnabled() { @@ -63,6 +65,9 @@ export default class LauncherWidget extends BasicWidget { } this.child(this.innerWidget); + if (this.isHorizontalLayout && this.innerWidget.settings) { + this.innerWidget.settings.titlePlacement = "bottom"; + } return true; } @@ -86,29 +91,31 @@ export default class LauncherWidget extends BasicWidget { initBuiltinWidget(note) { const builtinWidget = note.getLabelValue("builtinWidget"); - - if (builtinWidget === 'calendar') { - return new CalendarWidget(note.title, note.getIcon()); - } else if (builtinWidget === 'spacer') { - // || has to be inside since 0 is a valid value - const baseSize = parseInt(note.getLabelValue("baseSize") || "40"); - const growthFactor = parseInt(note.getLabelValue("growthFactor") || "100"); - - return new SpacerWidget(baseSize, growthFactor); - } else if (builtinWidget === 'bookmarks') { - return new BookmarkButtons(); - } else if (builtinWidget === 'protectedSession') { - return new ProtectedSessionStatusWidget(); - } else if (builtinWidget === 'syncStatus') { - return new SyncStatusWidget(); - } else if (builtinWidget === 'backInHistoryButton') { - return new HistoryNavigationButton(note, "backInNoteHistory"); - } else if (builtinWidget === 'forwardInHistoryButton') { - return new HistoryNavigationButton(note, "forwardInNoteHistory"); - } else if (builtinWidget === 'todayInJournal') { - return new TodayLauncher(note); - } else { - throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); + switch (builtinWidget) { + case "calendar": + return new CalendarWidget(note.title, note.getIcon()); + case "spacer": + // || has to be inside since 0 is a valid value + const baseSize = parseInt(note.getLabelValue("baseSize") || "40"); + const growthFactor = parseInt(note.getLabelValue("growthFactor") || "100"); + + return new SpacerWidget(baseSize, growthFactor); + case "bookmarks": + return new BookmarkButtons(); + case "protectedSession": + return new ProtectedSessionStatusWidget(); + case "syncStatus": + return new SyncStatusWidget(); + case "backInHistoryButton": + return new HistoryNavigationButton(note, "backInNoteHistory"); + case "forwardInHistoryButton": + return new HistoryNavigationButton(note, "forwardInNoteHistory"); + case "todayInJournal": + return new TodayLauncher(note); + case "quickSearch": + return new QuickSearchLauncherWidget(this.isHorizontalLayout); + default: + throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); } } } diff --git a/src/public/app/widgets/containers/launcher_container.js b/src/public/app/widgets/containers/launcher_container.js index c5fb7d27d..e20969da4 100644 --- a/src/public/app/widgets/containers/launcher_container.js +++ b/src/public/app/widgets/containers/launcher_container.js @@ -4,12 +4,13 @@ import appContext from "../../components/app_context.js"; import LauncherWidget from "./launcher.js"; export default class LauncherContainer extends FlexContainer { - constructor() { - super('column'); + constructor(isHorizontalLayout) { + super(isHorizontalLayout ? "row" : "column"); this.id('launcher-container'); - this.css('height', '100%'); + this.css(isHorizontalLayout ? "width" : 'height', '100%'); this.filling(); + this.isHorizontalLayout = isHorizontalLayout; this.load(); } @@ -29,7 +30,7 @@ export default class LauncherContainer extends FlexContainer { for (const launcherNote of await visibleLaunchersRoot.getChildNotes()) { try { - const launcherWidget = new LauncherWidget(); + const launcherWidget = new LauncherWidget(this.isHorizontalLayout); const success = await launcherWidget.initLauncher(launcherNote); if (success) { diff --git a/src/public/app/widgets/containers/root_container.js b/src/public/app/widgets/containers/root_container.js index e17967756..23dcf1ac2 100644 --- a/src/public/app/widgets/containers/root_container.js +++ b/src/public/app/widgets/containers/root_container.js @@ -1,8 +1,8 @@ import FlexContainer from "./flex_container.js"; export default class RootContainer extends FlexContainer { - constructor() { - super('row'); + constructor(isHorizontalLayout) { + super(isHorizontalLayout ? "column" : "row"); this.id('root-widget'); this.css('height', '100%'); diff --git a/src/public/app/widgets/mobile_widgets/mobile_detail_menu.js b/src/public/app/widgets/mobile_widgets/mobile_detail_menu.js index 65386ce1d..7ad1592e4 100644 --- a/src/public/app/widgets/mobile_widgets/mobile_detail_menu.js +++ b/src/public/app/widgets/mobile_widgets/mobile_detail_menu.js @@ -6,12 +6,20 @@ import branchService from "../../services/branches.js"; import treeService from "../../services/tree.js"; import { t } from "../../services/i18n.js"; -const TPL = ``; +const TPL = ``; class MobileDetailMenuWidget extends BasicWidget { + + constructor(isHorizontalLayout) { + super(); + this.isHorizontalLayout = isHorizontalLayout; + } + doRender() { this.$widget = $(TPL); + this.$widget.addClass(this.isHorizontalLayout ? "bx-dots-vertical-rounded" : "bx-menu"); + this.$widget.on("click", async e => { const note = appContext.tabManager.getActiveContextNote(); diff --git a/src/public/app/widgets/quick_search_launcher.js b/src/public/app/widgets/quick_search_launcher.js new file mode 100644 index 000000000..a173bca24 --- /dev/null +++ b/src/public/app/widgets/quick_search_launcher.js @@ -0,0 +1,33 @@ +import utils from "../services/utils.js"; +import QuickSearchWidget from "./quick_search.js"; + +/** + * Similar to the {@link QuickSearchWidget} but meant to be included inside the launcher bar. + * + *
+ * Adds specific tweaks such as: + * + * - Hiding the widget on mobile. + */ +export default class QuickSearchLauncherWidget extends QuickSearchWidget { + + constructor(isHorizontalLayout) { + super(); + this.isHorizontalLayout = isHorizontalLayout; + } + + isEnabled() { + if (!this.isHorizontalLayout) { + // The quick search widget is added somewhere else on the vertical layout. + return false; + } + + if (utils.isMobile()) { + // The widget takes too much spaces to be included in the mobile layout. + return false; + } + + return super.isEnabled(); + } + +} \ No newline at end of file diff --git a/src/public/app/widgets/tab_row.js b/src/public/app/widgets/tab_row.js index 3437660ee..6ac6e177f 100644 --- a/src/public/app/widgets/tab_row.js +++ b/src/public/app/widgets/tab_row.js @@ -55,6 +55,10 @@ const TAB_ROW_TPL = ` background: var(--main-background-color); overflow: hidden; } + + .tab-row-widget.full-width { + background: var(--launcher-pane-background-color); + } .tab-row-widget * { box-sizing: inherit; diff --git a/src/public/app/widgets/type_widgets/options/appearance/theme.js b/src/public/app/widgets/type_widgets/options/appearance/theme.js index 5948b373f..7efedd523 100644 --- a/src/public/app/widgets/type_widgets/options/appearance/theme.js +++ b/src/public/app/widgets/type_widgets/options/appearance/theme.js @@ -5,6 +5,26 @@ import { t } from "../../../../services/i18n.js"; const TPL = `
+ `; export default class ThemeOptions extends OptionsWidget { @@ -27,6 +47,11 @@ export default class ThemeOptions extends OptionsWidget { this.$widget = $(TPL); this.$themeSelect = this.$widget.find(".theme-select"); this.$overrideThemeFonts = this.$widget.find(".override-theme-fonts"); + this.$layoutOrientation = this.$widget.find(`input[name="layout-orientation"]`).on("change", async () => { + const newLayoutOrientation = this.$widget.find(`input[name="layout-orientation"]:checked`).val(); + await this.updateOption("layoutOrientation", newLayoutOrientation); + utils.reloadFrontendApp("layout orientation change"); + }); this.$themeSelect.on('change', async () => { const newTheme = this.$themeSelect.val(); @@ -57,5 +82,8 @@ export default class ThemeOptions extends OptionsWidget { this.$themeSelect.val(options.theme); this.setCheckboxState(this.$overrideThemeFonts, options.overrideThemeFonts); + + this.$widget.find(`input[name="layout-orientation"][value="${options.layoutOrientation}"]`) + .prop("checked", "true"); } } diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index b543d2cca..55c6f7f2d 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -40,6 +40,10 @@ body { font-size: var(--main-font-size); } +body.mobile .desktop-only { + display: none !important; +} + a { text-decoration: none; } @@ -1022,6 +1026,18 @@ li.dropdown-submenu:hover > ul.dropdown-menu { overflow: auto; } +body:not(.mobile) #launcher-pane.horizontal .dropdown-submenu > .dropdown-menu { + left: calc(-100% + 10px); +} + +#launcher-pane.horizontal .right-dropdown-widget { + width: 53px; +} + +#launcher-pane.vertical .right-dropdown-widget { + height: 53px; +} + /* rotate caret on hover */ .dropdown-menu > li > a:hover:after { text-decoration: underline; @@ -1123,9 +1139,21 @@ li.dropdown-submenu:hover > ul.dropdown-menu { cursor: pointer; border: none; color: var(--launcher-pane-text-color); - background-color: var(--launcher-pane-background-color); - height: 53px; + background-color: var(--launcher-pane-background-color); +} + +#launcher-pane.vertical .launcher-button { width: 100%; + height: 53px; +} + +#launcher-pane.horizontal .launcher-button { + width: 53px; + height: 100%; +} + +#launcher-pane.horizontal .quick-search { + width: 350px; } #launcher-pane .icon-action:hover { diff --git a/src/public/translations/en/translation.json b/src/public/translations/en/translation.json index 9bfc970be..79e1153df 100644 --- a/src/public/translations/en/translation.json +++ b/src/public/translations/en/translation.json @@ -1061,7 +1061,12 @@ "theme_label": "Theme", "override_theme_fonts_label": "Override theme fonts", "light_theme": "Light", - "dark_theme": "Dark" + "dark_theme": "Dark", + "layout": "Layout", + "layout-vertical-title": "Vertical", + "layout-horizontal-title": "Horizontal", + "layout-vertical-description": "launcher bar is on the left (default)", + "layout-horizontal-description": "launcher bar is underneath the tab bar, the tab bar is now full width." }, "zoom_factor": { "title": "Zoom Factor (desktop build only)", diff --git a/src/routes/api/options.ts b/src/routes/api/options.ts index ecfe2dcbb..9649aff2b 100644 --- a/src/routes/api/options.ts +++ b/src/routes/api/options.ts @@ -66,7 +66,8 @@ const ALLOWED_OPTIONS = new Set([ 'editedNotesOpenInRibbon', 'locale', 'firstDayOfWeek', - 'textNoteEditorType' + 'textNoteEditorType', + 'layoutOrientation' ]); function getOptions() { diff --git a/src/services/hidden_subtree.ts b/src/services/hidden_subtree.ts index 558362d5e..b73dbf0ff 100644 --- a/src/services/hidden_subtree.ts +++ b/src/services/hidden_subtree.ts @@ -34,7 +34,7 @@ interface Item { baseSize?: string; growthFactor?: string; targetNoteId?: "_backendLog" | "_globalNoteMap"; - builtinWidget?: "bookmarks" | "spacer" | "backInHistoryButton" | "forwardInHistoryButton" | "syncStatus" | "protectedSession" | "todayInJournal" | "calendar"; + builtinWidget?: "bookmarks" | "spacer" | "backInHistoryButton" | "forwardInHistoryButton" | "syncStatus" | "protectedSession" | "todayInJournal" | "calendar" | "quickSearch"; command?: keyof typeof Command; } @@ -240,6 +240,7 @@ const HIDDEN_SUBTREE_DEFINITION: Item = { { id: '_lbBookmarks', title: 'Bookmarks', type: 'launcher', builtinWidget: 'bookmarks', icon: 'bx bx-bookmark' }, { id: '_lbToday', title: "Open Today's Journal Note", type: 'launcher', builtinWidget: 'todayInJournal', icon: 'bx bx-calendar-star' }, { id: '_lbSpacer2', title: 'Spacer', type: 'launcher', builtinWidget: 'spacer', baseSize: "0", growthFactor: "1" }, + { id: '_lbQuickSearch', title: "Quick Search", type: "launcher", builtinWidget: "quickSearch", icon: "bx bx-rectangle" }, { id: '_lbProtectedSession', title: 'Protected Session', type: 'launcher', builtinWidget: 'protectedSession', icon: 'bx bx bx-shield-quarter' }, { id: '_lbSyncStatus', title: 'Sync Status', type: 'launcher', builtinWidget: 'syncStatus', icon: 'bx bx-wifi' }, { id: '_lbSettings', title: 'Settings', type: 'launcher', command: 'showOptions', icon: 'bx bx-cog' } diff --git a/src/services/options_init.ts b/src/services/options_init.ts index 1dc7602d2..2d4efec54 100644 --- a/src/services/options_init.ts +++ b/src/services/options_init.ts @@ -134,7 +134,9 @@ const defaultOptions: DefaultOption[] = [ { name: "codeBlockWordWrap", value: "false", isSynced: true }, // Text note configuration - { name: "textNoteEditorType", value: "ckeditor-balloon", isSynced: true } + { name: "textNoteEditorType", value: "ckeditor-balloon", isSynced: true }, + + { name: "layoutOrientation", value: "vertical", isSynced: false } ]; /**