Rules-based browser launcher for TUI + GNOME. switchyard.aly.codes
tui gome bowser go
0

Configure Feed

Select the types of activity you want to include in your feed.

src: cleanup docs

Aly Raffauf (Jan 23, 2026, 9:26 AM EST) b8c24ea4 0ceda4aa

+8 -29
+5 -7
src/desktop_actions.go
··· 10 10 "github.com/diamondburned/gotk4/pkg/gio/v2" 11 11 ) 12 12 13 - // Desktop file actions have IDs, names, and exec commands. 14 13 type DesktopAction struct { 15 14 ID string // action ID (e.g., "new-private-window") 16 15 Name string // display name (e.g., "New Private Window") 17 16 Exec string // Exec command line for this action 18 17 } 19 18 20 - // findDesktopFile locates a desktop file by ID using XDG Base Directory specification. 21 - // It searches in XDG_DATA_HOME and XDG_DATA_DIRS, plus Flatpak-specific locations. 19 + // Manually find desktop files according to XDG directories 22 20 func findDesktopFile(appID string) string { 23 - // Build list of data directories to search, following XDG spec 24 21 var dataDirs []string 25 22 26 23 // XDG_DATA_HOME (default: ~/.local/share) ··· 41 38 } 42 39 } 43 40 44 - // Add Flatpak-specific locations 41 + // Flatpaks 45 42 dataDirs = append(dataDirs, "/var/lib/flatpak/exports/share") 46 43 if home := os.Getenv("HOME"); home != "" { 47 44 dataDirs = append(dataDirs, home+"/.local/share/flatpak/exports/share") ··· 58 55 return "" 59 56 } 60 57 61 - /** ListDesktopActions returns available actions for an AppInfo by parsing its desktop file directly. For some reason, AppInfo does not expose actions. So we either call g_desktop_app_info_list_actions, which is not bound to Go, or parse it ourselves. I'd prefer to not use Cgo. **/ 58 + /** ListDesktopActions returns available actions for an AppInfo by parsing its desktop file directly. For some reason, AppInfo does not expose actions. So we either call g_desktop_app_info_list_actions, which is not bound to Go, or parse it ourselves. I'd prefer to not use CGo. **/ 62 59 func ListDesktopActions(appInfo *gio.AppInfo) []DesktopAction { 63 60 if appInfo == nil { 64 61 return nil ··· 82 79 } 83 80 84 81 // parseDesktopFileActions parses a desktop file and extracts all actions. 82 + // Sketchier than what Glib gives us, but Glib doesn't give us this anyway. 85 83 // Desktop files are INI-format with sections like: 86 84 // 87 85 // [Desktop Entry] ··· 122 120 if currentSection != "" && strings.HasPrefix(currentSection, "Desktop Action ") { 123 121 if currentActionID != "" && currentActionName != "" && currentActionExec != "" { 124 122 /** Only add if exec contains %u or %U (accepts URLs) 125 - This is commented because it breaks man Chromium-based browers completely, and even some Firefox-based brwosers allow passing URLs to actions unequiped to deal with them (e.g. LibreWolf). Looks less polished for the user, but works in more cases. **/ 123 + This is commented because it breaks many Chromium-based browers completely, and even some Firefox-based brwosers allow passing URLs to actions unequiped to deal with them (e.g. LibreWolf). Looks less polished for the user, but works in more cases. **/ 126 124 127 125 // if strings.Contains(currentActionExec, "%u") || strings.Contains(currentActionExec, "%U") { 128 126 actions = append(actions, DesktopAction{
-1
src/dialog_about.go
··· 7 7 "github.com/diamondburned/gotk4/pkg/gtk/v4" 8 8 ) 9 9 10 - // showAboutDialog displays the application's about dialog. 11 10 func showAboutDialog(parent *adw.Window) { 12 11 about := adw.NewAboutDialog() 13 12
+1 -2
src/dialog_condition_edit.go
··· 37 37 errorLabel.AddCSSClass("error") 38 38 errorLabel.SetVisible(false) 39 39 40 - // Function to validate and update error display 40 + // Validate and update error display 41 41 updateValidation := func() { 42 42 condType := indexToConditionType(typeRow.Selected()) 43 43 pattern := patternRow.Text() ··· 84 84 } 85 85 } 86 86 87 - // Update condition 88 87 cond.Type = indexToConditionType(selectedType) 89 88 cond.Pattern = pattern 90 89
-1
src/dialog_default_browser.go
··· 7 7 "github.com/diamondburned/gotk4/pkg/gtk/v4" 8 8 ) 9 9 10 - // showDefaultBrowserPrompt displays a dialog asking to set Switchyard as default browser. 11 10 func showDefaultBrowserPrompt(parent gtk.Widgetter, cfg *Config, updateUI func()) { 12 11 dialog := adw.NewAlertDialog( 13 12 "Set as Default Browser?",
+2 -6
src/dialog_helpers.go
··· 7 7 "github.com/diamondburned/gotk4/pkg/gtk/v4" 8 8 ) 9 9 10 - // dialogHeader creates a standard dialog header with cancel and action buttons. 11 - // The action button will have the "suggested-action" CSS class applied. 12 10 func dialogHeader(cancelLabel, actionLabel string, onCancel, onAction func()) (*adw.HeaderBar, *gtk.Button) { 13 11 header := adw.NewHeaderBar() 14 12 header.SetShowStartTitleButtons(false) ··· 82 80 return dialog, content 83 81 } 84 82 85 - // conditionTypeToIndex converts a condition type string to combo row index. 83 + // converts condition type string to combo row index. 86 84 func conditionTypeToIndex(condType string) uint { 87 85 switch condType { 88 86 case "domain": ··· 98 96 } 99 97 } 100 98 101 - // indexToConditionType converts a combo row index to condition type string. 99 + // convert a combo row index to condition type string. 102 100 func indexToConditionType(index uint) string { 103 101 switch index { 104 102 case 0: ··· 114 112 } 115 113 } 116 114 117 - // conditionTypeComboRow creates a standardized combo row for selecting condition types. 118 115 func conditionTypeComboRow(title string, initialType string) *adw.ComboRow { 119 116 typeRow := adw.NewComboRow() 120 117 typeRow.SetTitle(title) ··· 123 120 return typeRow 124 121 } 125 122 126 - // getConditionTypeLabels returns the display labels for condition types in order. 127 123 func getConditionTypeLabels() []string { 128 124 return []string{"Exact Domain", "URL Contains", "Wildcard", "Regex"} 129 125 }
-1
src/dialog_hidden_browsers.go
··· 7 7 "github.com/diamondburned/gotk4/pkg/gtk/v4" 8 8 ) 9 9 10 - // showHiddenBrowsersDialog displays a dialog for selecting which browsers to hide from the picker. 11 10 func showHiddenBrowsersDialog(parent *adw.Window, cfg *Config, browsers []*Browser) { 12 11 dialog := adw.NewAlertDialog( 13 12 "Hidden Browsers",
-1
src/dialog_rule_edit.go
··· 7 7 "github.com/diamondburned/gotk4/pkg/gtk/v4" 8 8 ) 9 9 10 - // showEditRuleDialog displays the edit rule dialog. 11 10 func showEditRuleDialog(parent *adw.Window, cfg *Config, rule *Rule, browsers []*Browser, rebuildRulesList func()) { 12 11 // Ensure rules have at least one condition 13 12 if len(rule.Conditions) == 0 {
-1
src/pattern.go
··· 7 7 "strings" 8 8 ) 9 9 10 - // matchesPattern checks if a URL matches a given pattern based on the pattern type 11 10 func matchesPattern(url, pattern, patternType string) bool { 12 11 switch patternType { 13 12 case "domain":
-2
src/picker_actions.go
··· 13 13 "github.com/diamondburned/gotk4/pkg/gtk/v4" 14 14 ) 15 15 16 - // showBrowserActionsMenu shows a context menu with desktop file actions 17 16 func showBrowserActionsMenu(btn *gtk.Button, browser *Browser, url string) { 18 17 actions := ListDesktopActions(browser.AppInfo) 19 18 if len(actions) == 0 { ··· 44 43 dialog.Present(parent) 45 44 } 46 45 47 - // setupPickerActions sets up the action group for the picker window 48 46 func setupPickerActions(win *adw.Window, app *adw.Application, browsers []*Browser, url string, onClose func()) *gio.SimpleActionGroup { 49 47 actionGroup := gio.NewSimpleActionGroup() 50 48
-2
src/settings_rules.go
··· 118 118 return id 119 119 } 120 120 121 - // Function to rebuild the rules list UI 122 121 var rebuildRulesList func() 123 122 124 - // Function to create a rule row 125 123 createRuleRow := func(ruleIndex int) *adw.ActionRow { 126 124 rule := &cfg.Rules[ruleIndex] 127 125
-4
src/validation.go
··· 8 8 "strings" 9 9 ) 10 10 11 - // validateConditions checks if all conditions have non-empty patterns and valid types 12 11 func validateConditions(conditions []Condition) bool { 13 12 validTypes := map[string]bool{ 14 13 "domain": true, ··· 34 33 return true 35 34 } 36 35 37 - // validateDomainPattern checks if a domain pattern is valid (no wildcards allowed). 38 36 // Domain patterns should be exact hostnames like "example.com" or "api.github.com". 39 37 func validateDomainPattern(pattern string) error { 40 38 if pattern == "" { ··· 72 70 return nil 73 71 } 74 72 75 - // validateGlobPattern checks if a glob (wildcard) pattern is valid. 76 73 // Glob patterns can contain * wildcards for matching multiple characters. 77 74 func validateGlobPattern(pattern string) error { 78 75 if pattern == "" { ··· 108 105 return nil 109 106 } 110 107 111 - // validateConditionPattern checks if a condition's pattern is valid for its type. 112 108 // Returns an error with a descriptive message if invalid, nil if valid. 113 109 func validateConditionPattern(condType, pattern string) error { 114 110 if pattern == "" {
-1
src/window_picker.go
··· 11 11 "github.com/diamondburned/gotk4/pkg/pango" 12 12 ) 13 13 14 - // showPickerWindow displays the browser picker window 15 14 func showPickerWindow(app *adw.Application, url string, browsers []*Browser, cfg *Config) { 16 15 17 16 // Filter hidden_browsers from the list