Implement "Copy only" feature for menu items to enhance user control over clipboard operations and update related settings
This commit is contained in:
parent
cc77ab3ddf
commit
0af0554cc9
5
.changeset/stale-pants-perform.md
Normal file
5
.changeset/stale-pants-perform.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'pastebar-app-ui': patch
|
||||
---
|
||||
|
||||
Added user preference to copy only menu items to clipboard without auto-pasting
|
@ -145,6 +145,7 @@ function App() {
|
||||
clipTextMinLength: settings.clipTextMinLength?.valueInt,
|
||||
clipTextMaxLength: settings.clipTextMaxLength?.valueInt,
|
||||
isImageCaptureDisabled: settings.isImageCaptureDisabled?.valueBool,
|
||||
isMenuItemCopyOnlyEnabled: settings.isMenuItemCopyOnlyEnabled?.valueBool,
|
||||
isAutoClearSettingsEnabled: settings.isAutoClearSettingsEnabled?.valueBool,
|
||||
autoClearSettingsDuration: settings.autoClearSettingsDuration?.valueInt,
|
||||
autoClearSettingsDurationType:
|
||||
|
@ -1,6 +1,7 @@
|
||||
App restart required: App restart required
|
||||
Application Starts with Main Window Hidden: Application Starts with Main Window Hidden
|
||||
Change: Change
|
||||
Copy only from menu items: Copy only from menu items
|
||||
Display navbar items only when the mouse hovers over the navigation bar to minimize visible UI elements: Display navbar items only when the mouse hovers over the navigation bar to minimize visible UI elements
|
||||
Global System OS Hotkeys: Global System OS Hotkeys
|
||||
Hide Collections Navbar: Hide Collections Navbar
|
||||
@ -22,3 +23,5 @@ Show collections menu on the navbar: Show collections menu on the navbar
|
||||
Show navbar elements on hover only: Show navbar elements on hover only
|
||||
Show/Hide Main App Window: Show/Hide Main App Window
|
||||
Show/Hide Quick Paste Window: Show/Hide Quick Paste Window
|
||||
? When enabled, clicking menu items will only copy content to clipboard instead of auto-pasting. This gives you more control over when and where content is pasted.
|
||||
: When enabled, clicking menu items will only copy content to clipboard instead of auto-pasting. This gives you more control over when and where content is pasted.
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
uiStoreAtom,
|
||||
} from '~/store'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { ChevronDown, ChevronUp, MessageSquare, MessageSquareDashed } from 'lucide-react'
|
||||
import { MessageSquare, MessageSquareDashed } from 'lucide-react'
|
||||
import { useTheme } from 'next-themes'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Link } from 'react-router-dom'
|
||||
@ -71,6 +71,8 @@ export default function UserPreferences() {
|
||||
hotKeysShowHideQuickPasteWindow,
|
||||
setHotKeysShowHideMainAppWindow,
|
||||
setHotKeysShowHideQuickPasteWindow,
|
||||
isMenuItemCopyOnlyEnabled,
|
||||
setIsMenuItemCopyOnlyEnabled,
|
||||
} = useAtomValue(settingsStoreAtom)
|
||||
|
||||
const { setFontSize, fontSize, setIsSwapPanels, isSwapPanels, returnRoute, isMacOSX } =
|
||||
@ -529,6 +531,32 @@ export default function UserPreferences() {
|
||||
</Card>
|
||||
</Box>
|
||||
|
||||
<Box className="animate-in fade-in max-w-xl mt-4">
|
||||
<Card
|
||||
className={`${
|
||||
!isMenuItemCopyOnlyEnabled && 'opacity-80 bg-gray-100 dark:bg-gray-900/80'
|
||||
}`}
|
||||
>
|
||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-1">
|
||||
<CardTitle className="animate-in fade-in text-md font-medium w-full">
|
||||
{t('Copy only from menu items', { ns: 'settings2' })}
|
||||
</CardTitle>
|
||||
<Switch
|
||||
checked={isMenuItemCopyOnlyEnabled}
|
||||
className="ml-auto"
|
||||
onCheckedChange={() => {
|
||||
setIsMenuItemCopyOnlyEnabled(!isMenuItemCopyOnlyEnabled)
|
||||
}}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Text className="text-sm text-muted-foreground">
|
||||
{t('When enabled, clicking menu items will only copy content to clipboard instead of auto-pasting. This gives you more control over when and where content is pasted.', { ns: 'settings2' })}
|
||||
</Text>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Box>
|
||||
|
||||
<Box className="animate-in fade-in max-w-xl mt-4">
|
||||
<Card
|
||||
className={`${
|
||||
|
@ -86,6 +86,7 @@ type Settings = {
|
||||
clipTextMinLength: number
|
||||
clipTextMaxLength: number
|
||||
isImageCaptureDisabled: boolean
|
||||
isMenuItemCopyOnlyEnabled: boolean
|
||||
}
|
||||
|
||||
type Constants = {
|
||||
@ -161,6 +162,7 @@ export interface SettingsStoreState {
|
||||
setIsHideCollectionsOnNavBar: (isEnabled: boolean) => void
|
||||
setIsShowNavBarItemsOnHoverOnly: (isEnabled: boolean) => void
|
||||
setIsImageCaptureDisabled: (isEnabled: boolean) => void
|
||||
setIsMenuItemCopyOnlyEnabled: (isEnabled: boolean) => void
|
||||
hashPassword: (pass: string) => Promise<string>
|
||||
isNotTourCompletedOrSkipped: (tourName: string) => boolean
|
||||
verifyPassword: (pass: string, hash: string) => Promise<boolean>
|
||||
@ -246,6 +248,7 @@ const initialState: SettingsStoreState & Settings = {
|
||||
clipTextMinLength: 0,
|
||||
clipTextMaxLength: 5000,
|
||||
isImageCaptureDisabled: false,
|
||||
isMenuItemCopyOnlyEnabled: false,
|
||||
CONST: {
|
||||
APP_DETECT_LANGUAGES_SUPPORTED: [],
|
||||
},
|
||||
@ -303,6 +306,7 @@ const initialState: SettingsStoreState & Settings = {
|
||||
setClipTextMinLength: () => {},
|
||||
setClipTextMaxLength: () => {},
|
||||
setIsImageCaptureDisabled: () => {},
|
||||
setIsMenuItemCopyOnlyEnabled: () => {},
|
||||
initConstants: () => {},
|
||||
setAppDataDir: () => {}, // Keep if used for other general app data
|
||||
setCustomDbPath: () => {},
|
||||
@ -375,7 +379,8 @@ export const settingsStore = createStore<SettingsStoreState & Settings>()((set,
|
||||
name === 'isHistoryEnabled' ||
|
||||
name === 'userSelectedLanguage' ||
|
||||
name === 'isAppLocked' ||
|
||||
name === 'isImageCaptureDisabled'
|
||||
name === 'isImageCaptureDisabled' ||
|
||||
name === 'isMenuItemCopyOnlyEnabled'
|
||||
) {
|
||||
invoke('build_system_menu')
|
||||
}
|
||||
@ -611,6 +616,9 @@ export const settingsStore = createStore<SettingsStoreState & Settings>()((set,
|
||||
setIsImageCaptureDisabled: async (isEnabled: boolean) => {
|
||||
return get().updateSetting('isImageCaptureDisabled', isEnabled)
|
||||
},
|
||||
setIsMenuItemCopyOnlyEnabled: async (isEnabled: boolean) => {
|
||||
return get().updateSetting('isMenuItemCopyOnlyEnabled', isEnabled)
|
||||
},
|
||||
isNotTourCompletedOrSkipped: (tourName: string) => {
|
||||
const { appToursCompletedList, appToursSkippedList } = get()
|
||||
return (
|
||||
|
@ -718,10 +718,24 @@ async fn main() {
|
||||
debug_output(|| {
|
||||
println!("system tray received a click on item id{:?} ", item_id);
|
||||
});
|
||||
|
||||
let w = app.get_window("main").unwrap();
|
||||
let state: tauri::State<DbItems> = app.state::<DbItems>();
|
||||
let db_items_state = state.0.lock().unwrap();
|
||||
|
||||
// Get the copy-only setting
|
||||
let app_settings = app.state::<Mutex<HashMap<String, Setting>>>();
|
||||
let settings_map = app_settings.lock().unwrap();
|
||||
let is_copy_only = settings_map
|
||||
.get("isMenuItemCopyOnlyEnabled")
|
||||
.and_then(|setting| setting.value_bool)
|
||||
.unwrap_or(false);
|
||||
|
||||
debug_output(|| {
|
||||
println!("Looking for item with item_id: {:?}", item_id);
|
||||
println!("is_copy_only: {:?}", is_copy_only);
|
||||
});
|
||||
|
||||
let item_opt = db_items_state.iter().find(|&item| item.item_id == item_id);
|
||||
|
||||
if let Some(item) = item_opt {
|
||||
@ -743,26 +757,54 @@ async fn main() {
|
||||
|
||||
// Convert relative path to absolute path
|
||||
let absolute_path = db::to_absolute_image_path(&image_path);
|
||||
let img_data = std::fs::read(&absolute_path).expect("Failed to read image from path");
|
||||
let img_data =
|
||||
std::fs::read(&absolute_path).expect("Failed to read image from path");
|
||||
let base64_image = base64::encode(&img_data);
|
||||
|
||||
write_image_to_clipboard(base64_image).expect("Failed to write image to clipboard");
|
||||
}
|
||||
if item.is_link.unwrap_or(false) {
|
||||
let url = item.value.as_deref().unwrap_or("");
|
||||
let _ = opener::open(ensure_url_or_email_prefix(url))
|
||||
.map_err(|e| format!("Failed to open url: {}", e));
|
||||
if is_copy_only {
|
||||
// Copy URL to clipboard instead of opening it
|
||||
debug_output(|| {
|
||||
println!("Copying URL to clipboard: {}", url);
|
||||
});
|
||||
manager
|
||||
.write_text(url)
|
||||
.expect("failed to write to clipboard");
|
||||
} else {
|
||||
let _ = opener::open(ensure_url_or_email_prefix(url))
|
||||
.map_err(|e| format!("Failed to open url: {}", e));
|
||||
}
|
||||
} else if item.is_path.unwrap_or(false) {
|
||||
let path = item.value.as_deref().unwrap_or("");
|
||||
let _ = opener::open(path).map_err(|e| format!("Failed to open path: {}", e));
|
||||
if is_copy_only {
|
||||
// Copy path to clipboard instead of opening it
|
||||
debug_output(|| {
|
||||
println!("Copying path to clipboard: {}", path);
|
||||
});
|
||||
manager
|
||||
.write_text(path)
|
||||
.expect("failed to write to clipboard");
|
||||
} else {
|
||||
let _ = opener::open(path).map_err(|e| format!("Failed to open path: {}", e));
|
||||
}
|
||||
} else {
|
||||
if item.value.as_deref().unwrap_or("").is_empty() {
|
||||
debug_output(|| {
|
||||
println!("Copying item name to clipboard: {}", &item.name);
|
||||
});
|
||||
manager
|
||||
.write_text(&item.name)
|
||||
.expect("failed to write to clipboard");
|
||||
} else if let Some(ref item_value) = item.value {
|
||||
let text_to_copy = remove_special_bbcode_tags(item_value);
|
||||
debug_output(|| {
|
||||
println!("Copying item value to clipboard: {}", text_to_copy);
|
||||
});
|
||||
manager
|
||||
.write_text(remove_special_bbcode_tags(item_value))
|
||||
.write_text(text_to_copy)
|
||||
.expect("failed to write to clipboard");
|
||||
}
|
||||
}
|
||||
@ -782,12 +824,15 @@ async fn main() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||
if query_accessibility_permissions() {
|
||||
VKey.press_paste();
|
||||
} else {
|
||||
w.show().unwrap();
|
||||
w.emit("macosx-permissions-modal", "show").unwrap();
|
||||
// Only auto-paste if not in copy-only mode
|
||||
if !is_copy_only {
|
||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||
if query_accessibility_permissions() {
|
||||
VKey.press_paste();
|
||||
} else {
|
||||
w.show().unwrap();
|
||||
w.emit("macosx-permissions-modal", "show").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
w.emit("execMenuItemById", item_id).unwrap();
|
||||
@ -801,11 +846,23 @@ async fn main() {
|
||||
let delay = if cfg!(target_os = "windows") { 3 } else { 0 };
|
||||
|
||||
rt.block_on(async {
|
||||
copy_paste_clip_item_from_menu(app_clone, item_id_string, delay).await;
|
||||
if is_copy_only {
|
||||
// For copy-only mode, use copy function instead of copy-paste
|
||||
clipboard_commands::copy_clip_item(app_clone, item_id_string, true).await;
|
||||
} else {
|
||||
copy_paste_clip_item_from_menu(app_clone, item_id_string, delay).await;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
debug_output(|| {
|
||||
println!(
|
||||
"Item not found in db_items_state, checking recent history for: {:?}",
|
||||
item_id
|
||||
);
|
||||
});
|
||||
|
||||
let recent_history_state: tauri::State<DbRecentHistoryItems> =
|
||||
app.state::<DbRecentHistoryItems>();
|
||||
let db_recent_history_items_state = recent_history_state.0.lock().unwrap();
|
||||
@ -838,7 +895,8 @@ async fn main() {
|
||||
|
||||
// Convert relative path to absolute path
|
||||
let absolute_path = db::to_absolute_image_path(&image_path);
|
||||
let img_data = std::fs::read(&absolute_path).expect("Failed to read image from path");
|
||||
let img_data =
|
||||
std::fs::read(&absolute_path).expect("Failed to read image from path");
|
||||
let base64_image = base64::encode(&img_data);
|
||||
|
||||
write_image_to_clipboard(base64_image).expect("Failed to write image to clipboard");
|
||||
@ -867,12 +925,15 @@ async fn main() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||
if query_accessibility_permissions() {
|
||||
VKey.press_paste();
|
||||
} else {
|
||||
w.show().unwrap();
|
||||
w.emit("macosx-permissions-modal", "show").unwrap();
|
||||
// Only auto-paste if not in copy-only mode
|
||||
if !is_copy_only {
|
||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||
if query_accessibility_permissions() {
|
||||
VKey.press_paste();
|
||||
} else {
|
||||
w.show().unwrap();
|
||||
w.emit("macosx-permissions-modal", "show").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
w.emit("execMenuItemById", item_id).unwrap();
|
||||
|
Loading…
x
Reference in New Issue
Block a user