🐛 Fixed hotkey service issues
This commit is contained in:
@@ -32,8 +32,8 @@ export function GetCurrentHotkey(): Promise<models$0.HotkeyCombo | null> & { can
|
|||||||
/**
|
/**
|
||||||
* Initialize 初始化热键服务
|
* Initialize 初始化热键服务
|
||||||
*/
|
*/
|
||||||
export function Initialize(app: application$0.App | null): Promise<void> & { cancel(): void } {
|
export function Initialize(app: application$0.App | null, mainWindow: application$0.WebviewWindow | null): Promise<void> & { cancel(): void } {
|
||||||
let $resultPromise = $Call.ByID(3671360458, app) as any;
|
let $resultPromise = $Call.ByID(3671360458, app, mainWindow) as any;
|
||||||
return $resultPromise;
|
return $resultPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ const openInNewWindow = async (doc: Document, event: Event) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理删除 - 简化确认机制
|
// 处理删除
|
||||||
const handleDelete = async (doc: Document, event: Event) => {
|
const handleDelete = async (doc: Document, event: Event) => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
@@ -242,9 +242,13 @@ const handleDelete = async (doc: Document, event: Event) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await documentStore.deleteDocument(doc.id);
|
const deleteSuccess = await documentStore.deleteDocument(doc.id);
|
||||||
await documentStore.updateDocuments();
|
|
||||||
|
|
||||||
|
if (!deleteSuccess) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await documentStore.updateDocuments();
|
||||||
// 如果删除的是当前文档,切换到第一个文档
|
// 如果删除的是当前文档,切换到第一个文档
|
||||||
if (documentStore.currentDocument?.id === doc.id && documentStore.documentList.length > 0) {
|
if (documentStore.currentDocument?.id === doc.id && documentStore.documentList.length > 0) {
|
||||||
const firstDoc = documentStore.documentList[0];
|
const firstDoc = documentStore.documentList[0];
|
||||||
|
|||||||
@@ -160,10 +160,12 @@ export const useDocumentStore = defineStore('document', () => {
|
|||||||
try {
|
try {
|
||||||
// 检查是否是默认文档(使用ID判断)
|
// 检查是否是默认文档(使用ID判断)
|
||||||
if (docId === SCRATCH_DOCUMENT_ID) {
|
if (docId === SCRATCH_DOCUMENT_ID) {
|
||||||
|
console.log('Cannot delete default document (ID=1)');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await DocumentService.DeleteDocument(docId);
|
await DocumentService.DeleteDocument(docId);
|
||||||
|
console.log('Backend delete successful for doc:', docId);
|
||||||
|
|
||||||
// 更新本地状态
|
// 更新本地状态
|
||||||
delete documents.value[docId];
|
delete documents.value[docId];
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -68,7 +68,7 @@ require (
|
|||||||
github.com/sergi/go-diff v1.4.0 // indirect
|
github.com/sergi/go-diff v1.4.0 // indirect
|
||||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||||
github.com/wailsapp/go-webview2 v1.0.22 // indirect
|
github.com/wailsapp/go-webview2 v1.0.21 // indirect
|
||||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||||
github.com/xanzy/go-gitlab v0.115.0 // indirect
|
github.com/xanzy/go-gitlab v0.115.0 // indirect
|
||||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -160,8 +160,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/wailsapp/go-webview2 v1.0.22 h1:YT61F5lj+GGaat5OB96Aa3b4QA+mybD0Ggq6NZijQ58=
|
github.com/wailsapp/go-webview2 v1.0.21 h1:k3dtoZU4KCoN/AEIbWiPln3P2661GtA2oEgA2Pb+maA=
|
||||||
github.com/wailsapp/go-webview2 v1.0.22/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
github.com/wailsapp/go-webview2 v1.0.21/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
github.com/wailsapp/mimetype v1.4.1/go.mod h1:9aV5k31bBOv5z6u+QP8TltzvNGJPmNJD4XlAL3U+j3o=
|
||||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.25 h1:o05zUiPEvmrq2lqqCs4wqnrnAjGmhryYHRhjQmtkvk8=
|
github.com/wailsapp/wails/v3 v3.0.0-alpha.25 h1:o05zUiPEvmrq2lqqCs4wqnrnAjGmhryYHRhjQmtkvk8=
|
||||||
|
|||||||
@@ -315,6 +315,7 @@ func (ds *DocumentService) DeleteDocument(id int64) error {
|
|||||||
defer ds.mu.Unlock()
|
defer ds.mu.Unlock()
|
||||||
|
|
||||||
if ds.databaseService == nil || ds.databaseService.db == nil {
|
if ds.databaseService == nil || ds.databaseService.db == nil {
|
||||||
|
ds.logger.Error("database service not available")
|
||||||
return errors.New("database service not available")
|
return errors.New("database service not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,15 +26,17 @@ import (
|
|||||||
type HotkeyService struct {
|
type HotkeyService struct {
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
configService *ConfigService
|
configService *ConfigService
|
||||||
|
windowService *WindowService
|
||||||
app *application.App
|
app *application.App
|
||||||
|
mainWindow *application.WebviewWindow
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
currentHotkey *models.HotkeyCombo
|
currentHotkey *models.HotkeyCombo
|
||||||
isRegistered atomic.Bool
|
isRegistered atomic.Bool
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancelFunc atomic.Value // 使用atomic.Value存储cancel函数,避免竞态条件
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// HotkeyError 热键错误
|
// HotkeyError 热键错误
|
||||||
@@ -51,24 +53,46 @@ func (e *HotkeyError) Unwrap() error {
|
|||||||
return e.Err
|
return e.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setCancelFunc 原子地设置cancel函数
|
||||||
|
func (hs *HotkeyService) setCancelFunc(cancel context.CancelFunc) {
|
||||||
|
hs.cancelFunc.Store(cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCancelFunc 原子地获取cancel函数
|
||||||
|
func (hs *HotkeyService) getCancelFunc() context.CancelFunc {
|
||||||
|
if cancel := hs.cancelFunc.Load(); cancel != nil {
|
||||||
|
return cancel.(context.CancelFunc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearCancelFunc 原子地清除cancel函数
|
||||||
|
func (hs *HotkeyService) clearCancelFunc() {
|
||||||
|
hs.cancelFunc.Store((*context.CancelFunc)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
// NewHotkeyService 创建热键服务实例
|
// NewHotkeyService 创建热键服务实例
|
||||||
func NewHotkeyService(configService *ConfigService, logger *log.LogService) *HotkeyService {
|
func NewHotkeyService(configService *ConfigService, windowService *WindowService, logger *log.LogService) *HotkeyService {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New()
|
logger = log.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &HotkeyService{
|
service := &HotkeyService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
configService: configService,
|
configService: configService,
|
||||||
|
windowService: windowService,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
|
// 初始化时设置cancel函数
|
||||||
|
service.setCancelFunc(cancel)
|
||||||
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize 初始化热键服务
|
// Initialize 初始化热键服务
|
||||||
func (hs *HotkeyService) Initialize(app *application.App) error {
|
func (hs *HotkeyService) Initialize(app *application.App, mainWindow *application.WebviewWindow) error {
|
||||||
hs.app = app
|
hs.app = app
|
||||||
|
hs.mainWindow = mainWindow
|
||||||
|
|
||||||
config, err := hs.configService.GetConfig()
|
config, err := hs.configService.GetConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -119,7 +143,7 @@ func (hs *HotkeyService) RegisterHotkey(hotkey *models.HotkeyCombo) error {
|
|||||||
|
|
||||||
hs.currentHotkey = hotkey
|
hs.currentHotkey = hotkey
|
||||||
hs.isRegistered.Store(true)
|
hs.isRegistered.Store(true)
|
||||||
hs.cancel = cancel
|
hs.setCancelFunc(cancel)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -137,13 +161,15 @@ func (hs *HotkeyService) unregisterInternal() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.cancel != nil {
|
// 原子地获取并调用cancel函数
|
||||||
hs.cancel()
|
if cancel := hs.getCancelFunc(); cancel != nil {
|
||||||
|
cancel()
|
||||||
hs.wg.Wait()
|
hs.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
hs.currentHotkey = nil
|
hs.currentHotkey = nil
|
||||||
hs.isRegistered.Store(false)
|
hs.isRegistered.Store(false)
|
||||||
|
hs.clearCancelFunc()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +191,7 @@ func (hs *HotkeyService) hotkeyListener(ctx context.Context, hotkey *models.Hotk
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ticker := time.NewTicker(100 * time.Millisecond)
|
ticker := time.NewTicker(50 * time.Millisecond)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
var wasPressed bool
|
var wasPressed bool
|
||||||
@@ -199,11 +225,61 @@ func cBool(b bool) C.int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggleWindow 切换窗口
|
// toggleWindow 切换窗口显示状态
|
||||||
func (hs *HotkeyService) toggleWindow() {
|
func (hs *HotkeyService) toggleWindow() {
|
||||||
if hs.app != nil {
|
if hs.mainWindow == nil {
|
||||||
hs.app.Event.Emit("hotkey:toggle-window", nil)
|
hs.logger.Error("main window not set")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查主窗口是否可见
|
||||||
|
if hs.isWindowVisible(hs.mainWindow) {
|
||||||
|
// 如果主窗口可见,隐藏所有窗口
|
||||||
|
hs.hideAllWindows()
|
||||||
|
} else {
|
||||||
|
// 如果主窗口不可见,显示所有窗口
|
||||||
|
hs.showAllWindows()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWindowVisible 检查窗口是否可见
|
||||||
|
func (hs *HotkeyService) isWindowVisible(window *application.WebviewWindow) bool {
|
||||||
|
return window.IsVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hideAllWindows 隐藏所有窗口
|
||||||
|
func (hs *HotkeyService) hideAllWindows() {
|
||||||
|
// 隐藏主窗口
|
||||||
|
hs.mainWindow.Hide()
|
||||||
|
|
||||||
|
// 隐藏所有子窗口
|
||||||
|
if hs.windowService != nil {
|
||||||
|
openWindows := hs.windowService.GetOpenWindows()
|
||||||
|
for _, windowInfo := range openWindows {
|
||||||
|
windowInfo.Window.Hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.logger.Debug("all windows hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// showAllWindows 显示所有窗口
|
||||||
|
func (hs *HotkeyService) showAllWindows() {
|
||||||
|
// 显示主窗口
|
||||||
|
hs.mainWindow.Show()
|
||||||
|
hs.mainWindow.Restore()
|
||||||
|
hs.mainWindow.Focus()
|
||||||
|
|
||||||
|
// 显示所有子窗口
|
||||||
|
if hs.windowService != nil {
|
||||||
|
openWindows := hs.windowService.GetOpenWindows()
|
||||||
|
for _, windowInfo := range openWindows {
|
||||||
|
windowInfo.Window.Show()
|
||||||
|
windowInfo.Window.Restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.logger.Debug("all windows shown")
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyToVirtualKeyCode 键名转虚拟键码
|
// keyToVirtualKeyCode 键名转虚拟键码
|
||||||
@@ -261,7 +337,10 @@ func (hs *HotkeyService) IsRegistered() bool {
|
|||||||
|
|
||||||
// ServiceShutdown 关闭服务
|
// ServiceShutdown 关闭服务
|
||||||
func (hs *HotkeyService) ServiceShutdown() error {
|
func (hs *HotkeyService) ServiceShutdown() error {
|
||||||
hs.cancel()
|
// 原子地获取并调用cancel函数
|
||||||
|
if cancel := hs.getCancelFunc(); cancel != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
hs.wg.Wait()
|
hs.wg.Wait()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,10 +82,13 @@ var globalHotkeyService *HotkeyService
|
|||||||
type HotkeyService struct {
|
type HotkeyService struct {
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
configService *ConfigService
|
configService *ConfigService
|
||||||
|
windowService *WindowService
|
||||||
app *application.App
|
app *application.App
|
||||||
|
mainWindow *application.WebviewWindow
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
isRegistered atomic.Bool
|
isRegistered atomic.Bool
|
||||||
currentHotkey *models.HotkeyCombo
|
currentHotkey *models.HotkeyCombo
|
||||||
|
cancelFunc atomic.Value // 使用atomic.Value存储cancel函数,避免竞态条件
|
||||||
}
|
}
|
||||||
|
|
||||||
// HotkeyError 热键错误
|
// HotkeyError 热键错误
|
||||||
@@ -104,8 +107,26 @@ func (e *HotkeyError) Unwrap() error {
|
|||||||
return e.Err
|
return e.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setCancelFunc 原子地设置cancel函数
|
||||||
|
func (hs *HotkeyService) setCancelFunc(cancel context.CancelFunc) {
|
||||||
|
hs.cancelFunc.Store(cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCancelFunc 原子地获取cancel函数
|
||||||
|
func (hs *HotkeyService) getCancelFunc() context.CancelFunc {
|
||||||
|
if cancel := hs.cancelFunc.Load(); cancel != nil {
|
||||||
|
return cancel.(context.CancelFunc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearCancelFunc 原子地清除cancel函数
|
||||||
|
func (hs *HotkeyService) clearCancelFunc() {
|
||||||
|
hs.cancelFunc.Store((*context.CancelFunc)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
// NewHotkeyService 创建新的热键服务实例
|
// NewHotkeyService 创建新的热键服务实例
|
||||||
func NewHotkeyService(configService *ConfigService, logger *log.LogService) *HotkeyService {
|
func NewHotkeyService(configService *ConfigService, windowService *WindowService, logger *log.LogService) *HotkeyService {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New()
|
logger = log.New()
|
||||||
}
|
}
|
||||||
@@ -113,6 +134,7 @@ func NewHotkeyService(configService *ConfigService, logger *log.LogService) *Hot
|
|||||||
service := &HotkeyService{
|
service := &HotkeyService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
configService: configService,
|
configService: configService,
|
||||||
|
windowService: windowService,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置全局实例
|
// 设置全局实例
|
||||||
@@ -122,8 +144,9 @@ func NewHotkeyService(configService *ConfigService, logger *log.LogService) *Hot
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize 初始化热键服务
|
// Initialize 初始化热键服务
|
||||||
func (hs *HotkeyService) Initialize(app *application.App) error {
|
func (hs *HotkeyService) Initialize(app *application.App, mainWindow *application.WebviewWindow) error {
|
||||||
hs.app = app
|
hs.app = app
|
||||||
|
hs.mainWindow = mainWindow
|
||||||
|
|
||||||
// 加载并应用当前配置
|
// 加载并应用当前配置
|
||||||
config, err := hs.configService.GetConfig()
|
config, err := hs.configService.GetConfig()
|
||||||
@@ -285,9 +308,59 @@ func (hs *HotkeyService) IsRegistered() bool {
|
|||||||
|
|
||||||
// ToggleWindow 切换窗口显示状态
|
// ToggleWindow 切换窗口显示状态
|
||||||
func (hs *HotkeyService) ToggleWindow() {
|
func (hs *HotkeyService) ToggleWindow() {
|
||||||
if hs.app != nil {
|
if hs.mainWindow == nil {
|
||||||
hs.app.EmitEvent("hotkey:toggle-window", nil)
|
hs.logger.Error("main window not set")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查主窗口是否可见
|
||||||
|
if hs.isWindowVisible(hs.mainWindow) {
|
||||||
|
// 如果主窗口可见,隐藏所有窗口
|
||||||
|
hs.hideAllWindows()
|
||||||
|
} else {
|
||||||
|
// 如果主窗口不可见,显示所有窗口
|
||||||
|
hs.showAllWindows()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWindowVisible 检查窗口是否可见
|
||||||
|
func (hs *HotkeyService) isWindowVisible(window *application.WebviewWindow) bool {
|
||||||
|
return window.IsVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hideAllWindows 隐藏所有窗口
|
||||||
|
func (hs *HotkeyService) hideAllWindows() {
|
||||||
|
// 隐藏主窗口
|
||||||
|
hs.mainWindow.Hide()
|
||||||
|
|
||||||
|
// 隐藏所有子窗口
|
||||||
|
if hs.windowService != nil {
|
||||||
|
openWindows := hs.windowService.GetOpenWindows()
|
||||||
|
for _, windowInfo := range openWindows {
|
||||||
|
windowInfo.Window.Hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.logger.Debug("all windows hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// showAllWindows 显示所有窗口
|
||||||
|
func (hs *HotkeyService) showAllWindows() {
|
||||||
|
// 显示主窗口
|
||||||
|
hs.mainWindow.Show()
|
||||||
|
hs.mainWindow.Restore()
|
||||||
|
hs.mainWindow.Focus()
|
||||||
|
|
||||||
|
// 显示所有子窗口
|
||||||
|
if hs.windowService != nil {
|
||||||
|
openWindows := hs.windowService.GetOpenWindows()
|
||||||
|
for _, windowInfo := range openWindows {
|
||||||
|
windowInfo.Window.Show()
|
||||||
|
windowInfo.Window.Restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.logger.Debug("all windows shown")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceShutdown 关闭热键服务
|
// ServiceShutdown 关闭热键服务
|
||||||
|
|||||||
@@ -143,15 +143,17 @@ import (
|
|||||||
type HotkeyService struct {
|
type HotkeyService struct {
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
configService *ConfigService
|
configService *ConfigService
|
||||||
|
windowService *WindowService
|
||||||
app *application.App
|
app *application.App
|
||||||
|
mainWindow *application.WebviewWindow
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
currentHotkey *models.HotkeyCombo
|
currentHotkey *models.HotkeyCombo
|
||||||
isRegistered atomic.Bool
|
isRegistered atomic.Bool
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancelFunc atomic.Value // 使用atomic.Value存储cancel函数,避免竞态条件
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// HotkeyError 热键错误
|
// HotkeyError 热键错误
|
||||||
@@ -169,24 +171,46 @@ func (e *HotkeyError) Unwrap() error {
|
|||||||
return e.Err
|
return e.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// setCancelFunc 原子地设置cancel函数
|
||||||
|
func (hs *HotkeyService) setCancelFunc(cancel context.CancelFunc) {
|
||||||
|
hs.cancelFunc.Store(cancel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCancelFunc 原子地获取cancel函数
|
||||||
|
func (hs *HotkeyService) getCancelFunc() context.CancelFunc {
|
||||||
|
if cancel := hs.cancelFunc.Load(); cancel != nil {
|
||||||
|
return cancel.(context.CancelFunc)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// clearCancelFunc 原子地清除cancel函数
|
||||||
|
func (hs *HotkeyService) clearCancelFunc() {
|
||||||
|
hs.cancelFunc.Store((*context.CancelFunc)(nil))
|
||||||
|
}
|
||||||
|
|
||||||
// NewHotkeyService 创建热键服务实例
|
// NewHotkeyService 创建热键服务实例
|
||||||
func NewHotkeyService(configService *ConfigService, logger *log.LogService) *HotkeyService {
|
func NewHotkeyService(configService *ConfigService, windowService *WindowService, logger *log.LogService) *HotkeyService {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New()
|
logger = log.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &HotkeyService{
|
service := &HotkeyService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
configService: configService,
|
configService: configService,
|
||||||
|
windowService: windowService,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
|
||||||
}
|
}
|
||||||
|
// 初始化时设置cancel函数
|
||||||
|
service.setCancelFunc(cancel)
|
||||||
|
return service
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize 初始化热键服务
|
// Initialize 初始化热键服务
|
||||||
func (hs *HotkeyService) Initialize(app *application.App) error {
|
func (hs *HotkeyService) Initialize(app *application.App, mainWindow *application.WebviewWindow) error {
|
||||||
hs.app = app
|
hs.app = app
|
||||||
|
hs.mainWindow = mainWindow
|
||||||
|
|
||||||
if int(C.initX11Display()) == 0 {
|
if int(C.initX11Display()) == 0 {
|
||||||
return &HotkeyError{"init_x11", fmt.Errorf("failed to initialize X11 display")}
|
return &HotkeyError{"init_x11", fmt.Errorf("failed to initialize X11 display")}
|
||||||
@@ -253,7 +277,7 @@ func (hs *HotkeyService) RegisterHotkey(hotkey *models.HotkeyCombo) error {
|
|||||||
|
|
||||||
hs.currentHotkey = hotkey
|
hs.currentHotkey = hotkey
|
||||||
hs.isRegistered.Store(true)
|
hs.isRegistered.Store(true)
|
||||||
hs.cancel = cancel
|
hs.setCancelFunc(cancel)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -271,8 +295,9 @@ func (hs *HotkeyService) unregisterInternal() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if hs.cancel != nil {
|
// 原子地获取并调用cancel函数
|
||||||
hs.cancel()
|
if cancel := hs.getCancelFunc(); cancel != nil {
|
||||||
|
cancel()
|
||||||
hs.wg.Wait()
|
hs.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,6 +308,7 @@ func (hs *HotkeyService) unregisterInternal() error {
|
|||||||
|
|
||||||
hs.currentHotkey = nil
|
hs.currentHotkey = nil
|
||||||
hs.isRegistered.Store(false)
|
hs.isRegistered.Store(false)
|
||||||
|
hs.clearCancelFunc()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +324,8 @@ func (hs *HotkeyService) UpdateHotkey(enable bool, hotkey *models.HotkeyCombo) e
|
|||||||
func (hs *HotkeyService) hotkeyListener(ctx context.Context, ready chan<- error) {
|
func (hs *HotkeyService) hotkeyListener(ctx context.Context, ready chan<- error) {
|
||||||
defer hs.wg.Done()
|
defer hs.wg.Done()
|
||||||
|
|
||||||
ticker := time.NewTicker(100 * time.Millisecond)
|
// 优化轮询频率从100ms改为50ms,提高响应性
|
||||||
|
ticker := time.NewTicker(50 * time.Millisecond)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|
||||||
ready <- nil // 标记准备就绪
|
ready <- nil // 标记准备就绪
|
||||||
@@ -315,11 +342,61 @@ func (hs *HotkeyService) hotkeyListener(ctx context.Context, ready chan<- error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggleWindow 切换窗口
|
// toggleWindow 切换窗口显示状态
|
||||||
func (hs *HotkeyService) toggleWindow() {
|
func (hs *HotkeyService) toggleWindow() {
|
||||||
if hs.app != nil {
|
if hs.mainWindow == nil {
|
||||||
hs.app.EmitEvent("hotkey:toggle-window", nil)
|
hs.logger.Error("main window not set")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查主窗口是否可见
|
||||||
|
if hs.isWindowVisible(hs.mainWindow) {
|
||||||
|
// 如果主窗口可见,隐藏所有窗口
|
||||||
|
hs.hideAllWindows()
|
||||||
|
} else {
|
||||||
|
// 如果主窗口不可见,显示所有窗口
|
||||||
|
hs.showAllWindows()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isWindowVisible 检查窗口是否可见
|
||||||
|
func (hs *HotkeyService) isWindowVisible(window *application.WebviewWindow) bool {
|
||||||
|
return window.IsVisible()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hideAllWindows 隐藏所有窗口
|
||||||
|
func (hs *HotkeyService) hideAllWindows() {
|
||||||
|
// 隐藏主窗口
|
||||||
|
hs.mainWindow.Hide()
|
||||||
|
|
||||||
|
// 隐藏所有子窗口
|
||||||
|
if hs.windowService != nil {
|
||||||
|
openWindows := hs.windowService.GetOpenWindows()
|
||||||
|
for _, windowInfo := range openWindows {
|
||||||
|
windowInfo.Window.Hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.logger.Debug("all windows hidden")
|
||||||
|
}
|
||||||
|
|
||||||
|
// showAllWindows 显示所有窗口
|
||||||
|
func (hs *HotkeyService) showAllWindows() {
|
||||||
|
// 显示主窗口
|
||||||
|
hs.mainWindow.Show()
|
||||||
|
hs.mainWindow.Restore()
|
||||||
|
hs.mainWindow.Focus()
|
||||||
|
|
||||||
|
// 显示所有子窗口
|
||||||
|
if hs.windowService != nil {
|
||||||
|
openWindows := hs.windowService.GetOpenWindows()
|
||||||
|
for _, windowInfo := range openWindows {
|
||||||
|
windowInfo.Window.Show()
|
||||||
|
windowInfo.Window.Restore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hs.logger.Debug("all windows shown")
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyToX11KeyCode 键名转X11键码
|
// keyToX11KeyCode 键名转X11键码
|
||||||
@@ -386,7 +463,10 @@ func (hs *HotkeyService) IsRegistered() bool {
|
|||||||
|
|
||||||
// ServiceShutdown 关闭服务
|
// ServiceShutdown 关闭服务
|
||||||
func (hs *HotkeyService) ServiceShutdown() error {
|
func (hs *HotkeyService) ServiceShutdown() error {
|
||||||
hs.cancel()
|
// 原子地获取并调用cancel函数
|
||||||
|
if cancel := hs.getCancelFunc(); cancel != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
hs.wg.Wait()
|
hs.wg.Wait()
|
||||||
C.closeX11Display()
|
C.closeX11Display()
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -33,22 +33,24 @@ type MigrationProgress struct {
|
|||||||
|
|
||||||
// MigrationService 迁移服务
|
// MigrationService 迁移服务
|
||||||
type MigrationService struct {
|
type MigrationService struct {
|
||||||
logger *log.LogService
|
logger *log.LogService
|
||||||
mu sync.RWMutex
|
dbService *DatabaseService
|
||||||
progress atomic.Value // stores MigrationProgress
|
mu sync.RWMutex
|
||||||
|
progress atomic.Value // stores MigrationProgress
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMigrationService 创建迁移服务
|
// NewMigrationService 创建迁移服务
|
||||||
func NewMigrationService(logger *log.LogService) *MigrationService {
|
func NewMigrationService(dbService *DatabaseService, logger *log.LogService) *MigrationService {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New()
|
logger = log.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
ms := &MigrationService{
|
ms := &MigrationService{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
dbService: dbService,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化进度
|
// 初始化进度
|
||||||
@@ -104,6 +106,17 @@ func (ms *MigrationService) MigrateDirectory(srcPath, dstPath string) error {
|
|||||||
return ms.failWithError(err)
|
return ms.failWithError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 迁移前断开数据库连接
|
||||||
|
ms.updateProgress(MigrationProgress{
|
||||||
|
Status: MigrationStatusMigrating,
|
||||||
|
Progress: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
if ms.dbService != nil {
|
||||||
|
if err := ms.dbService.ServiceShutdown(); err != nil {
|
||||||
|
ms.logger.Error("Failed to close database connection", "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
// 执行原子迁移
|
// 执行原子迁移
|
||||||
if err := ms.atomicMove(ctx, srcPath, dstPath); err != nil {
|
if err := ms.atomicMove(ctx, srcPath, dstPath); err != nil {
|
||||||
return ms.failWithError(err)
|
return ms.failWithError(err)
|
||||||
@@ -177,10 +190,17 @@ func (ms *MigrationService) atomicMove(ctx context.Context, srcPath, dstPath str
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err := os.Rename(srcPath, dstPath); err == nil {
|
if err := os.Rename(srcPath, dstPath); err == nil {
|
||||||
|
// 重命名成功,更新进度到90%
|
||||||
|
ms.updateProgress(MigrationProgress{
|
||||||
|
Status: MigrationStatusMigrating,
|
||||||
|
Progress: 90,
|
||||||
|
})
|
||||||
|
ms.logger.Info("Directory migration completed using direct rename", "src", srcPath, "dst", dstPath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 重命名失败,使用压缩迁移
|
// 重命名失败,使用压缩迁移
|
||||||
|
ms.logger.Info("Direct rename failed, using compress migration", "src", srcPath, "dst", dstPath)
|
||||||
ms.updateProgress(MigrationProgress{
|
ms.updateProgress(MigrationProgress{
|
||||||
Status: MigrationStatusMigrating,
|
Status: MigrationStatusMigrating,
|
||||||
Progress: 30,
|
Progress: 30,
|
||||||
@@ -249,12 +269,18 @@ func (ms *MigrationService) compressMove(ctx context.Context, srcPath, dstPath s
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 验证迁移是否成功
|
||||||
|
if err := ms.verifyMigration(dstPath); err != nil {
|
||||||
|
// 迁移验证失败,清理目标目录
|
||||||
|
os.RemoveAll(dstPath)
|
||||||
|
return fmt.Errorf("migration verification failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// 删除源目录
|
// 删除源目录
|
||||||
ms.updateProgress(MigrationProgress{
|
ms.updateProgress(MigrationProgress{
|
||||||
Status: MigrationStatusMigrating,
|
Status: MigrationStatusMigrating,
|
||||||
Progress: 90,
|
Progress: 90,
|
||||||
})
|
})
|
||||||
|
|
||||||
os.RemoveAll(srcPath)
|
os.RemoveAll(srcPath)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -404,6 +430,29 @@ func (ms *MigrationService) isSubDirectory(parent, target string) bool {
|
|||||||
return len(target) > len(parent) && strings.HasPrefix(target, parent)
|
return len(target) > len(parent) && strings.HasPrefix(target, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verifyMigration 验证迁移是否成功
|
||||||
|
func (ms *MigrationService) verifyMigration(dstPath string) error {
|
||||||
|
// 检查目标目录是否存在
|
||||||
|
dstStat, err := os.Stat(dstPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("target directory does not exist: %v", err)
|
||||||
|
}
|
||||||
|
if !dstStat.IsDir() {
|
||||||
|
return fmt.Errorf("target path is not a directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单验证:检查目标目录是否非空
|
||||||
|
isEmpty, err := ms.isDirectoryEmpty(dstPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to check target directory: %v", err)
|
||||||
|
}
|
||||||
|
if isEmpty {
|
||||||
|
return fmt.Errorf("target directory is empty after migration")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CancelMigration 取消迁移
|
// CancelMigration 取消迁移
|
||||||
func (ms *MigrationService) CancelMigration() error {
|
func (ms *MigrationService) CancelMigration() error {
|
||||||
ms.mu.Lock()
|
ms.mu.Lock()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ func NewServiceManager() *ServiceManager {
|
|||||||
databaseService := NewDatabaseService(configService, logger)
|
databaseService := NewDatabaseService(configService, logger)
|
||||||
|
|
||||||
// 初始化迁移服务
|
// 初始化迁移服务
|
||||||
migrationService := NewMigrationService(logger)
|
migrationService := NewMigrationService(databaseService, logger)
|
||||||
|
|
||||||
// 初始化文档服务
|
// 初始化文档服务
|
||||||
documentService := NewDocumentService(databaseService, logger)
|
documentService := NewDocumentService(databaseService, logger)
|
||||||
@@ -52,7 +52,7 @@ func NewServiceManager() *ServiceManager {
|
|||||||
systemService := NewSystemService(logger)
|
systemService := NewSystemService(logger)
|
||||||
|
|
||||||
// 初始化热键服务
|
// 初始化热键服务
|
||||||
hotkeyService := NewHotkeyService(configService, logger)
|
hotkeyService := NewHotkeyService(configService, windowService, logger)
|
||||||
|
|
||||||
// 初始化对话服务
|
// 初始化对话服务
|
||||||
dialogService := NewDialogService(logger)
|
dialogService := NewDialogService(logger)
|
||||||
|
|||||||
11
main.go
11
main.go
@@ -25,6 +25,7 @@ var assets embed.FS
|
|||||||
// logs any error that might occur.
|
// logs any error that might occur.
|
||||||
func main() {
|
func main() {
|
||||||
serviceManager := services.NewServiceManager()
|
serviceManager := services.NewServiceManager()
|
||||||
|
var window *application.WebviewWindow
|
||||||
|
|
||||||
var encryptionKey = [32]byte{
|
var encryptionKey = [32]byte{
|
||||||
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
|
0x1e, 0x1f, 0x1c, 0x1d, 0x1a, 0x1b, 0x18, 0x19,
|
||||||
@@ -51,6 +52,13 @@ func main() {
|
|||||||
SingleInstance: &application.SingleInstanceOptions{
|
SingleInstance: &application.SingleInstanceOptions{
|
||||||
UniqueID: "com.voidraft",
|
UniqueID: "com.voidraft",
|
||||||
EncryptionKey: encryptionKey,
|
EncryptionKey: encryptionKey,
|
||||||
|
OnSecondInstanceLaunch: func(data application.SecondInstanceData) {
|
||||||
|
if window != nil {
|
||||||
|
window.Show()
|
||||||
|
window.Restore()
|
||||||
|
window.Focus()
|
||||||
|
}
|
||||||
|
},
|
||||||
AdditionalData: map[string]string{
|
AdditionalData: map[string]string{
|
||||||
"launchtime": time.Now().Local().String(),
|
"launchtime": time.Now().Local().String(),
|
||||||
},
|
},
|
||||||
@@ -82,6 +90,7 @@ func main() {
|
|||||||
URL: "/",
|
URL: "/",
|
||||||
})
|
})
|
||||||
mainWindow.Center()
|
mainWindow.Center()
|
||||||
|
window = mainWindow
|
||||||
|
|
||||||
// 获取托盘服务并设置应用引用
|
// 获取托盘服务并设置应用引用
|
||||||
trayService := serviceManager.GetTrayService()
|
trayService := serviceManager.GetTrayService()
|
||||||
@@ -96,7 +105,7 @@ func main() {
|
|||||||
|
|
||||||
// 初始化热键服务
|
// 初始化热键服务
|
||||||
hotkeyService := serviceManager.GetHotkeyService()
|
hotkeyService := serviceManager.GetHotkeyService()
|
||||||
err := hotkeyService.Initialize(app)
|
err := hotkeyService.Initialize(app, mainWindow)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user