diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index ce33d14470..354bd88403 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -327,6 +327,7 @@ export type CommandMappings = { exportAsPdf: CommandData; openNoteExternally: CommandData; openNoteCustom: CommandData; + openNoteOnServer: CommandData; renderActiveNote: CommandData; unhoist: CommandData; reloadFrontendApp: CommandData; diff --git a/apps/client/src/components/root_command_executor.ts b/apps/client/src/components/root_command_executor.ts index 632eb0a887..0bfdaeccc6 100644 --- a/apps/client/src/components/root_command_executor.ts +++ b/apps/client/src/components/root_command_executor.ts @@ -67,6 +67,13 @@ export default class RootCommandExecutor extends Component { } } + openNoteOnServerCommand() { + const noteId = appContext.tabManager.getActiveContextNoteId(); + if (noteId) { + openService.openNoteOnServer(noteId); + } + } + enterProtectedSessionCommand() { protectedSessionService.enterProtectedSession(); } diff --git a/apps/client/src/services/open.ts b/apps/client/src/services/open.ts index 0bcda805e8..eb93a205e3 100644 --- a/apps/client/src/services/open.ts +++ b/apps/client/src/services/open.ts @@ -1,5 +1,6 @@ import utils from "./utils.js"; import server from "./server.js"; +import options from "./options.js"; type ExecFunction = (command: string, cb: (err: string, stdout: string, stderror: string) => void) => void; @@ -171,6 +172,21 @@ function getHost() { return `${url.protocol}//${url.hostname}:${url.port}`; } +async function openNoteOnServer(noteId: string) { + // Get the sync server host from options + const syncServerHost = options.get("syncServerHost"); + + if (!syncServerHost) { + console.error("No sync server host configured"); + return; + } + + const url = new URL(`#root/${noteId}`, syncServerHost).toString(); + + // Use window.open to ensure link opens in external browser in Electron + window.open(url, '_blank', 'noopener,noreferrer'); +} + async function openDirectory(directory: string) { try { if (utils.isElectron()) { @@ -198,5 +214,6 @@ export default { openAttachmentExternally, openNoteCustom, openAttachmentCustom, + openNoteOnServer, openDirectory }; diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 1949d3357e..b14d0d8331 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -679,6 +679,7 @@ "open_note_externally": "Open note externally", "open_note_externally_title": "File will be open in an external application and watched for changes. You'll then be able to upload the modified version back to Trilium.", "open_note_custom": "Open note custom", + "open_note_on_server": "Open note on server", "import_files": "Import files", "export_note": "Export note", "delete_note": "Delete note", diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx index 1c1c17502c..41894789c7 100644 --- a/apps/client/src/widgets/ribbon/NoteActions.tsx +++ b/apps/client/src/widgets/ribbon/NoteActions.tsx @@ -1,4 +1,4 @@ -import { ConvertToAttachmentResponse } from "@triliumnext/commons"; +import { ConvertToAttachmentResponse, OptionNames } from "@triliumnext/commons"; import appContext, { CommandNames } from "../../components/app_context"; import FNote from "../../entities/fnote" import dialog from "../../services/dialog"; @@ -12,6 +12,7 @@ import { FormDropdownDivider, FormListItem } from "../react/FormList"; import { isElectron as getIsElectron, isMac as getIsMac } from "../../services/utils"; import { ParentComponent } from "../react/react_utils"; import { useContext } from "preact/hooks"; +import { useTriliumOption } from "../react/hooks"; import NoteContext from "../../components/note_context"; import branches from "../../services/branches"; @@ -52,6 +53,7 @@ function NoteContextMenu({ note, noteContext }: { note: FNote, noteContext?: Not const isMac = getIsMac(); const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap"].includes(note.type); const isSearchOrBook = ["search", "book"].includes(note.type); + const [ syncServerHost ] = useTriliumOption("syncServerHost"); return ( + {(syncServerHost && isElectron) && + + }