Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
@@ -0,0 +1,53 @@
use crate::notification::NotificationSendError;
use crate::windowing::winit::app::CustomEvent;
use crate::windowing::winit::notifications::NotificationInfo;
use crate::WindowId;
use futures::FutureExt;
use winit::event_loop::EventLoopProxy;
pub(super) async fn send_notification(
notification_info: NotificationInfo,
_window_id: WindowId,
proxy: EventLoopProxy<CustomEvent>,
) {
let NotificationInfo {
notification_content,
on_error,
} = notification_info;
let mut notification = notify_rust::Notification::new();
notification
.summary(notification_content.title())
.body(notification_content.body());
notification
.show_async()
.then(|handle| async move {
match handle {
Ok(handle) => {
// The call to on_close blocks until the notification is closed, so make the blocking
// call on its own thread in the `blocking` crate threadpool to avoid starving the shared
// background executor.
blocking::unblock(move || {
// Without the on_close handler, the notification will fail to appear.
handle.on_close(|reason| log::info!("Notification closed via {reason:?}"))
})
.await;
}
Err(err) => {
// Always consider the error to be a `NotificationSendError::Other`.
// Dbus does not report if a notification couldn't be shown because
// the application didn't have permissions, so we can never return a
// `NotificationSendError::PermissionDenied` error.
let error = NotificationSendError::Other {
error_message: err.to_string(),
};
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
on_error(error, ctx);
})));
}
}
})
.await
}
@@ -0,0 +1,49 @@
//! Module to display system desktop notifications through the winit windowing backend.
use crate::platform::NotificationInfo;
use crate::platform::{RequestNotificationPermissionsCallback, SendNotificationErrorCallback};
use crate::windowing::winit::app::CustomEvent;
use crate::{notification, WindowId};
use winit::event_loop::EventLoopProxy;
#[cfg_attr(target_os = "linux", path = "linux.rs")]
#[cfg_attr(target_os = "windows", path = "windows.rs")]
#[cfg_attr(target_family = "wasm", path = "wasm.rs")]
mod imp;
#[cfg(target_family = "wasm")]
pub(super) use imp::request_notification_permissions;
pub async fn send_notification(
notification_info: NotificationInfo,
window_id: WindowId,
event_loop_proxy: EventLoopProxy<CustomEvent>,
) {
imp::send_notification(notification_info, window_id, event_loop_proxy).await
}
pub(super) fn request_desktop_notification_permissions(
on_completion: RequestNotificationPermissionsCallback,
event_loop_proxy: &EventLoopProxy<CustomEvent>,
) {
let _ = event_loop_proxy.send_event(CustomEvent::RequestNotificationPermissions(Box::new(
|outcome, ctx| on_completion(outcome, ctx),
)));
}
pub(super) fn send_desktop_notification(
notification_content: notification::UserNotification,
window_id: WindowId,
on_error: SendNotificationErrorCallback,
event_loop_proxy: &EventLoopProxy<CustomEvent>,
) {
use crate::platform::NotificationInfo;
let _ = event_loop_proxy.send_event(CustomEvent::SendNotification {
window_id,
notification_info: NotificationInfo {
notification_content,
on_error,
},
});
}
@@ -0,0 +1,90 @@
use crate::notification::NotificationSendError;
use crate::notification::RequestPermissionsOutcome;
use crate::platform::NotificationInfo;
use crate::windowing::winit::app::RequestPermissionsCallback;
use crate::windowing::winit::CustomEvent;
use crate::WindowId;
use wasm_bindgen_futures::JsFuture;
use winit::event_loop::EventLoopProxy;
pub async fn send_notification(
notification_info: NotificationInfo,
_window_id: WindowId,
proxy: EventLoopProxy<CustomEvent>,
) {
let NotificationInfo {
notification_content,
on_error,
} = notification_info;
// First, we check to see if the page has permissions to send notifications. If not, we should prematurely
// execute the on_error callback. We can't rely on the result of the web_sys::Notification constructor to
// know whether the notification has the right permissions to actually send.
// https://developer.mozilla.org/en-US/docs/Web/API/Notification/Notification#return_value.
match web_sys::Notification::permission() {
web_sys::NotificationPermission::Granted => {
// If permissions are granted, send it! Constructing the Notification object is enough to launch it.
let _ = web_sys::Notification::new(notification_content.title());
}
web_sys::NotificationPermission::Default => {
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
on_error(NotificationSendError::PermissionsNotYetGranted, ctx)
})));
}
web_sys::NotificationPermission::Denied => {
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
on_error(NotificationSendError::PermissionsDenied, ctx)
})));
}
_ => {
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
on_error(
NotificationSendError::Other {
error_message: "unknown notifications permissions".to_string(),
},
ctx,
)
})));
}
}
}
pub async fn request_notification_permissions(
callback: RequestPermissionsCallback,
proxy: EventLoopProxy<CustomEvent>,
) {
// The web_sys request_permission method returns a Promise that resolves to a string indicating
// whether the permissions request was granted, denied, or default.
// See https://developer.mozilla.org/en-US/docs/Web/API/Notification/requestPermission_static.
let Ok(permissions_request_promise) = web_sys::Notification::request_permission() else {
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
callback(
RequestPermissionsOutcome::OtherError {
error_message: "Error sending notification permissions request".to_string(),
},
ctx,
);
})));
return;
};
let request_outcome = match JsFuture::from(permissions_request_promise)
.await
.map(|r| r.as_string())
{
Ok(Some(user_response)) if user_response == "granted" => {
RequestPermissionsOutcome::Accepted
}
// Any response besides "granted" is considered a permissions denied.
Ok(Some(_)) => RequestPermissionsOutcome::PermissionsDenied,
_ => RequestPermissionsOutcome::OtherError {
error_message: "Error receiving response from notification permissions request"
.to_string(),
},
};
// When the request has completed, we execute the callback with the outcome.
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
callback(request_outcome, ctx);
})));
}
@@ -0,0 +1,52 @@
use crate::notification::NotificationSendError;
use crate::windowing::winit::{app::CustomEvent, notifications::NotificationInfo};
use crate::WindowId;
use tauri_winrt_notification::Toast;
use winit::event_loop::EventLoopProxy;
pub(super) async fn send_notification(
notification_info: NotificationInfo,
window_id: WindowId,
proxy: EventLoopProxy<CustomEvent>,
) {
let NotificationInfo {
notification_content,
on_error,
} = notification_info;
let powershell_app_id = Toast::POWERSHELL_APP_ID.to_string();
let app_id = unsafe { fetch_windows_app_id() }
.ok()
.unwrap_or(powershell_app_id);
let proxy_clone = proxy.clone();
let toast = Toast::new(&app_id)
.title(notification_content.title())
.text1(notification_content.body())
.on_activated(move |_activated_arguments| {
let _ = proxy_clone
.send_event(CustomEvent::FocusWindow { window_id })
.map_err(|err| {
log::warn!("Unable to focus window after event loop closed: {err:?}");
});
Ok(())
});
if let Err(err) = toast.show() {
let error = NotificationSendError::Other {
error_message: err.to_string(),
};
let _ = proxy.send_event(CustomEvent::UpdateUIApp(Box::new(|ctx| {
on_error(error, ctx);
})));
}
}
unsafe fn fetch_windows_app_id() -> Result<String, anyhow::Error> {
let app_id_pwstr = windows::Win32::UI::Shell::GetCurrentProcessExplicitAppUserModelID()
.map_err(|win_err| {
log::warn!("error retrieving Win32 AppUserModel ID: {win_err:?}");
anyhow::anyhow!(win_err)
})?;
Ok(app_id_pwstr.to_string()?)
}