diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts b/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts index 341ac8f9c..843fee150 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts @@ -9,8 +9,8 @@ import { DEFAULT_SEARCH_ENGINES } from '../components/SelectionActionSearchModal const logger = loggerService.withContext('useSettingsActionsList') -const MAX_CUSTOM_ITEMS = 8 -const MAX_ENABLED_ITEMS = 6 +const MAX_CUSTOM_ITEMS = 10 +const MAX_ENABLED_ITEMS = 8 export const useActionItems = ( initialItems: ActionItem[] | undefined, diff --git a/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx b/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx index 505a3b8fd..37a56acba 100644 --- a/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx +++ b/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx @@ -202,6 +202,30 @@ const SelectionToolbar: FC<{ demo?: boolean }> = ({ demo = false }) => { } }, [customCss, demo]) + /** + * Check if text is a valid URI or file path + */ + const isUriOrFilePath = (text: string): boolean => { + const trimmed = text.trim() + // Must not contain newlines or whitespace + if (/\s/.test(trimmed)) { + return false + } + // URI patterns: http://, https://, ftp://, file://, etc. + if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed)) { + return true + } + // Windows absolute path: C:\, D:\, etc. + if (/^[a-zA-Z]:[/\\]/.test(trimmed)) { + return true + } + // Unix absolute path: /path/to/file + if (/^\/[^/]/.test(trimmed)) { + return true + } + return false + } + // copy selected text to clipboard const handleCopy = useCallback(async () => { if (selectedText.current) { @@ -219,6 +243,43 @@ const SelectionToolbar: FC<{ demo?: boolean }> = ({ demo = false }) => { } }, [setTimeoutTimer]) + const handleSearch = useCallback((action: ActionItem) => { + if (!action.selectedText) return + + const selectedText = action.selectedText.trim() + + let actionString = '' + if (isUriOrFilePath(selectedText)) { + actionString = selectedText + } else { + if (!action.searchEngine) return + + const customUrl = action.searchEngine.split('|')[1] + if (!customUrl) return + + actionString = customUrl.replace('{{queryString}}', encodeURIComponent(selectedText)) + } + + window.api?.openWebsite(actionString) + window.api?.selection.hideToolbar() + }, []) + + /** + * Quote the selected text to the inputbar of the main window + */ + const handleQuote = (action: ActionItem) => { + if (action.selectedText) { + window.api?.quoteToMainWindow(action.selectedText) + window.api?.selection.hideToolbar() + } + } + + const handleDefaultAction = (action: ActionItem) => { + // [macOS] only macOS has the available isFullscreen mode + window.api?.selection.processAction(action, isFullScreen.current) + window.api?.selection.hideToolbar() + } + const handleAction = useCallback( (action: ActionItem) => { if (demo) return @@ -241,36 +302,9 @@ const SelectionToolbar: FC<{ demo?: boolean }> = ({ demo = false }) => { break } }, - [demo, handleCopy] + [demo, handleCopy, handleSearch] ) - const handleSearch = (action: ActionItem) => { - if (!action.searchEngine) return - - const customUrl = action.searchEngine.split('|')[1] - if (!customUrl) return - - const searchUrl = customUrl.replace('{{queryString}}', encodeURIComponent(action.selectedText || '')) - window.api?.openWebsite(searchUrl) - window.api?.selection.hideToolbar() - } - - /** - * Quote the selected text to the inputbar of the main window - */ - const handleQuote = (action: ActionItem) => { - if (action.selectedText) { - window.api?.quoteToMainWindow(action.selectedText) - window.api?.selection.hideToolbar() - } - } - - const handleDefaultAction = (action: ActionItem) => { - // [macOS] only macOS has the available isFullscreen mode - window.api?.selection.processAction(action, isFullScreen.current) - window.api?.selection.hideToolbar() - } - return (