refactor(config): add popup behavior configuration
Add PopupBehavior enum with four modes: - split: open popups in split pane (default) - stacked: open popups in stacked pane - tabbed: open popups as new tab - windowed: open popups in new window This provides user control over popup placement while maintaining native WebKit lifecycle management.
- commit
- 504ed69539d84b4e1517a70efe4d69f23b6b9f16
- author
- bamen · · 2025-10-12 07:05 UTC
- parent
- c21700d
- signature
- unsigned
2 files · +30 -4
M internal/config/config.go +29 -4
| @@ -282,18 +282,42 @@ type TabKeyConfig struct { | |||
| 282 | 282 | PreviousTab string `mapstructure:"previous_tab" yaml:"previous_tab" json:"previous_tab"` | |
| 283 | 283 | } | |
| 284 | 284 | ||
| 285 | + | // PopupBehavior defines how popup windows should be opened | |
| 286 | + | type PopupBehavior string | |
| 287 | + | ||
| 288 | + | const ( | |
| 289 | + | // PopupBehaviorSplit opens popups in a split pane (default) | |
| 290 | + | PopupBehaviorSplit PopupBehavior = "split" | |
| 291 | + | // PopupBehaviorStacked opens popups in a stacked pane | |
| 292 | + | PopupBehaviorStacked PopupBehavior = "stacked" | |
| 293 | + | // PopupBehaviorTabbed opens popups as a new tab | |
| 294 | + | PopupBehaviorTabbed PopupBehavior = "tabbed" | |
| 295 | + | // PopupBehaviorWindowed opens popups in a new workspace window | |
| 296 | + | PopupBehaviorWindowed PopupBehavior = "windowed" | |
| 297 | + | ) | |
| 298 | + | ||
| 285 | 299 | // PopupBehaviorConfig defines handling for popup windows. | |
| 286 | 300 | type PopupBehaviorConfig struct { | |
| 287 | - | Placement string `mapstructure:"placement" yaml:"placement" json:"placement"` | |
| 288 | - | OpenInNewPane bool `mapstructure:"open_in_new_pane" yaml:"open_in_new_pane" json:"open_in_new_pane"` | |
| 289 | - | FollowPaneContext bool `mapstructure:"follow_pane_context" yaml:"follow_pane_context" json:"follow_pane_context"` | |
| 301 | + | // Behavior determines how popups are opened (split/stacked/tabbed/windowed) | |
| 302 | + | Behavior PopupBehavior `mapstructure:"behavior" yaml:"behavior" json:"behavior"` | |
| 303 | + | ||
| 304 | + | // Placement specifies direction for split behavior ("right", "left", "top", "bottom") | |
| 305 | + | // Only used when Behavior is "split" | |
| 306 | + | Placement string `mapstructure:"placement" yaml:"placement" json:"placement"` | |
| 307 | + | ||
| 308 | + | // OpenInNewPane controls whether popups are opened in workspace or blocked | |
| 309 | + | OpenInNewPane bool `mapstructure:"open_in_new_pane" yaml:"open_in_new_pane" json:"open_in_new_pane"` | |
| 310 | + | ||
| 311 | + | // FollowPaneContext determines if popup placement follows parent pane context | |
| 312 | + | FollowPaneContext bool `mapstructure:"follow_pane_context" yaml:"follow_pane_context" json:"follow_pane_context"` | |
| 290 | 313 | ||
| 291 | - | // New fields for distinguishing window types and behaviors | |
| 292 | 314 | // BlankTargetBehavior determines how to handle window.open(url, "_blank") intents | |
| 293 | 315 | // Accepted values: "pane" (default) or "tab" (future support) | |
| 294 | 316 | BlankTargetBehavior string `mapstructure:"blank_target_behavior" yaml:"blank_target_behavior" json:"blank_target_behavior"` | |
| 317 | + | ||
| 295 | 318 | // EnableSmartDetection uses WebKitWindowProperties to detect popup vs tab intents | |
| 296 | 319 | EnableSmartDetection bool `mapstructure:"enable_smart_detection" yaml:"enable_smart_detection" json:"enable_smart_detection"` | |
| 320 | + | ||
| 297 | 321 | // OAuthAutoClose enables auto-closing OAuth popups after successful auth redirects | |
| 298 | 322 | OAuthAutoClose bool `mapstructure:"oauth_auto_close" yaml:"oauth_auto_close" json:"oauth_auto_close"` | |
| 299 | 323 | } | |
| @@ -733,6 +757,7 @@ func (m *Manager) setDefaults() { | |||
| 733 | 757 | m.viper.SetDefault("workspace.tabs.close_tab", defaults.Workspace.Tabs.CloseTab) | |
| 734 | 758 | m.viper.SetDefault("workspace.tabs.next_tab", defaults.Workspace.Tabs.NextTab) | |
| 735 | 759 | m.viper.SetDefault("workspace.tabs.previous_tab", defaults.Workspace.Tabs.PreviousTab) | |
| 760 | + | m.viper.SetDefault("workspace.popups.behavior", string(defaults.Workspace.Popups.Behavior)) | |
| 736 | 761 | m.viper.SetDefault("workspace.popups.placement", defaults.Workspace.Popups.Placement) | |
| 737 | 762 | m.viper.SetDefault("workspace.popups.open_in_new_pane", defaults.Workspace.Popups.OpenInNewPane) | |
| 738 | 763 | m.viper.SetDefault("workspace.popups.follow_pane_context", defaults.Workspace.Popups.FollowPaneContext) | |
| @@ -282,18 +282,42 @@ type TabKeyConfig struct { | |||||
| 282 | PreviousTab string `mapstructure:"previous_tab" yaml:"previous_tab" json:"previous_tab"` | 282 | PreviousTab string `mapstructure:"previous_tab" yaml:"previous_tab" json:"previous_tab"` | ||
| 283 | } | 283 | } | ||
| 284 | 284 | ||||
| 285 | + | // PopupBehavior defines how popup windows should be opened | |||
| 286 | + | type PopupBehavior string | |||
| 287 | + | ||||
| 288 | + | const ( | |||
| 289 | + | // PopupBehaviorSplit opens popups in a split pane (default) | |||
| 290 | + | PopupBehaviorSplit PopupBehavior = "split" | |||
| 291 | + | // PopupBehaviorStacked opens popups in a stacked pane | |||
| 292 | + | PopupBehaviorStacked PopupBehavior = "stacked" | |||
| 293 | + | // PopupBehaviorTabbed opens popups as a new tab | |||
| 294 | + | PopupBehaviorTabbed PopupBehavior = "tabbed" | |||
| 295 | + | // PopupBehaviorWindowed opens popups in a new workspace window | |||
| 296 | + | PopupBehaviorWindowed PopupBehavior = "windowed" | |||
| 297 | + | ) | |||
| 298 | + | ||||
| 285 | // PopupBehaviorConfig defines handling for popup windows. | 299 | // PopupBehaviorConfig defines handling for popup windows. | ||
| 286 | type PopupBehaviorConfig struct { | 300 | type PopupBehaviorConfig struct { | ||
| 287 | - | Placement string `mapstructure:"placement" yaml:"placement" json:"placement"` | 301 | + | // Behavior determines how popups are opened (split/stacked/tabbed/windowed) |
| 288 | - | OpenInNewPane bool `mapstructure:"open_in_new_pane" yaml:"open_in_new_pane" json:"open_in_new_pane"` | 302 | + | Behavior PopupBehavior `mapstructure:"behavior" yaml:"behavior" json:"behavior"` |
| 289 | - | FollowPaneContext bool `mapstructure:"follow_pane_context" yaml:"follow_pane_context" json:"follow_pane_context"` | 303 | + | |
| 304 | + | // Placement specifies direction for split behavior ("right", "left", "top", "bottom") | |||
| 305 | + | // Only used when Behavior is "split" | |||
| 306 | + | Placement string `mapstructure:"placement" yaml:"placement" json:"placement"` | |||
| 307 | + | ||||
| 308 | + | // OpenInNewPane controls whether popups are opened in workspace or blocked | |||
| 309 | + | OpenInNewPane bool `mapstructure:"open_in_new_pane" yaml:"open_in_new_pane" json:"open_in_new_pane"` | |||
| 310 | + | ||||
| 311 | + | // FollowPaneContext determines if popup placement follows parent pane context | |||
| 312 | + | FollowPaneContext bool `mapstructure:"follow_pane_context" yaml:"follow_pane_context" json:"follow_pane_context"` | |||
| 290 | 313 | ||||
| 291 | - | // New fields for distinguishing window types and behaviors | |||
| 292 | // BlankTargetBehavior determines how to handle window.open(url, "_blank") intents | 314 | // BlankTargetBehavior determines how to handle window.open(url, "_blank") intents | ||
| 293 | // Accepted values: "pane" (default) or "tab" (future support) | 315 | // Accepted values: "pane" (default) or "tab" (future support) | ||
| 294 | BlankTargetBehavior string `mapstructure:"blank_target_behavior" yaml:"blank_target_behavior" json:"blank_target_behavior"` | 316 | BlankTargetBehavior string `mapstructure:"blank_target_behavior" yaml:"blank_target_behavior" json:"blank_target_behavior"` | ||
| 317 | + | ||||
| 295 | // EnableSmartDetection uses WebKitWindowProperties to detect popup vs tab intents | 318 | // EnableSmartDetection uses WebKitWindowProperties to detect popup vs tab intents | ||
| 296 | EnableSmartDetection bool `mapstructure:"enable_smart_detection" yaml:"enable_smart_detection" json:"enable_smart_detection"` | 319 | EnableSmartDetection bool `mapstructure:"enable_smart_detection" yaml:"enable_smart_detection" json:"enable_smart_detection"` | ||
| 320 | + | ||||
| 297 | // OAuthAutoClose enables auto-closing OAuth popups after successful auth redirects | 321 | // OAuthAutoClose enables auto-closing OAuth popups after successful auth redirects | ||
| 298 | OAuthAutoClose bool `mapstructure:"oauth_auto_close" yaml:"oauth_auto_close" json:"oauth_auto_close"` | 322 | OAuthAutoClose bool `mapstructure:"oauth_auto_close" yaml:"oauth_auto_close" json:"oauth_auto_close"` | ||
| 299 | } | 323 | } | ||
| @@ -733,6 +757,7 @@ func (m *Manager) setDefaults() { | |||||
| 733 | m.viper.SetDefault("workspace.tabs.close_tab", defaults.Workspace.Tabs.CloseTab) | 757 | m.viper.SetDefault("workspace.tabs.close_tab", defaults.Workspace.Tabs.CloseTab) | ||
| 734 | m.viper.SetDefault("workspace.tabs.next_tab", defaults.Workspace.Tabs.NextTab) | 758 | m.viper.SetDefault("workspace.tabs.next_tab", defaults.Workspace.Tabs.NextTab) | ||
| 735 | m.viper.SetDefault("workspace.tabs.previous_tab", defaults.Workspace.Tabs.PreviousTab) | 759 | m.viper.SetDefault("workspace.tabs.previous_tab", defaults.Workspace.Tabs.PreviousTab) | ||
| 760 | + | m.viper.SetDefault("workspace.popups.behavior", string(defaults.Workspace.Popups.Behavior)) | |||
| 736 | m.viper.SetDefault("workspace.popups.placement", defaults.Workspace.Popups.Placement) | 761 | m.viper.SetDefault("workspace.popups.placement", defaults.Workspace.Popups.Placement) | ||
| 737 | m.viper.SetDefault("workspace.popups.open_in_new_pane", defaults.Workspace.Popups.OpenInNewPane) | 762 | m.viper.SetDefault("workspace.popups.open_in_new_pane", defaults.Workspace.Popups.OpenInNewPane) | ||
| 738 | m.viper.SetDefault("workspace.popups.follow_pane_context", defaults.Workspace.Popups.FollowPaneContext) | 763 | m.viper.SetDefault("workspace.popups.follow_pane_context", defaults.Workspace.Popups.FollowPaneContext) | ||
M internal/config/defaults.go +1 -0
| @@ -191,6 +191,7 @@ func DefaultConfig() *Config { | |||
| 191 | 191 | PreviousTab: "cmdorctrl+shift+tab", | |
| 192 | 192 | }, | |
| 193 | 193 | Popups: PopupBehaviorConfig{ | |
| 194 | + | Behavior: PopupBehaviorSplit, // Default: open popups in split panes | |
| 194 | 195 | Placement: defaultPopupPlacement, | |
| 195 | 196 | OpenInNewPane: true, | |
| 196 | 197 | FollowPaneContext: true, | |
| @@ -191,6 +191,7 @@ func DefaultConfig() *Config { | |||||
| 191 | PreviousTab: "cmdorctrl+shift+tab", | 191 | PreviousTab: "cmdorctrl+shift+tab", | ||
| 192 | }, | 192 | }, | ||
| 193 | Popups: PopupBehaviorConfig{ | 193 | Popups: PopupBehaviorConfig{ | ||
| 194 | + | Behavior: PopupBehaviorSplit, // Default: open popups in split panes | |||
| 194 | Placement: defaultPopupPlacement, | 195 | Placement: defaultPopupPlacement, | ||
| 195 | OpenInNewPane: true, | 196 | OpenInNewPane: true, | ||
| 196 | FollowPaneContext: true, | 197 | FollowPaneContext: true, | ||