first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -86,5 +86,5 @@ impl std::fmt::Display for AppId {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "app_id_test.rs"]
|
||||
#[path = "app_id_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
mod debounce;
|
||||
|
||||
pub use debounce::debounce;
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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,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:?}"),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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))
|
||||
);
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
use galaxyui::{AppContext, Entity, SingletonEntity};
|
||||
use galaxyui_core::{AppContext, Entity, SingletonEntity};
|
||||
use serde_json::Value;
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
use galaxyui::color::ColorU;
|
||||
use galaxyui_core::color::ColorU;
|
||||
|
||||
pub trait Blend<Rhs = Self> {
|
||||
type Output;
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user