Major features: - Auto-compact: triggers conversation summarization when context window >= 85%, compacts Bedrock message history to a summary pair, and tracks live context tokens - Bedrock summarization: plumbs `is_summarization` flag through translator/client/response pipeline, handles SummarizeConversation input type, and marks `summarized` in metadata - Session restore: rebuilds bedrock_message_history from persisted task messages via newly-public `convert_proto_message`, preventing empty history on reconnect - Subagent orchestration: adds SubagentQuestion/Answer/CompletionSummary event types, parent-child question routing with depth limits, retry counting, and drain methods - Summarization UI: inline SummarizationView in AI blocks with progress/finished states Refactors: - Rename WarpTheme → GalaxyTheme across ~100 files (rebrand continuation) - Rename warp_home_config_dir → galaxy_home_config_dir and related path functions - Predefined rules: replace "System Defined Rule #N" with descriptive names (e.g. "Correctness Over Speed", "Never Guess") and add lookup helpers - Usage view: replace cumulative input/output token display with live context tokens, cache hit rate calculation, and separate cache read/write stats - Telemetry: remove verbose doc comments, simplify trait definitions - Facts view: simplify delete permission check (always allow local deletion) - Remove warp_managed_paths_watcher.rs (dead code) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
493 lines
15 KiB
Rust
493 lines
15 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use ai::LLMId;
|
|
use anyhow::Result;
|
|
use galaxy_core::ui::icons::Icon;
|
|
use galaxy_core::ui::theme::{AnsiColor, AnsiColors, Details, Fill, Image, TerminalColors};
|
|
use galaxy_core::ui::{appearance::Appearance, theme::GalaxyTheme};
|
|
use galaxyui::assets::asset_cache::AssetSource;
|
|
use galaxyui::platform;
|
|
use galaxyui::{
|
|
elements::{
|
|
Container, CrossAxisAlignment, Flex, MainAxisAlignment, MainAxisSize, ParentElement,
|
|
},
|
|
fonts::{Cache, FamilyId, Weight},
|
|
presenter::ChildView,
|
|
ui_components::components::{UiComponent as _, UiComponentStyles},
|
|
AddWindowOptions, AppContext, AssetProvider, Element, Entity, SingletonEntity as _,
|
|
TypedActionView, View, ViewContext, ViewHandle,
|
|
};
|
|
use onboarding::slides::OnboardingModelInfo;
|
|
use onboarding::{
|
|
AgentOnboardingEvent, AgentOnboardingView, MockTelemetryContextProvider, SelectedSettings,
|
|
};
|
|
use pathfinder_color::ColorU;
|
|
use rust_embed::RustEmbed;
|
|
use std::borrow::Cow;
|
|
|
|
#[derive(Clone, Copy, RustEmbed)]
|
|
#[folder = "../../app/assets"]
|
|
pub struct Assets;
|
|
|
|
pub static ASSETS: Assets = Assets;
|
|
|
|
impl AssetProvider for Assets {
|
|
fn get(&self, path: &str) -> Result<Cow<'_, [u8]>> {
|
|
<Assets as RustEmbed>::get(path)
|
|
.map(|f| f.data)
|
|
.ok_or_else(|| anyhow::anyhow!(format!("no asset exists at path {path}")))
|
|
}
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
// Initialize logging for the onboarding binary.
|
|
galaxy_logging::init(galaxy_logging::LogConfig {
|
|
is_cli: false,
|
|
log_destination: None,
|
|
})?;
|
|
|
|
let app_builder =
|
|
platform::AppBuilder::new(platform::AppCallbacks::default(), Box::new(ASSETS), None);
|
|
let _ = app_builder.run(move |ctx| {
|
|
// Register Appearance singleton so views can access Appearance::handle(ctx).
|
|
ctx.add_singleton_model(|ctx| build_appearance(phenomenon(), ctx));
|
|
|
|
// Register telemetry context provider for logging telemetry events.
|
|
ctx.add_singleton_model(MockTelemetryContextProvider::new_context_provider);
|
|
|
|
ctx.add_window(AddWindowOptions::default(), |ctx| {
|
|
OnboardingMainView::new(ctx)
|
|
});
|
|
|
|
onboarding::init(ctx);
|
|
});
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
enum OnboardingMainState {
|
|
Onboarding(ViewHandle<AgentOnboardingView>),
|
|
Finished(ViewHandle<FinishedOnboardingView>),
|
|
}
|
|
|
|
struct OnboardingMainView {
|
|
state: OnboardingMainState,
|
|
}
|
|
|
|
impl OnboardingMainView {
|
|
fn new(ctx: &mut ViewContext<Self>) -> Self {
|
|
let themes = [phenomenon(), dark_theme(), light_theme(), adeberry()];
|
|
let default_model_id = LLMId::from("auto");
|
|
let models = vec![
|
|
OnboardingModelInfo {
|
|
id: LLMId::from("auto"),
|
|
title: "Auto".to_string(),
|
|
icon: Icon::Oz,
|
|
requires_upgrade: false,
|
|
is_default: true,
|
|
},
|
|
OnboardingModelInfo {
|
|
id: LLMId::from("claude-sonnet"),
|
|
title: "Claude Sonnet".to_string(),
|
|
icon: Icon::ClaudeLogo,
|
|
requires_upgrade: false,
|
|
is_default: false,
|
|
},
|
|
OnboardingModelInfo {
|
|
id: LLMId::from("gpt-4o"),
|
|
title: "GPT-4o".to_string(),
|
|
icon: Icon::OpenAILogo,
|
|
requires_upgrade: true,
|
|
is_default: false,
|
|
},
|
|
];
|
|
let onboarding_view = ctx.add_typed_action_view(move |ctx| {
|
|
// agent_modality_enabled and no_ai_experiment are false for demo purposes
|
|
AgentOnboardingView::new(
|
|
themes.clone(),
|
|
true,
|
|
models.clone(),
|
|
default_model_id.clone(),
|
|
false,
|
|
false,
|
|
false,
|
|
None,
|
|
onboarding::OnboardingAuthState::LoggedOut,
|
|
ctx,
|
|
)
|
|
});
|
|
onboarding_view.update(ctx, |view, ctx| {
|
|
view.start_onboarding(ctx);
|
|
});
|
|
ctx.subscribe_to_view(&onboarding_view, |me, _view, event, ctx| {
|
|
me.handle_onboarding_event(event, ctx);
|
|
});
|
|
|
|
Self {
|
|
state: OnboardingMainState::Onboarding(onboarding_view),
|
|
}
|
|
}
|
|
|
|
fn handle_onboarding_event(
|
|
&mut self,
|
|
event: &AgentOnboardingEvent,
|
|
ctx: &mut ViewContext<Self>,
|
|
) {
|
|
match event {
|
|
AgentOnboardingEvent::ThemeSelected { theme_name } => {
|
|
let theme = match theme_name.as_str() {
|
|
"Phenomenon" => phenomenon(),
|
|
"Dark" => dark_theme(),
|
|
"Light" => light_theme(),
|
|
"Adeberry" => adeberry(),
|
|
_ => return,
|
|
};
|
|
|
|
Appearance::handle(ctx).update(ctx, |appearance, ctx| {
|
|
appearance.set_theme(theme, ctx);
|
|
});
|
|
}
|
|
AgentOnboardingEvent::OnboardingCompleted(selected_settings) => {
|
|
let finished_view = ctx.add_typed_action_view(|_| {
|
|
FinishedOnboardingView::new(Some(selected_settings.clone()))
|
|
});
|
|
self.state = OnboardingMainState::Finished(finished_view);
|
|
ctx.notify();
|
|
}
|
|
AgentOnboardingEvent::OnboardingSkipped => {
|
|
let finished_view =
|
|
ctx.add_typed_action_view(|_| FinishedOnboardingView::new(None));
|
|
self.state = OnboardingMainState::Finished(finished_view);
|
|
ctx.notify();
|
|
}
|
|
AgentOnboardingEvent::SyncWithOsToggled { .. }
|
|
| AgentOnboardingEvent::UpgradeRequested
|
|
| AgentOnboardingEvent::UpgradeCopyUrlRequested
|
|
| AgentOnboardingEvent::UpgradePasteTokenFromClipboardRequested
|
|
| AgentOnboardingEvent::LoginFromWelcomeRequested
|
|
| AgentOnboardingEvent::PrivacySettingsFromTerminalThemeSlideRequested
|
|
| AgentOnboardingEvent::AppBecameActive => {
|
|
// No-op in the standalone demo binary
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FinishedOnboardingView {
|
|
selected_settings: Option<SelectedSettings>,
|
|
}
|
|
|
|
impl FinishedOnboardingView {
|
|
fn new(selected_settings: Option<SelectedSettings>) -> Self {
|
|
Self { selected_settings }
|
|
}
|
|
}
|
|
|
|
impl Entity for FinishedOnboardingView {
|
|
type Event = ();
|
|
}
|
|
|
|
impl View for FinishedOnboardingView {
|
|
fn ui_name() -> &'static str {
|
|
"FinishedOnboardingView"
|
|
}
|
|
|
|
fn render(&self, app: &AppContext) -> Box<dyn Element> {
|
|
let appearance = Appearance::as_ref(app);
|
|
|
|
let header_text = if self.selected_settings.is_some() {
|
|
"Finished Onboarding"
|
|
} else {
|
|
"Skipped Onboarding"
|
|
};
|
|
|
|
let header = appearance
|
|
.ui_builder()
|
|
.paragraph(header_text)
|
|
.with_style(UiComponentStyles {
|
|
font_size: Some(28.),
|
|
font_weight: Some(Weight::Medium),
|
|
..Default::default()
|
|
})
|
|
.build()
|
|
.finish();
|
|
|
|
let details_text = match &self.selected_settings {
|
|
Some(selected_settings) => format!("SelectedSettings: {selected_settings:?}"),
|
|
None => "SelectedSettings: (none)".to_string(),
|
|
};
|
|
|
|
let details = appearance
|
|
.ui_builder()
|
|
.paragraph(details_text)
|
|
.with_style(UiComponentStyles {
|
|
font_size: Some(14.),
|
|
font_weight: Some(Weight::Normal),
|
|
..Default::default()
|
|
})
|
|
.build()
|
|
.finish();
|
|
|
|
let theme = appearance.theme();
|
|
|
|
Container::new(
|
|
Flex::column()
|
|
.with_main_axis_size(MainAxisSize::Max)
|
|
.with_main_axis_alignment(MainAxisAlignment::Center)
|
|
.with_cross_axis_alignment(CrossAxisAlignment::Center)
|
|
.with_child(header)
|
|
.with_child(Container::new(details).with_margin_top(12.).finish())
|
|
.finish(),
|
|
)
|
|
.with_background(theme.background())
|
|
.with_uniform_padding(64.)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
impl TypedActionView for FinishedOnboardingView {
|
|
type Action = ();
|
|
}
|
|
|
|
impl Entity for OnboardingMainView {
|
|
type Event = ();
|
|
}
|
|
|
|
impl View for OnboardingMainView {
|
|
fn ui_name() -> &'static str {
|
|
"OnboardingMainView"
|
|
}
|
|
|
|
fn render(&self, _app: &AppContext) -> Box<dyn Element> {
|
|
match &self.state {
|
|
OnboardingMainState::Onboarding(view) => ChildView::new(view).finish(),
|
|
OnboardingMainState::Finished(view) => ChildView::new(view).finish(),
|
|
}
|
|
}
|
|
|
|
fn on_focus(&mut self, focus_ctx: &galaxyui::FocusContext, ctx: &mut ViewContext<Self>) {
|
|
if let OnboardingMainState::Onboarding(view) = &self.state {
|
|
if focus_ctx.is_self_focused() {
|
|
ctx.focus(view);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TypedActionView for OnboardingMainView {
|
|
type Action = ();
|
|
}
|
|
|
|
// ---- Theme definitions copied from app::themes::default_themes (subset) ----
|
|
|
|
const DARK_MODE_NORMAL_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x616161FF),
|
|
AnsiColor::from_u32(0xFF8272FF),
|
|
AnsiColor::from_u32(0xB4FA72FF),
|
|
AnsiColor::from_u32(0xFEFDC2FF),
|
|
AnsiColor::from_u32(0xA5D5FEFF),
|
|
AnsiColor::from_u32(0xFF8FFDFF),
|
|
AnsiColor::from_u32(0xD0D1FEFF),
|
|
AnsiColor::from_u32(0xF1F1F1FF),
|
|
);
|
|
|
|
const DARK_MODE_BRIGHT_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x8E8E8EFF),
|
|
AnsiColor::from_u32(0xFFC4BDFF),
|
|
AnsiColor::from_u32(0xD6FCB9FF),
|
|
AnsiColor::from_u32(0xFEFDD5FF),
|
|
AnsiColor::from_u32(0xC1E3FEFF),
|
|
AnsiColor::from_u32(0xFFB1FEFF),
|
|
AnsiColor::from_u32(0xE5E6FEFF),
|
|
AnsiColor::from_u32(0xFEFFFFFF),
|
|
);
|
|
|
|
const LIGHT_MODE_NORMAL_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x212121FF),
|
|
AnsiColor::from_u32(0xC30771FF),
|
|
AnsiColor::from_u32(0x10A778FF),
|
|
AnsiColor::from_u32(0xA89C14FF),
|
|
AnsiColor::from_u32(0x008EC4FF),
|
|
AnsiColor::from_u32(0x523C79FF),
|
|
AnsiColor::from_u32(0x20A5BAFF),
|
|
AnsiColor::from_u32(0xE0E0E0FF),
|
|
);
|
|
|
|
const LIGHT_MODE_BRIGHT_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x212121FF),
|
|
AnsiColor::from_u32(0xFB007AFF),
|
|
AnsiColor::from_u32(0x5FD7AFFF),
|
|
AnsiColor::from_u32(0xF3E430FF),
|
|
AnsiColor::from_u32(0x20BBFCFF),
|
|
AnsiColor::from_u32(0x6855DEFF),
|
|
AnsiColor::from_u32(0x4FB8CCFF),
|
|
AnsiColor::from_u32(0xF1F1F1FF),
|
|
);
|
|
|
|
const PHENOMENON_NORMAL_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x121212FF),
|
|
AnsiColor::from_u32(0xD22D1EFF),
|
|
AnsiColor::from_u32(0x1CA05AFF),
|
|
AnsiColor::from_u32(0xE5A01AFF),
|
|
AnsiColor::from_u32(0x3780E9FF),
|
|
AnsiColor::from_u32(0xBF409DFF),
|
|
AnsiColor::from_u32(0x799C92FF),
|
|
AnsiColor::from_u32(0xFAF9F6FF),
|
|
);
|
|
|
|
const PHENOMENON_BRIGHT_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x292929FF),
|
|
AnsiColor::from_u32(0xAE756FFF),
|
|
AnsiColor::from_u32(0x789B88FF),
|
|
AnsiColor::from_u32(0xBD9F65FF),
|
|
AnsiColor::from_u32(0x6F839FFF),
|
|
AnsiColor::from_u32(0xA57899FF),
|
|
AnsiColor::from_u32(0xBFC5C3FF),
|
|
AnsiColor::from_u32(0xFFFFFFFF),
|
|
);
|
|
|
|
const ADEBERRY_NORMAL_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x121212FF),
|
|
AnsiColor::from_u32(0xC76156FF),
|
|
AnsiColor::from_u32(0x57C78AFF),
|
|
AnsiColor::from_u32(0xC8A35AFF),
|
|
AnsiColor::from_u32(0x5785C7FF),
|
|
AnsiColor::from_u32(0xC756A9FF),
|
|
AnsiColor::from_u32(0x57C7C3FF),
|
|
AnsiColor::from_u32(0xEEEDEBFF),
|
|
);
|
|
|
|
const ADEBERRY_BRIGHT_COLORS: AnsiColors = AnsiColors::new(
|
|
AnsiColor::from_u32(0x292929FF),
|
|
AnsiColor::from_u32(0xE3493BFF),
|
|
AnsiColor::from_u32(0x1CA05AFF),
|
|
AnsiColor::from_u32(0xE3AA3BFF),
|
|
AnsiColor::from_u32(0x3BE38AFF),
|
|
AnsiColor::from_u32(0xC8A35AFF),
|
|
AnsiColor::from_u32(0x3BE3DDFF),
|
|
AnsiColor::from_u32(0xFFFFFFFF),
|
|
);
|
|
|
|
fn dark_mode_colors() -> TerminalColors {
|
|
TerminalColors::new(DARK_MODE_NORMAL_COLORS, DARK_MODE_BRIGHT_COLORS)
|
|
}
|
|
|
|
fn light_mode_colors() -> TerminalColors {
|
|
TerminalColors::new(LIGHT_MODE_NORMAL_COLORS, LIGHT_MODE_BRIGHT_COLORS)
|
|
}
|
|
|
|
fn phenomenon_colors() -> TerminalColors {
|
|
TerminalColors::new(PHENOMENON_NORMAL_COLORS, PHENOMENON_BRIGHT_COLORS)
|
|
}
|
|
|
|
fn adeberry_colors() -> TerminalColors {
|
|
TerminalColors::new(ADEBERRY_NORMAL_COLORS, ADEBERRY_BRIGHT_COLORS)
|
|
}
|
|
|
|
fn dark_theme() -> GalaxyTheme {
|
|
GalaxyTheme::new(
|
|
Fill::Solid(ColorU::from_u32(0x000000FF)),
|
|
ColorU::from_u32(0xffffffff),
|
|
Fill::Solid(ColorU::from_u32(0x19AAD8FF)),
|
|
None,
|
|
Some(Details::Darker),
|
|
dark_mode_colors(),
|
|
None,
|
|
Some("Dark".to_string()),
|
|
)
|
|
}
|
|
|
|
fn light_theme() -> GalaxyTheme {
|
|
GalaxyTheme::new(
|
|
Fill::Solid(ColorU::white()),
|
|
ColorU::new(17, 17, 17, 0xFF),
|
|
Fill::Solid(ColorU::from_u32(0x00c2ffff)),
|
|
None,
|
|
Some(Details::Lighter),
|
|
light_mode_colors(),
|
|
None,
|
|
Some("Light".to_string()),
|
|
)
|
|
}
|
|
|
|
fn phenomenon() -> GalaxyTheme {
|
|
GalaxyTheme::new(
|
|
Fill::Solid(ColorU::from_u32(0x121212FF)),
|
|
ColorU::from_u32(0xFAF9F6FF),
|
|
Fill::Solid(ColorU::from_u32(0x2E5D9EFF)),
|
|
None,
|
|
Some(Details::Darker),
|
|
phenomenon_colors(),
|
|
Some(Image {
|
|
source: AssetSource::Bundled {
|
|
// Match app's asset layout: this image lives under app/assets/async.
|
|
path: "async/jpg/phenomenon_bg.jpg",
|
|
},
|
|
opacity: 100,
|
|
}),
|
|
Some("Phenomenon".to_string()),
|
|
)
|
|
}
|
|
|
|
fn adeberry() -> GalaxyTheme {
|
|
GalaxyTheme::new(
|
|
Fill::Solid(ColorU::from_u32(0x1D2022FF)),
|
|
ColorU::from_u32(0xE4EEF5FF),
|
|
Fill::Solid(ColorU::from_u32(0x6C96B4FF)),
|
|
None,
|
|
Some(Details::Darker),
|
|
adeberry_colors(),
|
|
None,
|
|
Some("Adeberry".to_string()),
|
|
)
|
|
}
|
|
|
|
fn build_appearance(theme: GalaxyTheme, ctx: &mut AppContext) -> Appearance {
|
|
let ui_font_family =
|
|
load_default_ui_font_family(ctx).expect("unable to load default ui font family");
|
|
|
|
Appearance::new(
|
|
theme,
|
|
ui_font_family,
|
|
13.0,
|
|
Weight::Normal,
|
|
ui_font_family,
|
|
1.2,
|
|
ui_font_family,
|
|
ui_font_family,
|
|
)
|
|
}
|
|
|
|
fn load_default_ui_font_family(ctx: &mut AppContext) -> anyhow::Result<FamilyId> {
|
|
Cache::handle(ctx).update(ctx, |font_cache, _| {
|
|
// On Windows, default to use Segoe UI as the UI font.
|
|
#[cfg(windows)]
|
|
if let Ok(font_family_id) = font_cache.load_system_font("Segoe UI") {
|
|
return Ok(font_family_id);
|
|
}
|
|
|
|
font_cache.load_family_from_bytes(
|
|
"Roboto",
|
|
vec![
|
|
ASSETS
|
|
.get("bundled/fonts/roboto/Roboto-Italic.ttf")?
|
|
.to_vec(),
|
|
ASSETS.get("bundled/fonts/roboto/Roboto-Bold.ttf")?.to_vec(),
|
|
ASSETS
|
|
.get("bundled/fonts/roboto/Roboto-Regular.ttf")?
|
|
.to_vec(),
|
|
ASSETS
|
|
.get("bundled/fonts/roboto/Roboto-Medium.ttf")?
|
|
.to_vec(),
|
|
ASSETS
|
|
.get("bundled/fonts/roboto/RobotoFlex-Semibold.ttf")?
|
|
.to_vec(),
|
|
ASSETS
|
|
.get("bundled/fonts/roboto/Roboto-BoldItalic.ttf")?
|
|
.to_vec(),
|
|
],
|
|
)
|
|
})
|
|
}
|