first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+1 -1
View File
@@ -86,5 +86,5 @@ impl std::fmt::Display for AppId {
}
#[cfg(test)]
#[path = "app_id_test.rs"]
#[path = "app_id_tests.rs"]
mod tests;
+23
View File
@@ -0,0 +1,23 @@
use super::*;
#[test]
fn test_parse_valid_app_id() {
let app_id_string = "com.example.App";
let app_id = AppId::parse(app_id_string).expect("should not fail to parse");
assert_eq!(app_id.qualifier(), "com");
assert_eq!(app_id.organization(), "example");
assert_eq!(app_id.application_name(), "App");
assert_eq!(app_id_string, &app_id.to_string());
}
#[test]
fn test_parse_invalid_app_id() {
assert!(
AppId::parse("com.example").is_err(),
"should fail to parse two-part app ID string"
);
assert!(
AppId::parse("com.example.App.Blah").is_err(),
"should fail to parse four-part app ID string"
);
}
+129
View File
@@ -0,0 +1,129 @@
use std::time::Duration;
use std::{pin, task};
use futures_lite::{ready, Stream};
use galaxyui_core::r#async::Timer;
use pin::Pin;
use pin_project::pin_project;
use task::{Context, Poll};
/// Debounce takes in a stream and limits the rate of firing events from the stream
/// by bundling all events occurred within the set interval into one.
///
/// For example, if the interval is set to 50 and the following events were fired:
/// E1 fired at T_0
/// E2 fired at T_30
/// E3 fired at T_60
/// E4 fired at T_200
///
/// Debounce will first receive E1 at T_0 and set the timer to expire at T_50. At T_30,
/// E2 came in and the previous timer has not expired, debounce will change the last
/// event to E2 and set timer at T_80 (T_30 + 50). At T_60, E3 came in and similarly
/// the previous timer has not expired, debounce will change the last
/// event to E3 and set timer at T_110 (T_60 + 50). At T_110, the timer expired and emits
/// E3. At T_200, E4 came in and debounce set the timer at T_250. At T_250, E4 was emitted.
///
/// +---------+ +-----------+ +-----------+
/// | stream | | debounce | | executor |
/// +---------+ +-----------+ +-----------+
/// | | |
/// | T_0 E1 | |
/// |-------------------------------->| |
/// |-------------------------------\ | |
/// || record event and start timer |-| |
/// ||------------------------------| | |
/// | | |
/// | T_30 E2 | |
/// |-------------------------------->| |
/// | -----------------------------\ | |
/// | | timer not expired yet: |-| |
/// | | reset timer and last event | | |
/// | |----------------------------| | |
/// | T_60 E3 | |
/// |-------------------------------->| |
/// | -----------------------------\ | |
/// | | timer not expired yet: |-| |
/// | | reset timer and last event | | |
/// | |----------------------------| | |
/// | | T_110 E3 |
/// | |--------------->|
/// | ---------------------------\ | |
/// | | time expired: emit event |-| |
/// | |--------------------------| | |
/// | | |
/// | T_200 E4 | |
/// |-------------------------------->| |
/// | ---------------------------\ | |
/// | | time expired: emit event |-| |
/// | |--------------------------| | |
/// | | |
/// | | T_250 E4 |
/// | |--------------->|
/// | | |
#[pin_project]
pub struct Debounce<S: Stream> {
period: Duration,
#[pin]
stream: S,
#[pin]
timer: Option<Timer>,
last_item: Option<S::Item>,
has_expired: bool,
}
pub fn debounce<S>(period: Duration, stream: S) -> impl Stream<Item = S::Item>
where
S: Stream,
{
Debounce {
period,
stream,
timer: None,
last_item: None,
has_expired: false,
}
}
impl<S> Stream for Debounce<S>
where
S: Stream,
{
type Item = S::Item;
fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut this = self.as_mut().project();
// Stream has expired already--return Poll::Ready(None).
if *this.has_expired {
return Poll::Ready(None);
}
let mut stream = this.stream;
// Read out everything from the stream until the stream is exhausted.
while let Poll::Ready(item) = stream.as_mut().poll_next(ctx) {
match item {
Some(item) => {
*this.last_item = Some(item);
*this.timer = Some(Timer::after(*this.period));
}
None => {
*this.timer = None;
*this.has_expired = true;
return Poll::Ready(this.last_item.take());
}
}
}
if let Some(timer) = this.timer.as_pin_mut() {
ready!(timer.poll_next(ctx));
// The timer is done--return the last item.
let mut this = self.project();
*this.timer = None;
return Poll::Ready(this.last_item.take());
}
Poll::Pending
}
}
+3
View File
@@ -0,0 +1,3 @@
mod debounce;
pub use debounce::debounce;
+18 -4
View File
@@ -27,6 +27,15 @@ pub struct ChannelConfig {
pub mcp_static_config: Option<McpStaticConfig>,
}
/// Configuration for GCP Identity-Aware Proxy authentication, present only on staging builds.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct IapConfig {
/// The IAP OAuth2 client ID used as the audience for identity tokens.
pub audiences: Cow<'static, str>,
/// The service account email to impersonate when acquiring IAP credentials.
pub service_account_email: Cow<'static, str>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct WarpServerConfig {
/// The root URL for the standard server pool.
@@ -38,15 +47,20 @@ pub struct WarpServerConfig {
pub session_sharing_server_url: Option<Cow<'static, str>>,
/// The API key to use when making requests to Firebase Authentication endpoints.
pub firebase_auth_api_key: Cow<'static, str>,
/// Configuration for GCP Identity-Aware Proxy authentication, present only on
/// staging builds. [`None`] on production builds.
#[serde(default)]
pub iap_config: Option<IapConfig>,
}
impl WarpServerConfig {
pub fn production() -> Self {
Self {
server_root_url: "".into(),
rtc_server_url: "".into(),
session_sharing_server_url: None,
firebase_auth_api_key: "".into(),
server_root_url: "https://app.warp.dev".into(),
rtc_server_url: "wss://rtc.app.warp.dev/graphql/v2".into(),
session_sharing_server_url: Some("wss://sessions.app.warp.dev".into()),
firebase_auth_api_key: "AIzaSyBdy3O3S9hrdayLJxJ7mriBR4qgUaUygAs".into(),
iap_config: None,
}
}
}
+12
View File
@@ -58,6 +58,18 @@ impl Channel {
Channel::Oss => "galaxy-ai-oss",
}
}
/// Returns the Warp Control CLI command name corresponding to this channel.
pub fn warpctrl_command_name(&self) -> &'static str {
match self {
Channel::Stable => "warpctrl",
Channel::Dev => "warpctrl-dev",
Channel::Preview => "warpctrl-preview",
Channel::Local => "warpctrl-local",
Channel::Integration => "warpctrl-integration",
Channel::Oss => "warpctrl-oss",
}
}
}
impl fmt::Display for Channel {
+32 -32
View File
@@ -1,17 +1,17 @@
use std::borrow::Cow;
use std::collections::HashSet;
use lazy_static::lazy_static;
use parking_lot::Mutex;
use std::{borrow::Cow, collections::HashSet};
use url::{Origin, ParseError, Url};
use crate::AppId;
use crate::{
channel::config::{
ChannelConfig, McpOAuthProviderConfig, OzConfig, RudderStackDestination, WarpServerConfig,
},
features::FeatureFlag,
};
use super::Channel;
use crate::channel::config::{
ChannelConfig, IapConfig, McpOAuthProviderConfig, OzConfig, RudderStackDestination,
WarpServerConfig,
};
use crate::features::FeatureFlag;
use crate::AppId;
lazy_static! {
static ref CHANNEL_STATE: Mutex<ChannelState> = Mutex::new(ChannelState::init());
@@ -19,8 +19,8 @@ lazy_static! {
#[cfg(feature = "test-util")]
lazy_static! {
static ref MOCK_SERVER: mockito::ServerGuard = mockito::Server::new();
static ref MOCK_SERVER_URL: String = MOCK_SERVER.url();
static ref MOCK_SERVER: Mutex<mockito::ServerGuard> = Mutex::new(mockito::Server::new());
static ref MOCK_SERVER_URL: String = MOCK_SERVER.lock().url();
static ref APP_VERSION: Mutex<Option<&'static str>> = Mutex::new(None);
}
@@ -54,6 +54,13 @@ impl ChannelState {
}
}
/// Returns the server used by test-only URL routing so downstream tests can install mocks.
#[cfg(feature = "test-util")]
pub fn mock_server() -> parking_lot::MutexGuard<'static, mockito::ServerGuard> {
lazy_static::initialize(&MOCK_SERVER_URL);
MOCK_SERVER.lock()
}
pub fn new(channel: Channel, mut config: ChannelConfig) -> Self {
if let Some(app_id) = app_id_from_bundle() {
config.app_id = app_id;
@@ -216,6 +223,10 @@ impl ChannelState {
.clone()
}
pub fn iap_config() -> Option<IapConfig> {
CHANNEL_STATE.lock().config.server_config.iap_config.clone()
}
pub fn ws_server_url() -> Cow<'static, str> {
CHANNEL_STATE
.lock()
@@ -422,28 +433,17 @@ fn app_id_from_bundle() -> Option<AppId> {
// We skip this for tests, as the call to `mainBundle` can take 30+ms,
// which is a significant portion of the total test runtime.
#[cfg(all(target_os = "macos", not(feature = "test-util")))]
#[allow(deprecated)]
unsafe {
use cocoa::{
base::{id, nil},
foundation::NSBundle,
};
use galaxyui::platform::mac::utils::nsstring_as_str;
use objc::{msg_send, sel, sel_impl};
{
use objc2_foundation::NSBundle;
let bundle = id::mainBundle();
if bundle != nil {
let nsstring: id = msg_send![bundle, bundleIdentifier];
if nsstring != nil {
let app_id = nsstring_as_str(nsstring)
.expect("bundle IDs should always be valid UTF-8 strings");
if !app_id.is_empty() {
return Some(
AppId::parse(app_id)
.expect("macOS bundle identifier has an unexpected format"),
);
}
let bundle = NSBundle::mainBundle();
if let Some(bundle_identifier) = bundle.bundleIdentifier() {
let app_id = bundle_identifier.to_string();
if !app_id.is_empty() {
return Some(
AppId::parse(&app_id)
.expect("macOS bundle identifier has an unexpected format"),
);
}
}
}
+2 -4
View File
@@ -1,10 +1,8 @@
//! ContextFlag flags are for behaviors that need to be conditionally enabled or disabled based
//! on where the app is being run and are a permanent part of the app.
use std::{
str::FromStr,
sync::atomic::{AtomicBool, Ordering},
};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use enum_iterator::{cardinality, Sequence};
+54 -4
View File
@@ -9,15 +9,24 @@ mod websocket;
// Re-export for macro use.
#[doc(hidden)]
pub use inventory::submit;
pub use registration::{register_error, ErrorRegistration, RegisteredError};
pub use self::anyhow::AnyhowErrorExt;
pub use registration::{ErrorRegistration, RegisteredError};
pub use registration::register_error;
/// The `target` that is set by log entries from this module.
pub const LOG_TARGET: &str = "errors::report_error";
/// Controls how often a [`report_error!`] invocation logs errors.
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ReportErrorLogMode {
/// Log every time the error is reported.
#[default]
EveryTime,
/// Log only the first time this macro invocation is reached during the
/// current app run.
OncePerRun,
}
/// Reports an error encountered during execution.
///
/// This checks whether or not the error is actionable, and logs an error or
@@ -26,7 +35,7 @@ pub const LOG_TARGET: &str = "errors::report_error";
/// upon.)
#[macro_export]
macro_rules! report_error {
($err:expr) => {{
(@log $err:expr) => {{
#[allow(unused_imports)]
use $crate::errors::{AnyhowErrorExt as _, ErrorExt as _, LOG_TARGET};
let err = $err;
@@ -38,6 +47,38 @@ macro_rules! report_error {
};
log::log!(target: LOG_TARGET, log_level, "{:#}", err);
}};
(@once_per_run $err:expr) => {{
static HAS_LOGGED_REPORT_ERROR: ::std::sync::atomic::AtomicBool =
::std::sync::atomic::AtomicBool::new(false);
if !HAS_LOGGED_REPORT_ERROR.swap(true, ::std::sync::atomic::Ordering::Relaxed) {
$crate::report_error!(@log $err);
}
}};
($err:expr) => {{
$crate::report_error!(@log $err);
}};
($err:expr, $crate::errors::ReportErrorLogMode::EveryTime) => {{
$crate::report_error!(@log $err);
}};
($err:expr, ReportErrorLogMode::EveryTime) => {{
$crate::report_error!(@log $err);
}};
($err:expr, $crate::errors::ReportErrorLogMode::OncePerRun) => {{
$crate::report_error!(@once_per_run $err);
}};
($err:expr, ReportErrorLogMode::OncePerRun) => {{
$crate::report_error!(@once_per_run $err);
}};
($err:expr, $log_mode:expr) => {{
match $log_mode {
$crate::errors::ReportErrorLogMode::EveryTime => {
$crate::report_error!(@log $err);
}
$crate::errors::ReportErrorLogMode::OncePerRun => {
$crate::report_error!(@once_per_run $err);
}
}
}};
}
pub use report_error;
@@ -54,6 +95,11 @@ macro_rules! report_if_error {
$crate::report_error!(error);
}
}};
($result:expr, $log_mode:expr) => {{
if let Err(error) = &$result {
$crate::report_error!(error, $log_mode);
}
}};
}
pub use report_if_error;
@@ -78,3 +124,7 @@ pub trait ErrorExt: RegisteredError + std::error::Error {
sentry::capture_error(self);
}
}
#[cfg(test)]
#[path = "errors_tests.rs"]
mod tests;
+116
View File
@@ -0,0 +1,116 @@
use std::sync::{Mutex, OnceLock};
use log::{Level, Log, Metadata, Record};
use crate::errors::{ReportErrorLogMode, LOG_TARGET};
#[derive(Clone, Debug, Eq, PartialEq)]
struct LogEntry {
target: String,
level: Level,
message: String,
}
struct TestLogger;
static LOGGER: TestLogger = TestLogger;
static LOGS: OnceLock<Mutex<Vec<LogEntry>>> = OnceLock::new();
impl Log for TestLogger {
fn enabled(&self, _metadata: &Metadata) -> bool {
true
}
fn log(&self, record: &Record) {
logs().lock().unwrap().push(LogEntry {
target: record.target().to_owned(),
level: record.level(),
message: record.args().to_string(),
});
}
fn flush(&self) {}
}
fn logs() -> &'static Mutex<Vec<LogEntry>> {
LOGS.get_or_init(|| Mutex::new(Vec::new()))
}
fn init_logger() {
let _ = log::set_logger(&LOGGER);
log::set_max_level(log::LevelFilter::Trace);
logs().lock().unwrap().clear();
}
fn logged_report_count(message: &str) -> usize {
logs()
.lock()
.unwrap()
.iter()
.filter(|entry| {
entry.target == LOG_TARGET && entry.level == Level::Error && entry.message == message
})
.count()
}
fn report_once_per_run_error() {
crate::report_error!(
anyhow::anyhow!("once per run"),
ReportErrorLogMode::OncePerRun
);
}
fn report_first_callsite_once_per_run_error() {
crate::report_error!(
anyhow::anyhow!("separate once per run"),
ReportErrorLogMode::OncePerRun
);
}
fn report_second_callsite_once_per_run_error() {
crate::report_error!(
anyhow::anyhow!("separate once per run"),
ReportErrorLogMode::OncePerRun
);
}
fn report_if_error_once_per_run(result: Result<(), anyhow::Error>) {
crate::report_if_error!(result, ReportErrorLogMode::OncePerRun);
}
#[test]
fn report_error_log_mode_controls_log_frequency() {
init_logger();
for _ in 0..2 {
crate::report_error!(anyhow::anyhow!("default"));
}
assert_eq!(logged_report_count("default"), 2);
logs().lock().unwrap().clear();
for _ in 0..2 {
crate::report_error!(
anyhow::anyhow!("explicit every time"),
ReportErrorLogMode::EveryTime
);
}
assert_eq!(logged_report_count("explicit every time"), 2);
logs().lock().unwrap().clear();
report_once_per_run_error();
report_once_per_run_error();
assert_eq!(logged_report_count("once per run"), 1);
logs().lock().unwrap().clear();
for _ in 0..2 {
report_first_callsite_once_per_run_error();
report_second_callsite_once_per_run_error();
}
assert_eq!(logged_report_count("separate once per run"), 2);
logs().lock().unwrap().clear();
for _ in 0..2 {
report_if_error_once_per_run(Err(anyhow::anyhow!("result once per run")));
}
assert_eq!(logged_report_count("result once per run"), 1);
}
+24 -7
View File
@@ -1,6 +1,7 @@
use galaxyui::{Entity, ModelContext, SingletonEntity};
use galaxyui_core::{Entity, ModelContext, SingletonEntity};
use std::sync::OnceLock;
// Global execution mode, for logic that runs outside the UI framework.
static GLOBAL_EXECUTION_MODE: OnceLock<ExecutionMode> = OnceLock::new();
@@ -11,6 +12,8 @@ pub enum ExecutionMode {
App,
/// Warp is running as a CLI.
Sdk,
/// Warp is running as the remote server daemon.
RemoteServerDaemon,
}
impl ExecutionMode {
@@ -20,6 +23,7 @@ impl ExecutionMode {
match self {
ExecutionMode::App => "warp-app",
ExecutionMode::Sdk => "warp-cli",
ExecutionMode::RemoteServerDaemon => "warp-remote-server-daemon",
}
}
}
@@ -66,7 +70,7 @@ impl AppExecutionMode {
/// Whether the app can *automatically* update. This does not prevent manual updates.
pub fn can_autoupdate(&self) -> bool {
self.is_app()
self.is_app() && cfg!(not(target_family = "wasm"))
}
/// Whether the app can automatically start MCP servers from the previous session.
@@ -74,6 +78,13 @@ impl AppExecutionMode {
self.is_app()
}
/// Whether the app can show interactive onboarding UIs (e.g. the onboarding
/// callout tutorial). Onboarding requires a user to interact with it, so it
/// is disabled in headless modes like SDK/CLI.
pub fn can_show_onboarding(&self) -> bool {
self.is_app()
}
/// Whether the app can sync agent conversations (tasks and cloud conversation metadata).
/// In CLI mode, we don't need this data since there's no user viewing it.
pub fn can_fetch_agent_runs_for_management(&self) -> bool {
@@ -81,17 +92,23 @@ impl AppExecutionMode {
}
/// Whether telemetry should be sent synchronously at shutdown.
/// In CLI mode, we synchronously send events at shutdown because there's a higher likelihood
/// that they will be lost otherwise.
/// In CLI and daemon modes, we synchronously send events at shutdown because there's a
/// higher likelihood that they will be lost otherwise.
pub fn send_telemetry_at_shutdown(&self) -> bool {
matches!(self.mode, ExecutionMode::Sdk)
matches!(
self.mode,
ExecutionMode::Sdk | ExecutionMode::RemoteServerDaemon
)
}
/// If true, the app is running autonomously, without a user present.
/// Wherever possible, prefer more targeted capability checks like
/// [`Self::can_autostart_mcp_servers`].
pub fn is_autonomous(&self) -> bool {
matches!(self.mode, ExecutionMode::Sdk)
matches!(
self.mode,
ExecutionMode::Sdk | ExecutionMode::RemoteServerDaemon
)
}
/// Returns the client ID to report to the server.
@@ -112,7 +129,7 @@ impl Entity for AppExecutionMode {
impl SingletonEntity for AppExecutionMode {}
/// Returns the current global client ID string ("warp-app" or "warp-cli").
/// Returns the current global client ID string ("warp-app", "warp-cli", or "warp-remote-server-daemon").
/// This is set when AppExecutionMode is constructed during application start.
/// Returns None if the execution mode has not been set yet.
pub fn current_client_id() -> Option<&'static str> {
+1 -2
View File
@@ -1,6 +1,5 @@
pub use galaxy_features::*;
use galaxyui::platform::menu::{CustomMenuItem, MenuItem, MenuItemPropertyChanges};
use galaxyui_core::platform::menu::{CustomMenuItem, MenuItem, MenuItemPropertyChanges};
fn feature_flag_menu_item(flag: FeatureFlag) -> MenuItem {
MenuItem::Custom(CustomMenuItem::new(
&format!("{flag:?}"),
+3 -1
View File
@@ -1,11 +1,13 @@
use std::fmt;
use serde::{Deserialize, Serialize};
/// Opaque identifier for a remote host.
///
/// Returned by the server in `InitializeResponse`. Used by
/// `RemoteServerManager` and downstream features to deduplicate
/// host-scoped models (e.g. `RepoMetadataModel`) across sessions.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct HostId(String);
impl HostId {
+4 -2
View File
@@ -1,6 +1,6 @@
use std::time::Duration;
use galaxyui::{Entity, SingletonEntity};
use galaxyui_core::{Entity, SingletonEntity};
use instant::Instant;
use serde::{Deserialize, Serialize};
@@ -37,8 +37,10 @@ impl IntervalTimer {
}
pub fn mark_interval_end(&mut self, name: impl Into<String>) {
let name = name.into();
tracing::info!(name);
self.intervals
.push(TimingInterval::new(name.into(), Instant::now()))
.push(TimingInterval::new(name, Instant::now()))
}
pub fn compute_duration_for_interval(&self, name: &str) -> Option<Duration> {
+5 -2
View File
@@ -1,5 +1,6 @@
pub mod app_id;
pub mod assertions;
pub mod r#async;
pub mod channel;
pub mod command;
pub mod context_flag;
@@ -19,7 +20,6 @@ pub use settings;
pub use settings::{
define_setting, define_settings_group, implement_setting_for_enum, maybe_define_setting,
};
pub mod host_id;
pub mod session_id;
pub mod sync_queue;
pub mod telemetry;
@@ -27,5 +27,8 @@ pub mod ui;
pub mod user_preferences;
pub use app_id::AppId;
pub use host_id::HostId;
pub use session_id::SessionId;
pub use warp_util::host_id::HostId;
// Re-export galaxyui_core so that it can be referenced safely from the
// telemetry macros.
pub use galaxyui_core;
@@ -1,14 +1,14 @@
//! Module containing operating system information such as the name, category, and version.
use serde::Serialize;
use serde_with::SerializeDisplay;
use std::fmt::{Display, Formatter};
use std::sync::OnceLock;
use serde::Serialize;
use serde_with::SerializeDisplay;
#[cfg(target_family = "wasm")]
use galaxyui::platform::wasm;
use galaxyui_core::platform::wasm;
#[cfg(target_family = "wasm")]
use galaxyui::platform::OperatingSystem;
use galaxyui_core::platform::OperatingSystem;
static OS_INFO: OnceLock<Result<OperatingSystemInfo, OperatingSystemInfoError>> = OnceLock::new();
@@ -131,7 +131,7 @@ pub enum OperatingSystemCategory {
impl OperatingSystemCategory {
#[cfg_attr(target_family = "wasm", allow(dead_code))]
fn new() -> Option<Self> {
if cfg!(target_os = "linux") {
if cfg!(any(target_os = "linux", target_os = "freebsd")) {
Some(OperatingSystemCategory::Linux)
} else if cfg!(target_os = "macos") {
Some(OperatingSystemCategory::Mac)
+5 -7
View File
@@ -19,10 +19,8 @@ use std::path::{Path, PathBuf};
use cfg_if::cfg_if;
use directories::BaseDirs;
use crate::{
channel::{Channel, ChannelState},
AppId,
};
use crate::channel::{Channel, ChannelState};
use crate::AppId;
/// The name of the directory in which to put non-global Warp Core-specific files.
///
@@ -366,7 +364,7 @@ fn project_dirs_for_app_id(
data_profile: Option<&str>,
) -> Option<directories::ProjectDirs> {
cfg_if::cfg_if! {
if #[cfg(target_os = "linux")] {
if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
// Adjust the base application name so that we end up with
// directories like "warp-terminal" and "warp-terminal-dev", to
// match our Linux package name.
@@ -426,7 +424,7 @@ pub fn app_group_container_path() -> Option<PathBuf> {
/// Returns the path to resources included in the Warp distribution.
///
/// Unlike [`galaxyui::AssetProvider`] assets, which are generally embedded in the binary, these are
/// Unlike [`galaxyui_core::AssetProvider`] assets, which are generally embedded in the binary, these are
/// stored on the filesystem alongside the rest of Warp.
///
/// ## macOS
@@ -448,7 +446,7 @@ pub fn bundled_resources_dir() -> Option<PathBuf> {
.join("Contents")
.join("Resources")
})
} else if #[cfg(target_os = "linux")] {
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
std::env::current_exe()
.ok()
.and_then(|executable| std::fs::canonicalize(executable).ok())
+9 -9
View File
@@ -8,8 +8,8 @@ fn test_data_dir_path() {
// ChannelState, by default, is configured for Channel::Oss.
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
assert_eq!(data_dir(), home_dir.join(".warp-core-oss"));
} else if #[cfg(target_os = "linux")] {
assert_eq!(data_dir(), home_dir.join(".warp-oss"));
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(data_dir(), home_dir.join(".local/share/warp-oss"));
} else if #[cfg(windows)] {
assert_eq!(data_dir(), home_dir.join("AppData\\Roaming\\warp\\WarpOss\\data"));
@@ -25,8 +25,8 @@ fn test_config_local_dir_path() {
// ChannelState, by default, is configured for Channel::Oss.
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
assert_eq!(config_local_dir(), home_dir.join(".warp-core-oss"));
} else if #[cfg(target_os = "linux")] {
assert_eq!(config_local_dir(), home_dir.join(".warp-oss"));
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(config_local_dir(), home_dir.join(".config/warp-oss"));
} else if #[cfg(windows)] {
assert_eq!(config_local_dir(), home_dir.join("AppData\\Local\\warp\\WarpOss\\config"));
@@ -69,7 +69,7 @@ fn test_cache_dir_path() {
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
assert_eq!(cache_dir(), home_dir.join("Library/Application Support/dev.warp.WarpOss"));
} else if #[cfg(target_os = "linux")] {
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(cache_dir(), home_dir.join(".cache/warp-oss"));
} else if #[cfg(windows)] {
assert_eq!(cache_dir(), home_dir.join("AppData\\Local\\warp\\WarpOss\\cache"));
@@ -86,7 +86,7 @@ fn test_state_dir_path() {
// ChannelState, by default, is configured for Channel::Oss.
if #[cfg(target_os = "macos")] {
assert_eq!(state_dir(), home_dir.join("Library/Application Support/dev.warp.WarpOss"));
} else if #[cfg(target_os = "linux")] {
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(state_dir(), home_dir.join(".local/state/warp-oss"));
} else if #[cfg(windows)] {
assert_eq!(state_dir(), home_dir.join("AppData\\Local\\warp\\WarpOss\\data"));
@@ -103,7 +103,7 @@ fn test_project_path_for_warp_app_id() {
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
assert_eq!(project_dirs.project_path(), "dev.warp.Warp");
} else if #[cfg(target_os = "linux")] {
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(project_dirs.project_path(), "warp-terminal");
} else if #[cfg(windows)] {
assert_eq!(project_dirs.project_path(), "warp\\Warp");
@@ -120,7 +120,7 @@ fn test_project_path_for_warp_dev_app_id() {
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
assert_eq!(project_dirs.project_path(), "dev.warp.WarpDev");
} else if #[cfg(target_os = "linux")] {
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(project_dirs.project_path(), "warp-terminal-dev");
} else if #[cfg(windows)] {
assert_eq!(project_dirs.project_path(), "warp\\WarpDev");
@@ -137,7 +137,7 @@ fn test_project_path_for_oss_app_id() {
cfg_if::cfg_if! {
if #[cfg(target_os = "macos")] {
assert_eq!(project_dirs.project_path(), "dev.warp.WarpOss");
} else if #[cfg(target_os = "linux")] {
} else if #[cfg(any(target_os = "linux", target_os = "freebsd"))] {
assert_eq!(project_dirs.project_path(), "warp-oss");
} else if #[cfg(windows)] {
assert_eq!(project_dirs.project_path(), "warp\\WarpOss");
@@ -1,15 +1,16 @@
use std::{collections::HashSet, ops::Range};
use std::collections::HashSet;
use std::ops::Range;
use galaxyui::elements::SmartSelectFn;
use galaxyui_core::elements::SmartSelectFn;
use lazy_static::lazy_static;
use regex::Regex;
use settings::{macros::define_settings_group, Setting, SupportedPlatforms, SyncToCloud};
use settings::ChangeEventReason;
use settings::macros::define_settings_group;
use settings::{Setting, SupportedPlatforms, SyncToCloud};
use settings_value::SettingsValue;
use string_offset::ByteOffset;
use galaxyui::text::{
word_boundaries::WordBoundariesPolicy,
words::{is_default_word_boundary, DEFAULT_WORD_BOUNDARY_CHARS},
};
use galaxyui_core::text::word_boundaries::WordBoundariesPolicy;
use galaxyui_core::text::words::{is_default_word_boundary, DEFAULT_WORD_BOUNDARY_CHARS};
/// Upper limit for how many characters in either direction we'll search for patterns. Need to
/// limit this to avoid running regex on absurdly long words
@@ -219,5 +220,5 @@ impl SemanticSelection {
#[cfg(test)]
#[cfg(feature = "test-util")]
#[path = "mod_test.rs"]
#[path = "mod_tests.rs"]
mod tests;
@@ -0,0 +1,117 @@
use super::*;
#[test]
fn test_basic_url_selection() {
let sel = SemanticSelection::mock(true, "");
assert_eq!(
sel.smart_search("http://stackoverflow.com foo", 5.into()),
Some(ByteOffset::range(0..24))
);
assert_eq!(
sel.smart_search("click here:http://stackoverflow.com", 15.into()),
Some(ByteOffset::range(11..35))
);
assert_eq!(
sel.smart_search("word here https://andy:foo@stackoverflow.com/questions/28265036/how?foo=bar&baz=food#thing-here other/stuff", 35.into()),
Some(ByteOffset::range(10..95))
);
}
#[test]
fn test_other_url_selection() {
let sel = SemanticSelection::mock(true, "");
assert_eq!(
sel.smart_search("ssh://git@github.com:acarl005/dotfiles.git", 0.into()),
Some(ByteOffset::range(0..42))
);
assert_eq!(
sel.smart_search("data hdfs://hadoopNS/data/users.csv there", 20.into()),
Some(ByteOffset::range(5..35))
);
assert_eq!(
sel.smart_search("send files here&ftp://thing.网站/file please", 30.into()),
Some(ByteOffset::range(16..39))
);
assert_eq!(
sel.smart_search("http://[2001:db8::1]:80", 10.into()),
Some(ByteOffset::range(0..23))
);
}
#[test]
fn test_email_selection() {
let sel = SemanticSelection::mock(true, "");
assert_eq!(
sel.smart_search(
"mail to: acarl005@g.ucla.edu andy+1.hello@warp.dev",
15.into()
),
Some(ByteOffset::range(9..28))
);
assert_eq!(
sel.smart_search(
"mail to: acarl005@g.ucla.edu andy+1.hello@warp.dev",
33.into()
),
Some(ByteOffset::range(29..50))
);
}
#[test]
fn test_numerical_selection() {
let sel = SemanticSelection::mock(true, "");
assert_eq!(
sel.smart_search("6.02e-23 -8.1e1 3.1415 8.1e+10-3.1415", 2.into()),
Some(ByteOffset::range(0..8))
);
assert_eq!(
sel.smart_search("6.02e-23 -8.1e1 3.1415 8.1e+10-3.1415", 11.into()),
Some(ByteOffset::range(9..15))
);
assert_eq!(
sel.smart_search("6.02e-23 -8.1e1 3.1415 8.1e+10-3.1415", 18.into()),
Some(ByteOffset::range(16..22))
);
assert_eq!(
sel.smart_search("6.02e-23 -8.1e1 3.1415 8.1e+10-3.1415", 27.into()),
Some(ByteOffset::range(23..30))
);
}
#[test]
fn test_filepath_selection() {
let sel = SemanticSelection::mock(true, "");
assert_eq!(
sel.smart_search("~/.config/nvim/init.lua C:\\system\\file\\path", 2.into()),
Some(ByteOffset::range(0..23))
);
assert_eq!(
sel.smart_search("~/.config/nvim/init.lua C:\\system\\file\\path", 30.into()),
Some(ByteOffset::range(24..43))
);
assert_eq!(
sel.smart_search("scp ./foo.txt andy@ubuntu:/etc/foo.txt", 7.into()),
Some(ByteOffset::range(4..13))
);
assert_eq!(
sel.smart_search("scp ./foo.txt andy@ubuntu:/etc/foo.txt", 28.into()),
Some(ByteOffset::range(26..38))
);
}
#[test]
fn test_identifier_selection() {
let sel = SemanticSelection::mock(true, "");
assert_eq!(
sel.smart_search("192.168.0.1:3000 api-service-pod foo-bar--baz", 6.into()),
Some(ByteOffset::range(0..11))
);
assert_eq!(
sel.smart_search("192.168.0.1:3000 api-service-pod foo-bar--baz", 30.into()),
Some(ByteOffset::range(17..32))
);
assert_eq!(
sel.smart_search("192.168.0.1:3000 api-service-pod foo-bar--baz", 33.into()),
Some(ByteOffset::range(33..40))
);
}
+9 -8
View File
@@ -1,10 +1,3 @@
use async_broadcast::{InactiveReceiver, Sender as BroadcastSender};
use futures::channel::mpsc::{self, Receiver as MpscReceiver, Sender as MpscSender};
use futures::channel::oneshot::{self, Receiver, Sender};
use futures::future::{AbortHandle, Abortable};
use futures::StreamExt;
use galaxyui::r#async::executor::Background;
use instant::Instant;
use std::collections::HashMap;
use std::future::Future;
use std::sync::atomic::{AtomicU64, Ordering};
@@ -12,7 +5,15 @@ use std::sync::{Arc, Mutex};
use std::time::Duration;
use anyhow::Result;
use galaxyui::{r#async::Timer, Entity, RetryOption, SingletonEntity};
use async_broadcast::{InactiveReceiver, Sender as BroadcastSender};
use futures::channel::mpsc::{self, Receiver as MpscReceiver, Sender as MpscSender};
use futures::channel::oneshot::{self, Receiver, Sender};
use futures::future::{AbortHandle, Abortable};
use futures::StreamExt;
use galaxyui_core::r#async::executor::Background;
use instant::Instant;
use galaxyui_core::r#async::Timer;
use galaxyui_core::{Entity, RetryOption, SingletonEntity};
const DEFAULT_BUFFER_SIZE: usize = 1024;
const DEFAULT_SYNC_RETRY_STRATEGY: RetryOption = RetryOption::exponential(
+1 -1
View File
@@ -2,7 +2,7 @@ use std::sync::Arc;
use futures::channel::oneshot;
use futures::StreamExt;
use galaxyui::r#async::executor::Background;
use galaxyui_core::r#async::executor::Background;
use super::*;
+1 -1
View File
@@ -1,4 +1,4 @@
use galaxyui::{AppContext, Entity, SingletonEntity};
use galaxyui_core::{AppContext, Entity, SingletonEntity};
use serde_json::Value;
use strum::IntoEnumIterator;
+6 -7
View File
@@ -1,9 +1,8 @@
use galaxyui::{
fonts::{FamilyId, Weight},
Entity, ModelContext, SingletonEntity,
};
use galaxyui_core::fonts::{FamilyId, Weight};
use galaxyui_core::{Entity, ModelContext, SingletonEntity};
use super::{builder::UiBuilder, theme::GalaxyTheme};
use super::builder::UiBuilder;
use super::theme::GalaxyTheme;
/// The standard font size to use for headers (e.g.: in dialogs).
const HEADER_FONT_SIZE: f32 = 18.;
@@ -35,7 +34,7 @@ pub struct Appearance {
/// Defines appearance change events.
///
/// For any properties that are read from appearance (e.g.: theme, font, etc.),
/// users should listen for these events rather than directly listenting to
/// users should listen for these events rather than directly listening to
/// settings change events for the underlying properties.
///
/// NOTE: You do NOT need to set up subscriptions for these events and use them
@@ -101,7 +100,7 @@ impl Appearance {
#[cfg(feature = "test-util")]
pub fn mock() -> Self {
use galaxyui::color::ColorU;
use galaxyui_core::color::ColorU;
use crate::ui::theme::{mock_terminal_colors, Details, Fill};
+29 -35
View File
@@ -1,47 +1,41 @@
use std::borrow::Cow;
use std::rc::Rc;
use super::color::{blend::Blend, contrast::MinimumAllowedContrast, ContrastingColor};
use super::theme::color::internal_colors::{self, text_main};
use super::theme::{Fill, GalaxyTheme};
use galaxyui::color::ColorU;
use galaxyui::elements::{
use galaxyui_core::color::ColorU;
use galaxyui_core::elements::{
ChildAnchor, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Flex, Hoverable,
OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, Stack, Text,
Icon, MouseStateHandle, OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds,
Radius, Stack, Text,
};
use galaxyui::fonts::{Properties, Weight};
use galaxyui::geometry::vector::{vec2f, Vector2F};
use galaxyui::platform::Cursor;
use galaxyui::ui_components::components::UiComponent;
use galaxyui::ui_components::keyboard_shortcut::KeyboardShortcut;
use galaxyui::ui_components::link::{LinkStyles, OnClickFn};
use galaxyui::ui_components::list::{List, ListStyle};
use galaxyui::ui_components::radio_buttons::{
use galaxyui_core::fonts::{FamilyId, Properties, Weight};
use galaxyui_core::geometry::vector::{vec2f, Vector2F};
use galaxyui_core::keymap::Keystroke;
use galaxyui_core::platform::Cursor;
use galaxyui_core::ui_components::button::{Button, ButtonVariant};
use galaxyui_core::ui_components::checkbox::Checkbox;
use galaxyui_core::ui_components::components::{Coords, UiComponent, UiComponentStyles};
use galaxyui_core::ui_components::keyboard_shortcut::KeyboardShortcut;
use galaxyui_core::ui_components::link::{Link, LinkStyles, OnClickFn};
use galaxyui_core::ui_components::list::{List, ListStyle};
use galaxyui_core::ui_components::progress_bar::ProgressBar;
use galaxyui_core::ui_components::radio_buttons::{
RadioButtonItem, RadioButtonLayout, RadioButtonStateHandle, RadioButtons,
};
use galaxyui::ui_components::slider::{Slider, SliderStateHandle};
use galaxyui::ui_components::switch::{Switch, SwitchStateHandle, TRACK_COLOR};
use galaxyui::ui_components::text::WrappableText;
use galaxyui::ui_components::toggle_menu::{
use galaxyui_core::ui_components::slider::{Slider, SliderStateHandle};
use galaxyui_core::ui_components::switch::{Switch, SwitchStateHandle, TRACK_COLOR};
use galaxyui_core::ui_components::text::{Paragraph, Span, WrappableText};
use galaxyui_core::ui_components::text_input::TextInput;
use galaxyui_core::ui_components::toggle_menu::{
ToggleMenu, ToggleMenuCallback, ToggleMenuItem, ToggleMenuStateHandle,
};
use galaxyui::ui_components::tool_tip::{Tooltip, TooltipWithSublabel};
use galaxyui::View;
use galaxyui::{
elements::{Icon, MouseStateHandle},
fonts::FamilyId,
keymap::Keystroke,
ui_components::{
button::{Button, ButtonVariant},
checkbox::Checkbox,
components::{Coords, UiComponentStyles},
link::Link,
progress_bar::ProgressBar,
text::{Paragraph, Span},
text_input::TextInput,
},
Element, ViewHandle,
};
use galaxyui_core::ui_components::tool_tip::{Tooltip, TooltipWithSublabel};
use galaxyui_core::{Element, View, ViewHandle};
use super::color::blend::Blend;
use super::color::contrast::MinimumAllowedContrast;
use super::color::ContrastingColor;
use super::theme::color::internal_colors::{self, text_main};
use super::theme::{Fill, GalaxyTheme};
const CLOSE_SVG_PATH: &str = "bundled/svg/close.svg";
const COPY_SVG_PATH: &str = "bundled/svg/copy.svg";
+1 -1
View File
@@ -1,4 +1,4 @@
use galaxyui::color::ColorU;
use galaxyui_core::color::ColorU;
pub trait Blend<Rhs = Self> {
type Output;
+4 -3
View File
@@ -1,6 +1,7 @@
use galaxyui::color::ColorU;
use galaxyui_core::color::ColorU;
use super::{blend::Blend, coloru_with_opacity, Rgb};
use super::blend::Blend;
use super::{coloru_with_opacity, Rgb};
/// Offset to the relative luminance when computing the contrast ratio per the formula defined in
/// the [W3C Spec](https://www.w3.org/TR/WCAG20-TECHS/G17.html). This offset is included to
@@ -11,7 +12,7 @@ const LUMINANCE_OFFSET_FOR_CONTRAST_RATIO: f32 = 0.05;
/// Returns a new foreground color that when rendered against `background_color` would have a
/// contrast of at least `minimum_allowed_contrast`. NOTE the `background_color` must be fully
/// opaque in in order to perform proper contrast checking.
/// opaque in order to perform proper contrast checking.
///
/// If `foreground_color` already meets the minimum contrast, it is returned unchanged.
///
@@ -1,7 +1,8 @@
use super::*;
use rand::prelude::StdRng;
use rand::{Rng, SeedableRng};
use super::*;
#[test]
fn foreground_color_with_minimum_contrast_foreground_already_meets_minimum() {
assert_eq!(
+4 -2
View File
@@ -1,6 +1,8 @@
use galaxyui::color::ColorU;
use std::borrow::Cow;
use std::fmt;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::{borrow::Cow, fmt};
use galaxyui_core::color::ColorU;
use super::OPAQUE;
+2 -2
View File
@@ -1,4 +1,4 @@
use galaxyui::color::ColorU;
use galaxyui_core::color::ColorU;
use self::contrast::{high_enough_contrast, pick_constrasting_color, MinimumAllowedContrast};
@@ -78,7 +78,7 @@ pub fn darken(c: ColorU) -> ColorU {
/// Finds a ligher version of the given color using LIGHTEN_COLORU_SHADE_FACTOR form factor.
pub fn lighten(c: ColorU) -> ColorU {
// aplying the shade factor only to the difference between 255 and channel
// applying the shade factor only to the difference between 255 and channel
// (doing so to the actual channel value could produce incorrect results
// since channels are capped at 255 value).
let r = ((OPAQUE - c.r) as f32 * LIGHTEN_COLORU_SHADE_FACTOR).ceil() as u8;
@@ -1,5 +1,6 @@
use galaxyui_core::elements::Icon as WarpUiIcon;
use crate::ui::theme::Fill;
use galaxyui::elements::Icon as WarpUiIcon;
pub enum ExternalProductIcon {
Heroku,
+77 -25
View File
@@ -1,10 +1,11 @@
use galaxyui_core::elements::Icon as WarpUiIcon;
use crate::ui::theme::Fill;
use galaxyui::elements::Icon as WarpUiIcon;
/// Default icon dimensions that apply to all icons used within the ui system.
pub const ICON_DIMENSIONS: f32 = 24.;
/// Icon enum to be used within the app in place of the galaxyui::elements::Icon directly. It
/// Icon enum to be used within the app in place of the galaxyui_core::elements::Icon directly. It
/// abstracts things like svg paths out and provides a utility method to convert into the actual Icon.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Icon {
@@ -75,6 +76,7 @@ pub enum Icon {
LinkExternal,
CheckCircleBroken,
Link,
QrCode,
Refresh,
RefreshCcw,
RefreshCw04,
@@ -238,17 +240,17 @@ pub enum Icon {
ContextWindowSixtyPct,
ContextWindowWarning,
ContextWindowSummarized,
ConversationContext0,
ConversationContext10,
ConversationContext20,
ConversationContext30,
ConversationContext40,
ConversationContext50,
ConversationContext60,
ConversationContext70,
ConversationContext80,
ConversationContext90,
ConversationContext100,
ContextRemaining0,
ContextRemaining10,
ContextRemaining20,
ContextRemaining30,
ContextRemaining40,
ContextRemaining50,
ContextRemaining60,
ContextRemaining70,
ContextRemaining80,
ContextRemaining90,
ContextRemaining100,
LeftSidebarOpen,
LeftSidebarClose,
Diff,
@@ -263,6 +265,7 @@ pub enum Icon {
FolderClosed,
FileCopy,
Credits,
CreditCard,
AddressedComment,
ClockSnooze,
Hand,
@@ -272,6 +275,7 @@ pub enum Icon {
ClaudeLogo,
GeminiLogo,
OpenAILogo,
XLogo,
AmpLogo,
DroidLogo,
OpenCodeLogo,
@@ -280,6 +284,8 @@ pub enum Icon {
AuggieLogo,
BedrockLogo,
CursorLogo,
GooseLogo,
AntigravityLogo,
NLD,
Oz,
OzCloud,
@@ -296,6 +302,7 @@ pub enum Icon {
CalloutTriangleBorderLeft,
CalloutTriangleFillLeft,
DragIndicator,
DragIndicatorVertical,
Ellipse,
Inbox,
Menu01,
@@ -308,14 +315,37 @@ pub enum Icon {
UploadCloud,
ClockPlus,
SwitchHorizontal01,
SwitchVertical02,
HeartHand,
MessageChatSquare,
Pin,
PinFilled,
PinFilledDiagonal,
Atom,
Cognition,
Dataflow04,
LayersThree01,
Aws,
// Language-specific icons for the code block dropdown
MermaidLang,
GoLang,
CppLang,
JavaScriptLang,
PythonLang,
RustLang,
SqlLang,
JsonLang,
PhpLang,
KotlinLang,
}
impl From<Icon> for &'static str {
fn from(icon: Icon) -> &'static str {
match icon {
Icon::Menu => "bundled/svg/layout-left.svg",
Icon::Pin => "bundled/svg/pin-01.svg",
Icon::PinFilled => "bundled/svg/pin-filled.svg",
Icon::PinFilledDiagonal => "bundled/svg/pin-filled-diagonal.svg",
Icon::AtSign => "bundled/svg/at-sign.svg",
Icon::Plus => "bundled/svg/plus.svg",
Icon::Copy => "bundled/svg/copy.svg",
@@ -382,10 +412,12 @@ impl From<Icon> for &'static str {
Icon::ArrowDown => "bundled/svg/arrow-narrow-down.svg",
Icon::ArrowSplit => "bundled/svg/arrow-split.svg",
Icon::SwitchHorizontal01 => "bundled/svg/switch-horizontal-01.svg",
Icon::SwitchVertical02 => "bundled/svg/switch-vertical-02.svg",
Icon::ArrowDropDown => "bundled/svg/arrow-drop-down.svg",
Icon::CheckCircleBroken => "bundled/svg/check-circle-broken.svg",
Icon::LinkExternal => "bundled/svg/link-external-02.svg",
Icon::Link => "bundled/svg/link-03.svg",
Icon::QrCode => "bundled/svg/qr-code-02.svg",
Icon::Refresh => "bundled/svg/refresh.svg",
Icon::RefreshCcw => "bundled/svg/refresh-ccw-01.svg",
Icon::RefreshCw04 => "bundled/svg/refresh-cw-04.svg",
@@ -546,17 +578,17 @@ impl From<Icon> for &'static str {
Icon::ContextWindowSixtyPct => "bundled/svg/context-window-60-pct.svg",
Icon::ContextWindowWarning => "bundled/svg/context-window-warning.svg",
Icon::ContextWindowSummarized => "bundled/svg/context-window-summarized.svg",
Icon::ConversationContext0 => "bundled/svg/conversation-context-0.svg",
Icon::ConversationContext10 => "bundled/svg/conversation-context-10.svg",
Icon::ConversationContext20 => "bundled/svg/conversation-context-20.svg",
Icon::ConversationContext30 => "bundled/svg/conversation-context-30.svg",
Icon::ConversationContext40 => "bundled/svg/conversation-context-40.svg",
Icon::ConversationContext50 => "bundled/svg/conversation-context-50.svg",
Icon::ConversationContext60 => "bundled/svg/conversation-context-60.svg",
Icon::ConversationContext70 => "bundled/svg/conversation-context-70.svg",
Icon::ConversationContext80 => "bundled/svg/conversation-context-80.svg",
Icon::ConversationContext90 => "bundled/svg/conversation-context-90.svg",
Icon::ConversationContext100 => "bundled/svg/conversation-context-100.svg",
Icon::ContextRemaining0 => "bundled/svg/context-remaining-0.svg",
Icon::ContextRemaining10 => "bundled/svg/context-remaining-10.svg",
Icon::ContextRemaining20 => "bundled/svg/context-remaining-20.svg",
Icon::ContextRemaining30 => "bundled/svg/context-remaining-30.svg",
Icon::ContextRemaining40 => "bundled/svg/context-remaining-40.svg",
Icon::ContextRemaining50 => "bundled/svg/context-remaining-50.svg",
Icon::ContextRemaining60 => "bundled/svg/context-remaining-60.svg",
Icon::ContextRemaining70 => "bundled/svg/context-remaining-70.svg",
Icon::ContextRemaining80 => "bundled/svg/context-remaining-80.svg",
Icon::ContextRemaining90 => "bundled/svg/context-remaining-90.svg",
Icon::ContextRemaining100 => "bundled/svg/context-remaining-100.svg",
Icon::LeftSidebarOpen => "bundled/svg/left-panel-open.svg",
Icon::LeftSidebarClose => "bundled/svg/left-panel-close.svg",
Icon::Diff => "bundled/svg/diff.svg",
@@ -571,6 +603,7 @@ impl From<Icon> for &'static str {
Icon::FolderClosed => "bundled/svg/folder-closed.svg",
Icon::FileCopy => "bundled/svg/file_copy.svg",
Icon::Credits => "bundled/svg/credits.svg",
Icon::CreditCard => "bundled/svg/credit-card.svg",
Icon::AddressedComment => "bundled/svg/addressed-comment.svg",
Icon::ClockSnooze => "bundled/svg/clock-snooze.svg",
Icon::Hand => "bundled/svg/hand.svg",
@@ -580,7 +613,7 @@ impl From<Icon> for &'static str {
Icon::ClaudeLogo => "bundled/svg/claude.svg",
Icon::GeminiLogo => "bundled/svg/gemini_cli.svg",
Icon::OpenAILogo => "bundled/svg/openai.svg",
Icon::BedrockLogo => "bundled/svg/bedrock.svg",
Icon::XLogo => "bundled/svg/x-logo.svg",
Icon::AmpLogo => "bundled/svg/amp.svg",
Icon::DroidLogo => "bundled/svg/droid.svg",
Icon::OpenCodeLogo => "bundled/svg/opencode.svg",
@@ -588,6 +621,8 @@ impl From<Icon> for &'static str {
Icon::PiLogo => "bundled/svg/pi.svg",
Icon::AuggieLogo => "bundled/svg/auggie.svg",
Icon::CursorLogo => "bundled/svg/cursor.svg",
Icon::GooseLogo => "bundled/svg/goose.svg",
Icon::AntigravityLogo => "bundled/svg/antigravity_cli.svg",
Icon::NLD => "bundled/svg/nld.svg",
Icon::Oz => "bundled/svg/oz.svg",
Icon::OzCloud => "bundled/svg/oz-cloud.svg",
@@ -604,6 +639,7 @@ impl From<Icon> for &'static str {
Icon::CalloutTriangleBorderLeft => "bundled/svg/callout-triangle-border-left.svg",
Icon::CalloutTriangleFillLeft => "bundled/svg/callout-triangle-fill-left.svg",
Icon::DragIndicator => "bundled/svg/drag_indicator.svg",
Icon::DragIndicatorVertical => "bundled/svg/drag_indicator_vertical.svg",
Icon::Ellipse => "bundled/svg/ellipse.svg",
Icon::Inbox => "bundled/svg/inbox-01.svg",
Icon::Menu01 => "bundled/svg/menu-01.svg",
@@ -617,6 +653,22 @@ impl From<Icon> for &'static str {
Icon::ClockPlus => "bundled/svg/clock-plus.svg",
Icon::HeartHand => "bundled/svg/heart-hand.svg",
Icon::MessageChatSquare => "bundled/svg/message-chat-square.svg",
Icon::Atom => "bundled/svg/atom-02.svg",
Icon::Cognition => "bundled/svg/cognition.svg",
Icon::Dataflow04 => "bundled/svg/dataflow-04.svg",
Icon::LayersThree01 => "bundled/svg/layers-three-01.svg",
Icon::Aws => "bundled/svg/aws.svg",
Icon::MermaidLang => "bundled/svg/file_type/mermaid.svg",
Icon::GoLang => "bundled/svg/file_type/go.svg",
Icon::CppLang => "bundled/svg/file_type/cpp.svg",
Icon::JavaScriptLang => "bundled/svg/file_type/javascript.svg",
Icon::PythonLang => "bundled/svg/file_type/python.svg",
Icon::RustLang => "bundled/svg/file_type/rust.svg",
Icon::SqlLang => "bundled/svg/file_type/sql.svg",
Icon::JsonLang => "bundled/svg/file_type/json.svg",
Icon::PhpLang => "bundled/svg/file_type/php.svg",
Icon::KotlinLang => "bundled/svg/file_type/kotlin.svg",
Icon::BedrockLogo => "bundled/svg/bedrock.svg",
}
}
}
+18 -12
View File
@@ -3,21 +3,18 @@
//! These colors can be further understood here:
//! https://docs.google.com/document/d/1YMovEoXsPRziPk99a4i9LZNEKGm_rjEyzhcHsFkT3ac/edit.
use getset::Getters;
use serde::{Deserialize, Serialize};
use galaxyui_core::color::ColorU;
use self::internal_colors::{
accent_overlay_2, fg_overlay_1, fg_overlay_2, fg_overlay_3, neutral_1, neutral_2, neutral_3,
neutral_4,
};
use super::{AnsiColor, AnsiColorIdentifier, Fill, GalaxyTheme, TerminalColors};
use crate::ui::color::{
blend::Blend,
contrast::{pick_best_foreground_color, MinimumAllowedContrast},
Opacity,
};
use galaxyui::color::ColorU;
use getset::Getters;
use serde::{Deserialize, Serialize};
use super::{AnsiColor, AnsiColorIdentifier, Fill, TerminalColors, GalaxyTheme};
use crate::ui::color::blend::Blend;
use crate::ui::color::contrast::{pick_best_foreground_color, MinimumAllowedContrast};
use crate::ui::color::Opacity;
const BLOCK_SELECTION_OPACITY: Opacity = 10;
@@ -409,10 +406,18 @@ impl GalaxyTheme {
self.ansi_fg(AnsiColorIdentifier::Yellow.to_ansi_color(&self.terminal_colors().normal))
}
pub fn ansi_bg_magenta(&self) -> ColorU {
self.ansi_bg(AnsiColorIdentifier::Magenta.to_ansi_color(&self.terminal_colors().normal))
}
pub fn ansi_fg_magenta(&self) -> ColorU {
self.ansi_fg(AnsiColorIdentifier::Magenta.to_ansi_color(&self.terminal_colors().normal))
}
pub fn ansi_bg_yellow(&self) -> ColorU {
self.ansi_bg(AnsiColorIdentifier::Yellow.to_ansi_color(&self.terminal_colors().normal))
}
pub fn ansi_fg_cyan(&self) -> ColorU {
self.ansi_fg(AnsiColorIdentifier::Cyan.to_ansi_color(&self.terminal_colors().normal))
}
@@ -421,7 +426,8 @@ impl GalaxyTheme {
/// Internal color system tokens, defined in "Colors" [Figma project](https://www.figma.com/design/dnvTdLbfFaosFSP00F30S0/Colors).
/// Should not be used directly outside of reusable components. Use color methods on `GalaxyTheme` instead.
pub mod internal_colors {
use galaxyui::color::ColorU;
use galaxyui_core::color::ColorU;
use super::{Fill, GalaxyTheme};
use crate::ui::color::blend::Blend;
+20 -36
View File
@@ -3,23 +3,19 @@ pub mod phenomenon;
use std::path::PathBuf;
use crate::paths::themes_dir;
use super::color::{
blend::Blend,
coloru_with_opacity,
contrast::{pick_best_foreground_color, MinimumAllowedContrast},
hex_color, mid_coloru, ContrastingColor, Opacity, OPAQUE,
};
// Import relative_luminance from contrast module for brightness calculation
use crate::ui::color::contrast::relative_luminance;
use dirs::home_dir;
use galaxyui_core::assets::asset_cache::AssetSource;
use galaxyui_core::color::ColorU;
use galaxyui_core::geometry::vector::vec2f;
use serde::{Deserialize, Serialize};
use self::color::CustomDetails;
use dirs::home_dir;
use galaxyui::{assets::asset_cache::AssetSource, color::ColorU, geometry::vector::vec2f};
use serde::{Deserialize, Serialize};
use super::color::blend::Blend;
use super::color::contrast::{pick_best_foreground_color, MinimumAllowedContrast};
use super::color::{coloru_with_opacity, hex_color, mid_coloru, ContrastingColor, Opacity, OPAQUE};
use crate::paths::themes_dir;
// Import relative_luminance from contrast module for brightness calculation
use crate::ui::color::contrast::relative_luminance;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Image {
@@ -42,7 +38,7 @@ impl Serialize for Image {
where
S: serde::Serializer,
{
let AssetSource::LocalFile { path } = self.source.clone() else {
let AssetSource::LocalFile { path, .. } = self.source.clone() else {
return Err(serde::ser::Error::custom(
"image path was serialized but it's not a local file",
));
@@ -79,6 +75,7 @@ impl<'de> Deserialize<'de> for Image {
Ok(Image {
source: AssetSource::LocalFile {
path: path.to_str().unwrap_or_default().to_owned(),
content_version: None,
},
opacity: value.opacity,
})
@@ -439,17 +436,17 @@ impl ContrastingColor for Fill {
}
}
impl From<Fill> for galaxyui::elements::Fill {
impl From<Fill> for galaxyui_core::elements::Fill {
fn from(theme: Fill) -> Self {
match theme {
Fill::Solid(c) => galaxyui::elements::Fill::Solid(c),
Fill::HorizontalGradient(g) => galaxyui::elements::Fill::Gradient {
Fill::Solid(c) => galaxyui_core::elements::Fill::Solid(c),
Fill::HorizontalGradient(g) => galaxyui_core::elements::Fill::Gradient {
start: vec2f(0.0, 0.0),
end: vec2f(1.0, 0.0),
start_color: g.left,
end_color: g.right,
},
Fill::VerticalGradient(g) => galaxyui::elements::Fill::Gradient {
Fill::VerticalGradient(g) => galaxyui_core::elements::Fill::Gradient {
start: vec2f(0.0, 0.0),
end: vec2f(0.0, 1.0),
start_color: g.top,
@@ -530,12 +527,15 @@ impl AnsiColors {
Eq,
schemars::JsonSchema,
settings_value::SettingsValue,
strum_macros::Display,
strum_macros::EnumString,
)]
#[schemars(
description = "One of the eight standard ANSI terminal colors.",
rename_all = "snake_case"
)]
#[serde(rename_all = "lowercase")]
#[strum(ascii_case_insensitive)]
pub enum AnsiColorIdentifier {
Black,
Red,
@@ -547,22 +547,6 @@ pub enum AnsiColorIdentifier {
White,
}
impl std::fmt::Display for AnsiColorIdentifier {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let color_name = match self {
Self::Black => "Black",
Self::Red => "Red",
Self::Green => "Green",
Self::Yellow => "Yellow",
Self::Blue => "Blue",
Self::Magenta => "Magenta",
Self::Cyan => "Cyan",
Self::White => "White",
};
write!(f, "{color_name}")
}
}
impl AnsiColorIdentifier {
pub fn to_ansi_color(self, colors: &AnsiColors) -> AnsiColor {
match self {
@@ -1,8 +1,7 @@
use galaxyui::color::ColorU;
use crate::ui::color::blend::Blend;
use galaxyui_core::color::ColorU;
use super::Fill;
use crate::ui::color::blend::Blend;
const PHENOMENON_BACKGROUND: u32 = 0x121212FF;
const PHENOMENON_FOREGROUND: u32 = 0xFAF9F6FF;
@@ -13,8 +12,8 @@ const PHENOMENON_LABEL_TEXT: u32 = 0xFAF9F699;
const PHENOMENON_DISABLED_LABEL_TEXT: u32 = 0xFAF9F680;
const PHENOMENON_SUBTLE_BORDER: u32 = 0xFAF9F633;
const PHENOMENON_MODAL_BACKGROUND: u32 = 0x2A2A2AFF;
const PHENOMENON_MODAL_BADGE_BACKGROUND: u32 = 0xFF8FFD1A;
const PHENOMENON_MODAL_BADGE_TEXT: u32 = 0xFF8FFDFF;
const PHENOMENON_MODAL_BADGE_BACKGROUND: u32 = 0xBF409D1A;
const PHENOMENON_MODAL_BADGE_TEXT: u32 = 0xBF409DFF;
const PHENOMENON_MODAL_TITLE_TEXT: u32 = 0xFFFFFFFF;
const PHENOMENON_MODAL_FEATURE_TITLE_TEXT: u32 = 0xE6E6E6FF;
const PHENOMENON_MODAL_FEATURE_DESCRIPTION_TEXT: u32 = 0x9B9B9BFF;
@@ -184,7 +184,8 @@ fn test_deserialize_image() {
.join("warp.jpg")
.to_str()
.unwrap_or_default()
.to_owned()
.to_owned(),
content_version: None,
}
);
@@ -198,7 +199,8 @@ fn test_deserialize_image() {
assert_eq!(
image.source,
AssetSource::LocalFile {
path: "/warp.jpg".to_owned()
path: "/warp.jpg".to_owned(),
content_version: None,
}
);
@@ -216,7 +218,8 @@ fn test_deserialize_image() {
.join("warp.jpg")
.to_str()
.unwrap_or_default()
.to_owned()
.to_owned(),
content_version: None,
}
);
+3 -3
View File
@@ -6,11 +6,11 @@
use std::ops::Deref;
use galaxyui::SingletonEntity;
use galaxyui_core::SingletonEntity;
use galaxyui_extras::user_preferences::UserPreferences;
use settings::PrivatePreferences;
/// An extension trait on [`galaxyui::AppContext`] for accessing private user
/// An extension trait on [`galaxyui_core::AppContext`] for accessing private user
/// preferences.
///
/// Private settings are always stored in the platform-native store (e.g.
@@ -24,7 +24,7 @@ pub trait GetUserPreferences {
fn private_user_preferences(&self) -> &dyn UserPreferences;
}
impl GetUserPreferences for galaxyui::AppContext {
impl GetUserPreferences for galaxyui_core::AppContext {
fn private_user_preferences(&self) -> &dyn UserPreferences {
<PrivatePreferences as SingletonEntity>::as_ref(self).deref()
}