Files
cursor-byok/internal/bridge/window.go

258 lines
6.8 KiB
Go

package bridge
import (
"cursor/internal/buildinfo"
"cursor/internal/client"
"cursor/internal/updater"
"fmt"
"os"
"os/exec"
goruntime "runtime"
"sync"
"github.com/leaanthony/u"
"github.com/wailsapp/wails/v3/pkg/application"
"github.com/wailsapp/wails/v3/pkg/events"
)
// modelEditorContext 保存当前模型编辑器窗口的初始化上下文。
type modelEditorContext struct {
Index int `json:"index"`
AdapterJSON string `json:"adapterJSON"`
}
// WindowService 定义了当前模块中的 WindowService 类型。
type WindowService struct {
app *application.App
updater *updater.Manager
modelConfigWindow *application.WebviewWindow
modelEditorWindow *application.WebviewWindow
editorCtx *modelEditorContext
mu sync.RWMutex
}
// NewWindowService 用于处理与 NewWindowService 相关的逻辑。
func NewWindowService() *WindowService {
return &WindowService{}
}
// SetApp 用于处理与 SetApp 相关的逻辑。
func (s *WindowService) SetApp(app *application.App) {
s.mu.Lock()
defer s.mu.Unlock()
s.app = app
}
// SetUpdater 关联更新管理器,供前端手动触发检查更新。
func (s *WindowService) SetUpdater(manager *updater.Manager) {
s.mu.Lock()
defer s.mu.Unlock()
s.updater = manager
}
// GetAppVersion 返回当前应用版本号。
func (s *WindowService) GetAppVersion() string {
return buildinfo.CurrentVersion()
}
// CheckForUpdates 触发一次手动检查更新。
func (s *WindowService) CheckForUpdates() {
s.mu.RLock()
manager := s.updater
s.mu.RUnlock()
if manager == nil {
return
}
manager.CheckNow(true)
}
// InstallReadyUpdate 安装当前已下载完成的更新。
func (s *WindowService) InstallReadyUpdate() error {
s.mu.RLock()
manager := s.updater
s.mu.RUnlock()
if manager == nil {
return fmt.Errorf("更新管理器未初始化")
}
return manager.InstallReadyUpdate()
}
// OpenConfigWindow 打开本地设置目录。
func (s *WindowService) OpenConfigWindow() {
_ = os.MkdirAll(client.ResolveSettingsRootPath(), 0o755)
openDirectory(client.ResolveSettingsRootPath())
}
// OpenModelConfigWindow 打开模型配置独立窗口。如果窗口已存在则聚焦。
func (s *WindowService) OpenModelConfigWindow() {
s.mu.Lock()
defer s.mu.Unlock()
if s.app == nil {
return
}
if s.modelConfigWindow != nil {
s.modelConfigWindow.Show()
s.modelConfigWindow.Focus()
return
}
win := s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "模型配置",
Width: 980,
Height: 700,
MinWidth: 820,
MinHeight: 560,
DisableResize: false,
Frameless: goruntime.GOOS == "windows",
URL: "/#/model-config",
Hidden: false,
HideOnEscape: false,
MinimiseButtonState: application.ButtonEnabled,
MaximiseButtonState: application.ButtonEnabled,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.RGBA{Red: 25, Green: 25, Blue: 25, Alpha: 255},
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
DisableShadow: false,
TitleBar: application.MacTitleBar{
AppearsTransparent: true,
Hide: false,
HideTitle: true,
FullSizeContent: true,
UseToolbar: false,
HideToolbarSeparator: true,
},
WebviewPreferences: application.MacWebviewPreferences{
FullscreenEnabled: u.True,
TextInteractionEnabled: u.True,
AllowsBackForwardNavigationGestures: u.False,
},
},
Windows: application.WindowsWindow{
HiddenOnTaskbar: false,
},
})
win.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
s.mu.Lock()
defer s.mu.Unlock()
s.modelConfigWindow = nil
})
s.modelConfigWindow = win
}
// OpenModelEditorWindow 打开模型编辑器独立窗口。
// index < 0 表示新增,>= 0 表示编辑对应索引的适配器。
// adapterJSON 为编辑器初始数据的 JSON 字符串。
func (s *WindowService) OpenModelEditorWindow(index int, adapterJSON string) {
s.mu.Lock()
defer s.mu.Unlock()
if s.app == nil {
return
}
s.editorCtx = &modelEditorContext{
Index: index,
AdapterJSON: adapterJSON,
}
if s.modelEditorWindow != nil {
s.modelEditorWindow.Show()
s.modelEditorWindow.Focus()
return
}
title := "新增模型配置"
if index >= 0 {
title = "编辑模型配置"
}
win := s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: title,
Width: 840,
Height: 680,
MinWidth: 740,
MinHeight: 600,
DisableResize: false,
Frameless: goruntime.GOOS == "windows",
URL: fmt.Sprintf("/#/model-editor?index=%d", index),
Hidden: false,
HideOnEscape: false,
MinimiseButtonState: application.ButtonEnabled,
MaximiseButtonState: application.ButtonEnabled,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.RGBA{Red: 25, Green: 25, Blue: 25, Alpha: 255},
Mac: application.MacWindow{
Backdrop: application.MacBackdropLiquidGlass,
DisableShadow: false,
TitleBar: application.MacTitleBar{
AppearsTransparent: true,
Hide: false,
HideTitle: true,
FullSizeContent: true,
UseToolbar: false,
HideToolbarSeparator: true,
},
WebviewPreferences: application.MacWebviewPreferences{
FullscreenEnabled: u.False,
TextInteractionEnabled: u.True,
AllowsBackForwardNavigationGestures: u.False,
},
},
Windows: application.WindowsWindow{
HiddenOnTaskbar: false,
},
})
win.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
s.mu.Lock()
defer s.mu.Unlock()
s.modelEditorWindow = nil
s.editorCtx = nil
})
s.modelEditorWindow = win
}
// GetModelEditorContext 返回当前编辑器窗口的初始化上下文。
func (s *WindowService) GetModelEditorContext() map[string]any {
s.mu.RLock()
defer s.mu.RUnlock()
if s.editorCtx == nil {
return map[string]any{
"index": -1,
"adapterJSON": "{}",
}
}
return map[string]any{
"index": s.editorCtx.Index,
"adapterJSON": s.editorCtx.AdapterJSON,
}
}
// OpenHistoryWindow 用于处理与 OpenHistoryWindow 相关的逻辑。
func (s *WindowService) OpenHistoryWindow() {
_ = os.MkdirAll(client.ResolveLogsRootPath(), 0o755)
openDirectory(client.ResolveLogsRootPath())
}
// openDirectory 用于处理与 openDirectory 相关的逻辑。
func openDirectory(path string) {
if path == "" {
return
}
switch goruntime.GOOS {
case "darwin":
_ = exec.Command("open", path).Start()
case "windows":
_ = exec.Command("explorer", path).Start()
default:
_ = exec.Command("xdg-open", path).Start()
}
}