Files
Local-Cursor/internal/bridge/window.go
FuDie0915 db73cf5a39 chore: import Local-Cursor v1.1.8 source snapshot
Includes the current working directory changes. Historical commits and release binaries are excluded to limit Gitea storage.
Source base: d7515973fbfd6dd2001fa156c483455b8081c7cb
2026-11-10 01:24:28 +08:00

346 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package bridge
import (
"cursor/internal/buildinfo"
"cursor/internal/client"
"cursor/internal/cursor"
"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
modelConfigWindow *application.WebviewWindow
modelEditorWindow *application.WebviewWindow
skillWindow *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
}
// GetAppVersion 返回当前应用版本号。
func (s *WindowService) GetAppVersion() string {
return buildinfo.CurrentVersion()
}
// OpenConfigWindow 打开本地设置目录。
func (s *WindowService) OpenConfigWindow() {
logServiceCall("window", "open_config_directory")
if err := os.MkdirAll(client.ResolveSettingsRootPath(), 0o755); err != nil {
logServiceResult("window", "open_config_directory", err)
return
}
logServiceResult("window", "open_config_directory", openDirectory(client.ResolveSettingsRootPath()))
}
// OpenModelConfigWindow 打开模型配置独立窗口。如果窗口已存在则聚焦。
func (s *WindowService) OpenModelConfigWindow() {
logServiceCall("window", "open_model_config")
s.mu.Lock()
defer s.mu.Unlock()
if s.app == nil {
logServiceResult("window", "open_model_config", errServiceNotReady)
return
}
if s.modelConfigWindow != nil {
s.modelConfigWindow.Show()
s.modelConfigWindow.Focus()
logServiceResult("window", "open_model_config", nil)
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
logServiceResult("window", "open_model_config", nil)
}
// OpenModelEditorWindow 打开模型编辑器独立窗口。
// index < 0 表示新增,>= 0 表示编辑对应索引的适配器。
// adapterJSON 为编辑器初始数据的 JSON 字符串。
func (s *WindowService) OpenModelEditorWindow(index int, adapterJSON string) {
logServiceCall("window", "open_model_editor", "index", index)
s.mu.Lock()
defer s.mu.Unlock()
if s.app == nil {
logServiceResult("window", "open_model_editor", errServiceNotReady)
return
}
s.editorCtx = &modelEditorContext{
Index: index,
AdapterJSON: adapterJSON,
}
if s.modelEditorWindow != nil {
s.modelEditorWindow.Show()
s.modelEditorWindow.Focus()
logServiceResult("window", "open_model_editor", nil)
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.ButtonHidden,
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
logServiceResult("window", "open_model_editor", nil)
}
// 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,
}
}
// CursorStatus 描述 Cursor IDE 的连接状态。
type CursorStatus struct {
InstallFound bool `json:"installFound"`
InstallPath string `json:"installPath"`
ProcessRunning bool `json:"processRunning"`
}
// GetCursorStatus 返回 Cursor 安装目录探测结果与进程运行状态。
func (s *WindowService) GetCursorStatus() CursorStatus {
path := cursor.DetectCursorInstallPath()
return CursorStatus{
InstallFound: path != "",
InstallPath: path,
ProcessRunning: cursor.IsCursorRunning(),
}
}
// LocalizeCursor 关闭 Cursor 进程并运行社区汉化包。
// installDir 为空时自动检测 Cursor 安装目录。
func (s *WindowService) LocalizeCursor(installDir string) (string, error) {
logServiceCall("cursor", "localize")
output, err := cursor.LocalizeCursor(installDir)
logServiceResult("cursor", "localize", err)
return output, err
}
// OpenCursor 启动 Cursor IDE(installDir 为空时自动检测)。
func (s *WindowService) OpenCursor(installDir string) error {
logServiceCall("cursor", "open", "install_dir", installDir)
err := cursor.OpenCursor(installDir)
logServiceResult("cursor", "open", err)
return err
}
// SelectDirectory 弹出系统目录选择对话框,返回所选目录;取消时返回空字符串。
func (s *WindowService) SelectDirectory() (string, error) {
logServiceCall("window", "select_cursor_directory")
if s == nil || s.app == nil {
err := errServiceNotReady
logServiceResult("window", "select_cursor_directory", err)
return "", err
}
path, err := s.app.Dialog.OpenFile().
CanChooseDirectories(true).
CanChooseFiles(false).
SetTitle("选择 Cursor 安装目录").
PromptForSingleSelection()
logServiceResult("window", "select_cursor_directory", err)
return path, err
}
// OpenSkillWindow 打开 Skill 管理子窗口。如果窗口已存在则聚焦。
func (s *WindowService) OpenSkillWindow() {
logServiceCall("window", "open_skills")
s.mu.Lock()
defer s.mu.Unlock()
if s.app == nil {
logServiceResult("window", "open_skills", errServiceNotReady)
return
}
if s.skillWindow != nil {
s.skillWindow.Show()
s.skillWindow.Focus()
logServiceResult("window", "open_skills", nil)
return
}
win := s.app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "Skill 管理",
Width: 720,
Height: 640,
MinWidth: 620,
MinHeight: 520,
DisableResize: false,
Frameless: goruntime.GOOS == "windows",
URL: "/#/skills",
Hidden: false,
HideOnEscape: false,
MinimiseButtonState: application.ButtonEnabled,
MaximiseButtonState: application.ButtonEnabled,
CloseButtonState: application.ButtonEnabled,
BackgroundColour: application.RGBA{Red: 25, Green: 25, Blue: 25, Alpha: 255},
Windows: application.WindowsWindow{
HiddenOnTaskbar: false,
},
})
win.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) {
s.mu.Lock()
defer s.mu.Unlock()
s.skillWindow = nil
})
s.skillWindow = win
logServiceResult("window", "open_skills", nil)
}
// OpenHistoryWindow 打开日志目录。
func (s *WindowService) OpenHistoryWindow() {
logServiceCall("window", "open_logs_directory")
if err := os.MkdirAll(client.ResolveLogsRootPath(), 0o755); err != nil {
logServiceResult("window", "open_logs_directory", err)
return
}
logServiceResult("window", "open_logs_directory", openDirectory(client.ResolveLogsRootPath()))
}
// openDirectory 使用系统默认文件管理器打开目录并返回启动错误。
func openDirectory(path string) error {
if path == "" {
return nil
}
var err error
switch goruntime.GOOS {
case "darwin":
err = exec.Command("open", path).Start()
case "windows":
err = exec.Command("explorer", path).Start()
default:
err = exec.Command("xdg-open", path).Start()
}
return err
}