Summary
Unify the search surface in a single main WebView by moving the model dropdown and session history out of standalone popup WebviewWindows and into a shared SearchOverlayHost alongside the existing SearchPanel.
On Windows, main becomes a transparent monitor-sized SearchWindow. The native layer applies a unioned window region for the visible SearchPanel and active search overlay, so unused transparent areas remain absent from native rendering and hit testing. An optional fully click-through SearchBackgroundWindow may be used only for monitor-wide visual effects.
This supersedes the earlier native-rendered standalone-popup proposal in this RFC.
Motivation
The current popup system creates separate transparent WebviewWindows for the model dropdown and session history. This requires separate renderer startup, popup preload and readiness events, DTO replication, screen-coordinate translation, cross-window keyboard forwarding, native focus-family bookkeeping, and extra show/hide synchronization.
It also prevents transitions that move naturally between the search panel and its overlays because the source and destination are rendered by different WebViews and HWNDs.
The intended outcome is:
- one Vue rendering tree for the complete search surface;
- direct shared state instead of cross-window popup transport;
- coordinated panel and overlay animation;
- no standalone search popup WebViews;
- native click-through outside visible TouchAI UI;
- preserved keyboard, focus, outside-click, prefetch, pinning, IME, and blur-to-hide behavior;
- no global input hook or
WM_NCHITTEST-based cross-process pass-through.
Affected boundaries
The change affects the desktop frontend, Tauri window runtime, Windows native window regions, search-surface focus behavior, popup rendering, and desktop E2E coverage. It does not change agent, session, MCP, tool, or database semantics.
Proposed design
Search surface structure
Use TouchAI-specific terminology and a direct architecture:
SearchSurface
├── SearchPanel
│ ├── ConversationPanel
│ ├── AskUserPanel
│ ├── SearchBar
│ └── QuickSearchPanel
└── SearchOverlayHost
├── ModelDropdown
└── SessionHistory
SearchPanel is the existing primary search/conversation content. The design does not use the reference project's "island" terminology.
Frontend ownership
Render both search overlays inside main, outside the clipped SearchPanel container and within a shared top-level stacking context.
Use a small explicit active-overlay state for the two current overlay types. Do not introduce a generic plugin registry, native/in-main adapter layer, or long-lived compatibility abstraction.
Preserve existing behavior:
- model and history prefetch;
- filter focus on open;
- Arrow/Enter navigation;
- Escape, Ctrl+M, and Ctrl+H behavior;
- mutual exclusion with Quick Search and other overlays;
- click inside versus outside semantics;
- stale asynchronous history-search protection;
- intentional focus restoration after selection or dismissal;
- existing pin, drag, maximize/expanded, IME, and hide-on-blur behavior.
Position overlays in the shared WebView coordinate system using direct anchor, flip, align, and clamp rules. Remove native screen-coordinate popup placement after the standalone popup path is deleted.
Native window ownership
On Windows, make main a transparent SearchWindow sized to the active monitor. The frontend reports the stable logical rectangles of the visible SearchPanel and active SearchOverlay.
A dedicated native region module:
- converts logical rectangles to physical pixels using the active monitor scale factor;
- creates rounded Win32 regions;
- combines them with
CombineRgn(..., RGN_OR);
- applies the result with
SetWindowRgn;
- recomputes after layout, DPI, monitor, or visibility changes.
The native region is updated at stable transition boundaries, not once per animation frame. The animation-start region covers the required panel/overlay animation envelope; the final region is tightened after the transition.
The existing generic rounded-corner fallback must no longer clear or replace the deliberate main search region. Settings keeps its existing DWM/rounded-region behavior.
Optional full-monitor visuals
If monitor-wide visuals are needed, render them in a separate SearchBackgroundWindow that is non-focusable and uses Tauri's whole-window set_ignore_cursor_events(true). It is not part of the interaction or focus model.
Do not rely on CSS transparency, pointer-events: none, WS_EX_TRANSPARENT, layered alpha, or HTTRANSPARENT for selective pass-through to arbitrary external applications.
Animation
Use CSS transform, opacity, and selective clipping for panel/overlay transitions. Do not use CSS height as the primary surface animation, per-frame native window resizing, per-frame region IPC, or a custom WndProc hook.
Removed architecture
Delete the standalone search popup implementation after the unified host is in place:
- popup WebviewWindow creation and preload;
PopupView route and bootstrap;
- popup-ready/data/keydown event transport;
- popup DTO replay and window-label identity;
- Rust popup registry and popup surface sessions;
- native popup focus-family and close/refocus plumbing.
Settings, tray UI, system dialogs, and other genuinely separate windows remain separate.
Alternatives and trade-offs
Keep standalone WebView popups
Lowest implementation cost, but retains duplicate renderer state, positioning/focus IPC, and prevents seamless same-tree animations.
Rewrite standalone popups with a native renderer
Removes popup WebViews, but duplicates UI logic outside Vue, makes exact visual parity harder, and still keeps cross-window focus, geometry, and animation boundaries. This was the previous direction of this RFC and is superseded.
Use one bounded main window and resize it around each overlay
Avoids a monitor-sized WebView but couples transient overlays to native resize/reposition, reintroduces WebView2 redraw and coordinate-origin changes, and complicates existing content-driven search sizing.
Use a full-screen transparent WebView without a native region
Rejected because transparent HWND pixels still intercept native pointer input. CSS pointer-events cannot route input to another process.
Use WM_NCHITTEST and return HTTRANSPARENT
Rejected as the primary design. Documented HTTRANSPARENT continuation is limited to underlying windows in the same thread and is not a reliable arbitrary cross-process desktop pass-through contract.
Use SetWindowRgn
Selected for the Windows interaction boundary because it removes both rendering and native hit testing outside the unioned UI region without a WndProc hook. The trade-off is Windows-specific region maintenance across DPI, display, layout, and animation-boundary changes.
Upstream references
Testing and rollout
This RFC defines the target architecture directly. The implementation should remain focused and avoid a permanent compatibility path or dual popup architecture.
Verification must cover:
- model dropdown and session history opening, filtering, selection, and dismissal;
- Arrow/Enter, Escape, Ctrl+M, and Ctrl+H behavior;
- focus placement and restoration;
- trigger, overlay-internal, panel-external, and application-external pointer behavior;
- Quick Search and overlay mutual exclusion;
- stale asynchronous result rejection;
- pinning, drag, expanded state, hide-on-blur, and rapid global-shortcut toggling;
- Chinese IME and WebView2 focus recovery after hide/show;
- monitor work areas, negative virtual-screen coordinates, mixed DPI, display changes, and reconnects;
- native click and wheel pass-through outside the unioned search region;
- no taskbar or Alt+Tab regression;
- no per-frame native resize or region IPC;
- warm shortcut-to-focused-input and overlay animation smoothness.
Required validation includes pnpm test:pr, desktop E2E coverage, relevant Rust tests, and direct Windows interaction verification for cross-application click-through.
Summary
Unify the search surface in a single
mainWebView by moving the model dropdown and session history out of standalone popup WebviewWindows and into a sharedSearchOverlayHostalongside the existingSearchPanel.On Windows,
mainbecomes a transparent monitor-sizedSearchWindow. The native layer applies a unioned window region for the visibleSearchPaneland active search overlay, so unused transparent areas remain absent from native rendering and hit testing. An optional fully click-throughSearchBackgroundWindowmay be used only for monitor-wide visual effects.This supersedes the earlier native-rendered standalone-popup proposal in this RFC.
Motivation
The current popup system creates separate transparent WebviewWindows for the model dropdown and session history. This requires separate renderer startup, popup preload and readiness events, DTO replication, screen-coordinate translation, cross-window keyboard forwarding, native focus-family bookkeeping, and extra show/hide synchronization.
It also prevents transitions that move naturally between the search panel and its overlays because the source and destination are rendered by different WebViews and HWNDs.
The intended outcome is:
WM_NCHITTEST-based cross-process pass-through.Affected boundaries
The change affects the desktop frontend, Tauri window runtime, Windows native window regions, search-surface focus behavior, popup rendering, and desktop E2E coverage. It does not change agent, session, MCP, tool, or database semantics.
Proposed design
Search surface structure
Use TouchAI-specific terminology and a direct architecture:
SearchPanelis the existing primary search/conversation content. The design does not use the reference project's "island" terminology.Frontend ownership
Render both search overlays inside
main, outside the clippedSearchPanelcontainer and within a shared top-level stacking context.Use a small explicit active-overlay state for the two current overlay types. Do not introduce a generic plugin registry, native/in-main adapter layer, or long-lived compatibility abstraction.
Preserve existing behavior:
Position overlays in the shared WebView coordinate system using direct anchor, flip, align, and clamp rules. Remove native screen-coordinate popup placement after the standalone popup path is deleted.
Native window ownership
On Windows, make
maina transparentSearchWindowsized to the active monitor. The frontend reports the stable logical rectangles of the visibleSearchPaneland activeSearchOverlay.A dedicated native region module:
CombineRgn(..., RGN_OR);SetWindowRgn;The native region is updated at stable transition boundaries, not once per animation frame. The animation-start region covers the required panel/overlay animation envelope; the final region is tightened after the transition.
The existing generic rounded-corner fallback must no longer clear or replace the deliberate
mainsearch region. Settings keeps its existing DWM/rounded-region behavior.Optional full-monitor visuals
If monitor-wide visuals are needed, render them in a separate
SearchBackgroundWindowthat is non-focusable and uses Tauri's whole-windowset_ignore_cursor_events(true). It is not part of the interaction or focus model.Do not rely on CSS transparency,
pointer-events: none,WS_EX_TRANSPARENT, layered alpha, orHTTRANSPARENTfor selective pass-through to arbitrary external applications.Animation
Use CSS
transform,opacity, and selective clipping for panel/overlay transitions. Do not use CSSheightas the primary surface animation, per-frame native window resizing, per-frame region IPC, or a custom WndProc hook.Removed architecture
Delete the standalone search popup implementation after the unified host is in place:
PopupViewroute and bootstrap;Settings, tray UI, system dialogs, and other genuinely separate windows remain separate.
Alternatives and trade-offs
Keep standalone WebView popups
Lowest implementation cost, but retains duplicate renderer state, positioning/focus IPC, and prevents seamless same-tree animations.
Rewrite standalone popups with a native renderer
Removes popup WebViews, but duplicates UI logic outside Vue, makes exact visual parity harder, and still keeps cross-window focus, geometry, and animation boundaries. This was the previous direction of this RFC and is superseded.
Use one bounded main window and resize it around each overlay
Avoids a monitor-sized WebView but couples transient overlays to native resize/reposition, reintroduces WebView2 redraw and coordinate-origin changes, and complicates existing content-driven search sizing.
Use a full-screen transparent WebView without a native region
Rejected because transparent HWND pixels still intercept native pointer input. CSS
pointer-eventscannot route input to another process.Use
WM_NCHITTESTand returnHTTRANSPARENTRejected as the primary design. Documented
HTTRANSPARENTcontinuation is limited to underlying windows in the same thread and is not a reliable arbitrary cross-process desktop pass-through contract.Use
SetWindowRgnSelected for the Windows interaction boundary because it removes both rendering and native hit testing outside the unioned UI region without a WndProc hook. The trade-off is Windows-specific region maintenance across DPI, display, layout, and animation-boundary changes.
Upstream references
WebviewWindow::set_ignore_cursor_events: https://docs.rs/tauri/2.11.1/tauri/webview/struct.WebviewWindow.html#method.set_ignore_cursor_eventsSetWindowRgn: https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowrgnCombineRgn: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-combinergnWM_NCHITTEST: https://learn.microsoft.com/en-us/windows/win32/inputdev/wm-nchittestTesting and rollout
This RFC defines the target architecture directly. The implementation should remain focused and avoid a permanent compatibility path or dual popup architecture.
Verification must cover:
Required validation includes
pnpm test:pr, desktop E2E coverage, relevant Rust tests, and direct Windows interaction verification for cross-application click-through.