Merge remote-tracking branch 'origin/main' into fix/cli-launcher-all-platforms

# Conflicts:
#	src/platform/locales/text/en.json
#	src/platform/locales/text/zh.json
Цей коміт міститься в:
l0ng-ai committed 2026-11-07 22:24:10 +08:00
коміт 5890e8abc5
51 files changed
+516 -33

No files matched your search

+1
Переглянути файл
@@ -87,6 +87,7 @@ pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
window_group::window_group_close,
window_group::window_group_close_tabs,
window_group::window_group_cancel_close,
window_group::window_group_before_close,
window_group::window_group_reorder,
window_group::window_group_restore,
app::light_edit_open,
+14
Переглянути файл
@@ -55,6 +55,20 @@ pub fn window_group_cancel_close(window: WebviewWindow) {
groups::cancel_close(window.app_handle(), window.label());
}
/// This window closes next (its prompts passed): a fullscreen group's host first hands its Space
/// to the tab that takes over.
#[tauri::command]
#[specta::specta]
pub async fn window_group_before_close(window: WebviewWindow) -> IdeResult<()> {
#[cfg(target_os = "macos")]
return groups::before_close(window.app_handle(), window.label()).await;
#[cfg(not(target_os = "macos"))]
{
let _ = window;
Ok(())
}
}
#[tauri::command]
#[specta::specta]
pub async fn window_group_reorder(window: WebviewWindow, labels: Vec<String>) -> IdeResult<()> {
+38
Переглянути файл
@@ -173,6 +173,44 @@ pub fn watch_fullscreen_host(host: &WebviewWindow, left: impl Fn() + Send + 'sta
})
}
thread_local! {
/// Windows being waited on to finish entering fullscreen.
static ENTERING_FULLSCREEN: std::cell::RefCell<std::collections::HashMap<String, FullscreenObserver>> = std::cell::RefCell::default();
}
/// Toggles `window` into native fullscreen and resolves once it is there, or after `timeout`.
pub async fn enter_fullscreen_and_wait(window: &WebviewWindow, timeout: std::time::Duration) -> tauri::Result<()> {
let (tx, rx) = tokio::sync::oneshot::channel();
let label = window.label().to_owned();
let target = window.clone();
on_main(window, move || unsafe {
let Ok(pointer) = target.ns_window() else { return };
let native = &*pointer.cast::<NSWindow>();
let tx = std::cell::Cell::new(Some(tx));
let block = block2::RcBlock::new(move |_| {
if let Some(tx) = tx.take() {
let _ = tx.send(());
}
});
let observer = NSNotificationCenter::defaultCenter().addObserverForName_object_queue_usingBlock(
Some(objc2_app_kit::NSWindowDidEnterFullScreenNotification),
Some(native),
None,
&block,
);
ENTERING_FULLSCREEN.with_borrow_mut(|waits| waits.insert(label, FullscreenObserver(observer)));
})?;
let toggled = native_toggle_fullscreen(window);
if toggled.is_ok() {
let _ = tokio::time::timeout(timeout, rx).await;
}
let label = window.label().to_owned();
on_main(window, move || {
ENTERING_FULLSCREEN.with_borrow_mut(|waits| waits.remove(&label));
})?;
toggled
}
/// An observer on the workspace's notification center, removed when dropped.
struct WorkspaceObserver(Retained<NSNotificationCenter>, Retained<ProtocolObject<dyn NSObjectProtocol>>);
+33 -2
Переглянути файл
@@ -19,6 +19,9 @@ const FRAME_SETTLE_AFTER_MOVE: Duration = Duration::from_millis(100);
/// Longest wait for a closed host's fullscreen Space to go before the next tab takes the screen.
#[cfg(target_os = "macos")]
const SPACE_TEARDOWN: Duration = Duration::from_millis(1500);
/// Longest wait for the tab taking over from a closing host to enter its own fullscreen Space.
#[cfg(target_os = "macos")]
const SPACE_HANDOVER: Duration = Duration::from_millis(2000);
/// Coalesces the move/resize events a drag emits.
static FRAME_SYNC_GENERATION: AtomicU64 = AtomicU64::new(0);
@@ -327,6 +330,13 @@ pub fn toggle_fullscreen(window: &WebviewWindow) -> IdeResult<bool> {
/// Makes `host` the fullscreen window of its group, or of its own Space when ungrouped.
#[cfg(target_os = "macos")]
fn host_fullscreen(app: &AppHandle, host: &WebviewWindow) -> IdeResult<()> {
prepare_host(app, host)?;
crate::macos::native_toggle_fullscreen(host).map_err(|e| IdeError::internal(e.to_string()))
}
/// `host` and the other members of its group, ready for `host` to enter fullscreen.
#[cfg(target_os = "macos")]
fn prepare_host(app: &AppHandle, host: &WebviewWindow) -> IdeResult<()> {
let err = |e: tauri::Error| IdeError::internal(e.to_string());
let members = app.state::<WindowsState>().groups.lock().group(host.label()).map(|g| g.members.clone());
crate::macos::set_fullscreen_auxiliary(host, None).map_err(err)?;
@@ -338,8 +348,29 @@ fn host_fullscreen(app: &AppHandle, host: &WebviewWindow) -> IdeResult<()> {
if members.is_some() {
watch_host(app, host)?;
}
host.show().map_err(err)?;
crate::macos::native_toggle_fullscreen(host).map_err(err)
host.show().map_err(err)
}
/// A fullscreen group's host is about to close, past its prompts. The tab that takes over leaves
/// for a Space of its own first, so the screen moves straight to it; were the host closed first,
/// its Space would end and show the desktop until the next one opened.
#[cfg(target_os = "macos")]
pub async fn before_close(app: &AppHandle, caller: &str) -> IdeResult<()> {
let state = app.state::<WindowsState>();
let Some(members) = state.groups.lock().group(caller).map(|g| g.members.clone()) else { return Ok(()) };
if state.quitting() || fullscreen_host(app, &members).is_none_or(|host| host.label() != caller) {
return Ok(());
}
// Detached now, the closing host leaves nothing for `destroyed` to hand over.
let next = state.groups.lock().detach(caller);
changed(app);
let Some(next) = next.and_then(|label| app.get_webview_window(&label)) else { return Ok(()) };
prepare_host(app, &next)?;
crate::macos::enter_fullscreen_and_wait(&next, SPACE_HANDOVER)
.await
.map_err(|e| IdeError::internal(e.to_string()))?;
windows::focus(&next);
Ok(())
}
#[cfg(target_os = "macos")]