diff --git a/TodoList/App.xaml b/TodoList/App.xaml
deleted file mode 100644
index 3f556f9..0000000
--- a/TodoList/App.xaml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
diff --git a/TodoList/App.xaml.cs b/TodoList/App.xaml.cs
deleted file mode 100644
index 033b4e6..0000000
--- a/TodoList/App.xaml.cs
+++ /dev/null
@@ -1,364 +0,0 @@
-using System;
-using System.Threading;
-using System.Windows;
-using System.Windows.Interop;
-using Microsoft.Win32;
-using TodoList.Services;
-using TodoList.ViewModels;
-using TodoList.Views;
-using System.Linq;
-using System.Reflection;
-
-namespace TodoList
-{
- public partial class App : System.Windows.Application
- {
- private const string MainWindowTitle = "待办事项";
- private IDataService _dataService;
- private GlobalShortcutService _shortcutService;
- private MainWindow _mainWindow;
- private QuickEntryWindow? _quickEntryWindow;
- private SettingsService _settingsService;
- private NotifyIcon _notifyIcon;
- private Mutex _mutex;
- private EventWaitHandle _eventWaitHandle;
- private const string UniqueEventName = "Global\\TodoListApp_Event_v1";
-
- [System.Runtime.InteropServices.DllImport("user32.dll")]
- private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
-
- [System.Runtime.InteropServices.DllImport("user32.dll")]
- private static extern bool SetForegroundWindow(IntPtr hWnd);
-
- [System.Runtime.InteropServices.DllImport("user32.dll")]
- private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
-
- public App()
- {
- // Disable hardware acceleration to prevent black screen issues
- // Must be set before any UI is created
- System.Windows.Media.RenderOptions.ProcessRenderMode = System.Windows.Interop.RenderMode.SoftwareOnly;
- }
-
- protected override void OnStartup(StartupEventArgs e)
- {
- // Ensure app doesn't shutdown when main window closes (we hide it)
- this.ShutdownMode = ShutdownMode.OnExplicitShutdown;
-
- const string appName = "Global\\TodoListApp_Unique_Mutex_v1";
- bool createdNew;
- _mutex = new Mutex(true, appName, out createdNew);
-
- if (!createdNew)
- {
- // Signal the existing instance
- try
- {
- using (var evt = EventWaitHandle.OpenExisting(UniqueEventName))
- {
- evt.Set();
- }
- }
- catch
- {
- // Fallback to old method if event open fails
- var hWnd = FindWindow(null, MainWindowTitle);
- if (hWnd != IntPtr.Zero)
- {
- ShowWindow(hWnd, 9); // SW_RESTORE
- SetForegroundWindow(hWnd);
- }
- }
-
- // Force exit to prevent second instance from running
- Environment.Exit(0);
- return;
- }
-
- // Create the event handle for this instance
- _eventWaitHandle = new EventWaitHandle(false, EventResetMode.AutoReset, UniqueEventName);
-
- // Start a thread to listen for signals
- Thread thread = new Thread(() =>
- {
- while (true)
- {
- _eventWaitHandle.WaitOne();
- this.Dispatcher.Invoke(() => ShowMainWindow());
- }
- });
- thread.IsBackground = true;
- thread.Start();
-
- base.OnStartup(e);
-
- // Configure Auto Start
- ConfigureAutoStart();
-
- // Create Desktop/StartMenu Shortcut if needed (optional feature)
- // CreateShortcut();
-
- this.DispatcherUnhandledException += App_DispatcherUnhandledException;
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-
- try
- {
- _settingsService = new SettingsService();
- _dataService = new SqliteDataService();
- _shortcutService = new GlobalShortcutService();
-
- var mainViewModel = new MainViewModel(_dataService, _settingsService);
-
- _mainWindow = new MainWindow(mainViewModel);
- _mainWindow.Title = MainWindowTitle;
- _mainWindow.Loaded += MainWindow_Loaded;
-
- // Initialize Tray Icon
- InitializeTrayIcon();
-
- // Check for silent mode
- bool silent = e.Args.Contains("--silent") || e.Args.Contains("-s");
-
- if (!silent)
- {
- _mainWindow.Show();
- }
- }
- catch (Exception ex)
- {
- Log("Startup Error: " + ex.ToString());
- System.Windows.MessageBox.Show("Startup Error: " + ex.Message);
- }
- }
-
- private void ConfigureAutoStart()
- {
- try
- {
- var exePath = System.Diagnostics.Process.GetCurrentProcess().MainModule?.FileName;
- string cmd = $"\"{exePath}\" --silent";
-
- // If running as dotnet tool, try to find the shim or stable entry point
- // Usually %USERPROFILE%\.dotnet\tools\todo.exe
- var userProfile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
- var toolShim = System.IO.Path.Combine(userProfile, ".dotnet", "tools", "todo.exe");
-
- if (System.IO.File.Exists(toolShim))
- {
- // If the shim exists and we are likely running it (or just installed it), prefer the shim
- // This handles updates better as the shim path stays constant.
- // But we should verify if the current process IS related to it?
- // Actually, if installed as tool, we definitely want to use the shim.
- cmd = $"\"{toolShim}\" --silent";
- }
-
- var key = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
- if (key != null)
- {
- var existing = key.GetValue("TodoListApp");
- if (existing == null || existing.ToString() != cmd)
- {
- key.SetValue("TodoListApp", cmd);
- }
- key.Close();
- }
- }
- catch (Exception ex)
- {
- Log("AutoStart Error: " + ex.Message);
- }
- }
-
- private void InitializeTrayIcon()
- {
- _notifyIcon = new NotifyIcon();
-
- // Try load icon from resource or file
- try
- {
- // Load from embedded resource
- var resourceUri = new Uri("pack://application:,,,/icon.ico");
- var streamInfo = System.Windows.Application.GetResourceStream(resourceUri);
-
- if (streamInfo != null)
- {
- using (var stream = streamInfo.Stream)
- {
- _notifyIcon.Icon = new System.Drawing.Icon(stream);
- }
- }
- else
- {
- _notifyIcon.Icon = System.Drawing.SystemIcons.Application;
- }
- }
- catch
- {
- _notifyIcon.Icon = System.Drawing.SystemIcons.Application;
- }
-
- _notifyIcon.Visible = true;
- _notifyIcon.Text = GetNotifyIconText();
- _notifyIcon.DoubleClick += (s, e) => ShowMainWindow();
-
- var contextMenu = new ContextMenuStrip();
- contextMenu.Items.Add("打开主界面", null, (s, e) => ShowMainWindow());
- contextMenu.Items.Add("退出", null, (s, e) => ExitApplication());
- _notifyIcon.ContextMenuStrip = contextMenu;
- }
-
- private static string GetNotifyIconText()
- {
- var version = GetDisplayVersion();
- var text = string.IsNullOrWhiteSpace(version) ? MainWindowTitle : $"{MainWindowTitle} v{version}";
-
- return text.Length > 63 ? text[..63] : text;
- }
-
- private static string? GetDisplayVersion()
- {
- var asm = Assembly.GetExecutingAssembly();
-
- var info = asm.GetCustomAttribute()?.InformationalVersion?.Trim();
- if (!string.IsNullOrWhiteSpace(info))
- {
- var plus = info.IndexOf('+');
- return plus >= 0 ? info[..plus] : info;
- }
-
- var file = asm.GetCustomAttribute()?.Version?.Trim();
- if (!string.IsNullOrWhiteSpace(file))
- {
- return file;
- }
-
- return asm.GetName().Version?.ToString();
- }
-
- private void ShowMainWindow()
- {
- Log("ShowMainWindow called");
- if (_mainWindow != null)
- {
- if (_mainWindow.WindowState == WindowState.Minimized)
- {
- _mainWindow.WindowState = WindowState.Normal;
- }
- _mainWindow.Show();
- _mainWindow.Activate();
-
- // Force foreground
- var helper = new WindowInteropHelper(_mainWindow);
- var handle = helper.Handle;
- if (handle != IntPtr.Zero)
- {
- SetForegroundWindow(handle);
- }
- }
- }
-
- private void ExitApplication()
- {
- _notifyIcon?.Dispose();
- _notifyIcon = null;
- Shutdown();
- }
-
- private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
- {
- Log("Dispatcher Error: " + e.Exception.ToString());
- System.Windows.MessageBox.Show("Dispatcher Error: " + e.Exception.Message);
- e.Handled = true;
- }
-
- private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- Log("Domain Error: " + e.ExceptionObject.ToString());
- System.Windows.MessageBox.Show("Critical Error: " + e.ExceptionObject.ToString());
- }
-
- private void Log(string message)
- {
- try
- {
- System.IO.File.AppendAllText("error.log", DateTime.Now + ": " + message + Environment.NewLine);
- }
- catch { }
- }
-
- private bool _isHotkeyRegistered;
-
- private void RegisterHotkey()
- {
- if (_isHotkeyRegistered || _shortcutService == null || _settingsService == null) return;
-
- var helper = new WindowInteropHelper(_mainWindow);
- var handle = helper.Handle;
-
- if (handle != IntPtr.Zero)
- {
- var settings = _settingsService.Settings;
- var mods = GlobalShortcutService.GetModifier(settings.ShortcutModifiers);
- var key = GlobalShortcutService.GetKey(settings.ShortcutKey);
-
- _shortcutService.Register(handle, OnHotKeyPressed, mods, key);
- _isHotkeyRegistered = true;
-
- // Subscribe to settings changes to update hotkey
- _settingsService.Settings.PropertyChanged += (s, args) =>
- {
- if (args.PropertyName == nameof(AppSettings.ShortcutModifiers) ||
- args.PropertyName == nameof(AppSettings.ShortcutKey))
- {
- var newMods = GlobalShortcutService.GetModifier(_settingsService.Settings.ShortcutModifiers);
- var newKey = GlobalShortcutService.GetKey(_settingsService.Settings.ShortcutKey);
- _shortcutService.UpdateShortcut(newMods, newKey);
- }
- };
- }
- }
-
- private void OnHotKeyPressed()
- {
- Log("Hotkey pressed.");
- ShowQuickEntryWindow();
- }
-
- private void ShowQuickEntryWindow()
- {
- if (_quickEntryWindow == null)
- {
- _quickEntryWindow = new QuickEntryWindow(_dataService);
- _quickEntryWindow.Title = "新建待办";
- }
-
- if (_quickEntryWindow.WindowState == WindowState.Minimized)
- {
- _quickEntryWindow.WindowState = WindowState.Normal;
- }
-
- _quickEntryWindow.Show();
- _quickEntryWindow.Activate();
-
- var helper = new WindowInteropHelper(_quickEntryWindow);
- var handle = helper.Handle;
- if (handle != IntPtr.Zero)
- {
- SetForegroundWindow(handle);
- }
- }
-
- protected override void OnExit(ExitEventArgs e)
- {
- _notifyIcon?.Dispose();
- _shortcutService?.Dispose();
- base.OnExit(e);
- }
-
- // We can hook into MainWindow's Loaded event to register hotkey
- private void MainWindow_Loaded(object sender, RoutedEventArgs e)
- {
- RegisterHotkey();
- }
- }
-}
diff --git a/TodoList/AssemblyInfo.cs b/TodoList/AssemblyInfo.cs
deleted file mode 100644
index cc29e7f..0000000
--- a/TodoList/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Windows;
-
-[assembly:ThemeInfo(
- ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
- //(used if a resource is not found in the page,
- // or application resource dictionaries)
- ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
- //(used if a resource is not found in the page,
- // app, or any theme specific resource dictionaries)
-)]
diff --git a/TodoList/BuildSetup.ps1 b/TodoList/BuildSetup.ps1
deleted file mode 100644
index 2bb2da4..0000000
--- a/TodoList/BuildSetup.ps1
+++ /dev/null
@@ -1,55 +0,0 @@
-$ErrorActionPreference = "Stop"
-
-# Basic configuration
-$ScriptPath = $PSScriptRoot
-$ProjectFile = (Get-ChildItem -Path $ScriptPath -Filter "*.csproj" -File)[0].FullName
-$SetupScript = Join-Path $ScriptPath "setup.iss"
-
-# Read version from project file
-$currentVersion = "1.0.0"
-[xml]$csproj = Get-Content $ProjectFile
-if ($csproj.Project.PropertyGroup.Version) {
- $currentVersion = $csproj.Project.PropertyGroup.Version
-}
-
-# Increment version
-$versionParts = $currentVersion.Split(".")
-$patch = [int]$versionParts[2] + 1
-$newVersion = $versionParts[0] + "." + $versionParts[1] + "." + $patch
-
-# Update project version
-$content = Get-Content $ProjectFile -Raw
-$content = $content -replace ".*", "$newVersion"
-Set-Content $ProjectFile -Value $content
-
-# Update setup script version
-if (Test-Path $SetupScript) {
- $issContent = Get-Content $SetupScript
- for ($i = 0; $i -lt $issContent.Count; $i++) {
- if ($issContent[$i] -like '#define MyAppVersion *') {
- $issContent[$i] = '#define MyAppVersion "' + $newVersion + '"'
- break
- }
- }
- Set-Content $SetupScript -Value $issContent
-}
-
-# Build project
-dotnet publish $ProjectFile -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true
-if ($LASTEXITCODE -ne 0) {
- Write-Error "Build failed"
- exit 1
-}
-
-# Package
-$ISCC = "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe"
-if (Test-Path $ISCC) {
- & $ISCC $SetupScript
- if ($LASTEXITCODE -eq 0) {
- Write-Host "Setup package created successfully!" -ForegroundColor Green
- } else {
- Write-Error "Packaging failed"
- }
-} else {
- Write-Error "Inno Setup compiler not found"
-}
diff --git a/TodoList/Converters/EnumDescriptionConverter.cs b/TodoList/Converters/EnumDescriptionConverter.cs
deleted file mode 100644
index 9d7d0f8..0000000
--- a/TodoList/Converters/EnumDescriptionConverter.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Globalization;
-using System.Reflection;
-using System.Windows.Data;
-
-namespace TodoList.Converters
-{
- public class EnumDescriptionConverter : IValueConverter
- {
- public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
- {
- if (value == null) return string.Empty;
-
- var type = value.GetType();
- var name = Enum.GetName(type, value);
- if (name == null) return value.ToString();
-
- var field = type.GetField(name);
- if (field == null) return value.ToString();
-
- var attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;
- return attr?.Description ?? value.ToString();
- }
-
- public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
- {
- return value;
- }
- }
-}
diff --git a/TodoList/Models/TodoItem.cs b/TodoList/Models/TodoItem.cs
deleted file mode 100644
index 49f0607..0000000
--- a/TodoList/Models/TodoItem.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Text.Json.Serialization;
-using CommunityToolkit.Mvvm.ComponentModel;
-
-namespace TodoList.Models
-{
- public enum TodoPriority
- {
- [Description("低")]
- Low,
- [Description("中")]
- Medium,
- [Description("高")]
- High
- }
-
- public enum SyncStatus
- {
- Synced,
- Pending,
- Failed
- }
-
- public enum SortBy
- {
- [Description("创建时间")]
- CreatedAt,
- [Description("完成时间")]
- CompletedAt,
- [Description("优先级")]
- Priority
- }
-
- public enum SortOrder
- {
- [Description("升序")]
- Ascending,
- [Description("降序")]
- Descending
- }
-
- public partial class TodoItem : ObservableObject
- {
- [ObservableProperty]
- [property: SQLite.PrimaryKey]
- private string id = Guid.NewGuid().ToString();
-
- [ObservableProperty]
- private string content = string.Empty;
-
- [ObservableProperty]
- private bool isCompleted;
-
- [ObservableProperty]
- private TodoPriority priority = TodoPriority.Medium;
-
- [ObservableProperty]
- private DateTime createdAt = DateTime.Now;
-
- [ObservableProperty]
- private DateTime? completedAt;
-
- [ObservableProperty]
- private SyncStatus syncStatus = SyncStatus.Pending;
- }
-}
diff --git a/TodoList/Services/FileDataService.cs b/TodoList/Services/FileDataService.cs
deleted file mode 100644
index 929da82..0000000
--- a/TodoList/Services/FileDataService.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text.Json;
-using System.Threading.Tasks;
-using TodoList.Models;
-
-namespace TodoList.Services
-{
- public class FileDataService : IDataService
- {
- private readonly string _filePath;
-
- public FileDataService()
- {
- var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- var folder = Path.Combine(appData, "TodoListApp");
- Directory.CreateDirectory(folder);
- _filePath = Path.Combine(folder, "tasks.json");
- }
-
- public async Task> LoadTasksAsync(bool? completed = null)
- {
- if (!File.Exists(_filePath))
- {
- return new List();
- }
-
- try
- {
- using var stream = File.OpenRead(_filePath);
- var items = await JsonSerializer.DeserializeAsync>(stream);
- var tasks = items ?? new List();
-
- if (completed.HasValue)
- {
- return tasks.Where(t => t.IsCompleted == completed.Value).ToList();
- }
- return tasks;
- }
- catch
- {
- return new List();
- }
- }
-
- public async Task SaveTaskAsync(TodoItem task)
- {
- var tasks = await LoadTasksAsync();
- var existing = tasks.Find(t => t.Id == task.Id);
- if (existing != null)
- {
- tasks.Remove(existing);
- }
- tasks.Add(task);
- await SaveAllAsync(tasks);
- return task;
- }
-
- public async Task UpdateTaskAsync(TodoItem task)
- {
- var tasks = await LoadTasksAsync();
- var existing = tasks.Find(t => t.Id == task.Id);
- if (existing != null)
- {
- tasks.Remove(existing);
- tasks.Add(task);
- await SaveAllAsync(tasks);
- }
- return task;
- }
-
- public async Task ToggleCompleteAsync(string id)
- {
- var tasks = await LoadTasksAsync();
- var task = tasks.Find(t => t.Id == id);
- if (task != null)
- {
- task.IsCompleted = !task.IsCompleted;
- task.CompletedAt = task.IsCompleted ? DateTime.Now : null;
- task.SyncStatus = SyncStatus.Pending;
- await SaveAllAsync(tasks);
- }
- return task;
- }
-
- public async Task SaveAllAsync(List tasks)
- {
- using var stream = File.Create(_filePath);
- await JsonSerializer.SerializeAsync(stream, tasks, new JsonSerializerOptions { WriteIndented = true });
- }
-
- public async Task DeleteTaskAsync(string id)
- {
- var tasks = await LoadTasksAsync();
- var existing = tasks.Find(t => t.Id == id);
- if (existing != null)
- {
- tasks.Remove(existing);
- await SaveAllAsync(tasks);
- }
- }
- }
-}
diff --git a/TodoList/Services/GlobalShortcutService.cs b/TodoList/Services/GlobalShortcutService.cs
deleted file mode 100644
index b8a5c46..0000000
--- a/TodoList/Services/GlobalShortcutService.cs
+++ /dev/null
@@ -1,127 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Windows.Input;
-using System.Windows.Interop;
-
-namespace TodoList.Services
-{
- public class GlobalShortcutService : IDisposable
- {
- private const int HOTKEY_ID = 9000;
-
- public const uint MOD_ALT = 0x0001;
- public const uint MOD_CONTROL = 0x0002;
- public const uint MOD_SHIFT = 0x0004;
- public const uint MOD_WIN = 0x0008;
-
- [DllImport("user32.dll")]
- private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
-
- [DllImport("user32.dll")]
- private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
-
- private IntPtr _windowHandle;
- private HwndSource _source;
- private Action _onHotKeyPressed;
- private bool _isRegistered;
-
- public void Register(IntPtr windowHandle, Action onHotKeyPressed, uint modifiers, uint key)
- {
- // If already registered, unregister first (to support updating)
- if (_isRegistered)
- {
- UnregisterHotKey(_windowHandle, HOTKEY_ID);
- _source?.RemoveHook(HwndHook);
- _isRegistered = false;
- }
-
- _windowHandle = windowHandle;
- _onHotKeyPressed = onHotKeyPressed;
-
- _source = HwndSource.FromHwnd(_windowHandle);
- if (_source == null) return; // Should not happen if handle is valid
-
- _source.AddHook(HwndHook);
-
- if (RegisterHotKey(_windowHandle, HOTKEY_ID, modifiers, key))
- {
- _isRegistered = true;
- }
- else
- {
- System.Diagnostics.Debug.WriteLine("Failed to register hotkey.");
- }
- }
-
- public void UpdateShortcut(uint modifiers, uint key)
- {
- if (_windowHandle != IntPtr.Zero && _onHotKeyPressed != null)
- {
- // Re-register with new keys
- Register(_windowHandle, _onHotKeyPressed, modifiers, key);
- }
- }
-
- private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
- {
- const int WM_HOTKEY = 0x0312;
- if (msg == WM_HOTKEY)
- {
- if (wParam.ToInt32() == HOTKEY_ID)
- {
- _onHotKeyPressed?.Invoke();
- handled = true;
- }
- }
- return IntPtr.Zero;
- }
-
- public void Unregister()
- {
- if (_isRegistered)
- {
- _source?.RemoveHook(HwndHook);
- UnregisterHotKey(_windowHandle, HOTKEY_ID);
- _isRegistered = false;
- }
- }
-
- public void Dispose()
- {
- Unregister();
- }
-
- public static uint GetModifier(string modifiers)
- {
- uint mod = 0;
- if (string.IsNullOrEmpty(modifiers)) return mod;
-
- var parts = modifiers.Split(',');
- foreach (var part in parts)
- {
- var p = part.Trim();
- if (p.Equals("Control", StringComparison.OrdinalIgnoreCase)) mod |= MOD_CONTROL;
- if (p.Equals("Alt", StringComparison.OrdinalIgnoreCase)) mod |= MOD_ALT;
- if (p.Equals("Shift", StringComparison.OrdinalIgnoreCase)) mod |= MOD_SHIFT;
- if (p.Equals("Windows", StringComparison.OrdinalIgnoreCase)) mod |= MOD_WIN;
- }
- return mod;
- }
-
- public static uint GetKey(string key)
- {
- if (Enum.TryParse(key, out var k))
- {
- return (uint)KeyInterop.VirtualKeyFromKey(k);
- }
- // Fallback for simple letters if Key enum doesn't match directly (though it should for A-Z)
- if (key.Length == 1)
- {
- char c = char.ToUpper(key[0]);
- if (c >= 'A' && c <= 'Z') return (uint)c;
- if (c >= '0' && c <= '9') return (uint)c;
- }
- return 0x41; // Default 'A'
- }
- }
-}
diff --git a/TodoList/Services/IDataService.cs b/TodoList/Services/IDataService.cs
deleted file mode 100644
index f5e690a..0000000
--- a/TodoList/Services/IDataService.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using TodoList.Models;
-
-namespace TodoList.Services
-{
- public interface IDataService
- {
- Task> LoadTasksAsync(bool? completed = null);
- Task SaveTaskAsync(TodoItem task);
- Task UpdateTaskAsync(TodoItem task);
- Task ToggleCompleteAsync(string id);
- Task DeleteTaskAsync(string id);
- }
-}
diff --git a/TodoList/Services/SettingsService.cs b/TodoList/Services/SettingsService.cs
deleted file mode 100644
index cc2763b..0000000
--- a/TodoList/Services/SettingsService.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.IO;
-using System.Text.Json;
-using System.Threading.Tasks;
-using CommunityToolkit.Mvvm.ComponentModel;
-
-namespace TodoList.Services
-{
- public partial class AppSettings : ObservableObject
- {
- [ObservableProperty]
- private string shortcutModifiers = "Control,Alt"; // Comma separated
-
- [ObservableProperty]
- private string shortcutKey = "A";
- }
-
- public class SettingsService
- {
- private readonly string _filePath;
- public AppSettings Settings { get; private set; }
-
- public SettingsService()
- {
- var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- var folder = Path.Combine(appData, "TodoListApp");
- Directory.CreateDirectory(folder);
- _filePath = Path.Combine(folder, "settings.json");
- Settings = LoadSettings();
- }
-
- private AppSettings LoadSettings()
- {
- if (!File.Exists(_filePath)) return new AppSettings();
- try
- {
- var json = File.ReadAllText(_filePath);
- return JsonSerializer.Deserialize(json) ?? new AppSettings();
- }
- catch
- {
- return new AppSettings();
- }
- }
-
- public void SaveSettings()
- {
- try
- {
- var json = JsonSerializer.Serialize(Settings, new JsonSerializerOptions { WriteIndented = true });
- File.WriteAllText(_filePath, json);
- }
- catch { }
- }
- }
-}
diff --git a/TodoList/Services/SqliteDataService.cs b/TodoList/Services/SqliteDataService.cs
deleted file mode 100644
index a541bd4..0000000
--- a/TodoList/Services/SqliteDataService.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Threading.Tasks;
-using SQLite;
-using TodoList.Models;
-
-namespace TodoList.Services
-{
- public class SqliteDataService : IDataService
- {
- private readonly SQLiteAsyncConnection _database;
-
- public SqliteDataService()
- {
- var appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- var folder = Path.Combine(appData, "TodoListApp");
- Directory.CreateDirectory(folder);
- var databasePath = Path.Combine(folder, "TodoList.sqlite");
-
- _database = new SQLiteAsyncConnection(databasePath);
- _database.CreateTableAsync().Wait();
- }
-
- public async Task> LoadTasksAsync(bool? completed = null)
- {
- var query = _database.Table();
- if (completed.HasValue)
- {
- query = query.Where(t => t.IsCompleted == completed.Value);
- }
- return await query.ToListAsync();
- }
-
- public async Task SaveTaskAsync(TodoItem task)
- {
- await _database.InsertOrReplaceAsync(task);
- return task;
- }
-
- public async Task UpdateTaskAsync(TodoItem task)
- {
- await _database.UpdateAsync(task);
- return task;
- }
-
- public async Task ToggleCompleteAsync(string id)
- {
- var task = await _database.FindAsync(id);
- if (task != null)
- {
- task.IsCompleted = !task.IsCompleted;
- task.CompletedAt = task.IsCompleted ? DateTime.Now : null;
- task.SyncStatus = SyncStatus.Pending;
- await _database.UpdateAsync(task);
- }
- return task;
- }
-
- public async Task SaveAllAsync(List tasks)
- {
- await _database.RunInTransactionAsync(tran =>
- {
- foreach (var task in tasks)
- {
- tran.InsertOrReplace(task);
- }
- });
- }
-
- public async Task DeleteTaskAsync(string id)
- {
- await _database.DeleteAsync(id);
- }
- }
-}
diff --git a/TodoList/TodoList.csproj b/TodoList/TodoList.csproj
deleted file mode 100644
index f8328a4..0000000
--- a/TodoList/TodoList.csproj
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
- WinExe
- net8.0-windows
- enable
- enable
- 65001
- true
- true
- icon.ico
- 1.0.21
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/TodoList/ViewModels/MainViewModel.cs b/TodoList/ViewModels/MainViewModel.cs
deleted file mode 100644
index 83c7454..0000000
--- a/TodoList/ViewModels/MainViewModel.cs
+++ /dev/null
@@ -1,244 +0,0 @@
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using CommunityToolkit.Mvvm.Messaging;
-using System.Collections.ObjectModel;
-using System.Linq;
-using System.Threading.Tasks;
-using TodoList.Models;
-using TodoList.Services;
-
-namespace TodoList.ViewModels
-{
- public partial class MainViewModel : ObservableObject, IRecipient
- {
- private readonly IDataService _dataService;
- private readonly SettingsService _settingsService;
-
- [ObservableProperty]
- private ObservableCollection tasks = new();
-
- [ObservableProperty]
- private bool showCompleted = false;
-
- [ObservableProperty]
- private string newContent;
-
- [ObservableProperty]
- private TodoPriority newPriority = TodoPriority.Medium;
-
- [ObservableProperty]
- private bool isEditDialogOpen;
-
- [ObservableProperty]
- private TodoItem editingTask;
-
- [ObservableProperty]
- private string editContent;
-
- [ObservableProperty]
- private TodoPriority editPriority = TodoPriority.Medium;
-
- [ObservableProperty]
- private bool isSettingsOpen;
-
- [ObservableProperty]
- [NotifyPropertyChangedFor(nameof(FullShortcut))]
- private string shortcutKey;
-
- [ObservableProperty]
- [NotifyPropertyChangedFor(nameof(FullShortcut))]
- private string shortcutModifiers;
-
- [ObservableProperty]
- private SortBy sortBy = SortBy.Priority;
-
- [ObservableProperty]
- private Models.SortOrder sortOrder = Models.SortOrder.Descending;
-
- public string FullShortcut
- {
- get
- {
- var mods = ShortcutModifiers?.Replace(",", " + ");
- return string.IsNullOrEmpty(mods) ? ShortcutKey : $"{mods} + {ShortcutKey}";
- }
- }
-
- public MainViewModel(IDataService dataService, SettingsService settingsService)
- {
- _dataService = dataService;
- _settingsService = settingsService;
- ShortcutKey = _settingsService.Settings.ShortcutKey;
- ShortcutModifiers = _settingsService.Settings.ShortcutModifiers;
-
- WeakReferenceMessenger.Default.Register(this);
- LoadTasksCommand.Execute(null);
- }
-
- [RelayCommand]
- private void OpenSettings()
- {
- IsSettingsOpen = true;
- ShortcutKey = _settingsService.Settings.ShortcutKey;
- ShortcutModifiers = _settingsService.Settings.ShortcutModifiers;
- }
-
- [RelayCommand]
- private void CloseSettings()
- {
- IsSettingsOpen = false;
- }
-
- [RelayCommand]
- private void SaveSettings()
- {
- if (!string.IsNullOrWhiteSpace(ShortcutKey))
- {
- _settingsService.Settings.ShortcutKey = ShortcutKey.ToUpper();
- _settingsService.Settings.ShortcutModifiers = ShortcutModifiers;
- _settingsService.SaveSettings();
- }
- IsSettingsOpen = false;
- }
-
- async partial void OnShowCompletedChanged(bool value)
- {
- await LoadTasksAsync();
- }
-
- async partial void OnSortByChanged(SortBy value)
- {
- await LoadTasksAsync();
- }
-
- async partial void OnSortOrderChanged(Models.SortOrder value)
- {
- await LoadTasksAsync();
- }
-
- [RelayCommand]
- private async Task AddTaskAsync()
- {
- if (string.IsNullOrWhiteSpace(NewContent)) return;
-
- var newTask = new TodoItem
- {
- Content = NewContent,
- Priority = NewPriority,
- IsCompleted = false,
- SyncStatus = SyncStatus.Pending
- };
-
- await _dataService.SaveTaskAsync(newTask);
- NewContent = string.Empty;
- NewPriority = TodoPriority.Medium;
- await LoadTasksAsync();
- }
-
- [RelayCommand]
- private async Task LoadTasksAsync()
- {
- var allTasks = await _dataService.LoadTasksAsync();
-
- var filtered = ShowCompleted
- ? allTasks
- : allTasks.Where(t => !t.IsCompleted).ToList();
-
- IOrderedEnumerable sorted;
-
- if (SortBy == SortBy.Priority)
- {
- sorted = SortOrder == Models.SortOrder.Ascending
- ? filtered.OrderBy(t => t.Priority).ThenBy(t => t.CreatedAt)
- : filtered.OrderByDescending(t => t.Priority).ThenBy(t => t.CreatedAt);
- }
- else if (SortBy == SortBy.CreatedAt)
- {
- sorted = SortOrder == Models.SortOrder.Ascending
- ? filtered.OrderBy(t => t.CreatedAt)
- : filtered.OrderByDescending(t => t.CreatedAt);
- }
- else
- {
- sorted = SortOrder == Models.SortOrder.Ascending
- ? filtered.OrderBy(t => t.CompletedAt).ThenBy(t => t.CreatedAt)
- : filtered.OrderByDescending(t => t.CompletedAt).ThenBy(t => t.CreatedAt);
- }
-
- Tasks.Clear();
- foreach (var t in sorted)
- {
- Tasks.Add(t);
- }
- }
-
- [RelayCommand]
- private async Task ToggleCompleteAsync(TodoItem item)
- {
- if (item == null) return;
-
- if (item.IsCompleted)
- {
- item.CompletedAt = DateTime.Now;
- }
- else
- {
- item.CompletedAt = null;
- }
-
- item.SyncStatus = SyncStatus.Pending;
- await _dataService.SaveTaskAsync(item);
- await LoadTasksAsync();
- }
-
- [RelayCommand]
- private async Task DeleteAsync(TodoItem item)
- {
- if (item == null) return;
- await _dataService.DeleteTaskAsync(item.Id);
- Tasks.Remove(item);
- }
-
- [RelayCommand]
- private void OpenEditDialog(TodoItem item)
- {
- if (item == null) return;
- EditingTask = item;
- EditContent = item.Content;
- EditPriority = item.Priority;
- IsEditDialogOpen = true;
- }
-
- [RelayCommand]
- private void CloseEditDialog()
- {
- IsEditDialogOpen = false;
- EditingTask = null;
- EditContent = string.Empty;
- EditPriority = TodoPriority.Medium;
- }
-
- [RelayCommand]
- private async Task SaveEditAsync()
- {
- if (EditingTask == null || string.IsNullOrWhiteSpace(EditContent)) return;
-
- EditingTask.Content = EditContent;
- EditingTask.Priority = EditPriority;
- EditingTask.SyncStatus = SyncStatus.Pending;
-
- await _dataService.SaveTaskAsync(EditingTask);
- await LoadTasksAsync();
- CloseEditDialog();
- }
-
- public async void Receive(TaskAddedMessage message)
- {
- await LoadTasksAsync();
- }
- }
-
- public class TaskAddedMessage
- {
- }
-}
diff --git a/TodoList/ViewModels/QuickEntryViewModel.cs b/TodoList/ViewModels/QuickEntryViewModel.cs
deleted file mode 100644
index 065af8e..0000000
--- a/TodoList/ViewModels/QuickEntryViewModel.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using CommunityToolkit.Mvvm.ComponentModel;
-using CommunityToolkit.Mvvm.Input;
-using CommunityToolkit.Mvvm.Messaging;
-using System;
-using System.Threading.Tasks;
-using TodoList.Models;
-using TodoList.Services;
-
-namespace TodoList.ViewModels
-{
- public partial class QuickEntryViewModel : ObservableObject
- {
- private readonly IDataService _dataService;
- private Action _closeAction;
-
- [ObservableProperty]
- private string content;
-
- [ObservableProperty]
- private TodoPriority priority = TodoPriority.Medium;
-
- public QuickEntryViewModel(IDataService dataService, Action closeAction)
- {
- _dataService = dataService;
- _closeAction = closeAction;
- }
-
- [RelayCommand]
- private async Task SaveAsync()
- {
- if (string.IsNullOrWhiteSpace(Content)) return;
-
- var newTask = new TodoItem
- {
- Content = Content,
- Priority = Priority,
- IsCompleted = false,
- SyncStatus = SyncStatus.Pending
- };
-
- await _dataService.SaveTaskAsync(newTask);
-
- // Notify MainViewModel
- WeakReferenceMessenger.Default.Send(new TaskAddedMessage());
-
- // Reset and close
- Content = string.Empty;
- Priority = TodoPriority.Medium;
- _closeAction?.Invoke();
- }
-
- [RelayCommand]
- private void Cancel()
- {
- _closeAction?.Invoke();
- }
- }
-}
diff --git a/TodoList/Views/MainWindow.xaml b/TodoList/Views/MainWindow.xaml
deleted file mode 100644
index 1fd19d8..0000000
--- a/TodoList/Views/MainWindow.xaml
+++ /dev/null
@@ -1,340 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/TodoList/Views/MainWindow.xaml.cs b/TodoList/Views/MainWindow.xaml.cs
deleted file mode 100644
index adef138..0000000
--- a/TodoList/Views/MainWindow.xaml.cs
+++ /dev/null
@@ -1,96 +0,0 @@
-using System.Diagnostics;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Input;
-using TodoList.ViewModels;
-
-namespace TodoList.Views
-{
- public partial class MainWindow : Window
- {
- public MainWindow(MainViewModel viewModel)
- {
- InitializeComponent();
- DataContext = viewModel;
- }
-
- protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
- {
- e.Cancel = true;
- this.Hide();
- // Verify if app shuts down? No, ShutdownMode is Explicit.
- }
-
- private void Window_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- if (e.Key == Key.Escape)
- {
- // If settings are open, close settings?
- // But user requirement is "Equals pressing X button", which usually means Close/Hide window.
- // However, if we want better UX:
- if (DataContext is MainViewModel { IsSettingsOpen: true } vm)
- {
- vm.IsSettingsOpen = false;
- e.Handled = true;
- return;
- }
-
- // Default behavior: Close (Hide) Window
- Close();
- }
- }
-
- private void ShortcutBox_PreviewKeyDown(object sender, System.Windows.Input.KeyEventArgs e)
- {
- e.Handled = true;
-
- // Ignore modifier keys alone being the "main" key
- if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl ||
- e.Key == Key.LeftAlt || e.Key == Key.RightAlt ||
- e.Key == Key.LeftShift || e.Key == Key.RightShift ||
- e.Key == Key.LWin || e.Key == Key.RWin)
- {
- return;
- }
-
- var key = e.Key;
- if (key == Key.System) key = e.SystemKey; // Handle Alt+Key
-
- // Build modifier string
- var modifiers = new System.Collections.Generic.List();
- if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) modifiers.Add("Control");
- if ((Keyboard.Modifiers & ModifierKeys.Alt) == ModifierKeys.Alt) modifiers.Add("Alt");
- if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift) modifiers.Add("Shift");
- if ((Keyboard.Modifiers & ModifierKeys.Windows) == ModifierKeys.Windows) modifiers.Add("Windows");
-
- // Map key to string
- string keyStr = key.ToString();
-
- // Simple mapping for letters/digits (A-Z, 0-9)
- if (keyStr.Length == 2 && keyStr.StartsWith("D") && char.IsDigit(keyStr[1]))
- {
- keyStr = keyStr.Substring(1);
- }
-
- // Update ViewModel
- if (DataContext is MainViewModel vm)
- {
- vm.ShortcutModifiers = string.Join(",", modifiers);
- vm.ShortcutKey = keyStr;
- }
- }
-
- private void ListBoxItem_MouseDoubleClick(object sender, MouseButtonEventArgs e)
- {
- if (sender is ListBoxItem item && item.DataContext is Models.TodoItem todoItem && DataContext is MainViewModel vm)
- {
- vm.OpenEditDialogCommand.Execute(todoItem);
- }
- }
-
- private void Hyperlink_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e)
- {
- Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri) { UseShellExecute = true });
- }
- }
-}
diff --git a/TodoList/Views/QuickEntryWindow.xaml b/TodoList/Views/QuickEntryWindow.xaml
deleted file mode 100644
index 8293835..0000000
--- a/TodoList/Views/QuickEntryWindow.xaml
+++ /dev/null
@@ -1,84 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/TodoList/Views/QuickEntryWindow.xaml.cs b/TodoList/Views/QuickEntryWindow.xaml.cs
deleted file mode 100644
index 768c34f..0000000
--- a/TodoList/Views/QuickEntryWindow.xaml.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Windows;
-using TodoList.Services;
-using TodoList.ViewModels;
-
-namespace TodoList.Views
-{
- public partial class QuickEntryWindow : Window
- {
- public QuickEntryWindow(IDataService dataService)
- {
- InitializeComponent();
- DataContext = new QuickEntryViewModel(dataService, () => this.Hide());
- }
-
- protected override void OnActivated(EventArgs e)
- {
- base.OnActivated(e);
- InputBox.Focus();
- InputBox.SelectAll();
- }
- }
-}
diff --git a/TodoList/icon.ico b/TodoList/icon.ico
deleted file mode 100644
index 8720501..0000000
Binary files a/TodoList/icon.ico and /dev/null differ
diff --git a/TodoList/setup.iss b/TodoList/setup.iss
deleted file mode 100644
index a65fef8..0000000
--- a/TodoList/setup.iss
+++ /dev/null
@@ -1,56 +0,0 @@
-#define MyAppName "TodoList"
-#define MyAppVersion "1.0.21"
-#define MyAppPublisher "ShaoHua"
-#define MyAppURL "https://git.we965.cn/Tools/TodoList"
-#define MyAppExeName "TodoList.exe"
-
-[Setup]
-; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
-; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
-AppId={{8B8A6E3F-1234-5678-9ABC-DEF012345678}
-AppName={#MyAppName}
-AppVersion={#MyAppVersion}
-;AppVerName={#MyAppName} {#MyAppVersion}
-AppPublisher={#MyAppPublisher}
-AppPublisherURL={#MyAppURL}
-AppSupportURL={#MyAppURL}
-AppUpdatesURL={#MyAppURL}
-DefaultDirName={autopf}\{#MyAppName}
-DisableProgramGroupPage=yes
-; Remove the following line to run in administrative install mode (install for all users.)
-PrivilegesRequired=lowest
-OutputDir=Output
-OutputBaseFilename={#MyAppName}_Setup_v{#MyAppVersion}
-SetupIconFile=icon.ico
-Compression=lzma
-SolidCompression=yes
-WizardStyle=modern
-
-[Languages]
-Name: "chinesesimplified"; MessagesFile: "compiler:Languages\ChineseSimplified.isl"
-
-[Tasks]
-Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
-
-[Files]
-Source: "bin\Release\net8.0-windows\win-x64\publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
-; NOTE: Don't use "Flags: ignoreversion" on any shared system files
-
-[Icons]
-Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
-Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
-
-[Run]
-Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
-
-
-
-
-
-
-
-
-
-
-
-