package services import ( "cmbone/internal/models" "embed" "encoding/json" "fmt" "log" "runtime" "sync" "github.com/wailsapp/wails/v3/pkg/application" "github.com/wailsapp/wails/v3/pkg/events" ) type AppService struct { App *application.App SecondWindow *application.WebviewWindow MainWindow *application.WebviewWindow Config *AppConfigService windowStateMu sync.Mutex Tray *application.SystemTray I18nFS embed.FS Menu *application.Menu MenuQuit *application.MenuItem MenuSetting *application.MenuItem MenuSecondBar *application.MenuItem MenuVersion *application.MenuItem } const HotkeyShowSecondWindow = 1 const ( mainWindowWidth = 1280 mainWindowHeight = 800 mainWindowMinWidth = 1024 mainWindowMinHeight = 720 secondWindowWidth = 340 secondWindowHeight = 320 ) func ConfigureAppService(a *AppService, app *application.App, i18nFS embed.FS, suidb *SuiStore) { a.App = app a.I18nFS = i18nFS a.Config = NewAppConfigService(suidb) isWindows := runtime.GOOS == "windows" mainWindowState := a.loadMainWindowStateOptions() mainWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "main", Name: "main", Width: mainWindowState.Width, Height: mainWindowState.Height, MinWidth: mainWindowMinWidth, MinHeight: mainWindowMinHeight, InitialPosition: application.WindowCentered, StartState: mainWindowState.StartState, DisableResize: false, Frameless: isWindows, Mac: application.MacWindow{ InvisibleTitleBarHeight: 50, Backdrop: application.MacBackdropTranslucent, TitleBar: application.MacTitleBarHiddenInset, }, Windows: application.WindowsWindow{ DisableIcon: true, }, URL: "/", }) secondWindow := app.NewWebviewWindowWithOptions(application.WebviewWindowOptions{ Title: "second", Name: "second", Width: secondWindowWidth, Height: secondWindowHeight, Mac: application.MacWindow{ InvisibleTitleBarHeight: 50, TitleBar: application.MacTitleBarHidden, Backdrop: application.MacBackdropTransparent, }, BackgroundColour: application.NewRGB(27, 38, 54), URL: "/#/second", }) secondWindow.Hide() a.SecondWindow = secondWindow a.MainWindow = mainWindow a.registerMainWindowStatePersistence() RegisterWindowAndCallback(HotkeyShowSecondWindow, a.SecondWindow, func() { if a.SecondWindow == nil { return } a.SecondWindow.Show() a.SecondWindow.Focus() }) a.MainWindow.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { a.persistMainWindowState() a.MainWindow.Hide() e.Cancel() }) a.SecondWindow.RegisterHook(events.Common.WindowClosing, func(e *application.WindowEvent) { a.SecondWindow.Hide() e.Cancel() }) initTray(a, NewAppConfigService(suidb).GetLanguage()) } func (a *AppService) OpenSecondWindow() { if a.SecondWindow != nil { a.SecondWindow.Show() } } func initTray(a *AppService, langValue string) { a.Tray = a.App.NewSystemTray() a.Tray.SetLabel("cmbone") loadAppMenu(a, langValue) } func loadLang(i18nemb embed.FS, lang string) (*models.Lang, error) { file := fmt.Sprintf("frontend/src/locales/%s.json", lang) data, err := i18nemb.ReadFile(file) if err != nil { return nil, err } var i18nva models.Lang err = json.Unmarshal(data, &i18nva) return &i18nva, err } func loadAppMenu(a *AppService, langValue string) { lang, langerr := loadLang(a.I18nFS, langValue) if langerr != nil { log.Printf("load language %q failed: %v", langValue, langerr) return } a.Menu = application.NewMenu() a.MenuSecondBar = a.Menu.Add(lang.AppMenu.Second).OnClick(func(ctx *application.Context) { a.SecondWindow.Show() a.SecondWindow.Focus() }) a.MenuSetting = a.Menu.Add(lang.AppMenu.Preferences).OnClick(func(ctx *application.Context) { a.MainWindow.Show() a.MainWindow.Focus() }) a.Menu.AddSeparator() item := a.Menu.Add(lang.AppMenu.Version) item.SetEnabled(false) a.MenuVersion = item a.Menu.AddSeparator() a.MenuQuit = a.Menu.Add(lang.AppMenu.Quit).OnClick(func(ctx *application.Context) { a.App.Quit() }) a.Tray.SetMenu(a.Menu) } func updateAppMenu(a *AppService, langValue string) { lang, langerr := loadLang(a.I18nFS, langValue) if langerr != nil { log.Printf("load language %q failed: %v", langValue, langerr) return } a.MenuSecondBar.SetLabel(lang.AppMenu.Second) a.MenuSetting.SetLabel(lang.AppMenu.Preferences) a.MenuVersion.SetLabel(lang.AppMenu.Version) a.MenuQuit.SetLabel(lang.AppMenu.Quit) if runtime.GOOS == "darwin" { a.Menu.Update() } else { a.Tray.SetMenu(a.Menu) } } func (s *AppService) SetLanguage(lang string) error { current := s.Config.GetLanguage() if current == lang { return nil } updateAppMenu(s, lang) return s.Config.SetLanguage(lang) }