Files
galaxy/crates/onboarding/src/bin/main.rs
T
Ryan WardandClaude Opus 4.6 6f54e2cb30 v1.4.0: Auto-compact streaming, Bedrock summarization support, subagent orchestration, and Galaxy rebrand continuation
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>
2026-05-21 11:59:37 -05:00

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(),
],
)
})
}