diff --git a/src/SharpFM/MainWindow.axaml b/src/SharpFM/MainWindow.axaml
index 82d6f26..33c4a91 100644
--- a/src/SharpFM/MainWindow.axaml
+++ b/src/SharpFM/MainWindow.axaml
@@ -22,7 +22,10 @@
-
+
diff --git a/src/SharpFM/MainWindow.axaml.cs b/src/SharpFM/MainWindow.axaml.cs
index 025e31c..8670496 100644
--- a/src/SharpFM/MainWindow.axaml.cs
+++ b/src/SharpFM/MainWindow.axaml.cs
@@ -4,6 +4,10 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.VisualTree;
+using AvaloniaEdit;
+using AvaloniaEdit.Editing;
using SharpFM.Diagnostics;
using SharpFM.Plugin;
using SharpFM.Plugin.UI;
@@ -34,6 +38,9 @@ public MainWindow()
if (rawClipboard != null)
rawClipboard.Click += (_, _) => new RawClipboardWindow().Show(this);
+ // Tunnel-phase Ctrl+V — see OnPreviewKeyDown.
+ AddHandler(KeyDownEvent, OnPreviewKeyDown, RoutingStrategies.Tunnel);
+
// Wire up plugin UI when DataContext is set
DataContextChanged += OnDataContextChanged;
}
@@ -243,4 +250,34 @@ private void CloseTab_Click(object? sender, Avalonia.Interactivity.RoutedEventAr
if ((sender as Button)?.Tag is OpenTabViewModel tab)
vm.OpenTabs.Close(tab);
}
+
+ // Ctrl+V dispatch (issue #197). A Window-level KeyBinding fires before
+ // the focused control sees the keystroke, which broke plain-text paste
+ // in the script editor and the search box. Bubble-phase OnKeyDown does
+ // the opposite — text editors mark Ctrl+V handled, so the Window never
+ // sees the global case. Tunnel-phase preview lets us inspect the key
+ // first and decide based on focus: hand off to text editors, otherwise
+ // intercept for the FileMaker-clip paste.
+ private void OnPreviewKeyDown(object? sender, KeyEventArgs e)
+ {
+ if (e.Key != Key.V) return;
+ if (e.KeyModifiers != KeyModifiers.Control) return;
+ if (IsTextInputFocused()) return;
+ if (DataContext is not MainWindowViewModel vm) return;
+
+ e.Handled = true;
+ _ = vm.PasteFileMakerClipData();
+ }
+
+ private bool IsTextInputFocused()
+ {
+ var focused = FocusManager?.GetFocusedElement() as Visual;
+ while (focused is not null)
+ {
+ if (focused is TextBox or TextEditor or TextArea)
+ return true;
+ focused = focused.GetVisualParent();
+ }
+ return false;
+ }
}