From 924d1495a38ad5cadc2c5dd1eb8d0f8f84d36209 Mon Sep 17 00:00:00 2001 From: Mike Sawka Date: Wed, 16 Oct 2024 11:04:22 -0700 Subject: [PATCH] add new generic open clipboard url (#1045) --- frontend/app/app.tsx | 51 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/frontend/app/app.tsx b/frontend/app/app.tsx index f54f41642..15e5da4d6 100644 --- a/frontend/app/app.tsx +++ b/frontend/app/app.tsx @@ -4,7 +4,16 @@ import { useWaveObjectValue } from "@/app/store/wos"; import { Workspace } from "@/app/workspace/workspace"; import { ContextMenuModel } from "@/store/contextmenu"; -import { PLATFORM, WOS, atoms, getApi, globalStore, removeFlashError, useSettingsPrefixAtom } from "@/store/global"; +import { + PLATFORM, + WOS, + atoms, + createBlock, + getApi, + globalStore, + removeFlashError, + useSettingsPrefixAtom, +} from "@/store/global"; import { appHandleKeyDown } from "@/store/keymodel"; import { getWebServerEndpoint } from "@/util/endpoints"; import { getElemAsStr } from "@/util/focusutil"; @@ -36,7 +45,7 @@ const App = () => { ); }; -function isContentEditableBeingEdited() { +function isContentEditableBeingEdited(): boolean { const activeElement = document.activeElement; return ( activeElement && @@ -45,17 +54,17 @@ function isContentEditableBeingEdited() { ); } -function canEnablePaste() { +function canEnablePaste(): boolean { const activeElement = document.activeElement; return activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA" || isContentEditableBeingEdited(); } -function canEnableCopy() { +function canEnableCopy(): boolean { const sel = window.getSelection(); return !util.isBlank(sel?.toString()); } -function canEnableCut() { +function canEnableCut(): boolean { const sel = window.getSelection(); if (document.activeElement?.classList.contains("xterm-helper-textarea")) { return false; @@ -63,12 +72,26 @@ function canEnableCut() { return !util.isBlank(sel?.toString()) && canEnablePaste(); } -function handleContextMenu(e: React.MouseEvent) { +async function getClipboardURL(): Promise { + try { + const clipboardText = await navigator.clipboard.readText(); + if (clipboardText == null) { + return null; + } + const url = new URL(clipboardText); + return url; + } catch (e) { + return null; + } +} + +async function handleContextMenu(e: React.MouseEvent) { e.preventDefault(); const canPaste = canEnablePaste(); const canCopy = canEnableCopy(); const canCut = canEnableCut(); - if (!canPaste && !canCopy && !canCut) { + const clipboardURL = await getClipboardURL(); + if (!canPaste && !canCopy && !canCut && !clipboardURL) { return; } let menu: ContextMenuItem[] = []; @@ -81,6 +104,20 @@ function handleContextMenu(e: React.MouseEvent) { if (canPaste) { menu.push({ label: "Paste", role: "paste" }); } + if (clipboardURL) { + menu.push({ type: "separator" }); + menu.push({ + label: "Open Clipboard URL (" + clipboardURL.hostname + ")", + click: () => { + createBlock({ + meta: { + view: "web", + url: clipboardURL.toString(), + }, + }); + }, + }); + } ContextMenuModel.showContextMenu(menu, e); }