mirror of
https://github.com/mudler/LocalAI.git
synced 2026-02-14 08:31:10 -05:00
796 lines
22 KiB
Go
796 lines
22 KiB
Go
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)
|
|
}
|