From caab380c5d0da840a0c60e166d010abf351a3928 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Tue, 9 Sep 2025 00:14:58 +0200 Subject: [PATCH] feat(launcher): show welcome page (#6234) feat(launcher): add welcome window Signed-off-by: Ettore Di Giacinto --- cmd/launcher/internal/launcher.go | 8 ++ cmd/launcher/internal/launcher_test.go | 8 ++ cmd/launcher/internal/systray_manager.go | 10 ++ cmd/launcher/internal/ui.go | 118 +++++++++++++++++++++++ cmd/launcher/main.go | 6 ++ 5 files changed, 150 insertions(+) diff --git a/cmd/launcher/internal/launcher.go b/cmd/launcher/internal/launcher.go index a807b388b..0b5592fcc 100644 --- a/cmd/launcher/internal/launcher.go +++ b/cmd/launcher/internal/launcher.go @@ -31,6 +31,7 @@ type Config struct { StartOnBoot bool `json:"start_on_boot"` LogLevel string `json:"log_level"` EnvironmentVars map[string]string `json:"environment_vars"` + ShowWelcome *bool `json:"show_welcome"` } // Launcher represents the main launcher application @@ -148,6 +149,13 @@ func (l *Launcher) Initialize() error { log.Printf("Initializing empty EnvironmentVars map") } + // Set default welcome window preference + if l.config.ShowWelcome == nil { + true := true + l.config.ShowWelcome = &true + log.Printf("Setting default ShowWelcome: true") + } + // Create directories os.MkdirAll(l.config.ModelsPath, 0755) os.MkdirAll(l.config.BackendsPath, 0755) diff --git a/cmd/launcher/internal/launcher_test.go b/cmd/launcher/internal/launcher_test.go index 1b53a4c2e..15a2a24ee 100644 --- a/cmd/launcher/internal/launcher_test.go +++ b/cmd/launcher/internal/launcher_test.go @@ -48,6 +48,14 @@ var _ = Describe("Launcher", func() { config := launcherInstance.GetConfig() Expect(config.ModelsPath).ToNot(BeEmpty()) Expect(config.BackendsPath).ToNot(BeEmpty()) + }) + + It("should set default ShowWelcome to true", func() { + err := launcherInstance.Initialize() + Expect(err).ToNot(HaveOccurred()) + + config := launcherInstance.GetConfig() + Expect(config.ShowWelcome).To(BeTrue()) Expect(config.Address).To(Equal("127.0.0.1:8080")) Expect(config.LogLevel).To(Equal("info")) }) diff --git a/cmd/launcher/internal/systray_manager.go b/cmd/launcher/internal/systray_manager.go index 9bc36fd43..1c5192998 100644 --- a/cmd/launcher/internal/systray_manager.go +++ b/cmd/launcher/internal/systray_manager.go @@ -177,6 +177,9 @@ func (sm *SystrayManager) recreateMenu() { fyne.NewMenuItem("Settings", func() { sm.showSettings() }), + fyne.NewMenuItem("Show Welcome Window", func() { + sm.showWelcomeWindow() + }), fyne.NewMenuItem("Open Data Folder", func() { sm.openDataFolder() }), @@ -243,6 +246,13 @@ func (sm *SystrayManager) showSettings() { sm.window.RequestFocus() } +// showWelcomeWindow shows the welcome window +func (sm *SystrayManager) showWelcomeWindow() { + if sm.launcher.GetUI() != nil { + sm.launcher.GetUI().ShowWelcomeWindow() + } +} + // openDataFolder opens the data folder in file manager func (sm *SystrayManager) openDataFolder() { dataPath := sm.launcher.GetDataPath() diff --git a/cmd/launcher/internal/ui.go b/cmd/launcher/internal/ui.go index 7930abf41..7efd781d9 100644 --- a/cmd/launcher/internal/ui.go +++ b/cmd/launcher/internal/ui.go @@ -675,3 +675,121 @@ func (ui *LauncherUI) UpdateRunningState(isRunning bool) { } }) } + +// ShowWelcomeWindow displays the welcome window with helpful information +func (ui *LauncherUI) ShowWelcomeWindow() { + if ui.launcher == nil || ui.launcher.window == nil { + log.Printf("Cannot show welcome window: launcher or window is nil") + return + } + + fyne.DoAndWait(func() { + // Create welcome window + welcomeWindow := ui.launcher.app.NewWindow("Welcome to LocalAI Launcher") + welcomeWindow.Resize(fyne.NewSize(600, 500)) + welcomeWindow.CenterOnScreen() + welcomeWindow.SetCloseIntercept(func() { + welcomeWindow.Close() + }) + + // Title + titleLabel := widget.NewLabel("Welcome to LocalAI Launcher!") + titleLabel.TextStyle = fyne.TextStyle{Bold: true} + titleLabel.Alignment = fyne.TextAlignCenter + + // Welcome message + welcomeText := `LocalAI Launcher makes it easy to run LocalAI on your system. + +What you can do: +• Start and stop LocalAI server +• Configure models and backends paths +• Set environment variables +• Check for updates automatically +• Access LocalAI WebUI when running + +Getting Started: +1. Configure your models and backends paths +2. Click "Start LocalAI" to begin +3. Use "Open WebUI" to access the interface +4. Check the system tray for quick access` + + welcomeLabel := widget.NewLabel(welcomeText) + welcomeLabel.Wrapping = fyne.TextWrapWord + + // Useful links section + linksTitle := widget.NewLabel("Useful Links:") + linksTitle.TextStyle = fyne.TextStyle{Bold: true} + + // Create link buttons + docsButton := widget.NewButton("📚 Documentation", func() { + ui.openURL("https://localai.io/docs/") + }) + + githubButton := widget.NewButton("🐙 GitHub Repository", func() { + ui.openURL("https://github.com/mudler/LocalAI") + }) + + modelsButton := widget.NewButton("🤖 Model Gallery", func() { + ui.openURL("https://localai.io/models/") + }) + + communityButton := widget.NewButton("💬 Community", func() { + ui.openURL("https://discord.gg/XgwjKptP7Z") + }) + + // Checkbox to disable welcome window + dontShowAgainCheck := widget.NewCheck("Don't show this welcome window again", func(checked bool) { + if ui.launcher != nil { + config := ui.launcher.GetConfig() + v := !checked + config.ShowWelcome = &v + ui.launcher.SetConfig(config) + } + }) + + config := ui.launcher.GetConfig() + if config.ShowWelcome != nil { + dontShowAgainCheck.SetChecked(*config.ShowWelcome) + } + + // Close button + closeButton := widget.NewButton("Get Started", func() { + welcomeWindow.Close() + }) + closeButton.Importance = widget.HighImportance + + // Layout + linksContainer := container.NewVBox( + linksTitle, + docsButton, + githubButton, + modelsButton, + communityButton, + ) + + content := container.NewVBox( + titleLabel, + widget.NewSeparator(), + welcomeLabel, + widget.NewSeparator(), + linksContainer, + widget.NewSeparator(), + dontShowAgainCheck, + widget.NewSeparator(), + closeButton, + ) + + welcomeWindow.SetContent(content) + welcomeWindow.Show() + }) +} + +// openURL opens a URL in the default browser +func (ui *LauncherUI) openURL(urlString string) { + parsedURL, err := url.Parse(urlString) + if err != nil { + log.Printf("Failed to parse URL %s: %v", urlString, err) + return + } + fyne.CurrentApp().OpenURL(parsedURL) +} diff --git a/cmd/launcher/main.go b/cmd/launcher/main.go index 60ba1da08..6441dcf48 100644 --- a/cmd/launcher/main.go +++ b/cmd/launcher/main.go @@ -55,6 +55,12 @@ func main() { // Load configuration into UI launcher.GetUI().LoadConfiguration() launcher.GetUI().UpdateStatus("Ready") + + // Show welcome window if configured to do so + config := launcher.GetConfig() + if *config.ShowWelcome { + launcher.GetUI().ShowWelcomeWindow() + } } }()