[Quicker] 剪贴板 - 源码归档

动作:剪贴板

WASD极速导航 | 收藏云同步 | 临时置顶 | 图片预览 | 一键收藏 | 零秒启动

更新时间:2025-12-28 18:27
原始文件:剪贴板.cs

核心代码

//css_ref PresentationCore
//css_ref PresentationFramework
//css_ref WindowsBase
//css_ref System.Xaml
//css_ref System.Xml
//css_ref System.Drawing
//css_ref System.Windows.Forms

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Threading;
using System.Xml.Serialization;
using System.ComponentModel; 
using System.Windows.Data;
using System.Windows.Media.Imaging; 
using System;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Reflection;
using System.Threading.Tasks;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using System.Windows.Media.Animation;

// --- 性能监控辅助类 ---
public static class Perf
{
    private static Stopwatch _sw = new Stopwatch();
    
    // 简单的耗时测量,超过阈值才记录
    public static void Measure(string name, Action action, int thresholdMs = 15)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            action();
        }
        finally
        {
            sw.Stop();
            long ms = sw.ElapsedMilliseconds;
            if (ms >= thresholdMs)
            {
                ClipboardHistoryWindow.Log($"[Perf] {name} 耗时: {ms}ms");
            }
        }
    }

    public static T Measure<T>(string name, Func<T> func, int thresholdMs = 15)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            return func();
        }
        finally
        {
            sw.Stop();
            long ms = sw.ElapsedMilliseconds;
            if (ms >= thresholdMs)
            {
                ClipboardHistoryWindow.Log($"[Perf] {name} 耗时: {ms}ms");
            }
        }
    }

    // 新增:资源监控指标记录
    public static void LogStats(string context)
    {
        try {
            using (var proc = System.Diagnostics.Process.GetCurrentProcess()) {
                double mem = proc.WorkingSet64 / 1024.0 / 1024.0;
                double cpu = proc.TotalProcessorTime.TotalSeconds;
                ClipboardHistoryWindow.Log($"[Metrics] {context} -> Memory: {mem:F1}MB, CPU Time: {cpu:F2}s");
            }
        } catch {}
    }
}


