feat: wire gui instance start and delete confirmation

This commit is contained in:
QiuSW
2026-07-22 17:02:59 +08:00
parent 46578c8743
commit 176e3cdad2
12 changed files with 600 additions and 59 deletions
+99 -2
View File
@@ -10,6 +10,7 @@ import (
"os"
"path/filepath"
"strings"
"time"
"chub/internal/domain"
"chub/internal/platform/browser"
@@ -106,6 +107,7 @@ func runWindow(logger *slog.Logger) {
directoryPicker := files.NewDirectoryPicker()
shell.OnChooseDirectory(directoryPicker.ChooseDirectory, window.Invalidate)
discoverer := browser.NewDiscoverer()
shell.OnStartInstance(instanceStarter{launcher: browser.NewOSLauncher(), resolver: discoverer}.Start, window.Invalidate)
shell.OnPathSearch(func(ctx context.Context, field ui.PathField, current string) (string, error) {
switch field {
case ui.PathChromeExecutable:
@@ -138,12 +140,16 @@ func runWindow(logger *slog.Logger) {
shell.SetSettings(ui.SettingsState{ChromePath: saved.Settings.ChromePath, EdgePath: saved.Settings.EdgePath, DefaultDir: saved.Settings.DefaultDir, LogDir: saved.Settings.LogDir, CloseOnExit: saved.Settings.CloseOnExit})
rows := make([]ui.InstanceRow, 0, len(saved.Instances))
for _, item := range saved.Instances {
rows = append(rows, ui.InstanceRow{ID: item.ID, Name: item.Name, Browser: string(item.Launch.Kind), UserDataDir: item.Launch.UserDataDir, Status: "已退出"})
rows = append(rows, ui.InstanceRow{ID: item.ID, Name: item.Name, Browser: browserLabel(item.Launch.Kind), UserDataDir: item.Launch.UserDataDir, TargetURL: item.Launch.TargetURL, Status: "已退出"})
}
shell.SetInstances(rows)
shell.OnSave(func(value ui.SettingsState) {
saved.Settings = config.Settings{ChromePath: value.ChromePath, EdgePath: value.EdgePath, DefaultDir: value.DefaultDir, LogDir: value.LogDir, CloseOnExit: value.CloseOnExit}
go saveSettings(logger, store, saved)
go saveSettings(logger, store, configSnapshot(saved))
})
shell.OnInstancesChanged(func(rows []ui.InstanceRow) {
saved.Instances = mergeInstanceConfig(saved.Instances, rows)
go saveSettings(logger, store, configSnapshot(saved))
})
}
}
@@ -170,3 +176,94 @@ func saveSettings(logger *slog.Logger, store *config.Store, value config.File) {
logger.Error("save settings", "error", err)
}
}
type executableResolver interface {
Resolve(context.Context, domain.BrowserKind, string) (string, error)
}
type processLauncher interface {
Start(context.Context, domain.LaunchSpec) (browser.ProcessHandle, error)
}
type instanceStarter struct {
launcher processLauncher
resolver executableResolver
}
func (s instanceStarter) Start(ctx context.Context, row ui.InstanceRow, settings ui.SettingsState) (int, error) {
kind, err := browserKind(row.Browser)
if err != nil {
return 0, err
}
if s.launcher == nil || s.resolver == nil {
return 0, errors.New("浏览器启动服务尚未准备好")
}
configuredExecutable := settings.ChromePath
if kind == domain.BrowserEdge {
configuredExecutable = settings.EdgePath
}
executable, err := s.resolver.Resolve(ctx, kind, configuredExecutable)
if err != nil {
return 0, fmt.Errorf("无法找到%s可执行文件:%w", browserLabel(kind), err)
}
handle, err := s.launcher.Start(ctx, domain.LaunchSpec{
Kind: kind,
Executable: executable,
UserDataDir: row.UserDataDir,
TargetURL: row.TargetURL,
})
if err != nil {
return 0, err
}
return handle.PID(), nil
}
func browserKind(value string) (domain.BrowserKind, error) {
switch strings.ToLower(strings.TrimSpace(value)) {
case "chrome":
return domain.BrowserChrome, nil
case "edge":
return domain.BrowserEdge, nil
default:
return "", fmt.Errorf("不支持的浏览器类型:%s", value)
}
}
func browserLabel(kind domain.BrowserKind) string {
if kind == domain.BrowserEdge {
return "Edge"
}
return "Chrome"
}
func mergeInstanceConfig(existing []config.Instance, rows []ui.InstanceRow) []config.Instance {
byID := make(map[string]config.Instance, len(existing))
for _, item := range existing {
byID[item.ID] = item
}
updated := make([]config.Instance, 0, len(rows))
for _, row := range rows {
kind, err := browserKind(row.Browser)
if err != nil {
continue
}
item := byID[row.ID]
item.ID = row.ID
item.Name = row.Name
item.Launch.Kind = kind
item.Launch.UserDataDir = row.UserDataDir
item.Launch.TargetURL = row.TargetURL
item.UpdatedAt = time.Now()
updated = append(updated, item)
}
return updated
}
func configSnapshot(value config.File) config.File {
clone := value
clone.Instances = append([]config.Instance(nil), value.Instances...)
for i := range clone.Instances {
clone.Instances[i].Launch.ExtraArgs = append([]string(nil), clone.Instances[i].Launch.ExtraArgs...)
}
return clone
}