fix(shell): coordinate startup cover and tab stills

This commit is contained in:
CRTkafa 2026-09-30 12:25:46 +07:00
parent 5c93b78704
commit 526a4a656d
13 changed files with 442 additions and 52 deletions

View file

@ -52,6 +52,8 @@ pub struct AppConfig {
pub min_window_size: (f64, f64),
/// The window's color before the shell paints (no white flash).
pub background: (u8, u8, u8),
/// Wait for the shell's `shell_ready` command before showing the first window.
pub defer_window_show: bool,
/// The shell draws the titlebar (Windows/Linux: frameless; macOS: an overlay titlebar with the
/// traffic lights over the shell). Off: the OS frame, for a shell without one.
pub custom_titlebar: bool,
@ -74,6 +76,7 @@ impl AppConfig {
window_size: (1360.0, 900.0),
min_window_size: (900.0, 560.0),
background: (7, 8, 12),
defer_window_show: false,
custom_titlebar: true,
deep_link: false,
updater: false,
@ -319,8 +322,8 @@ async fn tabs_sync(webview: Webview, app: AppHandle, items: Vec<TabLayout>, view
// and the app's per-tab state goes with the old one.
let existing = match existing {
Some(tab) if current_profile.as_deref() != Some(item.profile.as_str()) => {
host::forget_tab(&app, &label);
let _ = tab.close();
host::forget_tab(&label);
profiles(&app)?.tab_profiles.remove(&label);
hooks.tab_closed(&app, &label, TabClosed::Replaced);
None
@ -383,8 +386,8 @@ async fn tabs_sync(webview: Webview, app: AppHandle, items: Vec<TabLayout>, view
if let Some(id) = policy::tab_id(existing.label()) {
if !keep.contains(id) {
let label = existing.label().to_owned();
host::forget_tab(&app, &label);
let _ = existing.close();
host::forget_tab(&label);
if let Ok(mut data) = profiles(&app) {
data.tab_profiles.remove(&label);
}
@ -423,6 +426,25 @@ async fn shell_focus(webview: Webview) -> Result<(), String> {
webview.set_focus().map_err(|error| format!("FOCUS: {error}"))
}
/// The shell calls this after its first DOM commit, so startup never exposes an empty webview.
#[tauri::command]
async fn shell_ready(webview: Webview, app: AppHandle) -> Result<(), String> {
shell_only(&webview)?;
app.get_window(policy::MAIN_LABEL)
.ok_or("WINDOW: main window missing")?
.show()
.map_err(|error| format!("WINDOW: {error}"))
}
/// The shell has committed a captured tab frame; its native webview can now be hidden.
#[tauri::command]
async fn tab_still_ready(webview: Webview, app: AppHandle, id: String, token: u64) -> Result<(), String> {
shell_only(&webview)?;
if !policy::valid_tab_id(&id) { return Err("VALIDATION: invalid tab id".into()); }
host::acknowledge_still(&app, &id, token);
Ok(())
}
/// Raises the shell above the tab webviews while one of its overlays (menu, dialog, palette,
/// drag) is open, so the panes stay on screen under it, or lowers it again. Returns whether this
/// platform can; when not, the shell hides the tabs for the overlay instead.
@ -701,6 +723,8 @@ fn host_handler() -> impl Fn(tauri::ipc::Invoke<Wry>) -> bool + Send + Sync + 's
tab_action,
open_external_url,
shell_focus,
shell_ready,
tab_still_ready,
shell_raise,
window_system_menu,
profiles_get,
@ -864,6 +888,18 @@ fn open_window(app: &mut tauri::App<Wry>, config: &AppConfig) -> Result<(), Box<
let shell = shell.transparent(true);
let shell = window.add_child(shell, LogicalPosition::new(0.0, 0.0), LogicalSize::new(size.width, size.height))?;
host::attach_shell(&shell);
window.show()?;
if config.defer_window_show {
// A shell that opts in shows the window after initial layout and sync. If JS fails to
// start, make the failure visible rather than leaving a hidden process.
let fallback = window.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(3));
if fallback.is_visible().is_ok_and(|visible| !visible) {
let _ = fallback.show();
}
});
} else {
window.show()?;
}
Ok(())
}