diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index b5ad30003..e0b8c651b 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -1,40 +1,41 @@ -import froca from "../services/froca.js"; -import RootCommandExecutor from "./root_command_executor.js"; -import Entrypoints from "./entrypoints.js"; -import options from "../services/options.js"; -import utils, { hasTouchBar } from "../services/utils.js"; -import zoomComponent from "./zoom.js"; -import TabManager from "./tab_manager.js"; -import Component from "./component.js"; -import keyboardActionsService from "../services/keyboard_actions.js"; -import linkService, { type ViewScope } from "../services/link.js"; -import MobileScreenSwitcherExecutor, { type Screen } from "./mobile_screen_switcher.js"; -import MainTreeExecutors from "./main_tree_executors.js"; -import toast from "../services/toast.js"; -import ShortcutComponent from "./shortcut_component.js"; -import { t, initLocale } from "../services/i18n.js"; -import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js"; -import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js"; -import type { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js"; -import type LoadResults from "../services/load_results.js"; -import type { Attribute } from "../services/attribute_parser.js"; -import type NoteTreeWidget from "../widgets/note_tree.js"; -import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js"; -import type { NativeImage, TouchBar } from "electron"; -import TouchBarComponent from "./touch_bar.js"; import type { CKTextEditor } from "@triliumnext/ckeditor5"; import type CodeMirror from "@triliumnext/codemirror"; -import { StartupChecks } from "./startup_checks.js"; -import type { CreateNoteOpts } from "../services/note_create.js"; -import { ColumnComponent } from "tabulator-tables"; -import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx"; -import type RootContainer from "../widgets/containers/root_container.js"; import { SqlExecuteResults } from "@triliumnext/commons"; -import { AddLinkOpts } from "../widgets/dialogs/add_link.jsx"; -import { IncludeNoteOpts } from "../widgets/dialogs/include_note.jsx"; +import type { NativeImage, TouchBar } from "electron"; +import { ColumnComponent } from "tabulator-tables"; + +import type { Attribute } from "../services/attribute_parser.js"; +import froca from "../services/froca.js"; +import { initLocale,t } from "../services/i18n.js"; +import keyboardActionsService from "../services/keyboard_actions.js"; +import linkService, { type ViewScope } from "../services/link.js"; +import type LoadResults from "../services/load_results.js"; +import type { CreateNoteOpts } from "../services/note_create.js"; +import options from "../services/options.js"; +import toast from "../services/toast.js"; +import utils, { hasTouchBar } from "../services/utils.js"; import { ReactWrappedWidget } from "../widgets/basic_widget.js"; -import type { MarkdownImportOpts } from "../widgets/dialogs/markdown_import.jsx"; +import type RootContainer from "../widgets/containers/root_container.js"; +import { AddLinkOpts } from "../widgets/dialogs/add_link.jsx"; +import type { ConfirmWithMessageOptions, ConfirmWithTitleOptions } from "../widgets/dialogs/confirm.js"; +import type { ResolveOptions } from "../widgets/dialogs/delete_notes.js"; +import { IncludeNoteOpts } from "../widgets/dialogs/include_note.jsx"; import type { InfoProps } from "../widgets/dialogs/info.jsx"; +import type { MarkdownImportOpts } from "../widgets/dialogs/markdown_import.jsx"; +import { ChooseNoteTypeCallback } from "../widgets/dialogs/note_type_chooser.jsx"; +import type { PromptDialogOptions } from "../widgets/dialogs/prompt.js"; +import type NoteTreeWidget from "../widgets/note_tree.js"; +import Component from "./component.js"; +import Entrypoints from "./entrypoints.js"; +import MainTreeExecutors from "./main_tree_executors.js"; +import MobileScreenSwitcherExecutor, { type Screen } from "./mobile_screen_switcher.js"; +import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js"; +import RootCommandExecutor from "./root_command_executor.js"; +import ShortcutComponent from "./shortcut_component.js"; +import { StartupChecks } from "./startup_checks.js"; +import TabManager from "./tab_manager.js"; +import TouchBarComponent from "./touch_bar.js"; +import zoomComponent from "./zoom.js"; interface Layout { getRootWidget: (appContext: AppContext) => RootContainer; @@ -447,6 +448,7 @@ type EventMappings = { }; searchRefreshed: { ntxId?: string | null }; textEditorRefreshed: { ntxId?: string | null, editor: CKTextEditor }; + contentElRefreshed: { ntxId?: string | null, contentEl: HTMLElement }; hoistedNoteChanged: { noteId: string; ntxId: string | null; @@ -695,10 +697,8 @@ $(window).on("beforeunload", () => { console.log(`Component ${component.componentId} is not finished saving its state.`); allSaved = false; } - } else { - if (!listener()) { - allSaved = false; - } + } else if (!listener()) { + allSaved = false; } } @@ -708,7 +708,7 @@ $(window).on("beforeunload", () => { } }); -$(window).on("hashchange", function () { +$(window).on("hashchange", () => { const { notePath, ntxId, viewScope, searchString } = linkService.parseNavigationStateFromUrl(window.location.href); if (notePath || ntxId) { diff --git a/apps/client/src/components/note_context.ts b/apps/client/src/components/note_context.ts index 15791c741..5295007a7 100644 --- a/apps/client/src/components/note_context.ts +++ b/apps/client/src/components/note_context.ts @@ -390,7 +390,7 @@ class NoteContext extends Component implements EventListener<"entitiesReloaded"> * If no content could be determined `null` is returned instead. */ async getContentElement() { - return this.timeout>( + return this.timeout | null>( new Promise((resolve) => appContext.triggerCommand("executeWithContentElement", { resolve, diff --git a/apps/client/src/layouts/desktop_layout.tsx b/apps/client/src/layouts/desktop_layout.tsx index 7a6cfc3c1..758b2e9c9 100644 --- a/apps/client/src/layouts/desktop_layout.tsx +++ b/apps/client/src/layouts/desktop_layout.tsx @@ -9,6 +9,7 @@ import CreatePaneButton from "../widgets/buttons/create_pane_button.js"; import GlobalMenu from "../widgets/buttons/global_menu.jsx"; import LeftPaneToggle from "../widgets/buttons/left_pane_toggle.js"; import MovePaneButton from "../widgets/buttons/move_pane_button.js"; +import RightPaneToggle from "../widgets/buttons/right_pane_toggle.jsx"; import CloseZenModeButton from "../widgets/close_zen_button.jsx"; import NoteList from "../widgets/collections/NoteList.jsx"; import ContentHeader from "../widgets/containers/content_header.js"; @@ -44,6 +45,7 @@ import Ribbon from "../widgets/ribbon/Ribbon.jsx"; import ScrollPadding from "../widgets/scroll_padding.js"; import SearchResult from "../widgets/search_result.jsx"; import SharedInfo from "../widgets/shared_info.jsx"; +import RightPanelContainer from "../widgets/sidebar/RightPanelContainer.jsx"; import SqlResults from "../widgets/sql_result.js"; import SqlTableSchemas from "../widgets/sql_table_schemas.js"; import TabRowWidget from "../widgets/tab_row.js"; @@ -90,6 +92,7 @@ export default class DesktopLayout { .optChild(launcherPaneIsHorizontal, ) .child() .child(new TabRowWidget().class("full-width")) + .optChild(launcherPaneIsHorizontal && isNewLayout, ) .optChild(customTitleBarButtons, ) .css("height", "40px") .css("background-color", "var(--launcher-pane-background-color)") @@ -113,10 +116,14 @@ export default class DesktopLayout { .css("flex-grow", "1") .optChild(!fullWidthTabBar, new FlexContainer("row") + .class("tab-row-container") .child() .child(new TabRowWidget()) + .optChild(isNewLayout, ) .optChild(customTitleBarButtons, ) - .css("height", "40px")) + .css("height", "40px") + .css("align-items", "center") + ) .optChild(isNewLayout, ) .child( new FlexContainer("row") @@ -145,7 +152,7 @@ export default class DesktopLayout { .optChild(isNewLayout, )) .optChild(!isNewLayout, ) .child(new WatchedFileUpdateStatusWidget()) - .child() + .optChild(!isNewLayout, ) .child( new ScrollingContainer() .filling() @@ -165,21 +172,19 @@ export default class DesktopLayout { ) .child() .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("note-detail-pane")) ) ) .child(...this.customWidgets.get("center-pane")) ) - .child( + .optChild(!isNewLayout, new RightPaneContainer() .child(new TocWidget()) .child(new HighlightsListWidget()) .child(...this.customWidgets.get("right-pane")) ) + .optChild(isNewLayout, ) ) .optChild(!launcherPaneIsHorizontal && isNewLayout, ) ) diff --git a/apps/client/src/stylesheets/style.css b/apps/client/src/stylesheets/style.css index f10ace694..5ea6776ff 100644 --- a/apps/client/src/stylesheets/style.css +++ b/apps/client/src/stylesheets/style.css @@ -1961,7 +1961,7 @@ body.electron.platform-darwin:not(.native-titlebar):not(.full-screen) #tab-row-l width: 80px; } -.tab-row-widget { +.tab-row-container { padding-inline-end: calc(100vw - env(titlebar-area-width, 100vw)); } diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 0f1e12781..b45f26cb5 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1723,7 +1723,12 @@ }, "highlights_list_2": { "title": "Highlights List", - "options": "Options" + "title_with_count_one": "{{count}} highlight", + "title_with_count_other": "{{count}} highlights", + "options": "Options", + "modal_title": "Configure Highlights List", + "menu_configure": "Configure highlights list...", + "no_highlights": "No highlights found." }, "quick-search": { "placeholder": "Quick search", @@ -1794,7 +1799,8 @@ }, "toc": { "table_of_contents": "Table of Contents", - "options": "Options" + "options": "Options", + "no_headings": "No headings." }, "watched_file_update_status": { "file_last_modified": "File has been last modified on .", @@ -2196,5 +2202,11 @@ "note_paths_other": "{{count}} paths", "note_paths_title": "Note paths", "code_note_switcher": "Change language mode" + }, + "right_pane": { + "empty_message": "Nothing to show for this note", + "empty_button": "Hide the panel", + "toggle": "Toggle right panel", + "custom_widget_go_to_source": "Go to source code" } } diff --git a/apps/client/src/widgets/FloatingButtonsDefinitions.tsx b/apps/client/src/widgets/FloatingButtonsDefinitions.tsx index 4cd6e1c5f..35dbc92ae 100644 --- a/apps/client/src/widgets/FloatingButtonsDefinitions.tsx +++ b/apps/client/src/widgets/FloatingButtonsDefinitions.tsx @@ -78,10 +78,8 @@ export const POPUP_HIDDEN_FLOATING_BUTTONS: FloatingButtonsList = [ ToggleReadOnlyButton ]; -const isNewLayout = isExperimentalFeatureEnabled("new-layout"); - function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefaultViewMode }: FloatingButtonContext) { - const isEnabled = !isNewLayout && (note.noteId === "_backendLog" || note.type === "render") && isDefaultViewMode; + const isEnabled = (note.noteId === "_backendLog" || note.type === "render") && isDefaultViewMode; return isEnabled && (null); const isEnabled = ( - !isNewLayout - && ["mermaid", "canvas", "mindMap", "image"].includes(note?.type ?? "") + ["mermaid", "canvas", "mindMap", "image"].includes(note?.type ?? "") && note?.isContentAvailable() && isDefaultViewMode ); @@ -287,7 +284,7 @@ function CopyImageReferenceButton({ note, isDefaultViewMode }: FloatingButtonCon } function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingButtonContext) { - const isEnabled = !isNewLayout && ["mermaid", "mindMap"].includes(note?.type ?? "") + const isEnabled = ["mermaid", "mindMap"].includes(note?.type ?? "") && note?.isContentAvailable() && isDefaultViewMode; return isEnabled && ( <> @@ -308,7 +305,7 @@ function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingB function InAppHelpButton({ note }: FloatingButtonContext) { const helpUrl = getHelpUrlForNote(note); - const isEnabled = !!helpUrl && !isNewLayout; + const isEnabled = !!helpUrl; return isEnabled && ( 0; + const isEnabled = isDefaultViewMode && backlinkCount > 0; return (isEnabled &&
setRightPaneVisible(!rightPaneVisible)} + /> + ); +} diff --git a/apps/client/src/widgets/highlights_list.ts b/apps/client/src/widgets/highlights_list.ts index 5023facb9..b6e3f2e6f 100644 --- a/apps/client/src/widgets/highlights_list.ts +++ b/apps/client/src/widgets/highlights_list.ts @@ -5,14 +5,14 @@ * - For example, if there is a formula in the middle of the highlighted text, the two ends of the formula will be regarded as two entries */ -import { t } from "../services/i18n.js"; -import attributeService from "../services/attributes.js"; -import RightPanelWidget from "./right_panel_widget.js"; -import options from "../services/options.js"; -import OnClickButtonWidget from "./buttons/onclick_button.js"; import appContext, { type EventData } from "../components/app_context.js"; import type FNote from "../entities/fnote.js"; +import attributeService from "../services/attributes.js"; +import { t } from "../services/i18n.js"; import katex from "../services/math.js"; +import options from "../services/options.js"; +import OnClickButtonWidget from "./buttons/onclick_button.js"; +import RightPanelWidget from "./right_panel_widget.js"; const TPL = /*html*/`