public static void Exec(Quicker.Public.IStepContext context)
{
    if (System.Threading.Thread.CurrentThread.GetApartmentState() != System.Threading.ApartmentState.STA)
    {
        System.Windows.Forms.MessageBox.Show("配置错误:\n请将【执行线程】修改为【后台线程 (STA 独立线程)】。");
        return;
    }

    IntPtr targetWin = ClipboardHistoryWindow.GetForegroundWindow();

    bool isSilentStart = false;
    try {
        object v = context.GetVarValue("silent");
        if (v != null)
        {
            if (v is bool b) isSilentStart = b;
            else if (v is string s) isSilentStart = s.Equals("true", StringComparison.OrdinalIgnoreCase);
        }
    } catch { }

    // 1. 寻找已存在的窗口
    Window existing = null;
    
    // 优先尝试当前程序集的静态实例
    if (ClipboardHistoryWindow.Instance != null)
    {
        try { if (ClipboardHistoryWindow.Instance.IsLoaded) existing = ClipboardHistoryWindow.Instance; } catch { }
    }
    
    // 如果没找到,通过全局窗口列表寻找(支持跨程序集唤起)
    if (existing == null && System.Windows.Application.Current != null)
    {
        try {
            System.Windows.Application.Current.Dispatcher.Invoke(() => {
                existing = System.Windows.Application.Current.Windows.Cast<Window>()
                            .FirstOrDefault(w => {
                                try {
                                    string t = w.Dispatcher.CheckAccess() ? w.Title : w.Dispatcher.Invoke(() => w.Title);
                                    return t == "剪贴板历史" || t == "剪贴板1";
                                } catch { return false; }
                            });
            });
        } catch { }
    }
    
    if (existing != null) 
    {
        ClipboardHistoryWindow.Log($"[Exec] 发现已有窗口,尝试切换显隐状态。");
        existing.Dispatcher.Invoke(() => {
            try {
                var type = existing.GetType();
                var updateMethod = type.GetMethod("UpdateTargetWindow");
                if (updateMethod != null) updateMethod.Invoke(existing, new object[] { targetWin });

                if (isSilentStart) return;

                // 核心控制:切换显隐 (Toggle)
                bool isVisible = existing.Visibility == Visibility.Visible;
                ClipboardHistoryWindow.Log($"[Exec] 状态分析: Visibility={existing.Visibility}, IsVisible={existing.IsVisible}");
                
                if (isVisible)
                {
                    ClipboardHistoryWindow.Log($"[Exec] 窗口已显示,执行隐藏。");
                    var hideMethod = type.GetMethod("HideWindow");
                    if (hideMethod != null) hideMethod.Invoke(existing, null);
                    else existing.Visibility = Visibility.Hidden;
                }
                else
                {
                    ClipboardHistoryWindow.Log($"[Exec] 窗口显示/唤醒。");
                    var showMethod = type.GetMethod("ShowAtMousePosition", new Type[] { typeof(bool) });
                    if (showMethod == null) showMethod = type.GetMethod("ShowAtMousePosition", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                    
                    if (showMethod != null)
                    {
                        if (showMethod.GetParameters().Length == 1)
                            showMethod.Invoke(existing, new object[] { true }); 
                        else
                            showMethod.Invoke(existing, null);
                    }
                    else {
                        existing.Visibility = Visibility.Visible;
                        existing.Show();
                        existing.Activate();
                        // If ShowAtMousePosition is not found, and we are showing the window,
                        // we should also try to refresh the collection view if it's a ClipboardHistoryWindow.
                        if (existing is ClipboardHistoryWindow chw)
                        {
                            chw.RefreshCollectionView(); // Call the new method directly if possible
                        }
                    }
                }
            } catch (Exception ex) {
                ClipboardHistoryWindow.Log($"[Exec] 唤醒过程出错: {ex.Message}");
            }
        });
        return; 
    } 


    try 
    {
        ClipboardHistoryWindow.Log($"[Exec] 创建全新窗口. Silent={isSilentStart}");
        var win = new ClipboardHistoryWindow(context, targetWin, isSilentStart);
        
        if (!isSilentStart)
        {
            win.Show();
            win.Activate();
        }
        
        // 阻塞在消息循环,直到窗口关闭
        System.Windows.Threading.Dispatcher.Run();
    }
    catch (Exception ex)
    {
        ClipboardHistoryWindow.Log($"[Exec] 崩溃退出: {ex.Message}\n{ex.StackTrace}");
    }
}

public enum ClipType { Text = 0, File = 1, Image = 2 }

public enum ContentCategory
{
    General,
    URL,
    Email,
    Phone,
    Code,
    JsonXml,
    Color,
    FilePath,
    DateTime,
    IPAddress,
    File,
    Image
}


public class ClipItem : INotifyPropertyChanged
{
    private bool _isFavorite;
    private bool _isPinned;
    private string _text;       
    private string _dataValue;  
    private ClipType _type;     
    private DateTime _timestamp;
    private int _index;
    
    private string _sourcePath;
    private string _targetPath;

    private BitmapImage _cachedImage;
    private ImageSource _sourceIcon;
    private ImageSource _targetIcon;
    public string ImageHash { get; set; } // 新增:保存图片哈希

    [XmlIgnore]
    public string IndexDisplay => _index.ToString("00");

    [XmlIgnore]
    public int Index 
    { 
        get { return _index; } 
        set { _index = value; OnPropertyChanged("Index"); OnPropertyChanged("IndexDisplay"); } 
    }

    [XmlIgnore] public IntPtr SourceWindowHandle { get; set; }
    [XmlIgnore] public IntPtr TargetWindowHandle { get; set; }

    public string Text 
    { 
        get { return _text; } 
        set { 
            _text = value; 
            OnPropertyChanged("Text"); 
            UpdateCategory();
        } 
    }

    private ContentCategory _category = ContentCategory.General;
    public ContentCategory Category 
    { 
        get { return _category; } 
        set { _category = value; OnPropertyChanged("Category"); OnPropertyChanged("CategoryLabel"); OnPropertyChanged("CategoryIcon"); OnPropertyChanged("CategoryColor"); } 
    }

    [XmlIgnore]
    public string CategoryLabel
    {
        get {
            switch (Category) {
                case ContentCategory.URL: return "链接";
                case ContentCategory.Email: return "邮箱";
                case ContentCategory.Phone: return "电话";
                case ContentCategory.Code: return "代码";
                case ContentCategory.JsonXml: return "结构化";
                case ContentCategory.Color: return "颜色";
                case ContentCategory.FilePath: return "路径";
                case ContentCategory.DateTime: return "时间";
                case ContentCategory.IPAddress: return "IP";
                case ContentCategory.File: return "文件";
                case ContentCategory.Image: return "图片";
                default: return "";
            }
        }
    }

    [XmlIgnore]
    public string CategoryIcon
    {
        get {
            switch (Category) {
                case ContentCategory.URL: return "";
                case ContentCategory.Email: return "";
                case ContentCategory.Phone: return "";
                case ContentCategory.Code: return "</>";
                case ContentCategory.JsonXml: return "{ }";
                case ContentCategory.Color: return "";
                case ContentCategory.FilePath: return "";
                case ContentCategory.DateTime: return "";
                case ContentCategory.IPAddress: return "";
                case ContentCategory.File: return "";
                case ContentCategory.Image: return ""; // 移除图片图标
                default: return ""; // 移除文字默认图标
            }
        }
    }

    [XmlIgnore]
    public string CategoryColor
    {
        get {
            switch (Category) {
                case ContentCategory.URL: return "#10b981";
                case ContentCategory.Email: return "#e67e22";
                case ContentCategory.Phone: return "#2ecc71";
                case ContentCategory.Code: return "#3b82f6";
                case ContentCategory.JsonXml: return "#8b5cf6";
                case ContentCategory.Color: return "#ec4899";
                case ContentCategory.FilePath: return "#f1c40f";
                case ContentCategory.DateTime: return "#7f8c8d";
                case ContentCategory.IPAddress: return "#34495e";
                case ContentCategory.File: return "#95a5a6";
                case ContentCategory.Image: return "#16a085";
                default: return "#9ca3af";
            }
        }
    }

    [XmlIgnore]
    public Visibility CategoryTagVisibility => Visibility.Collapsed; // 彻底移除标签

    [XmlIgnore]
    public Visibility CategoryVisibility => (Category != ContentCategory.General && Category != ContentCategory.Image) ? Visibility.Visible : Visibility.Collapsed;

    // ToolTip 智能显示:仅当文本被截断时才显示
    [XmlIgnore]
    public string ToolTipText
    {
        get
        {
            if (Type == ClipType.Image) return null; // 图片单独处理
            if (string.IsNullOrEmpty(Text)) return null;
            // 文本长度小于 16 字符且无换行,认为未被截断
            if (Text.Length <= 16 && !Text.Contains("\n")) return null;
            return Text;
        }
    }

    // 预编译正则表达式以获得最佳性能
    private static readonly Regex _regexUrl = new Regex(@"^(https?|ftp)://[^\s/$.?#].[^\s]*$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    private static readonly Regex _regexEmail = new Regex(@"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$", RegexOptions.Compiled);
    private static readonly Regex _regexHexColor = new Regex(@"^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8})$", RegexOptions.Compiled);
    private static readonly Regex _regexRgbColor = new Regex(@"^(rgb|hsl)a?\(.*\)$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    private static readonly Regex _regexWinPath = new Regex(@"^[a-zA-Z]:\\[\\\S|*\s]*$", RegexOptions.Compiled);
    private static readonly Regex _regexPhone = new Regex(@"^(\+86)?\d{11}$|^(\d{3,4}-)?\d{7,8}$", RegexOptions.Compiled);
    private static readonly Regex _regexIP = new Regex(@"^(\d{1,3}\.){3}\d{1,3}$", RegexOptions.Compiled);

    public void UpdateCategory()
    {
        if (Type == ClipType.File) { Category = ContentCategory.File; return; }
        if (Type == ClipType.Image) { Category = ContentCategory.Image; return; }

        if (Type != ClipType.Text || string.IsNullOrWhiteSpace(Text)) 
        {
            Category = ContentCategory.General;
            return;
        }

        string t = Text.Trim();
        
        if (_regexUrl.IsMatch(t)) { Category = ContentCategory.URL; return; }
        if (_regexEmail.IsMatch(t)) { Category = ContentCategory.Email; return; }
        if (_regexHexColor.IsMatch(t) || _regexRgbColor.IsMatch(t)) { Category = ContentCategory.Color; return; }
        if (_regexWinPath.IsMatch(t)) { Category = ContentCategory.FilePath; return; }
        if (_regexPhone.IsMatch(t)) { Category = ContentCategory.Phone; return; }
        if (_regexIP.IsMatch(t)) { Category = ContentCategory.IPAddress; return; }

        // JSON/XML
        if ((t.StartsWith("{") && t.EndsWith("}")) || (t.StartsWith("[") && t.EndsWith("]"))) { Category = ContentCategory.JsonXml; return; }
        if (t.StartsWith("<") && t.EndsWith(">")) { Category = ContentCategory.JsonXml; return; }

        // Code (Heuristic)
        if (t.Contains(";") && (t.Contains("{") || t.Contains("public") || t.Contains("var ") || t.Contains("function") || t.Contains("import "))) { Category = ContentCategory.Code; return; }

        Category = ContentCategory.General;
    }
    
    public string DataValue 
    { 
        get { return _dataValue; } 
        set { 
            _dataValue = value; 
            _cachedImage = null; 
            OnPropertyChanged("Thumbnail");
            OnPropertyChanged("PreviewImagePath");
        } 
    }
    
    public ClipType Type 
    { 
        get { return _type; } 
        set { 
            _type = value; 
            OnPropertyChanged("TextVisibility");
            OnPropertyChanged("ImageVisibility");
            OnPropertyChanged("Thumbnail"); 
            OnPropertyChanged("PreviewImagePath");
        } 
    }
    
    public bool IsFavorite 
    { 
        get { return _isFavorite; } 
        set { _isFavorite = value; OnPropertyChanged("IsFavorite"); OnPropertyChanged("IconVisibility"); } 
    }

    public bool IsPinned 
    { 
        get { return _isPinned; } 
        set { _isPinned = value; OnPropertyChanged("IsPinned"); } 
    }
    
    public DateTime Timestamp 
    { 
        get { return _timestamp; } 
        set { _timestamp = value; OnPropertyChanged("DisplayTime"); OnPropertyChanged("FriendlyTime"); } 
    }

    public string SourcePath
    {
        get { return _sourcePath; }
        set { _sourcePath = value; _sourceIcon = null; OnPropertyChanged("SourcePath"); OnPropertyChanged("SourceAppIcon"); OnPropertyChanged("SourceAppName"); OnPropertyChanged("HasSource"); }
    }

    public string TargetPath
    {
        get { return _targetPath; }
        set { _targetPath = value; _targetIcon = null; OnPropertyChanged("TargetPath"); OnPropertyChanged("TargetAppIcon"); OnPropertyChanged("TargetAppName"); OnPropertyChanged("HasTarget"); }
    }
    
    [XmlIgnore] public string DisplayTime { get { return Timestamp.ToString("HH:mm"); } }
    
    [XmlIgnore] 
    public string FriendlyTime 
    { 
        get 
        { 
            var span = DateTime.Now - Timestamp;
            if (span.TotalMinutes < 1) return "刚刚";
            if (span.TotalMinutes < 60) return string.Format("{0}分钟前", (int)span.TotalMinutes);
            if (span.TotalHours < 24) return string.Format("{0}小时前", (int)span.TotalHours);
            if (span.TotalDays < 2) return "昨天";
            return Timestamp.ToString("MM-dd");
        } 
    }
    
    [XmlIgnore] public string IconVisibility { get { return IsFavorite ? "Visible" : "Collapsed"; } }
    [XmlIgnore] public Visibility TextVisibility { get { return string.IsNullOrWhiteSpace(Text) ? Visibility.Collapsed : Visibility.Visible; } } 
    [XmlIgnore] public Visibility ImageVisibility { get { return Thumbnail != null ? Visibility.Visible : Visibility.Collapsed; } }

    [XmlIgnore] public Visibility HasSource { get { return string.IsNullOrEmpty(SourcePath) ? Visibility.Collapsed : Visibility.Visible; } }
    [XmlIgnore] public Visibility HasTarget { get { return string.IsNullOrEmpty(TargetPath) ? Visibility.Collapsed : Visibility.Visible; } }
    [XmlIgnore] public string SourceAppName { get { return string.IsNullOrEmpty(SourcePath) ? "" : System.IO.Path.GetFileNameWithoutExtension(SourcePath); } }
    [XmlIgnore] public string TargetAppName { get { return string.IsNullOrEmpty(TargetPath) ? "" : System.IO.Path.GetFileNameWithoutExtension(TargetPath); } }

    [XmlIgnore]
    public ImageSource SourceAppIcon
    {
        get
        {
            if (_sourceIcon == null && !string.IsNullOrEmpty(SourcePath))
                _sourceIcon = ClipboardHistoryWindow.GetIconFromPath(SourcePath);
            return _sourceIcon;
        }
    }

    [XmlIgnore]
    public ImageSource TargetAppIcon
    {
        get
        {
            if (_targetIcon == null && !string.IsNullOrEmpty(TargetPath))
                _targetIcon = ClipboardHistoryWindow.GetIconFromPath(TargetPath);
            return _targetIcon;
        }
    }

    // 预览大图(原始路径)
    [XmlIgnore]
    public string PreviewImagePath
    {
        get
        {
            if (Type == ClipType.Image && !string.IsNullOrEmpty(DataValue)) return DataValue;
            if (Type == ClipType.File && !string.IsNullOrEmpty(DataValue))
            {
                var paths = DataValue.Split('|');
                if (paths.Length > 0 && IsImageFile(paths[0])) return paths[0];
            }
            return null;
        }
    }

    // 列表缩略图(压缩)
    [XmlIgnore]
    public ImageSource Thumbnail
    {
        get
        {
            if (_cachedImage != null) return _cachedImage;
            if (string.IsNullOrEmpty(DataValue)) return null;

            string imagePath = PreviewImagePath;

            if (!string.IsNullOrEmpty(imagePath) && File.Exists(imagePath))
            {
                try
                {
                    var bi = new BitmapImage();
                    bi.BeginInit();
                    bi.UriSource = new Uri(imagePath);
                    bi.CacheOption = BitmapCacheOption.OnLoad; 
                    bi.DecodePixelHeight = 100; 
                    bi.EndInit();
                    bi.Freeze(); 
                    _cachedImage = bi;
                    return _cachedImage;
                }
                catch { }
            }
            return null;
        }
    }
    
    private bool IsImageFile(string path)
    {
        try {
            string ext = System.IO.Path.GetExtension(path).ToLower();
            return ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp" || ext == ".gif" || ext == ".webp";
        } catch { return false; }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(name)); }
}

public class ClipboardHistoryWindow : Window
{
    // 依赖属性:用于绑定 DragHandle 的显示状态
    public bool IsFavMode
    {
        get { return (bool)GetValue(IsFavModeProperty); }
        set { SetValue(IsFavModeProperty, value); }
    }
    public static ClipboardHistoryWindow Instance; 
    public static readonly DependencyProperty IsFavModeProperty =
        DependencyProperty.Register("IsFavMode", typeof(bool), typeof(ClipboardHistoryWindow), new PropertyMetadata(false));

    private Quicker.Public.IStepContext _context;
    private ObservableCollection<ClipItem> _items = new ObservableCollection<ClipItem>();
    private ICollectionView _itemsView;
    private TextBlock _tabAll;
    private TextBlock _tabFav;
    private TextBlock _syncIcon; // 同步状态图标
    private ListBox _listBox;
    private TextBox _searchBox; 
    private string _dataFile;
    private string _appDataPath;
    private string _configFile; 
    private string _imageCacheDir; 
    private DispatcherTimer _monitorTimer;
    private string _lastClipboardSignature; 
    private bool _showFavoritesOnly = false;
    private string _searchText = "";
    private bool _mouseActivateAtFirstItem = false;
    private ContentCategory _selectedCategory = ContentCategory.General; // General here means "All"

    private bool _isDarkTheme = false;
    
    private IntPtr _targetWindow;
    private bool _isModalOpen = false;
    private bool _forceClose = false;
    private bool _silentStart = false;
    
    private System.Windows.Controls.Primitives.ToggleButton _pinWindowBtn;
    public bool IsPinnedMode { get { return _pinWindowBtn != null && _pinWindowBtn.IsChecked == true; } }
    
    private int _tickCounter = 0;
    private uint _lastClipboardSequenceNumber = 0;
    public DateTime _lastShowTime = DateTime.MinValue; // 用于防止呼出时失焦闪退
    
    private static readonly object _saveLock = new object(); // 用于防止后台保存冲突
    private DispatcherTimer _searchTimer; // 搜索防抖定时器
    
    // 快速滚动选择模式
    private bool _isQuickSelectMode = false;
    private bool _hasScrolledInQuickMode = false; // 是否在快速模式下滚动过
    private DateTime _lastQuickModeTime = DateTime.MinValue; // 防止快速重复触发
    private bool _quickModeJustExited = false; // 刚从快速模式退出(用于防止窗口被隐藏)
    
    // 快捷键配置(默认 Ctrl+Alt+V,避免与系统输入法切换 Ctrl+Shift 冲突)
    private bool _hotkeyCtrl = true;   // 是否需要Ctrl
    private bool _hotkeyShift = false; // 是否需要Shift
    private bool _hotkeyAlt = true;    // 是否需要Alt
    private int _hotkeyKey = 0x56;     // 主键,默认V (0x56)
    private string _settingsFile;
    
    // 公开静态日志方法,供辅助类调用
    public static void Log(string msg) 
    {
        try {
            // 记录日志到 AppData,避免 GetCurrentDirectory 的不确定性
            string dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Quicker_ClipFlow");
            if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
            string path = Path.Combine(dir, "detailed_debug.log");
            
            string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
            string logEntry = $"[{time}] [Thread:{System.Threading.Thread.CurrentThread.ManagedThreadId}] {msg}\r\n";
            
            // 日志大小控制:超过 5MB 直接删除重建
            try {
                var info = new FileInfo(path);
                if (info.Exists && info.Length > 5 * 1024 * 1024) {
                    File.Delete(path);
                }
            } catch {}

            File.AppendAllText(path, logEntry);
        } catch {}
    }

    private void DebugLog(string msg) 
    {
        Log(msg);
    }
    
    public void ShowDebugLogs() 
    {
        try {
            string path = Path.Combine(_appDataPath, "perf_debug.log");
            if (File.Exists(path))
                Process.Start("notepad.exe", path);
        } catch {}
    }    
    private IntPtr _keyboardHookId = IntPtr.Zero;
    private IntPtr _mouseHookId = IntPtr.Zero;
    private LowLevelKeyboardProc _keyboardProc;
    private LowLevelMouseProc _mouseProc;
    
    // 全局钩子相关
    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    private delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);
    
    private const int WH_KEYBOARD_LL = 13;
    private const int WH_MOUSE_LL = 14;
    private const int WM_KEYDOWN = 0x0100;
    private const int WM_KEYUP = 0x0101;
    private const int WM_MOUSEWHEEL = 0x020A;
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    
    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool GetCursorPos(out POINT lpPoint);
    
    [DllImport("user32.dll")]
    private static extern short GetAsyncKeyState(int vKey);
    
    private const int VK_CONTROL = 0x11;
    private const int VK_SHIFT = 0x10;
    private const int VK_ALT = 0x12;
    private const int VK_C = 0x43;

    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

    [DllImport("user32.dll")]
    public static extern uint GetClipboardSequenceNumber();

    [DllImport("user32.dll")]
    public static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll")]
    static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, UIntPtr dwExtraInfo);
    private const int KEYEVENTF_KEYUP = 0x0002;

    [DllImport("user32.dll", SetLastError=true)]
    static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    [DllImport("user32.dll")]
    internal static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
    [DllImport("user32.dll")]
    internal static extern bool IsIconic(IntPtr hWnd);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool IsWindow(IntPtr hWnd);

    private const int SW_RESTORE = 9;

    [StructLayout(LayoutKind.Sequential)]
    public struct POINT { public int X; public int Y; }

    public ClipboardHistoryWindow(Quicker.Public.IStepContext context, IntPtr targetWindow, bool silentStart = false)
    {
        _context = context;
        _targetWindow = targetWindow; 
        _silentStart = silentStart;

        _appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Quicker_ClipFlow");
        if (!Directory.Exists(_appDataPath)) Directory.CreateDirectory(_appDataPath);

        _dataFile = Path.Combine(_appDataPath, "History_v7.xml"); 
        _configFile = Path.Combine(_appDataPath, "Config.txt");
        _settingsFile = Path.Combine(_appDataPath, "Settings.txt");
        _imageCacheDir = Path.Combine(_appDataPath, "Images");
        
        if (!Directory.Exists(_imageCacheDir)) Directory.CreateDirectory(_imageCacheDir);
        
        Instance = this;
        LoadSettings();
        DetectSystemTheme();

        this.Title = "剪贴板历史";
        this.Width = 360;
        this.Height = 560; 
        this.WindowStyle = WindowStyle.None;
        this.ResizeMode = ResizeMode.CanResize;
        this.MinWidth = 300;
        this.MinHeight = 400;
        this.AllowsTransparency = true;

        this.Background = Brushes.Transparent;
        
        // 如果是静默启动,初始完全透明并隐藏
        // 如果是正常启动,初始可见度设为1,防止 Loaded 时闪烁
        this.Opacity = silentStart ? 0 : 1; 
        this.Visibility = silentStart ? Visibility.Hidden : Visibility.Visible;
        this.ShowInTaskbar = !silentStart;
        
        this.WindowStartupLocation = WindowStartupLocation.Manual;

        LoadThemeResources();
        Perf.Measure("InitializeUI", () => InitializeUI(), 1);
        Perf.LogStats("StartupComplete"); // 记录启动后的资源占用
        
        // 启用缩放支持
        var chrome = new System.Windows.Shell.WindowChrome {
            CaptionHeight = 0,
            ResizeBorderThickness = new Thickness(5),
            GlassFrameThickness = new Thickness(0)
        };
        System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);

        LoadData(); 
        CleanupImageCache(false);
        // LoadWindowPosition 移至 Loaded 中统一处理,避免二次计算坐标
        
        this.Loaded += (s, e) => { 
            this.Opacity = 1;
            if (!_silentStart)
            {
                Log($"[Loaded] 窗口加载完成,执行初次显示逻辑。");
                _lastShowTime = DateTime.Now;
                this.ShowInTaskbar = true;
                
                if (_mouseActivateAtFirstItem) ShowAtMousePosition(true);
                else UpdatePositionToMouse();
                
                ResetFocus();
                this.Visibility = Visibility.Visible;
                this.Activate(); 
            }
            else
            {
                 Log($"[Loaded] 静默启动完成,窗口维持隐藏状态。");
            }
        };

        CheckClipboardAndAdd(true); 
        
        _monitorTimer = new DispatcherTimer();
        _monitorTimer.Interval = TimeSpan.FromMilliseconds(50); 
        _monitorTimer.Tick += new EventHandler(OnTimerTick);
        _monitorTimer.Start();

        this.Deactivated += new EventHandler(OnDeactivated);
        this.Closing += new System.ComponentModel.CancelEventHandler(OnClosing);
        this.PreviewKeyDown += new KeyEventHandler(OnWindowKeyDown);
        this.PreviewKeyUp += new KeyEventHandler(OnWindowKeyUp);
        this.PreviewMouseWheel += new MouseWheelEventHandler(OnWindowMouseWheel);
        this.MouseLeftButtonDown += (o, args) => { 
            // 只在空白区域拖动,避免拦截按钮点击
            if (args.OriginalSource is Border || args.OriginalSource is Grid || args.OriginalSource is Window)
            {
                if (args.ButtonState == MouseButtonState.Pressed) 
                    this.DragMove(); 
            }
        };
        
        // 初始化全局钩子
        InstallGlobalHooks();
    }
    
    private void InstallGlobalHooks()
    {
        _keyboardProc = KeyboardHookCallback;
        _mouseProc = MouseHookCallback;
        
        using (var curProcess = Process.GetCurrentProcess())
        using (var curModule = curProcess.MainModule)
        {
            _keyboardHookId = SetWindowsHookEx(WH_KEYBOARD_LL, _keyboardProc, GetModuleHandle(curModule.ModuleName), 0);
            _mouseHookId = SetWindowsHookEx(WH_MOUSE_LL, _mouseProc, GetModuleHandle(curModule.ModuleName), 0);
        }
    }
    
    private void UninstallGlobalHooks()
    {
        if (_keyboardHookId != IntPtr.Zero)
        {
            UnhookWindowsHookEx(_keyboardHookId);
            _keyboardHookId = IntPtr.Zero;
        }
        if (_mouseHookId != IntPtr.Zero)
        {
            UnhookWindowsHookEx(_mouseHookId);
            _mouseHookId = IntPtr.Zero;
        }
    }
    
    private IntPtr KeyboardHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            
            // 检测配置的快捷键
            bool ctrlPressed = (GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0;
            bool shiftPressed = (GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0;
            bool altPressed = (GetAsyncKeyState(VK_ALT) & 0x8000) != 0;
            
            bool hotkeyMatch = vkCode == _hotkeyKey &&
                               ctrlPressed == _hotkeyCtrl &&
                               shiftPressed == _hotkeyShift &&
                               altPressed == _hotkeyAlt;
            
            if (hotkeyMatch)
            {
                if ((int)wParam == WM_KEYDOWN)
                {
                    DebugLog($"[KeyDown C] isQuick={_isQuickSelectMode}, hasScrolled={_hasScrolledInQuickMode}");
                    
                    // 已经在快速模式中,忽略重复的KeyDown
                    if (_isQuickSelectMode)
                    {
                        DebugLog("[KeyDown C] 忽略:已在快速模式");
                        return (IntPtr)1;
                    }
                    
                    // 防止快速重复触发(300ms内不重复触发)
                    var elapsed = (DateTime.Now - _lastQuickModeTime).TotalMilliseconds;
                    if (elapsed < 300)
                    {
                        DebugLog($"[KeyDown C] 忽略:间隔太短 {elapsed:F0}ms");
                        return (IntPtr)1;
                    }
                    
                    // 如果窗口已经显示,则隐藏它(切换显示)
                    bool isVisible = false;
                    Dispatcher.Invoke(() => { isVisible = this.IsVisible; });
                    if (isVisible)
                    {
                        DebugLog("[KeyDown C] 窗口已显示,隐藏窗口");
                        _lastQuickModeTime = DateTime.Now;
                        Dispatcher.BeginInvoke(new Action(() => { HideWindow(); }));
                        return (IntPtr)1;
                    }
                    
                    // 同步设置标记,避免异步竞争
                    _isQuickSelectMode = true;
                    _hasScrolledInQuickMode = false;
                    _lastQuickModeTime = DateTime.Now;
                    var targetWin = GetForegroundWindow();
                    
                    Perf.Measure("Hook_KeyDown_Show", () => {
                        DebugLog("[KeyDown C] 进入快速模式");
                        
                        Dispatcher.BeginInvoke(new Action(() => {
                            _targetWindow = targetWin;
                            ShowAtMousePosition(true);
                        }));
                    });
                    return (IntPtr)1; // 阻止默认行为
                }
                else if ((int)wParam == WM_KEYUP)
                {
                    DebugLog($"[KeyUp C] isQuick={_isQuickSelectMode}, hasScrolled={_hasScrolledInQuickMode}");
                    
                    if (!_isQuickSelectMode)
                    {
                        DebugLog("[KeyUp C] 忽略:不在快速模式");
                        return CallNextHookEx(_keyboardHookId, nCode, wParam, lParam);
                    }
                    
                    // 同步读取标记
                    bool shouldPaste = _hasScrolledInQuickMode;
                    _isQuickSelectMode = false;
                    _hasScrolledInQuickMode = false;
                    
                    DebugLog($"[KeyUp C] 退出快速模式, shouldPaste={shouldPaste}");
                    
                    if (shouldPaste)
                    {
                        Dispatcher.BeginInvoke(new Action(() => {
                            var item = _listBox.SelectedItem as ClipItem;
                            DebugLog($"[KeyUp C] 执行粘贴: {(item != null ? item.Text?.Substring(0, Math.Min(20, item.Text?.Length ?? 0)) : "null")}");
                            if (item != null)
                                CopyAndClose(item);
                            else
                                HideWindow();
                        }));
                    }
                    else
                    {
                        // 没有滚动,标记刚退出快速模式,防止窗口被隐藏
                        _quickModeJustExited = true;
                        Dispatcher.BeginInvoke(new Action(() => {
                            _quickModeJustExited = false;
                            // 确保窗口获得焦点
                            this.Activate();
                            ResetFocus();
                        }));
                    }
                    return (IntPtr)1;
                }
            }
            
            // 如果在快速选择模式下松开了任意修饰键
            bool isModifierKey = vkCode == VK_CONTROL || vkCode == VK_SHIFT || vkCode == VK_ALT;
            if (_isQuickSelectMode && (int)wParam == WM_KEYUP && isModifierKey)
            {
                string keyName = vkCode == VK_CONTROL ? "Ctrl" : (vkCode == VK_SHIFT ? "Shift" : "Alt");
                DebugLog($"[KeyUp {keyName}] isQuick={_isQuickSelectMode}, hasScrolled={_hasScrolledInQuickMode}");
                
                bool shouldPaste = _hasScrolledInQuickMode;
                _isQuickSelectMode = false;
                _hasScrolledInQuickMode = false;
                
                DebugLog($"[KeyUp {keyName}] 退出快速模式, shouldPaste={shouldPaste}");
                
                if (shouldPaste)
                {
                    Dispatcher.BeginInvoke(new Action(() => {
                        var item = _listBox.SelectedItem as ClipItem;
                        DebugLog($"[KeyUp {keyName}] 执行粘贴");
                        if (item != null)
                            CopyAndClose(item);
                        else
                            HideWindow();
                    }));
                }
                else
                {
                    // 没有滚动,标记刚退出快速模式,防止窗口被隐藏
                    _quickModeJustExited = true;
                    Dispatcher.BeginInvoke(new Action(() => {
                        _quickModeJustExited = false;
                        this.Activate();
                        ResetFocus();
                    }));
                }
            }
        }
        return CallNextHookEx(_keyboardHookId, nCode, wParam, lParam);
    }
    
    private IntPtr MouseHookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && (int)wParam == WM_MOUSEWHEEL)
        {
            // 只有在快速选择模式下才处理滚轮
            if (!_isQuickSelectMode)
            {
                return CallNextHookEx(_mouseHookId, nCode, wParam, lParam);
            }
            
            // 再次检查窗口是否可见
            bool isVisible = false;
            try { isVisible = Dispatcher.Invoke(() => this.IsVisible); } catch { }
            if (!isVisible)
            {
                _isQuickSelectMode = false; // 状态修正
                return CallNextHookEx(_mouseHookId, nCode, wParam, lParam);
            }
            
            DebugLog($"[MouseWheel] 滚动,设置 hasScrolled=true");
            // 获取滚轮方向
            int delta = (short)((Marshal.ReadInt32(lParam, 8) >> 16) & 0xFFFF);
            
            // 同步设置标记
            _hasScrolledInQuickMode = true;
            
            Dispatcher.BeginInvoke(new Action(() => {
                if (_listBox != null)
                {
                    int currentIndex = _listBox.SelectedIndex;
                    int newIndex = currentIndex;
                    
                    if (delta < 0) // 向下滚
                        newIndex = Math.Min(currentIndex + 1, _listBox.Items.Count - 1);
                    else // 向上滚
                        newIndex = Math.Max(currentIndex - 1, 0);
                    
                    if (newIndex != currentIndex && newIndex >= 0)
                    {
                        _listBox.SelectedIndex = newIndex;
                        _listBox.ScrollIntoView(_listBox.SelectedItem);
                    }
                }
            }));
            return (IntPtr)1; // 阻止滚轮事件传递
        }
        return CallNextHookEx(_mouseHookId, nCode, wParam, lParam);
    }
    
    public void ShowAtMousePosition(bool isMouseActivate)
    {
        Log($"[ShowAtMousePosition] 激活窗口. MouseActivate={isMouseActivate}");
        
        // --- 重置状态逻辑 ---
        bool needRefresh = false;

        // 1. 重置搜索
        if (!string.IsNullOrEmpty(_searchBox.Text))
        {
            _searchBox.Text = ""; 
            _searchText = "";     
            needRefresh = true;
        }

        // 2. 重置标签页到“全部”
        if (IsFavMode || _selectedCategory != ContentCategory.General)
        {
            _selectedCategory = ContentCategory.General;
            SwitchTab(false); // SwitchTab 内部会 Refresh
            needRefresh = false; // 避免重复刷新
        }
        
        if (needRefresh && _itemsView != null) _itemsView.Refresh();
        // ------------------

        if (needRefresh && _itemsView != null) _itemsView.Refresh();
        // ------------------

        if (!_mouseActivateAtFirstItem)
        {
            // 如果未启用鼠标跟随,则恢复原来的记忆位置逻辑
            UpdatePositionToMouse(); // 其实是 LoadPosition
            this.ShowInTaskbar = true;
            this.Show();
            this.Activate();
            if (_listBox.Items.Count > 0 && _listBox.SelectedIndex < 0) _listBox.SelectedIndex = 0;
            return;
        }

        POINT pt;
        GetCursorPos(out pt);
        
        // DPI 适配:将物理像素转换为逻辑像素
        double scaleX = 1.0, scaleY = 1.0;
        try {
            var source = PresentationSource.FromVisual(this);
            if (source != null && source.CompositionTarget != null) {
                 scaleX = source.CompositionTarget.TransformToDevice.M11;
                 scaleY = source.CompositionTarget.TransformToDevice.M22;
            }
        } catch {}

        double logicalMouseX = pt.X / scaleX;
        double logicalMouseY = pt.Y / scaleY;
        
        // 窗口显示在鼠标位置,稍微偏移避免遮挡
        this.Left = logicalMouseX + 10;
        this.Top = logicalMouseY - 100;

        Log($"[ShowAtMousePosition] 最终定位: Left={this.Left}, Top={this.Top}");
        
        // 确保不超出工作区(排除任务栏)
        var workArea = SystemParameters.WorkArea;
        if (this.Left + this.Width > workArea.Right)
            this.Left = logicalMouseX - this.Width - 10;
        if (this.Left < workArea.Left)
            this.Left = workArea.Left;
        if (this.Top < workArea.Top)
            this.Top = workArea.Top;
        if (this.Top + this.Height > workArea.Bottom)
            this.Top = workArea.Bottom - this.Height;
        
        bool wasHidden = this.Visibility != Visibility.Visible;
        this.Visibility = Visibility.Visible;
        this.Show(); 
        
        if (wasHidden)
        {
            RefreshCollectionView();
        }
        
        if (isMouseActivate)
        {
            this.Activate();
            this.Focus();
            ResetFocus();
        }
        
        if (_listBox.Items.Count > 0 && _listBox.SelectedIndex < 0)
            _listBox.SelectedIndex = 0;
            
        // 强制重绘一下,防止位置更新延迟
        // this.InvalidateVisual();
    }
    
    public void UpdateTargetWindow(IntPtr target) { _targetWindow = target; }
    
    public void ResetFocus()
    {
        if (_listBox != null) {
            _listBox.Focus();
            if (_listBox.Items.Count > 0) {
                _listBox.SelectedIndex = 0;
                var item = _listBox.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
                if(item != null) item.Focus();
            }
        }
    }
    private ToolTip _activeToolTip; // 追踪当前打开的 ToolTip
    private DispatcherTimer _tooltipTimer;
    
    private void ShowSelectedItemToolTip()
    {
        // 先关闭之前的 ToolTip
        CloseActiveToolTip();
        
        if (_listBox.SelectedIndex < 0) return;
        
        // 检查是否需要显示 ToolTip
        var item = _listBox.SelectedItem as ClipItem;
        if (item == null) return;
        
        // 文本类型:检查是否需要显示(未截断则不显示)
        // 图片类型:始终显示预览
        bool needsToolTip = (item.Type == ClipType.Image) || (item.ToolTipText != null);
        if (!needsToolTip) return;
        
        // 获取当前选中的 ListBoxItem
        var container = _listBox.ItemContainerGenerator.ContainerFromIndex(_listBox.SelectedIndex) as ListBoxItem;
        if (container == null) return;
        
        // 找到内容区域的 StackPanel
        var contentPanel = FindVisualChild<StackPanel>(container);
        if (contentPanel == null) return;
        
        // 创建 ToolTip 内容
        object tooltipContent;
        if (item.Type == ClipType.Image && !string.IsNullOrEmpty(item.PreviewImagePath))
        {
            // 图片预览
            var img = new Image 
            { 
                Source = new BitmapImage(new Uri(item.PreviewImagePath)),
                MaxWidth = 500,
                MaxHeight = 400,
                Stretch = Stretch.Uniform
            };
            tooltipContent = img;
        }
        else
        {
            // 文本预览
            tooltipContent = item.ToolTipText;
        }
        
        // 创建并显示 ToolTip
        var tooltip = new ToolTip
        {
            Content = tooltipContent,
            Placement = System.Windows.Controls.Primitives.PlacementMode.Right,
            PlacementTarget = contentPanel,
            IsOpen = true,
            MaxWidth = 500
        };
        _activeToolTip = tooltip;
        
        // 3秒后自动关闭
        _tooltipTimer?.Stop();
        _tooltipTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(3) };
        _tooltipTimer.Tick += (s, e) => { CloseActiveToolTip(); };
        _tooltipTimer.Start();
    }
    
    private void CloseActiveToolTip()
    {
        if (_activeToolTip != null)
        {
            _activeToolTip.IsOpen = false;
            _activeToolTip = null;
        }
        _tooltipTimer?.Stop();
    }
    
    private static T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
        {
            var child = VisualTreeHelper.GetChild(parent, i);
            if (child is T result) return result;
            var found = FindVisualChild<T>(child);
            if (found != null) return found;
        }
        return null;
    }

    public void HideWindow() 
    { 
        SaveWindowPosition();
        this.Hide(); 
        this.ShowInTaskbar = false;
    }

    public void Quit() 
    { 
        Log("[Quit] 用户请求退出");
        Instance = null;
        _forceClose = true; 
        CleanupResources();
        
        this.Close(); 
        // 不再主动关闭 Dispatcher,交给 Quicker 管理线程生命周期
    }

    public void UpdatePositionToMouse()
    {
        this.ShowInTaskbar = true;
        
        bool loadedFromConfig = false;
        try {
            if (File.Exists(_configFile))
            {
                var parts = File.ReadAllText(_configFile).Split(',');
                if (parts.Length == 2)
                {
                    double l = double.Parse(parts[0]);
                    double t = double.Parse(parts[1]);
                    
                    // 屏幕越界检查:必须确保窗口的大部分都在可见区域内 (使用窗口宽高进行判定)
                    var screenW = SystemParameters.VirtualScreenWidth;
                    var screenH = SystemParameters.VirtualScreenHeight;
                    var screenL = SystemParameters.VirtualScreenLeft;
                    var screenT = SystemParameters.VirtualScreenTop;

                    if (l + 100 > screenL + screenW || l < screenL ||
                        t + 100 > screenT + screenH || t < screenT)
                    {
                        Log($"[UpdatePosition] 发现坐标越界 ({l}, {t}),虚拟屏幕边界: L={screenL}, T={screenT}, W={screenW}, H={screenH}");
                    }
                    else
                    {
                        this.Left = l;
                        this.Top = t;
                        Log($"[UpdatePosition] 成功恢复记忆位置: Left={l}, Top={t}");
                        loadedFromConfig = true;
                    }
                }
            }
        } catch (Exception ex) { 
            Log($"[UpdatePosition] 配置文件读取异常: {ex.Message}"); 
        }
        
        if (!loadedFromConfig)
        {
            Log("[UpdatePosition] 设置居中显示 (Fallback)");
            this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
        }
    }

    private void SaveWindowPosition()
    {
        try {
            File.WriteAllText(_configFile, $"{this.Left},{this.Top}");
        } catch { }
    }

    private void LoadWindowPosition()
    {
        UpdatePositionToMouse();
    }
    
    private void CleanupImageCache(bool aggressive)
    {
        try {
            if (_items == null) return;
            var dir = new DirectoryInfo(_imageCacheDir);
            var activeFiles = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            foreach (var item in _items)
            {
                if (item.Type == ClipType.Image && !string.IsNullOrEmpty(item.DataValue))
                    try { activeFiles.Add(Path.GetFullPath(item.DataValue)); } catch {}
            }
            foreach (var file in dir.GetFiles()) {
                if (activeFiles.Contains(file.FullName)) continue; 
                if (aggressive || DateTime.Now - file.CreationTime > TimeSpan.FromDays(7))
                    try { file.Delete(); } catch { }
            }
        } catch { }
    }

    private string GetBitmapHash(BitmapSource source)
    {
        using (var ms = new MemoryStream())
        {
            var encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(source));
            encoder.Save(ms);
            ms.Position = 0;
            using (var md5 = MD5.Create())
            {
                var hash = md5.ComputeHash(ms);
                return BitConverter.ToString(hash).Replace("-", "").ToLower();
            }
        }
    }

    public static ImageSource GetIconFromPath(string path) {
        try {
            if (!File.Exists(path)) return null;
            using (var sysicon = System.Drawing.Icon.ExtractAssociatedIcon(path)) {
                return System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(
                    sysicon.Handle, System.Windows.Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            }
        } catch { return null; }
    }

    private string GetProcessPath(IntPtr hwnd) {
        try {
            if (hwnd == IntPtr.Zero) return null;
            uint pid;
            GetWindowThreadProcessId(hwnd, out pid);
            if (pid == 0) return null;
            var p = Process.GetProcessById((int)pid);
            if (p != null) return p.MainModule.FileName; 
        } catch { }
        return null;
    }

    private void ActivateApp(string path, IntPtr hwnd)
    {
        bool activated = false;
        if (hwnd != IntPtr.Zero && IsWindow(hwnd))
        {
            if (IsIconic(hwnd)) ShowWindow(hwnd, SW_RESTORE);
            SetForegroundWindow(hwnd);
            activated = true;
        }
        if (!activated && !string.IsNullOrEmpty(path))
        {
            try {
                var fileName = Path.GetFileNameWithoutExtension(path);
                var procs = Process.GetProcessesByName(fileName);
                foreach (var p in procs) {
                    if (p.MainWindowHandle != IntPtr.Zero) {
                        if (IsIconic(p.MainWindowHandle)) ShowWindow(p.MainWindowHandle, SW_RESTORE);
                        SetForegroundWindow(p.MainWindowHandle);
                        break;
                    }
                }
            } catch { }
        }
    }

    private void DetectSystemTheme()
    {
        try {
            using (var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize")) {
                if (key != null) {
                    object val = key.GetValue("AppsUseLightTheme");
                    if (val is int && (int)val == 0) _isDarkTheme = true;
                }
            }
        } catch { _isDarkTheme = false; }
    }

    private void LoadThemeResources()
    {
        Func<string, SolidColorBrush> hex = (h) => new SolidColorBrush((Color)ColorConverter.ConvertFromString(h));
        if (_isDarkTheme)
        {
            this.Resources["BgBrush"] = hex("#202020");
            this.Resources["BorderBrush"] = hex("#383838");
            this.Resources["CardBg"] = hex("#2D2D2D");
            this.Resources["CardHover"] = hex("#454545"); 
            this.Resources["CardSelected"] = hex("#505050");
            this.Resources["TextMain"] = hex("#E0E0E0");
            this.Resources["TextSub"] = hex("#A0A0A0");
            this.Resources["TextSearch"] = hex("#FFFFFF");
            this.Resources["SearchBg"] = hex("#2D2D2D");
            this.Resources["IconColor"] = hex("#CCCCCC");
            this.Resources["StarColor"] = hex("#FFD700");
            this.Resources["ActiveTab"] = hex("#FFFFFF");
            this.Resources["InactiveTab"] = hex("#888888");
        }
        else
        {
            this.Resources["BgBrush"] = hex("#F7F9FC");
            this.Resources["BorderBrush"] = hex("#E0E0E0");
            this.Resources["CardBg"] = hex("#FFFFFF");
            this.Resources["CardHover"] = hex("#E8F0FE"); 
            this.Resources["CardSelected"] = hex("#D0E0FD"); 
            this.Resources["TextMain"] = hex("#333333");
            this.Resources["TextSub"] = hex("#AAAAAA");
            this.Resources["TextSearch"] = hex("#333333");
            this.Resources["SearchBg"] = hex("#FFFFFF");
            this.Resources["IconColor"] = hex("#999999");
            this.Resources["StarColor"] = hex("#FFB900");
            this.Resources["ActiveTab"] = hex("#333333");
            this.Resources["InactiveTab"] = hex("#999999");
        }
    }

    private void OnWindowKeyDown(object sender, KeyEventArgs e)
    {
        Log($"[OnWindowKeyDown] 按键: {e.Key}");

        // 1. 全局 ESC 关闭
        if (e.Key == Key.Escape)
        {
            HideWindow(); 
            e.Handled = true;
            return;
        }

        // 2. 搜索框内按键处理
        if (e.OriginalSource == _searchBox)
        {
            // 在搜索框里按 Down 直接跳列表
            if (e.Key == Key.Down)
            {
                if (_listBox.Items.Count > 0) {
                    _listBox.SelectedIndex = 0;
                    _listBox.Focus();
                    _listBox.ScrollIntoView(_listBox.Items[0]);
                    var item = _listBox.ItemContainerGenerator.ContainerFromIndex(0) as ListBoxItem;
                    if(item != null) item.Focus();
                    e.Handled = true;
                }
            }
            return; 
        }

        // 3. 极速键盘导航 (WASD模式 + 空格选中)
        if (Keyboard.Modifiers == ModifierKeys.None && !(e.OriginalSource is TextBox))
        {
            bool handled = true;
            switch(e.Key)
            {
                case Key.W: // 上
                    if (_listBox.SelectedIndex > 0) {
                        _listBox.SelectedIndex--;
                        _listBox.ScrollIntoView(_listBox.SelectedItem);
                        ShowSelectedItemToolTip();
                    }
                    break;
                case Key.S: // 下
                    if (_listBox.SelectedIndex < _listBox.Items.Count - 1) {
                        _listBox.SelectedIndex++;
                        _listBox.ScrollIntoView(_listBox.SelectedItem);
                        ShowSelectedItemToolTip();
                    }
                    break;
                case Key.A: // 左翻页 (全部)
                    if (IsFavMode) SwitchTab(false);
                    break;
                case Key.D: // 右翻页 (收藏)
                    if (!IsFavMode) SwitchTab(true);
                    break;
                case Key.Space: // 空格选中
                case Key.Enter: // 回车选中
                    if (_listBox.SelectedItem is ClipItem item) CopyAndClose(item);
                    break;
                case Key.P: // P键置顶 (保留一个原快捷功能)
                    if (_listBox.SelectedItem is ClipItem pItem) {
                        pItem.IsPinned = !pItem.IsPinned;
                        MoveItemToTop(pItem);
                        SaveData();
                        if (_showFavoritesOnly && !pItem.IsFavorite) _itemsView.Refresh(); 
                    }
                    break;
                default:
                    handled = false;
                    break;
            }
            if (handled) {
                e.Handled = true;
                return;
            }
        }

        // 4. 数字键快速选择 (1-9)
        if ((e.Key >= Key.D1 && e.Key <= Key.D9) || (e.Key >= Key.NumPad1 && e.Key <= Key.NumPad9))
        {
            if (Keyboard.Modifiers == ModifierKeys.None)
            {
                int index = (e.Key >= Key.D1 && e.Key <= Key.D9) ? (e.Key - Key.D1) : (e.Key - Key.NumPad1);
                
                if (index < _listBox.Items.Count)
                {
                    var item = _listBox.Items[index] as ClipItem;
                    if (item != null)
                    {
                        CopyAndClose(item);
                        e.Handled = true;
                        return;
                    }
                }
            }
        }

        // 5. 其他功能键
        if (e.Key == Key.Q)
        {
            var item = _listBox.SelectedItem as ClipItem;
            if (item != null && item.Type == ClipType.Text)
            {
                ShowQrCode(item.Text);
                e.Handled = true;
                return;
            }
        }
        
        // Debug
        /*if (e.Key == Key.D && Keyboard.Modifiers == ModifierKeys.Control)
        {
            // ShowDebugLogs();
            e.Handled = true;
            return;
        }*/ // D键被占用了,Debug可以先移除或者换键

        if (e.Key == Key.Delete) {
            var item = _listBox.SelectedItem as ClipItem;
            if (item != null) { _items.Remove(item); UpdateIndices(); SaveData(); }
        }
    }

    private void OnWindowKeyUp(object sender, KeyEventArgs e)
    {
        // 全局钩子已经处理了快速选择模式的KeyUp,这里不需要再处理
        // 保留此方法但不做任何操作,避免重复触发
    }

    private void OnWindowMouseWheel(object sender, MouseWheelEventArgs e)
    {
        // 全局钩子已经处理了快速选择模式的滚轮,这里不需要再处理
    }

    private void OnTimerTick(object sender, EventArgs e) 
    { 
        Perf.Measure("TimerTick", () => {
            _tickCounter++;
            if (_tickCounter >= 10) 
            {
                CheckClipboardAndAdd();
                _tickCounter = 0;
            }
        }, 30); // Timer整体允许比内部稍长
    }

    private void CheckClipboardAndAdd(bool silent = false)
    {
        uint currentSeq = GetClipboardSequenceNumber();
        if (currentSeq == _lastClipboardSequenceNumber) return;

        // 调试:观察到变化
        // Log($"[Debug] 剪贴板序号变化: {_lastClipboardSequenceNumber} -> {currentSeq}");

        // 为了测试观测,将阈值降至 1ms
        Perf.Measure("CheckClipboardAndAdd", () => {
            try
            {
                if (currentSeq != _lastClipboardSequenceNumber)
                {
                    Log($"[CheckClipboardAndAdd] 检出剪贴板变化: {_lastClipboardSequenceNumber} -> {currentSeq}");
                    _lastClipboardSequenceNumber = currentSeq;
                }
                if (!Clipboard.ContainsText() && !Clipboard.ContainsImage() && !Clipboard.ContainsFileDropList()) return;

                string currentSig = "";
                
                // 测量获取数据对象耗时
                IDataObject data = null;
                Perf.Measure("Clipboard.GetDataObject", () => { data = Clipboard.GetDataObject(); }, 10);
                
                if (data == null) return;

                string text = null;
                if (data.GetDataPresent(DataFormats.UnicodeText)) text = data.GetData(DataFormats.UnicodeText) as string;

                if (!string.IsNullOrWhiteSpace(text))
                {
                    text = text.Trim();
                    currentSig = "TXT:" + text.GetHashCode() + text.Length;
                    if (currentSig == _lastClipboardSignature) return;
                    
                    if (silent) { _lastClipboardSignature = currentSig; return; }

                    if (_items.Count > 0 && _items[0].Text == text) {
                         _lastClipboardSignature = currentSig;
                         return;
                    }

                    var newItem = new ClipItem {
                        Type = ClipType.Text,
                        Text = text,
                        Timestamp = DateTime.Now,
                        SourcePath = GetProcessPath(GetForegroundWindow())
                    };
                    
                    Perf.Measure("AddNewItem_Text", () => AddNewItem(newItem));
                    _lastClipboardSignature = currentSig;
                }
                else if (Clipboard.ContainsImage())
                {
                    // 核心优化:将图像处理移至后台
                    var bmp = Clipboard.GetImage();
                    if (bmp != null)
                    {
                        // 必须 Freeze 才能跨线程访问
                        bmp.Freeze();
                        string lastSig = _lastClipboardSignature;
                        string cacheDir = _imageCacheDir;

                        Task.Run(() => {
                            Perf.Measure("ProcessImageBackground", () => {
                                try {
                                    string hash = GetBitmapHash(bmp);
                                    string currentSig = "IMG:" + hash;
                                    
                                    // 检查重复(后台检查)
                                    bool isDuplicate = false;
                                    this.Dispatcher.Invoke(() => {
                                        if (currentSig == _lastClipboardSignature) isDuplicate = true;
                                        else if (_items.Count > 0 && _items[0].Type == ClipType.Image && _items[0].ImageHash == hash) isDuplicate = true;
                                    });
                                    if (isDuplicate) return;

                                    if (silent) { 
                                        this.Dispatcher.Invoke(() => _lastClipboardSignature = currentSig); 
                                        return; 
                                    }

                                    string fileName = DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".png";
                                    string path = Path.Combine(cacheDir, fileName);
                                    
                                    Perf.Measure("SaveImageFile", () => {
                                        using (var fs = new FileStream(path, FileMode.Create))
                                        {
                                            var encoder = new PngBitmapEncoder();
                                            encoder.Frames.Add(BitmapFrame.Create(bmp));
                                            encoder.Save(fs);
                                        }
                                    }, 1);

                                    this.Dispatcher.Invoke(() => {
                                        var newItem = new ClipItem {
                                            Type = ClipType.Image,
                                            Text = "", 
                                            DataValue = path,
                                            ImageHash = hash, 
                                            Timestamp = DateTime.Now,
                                            SourcePath = GetProcessPath(GetForegroundWindow())
                                        };
                                        AddNewItem(newItem);
                                        _lastClipboardSignature = currentSig;
                                    });
                                } catch (Exception ex) {
                                    Log("Background Image Processing Error: " + ex.Message);
                                }
                            }, 15);
                        });
                    }
                }
                else if (Clipboard.ContainsFileDropList())
                {
                    var files = Clipboard.GetFileDropList();
                    if (files.Count > 0)
                    {
                        string joined = string.Join("|", files.Cast<string>());
                        currentSig = "FILE:" + joined.GetHashCode();
                        if (currentSig == _lastClipboardSignature) return;

                        if (silent) { _lastClipboardSignature = currentSig; return; }

                        var newItem = new ClipItem {
                            Type = ClipType.File,
                            Text = string.Join(", ", files.Cast<string>().Select(Path.GetFileName)), // 去掉 [文件]
                            DataValue = joined,
                            Timestamp = DateTime.Now,
                            SourcePath = GetProcessPath(GetForegroundWindow())
                        };
                        AddNewItem(newItem);
                        _lastClipboardSignature = currentSig;
                    }
                }
            }
            catch (Exception ex) 
            {
                Log("CheckClipboardAndAdd Error: " + ex.Message);
            }
        }, 15); // 恢复 15ms 阈值
    }

    private void AddNewItem(ClipItem item)
    {
        Log($"[AddNewItem] 新增项: {item.Text?.Substring(0, Math.Min(20, item.Text?.Length ?? 0))}...");
        Perf.Measure("AddNewItem_" + item.Type, () => {
            // 新条目插入到所有置顶项之后
            int insertIndex = _items.Count(x => x.IsPinned);
            _items.Insert(insertIndex, item);
            
            for (int i = insertIndex + 1; i < _items.Count; i++)
            {
                bool duplicate = false;
                if (item.Type == ClipType.Text && _items[i].Type == ClipType.Text && _items[i].Text == item.Text) duplicate = true;
                else if (item.Type == ClipType.Image && _items[i].Type == ClipType.Image && item.DataValue == _items[i].DataValue) duplicate = true;
                
                if (duplicate && !_items[i].IsPinned && !_items[i].IsFavorite)
                {
                    _items.RemoveAt(i);
                    i--;
                }
            }

            while (_items.Count > 100)
            {
                var last = _items.LastOrDefault(x => !x.IsPinned && !x.IsFavorite);
                if (last != null) _items.Remove(last);
                else break;
            }

            UpdateIndices();
            SaveData();
        }, 1);
    }

    private void LoadSettings()
    {
        try {
            if (File.Exists(_settingsFile))
            {
                var lines = File.ReadAllLines(_settingsFile);
                foreach (var line in lines)
                {
                    var parts = line.Split('=');
                    if (parts.Length != 2) continue;
                    var key = parts[0].Trim();
                    var val = parts[1].Trim();
                    
                    if (key == "HotkeyCtrl") _hotkeyCtrl = val == "1";
                    else if (key == "HotkeyShift") _hotkeyShift = val == "1";
                    else if (key == "HotkeyAlt") _hotkeyAlt = val == "1";
                    else if (key == "HotkeyKey") int.TryParse(val, out _hotkeyKey);
                    else if (key == "MouseAtFirst") _mouseActivateAtFirstItem = val == "1";
                }
            }
        } catch { }
    }
    
    private void SaveSettings()
    {
        try {
            var lines = new[] {
                $"HotkeyCtrl={(_hotkeyCtrl ? "1" : "0")}",
                $"HotkeyShift={(_hotkeyShift ? "1" : "0")}",
                $"HotkeyAlt={(_hotkeyAlt ? "1" : "0")}",
                $"HotkeyAlt={(_hotkeyAlt ? "1" : "0")}",
                $"HotkeyKey={_hotkeyKey}",
                $"MouseAtFirst={(_mouseActivateAtFirstItem ? "1" : "0")}"
            };
            File.WriteAllLines(_settingsFile, lines);
        } catch { }
    }
    
    private void ShowSettingsDialog()
    {
        _isModalOpen = true;
        
        var win = new Window {
            Title = "设置",
            Width = 350,
            Height = 250,
            WindowStartupLocation = WindowStartupLocation.CenterScreen,
            WindowStyle = WindowStyle.ToolWindow,
            ResizeMode = ResizeMode.NoResize,
            Background = (Brush)this.FindResource("BgBrush")
        };
        
        var grid = new Grid { Margin = new Thickness(20) };
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        grid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        grid.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
        
        // 快捷键标签
        var label = new TextBlock { 
            Text = "快速粘贴快捷键:", 
            Foreground = (Brush)this.FindResource("TextMain"),
            Margin = new Thickness(0, 0, 0, 10)
        };
        Grid.SetRow(label, 0);
        grid.Children.Add(label);
        
        // 修饰键选择
        var modPanel = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 0, 0, 10) };
        var chkCtrl = new CheckBox { Content = "Ctrl", IsChecked = _hotkeyCtrl, Foreground = (Brush)this.FindResource("TextMain"), Margin = new Thickness(0, 0, 15, 0) };
        var chkShift = new CheckBox { Content = "Shift", IsChecked = _hotkeyShift, Foreground = (Brush)this.FindResource("TextMain"), Margin = new Thickness(0, 0, 15, 0) };
        var chkAlt = new CheckBox { Content = "Alt", IsChecked = _hotkeyAlt, Foreground = (Brush)this.FindResource("TextMain"), Margin = new Thickness(0, 0, 15, 0) };
        modPanel.Children.Add(chkCtrl);
        modPanel.Children.Add(chkShift);
        modPanel.Children.Add(chkAlt);
        
        // 主键选择
        var keyLabel = new TextBlock { Text = " + ", Foreground = (Brush)this.FindResource("TextMain"), VerticalAlignment = VerticalAlignment.Center };
        modPanel.Children.Add(keyLabel);
        
        var keyCombo = new ComboBox { Width = 60, SelectedIndex = 0 };
        for (char c = 'A'; c <= 'Z'; c++) keyCombo.Items.Add(c.ToString());
        keyCombo.SelectedIndex = _hotkeyKey - 0x41; // A=0x41
        if (keyCombo.SelectedIndex < 0) keyCombo.SelectedIndex = 2; // 默认C
        modPanel.Children.Add(keyCombo);
        
        Grid.SetRow(modPanel, 1);
        grid.Children.Add(modPanel);
        
        // 中间面板:新选项 + 提示
        var midPanel = new StackPanel();
        
        var chkMouse = new CheckBox {
            Content = "在鼠标处激活窗口 (首项对齐鼠标)",
            IsChecked = _mouseActivateAtFirstItem,
            Foreground = (Brush)this.FindResource("TextMain"),
            Margin = new Thickness(0, 0, 0, 10),
            FontSize = 12
        };
        midPanel.Children.Add(chkMouse);
        
        // 提示
        var tip = new TextBlock {
            Text = "按住快捷键呼出窗口,滚动鼠标选择后松开即可粘贴",
            Foreground = (Brush)this.FindResource("TextSub"),
            FontSize = 11,
            TextWrapping = TextWrapping.Wrap
        };
        midPanel.Children.Add(tip);
        
        Grid.SetRow(midPanel, 2);
        grid.Children.Add(midPanel);
        
        // 按钮
        var btnPanel = new Grid { Margin = new Thickness(0, 15, 0, 0) };
        var linksPanel = new StackPanel { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
        
        var openFolder = new TextBlock { 
            Text = "数据文件夹", 
            Foreground = (Brush)this.FindResource("TextSub"),
            FontSize = 10,
            Cursor = Cursors.Hand,
            TextDecorations = TextDecorations.Underline,
            Margin = new Thickness(0, 0, 10, 0)
        };
        openFolder.MouseLeftButtonUp += (s, e) => {
            try { Process.Start("explorer.exe", _appDataPath); } catch { }
        };
        linksPanel.Children.Add(openFolder);

        var openLog = new TextBlock { 
            Text = "查看日志", 
            Foreground = (Brush)this.FindResource("TextSub"),
            FontSize = 10,
            Cursor = Cursors.Hand,
            TextDecorations = TextDecorations.Underline
        };
        openLog.MouseLeftButtonUp += (s, e) => {
            try { 
                string dir = Directory.GetCurrentDirectory();
                if (dir.ToLower().Contains("system32") || dir.ToLower().Contains("program files"))
                    dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Quicker_ClipFlow");
                string path = Path.Combine(dir, "detailed_debug.log");
                if (File.Exists(path)) Process.Start("notepad.exe", path);
                else MessageBox.Show("日志文件尚不存在,请先产生一些操作。");
            } catch { }
        };
        linksPanel.Children.Add(openLog);
        
        btnPanel.Children.Add(linksPanel);

        var actions = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = HorizontalAlignment.Right };
        var btnSave = new Button { Content = "保存", Width = 60, Margin = new Thickness(0, 0, 10, 0) };
        var btnCancel = new Button { Content = "取消", Width = 60 };
        actions.Children.Add(btnSave);
        actions.Children.Add(btnCancel);
        btnPanel.Children.Add(actions);
        
        btnSave.Click += (s, e) => {
            _hotkeyCtrl = chkCtrl.IsChecked == true;
            _hotkeyShift = chkShift.IsChecked == true;
            _hotkeyAlt = chkAlt.IsChecked == true;
            _hotkeyKey = 0x41 + keyCombo.SelectedIndex;
            _mouseActivateAtFirstItem = chkMouse.IsChecked == true;
            SaveSettings();
            win.Close();
        };
        btnCancel.Click += (s, e) => win.Close();

        Grid.SetRow(btnPanel, 3);
        grid.Children.Add(btnPanel);
        
        win.Content = grid;
        win.ShowDialog();
        _isModalOpen = false;
    }

    private void LoadData()
    {
        Perf.Measure("LoadData", () => {
            _items = new ObservableCollection<ClipItem>();
            if (File.Exists(_dataFile))
            {
                try {
                    var xs = new XmlSerializer(typeof(List<ClipItem>));
                    using (var sr = new StreamReader(_dataFile))
                    {
                        var list = (List<ClipItem>)xs.Deserialize(sr);
                        foreach (var item in list) 
                        {
                            item.UpdateCategory();
                            _items.Add(item);
                        }
                    }
                } catch { }
            }
            else { }
    
            _itemsView = CollectionViewSource.GetDefaultView(_items);
            _itemsView.Filter = ItemFilter;
            _listBox.ItemsSource = _itemsView;
            
            // 监听视图变化,自动更新序号
            if (_itemsView is INotifyCollectionChanged viewChanged)
            {
                viewChanged.CollectionChanged += (s, e) => { UpdateIndices(); };
            }
            
            UpdateIndices();
        }, 1);
        
        // 启动后异步拉取云端收藏 (延迟2秒执行,避免影响启动速度和网络拥塞)
        Task.Delay(2000).ContinueWith(t => PullFavoritesFromCloud());
    }
    
    // 拉取重试次数
    private int _pullRetryCount = 0;
    
    private void PullFavoritesFromCloud()
    {
        SetSyncState(true);
        try
        {
            Log("[CloudSync] 开始拉取云端收藏...");
            
            string json = CloudStateHelper.ReadText("ClipFlow_Favorites");
            if (string.IsNullOrEmpty(json))
            {
                // 如果是首次拉取失败(非空数据而是读取失败),尝试重试一次
                if (_pullRetryCount < 1)
                {
                    _pullRetryCount++;
                    Log("[CloudSync] 读取失败,5秒后重试...");
                    SetSyncState(false);
                    Task.Delay(5000).ContinueWith(t => PullFavoritesFromCloud());
                    return;
                }
                Log("[CloudSync] 云端无数据或读取失败");
                return;
            }
            
            var cloudItems = Newtonsoft.Json.JsonConvert.DeserializeObject<List<CloudFavoriteItem>>(json);
            if (cloudItems == null || cloudItems.Count == 0)
            {
                Log("[CloudSync] 云端收藏为空");
                return;
            }
            
            Log($"[CloudSync] 云端有 {cloudItems.Count} 条收藏");
            
            // 获取本地已有的文本集合(用于去重)
            var localTexts = new HashSet<string>();
            this.Dispatcher.Invoke(() => {
                foreach (var item in _items)
                {
                    if (item.Type == ClipType.Text && !string.IsNullOrEmpty(item.Text))
                        localTexts.Add(item.Text);
                }
            });
            
            int addedCount = 0;
            foreach (var cloudItem in cloudItems)
            {
                if (string.IsNullOrEmpty(cloudItem.Text)) continue;
                
                // 跳过本地已存在的
                if (localTexts.Contains(cloudItem.Text)) continue;
                
                // 添加到本地
                this.Dispatcher.Invoke(() => {
                    var newItem = new ClipItem
                    {
                        Text = cloudItem.Text,
                        Type = ClipType.Text,
                        IsFavorite = true,
                        IsPinned = cloudItem.IsPinned,
                        Timestamp = DateTime.TryParse(cloudItem.Time, out var dt) ? dt : DateTime.Now
                    };
                    newItem.UpdateCategory();
                    _items.Insert(0, newItem);
                });
                localTexts.Add(cloudItem.Text); // 防止重复添加
                addedCount++;
            }
            
            if (addedCount > 0)
            {
                Log($"[CloudSync] 从云端恢复 {addedCount} 条收藏,准备同步到 UI...");
                this.Dispatcher.Invoke(() => {
                    UpdateIndices();
                    if (_itemsView != null) _itemsView.Refresh();
                    SaveData(); // 保存到本地文件
                    Log($"[CloudSync] UI 同步完成,当前列表总数: {_items.Count}");
                });
            }
            else
            {
                Log("[CloudSync] 无新项目需要合并到本地");
            }
        }
        catch (Exception ex)
        {
            Log($"[CloudSync] 拉取异常: {ex.Message}");
        }
        finally
        {
            SetSyncState(false);
        }
    }
    
    // 云端收藏项的 DTO
    private class CloudFavoriteItem
    {
        public string Text { get; set; }
        public string Time { get; set; }
        public bool IsPinned { get; set; }
    }

    private void SaveData()
    {
        // 改为后台执行,避免阻塞 UI 线程 (日志显示此处耗时 20-50ms)
        var itemsCopy = _items.ToList();
        string dataFile = _dataFile;
        
        Task.Run(() => {
            lock (_saveLock) // 确保同一时间只有一个后台线程在写文件
            {
                Perf.Measure("SaveData", () => {
                    try {
                        var xs = new XmlSerializer(typeof(List<ClipItem>));
                        string tempFile = dataFile + ".tmp";
                        
                        Perf.Measure("SerializeOps", () => {
                            using (var sw = new StreamWriter(tempFile, false, System.Text.Encoding.UTF8))
                            {
                                xs.Serialize(sw, itemsCopy);
                            }
                        }, 1);
                        
                        if (File.Exists(dataFile)) File.Delete(dataFile);
                        File.Move(tempFile, dataFile);
                        Perf.LogStats("SaveDataSuccess"); // 记录保存后的资源占用
                    } catch (Exception ex) {
                        Log("SaveData Error: " + ex.Message);
                    }
                }, 1);
            }
            // 注意:云同步不在这里触发,而是在收藏状态变化时触发
        });
    }
    
    // 触发收藏云同步(防抖延迟执行,确保最终状态上传)
    private DispatcherTimer _cloudSyncTimer;
    private void TriggerFavoriteCloudSync()
    {
        Log("[CloudSync] 准备同步收藏状态...");
        if (_cloudSyncTimer == null)
        {
            _cloudSyncTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
            _cloudSyncTimer.Tick += (s, e) => {
                _cloudSyncTimer.Stop();
                var itemsCopy = _items.ToList();
                Task.Run(() => SyncFavoritesToCloud(itemsCopy));
            };
        }
        _cloudSyncTimer.Stop();
        _cloudSyncTimer.Start();
    }
    
    // 强制刷新 UI 列表显示
    public void RefreshCollectionView()
    {
        this.Dispatcher.Invoke(() => {
            UpdateIndices();
            if (_itemsView != null) {
                _itemsView.Refresh();
                Log($"[UI] 视图已刷新,当前总项数: {_items.Count}");
            }
        });
    }

    // 设置同步状态图标显示/隐藏
    private void SetSyncState(bool isSyncing)
    {
        this.Dispatcher.Invoke(() => {
            if (_syncIcon == null) return;
            
            if (isSyncing)
            {
                _syncIcon.Visibility = Visibility.Visible;
                var tag = _syncIcon.Tag as dynamic;
                if (tag != null)
                {
                    (tag.Transform as RotateTransform)?.BeginAnimation(RotateTransform.AngleProperty, tag.Animation as DoubleAnimation);
                }
            }
            else
            {
                _syncIcon.Visibility = Visibility.Collapsed;
                var tag = _syncIcon.Tag as dynamic;
                if (tag != null)
                {
                    (tag.Transform as RotateTransform)?.BeginAnimation(RotateTransform.AngleProperty, null); // 停止动画
                }
            }
        });
    }
    
    private static readonly object _cloudSyncLock = new object();
    
    private void SyncFavoritesToCloud(List<ClipItem> items)
    {
        SetSyncState(true);
        lock (_cloudSyncLock)
        {
            try
            {
                // 筛选收藏的文本项
                var favTexts = items
                    .Where(i => i.IsFavorite && i.Type == ClipType.Text && !string.IsNullOrEmpty(i.Text))
                    .Select(i => new { 
                        Text = i.Text, 
                        Time = i.Timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
                        IsPinned = i.IsPinned
                    })
                    .ToList();
                
                // 即使 favTexts 数量为 0 也要上传(表示清空了云端收藏)
                // 序列化为 JSON
                var json = Newtonsoft.Json.JsonConvert.SerializeObject(favTexts);
                Log($"[CloudSync] 开始上传 {favTexts.Count} 条收藏文本...");
                
                // 上传到云端(key 固定,不设过期)
                bool success = CloudStateHelper.SaveText("ClipFlow_Favorites", json);
                Log($"[CloudSync] 上传结果: {(success ? "成功" : "失败")}");
            }
            catch (Exception ex)
            {
                Log($"[CloudSync] 同步异常: {ex.Message}");
            }
            finally
            {
                SetSyncState(false);
            }
        }
    }

    private bool ItemFilter(object item)
    {
        var clip = item as ClipItem;
        if (clip == null) return false;

        // 收藏标签页:只显示收藏项
        // 全部标签页:隐藏所有收藏项(仅作为未归档流水账)
        bool matchTab = _showFavoritesOnly ? clip.IsFavorite : !clip.IsFavorite;

        bool matchSearch = string.IsNullOrEmpty(_searchText) || 
                           (!string.IsNullOrEmpty(clip.Text) && clip.Text.IndexOf(_searchText, StringComparison.OrdinalIgnoreCase) >= 0);
        
        bool matchCategory = _selectedCategory == ContentCategory.General || clip.Category == _selectedCategory;

        return matchTab && matchSearch && matchCategory;
    }

    private void CopyAndClose(ClipItem item)
    {
        if (item == null) return;
        Log($"[CopyAndClose] 点击粘贴. 内容: {item.Text?.Substring(0, Math.Min(20, item.Text?.Length ?? 0))}...");
        
        // 记录调用堆栈,找出是谁触发的粘贴
        var stack = Environment.StackTrace;
        DebugLog($"[CopyAndClose] 被调用,内容: {item.Text?.Substring(0, Math.Min(20, item.Text?.Length ?? 0))}");
        foreach (var line in stack.Split('\n').Skip(1).Take(5))
            DebugLog($"  {line.Trim()}");

        try
        {
            if (item.Type == ClipType.Text)
            {
                Clipboard.SetText(item.Text);
            }
            else if (item.Type == ClipType.Image && File.Exists(item.DataValue))
            {
                var img = new BitmapImage(new Uri(item.DataValue));
                Clipboard.SetImage(img);
            }
            else if (item.Type == ClipType.File)
            {
                var list = new System.Collections.Specialized.StringCollection();
                foreach(var f in item.DataValue.Split('|')) list.Add(f);
                Clipboard.SetFileDropList(list);
            }

            _lastClipboardSignature = "IGNORE_ONCE"; 
            
            // 如果是收藏项或置顶项,复制时不移动位置,保持排序
            if (!item.IsFavorite && !item.IsPinned)
            {
                _items.Remove(item);
                // 插入到所有置顶项之后
                int insertIndex = _items.Count(x => x.IsPinned);
                _items.Insert(insertIndex, item);
                UpdateIndices();
                SaveData();
            }

            // 如果没钉住窗口,则关闭/隐藏
            if (!IsPinnedMode)
            {
                HideWindow();
            }

            // 智能激活窗口逻辑
            // 如果捕获的句柄有效,且不属于 Quicker 自身(这步很难精准判断,但可以尝试激活)
            // 更好的策略是:如果是点击 Quicker 面板启动的,我们只要 HideWindow,系统通常会自动激活 Z-Order 下一层的窗口(即之前的目标窗口)
            // 所以我们只在 _targetWindow 看起来非常靠谱时才强制激活
            
            if (_targetWindow != IntPtr.Zero && IsWindow(_targetWindow))
            {
                // 尝试激活,但不阻塞
                ActivateApp(null, _targetWindow);
            }
            
            // 异步发送按键,给予窗口切换足够的缓冲时间
            new Task(async () => {
                await Task.Delay(250); // 增加延时到 250ms,确保焦点已切换
                
                // 发送 Ctrl+V
                keybd_event(17, 0, 0, UIntPtr.Zero); // Ctrl Down
                keybd_event(86, 0, 0, UIntPtr.Zero); // V Down
                keybd_event(86, 0, KEYEVENTF_KEYUP, UIntPtr.Zero); // V Up
                keybd_event(17, 0, KEYEVENTF_KEYUP, UIntPtr.Zero); // Ctrl Up
            }).Start();
        }
        catch (Exception ex)
        {
            MessageBox.Show("复制失败: " + ex.Message);
        }
    }

    private void ShowQrCode(string text)
    {
        Window qrWin = new Window
        {
            Title = "文本查看", Width = 400, Height = 300, 
            WindowStartupLocation = WindowStartupLocation.CenterScreen,
            WindowStyle = WindowStyle.ToolWindow,
            Background = (Brush)this.FindResource("CardBg")
        };
        var tb = new TextBox { 
            Text = text, TextWrapping = TextWrapping.Wrap, 
            FontSize = 14, Margin = new Thickness(10), 
            BorderThickness = new Thickness(0), Background = Brushes.Transparent,
            Foreground = (Brush)this.FindResource("TextMain"),
            IsReadOnly = true,
            VerticalScrollBarVisibility = ScrollBarVisibility.Auto
        };
        qrWin.Content = tb;
        _isModalOpen = true;
        qrWin.ShowDialog();
        _isModalOpen = false;
    }

    private TextBlock CreateTabButton(string text, bool isActive)
    {
        return new TextBlock
        {
            Text = text,
            FontSize = 13,
            FontWeight = isActive ? FontWeights.Bold : FontWeights.Normal,
            Foreground = (Brush)this.FindResource(isActive ? "ActiveTab" : "InactiveTab"),
            Cursor = Cursors.Hand,
            VerticalAlignment = VerticalAlignment.Center
        };
    }

    private void SwitchTab(bool favOnly)
    {
        _showFavoritesOnly = favOnly;
        // 更新依赖属性状态,驱动 XAML 隐藏/显示拖拽手柄
        this.IsFavMode = favOnly;
        
        if (_tabAll != null)
        {
            _tabAll.Foreground = (Brush)this.FindResource(!favOnly ? "ActiveTab" : "InactiveTab");
            _tabAll.FontWeight = !favOnly ? FontWeights.Bold : FontWeights.Normal;
        }
        
        if (_tabFav != null)
        {
            _tabFav.Foreground = (Brush)this.FindResource(favOnly ? "ActiveTab" : "InactiveTab");
            _tabFav.FontWeight = favOnly ? FontWeights.Bold : FontWeights.Normal;
        }

        if (_itemsView != null) 
        {
            _itemsView.Refresh();
        }
    }

    private object GetElementFromPoint(ListBox listbox, Point point)
    {
        UIElement element = listbox.InputHitTest(point) as UIElement;
        while (element != null)
        {
            if (element == listbox) return null;
            object item = listbox.ItemContainerGenerator.ItemFromContainer(element);
            if (!item.Equals(DependencyProperty.UnsetValue)) return item;
            element = VisualTreeHelper.GetParent(element) as UIElement;
        }
        return null;
    }

    private void ListBox_ItemClicked(object sender, MouseButtonEventArgs e)
    {
        var original = e.OriginalSource as DependencyObject;
        if (original == null) return;

        // 排除 ToggleButton (收藏/置顶按钮)
        if (GetDependencyObjectFromVisualTree(original, typeof(System.Windows.Controls.Primitives.ToggleButton)) != null) return;
        
        // 排除 DragHandle (拖拽手柄)
        if (original is FrameworkElement fe && fe.Tag?.ToString() == "DragHandle") return;
        
        // 排除 ScrollBar (由 ListBox 内部处理)
        if (GetDependencyObjectFromVisualTree(original, typeof(System.Windows.Controls.Primitives.ScrollBar)) != null) return;

        // 获取点击的 ListBoxItem
        var container = GetDependencyObjectFromVisualTree(original, typeof(ListBoxItem)) as ListBoxItem;
        if (container != null)
        {
            var item = _listBox.ItemContainerGenerator.ItemFromContainer(container) as ClipItem;
            if (item != null)
            {
                CopyAndClose(item);
            }
        }
    }
    
    private void ListBox_ItemRightClicked(object sender, MouseButtonEventArgs e)
    {
        var item = GetElementFromPoint(_listBox, e.GetPosition(_listBox)) as ClipItem;
        if (item != null)
        {
            _listBox.SelectedItem = item;
            ContextMenu cm = new ContextMenu();
            MenuItem miDel = new MenuItem { Header = "删除" };
            miDel.Click += (s, args) => { 
                _items.Remove(item); 
                UpdateIndices(); 
                SaveData(); 
            };
            
            MenuItem miFav = new MenuItem { Header = item.IsFavorite ? "取消收藏" : "收藏" };
            miFav.Click += (s, args) => { item.IsFavorite = !item.IsFavorite; SaveData(); TriggerFavoriteCloudSync(); };

            cm.Items.Add(miFav);
            cm.Items.Add(miDel);
            cm.IsOpen = true;
        }
    }

    private void ListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (e.OriginalSource is DependencyObject obj)
        {
            var parent = obj;
            bool isHandle = false;
            // 只要点击的是左侧序号区域或右侧拖动柄,就触发拖拽
            while (parent != null && !(parent is ListBoxItem))
            {
                if (parent is FrameworkElement fe)
                {
                    if (fe.Tag != null && fe.Tag.ToString() == "DragHandle")
                    {
                        isHandle = true;
                        break;
                    }
                }
                parent = VisualTreeHelper.GetParent(parent);
            }

            if (isHandle)
            {
                var itemContainer = GetDependencyObjectFromVisualTree(obj, typeof(ListBoxItem)) as ListBoxItem;
                if (itemContainer != null)
                {
                    var item = _listBox.ItemContainerGenerator.ItemFromContainer(itemContainer) as ClipItem;
                    if (item != null)
                    {
                        var data = new DataObject();
                        // 内部数据:用于在收藏夹内排序
                        data.SetData(typeof(ClipItem), item);
                        
                        // 外部数据:用于拖拽到其他软件粘贴
                        if (item.Type == ClipType.Text) {
                            data.SetData(DataFormats.UnicodeText, item.Text);
                            data.SetData(DataFormats.Text, item.Text); // 增加标准文本格式作为兜底
                        } else if (item.Type == ClipType.Image || item.Type == ClipType.File) {
                            var files = new System.Collections.Specialized.StringCollection();
                            if (item.Type == ClipType.Image) {
                                if (File.Exists(item.DataValue)) files.Add(Path.GetFullPath(item.DataValue));
                            } else {
                                foreach(var f in item.DataValue.Split('|')) {
                                    if (File.Exists(f)) files.Add(Path.GetFullPath(f));
                                }
                            }
                            if (files.Count > 0) {
                                data.SetFileDropList(files);
                                // 为记事本等纯文本编辑器增加路径文本输出,防止拒绝拖拽
                                string pathsText = string.Join(Environment.NewLine, files.Cast<string>());
                                data.SetData(DataFormats.UnicodeText, pathsText);
                                data.SetData(DataFormats.Text, pathsText);
                            }
                        }
                        
                        DragDrop.DoDragDrop(itemContainer, data, DragDropEffects.Copy | DragDropEffects.Move);
                        e.Handled = true;
                    }
                }
            }
        }
    }
    
    private DependencyObject GetDependencyObjectFromVisualTree(DependencyObject startObject, Type type)
    {
        var parent = startObject;
        while (parent != null)
        {
            if (type.IsInstanceOfType(parent)) return parent;
            parent = VisualTreeHelper.GetParent(parent);
        }
        return null;
    }

    private void ListBox_Drop(object sender, DragEventArgs e)
    {
        // 内部排序只在收藏夹模式下生效
        if (!_showFavoritesOnly) return;
        
        var droppedData = e.Data.GetData(typeof(ClipItem)) as ClipItem;
        var target = GetElementFromPoint(_listBox, e.GetPosition(_listBox)) as ClipItem;

        if (droppedData != null && target != null && droppedData != target)
        {
            int oldIdx = _items.IndexOf(droppedData);
            int newIdx = _items.IndexOf(target);

            if (oldIdx != -1 && newIdx != -1)
            {
                _items.Move(oldIdx, newIdx);
                UpdateIndices();
                SaveData();
            }
        }
    }

    private void OnToggleButtonClick(object sender, RoutedEventArgs e)
    {
        var btn = e.OriginalSource as System.Windows.Controls.Primitives.ToggleButton;
        if (btn != null)
        {
            var item = btn.DataContext as ClipItem;
            if (item != null)
            {
                string tag = btn.Tag as string;
                if (tag == "FavBtn")
                {
                    // 收藏按钮点击
                    Log($"[UI] 收藏按钮点击: IsFavorite={btn.IsChecked}");
                    item.IsFavorite = btn.IsChecked == true;
                    // 收藏状态改变会导致 item 进出当前 Filter,需要刷新视图
                    _itemsView.Refresh(); 
                    SaveData();
                    TriggerFavoriteCloudSync(); // 收藏变化时触发云同步
                }
                else if (tag == "PinBtn")
                {
                    // 置顶按钮点击
                    Log($"[UI] 置顶按钮点击: IsPinned={btn.IsChecked}");
                    MoveItemToTop(item);
                    SaveData();
                }
            }
        }
        e.Handled = true; 
    }

    private void OnWindowDeactivated(object sender, EventArgs e) 
    { 
        if (_isModalOpen) return;
        if (IsPinnedMode) return;
        if (_isQuickSelectMode) return;
        if (_quickModeJustExited) return;
        
        // 增加保护:如果窗口刚显示不到 1000ms,忽略失焦隐藏
        // 解决因激活瞬间焦點争夺导致的闪退
        var duration = (DateTime.Now - _lastShowTime).TotalMilliseconds;
        if (this.Visibility == Visibility.Visible && duration < 1000) return;

        try { 
            if (this.OwnedWindows.Count == 0) HideWindow(); 
        } catch {} 
    }
    
    private void OnDeactivated(object sender, EventArgs e)
    {
        OnWindowDeactivated(sender, e);
    }
    
    private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e) {
        if (!_forceClose) 
        {
            e.Cancel = true; 
            HideWindow();    
        }
        else 
        {
            CleanupResources();
        }
    }
    
    private void CleanupResources()
    {
        try {
            SaveData();
            SaveWindowPosition();
        } catch { }
        
        try {
            if (_monitorTimer != null) 
            {
                _monitorTimer.Stop();
                _monitorTimer = null;
            }
        } catch { }
        
        try {
            if (_searchTimer != null)
            {
                _searchTimer.Stop();
                _searchTimer = null;
            }
        } catch { }
        
        try {
            if (_tooltipTimer != null)
            {
                _tooltipTimer.Stop();
                _tooltipTimer = null;
            }
        } catch { }
        
        try {
            UninstallGlobalHooks();
        } catch { }
    }
    
    ~ClipboardHistoryWindow()
    {
        // 析构函数作为保险,确保钩子被卸载
        try { UninstallGlobalHooks(); } catch { }
    }

    private void UpdateIndices()
    {
        // 先将所有项的序号置空
        foreach (var item in _items) item.Index = 0;

        int idx = 1;
        // 仅对当前视图中可见的项进行编号
        if (_itemsView != null)
        {
             foreach (var item in _itemsView)
             {
                 if (item is ClipItem clip)
                 {
                     clip.Index = idx++;
                 }
             }
        }
        else
        {
             // Fallback: 如果视图未初始化,则按过滤逻辑模拟编号
             foreach (var item in _items)
             {
                 if (ItemFilter(item))
                 {
                     item.Index = idx++;
                 }
             }
        }
    }
    
    private void MoveItemToTop(ClipItem item)
    {
        if (item == null) return;
        
        int oldIndex = _items.IndexOf(item);
        if (oldIndex < 0) return;
        
        int newIndex = 0;
        if (item.IsPinned) 
        {
            newIndex = 0;
        }
        else
        {
            newIndex = _items.Count(x => x.IsPinned && x != item);
        }
        
        if (oldIndex != newIndex)
        {
            _items.Move(oldIndex, newIndex);
        }
        
        UpdateIndices();
    }

    private void InitializeUI()
    {
        var outerBorder = new Border
        {
            Background = (Brush)this.FindResource("BgBrush"),
            CornerRadius = new CornerRadius(12),
            BorderBrush = (Brush)this.FindResource("BorderBrush"),
            BorderThickness = new Thickness(1),
            Padding = new Thickness(0)
        };
        outerBorder.Effect = new DropShadowEffect { BlurRadius = 25, ShadowDepth = 8, Opacity = 0.4, Color = Colors.Black };

        var mainGrid = new Grid();
        mainGrid.Margin = new Thickness(15, 10, 15, 10);
        mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(45) }); 
        mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(35) }); 
        mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        mainGrid.RowDefinitions.Add(new RowDefinition { Height = new GridLength(25) });

        // Header
        var topBar = new Grid();
        topBar.Background = Brushes.Transparent; 
        
        topBar.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }); 
        topBar.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); 
        topBar.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); 

        var tabsPanel = new StackPanel { Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center };
        // 移除阻塞,允许子控件接收点击事件

        // 全部按钮
        _tabAll = CreateTabButton("全部", true);
        _tabAll.FontSize = 13;
        _tabAll.FontWeight = FontWeights.Bold;
        _tabAll.MouseLeftButtonUp += (s, e) => { 
            _selectedCategory = ContentCategory.General;
            SwitchTab(false); 
        };
        tabsPanel.Children.Add(_tabAll);

        // 分类下拉图标
        var catArrow = new TextBlock {
            Text = " ▾",
            FontSize = 14,
            Foreground = (Brush)this.FindResource("InactiveTab"),
            Cursor = Cursors.Hand,
            VerticalAlignment = VerticalAlignment.Center,
            Margin = new Thickness(2, 0, 10, 0)
        };
        catArrow.MouseLeftButtonUp += (s, e) => {
            ContextMenu menu = new ContextMenu();
            string[] names = { "全部历史", " 链接", " 邮箱", " 电话", " 代码", " 结构化", " 颜色", " 路径", " 时间", " IP", " 文件", "️ 图片" };
            for (int i = 0; i < names.Length; i++) {
                int idx = i;
                MenuItem mi = new MenuItem { Header = names[i] };
                mi.Click += (ms, me) => {
                    if (idx <= 0) _selectedCategory = ContentCategory.General;
                    else _selectedCategory = (ContentCategory)idx;
                    
                    // 切换类别时自动回到“全部”标签
                    SwitchTab(false);
                };
                menu.Items.Add(mi);
            }
            menu.IsOpen = true;
        };
        tabsPanel.Children.Add(catArrow);
        
        tabsPanel.Children.Add(new Border { Width = 1, Height = 14, Background = (Brush)this.FindResource("IconColor"), Margin = new Thickness(0, 0, 10, 0), Opacity = 0.5 });

        _tabFav = CreateTabButton("★ 收藏", false);
        _tabFav.FontSize = 13;
        _tabFav.FontWeight = FontWeights.Normal;
        _tabFav.FontWeight = FontWeights.Normal;
        _tabFav.MouseLeftButtonUp += (s, e) => { SwitchTab(true); };
        tabsPanel.Children.Add(_tabFav);

        // 同步状态图标 (默认隐藏)
        _syncIcon = new TextBlock {
            Text = "⏳", // 或者使用 ↻
            FontSize = 12,
            Foreground = (Brush)this.FindResource("TextSub"),
            Margin = new Thickness(8, 0, 0, 0),
            VerticalAlignment = VerticalAlignment.Center,
            Visibility = Visibility.Collapsed,
            ToolTip = "正在同步云端数据..."
        };
        // 定义旋转动画
        var rotateTransform = new RotateTransform(0, 5, 6); // 中心点大约在字符中心
        _syncIcon.RenderTransform = rotateTransform;
        var anim = new DoubleAnimation(0, 360, TimeSpan.FromSeconds(1));
        anim.RepeatBehavior = RepeatBehavior.Forever;
        
        // 存储动画以便按需启动
        _syncIcon.Tag = new { Transform = rotateTransform, Animation = anim };

        tabsPanel.Children.Add(_syncIcon);

        Grid.SetColumn(tabsPanel, 0);
        topBar.Children.Add(tabsPanel);

        // 修改:右上角替换为“钉住”按钮
        _pinWindowBtn = new System.Windows.Controls.Primitives.ToggleButton
        { 
            Content = "", Width = 30, Height = 26, FontSize = 12, 
            HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center,
            Background = Brushes.Transparent, BorderThickness = new Thickness(0), Cursor = Cursors.Hand, 
            Foreground = (Brush)this.FindResource("IconColor"),
            Margin = new Thickness(0, 0, 10, 0),
            ToolTip = "钉住窗口:失去焦点时不自动隐藏"
        };
        _pinWindowBtn.Checked += (s, e) => { this.Topmost = true; _pinWindowBtn.Foreground = (Brush)this.FindResource("StarColor"); };
        _pinWindowBtn.Unchecked += (s, e) => { this.Topmost = false; _pinWindowBtn.Foreground = (Brush)this.FindResource("IconColor"); };

        Grid.SetColumn(_pinWindowBtn, 1);
        topBar.Children.Add(_pinWindowBtn);

        // 关闭按钮
        var closeBtn = new Button 
        { 
            Content = "✕", Width = 40, Height = 40, FontSize = 18, FontWeight = FontWeights.Bold,
            HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center,
            Background = Brushes.Transparent, BorderThickness = new Thickness(0), Cursor = Cursors.Hand, 
            Foreground = (Brush)this.FindResource("IconColor"),
            Margin = new Thickness(0, 0, -5, 0)
        };
        closeBtn.Click += new RoutedEventHandler((s,e) => { HideWindow(); }); 

        Grid.SetColumn(closeBtn, 2); 
        topBar.Children.Add(closeBtn);
        Grid.SetRow(topBar, 0);
        mainGrid.Children.Add(topBar);

        var searchBorder = new Border {
            Background = (Brush)this.FindResource("SearchBg"), 
            CornerRadius = new CornerRadius(6),
            BorderBrush = (Brush)this.FindResource("BorderBrush"), 
            BorderThickness = new Thickness(1),
            Padding = new Thickness(5,0,5,0),
            Height = 28,
            Margin = new Thickness(2, 0, 2, 8)
        };
        
        _searchBox = new TextBox {
            BorderThickness = new Thickness(0), Background = Brushes.Transparent,
            VerticalContentAlignment = VerticalAlignment.Center, FontSize = 12,
            Foreground = (Brush)this.FindResource("TextSearch"),
            CaretBrush = (Brush)this.FindResource("TextSearch"),
            Height = 26,
            Tag = "搜索内容..."
        };
        _searchBox.TextChanged += (s, e) => {
            if (_searchTimer == null)
            {
                _searchTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
                _searchTimer.Tick += (st, se) => {
                    _searchTimer.Stop();
                    _searchText = _searchBox.Text;
                    if (_itemsView != null) _itemsView.Refresh();
                };
            }
            _searchTimer.Stop();
            _searchTimer.Start();
        };

        searchBorder.Child = _searchBox;
        Grid.SetRow(searchBorder, 1);
        mainGrid.Children.Add(searchBorder);

        // --- List Box Config ---
        _listBox = new ListBox();
        _listBox.Background = Brushes.Transparent;
        _listBox.BorderThickness = new Thickness(0);
        _listBox.Padding = new Thickness(2);
        _listBox.Style = null;
        _listBox.Focusable = true;
        
        string itemStyleXaml = @"
        <Style TargetType='ListBoxItem' 
               xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
               xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
            <Setter Property='Background' Value='Transparent'/>
            <Setter Property='BorderThickness' Value='0'/>
            <Setter Property='Margin' Value='2,1,2,1'/>
            <Setter Property='Padding' Value='0'/>
            <Setter Property='FocusVisualStyle' Value='{x:Null}'/>
            <Setter Property='HorizontalContentAlignment' Value='Stretch'/>
            <Setter Property='Template'>
                <Setter.Value>
                    <ControlTemplate TargetType='ListBoxItem'>
                        <Border x:Name='Bd' Background='{TemplateBinding Background}' 
                                CornerRadius='10' SnapsToDevicePixels='true' Margin='1,0,1,0'
                                BorderThickness='0' BorderBrush='Transparent'>
                            <ContentPresenter HorizontalAlignment='{TemplateBinding HorizontalContentAlignment}' 
                                              VerticalAlignment='{TemplateBinding VerticalContentAlignment}'/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property='IsSelected' Value='true'>
                                <Setter TargetName='Bd' Property='Background' Value='#1e293b'/>
                            </Trigger>
                            <Trigger Property='IsMouseOver' Value='true'>
                                <Setter TargetName='Bd' Property='Background' Value='#2d2d2d'/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property='IsSelected' Value='true'/>
                                    <Condition Property='IsMouseOver' Value='true'/>
                                </MultiTrigger.Conditions>
                                <Setter TargetName='Bd' Property='Background' Value='#1e293b'/>
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>";
        try {
            _listBox.ItemContainerStyle = (Style)System.Windows.Markup.XamlReader.Parse(itemStyleXaml);
        } catch (Exception ex) { 
            DebugLog("Style Parse Error: " + ex.Message); 
        }
        
        _listBox.HorizontalContentAlignment = HorizontalAlignment.Stretch;
        
        // 性能优化:启用 UI 虚拟化
        VirtualizingPanel.SetIsVirtualizing(_listBox, true);
        VirtualizingPanel.SetVirtualizationMode(_listBox, VirtualizationMode.Recycling);
        VirtualizingPanel.SetScrollUnit(_listBox, ScrollUnit.Pixel);
        _listBox.ItemsPanel = new ItemsPanelTemplate(new FrameworkElementFactory(typeof(VirtualizingStackPanel)));

        ScrollViewer.SetHorizontalScrollBarVisibility(_listBox, ScrollBarVisibility.Disabled);
        ScrollViewer.SetVerticalScrollBarVisibility(_listBox, ScrollBarVisibility.Hidden);

        // 数据模板
        string dataTemplateXaml = @"
        <DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
                      xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
            <Grid Margin='0,6,12,6' Background='Transparent'>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width='40'/> <!-- Index -->
                    <ColumnDefinition Width='Auto'/> <!-- Icon Box -->
                    <ColumnDefinition Width='*'/>  <!-- Content -->
                    <ColumnDefinition Width='Auto'/> <!-- Actions -->
                </Grid.ColumnDefinitions>
                
                <!-- Col 0: Index & Pin -->
                <Grid Grid.Column='0' HorizontalAlignment='Center' VerticalAlignment='Center'>
                    <TextBlock Text='{Binding IndexDisplay}' 
                               VerticalAlignment='Center' HorizontalAlignment='Center' 
                               Foreground='#999999' FontSize='14' Name='IndexTxt' FontFamily='Consolas'/>
                    <ToggleButton Name='PinBtn' IsChecked='{Binding IsPinned}' 
                                  Tag='PinBtn'
                                  Cursor='Hand' Width='30' Height='30'
                                  HorizontalAlignment='Stretch' VerticalAlignment='Stretch' 
                                  BorderThickness='0' Background='Transparent' Opacity='0'>
                        <ToggleButton.Template>
                            <ControlTemplate TargetType='ToggleButton'>
                                <Border Background='Transparent' Name='Bg'>
                                    <Path Name='PinImg' Width='14' Height='14' Stretch='Uniform' Fill='White' Visibility='Collapsed'
                                          Data='M16,12V4H17V2H7V4H8V12L6,14V16H11.2V22L12,22.8L12.8,22V16H18V14L16,12Z'/>
                                </Border>
                                <ControlTemplate.Triggers>
                                    <Trigger Property='IsMouseOver' Value='True'>
                                        <Setter TargetName='PinImg' Property='Visibility' Value='Visible'/>
                                    </Trigger>
                                    <Trigger Property='IsChecked' Value='True'>
                                        <Setter TargetName='PinImg' Property='Visibility' Value='Visible'/>
                                        <Setter TargetName='PinImg' Property='Fill' Value='#4CAF50'/>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </ToggleButton.Template>
                    </ToggleButton>
                </Grid>
                
                <!-- Col 1: Icon Box (Rounded Square) -->
                <Grid Grid.Column='1' VerticalAlignment='Center' HorizontalAlignment='Left' 
                      Visibility='{Binding CategoryVisibility}' Margin='0,0,12,0'>
                     <Border Width='34' Height='34' CornerRadius='8' Background='#1a1a1a' BorderBrush='#2d2d2d' BorderThickness='1'>
                         <TextBlock Text='{Binding CategoryIcon}' FontSize='12' Foreground='{Binding CategoryColor}' 
                                    VerticalAlignment='Center' HorizontalAlignment='Center'/>
                     </Border>
                </Grid>

                <!-- Col 2: Content -->
                <StackPanel Grid.Column='2' VerticalAlignment='Center' Margin='4,0,0,0'
                            ToolTipService.InitialShowDelay='200' 
                            ToolTipService.BetweenShowDelay='100'
                            ToolTipService.ShowDuration='30000'
                            ToolTip='{Binding ToolTipText}'>

                    <StackPanel Orientation='Horizontal' VerticalAlignment='Center'>
                        <TextBlock Text='{Binding Text}' Visibility='{Binding TextVisibility}' 
                                   FontSize='13' FontFamily='Microsoft YaHei UI' TextTrimming='CharacterEllipsis' 
                                   MaxHeight='54' TextWrapping='Wrap' Foreground='{DynamicResource TextMain}' 
                                   LineHeight='18' VerticalAlignment='Center'/>
                    </StackPanel>
                    
                    <Border CornerRadius='4' Background='Transparent' HorizontalAlignment='Left' 
                            Visibility='{Binding ImageVisibility}' Margin='0,4,0,0'
                            ToolTipService.InitialShowDelay='200'>
                        <Border.ToolTip>
                            <ToolTip Placement='Right'>
                                <Image Source='{Binding PreviewImagePath}' MaxWidth='500' MaxHeight='400' Stretch='Uniform'/>
                            </ToolTip>
                        </Border.ToolTip>
                        <Image Source='{Binding Thumbnail}' Height='50' Stretch='Uniform'/>
                    </Border>
                </StackPanel>

                <!-- Col 3: Actions & Time (Hover states) -->
                <Grid Grid.Column='3' VerticalAlignment='Center' HorizontalAlignment='Right'>
                    <TextBlock Name='TimeText' Text='{Binding FriendlyTime}' FontSize='9' Foreground='#999999' Margin='0,0,2,0'/>
                    <StackPanel Name='HoverBtns' Orientation='Horizontal' Visibility='Collapsed'>
                         <ToggleButton Name='FavBtn' IsChecked='{Binding IsFavorite}' Cursor='Hand' 
                                       Tag='FavBtn'
                                       Background='Transparent' BorderThickness='0' Width='20' Height='20' Margin='0,0,5,0'>
                             <ToggleButton.Template>
                                 <ControlTemplate TargetType='ToggleButton'>
                                     <Path Name='Star' Width='14' Height='14' Stretch='Uniform' Fill='#444444' Data='M12,17.27L18.18,21L16.54,13.97L22,9.24L14.81,8.62L12,2L9.19,8.62L2,9.24L7.45,13.97L5.82,21L12,17.27Z'/>
                                     <ControlTemplate.Triggers>
                                         <Trigger Property='IsChecked' Value='True'>
                                            <Setter TargetName='Star' Property='Fill' Value='#facc15'/>
                                         </Trigger>
                                     </ControlTemplate.Triggers>
                                 </ControlTemplate>
                             </ToggleButton.Template>
                         </ToggleButton>
                         <Border Width='20' Height='20' Background='Transparent' Cursor='SizeNS' Name='DragHandle' Tag='DragHandle' ToolTip='按住排序'>
                             <TextBlock Text='⋮⋮' FontSize='12' Foreground='#444444' VerticalAlignment='Center' HorizontalAlignment='Center'/>
                         </Border>
                    </StackPanel>
                </Grid>
            </Grid>
            <DataTemplate.Triggers>
                <!-- Selection Trigger -->
                <DataTrigger Binding='{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsSelected}' Value='True'>
                    <Setter TargetName='IndexTxt' Property='Foreground' Value='#3b82f6'/>
                    <Setter TargetName='TimeText' Property='Foreground' Value='#3b82f6'/>
                </DataTrigger>
                
                <DataTrigger Binding='{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}, Path=IsMouseOver}' Value='True'>
                    <Setter TargetName='TimeText' Property='Visibility' Value='Collapsed'/>
                    <Setter TargetName='HoverBtns' Property='Visibility' Value='Visible'/>
                    <Setter TargetName='PinBtn' Property='Opacity' Value='1'/>
                </DataTrigger>
                
                <Trigger SourceName='PinBtn' Property='IsMouseOver' Value='True'>
                    <Setter TargetName='IndexTxt' Property='Visibility' Value='Collapsed'/>
                </Trigger>
                
                <DataTrigger Binding='{Binding IsPinned}' Value='True'>
                    <Setter TargetName='IndexTxt' Property='Visibility' Value='Collapsed'/>
                    <Setter TargetName='PinBtn' Property='Opacity' Value='1'/>
                </DataTrigger>
                <DataTrigger Binding='{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=IsFavMode}' Value='False'>
                    <Setter TargetName='DragHandle' Property='Visibility' Value='Collapsed'/>
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>";



        try {
            dataTemplateXaml = dataTemplateXaml.Replace("Click='OnPinClick'", ""); 
            _listBox.ItemTemplate = (DataTemplate)XamlReader.Parse(dataTemplateXaml);
        } catch (Exception ex) { MessageBox.Show("模板错误: " + ex.Message); }

        _listBox.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(ListBox_ItemClicked);
        _listBox.PreviewMouseRightButtonUp += new MouseButtonEventHandler(ListBox_ItemRightClicked);
        _listBox.PreviewMouseDown += (s, e) => {
            if (e.ChangedButton == MouseButton.Middle) {
                var item = GetElementFromPoint(_listBox, e.GetPosition(_listBox)) as ClipItem;
                if (item != null) { _items.Remove(item); UpdateIndices(); SaveData(); }
            }
        };
        
        _listBox.PreviewMouseLeftButtonDown += ListBox_PreviewMouseLeftButtonDown;
        _listBox.Drop += ListBox_Drop;
        _listBox.AllowDrop = true;

        _listBox.AddHandler(System.Windows.Controls.Primitives.ToggleButton.ClickEvent, new RoutedEventHandler(OnToggleButtonClick));

        Grid.SetRow(_listBox, 2);
        mainGrid.Children.Add(_listBox);

        // Footer
        var footerGrid = new Grid();
        footerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
        footerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        footerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
        footerGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto }); 

        var footerTip = new TextBlock { 
            Text = "左键:复制 · 右键:菜单 · Q:查看 · 中键:删除", 
            FontSize = 9, Foreground = (Brush)this.FindResource("TextSub"), 
            VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(5,0,0,0) 
        };
        Grid.SetColumn(footerTip, 0);
        footerGrid.Children.Add(footerTip);

        var settingsBtn = new TextBlock {
            Text = "设置", FontSize = 10, Foreground = (Brush)this.FindResource("TextSub"),
            Cursor = Cursors.Hand, TextDecorations = TextDecorations.Underline,
            VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0,0,10,0)
        };
        settingsBtn.MouseLeftButtonUp += (s, e) => { ShowSettingsDialog(); };
        Grid.SetColumn(settingsBtn, 1);
        footerGrid.Children.Add(settingsBtn);

        var clearBtn = new TextBlock {
            Text = "清空", FontSize = 10, Foreground = (Brush)this.FindResource("TextSub"),
            Cursor = Cursors.Hand, TextDecorations = TextDecorations.Underline,
            VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0,0,10,0)
        };
        clearBtn.MouseLeftButtonUp += (s, e) => {
            if (MessageBox.Show("确定清空所有非收藏记录吗?", "确认", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                var keep = _items.Where(x => x.IsFavorite || x.IsPinned).ToList();
                _items.Clear();
                foreach(var k in keep) _items.Add(k);
                
                // 修复:不要重置签名,保持之前的指纹,防止清空后立刻被监控补回相同内容
                
                UpdateIndices();
                SaveData();
                CleanupImageCache(true); 
                
                if (_itemsView != null) _itemsView.Refresh();
            }
        };
        Grid.SetColumn(clearBtn, 2);
        footerGrid.Children.Add(clearBtn);

        var exitBtn = new TextBlock {
            Text = "退出", FontSize = 10, Foreground = (Brush)this.FindResource("TextSub"),
            Cursor = Cursors.Hand, TextDecorations = TextDecorations.Underline,
            VerticalAlignment = VerticalAlignment.Center, Margin = new Thickness(0,0,5,0)
        };
        exitBtn.MouseLeftButtonUp += (s, e) => {
            if (MessageBox.Show("确定要退出剪贴板历史工具吗?\n退出后将不再监听剪贴板。", "退出", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
            {
                Quit();
            }
        };
        Grid.SetColumn(exitBtn, 3);
        footerGrid.Children.Add(exitBtn);

        Grid.SetRow(footerGrid, 3);
        mainGrid.Children.Add(footerGrid);

        outerBorder.Child = mainGrid;
        this.Content = outerBorder;
    }
}

// CloudState Helper (通过反射调用 Quicker 内置云同步 API)
// ---------------------------------------------------------
static class CloudStateHelper 
{
    private static Type _cloudStateType;
    private static object _instance;
    private static System.Reflection.MethodInfo _saveMethod, _readMethod;
    private static bool _initialized = false;
    private static string _initError = null;

    static CloudStateHelper() { Init(); }

    static void Init()
    {
        if (_initialized) return;
        _initialized = true;

        try
        {
            // 在所有已加载的程序集中查找 CloudState 类型
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                _cloudStateType = assembly.GetType("Quicker.Public.CloudState");
                if (_cloudStateType != null) break;
            }
            
            if (_cloudStateType == null) 
            {
                _initError = "未找到 CloudState 类型";
                return;
            }

            // 获取单例实例(通过内部字段)
            var instanceField = _cloudStateType.GetField("OWJCWqTrDY4Wyj5Oc7t3", 
                System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public);
            
            if (instanceField != null)
                _instance = instanceField.GetValue(null);
            
            if (_instance == null)
            {
                _instance = Activator.CreateInstance(_cloudStateType, true);
                if (instanceField != null)
                    try { instanceField.SetValue(null, _instance); } catch { }
            }
            
            if (_instance == null)
            {
                _initError = "无法获取 CloudState 实例";
                return;
            }

            // 获取方法引用
            _saveMethod = _cloudStateType.GetMethod("SaveTextAsync", new[] { typeof(string), typeof(string), typeof(double) });
            _readMethod = _cloudStateType.GetMethod("ReadTextAsync", new[] { typeof(string), typeof(double) });
            
            if (_saveMethod == null) _initError = "未找到 SaveTextAsync 方法";
            else if (_readMethod == null) _initError = "未找到 ReadTextAsync 方法";
        }
        catch (Exception ex)
        {
            _initError = $"初始化异常: {ex.Message}";
        }
    }

    public static bool SaveText(string key, string value, double expireMinutes = 525600)
    {
        if (_instance == null || _saveMethod == null) { Init(); }
        
        if (_instance == null || _saveMethod == null) 
        {
            ClipboardHistoryWindow.Log($"[CloudSync] SaveText 初始化失败: {_initError}");
            return false;
        }

        string safeValue = value ?? "";
        ClipboardHistoryWindow.Log($"[CloudSync] 调用 SaveTextAsync: key={key}, len={safeValue.Length}, expire={expireMinutes}");
        
        try
        {
            var task = (System.Threading.Tasks.Task)_saveMethod.Invoke(_instance, new object[] { key, safeValue, expireMinutes });
            bool completed = task.Wait(10000); // 等待最多10秒
            
            if (task.IsFaulted)
            {
                var innerEx = task.Exception?.InnerException ?? task.Exception;
                ClipboardHistoryWindow.Log($"[CloudSync] SaveText 任务失败: {innerEx?.Message}");
                return false;
            }
            
            if (!task.IsCompleted)
            {
                ClipboardHistoryWindow.Log("[CloudSync] SaveText 超时");
                return false;
            }
            
            ClipboardHistoryWindow.Log("[CloudSync] SaveText 成功");
            return true;
        }
        catch (Exception ex)
        {
            var innerEx = ex.InnerException ?? ex;
            ClipboardHistoryWindow.Log($"[CloudSync] SaveText 异常: {innerEx.Message}");
            return false;
        }
    }

    public static string ReadText(string key, double expireMinutes = 525600)
    {
        if (_instance == null || _readMethod == null) { Init(); }
        
        if (_instance == null || _readMethod == null) 
        {
            ClipboardHistoryWindow.Log($"[CloudSync] ReadText 初始化失败: {_initError}");
            return null;
        }

        ClipboardHistoryWindow.Log($"[CloudSync] 调用 ReadTextAsync: key={key}");
        
        try
        {
            var task = (System.Threading.Tasks.Task)_readMethod.Invoke(_instance, new object[] { key, expireMinutes });
            task.Wait(30000); // 增加超时到30秒
            
            if (task.IsFaulted)
            {
                var innerEx = task.Exception?.InnerException ?? task.Exception;
                ClipboardHistoryWindow.Log($"[CloudSync] ReadText 任务失败: {innerEx?.Message}");
                return null;
            }
            
            if (task.IsCompleted)
            {
                var result = task.GetType().GetProperty("Result")?.GetValue(task) as string;
                ClipboardHistoryWindow.Log($"[CloudSync] ReadText 成功, len={result?.Length ?? 0}");
                return result;
            }
            
            ClipboardHistoryWindow.Log("[CloudSync] ReadText 超时");
            return null;
        }
        catch (Exception ex)
        {
            var innerEx = ex.InnerException ?? ex;
            ClipboardHistoryWindow.Log($"[CloudSync] ReadText 异常: {innerEx.Message}");
            return null;
        }
    }
}

