mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
can't allow opening externally on attachment list
This commit is contained in:
parent
cc02546ed3
commit
8284c673f9
@ -76,6 +76,10 @@ class AppContext extends Component {
|
||||
$("body").append($renderedWidget);
|
||||
|
||||
$renderedWidget.on('click', "[data-trigger-command]", function() {
|
||||
if ($(this).hasClass("disabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commandName = $(this).attr('data-trigger-command');
|
||||
const $component = $(this).closest(".component");
|
||||
const component = $component.prop("component");
|
||||
|
@ -4,11 +4,9 @@ import noteTooltipService from './services/note_tooltip.js';
|
||||
import bundleService from "./services/bundle.js";
|
||||
import noteAutocompleteService from './services/note_autocomplete.js';
|
||||
import macInit from './services/mac_init.js';
|
||||
import contextMenu from "./menus/context_menu.js";
|
||||
import electronContextMenu from "./menus/electron_context_menu.js";
|
||||
import DesktopLayout from "./layouts/desktop_layout.js";
|
||||
import glob from "./services/glob.js";
|
||||
import zoomService from './components/zoom.js';
|
||||
import options from "./services/options.js";
|
||||
|
||||
bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
||||
appContext.setLayout(new DesktopLayout(widgetBundles));
|
||||
@ -18,9 +16,8 @@ bundleService.getWidgetBundlesByParent().then(widgetBundles => {
|
||||
glob.setupGlobs();
|
||||
|
||||
if (utils.isElectron()) {
|
||||
utils.dynamicRequire('electron').ipcRenderer.on('globalShortcut', async function(event, actionName) {
|
||||
appContext.triggerCommand(actionName);
|
||||
});
|
||||
utils.dynamicRequire('electron').ipcRenderer.on('globalShortcut',
|
||||
async (event, actionName) => appContext.triggerCommand(actionName));
|
||||
}
|
||||
|
||||
macInit.init();
|
||||
@ -30,131 +27,5 @@ noteTooltipService.setupGlobalTooltip();
|
||||
noteAutocompleteService.init();
|
||||
|
||||
if (utils.isElectron()) {
|
||||
const electron = utils.dynamicRequire('electron');
|
||||
|
||||
const remote = utils.dynamicRequire('@electron/remote');
|
||||
const {webContents} = remote.getCurrentWindow();
|
||||
|
||||
webContents.on('context-menu', (event, params) => {
|
||||
const {editFlags} = params;
|
||||
const hasText = params.selectionText.trim().length > 0;
|
||||
const isMac = process.platform === "darwin";
|
||||
const platformModifier = isMac ? 'Meta' : 'Ctrl';
|
||||
|
||||
const items = [];
|
||||
|
||||
if (params.misspelledWord) {
|
||||
for (const suggestion of params.dictionarySuggestions) {
|
||||
items.push({
|
||||
title: suggestion,
|
||||
command: "replaceMisspelling",
|
||||
spellingSuggestion: suggestion,
|
||||
uiIcon: "bx bx-empty"
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: `Add "${params.misspelledWord}" to dictionary`,
|
||||
uiIcon: "bx bx-plus",
|
||||
handler: () => webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord)
|
||||
});
|
||||
|
||||
items.push({ title: `----` });
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
items.push({
|
||||
enabled: editFlags.canCut && hasText,
|
||||
title: `Cut <kbd>${platformModifier}+X`,
|
||||
uiIcon: "bx bx-cut",
|
||||
handler: () => webContents.cut()
|
||||
});
|
||||
}
|
||||
|
||||
if (params.isEditable || hasText) {
|
||||
items.push({
|
||||
enabled: editFlags.canCopy && hasText,
|
||||
title: `Copy <kbd>${platformModifier}+C`,
|
||||
uiIcon: "bx bx-copy",
|
||||
handler: () => webContents.copy()
|
||||
});
|
||||
}
|
||||
|
||||
if (!["", "javascript:", "about:blank#blocked"].includes(params.linkURL) && params.mediaType === 'none') {
|
||||
items.push({
|
||||
title: `Copy link`,
|
||||
uiIcon: "bx bx-copy",
|
||||
handler: () => {
|
||||
electron.clipboard.write({
|
||||
bookmark: params.linkText,
|
||||
text: params.linkURL
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
items.push({
|
||||
enabled: editFlags.canPaste,
|
||||
title: `Paste <kbd>${platformModifier}+V`,
|
||||
uiIcon: "bx bx-paste",
|
||||
handler: () => webContents.paste()
|
||||
});
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
items.push({
|
||||
enabled: editFlags.canPaste,
|
||||
title: `Paste as plain text <kbd>${platformModifier}+Shift+V`,
|
||||
uiIcon: "bx bx-paste",
|
||||
handler: () => webContents.pasteAndMatchStyle()
|
||||
});
|
||||
}
|
||||
|
||||
if (hasText) {
|
||||
const shortenedSelection = params.selectionText.length > 15
|
||||
? (`${params.selectionText.substr(0, 13)}…`)
|
||||
: params.selectionText;
|
||||
|
||||
// Read the search engine from the options and fallback to DuckDuckGo if the option is not set.
|
||||
const customSearchEngineName = options.get("customSearchEngineName");
|
||||
const customSearchEngineUrl = options.get("customSearchEngineUrl");
|
||||
let searchEngineName;
|
||||
let searchEngineUrl;
|
||||
if (customSearchEngineName && customSearchEngineUrl) {
|
||||
searchEngineName = customSearchEngineName;
|
||||
searchEngineUrl = customSearchEngineUrl;
|
||||
} else {
|
||||
searchEngineName = "Duckduckgo";
|
||||
searchEngineUrl = "https://duckduckgo.com/?q={keyword}";
|
||||
}
|
||||
|
||||
// Replace the placeholder with the real search keyword.
|
||||
let searchUrl = searchEngineUrl.replace("{keyword}", encodeURIComponent(params.selectionText));
|
||||
|
||||
items.push({
|
||||
enabled: editFlags.canPaste,
|
||||
title: `Search for "${shortenedSelection}" with ${searchEngineName}`,
|
||||
uiIcon: "bx bx-search-alt",
|
||||
handler: () => electron.shell.openExternal(searchUrl)
|
||||
});
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const zoomLevel = zoomService.getCurrentZoom();
|
||||
|
||||
contextMenu.show({
|
||||
x: params.x / zoomLevel,
|
||||
y: params.y / zoomLevel,
|
||||
items,
|
||||
selectMenuItemHandler: ({command, spellingSuggestion}) => {
|
||||
if (command === 'replaceMisspelling') {
|
||||
webContents.insertText(spellingSuggestion);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
electronContextMenu.setupContextMenu();
|
||||
}
|
||||
|
138
src/public/app/menus/electron_context_menu.js
Normal file
138
src/public/app/menus/electron_context_menu.js
Normal file
@ -0,0 +1,138 @@
|
||||
import utils from "../services/utils.js";
|
||||
import options from "../services/options.js";
|
||||
import zoomService from "../components/zoom.js";
|
||||
import contextMenu from "./context_menu.js";
|
||||
|
||||
function setupContextMenu() {
|
||||
const electron = utils.dynamicRequire('electron');
|
||||
|
||||
const remote = utils.dynamicRequire('@electron/remote');
|
||||
const {webContents} = remote.getCurrentWindow();
|
||||
|
||||
webContents.on('context-menu', (event, params) => {
|
||||
const {editFlags} = params;
|
||||
const hasText = params.selectionText.trim().length > 0;
|
||||
const isMac = process.platform === "darwin";
|
||||
const platformModifier = isMac ? 'Meta' : 'Ctrl';
|
||||
|
||||
const items = [];
|
||||
|
||||
if (params.misspelledWord) {
|
||||
for (const suggestion of params.dictionarySuggestions) {
|
||||
items.push({
|
||||
title: suggestion,
|
||||
command: "replaceMisspelling",
|
||||
spellingSuggestion: suggestion,
|
||||
uiIcon: "bx bx-empty"
|
||||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
title: `Add "${params.misspelledWord}" to dictionary`,
|
||||
uiIcon: "bx bx-plus",
|
||||
handler: () => webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord)
|
||||
});
|
||||
|
||||
items.push({ title: `----` });
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
items.push({
|
||||
enabled: editFlags.canCut && hasText,
|
||||
title: `Cut <kbd>${platformModifier}+X`,
|
||||
uiIcon: "bx bx-cut",
|
||||
handler: () => webContents.cut()
|
||||
});
|
||||
}
|
||||
|
||||
if (params.isEditable || hasText) {
|
||||
items.push({
|
||||
enabled: editFlags.canCopy && hasText,
|
||||
title: `Copy <kbd>${platformModifier}+C`,
|
||||
uiIcon: "bx bx-copy",
|
||||
handler: () => webContents.copy()
|
||||
});
|
||||
}
|
||||
|
||||
if (!["", "javascript:", "about:blank#blocked"].includes(params.linkURL) && params.mediaType === 'none') {
|
||||
items.push({
|
||||
title: `Copy link`,
|
||||
uiIcon: "bx bx-copy",
|
||||
handler: () => {
|
||||
electron.clipboard.write({
|
||||
bookmark: params.linkText,
|
||||
text: params.linkURL
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
items.push({
|
||||
enabled: editFlags.canPaste,
|
||||
title: `Paste <kbd>${platformModifier}+V`,
|
||||
uiIcon: "bx bx-paste",
|
||||
handler: () => webContents.paste()
|
||||
});
|
||||
}
|
||||
|
||||
if (params.isEditable) {
|
||||
items.push({
|
||||
enabled: editFlags.canPaste,
|
||||
title: `Paste as plain text <kbd>${platformModifier}+Shift+V`,
|
||||
uiIcon: "bx bx-paste",
|
||||
handler: () => webContents.pasteAndMatchStyle()
|
||||
});
|
||||
}
|
||||
|
||||
if (hasText) {
|
||||
const shortenedSelection = params.selectionText.length > 15
|
||||
? (`${params.selectionText.substr(0, 13)}…`)
|
||||
: params.selectionText;
|
||||
|
||||
// Read the search engine from the options and fallback to DuckDuckGo if the option is not set.
|
||||
const customSearchEngineName = options.get("customSearchEngineName");
|
||||
const customSearchEngineUrl = options.get("customSearchEngineUrl");
|
||||
let searchEngineName;
|
||||
let searchEngineUrl;
|
||||
if (customSearchEngineName && customSearchEngineUrl) {
|
||||
searchEngineName = customSearchEngineName;
|
||||
searchEngineUrl = customSearchEngineUrl;
|
||||
} else {
|
||||
searchEngineName = "Duckduckgo";
|
||||
searchEngineUrl = "https://duckduckgo.com/?q={keyword}";
|
||||
}
|
||||
|
||||
// Replace the placeholder with the real search keyword.
|
||||
let searchUrl = searchEngineUrl.replace("{keyword}", encodeURIComponent(params.selectionText));
|
||||
|
||||
items.push({
|
||||
enabled: editFlags.canPaste,
|
||||
title: `Search for "${shortenedSelection}" with ${searchEngineName}`,
|
||||
uiIcon: "bx bx-search-alt",
|
||||
handler: () => electron.shell.openExternal(searchUrl)
|
||||
});
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const zoomLevel = zoomService.getCurrentZoom();
|
||||
|
||||
contextMenu.show({
|
||||
x: params.x / zoomLevel,
|
||||
y: params.y / zoomLevel,
|
||||
items,
|
||||
selectMenuItemHandler: ({command, spellingSuggestion}) => {
|
||||
if (command === 'replaceMisspelling') {
|
||||
webContents.insertText(spellingSuggestion);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
setupContextMenu
|
||||
};
|
@ -27,7 +27,7 @@ async function mouseEnterHandler() {
|
||||
return;
|
||||
}
|
||||
|
||||
// this is to avoid showing tooltip from inside CKEditor link editor dialog
|
||||
// this is to avoid showing tooltip from inside the CKEditor link editor dialog
|
||||
if ($link.closest(".ck-link-actions").length) {
|
||||
return;
|
||||
}
|
||||
|
@ -67,13 +67,13 @@ const TPL = `
|
||||
</div>`;
|
||||
|
||||
export default class AttachmentDetailWidget extends BasicWidget {
|
||||
constructor(attachment) {
|
||||
constructor(attachment, isFullDetail) {
|
||||
super();
|
||||
|
||||
this.contentSized();
|
||||
this.attachment = attachment;
|
||||
this.attachmentActionsWidget = new AttachmentActionsWidget(attachment);
|
||||
this.isFullDetail = true;
|
||||
this.attachmentActionsWidget = new AttachmentActionsWidget(attachment, isFullDetail);
|
||||
this.isFullDetail = isFullDetail;
|
||||
this.child(this.attachmentActionsWidget);
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,8 @@ const TPL = `
|
||||
aria-expanded="false" class="icon-action icon-action-always-border bx bx-dots-vertical-rounded"></button>
|
||||
|
||||
<div class="dropdown-menu dropdown-menu-right">
|
||||
<a data-trigger-command="openAttachment" class="dropdown-item">Open</a>
|
||||
<a data-trigger-command="openAttachmentExternally" class="dropdown-item"
|
||||
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 externally</a>
|
||||
<a data-trigger-command="openAttachment" class="dropdown-item"
|
||||
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 externally</a>
|
||||
<a data-trigger-command="downloadAttachment" class="dropdown-item">Download</a>
|
||||
<a data-trigger-command="uploadNewAttachmentRevision" class="dropdown-item">Upload new revision</a>
|
||||
<a data-trigger-command="copyAttachmentReferenceToClipboard" class="dropdown-item">Copy reference to clipboard</a>
|
||||
@ -44,10 +42,11 @@ const TPL = `
|
||||
</div>`;
|
||||
|
||||
export default class AttachmentActionsWidget extends BasicWidget {
|
||||
constructor(attachment) {
|
||||
constructor(attachment, isFullDetail) {
|
||||
super();
|
||||
|
||||
this.attachment = attachment;
|
||||
this.isFullDetail = isFullDetail;
|
||||
}
|
||||
|
||||
get attachmentId() {
|
||||
@ -83,6 +82,17 @@ export default class AttachmentActionsWidget extends BasicWidget {
|
||||
toastService.showError("Upload of a new attachment revision failed.");
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.isFullDetail) {
|
||||
// we deactivate this button because the WatchedFileUpdateStatusWidget assumes only one visible attachment
|
||||
// in a note context, so it doesn't work in a list
|
||||
const $openAttachmentButton = this.$widget.find("[data-trigger-command='openAttachment']");
|
||||
$openAttachmentButton
|
||||
.addClass("disabled")
|
||||
.append($('<span class="disabled-tooltip"> (?)</span>')
|
||||
.attr("title", "Opening attachment externally is available only from the detail page, please first click on the attachment detail first and repeat the action.")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async openAttachmentCommand() {
|
||||
@ -101,10 +111,6 @@ export default class AttachmentActionsWidget extends BasicWidget {
|
||||
this.parent.copyAttachmentReferenceToClipboard();
|
||||
}
|
||||
|
||||
async openAttachmentExternallyCommand() {
|
||||
await openService.openAttachmentExternally(this.attachmentId, this.attachment.mime);
|
||||
}
|
||||
|
||||
async deleteAttachmentCommand() {
|
||||
if (!await dialogService.confirm(`Are you sure you want to delete attachment '${this.attachment.title}'?`)) {
|
||||
return;
|
||||
|
@ -37,8 +37,7 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
const attachmentDetailWidget = new AttachmentDetailWidget(attachment);
|
||||
attachmentDetailWidget.isFullDetail = true;
|
||||
const attachmentDetailWidget = new AttachmentDetailWidget(attachment, true);
|
||||
this.child(attachmentDetailWidget);
|
||||
|
||||
this.$wrapper.append(attachmentDetailWidget.render());
|
||||
|
@ -39,8 +39,7 @@ export default class AttachmentListTypeWidget extends TypeWidget {
|
||||
}
|
||||
|
||||
for (const attachment of attachments) {
|
||||
const attachmentDetailWidget = new AttachmentDetailWidget(attachment);
|
||||
attachmentDetailWidget.isFullDetail = false;
|
||||
const attachmentDetailWidget = new AttachmentDetailWidget(attachment, false);
|
||||
|
||||
this.child(attachmentDetailWidget);
|
||||
|
||||
|
@ -12,7 +12,7 @@ const TPL = `
|
||||
</style>
|
||||
|
||||
<p>File <code class="file-path"></code> has been last modified on <span class="file-last-modified"></span>.</p>
|
||||
|
||||
|
||||
<div style="display: flex; flex-direction: row; justify-content: space-evenly;">
|
||||
<button class="btn btn-sm file-upload-button">Upload modified file</button>
|
||||
|
||||
|
@ -195,6 +195,12 @@ div.ui-tooltip {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.dropdown-menu .disabled .disabled-tooltip {
|
||||
pointer-events: all;
|
||||
color: var(--menu-text-color);
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.dropdown-menu a:hover:not(.disabled), .dropdown-item:hover:not(.disabled) {
|
||||
color: var(--hover-item-text-color) !important;
|
||||
background-color: var(--hover-item-background-color) !important;
|
||||
|
Loading…
x
Reference in New Issue
Block a user