From 48772380159042e34e5b270ccb6f9808fd0f44f3 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 14 Dec 2025 22:56:39 +0200 Subject: [PATCH] refactor(layout): integrate copy image reference to button to all supported note types --- .../widgets/FloatingButtonsDefinitions.tsx | 62 ++++++++++--------- .../src/widgets/ribbon/NoteActionsCustom.tsx | 34 ++++++---- 2 files changed, 56 insertions(+), 40 deletions(-) diff --git a/apps/client/src/widgets/FloatingButtonsDefinitions.tsx b/apps/client/src/widgets/FloatingButtonsDefinitions.tsx index 171ea2fa8..5cbb6f10f 100644 --- a/apps/client/src/widgets/FloatingButtonsDefinitions.tsx +++ b/apps/client/src/widgets/FloatingButtonsDefinitions.tsx @@ -1,26 +1,27 @@ +import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons"; import { VNode } from "preact"; +import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks"; + import appContext, { EventData, EventNames } from "../components/app_context"; import Component from "../components/component"; import NoteContext from "../components/note_context"; import FNote from "../entities/fnote"; -import ActionButton, { ActionButtonProps } from "./react/ActionButton"; -import { useIsNoteReadOnly, useNoteLabelBoolean, useTriliumEvent, useTriliumOption, useWindowSize } from "./react/hooks"; -import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks"; -import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils"; -import server from "../services/server"; -import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons"; -import toast from "../services/toast"; +import attributes from "../services/attributes"; +import { isExperimentalFeatureEnabled } from "../services/experimental_features"; +import froca from "../services/froca"; import { t } from "../services/i18n"; import { copyImageReferenceToClipboard } from "../services/image"; -import tree from "../services/tree"; import { getHelpUrlForNote } from "../services/in_app_help"; -import froca from "../services/froca"; +import LoadResults from "../services/load_results"; +import server from "../services/server"; +import toast from "../services/toast"; +import tree from "../services/tree"; +import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils"; +import { ViewTypeOptions } from "./collections/interface"; +import ActionButton, { ActionButtonProps } from "./react/ActionButton"; +import { useIsNoteReadOnly, useNoteLabelBoolean, useTriliumEvent, useTriliumOption, useWindowSize } from "./react/hooks"; import NoteLink from "./react/NoteLink"; import RawHtml from "./react/RawHtml"; -import { ViewTypeOptions } from "./collections/interface"; -import attributes from "../services/attributes"; -import LoadResults from "../services/load_results"; -import { isExperimentalFeatureEnabled } from "../services/experimental_features"; export interface FloatingButtonContext { parentComponent: Component; @@ -38,7 +39,7 @@ function FloatingButton({ className, ...props }: ActionButtonProps) { className={`floating-button ${className ?? ""}`} noIconActionClass {...props} - /> + />; } export type FloatingButtonsList = ((context: FloatingButtonContext) => false | VNode)[]; @@ -85,7 +86,7 @@ function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefault text={t("backend_log.refresh")} icon="bx bx-refresh" onClick={() => parentComponent.triggerEvent("refreshData", { ntxId: noteContext.ntxId })} - /> + />; } function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: FloatingButtonContext) { @@ -97,7 +98,7 @@ function SwitchSplitOrientationButton({ note, isReadOnly, isDefaultViewMode }: F text={upcomingOrientation === "vertical" ? t("switch_layout_button.title_vertical") : t("switch_layout_button.title_horizontal")} icon={upcomingOrientation === "vertical" ? "bx bxs-dock-bottom" : "bx bxs-dock-left"} onClick={() => setSplitEditorOrientation(upcomingOrientation)} - /> + />; } function ToggleReadOnlyButton({ note, viewType, isDefaultViewMode }: FloatingButtonContext) { @@ -109,7 +110,7 @@ function ToggleReadOnlyButton({ note, viewType, isDefaultViewMode }: FloatingBut text={isReadOnly ? t("toggle_read_only_button.unlock-editing") : t("toggle_read_only_button.lock-editing")} icon={isReadOnly ? "bx bx-lock-open-alt" : "bx bx-lock-alt"} onClick={() => setReadOnly(!isReadOnly)} - /> + />; } function EditButton({ note, noteContext }: FloatingButtonContext) { @@ -132,7 +133,7 @@ function EditButton({ note, noteContext }: FloatingButtonContext) { icon="bx bx-pencil" className={animationClass} onClick={() => enableEditing()} - /> + />; } function ShowTocWidgetButton({ note, noteContext, isDefaultViewMode }: FloatingButtonContext) { @@ -150,7 +151,7 @@ function ShowTocWidgetButton({ note, noteContext, isDefaultViewMode }: FloatingB appContext.triggerEvent("showTocWidget", { noteId: noteContext.noteId }); } }} - /> + />; } function ShowHighlightsListWidgetButton({ note, noteContext, isDefaultViewMode }: FloatingButtonContext) { @@ -168,7 +169,7 @@ function ShowHighlightsListWidgetButton({ note, noteContext, isDefaultViewMode } appContext.triggerEvent("showHighlightsListWidget", { noteId: noteContext.noteId }); } }} - /> + />; } function RunActiveNoteButton({ note }: FloatingButtonContext) { @@ -177,7 +178,7 @@ function RunActiveNoteButton({ note }: FloatingButtonContext) { icon="bx bx-play" text={t("code_buttons.execute_button_title")} triggerCommand="runActiveNote" - /> + />; } function OpenTriliumApiDocsButton({ note }: FloatingButtonContext) { @@ -186,7 +187,7 @@ function OpenTriliumApiDocsButton({ note }: FloatingButtonContext) { icon="bx bx-help-circle" text={t("code_buttons.trilium_api_docs_button_title")} onClick={() => openInAppHelpFromUrl(note.mime.endsWith("frontend") ? "Q2z6av6JZVWm" : "MEtfsqa5VwNi")} - /> + />; } function SaveToNoteButton({ note }: FloatingButtonContext) { @@ -204,7 +205,7 @@ function SaveToNoteButton({ note }: FloatingButtonContext) { await appContext.tabManager.getActiveContext()?.setNote(notePath); } }} - /> + />; } function RelationMapButtons({ note, isDefaultViewMode, triggerEvent }: FloatingButtonContext) { @@ -237,7 +238,7 @@ function RelationMapButtons({ note, isDefaultViewMode, triggerEvent }: FloatingB /> - ) + ); } function GeoMapButtons({ triggerEvent, viewType, isReadOnly }: FloatingButtonContext) { @@ -253,8 +254,11 @@ function GeoMapButtons({ triggerEvent, viewType, isReadOnly }: FloatingButtonCon function CopyImageReferenceButton({ note, isDefaultViewMode }: FloatingButtonContext) { const hiddenImageCopyRef = useRef(null); - const isEnabled = ["mermaid", "canvas", "mindMap", "image"].includes(note?.type ?? "") - && note?.isContentAvailable() && isDefaultViewMode; + const isEnabled = ( + !isNewLayout + && ["mermaid", "canvas", "mindMap", "image"].includes(note?.type ?? "") + && note?.isContentAvailable() && isDefaultViewMode + ); return isEnabled && ( <> @@ -275,7 +279,7 @@ function CopyImageReferenceButton({ note, isDefaultViewMode }: FloatingButtonCon position: "absolute" // Take out of the the hidden image from flexbox to prevent the layout being affected }} /> - ) + ); } function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingButtonContext) { @@ -295,7 +299,7 @@ function ExportImageButtons({ note, triggerEvent, isDefaultViewMode }: FloatingB onClick={() => triggerEvent("exportPng")} /> - ) + ); } function InAppHelpButton({ note }: FloatingButtonContext) { @@ -308,7 +312,7 @@ function InAppHelpButton({ note }: FloatingButtonContext) { text={t("help-button.title")} onClick={() => helpUrl && openInAppHelpFromUrl(helpUrl)} /> - ) + ); } function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) { diff --git a/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx b/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx index 077094246..a023f48de 100644 --- a/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx +++ b/apps/client/src/widgets/ribbon/NoteActionsCustom.tsx @@ -1,3 +1,4 @@ +import { NoteType } from "@triliumnext/commons"; import { useContext } from "preact/hooks"; import FNote from "../../entities/fnote"; @@ -5,6 +6,7 @@ import { t } from "../../services/i18n"; import { downloadFileNote, openNoteExternally } from "../../services/open"; import ActionButton from "../react/ActionButton"; import { FormFileUploadActionButton } from "../react/FormFileUpload"; +import { useNoteProperty } from "../react/hooks"; import { ParentComponent } from "../react/react_utils"; import { buildUploadNewFileRevisionListener } from "./FilePropertiesTab"; import { buildUploadNewImageRevisionListener } from "./ImagePropertiesTab"; @@ -14,20 +16,31 @@ interface NoteActionsCustomProps { ntxId: string; } +interface NoteActionsCustomInnerProps extends NoteActionsCustomProps { + noteType: NoteType; +} + /** * Part of {@link NoteActions} on the new layout, but are rendered with a slight spacing * from the rest of the note items and the buttons differ based on the note type. */ export default function NoteActionsCustom(props: NoteActionsCustomProps) { - return ( + const noteType = useNoteProperty(props.note, "type"); + const innerProps: NoteActionsCustomInnerProps | undefined = noteType && { + ...props, + noteType + }; + + return (innerProps &&
- + +
); } //#region Note type mappings -function NoteActionsCustomInner(props: NoteActionsCustomProps) { +function NoteActionsCustomInner(props: NoteActionsCustomInnerProps) { switch (props.note.type) { case "file": return ; @@ -36,7 +49,7 @@ function NoteActionsCustomInner(props: NoteActionsCustomProps) { } } -function FileActions(props: NoteActionsCustomProps) { +function FileActions(props: NoteActionsCustomInnerProps) { return ( <> @@ -46,10 +59,9 @@ function FileActions(props: NoteActionsCustomProps) { ); } -function ImageActions(props: NoteActionsCustomProps) { +function ImageActions(props: NoteActionsCustomInnerProps) { return ( <> - @@ -59,7 +71,7 @@ function ImageActions(props: NoteActionsCustomProps) { //#endregion //#region Shared buttons -function UploadNewRevisionButton({ note, onChange }: NoteActionsCustomProps & { +function UploadNewRevisionButton({ note, onChange }: NoteActionsCustomInnerProps & { onChange: (files: FileList | null) => void; }) { return ( @@ -72,7 +84,7 @@ function UploadNewRevisionButton({ note, onChange }: NoteActionsCustomProps & { ); } -function OpenExternallyButton({ note }: NoteActionsCustomProps) { +function OpenExternallyButton({ note }: NoteActionsCustomInnerProps) { return (