动作配置 (JSON)

{
  "ActionId": "85ebb48b-359a-4ad3-8786-d07fb840268f",
  "Title": "剪贴板",
  "Description": "WASD极速导航 | 收藏云同步 | 临时置顶 | 图片预览 | 一键收藏 | 零秒启动",
  "Icon": "https://files.getquicker.net/_icons/84F475C785EF026EF5688569D83B31B347D59DA9.png",
  "Variables": [
    {
      "Type": 0,
      "Desc": "结果文本(自动)",
      "IsOutput": false,
      "Key": "text"
    },
    {
      "Type": 0,
      "Desc": "返回值(自动)",
      "IsOutput": false,
      "Key": "rtn"
    },
    {
      "Type": 0,
      "Desc": "错误信息(自动)",
      "IsOutput": false,
      "Key": "errMessage"
    },
    {
      "Type": 0,
      "Desc": "错误详情(自动)",
      "IsOutput": false,
      "Key": "errorDesc"
    },
    {
      "Type": 2,
      "Desc": "后台执行",
      "DefaultValue": "false",
      "IsInput": false,
      "Key": "后台执行线程"
    },
    {
      "Type": 2,
      "Desc": "静默启动",
      "DefaultValue": "false",
      "IsInput": false,
      "Key": "silent"
    }
  ],
  "References": []
}

使用方法

  1. 确保您已安装 动作构建 (Action Builder) 动作。
  2. 将本文提供的 .cs.json 文件下载到同一目录
  3. 选中 .json 文件,运行「动作构建」即可生成并安装动作。
posted @ 2025-12-27 22:17  luoluoluo22  阅读(11)  评论(0)    收藏  举报