Spaces:
Running
Running
Amlan-109
feat: Initial commit of LocalAI Amlan Edition with premium branding and personalization
750bbe6 | package launcher | |
| import ( | |
| "fmt" | |
| "log" | |
| "net/url" | |
| "fyne.io/fyne/v2" | |
| "fyne.io/fyne/v2/container" | |
| "fyne.io/fyne/v2/dialog" | |
| "fyne.io/fyne/v2/widget" | |
| ) | |
| // EnvVar represents an environment variable | |
| type EnvVar struct { | |
| Key string | |
| Value string | |
| } | |
| // LauncherUI handles the user interface | |
| type LauncherUI struct { | |
| // Status display | |
| statusLabel *widget.Label | |
| versionLabel *widget.Label | |
| // Control buttons | |
| startStopButton *widget.Button | |
| webUIButton *widget.Button | |
| updateButton *widget.Button | |
| downloadButton *widget.Button | |
| // Configuration | |
| modelsPathEntry *widget.Entry | |
| backendsPathEntry *widget.Entry | |
| addressEntry *widget.Entry | |
| logLevelSelect *widget.Select | |
| startOnBootCheck *widget.Check | |
| // Environment Variables | |
| envVarsData []EnvVar | |
| newEnvKeyEntry *widget.Entry | |
| newEnvValueEntry *widget.Entry | |
| updateEnvironmentDisplay func() | |
| // Logs | |
| logText *widget.Entry | |
| // Progress | |
| progressBar *widget.ProgressBar | |
| // Update management | |
| latestVersion string | |
| // Reference to launcher | |
| launcher *Launcher | |
| } | |
| // NewLauncherUI creates a new UI instance | |
| func NewLauncherUI() *LauncherUI { | |
| return &LauncherUI{ | |
| statusLabel: widget.NewLabel("Initializing..."), | |
| versionLabel: widget.NewLabel("Version: Unknown"), | |
| startStopButton: widget.NewButton("Start LocalAI", nil), | |
| webUIButton: widget.NewButton("Open WebUI", nil), | |
| updateButton: widget.NewButton("Check for Updates", nil), | |
| modelsPathEntry: widget.NewEntry(), | |
| backendsPathEntry: widget.NewEntry(), | |
| addressEntry: widget.NewEntry(), | |
| logLevelSelect: widget.NewSelect([]string{"error", "warn", "info", "debug", "trace"}, nil), | |
| startOnBootCheck: widget.NewCheck("Start LocalAI on system boot", nil), | |
| logText: widget.NewMultiLineEntry(), | |
| progressBar: widget.NewProgressBar(), | |
| envVarsData: []EnvVar{}, // Initialize the environment variables slice | |
| } | |
| } | |
| // CreateMainUI creates the main UI layout | |
| func (ui *LauncherUI) CreateMainUI(launcher *Launcher) *fyne.Container { | |
| ui.launcher = launcher | |
| ui.setupBindings() | |
| // Main tab with status and controls | |
| // Configuration is now the main content | |
| configTab := ui.createConfigTab() | |
| // Create a simple container instead of tabs since we only have settings | |
| tabs := container.NewVBox( | |
| widget.NewCard("LocalAI Launcher Settings", "", configTab), | |
| ) | |
| return tabs | |
| } | |
| // createConfigTab creates the configuration tab | |
| func (ui *LauncherUI) createConfigTab() *fyne.Container { | |
| // Path configuration | |
| pathsCard := widget.NewCard("Paths", "", container.NewGridWithColumns(2, | |
| widget.NewLabel("Models Path:"), | |
| ui.modelsPathEntry, | |
| widget.NewLabel("Backends Path:"), | |
| ui.backendsPathEntry, | |
| )) | |
| // Server configuration | |
| serverCard := widget.NewCard("Server", "", container.NewVBox( | |
| container.NewGridWithColumns(2, | |
| widget.NewLabel("Address:"), | |
| ui.addressEntry, | |
| widget.NewLabel("Log Level:"), | |
| ui.logLevelSelect, | |
| ), | |
| ui.startOnBootCheck, | |
| )) | |
| // Save button | |
| saveButton := widget.NewButton("Save Configuration", func() { | |
| ui.saveConfiguration() | |
| }) | |
| // Environment Variables section | |
| envCard := ui.createEnvironmentSection() | |
| return container.NewVBox( | |
| pathsCard, | |
| serverCard, | |
| envCard, | |
| saveButton, | |
| ) | |
| } | |
| // createEnvironmentSection creates the environment variables section for the config tab | |
| func (ui *LauncherUI) createEnvironmentSection() *fyne.Container { | |
| // Initialize environment variables widgets | |
| ui.newEnvKeyEntry = widget.NewEntry() | |
| ui.newEnvKeyEntry.SetPlaceHolder("Environment Variable Name") | |
| ui.newEnvValueEntry = widget.NewEntry() | |
| ui.newEnvValueEntry.SetPlaceHolder("Environment Variable Value") | |
| // Add button | |
| addButton := widget.NewButton("Add Environment Variable", func() { | |
| ui.addEnvironmentVariable() | |
| }) | |
| // Environment variables list with delete buttons | |
| ui.envVarsData = []EnvVar{} | |
| // Create container for environment variables | |
| envVarsContainer := container.NewVBox() | |
| // Update function to rebuild the environment variables display | |
| ui.updateEnvironmentDisplay = func() { | |
| envVarsContainer.Objects = nil | |
| for i, envVar := range ui.envVarsData { | |
| index := i // Capture index for closure | |
| // Create row with label and delete button | |
| envLabel := widget.NewLabel(fmt.Sprintf("%s = %s", envVar.Key, envVar.Value)) | |
| deleteBtn := widget.NewButton("Delete", func() { | |
| ui.confirmDeleteEnvironmentVariable(index) | |
| }) | |
| deleteBtn.Importance = widget.DangerImportance | |
| row := container.NewBorder(nil, nil, nil, deleteBtn, envLabel) | |
| envVarsContainer.Add(row) | |
| } | |
| envVarsContainer.Refresh() | |
| } | |
| // Create a scrollable container for the environment variables | |
| envScroll := container.NewScroll(envVarsContainer) | |
| envScroll.SetMinSize(fyne.NewSize(400, 150)) | |
| // Input section for adding new environment variables | |
| inputSection := container.NewVBox( | |
| container.NewGridWithColumns(2, | |
| ui.newEnvKeyEntry, | |
| ui.newEnvValueEntry, | |
| ), | |
| addButton, | |
| ) | |
| // Environment variables card | |
| envCard := widget.NewCard("Environment Variables", "", container.NewVBox( | |
| inputSection, | |
| widget.NewSeparator(), | |
| envScroll, | |
| )) | |
| return container.NewVBox(envCard) | |
| } | |
| // addEnvironmentVariable adds a new environment variable | |
| func (ui *LauncherUI) addEnvironmentVariable() { | |
| key := ui.newEnvKeyEntry.Text | |
| value := ui.newEnvValueEntry.Text | |
| log.Printf("addEnvironmentVariable: attempting to add %s=%s", key, value) | |
| log.Printf("addEnvironmentVariable: current ui.envVarsData has %d items: %v", len(ui.envVarsData), ui.envVarsData) | |
| if key == "" { | |
| log.Printf("addEnvironmentVariable: key is empty, showing error") | |
| dialog.ShowError(fmt.Errorf("environment variable name cannot be empty"), ui.launcher.window) | |
| return | |
| } | |
| // Check if key already exists | |
| for _, envVar := range ui.envVarsData { | |
| if envVar.Key == key { | |
| log.Printf("addEnvironmentVariable: key %s already exists, showing error", key) | |
| dialog.ShowError(fmt.Errorf("environment variable '%s' already exists", key), ui.launcher.window) | |
| return | |
| } | |
| } | |
| log.Printf("addEnvironmentVariable: adding new env var %s=%s", key, value) | |
| ui.envVarsData = append(ui.envVarsData, EnvVar{Key: key, Value: value}) | |
| log.Printf("addEnvironmentVariable: after adding, ui.envVarsData has %d items: %v", len(ui.envVarsData), ui.envVarsData) | |
| fyne.Do(func() { | |
| if ui.updateEnvironmentDisplay != nil { | |
| ui.updateEnvironmentDisplay() | |
| } | |
| // Clear input fields | |
| ui.newEnvKeyEntry.SetText("") | |
| ui.newEnvValueEntry.SetText("") | |
| }) | |
| log.Printf("addEnvironmentVariable: calling saveEnvironmentVariables") | |
| // Save to configuration | |
| ui.saveEnvironmentVariables() | |
| } | |
| // removeEnvironmentVariable removes an environment variable by index | |
| func (ui *LauncherUI) removeEnvironmentVariable(index int) { | |
| if index >= 0 && index < len(ui.envVarsData) { | |
| ui.envVarsData = append(ui.envVarsData[:index], ui.envVarsData[index+1:]...) | |
| fyne.Do(func() { | |
| if ui.updateEnvironmentDisplay != nil { | |
| ui.updateEnvironmentDisplay() | |
| } | |
| }) | |
| ui.saveEnvironmentVariables() | |
| } | |
| } | |
| // saveEnvironmentVariables saves environment variables to the configuration | |
| func (ui *LauncherUI) saveEnvironmentVariables() { | |
| if ui.launcher == nil { | |
| log.Printf("saveEnvironmentVariables: launcher is nil") | |
| return | |
| } | |
| config := ui.launcher.GetConfig() | |
| log.Printf("saveEnvironmentVariables: before - Environment vars: %v", config.EnvironmentVars) | |
| config.EnvironmentVars = make(map[string]string) | |
| for _, envVar := range ui.envVarsData { | |
| config.EnvironmentVars[envVar.Key] = envVar.Value | |
| log.Printf("saveEnvironmentVariables: adding %s=%s", envVar.Key, envVar.Value) | |
| } | |
| log.Printf("saveEnvironmentVariables: after - Environment vars: %v", config.EnvironmentVars) | |
| log.Printf("saveEnvironmentVariables: calling SetConfig with %d environment variables", len(config.EnvironmentVars)) | |
| err := ui.launcher.SetConfig(config) | |
| if err != nil { | |
| log.Printf("saveEnvironmentVariables: failed to save config: %v", err) | |
| } else { | |
| log.Printf("saveEnvironmentVariables: config saved successfully") | |
| } | |
| } | |
| // confirmDeleteEnvironmentVariable shows confirmation dialog for deleting an environment variable | |
| func (ui *LauncherUI) confirmDeleteEnvironmentVariable(index int) { | |
| if index >= 0 && index < len(ui.envVarsData) { | |
| envVar := ui.envVarsData[index] | |
| dialog.ShowConfirm("Remove Environment Variable", | |
| fmt.Sprintf("Remove environment variable '%s'?", envVar.Key), | |
| func(remove bool) { | |
| if remove { | |
| ui.removeEnvironmentVariable(index) | |
| } | |
| }, ui.launcher.window) | |
| } | |
| } | |
| // setupBindings sets up event handlers for UI elements | |
| func (ui *LauncherUI) setupBindings() { | |
| // Start/Stop button | |
| ui.startStopButton.OnTapped = func() { | |
| if ui.launcher.IsRunning() { | |
| ui.stopLocalAI() | |
| } else { | |
| ui.startLocalAI() | |
| } | |
| } | |
| // WebUI button | |
| ui.webUIButton.OnTapped = func() { | |
| ui.openWebUI() | |
| } | |
| ui.webUIButton.Disable() // Disabled until LocalAI is running | |
| // Update button | |
| ui.updateButton.OnTapped = func() { | |
| ui.checkForUpdates() | |
| } | |
| // Log level selection | |
| ui.logLevelSelect.OnChanged = func(selected string) { | |
| if ui.launcher != nil { | |
| config := ui.launcher.GetConfig() | |
| config.LogLevel = selected | |
| ui.launcher.SetConfig(config) | |
| } | |
| } | |
| } | |
| // startLocalAI starts the LocalAI service | |
| func (ui *LauncherUI) startLocalAI() { | |
| fyne.Do(func() { | |
| ui.startStopButton.Disable() | |
| }) | |
| ui.UpdateStatus("Starting LocalAI...") | |
| go func() { | |
| err := ui.launcher.StartLocalAI() | |
| if err != nil { | |
| ui.UpdateStatus("Failed to start: " + err.Error()) | |
| fyne.DoAndWait(func() { | |
| dialog.ShowError(err, ui.launcher.window) | |
| }) | |
| } else { | |
| fyne.Do(func() { | |
| ui.startStopButton.SetText("Stop LocalAI") | |
| ui.webUIButton.Enable() | |
| }) | |
| } | |
| fyne.Do(func() { | |
| ui.startStopButton.Enable() | |
| }) | |
| }() | |
| } | |
| // stopLocalAI stops the LocalAI service | |
| func (ui *LauncherUI) stopLocalAI() { | |
| fyne.Do(func() { | |
| ui.startStopButton.Disable() | |
| }) | |
| ui.UpdateStatus("Stopping LocalAI...") | |
| go func() { | |
| err := ui.launcher.StopLocalAI() | |
| if err != nil { | |
| fyne.DoAndWait(func() { | |
| dialog.ShowError(err, ui.launcher.window) | |
| }) | |
| } else { | |
| fyne.Do(func() { | |
| ui.startStopButton.SetText("Start LocalAI") | |
| ui.webUIButton.Disable() | |
| }) | |
| } | |
| fyne.Do(func() { | |
| ui.startStopButton.Enable() | |
| }) | |
| }() | |
| } | |
| // openWebUI opens the LocalAI WebUI in the default browser | |
| func (ui *LauncherUI) openWebUI() { | |
| webURL := ui.launcher.GetWebUIURL() | |
| parsedURL, err := url.Parse(webURL) | |
| if err != nil { | |
| dialog.ShowError(err, ui.launcher.window) | |
| return | |
| } | |
| // Open URL in default browser | |
| fyne.CurrentApp().OpenURL(parsedURL) | |
| } | |
| // saveConfiguration saves the current configuration | |
| func (ui *LauncherUI) saveConfiguration() { | |
| log.Printf("saveConfiguration: starting to save configuration") | |
| config := ui.launcher.GetConfig() | |
| log.Printf("saveConfiguration: current config Environment vars: %v", config.EnvironmentVars) | |
| log.Printf("saveConfiguration: ui.envVarsData has %d items: %v", len(ui.envVarsData), ui.envVarsData) | |
| config.ModelsPath = ui.modelsPathEntry.Text | |
| config.BackendsPath = ui.backendsPathEntry.Text | |
| config.Address = ui.addressEntry.Text | |
| config.LogLevel = ui.logLevelSelect.Selected | |
| config.StartOnBoot = ui.startOnBootCheck.Checked | |
| // Ensure environment variables are included in the configuration | |
| config.EnvironmentVars = make(map[string]string) | |
| for _, envVar := range ui.envVarsData { | |
| config.EnvironmentVars[envVar.Key] = envVar.Value | |
| log.Printf("saveConfiguration: adding env var %s=%s", envVar.Key, envVar.Value) | |
| } | |
| log.Printf("saveConfiguration: final config Environment vars: %v", config.EnvironmentVars) | |
| err := ui.launcher.SetConfig(config) | |
| if err != nil { | |
| log.Printf("saveConfiguration: failed to save config: %v", err) | |
| dialog.ShowError(err, ui.launcher.window) | |
| } else { | |
| log.Printf("saveConfiguration: config saved successfully") | |
| dialog.ShowInformation("Configuration", "Configuration saved successfully", ui.launcher.window) | |
| } | |
| } | |
| // checkForUpdates checks for available updates | |
| func (ui *LauncherUI) checkForUpdates() { | |
| fyne.Do(func() { | |
| ui.updateButton.Disable() | |
| }) | |
| ui.UpdateStatus("Checking for updates...") | |
| go func() { | |
| available, version, err := ui.launcher.CheckForUpdates() | |
| if err != nil { | |
| ui.UpdateStatus("Failed to check updates: " + err.Error()) | |
| fyne.DoAndWait(func() { | |
| dialog.ShowError(err, ui.launcher.window) | |
| }) | |
| } else if available { | |
| ui.latestVersion = version // Store the latest version | |
| ui.UpdateStatus("Update available: " + version) | |
| fyne.Do(func() { | |
| if ui.downloadButton != nil { | |
| ui.downloadButton.Enable() | |
| } | |
| }) | |
| ui.NotifyUpdateAvailable(version) | |
| } else { | |
| ui.UpdateStatus("No updates available") | |
| fyne.DoAndWait(func() { | |
| dialog.ShowInformation("Updates", "You are running the latest version", ui.launcher.window) | |
| }) | |
| } | |
| fyne.Do(func() { | |
| ui.updateButton.Enable() | |
| }) | |
| }() | |
| } | |
| // downloadUpdate downloads the latest update | |
| func (ui *LauncherUI) downloadUpdate() { | |
| // Use stored version or check for updates | |
| version := ui.latestVersion | |
| if version == "" { | |
| _, v, err := ui.launcher.CheckForUpdates() | |
| if err != nil { | |
| dialog.ShowError(err, ui.launcher.window) | |
| return | |
| } | |
| version = v | |
| ui.latestVersion = version | |
| } | |
| if version == "" { | |
| dialog.ShowError(fmt.Errorf("no version information available"), ui.launcher.window) | |
| return | |
| } | |
| // Disable buttons during download | |
| if ui.downloadButton != nil { | |
| fyne.Do(func() { | |
| ui.downloadButton.Disable() | |
| }) | |
| } | |
| fyne.Do(func() { | |
| ui.progressBar.Show() | |
| ui.progressBar.SetValue(0) | |
| }) | |
| ui.UpdateStatus("Downloading update " + version + "...") | |
| go func() { | |
| err := ui.launcher.DownloadUpdate(version, func(progress float64) { | |
| // Update progress bar | |
| fyne.Do(func() { | |
| ui.progressBar.SetValue(progress) | |
| }) | |
| // Update status with percentage | |
| percentage := int(progress * 100) | |
| ui.UpdateStatus(fmt.Sprintf("Downloading update %s... %d%%", version, percentage)) | |
| }) | |
| fyne.Do(func() { | |
| ui.progressBar.Hide() | |
| }) | |
| // Re-enable buttons after download | |
| if ui.downloadButton != nil { | |
| fyne.Do(func() { | |
| ui.downloadButton.Enable() | |
| }) | |
| } | |
| if err != nil { | |
| fyne.DoAndWait(func() { | |
| ui.UpdateStatus("Failed to download update: " + err.Error()) | |
| dialog.ShowError(err, ui.launcher.window) | |
| }) | |
| } else { | |
| fyne.DoAndWait(func() { | |
| ui.UpdateStatus("Update downloaded successfully") | |
| dialog.ShowInformation("Update", "Update downloaded successfully. Please restart the launcher to use the new version.", ui.launcher.window) | |
| }) | |
| } | |
| }() | |
| } | |
| // UpdateStatus updates the status label | |
| func (ui *LauncherUI) UpdateStatus(status string) { | |
| if ui.statusLabel != nil { | |
| fyne.Do(func() { | |
| ui.statusLabel.SetText(status) | |
| }) | |
| } | |
| } | |
| // OnLogUpdate handles new log content | |
| func (ui *LauncherUI) OnLogUpdate(logLine string) { | |
| if ui.logText != nil { | |
| fyne.Do(func() { | |
| currentText := ui.logText.Text | |
| ui.logText.SetText(currentText + logLine) | |
| // Auto-scroll to bottom (simplified) | |
| ui.logText.CursorRow = len(ui.logText.Text) | |
| }) | |
| } | |
| } | |
| // NotifyUpdateAvailable shows an update notification | |
| func (ui *LauncherUI) NotifyUpdateAvailable(version string) { | |
| if ui.launcher != nil && ui.launcher.window != nil { | |
| fyne.DoAndWait(func() { | |
| dialog.ShowConfirm("Update Available", | |
| "A new version ("+version+") is available. Would you like to download it?", | |
| func(confirmed bool) { | |
| if confirmed { | |
| ui.downloadUpdate() | |
| } | |
| }, ui.launcher.window) | |
| }) | |
| } | |
| } | |
| // LoadConfiguration loads the current configuration into UI elements | |
| func (ui *LauncherUI) LoadConfiguration() { | |
| if ui.launcher == nil { | |
| log.Printf("UI LoadConfiguration: launcher is nil") | |
| return | |
| } | |
| config := ui.launcher.GetConfig() | |
| log.Printf("UI LoadConfiguration: loading config - ModelsPath=%s, BackendsPath=%s, Address=%s, LogLevel=%s", | |
| config.ModelsPath, config.BackendsPath, config.Address, config.LogLevel) | |
| log.Printf("UI LoadConfiguration: Environment vars: %v", config.EnvironmentVars) | |
| ui.modelsPathEntry.SetText(config.ModelsPath) | |
| ui.backendsPathEntry.SetText(config.BackendsPath) | |
| ui.addressEntry.SetText(config.Address) | |
| ui.logLevelSelect.SetSelected(config.LogLevel) | |
| ui.startOnBootCheck.SetChecked(config.StartOnBoot) | |
| // Load environment variables | |
| ui.envVarsData = []EnvVar{} | |
| for key, value := range config.EnvironmentVars { | |
| ui.envVarsData = append(ui.envVarsData, EnvVar{Key: key, Value: value}) | |
| } | |
| if ui.updateEnvironmentDisplay != nil { | |
| fyne.Do(func() { | |
| ui.updateEnvironmentDisplay() | |
| }) | |
| } | |
| // Update version display | |
| version := ui.launcher.GetCurrentVersion() | |
| ui.versionLabel.SetText("Version: " + version) | |
| log.Printf("UI LoadConfiguration: configuration loaded successfully") | |
| } | |
| // showDownloadProgress shows a progress window for downloading LocalAI | |
| func (ui *LauncherUI) showDownloadProgress(version, title string) { | |
| fyne.DoAndWait(func() { | |
| // Create progress window using the launcher's app | |
| progressWindow := ui.launcher.app.NewWindow("Downloading LocalAI") | |
| progressWindow.Resize(fyne.NewSize(400, 250)) | |
| progressWindow.CenterOnScreen() | |
| // Progress bar | |
| progressBar := widget.NewProgressBar() | |
| progressBar.SetValue(0) | |
| // Status label | |
| statusLabel := widget.NewLabel("Preparing download...") | |
| // Release notes button | |
| releaseNotesButton := widget.NewButton("View Release Notes", func() { | |
| releaseNotesURL, err := ui.launcher.githubReleaseNotesURL(version) | |
| if err != nil { | |
| log.Printf("Failed to parse URL: %v", err) | |
| return | |
| } | |
| ui.launcher.app.OpenURL(releaseNotesURL) | |
| }) | |
| // Progress container | |
| progressContainer := container.NewVBox( | |
| widget.NewLabel(title), | |
| progressBar, | |
| statusLabel, | |
| widget.NewSeparator(), | |
| releaseNotesButton, | |
| ) | |
| progressWindow.SetContent(progressContainer) | |
| progressWindow.Show() | |
| // Start download in background | |
| go func() { | |
| err := ui.launcher.DownloadUpdate(version, func(progress float64) { | |
| // Update progress bar | |
| fyne.Do(func() { | |
| progressBar.SetValue(progress) | |
| percentage := int(progress * 100) | |
| statusLabel.SetText(fmt.Sprintf("Downloading... %d%%", percentage)) | |
| }) | |
| }) | |
| // Handle completion | |
| fyne.Do(func() { | |
| if err != nil { | |
| statusLabel.SetText(fmt.Sprintf("Download failed: %v", err)) | |
| // Show error dialog | |
| dialog.ShowError(err, progressWindow) | |
| } else { | |
| statusLabel.SetText("Download completed successfully!") | |
| progressBar.SetValue(1.0) | |
| // Show success dialog | |
| dialog.ShowConfirm("Installation Complete", | |
| "LocalAI has been downloaded and installed successfully. You can now start LocalAI from the launcher.", | |
| func(close bool) { | |
| progressWindow.Close() | |
| // Update status | |
| ui.UpdateStatus("LocalAI installed successfully") | |
| }, progressWindow) | |
| } | |
| }) | |
| }() | |
| }) | |
| } | |
| // UpdateRunningState updates UI based on LocalAI running state | |
| func (ui *LauncherUI) UpdateRunningState(isRunning bool) { | |
| fyne.Do(func() { | |
| if isRunning { | |
| ui.startStopButton.SetText("Stop LocalAI") | |
| ui.webUIButton.Enable() | |
| } else { | |
| ui.startStopButton.SetText("Start LocalAI") | |
| ui.webUIButton.Disable() | |
| } | |
| }) | |
| } | |
| // 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) | |
| } | |