fix(ui): improve omnibox pane handling and Ctrl+L toggle
- commit
- b2baf192033375f30f9137f22e55735ab3b49bd1
- author
- bnema · · 2025-12-22 21:52 UTC
- parent
- f95a7fa
- signature
- unsigned
2 files · +70 -57
M internal/ui/component/omnibox.go +47 -51
| @@ -20,12 +20,13 @@ import ( | |||
| 20 | 20 | ) | |
| 21 | 21 | ||
| 22 | 22 | const ( | |
| 23 | - | omniboxWidthPct = 0.8 // 80% of parent window width | |
| 24 | - | omniboxMaxWidth = 800 // Maximum width in pixels | |
| 25 | - | omniboxHeightPct = 0.6 // 60% of parent window height | |
| 26 | - | omniboxMaxResults = 10 | |
| 27 | - | omniboxTopMarginPct = 0.2 // 20% from top | |
| 28 | - | debounceDelayMs = 150 | |
| 23 | + | omniboxWidthPct = 0.8 // 80% of parent window width | |
| 24 | + | omniboxMaxWidth = 800 // Maximum width in pixels | |
| 25 | + | omniboxFallbackHeight = 600 // Fallback height when overlay not yet allocated | |
| 26 | + | omniboxHeightPct = 0.6 // 60% of parent window height | |
| 27 | + | omniboxMaxResults = 10 | |
| 28 | + | omniboxTopMarginPct = 0.2 // 20% from top | |
| 29 | + | debounceDelayMs = 150 | |
| 29 | 30 | ) | |
| 30 | 31 | ||
| 31 | 32 | // ViewMode distinguishes history search from favorites display. | |
| @@ -110,9 +111,6 @@ type Omnibox struct { | |||
| 110 | 111 | ||
| 111 | 112 | // Callback retention: keep GTK signal callbacks reachable by Go GC. | |
| 112 | 113 | retainedCallbacks []interface{} | |
| 113 | - | ||
| 114 | - | // Used to rate-limit allocation-related logs. | |
| 115 | - | loggedInvalidOverlayDims bool | |
| 116 | 114 | } | |
| 117 | 115 | ||
| 118 | 116 | // OmniboxConfig holds configuration for creating an Omnibox. | |
| @@ -195,33 +193,10 @@ func (o *Omnibox) resizeAndCenter(rowCount int) { | |||
| 195 | 193 | return | |
| 196 | 194 | } | |
| 197 | 195 | ||
| 198 | - | // Use GetAllocatedWidth/Height for actual rendered size | |
| 196 | + | // Use GetAllocatedWidth for actual rendered size (height not needed here) | |
| 199 | 197 | parentWidth := o.parentOverlay.GetAllocatedWidth() | |
| 200 | - | parentHeight := o.parentOverlay.GetAllocatedHeight() | |
| 201 | - | ||
| 202 | - | if parentWidth <= 0 || parentHeight <= 0 { | |
| 203 | - | // Overlay may not be allocated yet (e.g., right after adding/switching panes). | |
| 204 | - | // Retry once in the next main-loop tick and avoid spamming logs. | |
| 205 | - | if !o.loggedInvalidOverlayDims { | |
| 206 | - | log := logging.FromContext(o.ctx) | |
| 207 | - | log.Error(). | |
| 208 | - | Int("parent_width", parentWidth). | |
| 209 | - | Int("parent_height", parentHeight). | |
| 210 | - | Msg("omnibox: invalid parent overlay dimensions") | |
| 211 | - | o.loggedInvalidOverlayDims = true | |
| 212 | - | } | |
| 213 | - | ||
| 214 | - | var cb glib.SourceFunc = func(uintptr) bool { | |
| 215 | - | o.mu.RLock() | |
| 216 | - | visible := o.visible | |
| 217 | - | o.mu.RUnlock() | |
| 218 | - | if visible { | |
| 219 | - | o.resizeAndCenter(rowCount) | |
| 220 | - | } | |
| 221 | - | return false | |
| 222 | - | } | |
| 223 | - | glib.IdleAdd(&cb, 0) | |
| 224 | - | return | |
| 198 | + | if parentWidth <= 0 { | |
| 199 | + | parentWidth = omniboxMaxWidth | |
| 225 | 200 | } | |
| 226 | 201 | ||
| 227 | 202 | width := int(float64(parentWidth) * omniboxWidthPct) | |
| @@ -493,6 +468,13 @@ func (o *Omnibox) handleKeyPress(keyval uint, state gdk.ModifierType) bool { | |||
| 493 | 468 | o.Hide(o.ctx) | |
| 494 | 469 | return true | |
| 495 | 470 | ||
| 471 | + | case uint(gdk.KEY_l), uint(gdk.KEY_L): | |
| 472 | + | // Ctrl+L toggles omnibox (hides when visible) | |
| 473 | + | if ctrl { | |
| 474 | + | o.Hide(o.ctx) | |
| 475 | + | return true | |
| 476 | + | } | |
| 477 | + | ||
| 496 | 478 | case uint(gdk.KEY_Return), uint(gdk.KEY_KP_Enter): | |
| 497 | 479 | o.navigateToSelected() | |
| 498 | 480 | return true | |
| @@ -532,24 +514,31 @@ func (o *Omnibox) handleKeyPress(keyval uint, state gdk.ModifierType) bool { | |||
| 532 | 514 | } | |
| 533 | 515 | return false // Let entry handle 'y' for typing | |
| 534 | 516 | ||
| 517 | + | default: | |
| 518 | + | return o.handleCtrlNumberShortcut(keyval, ctrl) | |
| 519 | + | } | |
| 520 | + | ||
| 521 | + | return false // Let entry handle the key | |
| 522 | + | } | |
| 523 | + | ||
| 524 | + | // handleCtrlNumberShortcut handles Ctrl+1-9 and Ctrl+0 for quick navigation. | |
| 525 | + | func (o *Omnibox) handleCtrlNumberShortcut(keyval uint, ctrl bool) bool { | |
| 526 | + | if !ctrl { | |
| 527 | + | return false | |
| 528 | + | } | |
| 529 | + | ||
| 530 | + | const tenthItemIndex = 9 | |
| 531 | + | switch keyval { | |
| 535 | 532 | case uint(gdk.KEY_1), uint(gdk.KEY_2), uint(gdk.KEY_3), uint(gdk.KEY_4), uint(gdk.KEY_5), | |
| 536 | 533 | uint(gdk.KEY_6), uint(gdk.KEY_7), uint(gdk.KEY_8), uint(gdk.KEY_9): | |
| 537 | - | if ctrl { | |
| 538 | - | const firstShortcutKey = uint(gdk.KEY_1) | |
| 539 | - | index := int(keyval - firstShortcutKey) // 0-8 | |
| 540 | - | o.selectAndNavigate(index) | |
| 541 | - | return true | |
| 542 | - | } | |
| 543 | - | ||
| 534 | + | index := int(keyval - uint(gdk.KEY_1)) | |
| 535 | + | o.selectAndNavigate(index) | |
| 536 | + | return true | |
| 544 | 537 | case uint(gdk.KEY_0): | |
| 545 | - | if ctrl { | |
| 546 | - | const tenthItemIndex = 9 | |
| 547 | - | o.selectAndNavigate(tenthItemIndex) | |
| 548 | - | return true | |
| 549 | - | } | |
| 538 | + | o.selectAndNavigate(tenthItemIndex) | |
| 539 | + | return true | |
| 550 | 540 | } | |
| 551 | - | ||
| 552 | - | return false // Let entry handle the key | |
| 541 | + | return false | |
| 553 | 542 | } | |
| 554 | 543 | ||
| 555 | 544 | // onEntryChanged handles text input changes with debouncing. | |
| @@ -1245,9 +1234,17 @@ func (o *Omnibox) Show(ctx context.Context, query string) { | |||
| 1245 | 1234 | o.scrolledWin.SetMaxContentHeight(0) | |
| 1246 | 1235 | } | |
| 1247 | 1236 | ||
| 1248 | - | // Set width and vertical positioning (20% from top) | |
| 1237 | + | // Get parent dimensions with fallback if not yet allocated | |
| 1249 | 1238 | parentWidth := o.parentOverlay.GetAllocatedWidth() | |
| 1250 | 1239 | parentHeight := o.parentOverlay.GetAllocatedHeight() | |
| 1240 | + | if parentWidth <= 0 { | |
| 1241 | + | parentWidth = omniboxMaxWidth | |
| 1242 | + | } | |
| 1243 | + | if parentHeight <= 0 { | |
| 1244 | + | parentHeight = omniboxFallbackHeight | |
| 1245 | + | } | |
| 1246 | + | ||
| 1247 | + | // Set width and vertical positioning (20% from top) | |
| 1251 | 1248 | width := int(float64(parentWidth) * omniboxWidthPct) | |
| 1252 | 1249 | if width > omniboxMaxWidth { | |
| 1253 | 1250 | width = omniboxMaxWidth | |
| @@ -1258,7 +1255,6 @@ func (o *Omnibox) Show(ctx context.Context, query string) { | |||
| 1258 | 1255 | o.outerBox.SetMarginTop(marginTop) | |
| 1259 | 1256 | ||
| 1260 | 1257 | // Show the omnibox | |
| 1261 | - | o.loggedInvalidOverlayDims = false | |
| 1262 | 1258 | o.outerBox.SetVisible(true) | |
| 1263 | 1259 | ||
| 1264 | 1260 | // Trigger initial resize (will be updated when results arrive) | |
| @@ -20,12 +20,13 @@ import ( | |||||
| 20 | ) | 20 | ) | ||
| 21 | 21 | ||||
| 22 | const ( | 22 | const ( | ||
| 23 | - | omniboxWidthPct = 0.8 // 80% of parent window width | 23 | + | omniboxWidthPct = 0.8 // 80% of parent window width |
| 24 | - | omniboxMaxWidth = 800 // Maximum width in pixels | 24 | + | omniboxMaxWidth = 800 // Maximum width in pixels |
| 25 | - | omniboxHeightPct = 0.6 // 60% of parent window height | 25 | + | omniboxFallbackHeight = 600 // Fallback height when overlay not yet allocated |
| 26 | - | omniboxMaxResults = 10 | 26 | + | omniboxHeightPct = 0.6 // 60% of parent window height |
| 27 | - | omniboxTopMarginPct = 0.2 // 20% from top | 27 | + | omniboxMaxResults = 10 |
| 28 | - | debounceDelayMs = 150 | 28 | + | omniboxTopMarginPct = 0.2 // 20% from top |
| 29 | + | debounceDelayMs = 150 | |||
| 29 | ) | 30 | ) | ||
| 30 | 31 | ||||
| 31 | // ViewMode distinguishes history search from favorites display. | 32 | // ViewMode distinguishes history search from favorites display. | ||
| @@ -110,9 +111,6 @@ type Omnibox struct { | |||||
| 110 | 111 | ||||
| 111 | // Callback retention: keep GTK signal callbacks reachable by Go GC. | 112 | // Callback retention: keep GTK signal callbacks reachable by Go GC. | ||
| 112 | retainedCallbacks []interface{} | 113 | retainedCallbacks []interface{} | ||
| 113 | - | ||||
| 114 | - | // Used to rate-limit allocation-related logs. | |||
| 115 | - | loggedInvalidOverlayDims bool | |||
| 116 | } | 114 | } | ||
| 117 | 115 | ||||
| 118 | // OmniboxConfig holds configuration for creating an Omnibox. | 116 | // OmniboxConfig holds configuration for creating an Omnibox. | ||
| @@ -195,33 +193,10 @@ func (o *Omnibox) resizeAndCenter(rowCount int) { | |||||
| 195 | return | 193 | return | ||
| 196 | } | 194 | } | ||
| 197 | 195 | ||||
| 198 | - | // Use GetAllocatedWidth/Height for actual rendered size | 196 | + | // Use GetAllocatedWidth for actual rendered size (height not needed here) |
| 199 | parentWidth := o.parentOverlay.GetAllocatedWidth() | 197 | parentWidth := o.parentOverlay.GetAllocatedWidth() | ||
| 200 | - | parentHeight := o.parentOverlay.GetAllocatedHeight() | 198 | + | if parentWidth <= 0 { |
| 201 | - | 199 | + | parentWidth = omniboxMaxWidth | |
| 202 | - | if parentWidth <= 0 || parentHeight <= 0 { | |||
| 203 | - | // Overlay may not be allocated yet (e.g., right after adding/switching panes). | |||
| 204 | - | // Retry once in the next main-loop tick and avoid spamming logs. | |||
| 205 | - | if !o.loggedInvalidOverlayDims { | |||
| 206 | - | log := logging.FromContext(o.ctx) | |||
| 207 | - | log.Error(). | |||
| 208 | - | Int("parent_width", parentWidth). | |||
| 209 | - | Int("parent_height", parentHeight). | |||
| 210 | - | Msg("omnibox: invalid parent overlay dimensions") | |||
| 211 | - | o.loggedInvalidOverlayDims = true | |||
| 212 | - | } | |||
| 213 | - | ||||
| 214 | - | var cb glib.SourceFunc = func(uintptr) bool { | |||
| 215 | - | o.mu.RLock() | |||
| 216 | - | visible := o.visible | |||
| 217 | - | o.mu.RUnlock() | |||
| 218 | - | if visible { | |||
| 219 | - | o.resizeAndCenter(rowCount) | |||
| 220 | - | } | |||
| 221 | - | return false | |||
| 222 | - | } | |||
| 223 | - | glib.IdleAdd(&cb, 0) | |||
| 224 | - | return | |||
| 225 | } | 200 | } | ||
| 226 | 201 | ||||
| 227 | width := int(float64(parentWidth) * omniboxWidthPct) | 202 | width := int(float64(parentWidth) * omniboxWidthPct) | ||
| @@ -493,6 +468,13 @@ func (o *Omnibox) handleKeyPress(keyval uint, state gdk.ModifierType) bool { | |||||
| 493 | o.Hide(o.ctx) | 468 | o.Hide(o.ctx) | ||
| 494 | return true | 469 | return true | ||
| 495 | 470 | ||||
| 471 | + | case uint(gdk.KEY_l), uint(gdk.KEY_L): | |||
| 472 | + | // Ctrl+L toggles omnibox (hides when visible) | |||
| 473 | + | if ctrl { | |||
| 474 | + | o.Hide(o.ctx) | |||
| 475 | + | return true | |||
| 476 | + | } | |||
| 477 | + | ||||
| 496 | case uint(gdk.KEY_Return), uint(gdk.KEY_KP_Enter): | 478 | case uint(gdk.KEY_Return), uint(gdk.KEY_KP_Enter): | ||
| 497 | o.navigateToSelected() | 479 | o.navigateToSelected() | ||
| 498 | return true | 480 | return true | ||
| @@ -532,24 +514,31 @@ func (o *Omnibox) handleKeyPress(keyval uint, state gdk.ModifierType) bool { | |||||
| 532 | } | 514 | } | ||
| 533 | return false // Let entry handle 'y' for typing | 515 | return false // Let entry handle 'y' for typing | ||
| 534 | 516 | ||||
| 517 | + | default: | |||
| 518 | + | return o.handleCtrlNumberShortcut(keyval, ctrl) | |||
| 519 | + | } | |||
| 520 | + | ||||
| 521 | + | return false // Let entry handle the key | |||
| 522 | + | } | |||
| 523 | + | ||||
| 524 | + | // handleCtrlNumberShortcut handles Ctrl+1-9 and Ctrl+0 for quick navigation. | |||
| 525 | + | func (o *Omnibox) handleCtrlNumberShortcut(keyval uint, ctrl bool) bool { | |||
| 526 | + | if !ctrl { | |||
| 527 | + | return false | |||
| 528 | + | } | |||
| 529 | + | ||||
| 530 | + | const tenthItemIndex = 9 | |||
| 531 | + | switch keyval { | |||
| 535 | case uint(gdk.KEY_1), uint(gdk.KEY_2), uint(gdk.KEY_3), uint(gdk.KEY_4), uint(gdk.KEY_5), | 532 | case uint(gdk.KEY_1), uint(gdk.KEY_2), uint(gdk.KEY_3), uint(gdk.KEY_4), uint(gdk.KEY_5), | ||
| 536 | uint(gdk.KEY_6), uint(gdk.KEY_7), uint(gdk.KEY_8), uint(gdk.KEY_9): | 533 | uint(gdk.KEY_6), uint(gdk.KEY_7), uint(gdk.KEY_8), uint(gdk.KEY_9): | ||
| 537 | - | if ctrl { | 534 | + | index := int(keyval - uint(gdk.KEY_1)) |
| 538 | - | const firstShortcutKey = uint(gdk.KEY_1) | 535 | + | o.selectAndNavigate(index) |
| 539 | - | index := int(keyval - firstShortcutKey) // 0-8 | 536 | + | return true |
| 540 | - | o.selectAndNavigate(index) | |||
| 541 | - | return true | |||
| 542 | - | } | |||
| 543 | - | ||||
| 544 | case uint(gdk.KEY_0): | 537 | case uint(gdk.KEY_0): | ||
| 545 | - | if ctrl { | 538 | + | o.selectAndNavigate(tenthItemIndex) |
| 546 | - | const tenthItemIndex = 9 | 539 | + | return true |
| 547 | - | o.selectAndNavigate(tenthItemIndex) | |||
| 548 | - | return true | |||
| 549 | - | } | |||
| 550 | } | 540 | } | ||
| 551 | - | 541 | + | return false | |
| 552 | - | return false // Let entry handle the key | |||
| 553 | } | 542 | } | ||
| 554 | 543 | ||||
| 555 | // onEntryChanged handles text input changes with debouncing. | 544 | // onEntryChanged handles text input changes with debouncing. | ||
| @@ -1245,9 +1234,17 @@ func (o *Omnibox) Show(ctx context.Context, query string) { | |||||
| 1245 | o.scrolledWin.SetMaxContentHeight(0) | 1234 | o.scrolledWin.SetMaxContentHeight(0) | ||
| 1246 | } | 1235 | } | ||
| 1247 | 1236 | ||||
| 1248 | - | // Set width and vertical positioning (20% from top) | 1237 | + | // Get parent dimensions with fallback if not yet allocated |
| 1249 | parentWidth := o.parentOverlay.GetAllocatedWidth() | 1238 | parentWidth := o.parentOverlay.GetAllocatedWidth() | ||
| 1250 | parentHeight := o.parentOverlay.GetAllocatedHeight() | 1239 | parentHeight := o.parentOverlay.GetAllocatedHeight() | ||
| 1240 | + | if parentWidth <= 0 { | |||
| 1241 | + | parentWidth = omniboxMaxWidth | |||
| 1242 | + | } | |||
| 1243 | + | if parentHeight <= 0 { | |||
| 1244 | + | parentHeight = omniboxFallbackHeight | |||
| 1245 | + | } | |||
| 1246 | + | ||||
| 1247 | + | // Set width and vertical positioning (20% from top) | |||
| 1251 | width := int(float64(parentWidth) * omniboxWidthPct) | 1248 | width := int(float64(parentWidth) * omniboxWidthPct) | ||
| 1252 | if width > omniboxMaxWidth { | 1249 | if width > omniboxMaxWidth { | ||
| 1253 | width = omniboxMaxWidth | 1250 | width = omniboxMaxWidth | ||
| @@ -1258,7 +1255,6 @@ func (o *Omnibox) Show(ctx context.Context, query string) { | |||||
| 1258 | o.outerBox.SetMarginTop(marginTop) | 1255 | o.outerBox.SetMarginTop(marginTop) | ||
| 1259 | 1256 | ||||
| 1260 | // Show the omnibox | 1257 | // Show the omnibox | ||
| 1261 | - | o.loggedInvalidOverlayDims = false | |||
| 1262 | o.outerBox.SetVisible(true) | 1258 | o.outerBox.SetVisible(true) | ||
| 1263 | 1259 | ||||
| 1264 | // Trigger initial resize (will be updated when results arrive) | 1260 | // Trigger initial resize (will be updated when results arrive) | ||
M internal/ui/component/workspace_view.go +23 -6
| @@ -188,8 +188,16 @@ func (wv *WorkspaceView) SetActivePaneID(paneID entity.PaneID) error { | |||
| 188 | 188 | func (wv *WorkspaceView) setActivePaneIDInternal(paneID entity.PaneID) error { | |
| 189 | 189 | currentActiveID := wv.getActivePaneIDInternal() | |
| 190 | 190 | ||
| 191 | + | wv.logger.Debug(). | |
| 192 | + | Str("from_pane", string(currentActiveID)). | |
| 193 | + | Str("to_pane", string(paneID)). | |
| 194 | + | Bool("omnibox_visible", wv.omnibox != nil). | |
| 195 | + | Str("omnibox_pane", string(wv.omniboxPaneID)). | |
| 196 | + | Msg("active pane changing") | |
| 197 | + | ||
| 191 | 198 | // Destroy omnibox if active pane is changing | |
| 192 | 199 | if currentActiveID != paneID && wv.omnibox != nil { | |
| 200 | + | wv.logger.Debug().Msg("destroying omnibox due to pane change") | |
| 193 | 201 | wv.hideOmniboxInternal() | |
| 194 | 202 | } | |
| 195 | 203 | // Destroy find bar if active pane is changing | |
| @@ -478,12 +486,6 @@ func (wv *WorkspaceView) ShowOmnibox(ctx context.Context, query string) { | |||
| 478 | 486 | wv.mu.Lock() | |
| 479 | 487 | defer wv.mu.Unlock() | |
| 480 | 488 | ||
| 481 | - | // If omnibox already exists, just show it | |
| 482 | - | if wv.omnibox != nil { | |
| 483 | - | wv.omnibox.Show(ctx, query) | |
| 484 | - | return | |
| 485 | - | } | |
| 486 | - | ||
| 487 | 489 | // Get the active pane view | |
| 488 | 490 | activePaneID := wv.getActivePaneIDInternal() | |
| 489 | 491 | pv := wv.paneViews[activePaneID] | |
| @@ -492,6 +494,21 @@ func (wv *WorkspaceView) ShowOmnibox(ctx context.Context, query string) { | |||
| 492 | 494 | return | |
| 493 | 495 | } | |
| 494 | 496 | ||
| 497 | + | // If omnibox exists but on different pane, destroy and recreate on active pane | |
| 498 | + | if wv.omnibox != nil { | |
| 499 | + | if wv.omniboxPaneID != activePaneID { | |
| 500 | + | wv.logger.Debug(). | |
| 501 | + | Str("old_pane", string(wv.omniboxPaneID)). | |
| 502 | + | Str("new_pane", string(activePaneID)). | |
| 503 | + | Msg("omnibox pane changed, recreating") | |
| 504 | + | wv.hideOmniboxInternal() | |
| 505 | + | } else { | |
| 506 | + | wv.logger.Debug().Str("pane", string(activePaneID)).Msg("showing existing omnibox") | |
| 507 | + | wv.omnibox.Show(ctx, query) | |
| 508 | + | return | |
| 509 | + | } | |
| 510 | + | } | |
| 511 | + | ||
| 495 | 512 | // Create omnibox with pane-specific toast callback | |
| 496 | 513 | cfg := wv.omniboxCfg | |
| 497 | 514 | cfg.OnToast = func(message string) { | |
| @@ -188,8 +188,16 @@ func (wv *WorkspaceView) SetActivePaneID(paneID entity.PaneID) error { | |||||
| 188 | func (wv *WorkspaceView) setActivePaneIDInternal(paneID entity.PaneID) error { | 188 | func (wv *WorkspaceView) setActivePaneIDInternal(paneID entity.PaneID) error { | ||
| 189 | currentActiveID := wv.getActivePaneIDInternal() | 189 | currentActiveID := wv.getActivePaneIDInternal() | ||
| 190 | 190 | ||||
| 191 | + | wv.logger.Debug(). | |||
| 192 | + | Str("from_pane", string(currentActiveID)). | |||
| 193 | + | Str("to_pane", string(paneID)). | |||
| 194 | + | Bool("omnibox_visible", wv.omnibox != nil). | |||
| 195 | + | Str("omnibox_pane", string(wv.omniboxPaneID)). | |||
| 196 | + | Msg("active pane changing") | |||
| 197 | + | ||||
| 191 | // Destroy omnibox if active pane is changing | 198 | // Destroy omnibox if active pane is changing | ||
| 192 | if currentActiveID != paneID && wv.omnibox != nil { | 199 | if currentActiveID != paneID && wv.omnibox != nil { | ||
| 200 | + | wv.logger.Debug().Msg("destroying omnibox due to pane change") | |||
| 193 | wv.hideOmniboxInternal() | 201 | wv.hideOmniboxInternal() | ||
| 194 | } | 202 | } | ||
| 195 | // Destroy find bar if active pane is changing | 203 | // Destroy find bar if active pane is changing | ||
| @@ -478,12 +486,6 @@ func (wv *WorkspaceView) ShowOmnibox(ctx context.Context, query string) { | |||||
| 478 | wv.mu.Lock() | 486 | wv.mu.Lock() | ||
| 479 | defer wv.mu.Unlock() | 487 | defer wv.mu.Unlock() | ||
| 480 | 488 | ||||
| 481 | - | // If omnibox already exists, just show it | |||
| 482 | - | if wv.omnibox != nil { | |||
| 483 | - | wv.omnibox.Show(ctx, query) | |||
| 484 | - | return | |||
| 485 | - | } | |||
| 486 | - | ||||
| 487 | // Get the active pane view | 489 | // Get the active pane view | ||
| 488 | activePaneID := wv.getActivePaneIDInternal() | 490 | activePaneID := wv.getActivePaneIDInternal() | ||
| 489 | pv := wv.paneViews[activePaneID] | 491 | pv := wv.paneViews[activePaneID] | ||
| @@ -492,6 +494,21 @@ func (wv *WorkspaceView) ShowOmnibox(ctx context.Context, query string) { | |||||
| 492 | return | 494 | return | ||
| 493 | } | 495 | } | ||
| 494 | 496 | ||||
| 497 | + | // If omnibox exists but on different pane, destroy and recreate on active pane | |||
| 498 | + | if wv.omnibox != nil { | |||
| 499 | + | if wv.omniboxPaneID != activePaneID { | |||
| 500 | + | wv.logger.Debug(). | |||
| 501 | + | Str("old_pane", string(wv.omniboxPaneID)). | |||
| 502 | + | Str("new_pane", string(activePaneID)). | |||
| 503 | + | Msg("omnibox pane changed, recreating") | |||
| 504 | + | wv.hideOmniboxInternal() | |||
| 505 | + | } else { | |||
| 506 | + | wv.logger.Debug().Str("pane", string(activePaneID)).Msg("showing existing omnibox") | |||
| 507 | + | wv.omnibox.Show(ctx, query) | |||
| 508 | + | return | |||
| 509 | + | } | |||
| 510 | + | } | |||
| 511 | + | ||||
| 495 | // Create omnibox with pane-specific toast callback | 512 | // Create omnibox with pane-specific toast callback | ||
| 496 | cfg := wv.omniboxCfg | 513 | cfg := wv.omniboxCfg | ||
| 497 | cfg.OnToast = func(message string) { | 514 | cfg.OnToast = func(message string) { | ||