Significant progress. Performing cleanup now
This commit is contained in:
@@ -72,7 +72,8 @@ impl SettingsWidget for AboutPageWidget {
|
||||
"bundled/svg/bedrock.svg"
|
||||
};
|
||||
|
||||
let version = ChannelState::app_version().unwrap_or("v#.##.###");
|
||||
let version = ChannelState::app_version()
|
||||
.unwrap_or(concat!("v", env!("CARGO_PKG_VERSION")));
|
||||
|
||||
let version_text = ui_builder
|
||||
.span(version.to_string())
|
||||
|
||||
@@ -28,9 +28,9 @@ use crate::settings::{
|
||||
CanUseWarpCreditsWithByok, CodeSettings, CodebaseContextEnabled, FileBasedMcpEnabled,
|
||||
GitOperationsAutogenEnabled, IncludeAgentCommandsInHistory, IntelligentAutosuggestionsEnabled,
|
||||
MemoryEnabled, NLDInTerminalEnabled, NaturalLanguageAutosuggestionsEnabled,
|
||||
OrchestrationEnabled, RuleSuggestionsEnabled, SharedBlockTitleGenerationEnabled,
|
||||
RuleSuggestionsEnabled, SharedBlockTitleGenerationEnabled,
|
||||
ShouldRenderCLIAgentToolbar, ShouldRenderUseAgentToolbarForUserCommands,
|
||||
ShouldShowOzUpdatesInZeroState, ShowAgentTips, ShowConversationHistory, ShowHintText,
|
||||
ShowAgentTips, ShowConversationHistory, ShowHintText,
|
||||
ThinkingDisplayMode, VoiceInputEnabled, WarpDriveContextEnabled,
|
||||
};
|
||||
use crate::terminal::session_settings::{SessionSettings, SessionSettingsChangedEvent};
|
||||
@@ -269,29 +269,6 @@ pub fn init_actions_from_parent_view<T: Action + Clone>(
|
||||
.with_enabled(|| FeatureFlag::AgentTips.is_enabled())],
|
||||
app,
|
||||
);
|
||||
ToggleSettingActionPair::add_toggle_setting_action_pairs_as_bindings(
|
||||
vec![ToggleSettingActionPair::custom(
|
||||
SettingActionPairDescriptions::new(
|
||||
"Show Oz changelog in new agent conversation view",
|
||||
"Hide Oz changelog in new agent conversation view",
|
||||
),
|
||||
builder(SettingsAction::AI(
|
||||
AISettingsPageAction::ToggleShowOzUpdatesInZeroState,
|
||||
)),
|
||||
SettingActionPairContexts::new(
|
||||
context.clone()
|
||||
& id!(flags::IS_ANY_AI_ENABLED)
|
||||
& !id!(flags::SHOW_OZ_UPDATES_IN_ZERO_STATE_FLAG),
|
||||
context.clone()
|
||||
& id!(flags::IS_ANY_AI_ENABLED)
|
||||
& id!(flags::SHOW_OZ_UPDATES_IN_ZERO_STATE_FLAG),
|
||||
),
|
||||
None,
|
||||
)
|
||||
.with_group(bindings::BindingGroup::WarpAi)
|
||||
.with_enabled(|| FeatureFlag::AgentView.is_enabled())],
|
||||
app,
|
||||
);
|
||||
{
|
||||
use crate::settings::ThinkingDisplayMode;
|
||||
use galaxyui::keymap::FixedBinding;
|
||||
@@ -1468,12 +1445,8 @@ impl AISettingsPageView {
|
||||
widgets.push(Box::new(VoiceWidget::default()));
|
||||
}
|
||||
widgets.push(Box::new(CLIAgentWidget::default()));
|
||||
widgets.push(Box::new(ApiKeysWidget::new(ctx)));
|
||||
widgets.push(Box::new(AgentAttributionWidget::default()));
|
||||
widgets.push(Box::new(OtherAIWidget::default()));
|
||||
if FeatureFlag::AgentModeComputerUse.is_enabled() {
|
||||
widgets.push(Box::new(CloudAgentComputerUseWidget::default()));
|
||||
}
|
||||
}
|
||||
Some(AISubpage::WarpAgent) => {
|
||||
// Oz page: global toggle + Active AI + Input + Other
|
||||
@@ -1507,12 +1480,8 @@ impl AISettingsPageView {
|
||||
if voice_supported {
|
||||
widgets.push(Box::new(VoiceWidget::default()));
|
||||
}
|
||||
widgets.push(Box::new(ApiKeysWidget::new(ctx)));
|
||||
widgets.push(Box::new(AgentAttributionWidget::default()));
|
||||
widgets.push(Box::new(OtherAIWidget::default()));
|
||||
if FeatureFlag::AgentModeComputerUse.is_enabled() {
|
||||
widgets.push(Box::new(CloudAgentComputerUseWidget::default()));
|
||||
}
|
||||
}
|
||||
Some(AISubpage::Profiles) => {
|
||||
if !FeatureFlag::UsageBasedPricing.is_enabled() {
|
||||
@@ -2076,7 +2045,6 @@ pub enum AISettingsPageAction {
|
||||
ToggleCodebaseContext,
|
||||
ToggleShowInputHintText,
|
||||
ToggleShowAgentTips,
|
||||
ToggleShowOzUpdatesInZeroState,
|
||||
SetThinkingDisplayMode(ThinkingDisplayMode),
|
||||
AttemptLoginGatedUpgrade,
|
||||
RemoveCLIAgentToolbarEnabledCommand(String),
|
||||
@@ -2116,13 +2084,11 @@ pub enum AISettingsPageAction {
|
||||
SetBedrockProfile(String),
|
||||
ToggleBedrockCrossRegionInference,
|
||||
ToggleBedrockFallbackToWarp,
|
||||
ToggleCloudAgentComputerUse,
|
||||
ToggleFileBasedMcp,
|
||||
ToggleIncludeAgentCommandsInHistory,
|
||||
ToggleAgentAttribution,
|
||||
#[cfg(feature = "local_fs")]
|
||||
SetConversationLayout(crate::util::file::external_editor::settings::OpenConversationPreference),
|
||||
ToggleOrchestration,
|
||||
ToggleShowConversationHistory,
|
||||
ToggleAutoToggleRichInput,
|
||||
ToggleAutoOpenRichInputOnCLIAgentStart,
|
||||
@@ -2504,14 +2470,6 @@ impl TypedActionView for AISettingsPageView {
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
AISettingsPageAction::ToggleShowOzUpdatesInZeroState => {
|
||||
AISettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
report_if_error!(settings
|
||||
.should_show_oz_updates_in_zero_state
|
||||
.toggle_and_save_value(ctx));
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
AISettingsPageAction::SetThinkingDisplayMode(mode) => {
|
||||
AISettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
report_if_error!(settings.thinking_display_mode.set_value(*mode, ctx));
|
||||
@@ -2831,14 +2789,6 @@ impl TypedActionView for AISettingsPageView {
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
AISettingsPageAction::ToggleCloudAgentComputerUse => {
|
||||
AISettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
report_if_error!(settings
|
||||
.cloud_agent_computer_use_enabled
|
||||
.toggle_and_save_value(ctx));
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
AISettingsPageAction::ToggleFileBasedMcp => {
|
||||
AISettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
report_if_error!(settings.file_based_mcp_enabled.toggle_and_save_value(ctx));
|
||||
@@ -2872,12 +2822,6 @@ impl TypedActionView for AISettingsPageView {
|
||||
);
|
||||
ctx.notify();
|
||||
}
|
||||
AISettingsPageAction::ToggleOrchestration => {
|
||||
AISettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
report_if_error!(settings.orchestration_enabled.toggle_and_save_value(ctx));
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
AISettingsPageAction::ToggleShowConversationHistory => {
|
||||
AISettings::handle(ctx).update(ctx, |settings, ctx| {
|
||||
report_if_error!(settings
|
||||
@@ -5345,7 +5289,6 @@ impl SettingsWidget for VoiceWidget {
|
||||
}
|
||||
#[derive(Default)]
|
||||
struct OtherAIWidget {
|
||||
show_oz_updates_in_zero_state_toggle: SwitchStateHandle,
|
||||
use_agent_footer_toggle: SwitchStateHandle,
|
||||
show_conversation_history_toggle: SwitchStateHandle,
|
||||
}
|
||||
@@ -5405,15 +5348,6 @@ impl SettingsWidget for OtherAIWidget {
|
||||
|
||||
if FeatureFlag::AgentView.is_enabled() {
|
||||
let mut agent_view_column = Flex::column()
|
||||
.with_child(render_ai_setting_toggle::<ShouldShowOzUpdatesInZeroState>(
|
||||
"Show Oz changelog in new conversation view",
|
||||
AISettingsPageAction::ToggleShowOzUpdatesInZeroState,
|
||||
*ai_settings.should_show_oz_updates_in_zero_state,
|
||||
is_toggleable,
|
||||
self.show_oz_updates_in_zero_state_toggle.clone(),
|
||||
&view.local_only_icon_tooltip_states,
|
||||
app,
|
||||
))
|
||||
.with_child(render_ai_setting_toggle::<ShouldRenderUseAgentToolbarForUserCommands>(
|
||||
"Show \"Use Agent\" footer",
|
||||
AISettingsPageAction::ToggleUseAgentToolbar,
|
||||
@@ -5902,462 +5836,6 @@ impl SettingsWidget for AgentAttributionWidget {
|
||||
#[path = "ai_page_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
#[derive(Default)]
|
||||
struct CloudAgentComputerUseWidget {
|
||||
toggle: SwitchStateHandle,
|
||||
orchestration_toggle: SwitchStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for CloudAgentComputerUseWidget {
|
||||
type View = AISettingsPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"oz cloud agent computer use orchestration multi-agent"
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
use crate::ai::execution_profiles::{CloudAgentComputerUseState, ComputerUsePermission};
|
||||
|
||||
let is_any_ai_enabled = AISettings::as_ref(app).is_any_ai_enabled(app);
|
||||
|
||||
// Determine toggle state based on workspace autonomy setting and user preference
|
||||
let CloudAgentComputerUseState {
|
||||
enabled: is_checked,
|
||||
is_forced_by_org,
|
||||
} = ComputerUsePermission::resolve_cloud_agent_state(app);
|
||||
|
||||
// Toggle is disabled if forced by org settings OR if AI is globally disabled
|
||||
let is_disabled = is_forced_by_org || !is_any_ai_enabled;
|
||||
|
||||
let ui_builder = appearance.ui_builder();
|
||||
let toggle = if is_forced_by_org {
|
||||
// Disabled by organization setting - show tooltip on hover
|
||||
ui_builder
|
||||
.switch(self.toggle.clone())
|
||||
.check(is_checked)
|
||||
.with_tooltip(TooltipConfig {
|
||||
text: "This option is enforced by your organization's settings and cannot be customized.".to_string(),
|
||||
styles: ui_builder.default_tool_tip_styles(),
|
||||
})
|
||||
.disable()
|
||||
.build()
|
||||
.finish()
|
||||
} else if !is_any_ai_enabled {
|
||||
// Disabled because AI is off globally - no tooltip needed
|
||||
ui_builder
|
||||
.switch(self.toggle.clone())
|
||||
.check(is_checked)
|
||||
.with_disabled(true)
|
||||
.build()
|
||||
.finish()
|
||||
} else {
|
||||
// Enabled - allow toggling
|
||||
ui_builder
|
||||
.switch(self.toggle.clone())
|
||||
.check(is_checked)
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(AISettingsPageAction::ToggleCloudAgentComputerUse);
|
||||
})
|
||||
.finish()
|
||||
};
|
||||
|
||||
let toggle_row = build_toggle_element(
|
||||
render_body_item_label::<AISettingsPageAction>(
|
||||
"Computer use in Cloud Agents".to_string(),
|
||||
Some(styles::header_font_color(!is_disabled, app)),
|
||||
None,
|
||||
LocalOnlyIconState::Hidden,
|
||||
ToggleState::Enabled,
|
||||
appearance,
|
||||
),
|
||||
toggle,
|
||||
appearance,
|
||||
None,
|
||||
);
|
||||
|
||||
let mut column = Flex::column()
|
||||
.with_child(render_separator(appearance))
|
||||
.with_child(
|
||||
build_sub_header(
|
||||
appearance,
|
||||
"Experimental",
|
||||
Some(styles::header_font_color(is_any_ai_enabled, app)),
|
||||
)
|
||||
.with_padding_bottom(HEADER_PADDING)
|
||||
.finish(),
|
||||
)
|
||||
.with_child(toggle_row)
|
||||
.with_child(render_ai_setting_description(
|
||||
"Enable computer use in cloud agent conversations started from the Warp app.",
|
||||
!is_disabled,
|
||||
app,
|
||||
));
|
||||
|
||||
if FeatureFlag::Orchestration.is_enabled() {
|
||||
let ai_settings = AISettings::as_ref(app);
|
||||
column.add_child(render_ai_setting_toggle::<OrchestrationEnabled>(
|
||||
"Orchestration",
|
||||
AISettingsPageAction::ToggleOrchestration,
|
||||
*ai_settings.orchestration_enabled,
|
||||
is_any_ai_enabled,
|
||||
self.orchestration_toggle.clone(),
|
||||
&view.local_only_icon_tooltip_states,
|
||||
app,
|
||||
));
|
||||
column.add_child(render_ai_setting_description(
|
||||
"Enable multi-agent orchestration, allowing the agent to spawn and coordinate parallel sub-agents.",
|
||||
is_any_ai_enabled,
|
||||
app,
|
||||
));
|
||||
}
|
||||
|
||||
column.finish()
|
||||
}
|
||||
}
|
||||
|
||||
struct ApiKeysWidget {
|
||||
openai_api_key_editor: ViewHandle<EditorView>,
|
||||
anthropic_api_key_editor: ViewHandle<EditorView>,
|
||||
google_api_key_editor: ViewHandle<EditorView>,
|
||||
|
||||
can_use_warp_credits_with_byok: SwitchStateHandle,
|
||||
upgrade_highlight_index: HighlightedHyperlink,
|
||||
}
|
||||
|
||||
impl ApiKeysWidget {
|
||||
fn new(ctx: &mut ViewContext<<Self as SettingsWidget>::View>) -> Self {
|
||||
let ai_settings = AISettings::as_ref(ctx);
|
||||
let workspace_handle = UserWorkspaces::handle(ctx);
|
||||
let is_any_ai_enabled = ai_settings.is_any_ai_enabled(ctx);
|
||||
let is_byo_enabled = workspace_handle.as_ref(ctx).is_byo_api_key_enabled();
|
||||
|
||||
let ApiKeys {
|
||||
openai: openai_key,
|
||||
anthropic: anthropic_key,
|
||||
google: google_key,
|
||||
..
|
||||
} = ApiKeyManager::as_ref(ctx).keys().clone();
|
||||
|
||||
// A helper macro to create and configure an API key editor. This avoids a lot
|
||||
// of code duplication and ensures consistency between the editors.
|
||||
macro_rules! create_api_key_editor {
|
||||
($editor:ident, $key:ident, $set_func:ident, $placeholder:literal) => {
|
||||
let $editor = ctx.add_typed_action_view(move |ctx| {
|
||||
let appearance = Appearance::handle(ctx).as_ref(ctx);
|
||||
let options = SingleLineEditorOptions {
|
||||
is_password: true,
|
||||
text: TextOptions {
|
||||
font_size_override: Some(appearance.ui_font_size()),
|
||||
font_family_override: Some(appearance.monospace_font_family()),
|
||||
text_colors_override: Some(TextColors {
|
||||
default_color: appearance.theme().active_ui_text_color(),
|
||||
disabled_color: appearance.theme().disabled_ui_text_color(),
|
||||
hint_color: appearance.theme().disabled_ui_text_color(),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
let mut editor = EditorView::single_line(options, ctx);
|
||||
editor.set_placeholder_text($placeholder, ctx);
|
||||
if let Some(key) = &$key {
|
||||
editor.set_buffer_text(key, ctx);
|
||||
}
|
||||
editor
|
||||
});
|
||||
AISettingsPageView::update_editor_interaction_state(
|
||||
$editor.clone(),
|
||||
is_any_ai_enabled && is_byo_enabled,
|
||||
ctx,
|
||||
);
|
||||
ctx.subscribe_to_view(&$editor, |_, $editor, event, ctx| {
|
||||
if matches!(event, EditorEvent::Blurred | EditorEvent::Enter) {
|
||||
let buffer_text = $editor.as_ref(ctx).buffer_text(ctx);
|
||||
let key = buffer_text.is_empty().not().then_some(buffer_text);
|
||||
ApiKeyManager::handle(ctx).update(ctx, |model, ctx| {
|
||||
model.$set_func(key, ctx);
|
||||
});
|
||||
}
|
||||
});
|
||||
let editor_clone = $editor.clone();
|
||||
ctx.subscribe_to_model(&workspace_handle, move |_, workspace, event, ctx| {
|
||||
if let UserWorkspacesEvent::TeamsChanged = event {
|
||||
let is_any_ai_enabled =
|
||||
AISettings::handle(ctx).as_ref(ctx).is_any_ai_enabled(ctx);
|
||||
let is_byo_enabled = workspace.as_ref(ctx).is_byo_api_key_enabled();
|
||||
let is_enabled = is_any_ai_enabled && is_byo_enabled;
|
||||
let has_key = !editor_clone.as_ref(ctx).is_empty(ctx);
|
||||
|
||||
// If BYO is disabled, clear the API key from the editor and storage
|
||||
if !is_byo_enabled && has_key {
|
||||
editor_clone.update(ctx, |editor, ctx| {
|
||||
editor.set_buffer_text("", ctx);
|
||||
});
|
||||
ApiKeyManager::handle(ctx).update(ctx, |model, ctx| {
|
||||
model.$set_func(None, ctx);
|
||||
});
|
||||
}
|
||||
|
||||
AISettingsPageView::update_editor_interaction_state(
|
||||
editor_clone.clone(),
|
||||
is_enabled,
|
||||
ctx,
|
||||
);
|
||||
ctx.notify();
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
create_api_key_editor!(openai_api_key_editor, openai_key, set_openai_key, "sk-...");
|
||||
create_api_key_editor!(
|
||||
anthropic_api_key_editor,
|
||||
anthropic_key,
|
||||
set_anthropic_key,
|
||||
"sk-ant-..."
|
||||
);
|
||||
create_api_key_editor!(
|
||||
google_api_key_editor,
|
||||
google_key,
|
||||
set_google_key,
|
||||
"AIzaSy..."
|
||||
);
|
||||
|
||||
Self {
|
||||
openai_api_key_editor,
|
||||
anthropic_api_key_editor,
|
||||
google_api_key_editor,
|
||||
|
||||
can_use_warp_credits_with_byok: Default::default(),
|
||||
upgrade_highlight_index: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_api_keys_section(
|
||||
&self,
|
||||
appearance: &Appearance,
|
||||
app: &AppContext,
|
||||
is_byo_enabled: bool,
|
||||
) -> Box<dyn Element> {
|
||||
let ai_settings = AISettings::as_ref(app);
|
||||
let is_any_ai_enabled = ai_settings.is_any_ai_enabled(app);
|
||||
let is_enabled = is_any_ai_enabled && is_byo_enabled;
|
||||
|
||||
let mut column = Flex::column()
|
||||
.with_spacing(16.)
|
||||
.with_child(
|
||||
Container::new(
|
||||
render_ai_setting_description(
|
||||
"Use your own API keys from model providers for the Warp Agent to use. API keys are stored locally and never synced to the cloud. Using auto models or models from providers you have not provided API keys for will consume Warp credits.",
|
||||
is_enabled,
|
||||
app,
|
||||
))
|
||||
// Remove the bottom margin of the description so that it doesn't
|
||||
// create extra space between the description and the API key inputs.
|
||||
.with_margin_bottom(-styles::DESCRIPTION_MARGIN_BOTTOM).finish()
|
||||
);
|
||||
|
||||
/// Helper function to render the UI for an API key input field.
|
||||
fn render_api_key_input(
|
||||
appearance: &Appearance,
|
||||
label: &'static str,
|
||||
editor: ViewHandle<EditorView>,
|
||||
is_enabled: bool,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let padding = Some(Coords {
|
||||
top: 10.,
|
||||
bottom: 10.,
|
||||
left: 16.,
|
||||
right: 16.,
|
||||
});
|
||||
let editor_style = UiComponentStyles {
|
||||
padding,
|
||||
background: Some(appearance.theme().surface_2().into()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let label = Text::new_inline(label, appearance.ui_font_family(), CONTENT_FONT_SIZE)
|
||||
.with_color(styles::header_font_color(is_enabled, app).into())
|
||||
.finish();
|
||||
|
||||
let input = appearance
|
||||
.ui_builder()
|
||||
.text_input(editor)
|
||||
.with_style(editor_style)
|
||||
.build()
|
||||
.finish();
|
||||
|
||||
Flex::column()
|
||||
.with_spacing(8.)
|
||||
.with_child(label)
|
||||
.with_child(input)
|
||||
.finish()
|
||||
}
|
||||
|
||||
column.add_child(render_api_key_input(
|
||||
appearance,
|
||||
"OpenAI API Key",
|
||||
self.openai_api_key_editor.clone(),
|
||||
is_enabled,
|
||||
app,
|
||||
));
|
||||
column.add_child(render_api_key_input(
|
||||
appearance,
|
||||
"Anthropic API Key",
|
||||
self.anthropic_api_key_editor.clone(),
|
||||
is_enabled,
|
||||
app,
|
||||
));
|
||||
column.add_child(render_api_key_input(
|
||||
appearance,
|
||||
"Google API Key",
|
||||
self.google_api_key_editor.clone(),
|
||||
is_enabled,
|
||||
app,
|
||||
));
|
||||
|
||||
// Show upgrade CTA if BYOK is not enabled
|
||||
if !is_byo_enabled {
|
||||
let auth_state = AuthStateProvider::as_ref(app).get();
|
||||
let upgrade_text_fragments = if let Some(team) =
|
||||
UserWorkspaces::as_ref(app).current_team()
|
||||
{
|
||||
// Enterprise teams don't have a self-serve upgrade path; route them
|
||||
// to sales to enable BYOK on their existing plan.
|
||||
if team.billing_metadata.customer_type == CustomerType::Enterprise {
|
||||
vec![
|
||||
FormattedTextFragment::hyperlink("Contact sales", "mailto:sales@warp.dev"),
|
||||
FormattedTextFragment::plain_text(
|
||||
" to enable bringing your own API keys on your Enterprise plan.",
|
||||
),
|
||||
]
|
||||
} else {
|
||||
let current_user_email = auth_state.user_email().unwrap_or_default();
|
||||
let has_admin_permissions = team.has_admin_permissions(¤t_user_email);
|
||||
let upgrade_url = UserWorkspaces::upgrade_link_for_team(team.uid);
|
||||
if has_admin_permissions {
|
||||
vec![
|
||||
FormattedTextFragment::hyperlink(
|
||||
"Upgrade to the Build plan",
|
||||
upgrade_url,
|
||||
),
|
||||
FormattedTextFragment::plain_text(" to use your own API keys."),
|
||||
]
|
||||
} else {
|
||||
vec![FormattedTextFragment::plain_text(
|
||||
"Ask your team's admin to upgrade to the Build plan to use your own API keys.",
|
||||
)]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let user_id = auth_state.user_id().unwrap_or_default();
|
||||
let upgrade_url = UserWorkspaces::upgrade_link(user_id);
|
||||
vec![
|
||||
FormattedTextFragment::hyperlink("Upgrade to the Build plan", upgrade_url),
|
||||
FormattedTextFragment::plain_text(" to use your own API keys."),
|
||||
]
|
||||
};
|
||||
|
||||
let upgrade_text_element = FormattedTextElement::new(
|
||||
FormattedText::new([FormattedTextLine::Line(upgrade_text_fragments)]),
|
||||
appearance.ui_font_size(),
|
||||
appearance.ui_font_family(),
|
||||
appearance.ui_font_family(),
|
||||
blended_colors::text_sub(appearance.theme(), appearance.theme().surface_1()),
|
||||
self.upgrade_highlight_index.clone(),
|
||||
)
|
||||
.with_hyperlink_font_color(appearance.theme().accent().into_solid())
|
||||
.register_default_click_handlers(|url, ctx, _| {
|
||||
ctx.dispatch_typed_action(AISettingsPageAction::HyperlinkClick(url));
|
||||
});
|
||||
|
||||
column.add_child(Container::new(upgrade_text_element.finish()).finish());
|
||||
}
|
||||
|
||||
column.finish()
|
||||
}
|
||||
|
||||
fn render_can_use_warp_credits_with_byok_toggle(
|
||||
&self,
|
||||
view: &AISettingsPageView,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ai_settings = AISettings::as_ref(app);
|
||||
|
||||
let toggle = render_ai_setting_toggle::<CanUseWarpCreditsWithByok>(
|
||||
"Warp credit fallback",
|
||||
AISettingsPageAction::ToggleCanUseWarpCreditsWithByok,
|
||||
*ai_settings.can_use_warp_credits_with_byok,
|
||||
ai_settings.is_any_ai_enabled(app),
|
||||
self.can_use_warp_credits_with_byok.clone(),
|
||||
&view.local_only_icon_tooltip_states,
|
||||
app,
|
||||
);
|
||||
|
||||
let description = render_ai_setting_description(
|
||||
"When enabled, agent requests may be routed to one of Warp's provided models in the event of an error. Warp will prioritize using your API keys over your Warp credits.",
|
||||
ai_settings.is_any_ai_enabled(app),
|
||||
app,
|
||||
);
|
||||
|
||||
Flex::column()
|
||||
.with_child(toggle)
|
||||
.with_child(description)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SettingsWidget for ApiKeysWidget {
|
||||
type View = AISettingsPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"api keys bring your own byo openai anthropic google claude gemini gpt"
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ai_settings = AISettings::as_ref(app);
|
||||
let is_any_ai_enabled = ai_settings.is_any_ai_enabled(app);
|
||||
let is_byo_enabled = UserWorkspaces::as_ref(app).is_byo_api_key_enabled();
|
||||
|
||||
let mut column = Flex::column()
|
||||
.with_child(render_separator(appearance))
|
||||
.with_child(
|
||||
build_sub_header(
|
||||
appearance,
|
||||
"API Keys",
|
||||
Some(styles::header_font_color(is_any_ai_enabled, app)),
|
||||
)
|
||||
.with_padding_bottom(HEADER_PADDING)
|
||||
.finish(),
|
||||
)
|
||||
.with_child(self.render_api_keys_section(appearance, app, is_byo_enabled));
|
||||
|
||||
if is_byo_enabled {
|
||||
column.add_child(
|
||||
Container::new(self.render_can_use_warp_credits_with_byok_toggle(view, app))
|
||||
.with_margin_top(16.)
|
||||
.finish(),
|
||||
);
|
||||
}
|
||||
|
||||
Container::new(column.finish())
|
||||
.with_margin_bottom(HEADER_PADDING)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
struct BedrockSettingsWidget {
|
||||
enabled_toggle: SwitchStateHandle,
|
||||
cross_region_toggle: SwitchStateHandle,
|
||||
|
||||
@@ -30,7 +30,6 @@ use appearance_page::{AppearancePageAction, AppearanceSettingsPageView};
|
||||
use billing_and_usage_page::{BillingAndUsagePageEvent, BillingAndUsagePageView};
|
||||
use code_page::CodeSubpage;
|
||||
use code_page::{CodeSettingsPageAction, CodeSettingsPageEvent};
|
||||
use environments_page::EnvironmentsPageView;
|
||||
use features_page::{FeaturesPageView, FeaturesSettingsPageEvent};
|
||||
use itertools::Itertools as _;
|
||||
use keybindings::KeybindingsView;
|
||||
@@ -39,17 +38,14 @@ use mcp_servers_page::MCPServersSettingsPageView;
|
||||
use nav::{SettingsNavItem, SettingsUmbrella};
|
||||
use pathfinder_geometry::vector::Vector2F;
|
||||
use privacy_page::{PrivacyPageView, PrivacyPageViewEvent};
|
||||
use referrals_page::{ReferralsPageEvent, ReferralsPageView};
|
||||
use settings_file_footer::{render_footer, SettingsFooterKind, SettingsFooterMouseStates};
|
||||
use settings_page::{
|
||||
MatchData, SettingsPage, SettingsPageEvent, SettingsPageMeta, SettingsPageViewHandle,
|
||||
HEADER_PADDING,
|
||||
};
|
||||
use show_blocks_view::{ShowBlocksEvent, ShowBlocksView};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use teams_page::{TeamsPageView, TeamsPageViewEvent};
|
||||
use galaxy_core::send_telemetry_from_ctx;
|
||||
use galaxy_core::{
|
||||
channel::ChannelState, context_flag::ContextFlag, features::FeatureFlag,
|
||||
@@ -97,10 +93,8 @@ mod platform;
|
||||
mod platform_page;
|
||||
mod privacy;
|
||||
mod privacy_page;
|
||||
mod referrals_page;
|
||||
mod settings_file_footer;
|
||||
pub(crate) mod settings_page;
|
||||
mod show_blocks_view;
|
||||
mod tab_menu;
|
||||
mod teams_page;
|
||||
mod telemetry;
|
||||
@@ -195,9 +189,6 @@ pub enum SettingsSection {
|
||||
Features,
|
||||
Keybindings,
|
||||
Privacy,
|
||||
Referrals,
|
||||
SharedBlocks,
|
||||
Teams,
|
||||
WarpDrive,
|
||||
Warpify,
|
||||
/// Internal backing-page identifier for AISettingsPageView. Multiple subpages
|
||||
@@ -220,9 +211,6 @@ pub enum SettingsSection {
|
||||
// ── Code umbrella subpages ──
|
||||
CodeIndexing,
|
||||
EditorAndCodeReview,
|
||||
// ── Cloud platform umbrella subpages ──
|
||||
CloudEnvironments,
|
||||
OzCloudAPIKeys,
|
||||
}
|
||||
|
||||
use crate::util::bindings::custom_tag_to_keystroke;
|
||||
@@ -233,7 +221,6 @@ impl Display for SettingsSection {
|
||||
match self {
|
||||
SettingsSection::BillingAndUsage => write!(f, "Billing and usage"),
|
||||
SettingsSection::Keybindings => write!(f, "Keyboard shortcuts"),
|
||||
SettingsSection::SharedBlocks => write!(f, "Shared blocks"),
|
||||
SettingsSection::MCPServers => write!(f, "MCP Servers"),
|
||||
SettingsSection::WarpDrive => write!(f, "Galaxy Drive"),
|
||||
SettingsSection::WarpAgent => write!(f, "Galaxy Agent"),
|
||||
@@ -244,8 +231,6 @@ impl Display for SettingsSection {
|
||||
SettingsSection::Bedrock => write!(f, "AWS Bedrock"),
|
||||
SettingsSection::CodeIndexing => write!(f, "Indexing and projects"),
|
||||
SettingsSection::EditorAndCodeReview => write!(f, "Editor and Code Review"),
|
||||
SettingsSection::CloudEnvironments => write!(f, "Environments"),
|
||||
SettingsSection::OzCloudAPIKeys => write!(f, "Oz Cloud API Keys"),
|
||||
_ => write!(f, "{self:?}"),
|
||||
}
|
||||
}
|
||||
@@ -254,7 +239,7 @@ impl Display for SettingsSection {
|
||||
impl SettingsSection {
|
||||
/// Returns true if this section is a subpage under any umbrella.
|
||||
pub fn is_subpage(&self) -> bool {
|
||||
self.is_ai_subpage() || self.is_code_subpage() || self.is_cloud_platform_subpage()
|
||||
self.is_ai_subpage() || self.is_code_subpage()
|
||||
}
|
||||
|
||||
/// Returns true if this section is a subpage under the "Agents" umbrella.
|
||||
@@ -275,11 +260,6 @@ impl SettingsSection {
|
||||
matches!(self, Self::CodeIndexing | Self::EditorAndCodeReview)
|
||||
}
|
||||
|
||||
/// Returns true if this section is a subpage under the "Cloud platform" umbrella.
|
||||
pub fn is_cloud_platform_subpage(&self) -> bool {
|
||||
matches!(self, Self::CloudEnvironments | Self::OzCloudAPIKeys)
|
||||
}
|
||||
|
||||
/// Maps subpage sections back to their parent page section for page lookup.
|
||||
/// Non-subpage sections return themselves.
|
||||
pub fn parent_page_section(&self) -> Self {
|
||||
@@ -290,8 +270,6 @@ impl SettingsSection {
|
||||
s if s.is_ai_subpage() => Self::AI,
|
||||
// Code subpages render within the Code page.
|
||||
s if s.is_code_subpage() => Self::Code,
|
||||
// CloudEnvironments and OzCloudAPIKeys ARE their own backing pages
|
||||
// (1:1 mapping), so they return themselves.
|
||||
other => *other,
|
||||
}
|
||||
}
|
||||
@@ -312,11 +290,6 @@ impl SettingsSection {
|
||||
pub fn code_subpages() -> &'static [Self] {
|
||||
&[Self::CodeIndexing, Self::EditorAndCodeReview]
|
||||
}
|
||||
|
||||
/// The ordered list of Cloud platform subpage sections.
|
||||
pub fn cloud_platform_subpages() -> &'static [Self] {
|
||||
&[Self::CloudEnvironments, Self::OzCloudAPIKeys]
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SettingsSection {
|
||||
@@ -334,9 +307,6 @@ impl FromStr for SettingsSection {
|
||||
"Features" => Ok(Self::Features),
|
||||
"Keyboard shortcuts" => Ok(Self::Keybindings),
|
||||
"Privacy" => Ok(Self::Privacy),
|
||||
"Referrals" => Ok(Self::Referrals),
|
||||
"Shared blocks" => Ok(Self::SharedBlocks),
|
||||
"Teams" => Ok(Self::Teams),
|
||||
"Warpify" => Ok(Self::Warpify),
|
||||
"WarpDrive" | "Warp Drive" | "Galaxy Drive" => Ok(Self::WarpDrive),
|
||||
// This page was called "Oz" at one point, keep for backward compatibility.
|
||||
@@ -348,8 +318,6 @@ impl FromStr for SettingsSection {
|
||||
"AWS Bedrock" | "Bedrock" => Ok(Self::Bedrock),
|
||||
"Indexing and projects" | "CodeIndexing" => Ok(Self::CodeIndexing),
|
||||
"Editor and Code Review" | "EditorAndCodeReview" => Ok(Self::EditorAndCodeReview),
|
||||
"CloudEnvironments" => Ok(Self::CloudEnvironments),
|
||||
"Oz Cloud API Keys" | "OzCloudAPIKeys" => Ok(Self::OzCloudAPIKeys),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
@@ -961,15 +929,10 @@ macro_rules! update_page {
|
||||
SettingsPageViewHandle::Main(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Appearance(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Features(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::SharedBlocks(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Keybindings(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Teams(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Warpify(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::OzCloudAPIKeys(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Privacy(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Referrals(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::AI(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::CloudEnvironments(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::About(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::Code(handle) => $ctx.update_view(handle, $update),
|
||||
SettingsPageViewHandle::BillingAndUsage(handle) => $ctx.update_view(handle, $update),
|
||||
@@ -989,7 +952,6 @@ pub struct SettingsView {
|
||||
clipped_scroll_state: ClippedScrollStateHandle,
|
||||
context_menu: ViewHandle<Menu<SettingsAction>>,
|
||||
context_menu_state: Option<Vector2F>,
|
||||
environments_page_handle: ViewHandle<EnvironmentsPageView>,
|
||||
/// Sidebar navigation items (pages + umbrellas).
|
||||
nav_items: Vec<SettingsNavItem>,
|
||||
/// Handle to the AI settings page, used to switch subpage modes.
|
||||
@@ -1038,20 +1000,6 @@ impl SettingsView {
|
||||
me.handle_features_page_event(event, ctx);
|
||||
});
|
||||
|
||||
// Shared blocks page
|
||||
let block_client = ServerApiProvider::as_ref(ctx).get_block_client();
|
||||
let show_blocks_view_handle =
|
||||
ctx.add_typed_action_view(|ctx| ShowBlocksView::new(block_client, ctx));
|
||||
|
||||
ctx.subscribe_to_view(&show_blocks_view_handle, |_, _, event, ctx| match event {
|
||||
ShowBlocksEvent::ShowToast { message, flavor } => {
|
||||
ctx.emit(SettingsViewEvent::ShowToast {
|
||||
message: message.clone(),
|
||||
flavor: *flavor,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// About page
|
||||
let about_page_handle = ctx.add_view(AboutPageView::new);
|
||||
|
||||
@@ -1062,12 +1010,6 @@ impl SettingsView {
|
||||
me.handle_ai_page_event(event, ctx);
|
||||
});
|
||||
|
||||
// Environments page
|
||||
let environments_page_handle = ctx.add_typed_action_view(EnvironmentsPageView::new);
|
||||
ctx.subscribe_to_view(&environments_page_handle, |me, _, event, ctx| {
|
||||
me.handle_environments_page_event(event, ctx);
|
||||
});
|
||||
|
||||
// Billing and usage page
|
||||
let billing_and_usage_page_handle = ctx.add_typed_action_view(BillingAndUsagePageView::new);
|
||||
ctx.subscribe_to_view(&billing_and_usage_page_handle, |me, _, event, ctx| {
|
||||
@@ -1084,20 +1026,6 @@ impl SettingsView {
|
||||
me.handle_code_page_event(event, ctx);
|
||||
});
|
||||
|
||||
// Teams page, adding unconditionally, as `should_render` later on decides whether it
|
||||
// should be shown to the user or not
|
||||
let teams_page_handle = ctx.add_typed_action_view(TeamsPageView::new);
|
||||
ctx.subscribe_to_view(&teams_page_handle, |_, _, event, ctx| match event {
|
||||
TeamsPageViewEvent::TeamsChanged => ctx.notify(),
|
||||
TeamsPageViewEvent::OpenWarpDrive => ctx.emit(SettingsViewEvent::OpenWarpDrive),
|
||||
TeamsPageViewEvent::ShowToast { message, flavor } => {
|
||||
ctx.emit(SettingsViewEvent::ShowToast {
|
||||
message: message.clone(),
|
||||
flavor: *flavor,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let warpify_page_handle = ctx.add_typed_action_view(WarpifyPageView::new);
|
||||
ctx.subscribe_to_view(&warpify_page_handle, |me, _, event, ctx| {
|
||||
me.handle_warpify_page_event(event, ctx);
|
||||
@@ -1109,13 +1037,6 @@ impl SettingsView {
|
||||
me.handle_privacy_page_event(event, ctx);
|
||||
});
|
||||
|
||||
let referrals_client = ServerApiProvider::as_ref(ctx).get_referrals_client();
|
||||
let referrals_page_handle =
|
||||
ctx.add_typed_action_view(|ctx| ReferralsPageView::new(referrals_client, ctx));
|
||||
ctx.subscribe_to_view(&referrals_page_handle, |me, _, event, ctx| {
|
||||
me.handle_referrals_page_event(event, ctx);
|
||||
});
|
||||
|
||||
// Warp Drive page
|
||||
let warp_drive_page_handle =
|
||||
ctx.add_typed_action_view(warp_drive_page::WarpDriveSettingsPageView::new);
|
||||
@@ -1167,20 +1088,16 @@ impl SettingsView {
|
||||
SettingsPage::new(ai_page_handle),
|
||||
SettingsPage::new(billing_and_usage_page_handle),
|
||||
SettingsPage::new(code_page_handle),
|
||||
SettingsPage::new(teams_page_handle),
|
||||
SettingsPage::new(appearance_page_handle),
|
||||
SettingsPage::new(features_page_handle),
|
||||
SettingsPage::new(keybindings_handle),
|
||||
SettingsPage::new(platform_page_handle),
|
||||
SettingsPage::new(warpify_page_handle),
|
||||
SettingsPage::new(referrals_page_handle),
|
||||
SettingsPage::new(show_blocks_view_handle),
|
||||
SettingsPage::new(warp_drive_page_handle),
|
||||
];
|
||||
|
||||
settings_pages.extend(vec![
|
||||
SettingsPage::new(mcp_servers_page_handle),
|
||||
SettingsPage::new(environments_page_handle.clone()),
|
||||
SettingsPage::new(privacy_page_handle),
|
||||
SettingsPage::new(about_page_handle),
|
||||
]);
|
||||
@@ -1199,20 +1116,10 @@ impl SettingsView {
|
||||
SettingsSection::EditorAndCodeReview,
|
||||
],
|
||||
)),
|
||||
SettingsNavItem::Umbrella(SettingsUmbrella::new(
|
||||
"Cloud platform",
|
||||
vec![
|
||||
SettingsSection::CloudEnvironments,
|
||||
SettingsSection::OzCloudAPIKeys,
|
||||
],
|
||||
)),
|
||||
SettingsNavItem::Page(SettingsSection::Teams),
|
||||
SettingsNavItem::Page(SettingsSection::Appearance),
|
||||
SettingsNavItem::Page(SettingsSection::Features),
|
||||
SettingsNavItem::Page(SettingsSection::Keybindings),
|
||||
SettingsNavItem::Page(SettingsSection::Warpify),
|
||||
SettingsNavItem::Page(SettingsSection::Referrals),
|
||||
SettingsNavItem::Page(SettingsSection::SharedBlocks),
|
||||
SettingsNavItem::Page(SettingsSection::WarpDrive),
|
||||
SettingsNavItem::Page(SettingsSection::Privacy),
|
||||
SettingsNavItem::Page(SettingsSection::About),
|
||||
@@ -1250,7 +1157,6 @@ impl SettingsView {
|
||||
clipped_scroll_state: Default::default(),
|
||||
context_menu,
|
||||
context_menu_state: Default::default(),
|
||||
environments_page_handle,
|
||||
nav_items,
|
||||
ai_page_handle: ai_page_handle_for_nav,
|
||||
code_page_handle: code_page_handle_for_nav,
|
||||
@@ -1635,24 +1541,6 @@ impl SettingsView {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_environments_page_event(
|
||||
&mut self,
|
||||
event: &SettingsPageEvent,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
SettingsPageEvent::FocusModal => ctx.focus(&self.search_editor),
|
||||
SettingsPageEvent::EnvironmentSetupModeSelectorToggled { .. }
|
||||
| SettingsPageEvent::AgentAssistedEnvironmentModalToggled { .. } => {
|
||||
// Re-render so the modal overlay is shown/hidden.
|
||||
ctx.notify();
|
||||
}
|
||||
SettingsPageEvent::Pane(_) => {
|
||||
// Not applicable in standalone settings view.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_features_page_event(
|
||||
&mut self,
|
||||
event: &FeaturesSettingsPageEvent,
|
||||
@@ -1748,25 +1636,6 @@ impl SettingsView {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_referrals_page_event(
|
||||
&mut self,
|
||||
event: &ReferralsPageEvent,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
match event {
|
||||
ReferralsPageEvent::SignupAnonymousUser => {
|
||||
ctx.emit(SettingsViewEvent::SignupAnonymousUser)
|
||||
}
|
||||
ReferralsPageEvent::FocusModal => ctx.focus(&self.search_editor),
|
||||
ReferralsPageEvent::ShowToast { message, flavor } => {
|
||||
ctx.emit(SettingsViewEvent::ShowToast {
|
||||
message: message.clone(),
|
||||
flavor: *flavor,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_warp_drive_page_event(
|
||||
&mut self,
|
||||
event: &warp_drive_page::WarpDriveSettingsPageEvent,
|
||||
@@ -1881,9 +1750,6 @@ impl SettingsView {
|
||||
self.clear_search_query(ctx);
|
||||
}
|
||||
self.current_settings_page = section;
|
||||
if previous_section != section && section == SettingsSection::CloudEnvironments {
|
||||
send_telemetry_from_ctx!(SettingsTelemetryEvent::EnvironmentsPageOpened, ctx);
|
||||
}
|
||||
|
||||
// When navigating to a subpage, update the backing page's active subpage mode
|
||||
// and auto-expand the umbrella containing it.
|
||||
@@ -1949,40 +1815,20 @@ impl SettingsView {
|
||||
fn should_render_page(&self, settings_page: &SettingsPage, app: &AppContext) -> bool {
|
||||
match &settings_page.view_handle {
|
||||
SettingsPageViewHandle::Main(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Teams(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::SharedBlocks(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Keybindings(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Features(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Appearance(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::BillingAndUsage(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::About(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::OzCloudAPIKeys(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Privacy(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Warpify(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Referrals(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::AI(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::CloudEnvironments(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::MCPServers(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::Code(v) => v.as_ref(app).should_render(app),
|
||||
SettingsPageViewHandle::WarpDrive(v) => v.as_ref(app).should_render(app),
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the invite section of the teams page, optionally with an email to invite.
|
||||
pub fn open_teams_page_email_invite(
|
||||
&mut self,
|
||||
email: Option<&String>,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if let Some(team_page) = self.settings_page(SettingsSection::Teams) {
|
||||
if let SettingsPageViewHandle::Teams(view) = &team_page.view_handle {
|
||||
view.update(ctx, |view, ctx| {
|
||||
view.open_team_members(email, ctx);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the MCP servers page, optionally to list page or edit page.
|
||||
/// If `autoinstall_gallery_title` is provided, triggers auto-install of the specified gallery MCP.
|
||||
pub fn open_mcp_servers_page(
|
||||
@@ -2115,9 +1961,6 @@ impl SettingsView {
|
||||
SettingsPageViewHandle::Keybindings(view_handle) => {
|
||||
view_handle.update(ctx, |view, ctx| view.on_tab_pressed(ctx));
|
||||
}
|
||||
SettingsPageViewHandle::Teams(view_handle) => {
|
||||
view_handle.update(ctx, |view, ctx| view.on_tab_pressed(ctx));
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
@@ -2178,9 +2021,6 @@ impl SettingsView {
|
||||
SettingsPageViewHandle::Privacy(view) => {
|
||||
view.read(app, |view, _| view.get_modal_content())
|
||||
}
|
||||
SettingsPageViewHandle::OzCloudAPIKeys(view) => {
|
||||
view.read(app, |view, _| view.get_modal_content())
|
||||
}
|
||||
SettingsPageViewHandle::MCPServers(view) => {
|
||||
view.read(app, |view, _| view.get_modal_content(app))
|
||||
}
|
||||
@@ -2481,24 +2321,6 @@ impl View for SettingsView {
|
||||
);
|
||||
}
|
||||
|
||||
// Render environment setup mode selector overlay when open.
|
||||
if let Some(selector_handle) = self
|
||||
.environments_page_handle
|
||||
.as_ref(app)
|
||||
.environment_setup_mode_selector_handle()
|
||||
{
|
||||
stack.add_child(ChildView::new(selector_handle).finish());
|
||||
}
|
||||
|
||||
// Render agent-assisted environment modal overlay when open.
|
||||
if let Some(modal_handle) = self
|
||||
.environments_page_handle
|
||||
.as_ref(app)
|
||||
.agent_assisted_environment_modal_handle(app)
|
||||
{
|
||||
stack.add_child(ChildView::new(modal_handle).finish());
|
||||
}
|
||||
|
||||
SavePosition::new(stack.finish(), POSITION_ID).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,17 +43,15 @@ use crate::ui_components::buttons::icon_button;
|
||||
use crate::view_components::{Dropdown, DropdownItem};
|
||||
use crate::{
|
||||
appearance::Appearance,
|
||||
auth::auth_manager::AuthManager,
|
||||
channel::ChannelState,
|
||||
report_if_error, send_telemetry_from_ctx,
|
||||
server::telemetry::TelemetryEvent,
|
||||
settings::{AISettings, PrivacySettings},
|
||||
terminal::safe_mode_settings::{SafeModeEnabled, SafeModeSettings},
|
||||
ui_components::icons::Icon,
|
||||
util::links::PRIVACY_POLICY_URL,
|
||||
workspaces::{
|
||||
user_workspaces::UserWorkspaces,
|
||||
workspace::{AdminEnablementSetting, CustomerType, UgcCollectionEnablementSetting},
|
||||
workspace::{CustomerType, UgcCollectionEnablementSetting},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -97,25 +95,7 @@ const TELEMETRY_FREE_TIER_NOTE: &str =
|
||||
const TELEMETRY_DOCS_URL: &str =
|
||||
"https://docs.warp.dev/support-and-community/privacy-and-security/privacy#what-telemetry-data-does-warp-collect-and-why";
|
||||
|
||||
const DATA_MANAGEMENT_TITLE: &str = "Manage your data";
|
||||
const DATA_MANAGEMENT_DESCRIPTION: &str =
|
||||
"At any time, you may choose to delete your Warp account permanently. \
|
||||
You will no longer be able to use Warp.";
|
||||
const DATA_MANAGEMENT_LINK_TEXT: &str = "Visit the data management page";
|
||||
|
||||
const PRIVACY_POLICY_TITLE: &str = "Privacy policy";
|
||||
const PRIVACY_POLICY_LINK_TEXT: &str = "Read Warp's privacy policy";
|
||||
|
||||
pub fn data_management_url(custom_token: Option<&str>) -> String {
|
||||
match custom_token {
|
||||
Some(token) => format!(
|
||||
"{}/data_management?customToken={}",
|
||||
ChannelState::server_root_url(),
|
||||
token
|
||||
),
|
||||
None => format!("{}/data_management", ChannelState::server_root_url(),),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrivacyPageView {
|
||||
page: PageType<Self>,
|
||||
@@ -241,13 +221,10 @@ impl PrivacyPageView {
|
||||
Box::new(SecretRedactionWidget::default()),
|
||||
Box::new(AppAnalyticsWidget::default()),
|
||||
Box::new(CrashReportsWidget::default()),
|
||||
Box::new(CloudConversationStorageWidget::default()),
|
||||
];
|
||||
if ContextFlag::NetworkLogConsole.is_enabled() {
|
||||
widgets.push(Box::new(NetworkLogWidget::default()));
|
||||
}
|
||||
widgets.push(Box::new(DataManagementWidget::default()));
|
||||
widgets.push(Box::new(PrivacyPolicyWidget::default()));
|
||||
PageType::new_uncategorized(widgets, Some("Privacy"))
|
||||
}
|
||||
|
||||
@@ -327,17 +304,6 @@ impl PrivacyPageView {
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
fn toggle_cloud_conversation_storage(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
let privacy_settings_handle = PrivacySettings::handle(ctx);
|
||||
let old_value = privacy_settings_handle
|
||||
.as_ref(ctx)
|
||||
.is_cloud_conversation_storage_enabled;
|
||||
ctx.update_model(&privacy_settings_handle, |privacy_settings, ctx| {
|
||||
privacy_settings.set_is_cloud_conversation_storage_enabled(!old_value, ctx);
|
||||
});
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
fn queue_regex_removal(&mut self, idx: usize, ctx: &mut ViewContext<Self>) {
|
||||
// Check if this removal is already pending
|
||||
if self.pending_regex_removals.contains(&idx) {
|
||||
@@ -503,10 +469,8 @@ pub enum PrivacyPageAction {
|
||||
SetSecretDisplayMode(SecretDisplayMode),
|
||||
ToggleTelemetry,
|
||||
ToggleCrashReporting,
|
||||
ToggleCloudConversationStorage,
|
||||
LaunchNetworkLogging,
|
||||
RemoveCustomRegex(usize),
|
||||
OpenDataManagementWebpage,
|
||||
AddAllRecommendedRegexes,
|
||||
ShowAddRegexModal,
|
||||
AddRecommendedRegex(usize),
|
||||
@@ -584,19 +548,10 @@ impl TypedActionView for PrivacyPageView {
|
||||
}
|
||||
PrivacyPageAction::ToggleTelemetry => self.toggle_telemetry(ctx),
|
||||
PrivacyPageAction::ToggleCrashReporting => self.toggle_crash_reporting(ctx),
|
||||
PrivacyPageAction::ToggleCloudConversationStorage => {
|
||||
self.toggle_cloud_conversation_storage(ctx)
|
||||
}
|
||||
PrivacyPageAction::LaunchNetworkLogging => self.launch_network_logging(ctx),
|
||||
PrivacyPageAction::RemoveCustomRegex(idx) => {
|
||||
self.queue_regex_removal(*idx, ctx);
|
||||
}
|
||||
PrivacyPageAction::OpenDataManagementWebpage => {
|
||||
AuthManager::handle(ctx).update(ctx, |auth_manager, ctx| {
|
||||
auth_manager
|
||||
.open_url_maybe_with_anonymous_token(ctx, Box::new(data_management_url));
|
||||
});
|
||||
}
|
||||
PrivacyPageAction::AddAllRecommendedRegexes => {
|
||||
// First process any pending removals
|
||||
if !self.pending_regex_removals.is_empty() {
|
||||
@@ -1673,120 +1628,6 @@ impl SettingsWidget for CrashReportsWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct CloudConversationStorageWidget {
|
||||
switch_state: SwitchStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for CloudConversationStorageWidget {
|
||||
type View = PrivacyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"sync cloud conversation store storage ai agent"
|
||||
}
|
||||
|
||||
fn should_render(&self, app: &AppContext) -> bool {
|
||||
if !FeatureFlag::CloudConversations.is_enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hide the toggle entirely when AI is disabled: the setting has no
|
||||
// effect without AI (no agent conversations are produced), so showing
|
||||
// it is confusing.
|
||||
if !AISettings::as_ref(app).is_any_ai_enabled(app) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let privacy_settings = PrivacySettings::as_ref(app);
|
||||
!privacy_settings.is_telemetry_force_enabled()
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ui_builder = appearance.ui_builder();
|
||||
let privacy_settings = PrivacySettings::as_ref(app);
|
||||
let org_setting =
|
||||
UserWorkspaces::as_ref(app).get_cloud_conversation_storage_enablement_setting();
|
||||
|
||||
let (toggle_state, is_checked) = match org_setting {
|
||||
AdminEnablementSetting::Enable => (ToggleState::Disabled, true),
|
||||
AdminEnablementSetting::Disable => (ToggleState::Disabled, false),
|
||||
AdminEnablementSetting::RespectUserSetting => (
|
||||
ToggleState::Enabled,
|
||||
privacy_settings.is_cloud_conversation_storage_enabled,
|
||||
),
|
||||
};
|
||||
|
||||
let switch = ui_builder
|
||||
.switch(self.switch_state.clone())
|
||||
.check(is_checked);
|
||||
let switch = if matches!(toggle_state, ToggleState::Enabled) {
|
||||
switch
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(PrivacyPageAction::ToggleCloudConversationStorage)
|
||||
})
|
||||
.finish()
|
||||
} else {
|
||||
switch
|
||||
.with_tooltip(TooltipConfig {
|
||||
text: "This setting is managed by your organization.".to_string(),
|
||||
styles: ui_builder.default_tool_tip_styles(),
|
||||
})
|
||||
.disable()
|
||||
.build()
|
||||
.finish()
|
||||
};
|
||||
|
||||
Flex::column()
|
||||
.with_child(render_body_item::<PrivacyPageAction>(
|
||||
"Store AI conversations in the cloud".into(),
|
||||
None,
|
||||
LocalOnlyIconState::Hidden,
|
||||
toggle_state,
|
||||
appearance,
|
||||
switch,
|
||||
None,
|
||||
))
|
||||
.with_child(
|
||||
ui_builder
|
||||
.paragraph(
|
||||
if is_checked {
|
||||
"Agent conversations can be shared with others and are retained \
|
||||
when you log in on different devices. This data is only stored \
|
||||
for product functionality, and Warp will not use it for analytics."
|
||||
} else {
|
||||
"Agent conversations are only stored locally on your machine, are \
|
||||
lost upon logout, and cannot be shared. Note: conversation data \
|
||||
for ambient agents are still stored in the cloud."
|
||||
}
|
||||
.to_owned(),
|
||||
)
|
||||
.with_style(UiComponentStyles {
|
||||
font_color: Some(
|
||||
appearance
|
||||
.theme()
|
||||
.sub_text_color(appearance.theme().surface_2())
|
||||
.into_solid(),
|
||||
),
|
||||
margin: Some(
|
||||
Coords::default()
|
||||
.top(styles::DESCRIPTION_NEGATIVE_MARGIN_OFFSET)
|
||||
.bottom(styles::DESCRIPTION_MARGIN_BOTTOM),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct NetworkLogWidget {
|
||||
link_mouse_state: MouseStateHandle,
|
||||
@@ -1865,133 +1706,6 @@ impl SettingsWidget for NetworkLogWidget {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DataManagementWidget {
|
||||
link_mouse_state: MouseStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for DataManagementWidget {
|
||||
type View = PrivacyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"data management delete account"
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
_app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
let ui_builder = appearance.ui_builder();
|
||||
Flex::column()
|
||||
.with_child(render_body_item::<PrivacyPageAction>(
|
||||
DATA_MANAGEMENT_TITLE.into(),
|
||||
None,
|
||||
// Not rendering a setting, so no need to show local only icon state.
|
||||
LocalOnlyIconState::Hidden,
|
||||
ToggleState::Enabled,
|
||||
appearance,
|
||||
Empty::new().finish(),
|
||||
None,
|
||||
))
|
||||
.with_child(
|
||||
ui_builder
|
||||
.paragraph(DATA_MANAGEMENT_DESCRIPTION)
|
||||
.with_style(UiComponentStyles {
|
||||
font_color: Some(
|
||||
appearance
|
||||
.theme()
|
||||
.sub_text_color(appearance.theme().surface_2())
|
||||
.into_solid(),
|
||||
),
|
||||
margin: Some(
|
||||
Coords::default()
|
||||
.top(styles::DESCRIPTION_NEGATIVE_MARGIN_OFFSET)
|
||||
.bottom(styles::DESCRIPTION_LINE_MARGIN_BOTTOM),
|
||||
),
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.finish(),
|
||||
)
|
||||
.with_child(
|
||||
Align::new(
|
||||
appearance
|
||||
.ui_builder()
|
||||
.link(
|
||||
DATA_MANAGEMENT_LINK_TEXT.into(),
|
||||
None,
|
||||
Some(Box::new(|ctx| {
|
||||
ctx.dispatch_typed_action(
|
||||
PrivacyPageAction::OpenDataManagementWebpage,
|
||||
);
|
||||
})),
|
||||
self.link_mouse_state.clone(),
|
||||
)
|
||||
.soft_wrap(false)
|
||||
.build()
|
||||
.with_margin_bottom(styles::DESCRIPTION_MARGIN_BOTTOM)
|
||||
.finish(),
|
||||
)
|
||||
.left()
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PrivacyPolicyWidget {
|
||||
link_mouse_state: MouseStateHandle,
|
||||
}
|
||||
|
||||
impl SettingsWidget for PrivacyPolicyWidget {
|
||||
type View = PrivacyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"privacy policy terms"
|
||||
}
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
_view: &Self::View,
|
||||
appearance: &Appearance,
|
||||
_app: &AppContext,
|
||||
) -> Box<dyn Element> {
|
||||
Flex::column()
|
||||
.with_child(render_body_item::<PrivacyPageAction>(
|
||||
PRIVACY_POLICY_TITLE.into(),
|
||||
None,
|
||||
// Not rendering a setting, so no need to show local only icon state.
|
||||
LocalOnlyIconState::Hidden,
|
||||
ToggleState::Enabled,
|
||||
appearance,
|
||||
Empty::new().finish(),
|
||||
None,
|
||||
))
|
||||
.with_child(
|
||||
Align::new(
|
||||
appearance
|
||||
.ui_builder()
|
||||
.link(
|
||||
PRIVACY_POLICY_LINK_TEXT.into(),
|
||||
Some(PRIVACY_POLICY_URL.into()),
|
||||
None,
|
||||
self.link_mouse_state.clone(),
|
||||
)
|
||||
.soft_wrap(false)
|
||||
.build()
|
||||
.with_margin_bottom(styles::DESCRIPTION_MARGIN_BOTTOM)
|
||||
.finish(),
|
||||
)
|
||||
.left()
|
||||
.finish(),
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_actions_from_parent_view<T: Action + Clone>(
|
||||
app: &mut AppContext,
|
||||
context: &ContextPredicate,
|
||||
|
||||
@@ -11,15 +11,11 @@ use super::{
|
||||
appearance_page::AppearanceSettingsPageView,
|
||||
billing_and_usage_page::BillingAndUsagePageView,
|
||||
code_page::CodeSettingsPageView,
|
||||
environments_page::EnvironmentsPageView,
|
||||
features_page::FeaturesPageView,
|
||||
keybindings::KeybindingsView,
|
||||
main_page::MainSettingsPageView,
|
||||
mcp_servers_page::MCPServersSettingsPageView,
|
||||
privacy_page::PrivacyPageView,
|
||||
referrals_page::ReferralsPageView,
|
||||
show_blocks_view::ShowBlocksView,
|
||||
teams_page::TeamsPageView,
|
||||
warp_drive_page::WarpDriveSettingsPageView,
|
||||
warpify_page::WarpifyPageView,
|
||||
SettingsSection,
|
||||
@@ -106,17 +102,12 @@ pub enum SettingsPageViewHandle {
|
||||
Main(ViewHandle<MainSettingsPageView>),
|
||||
Appearance(ViewHandle<AppearanceSettingsPageView>),
|
||||
Features(ViewHandle<FeaturesPageView>),
|
||||
SharedBlocks(ViewHandle<ShowBlocksView>),
|
||||
Keybindings(ViewHandle<KeybindingsView>),
|
||||
About(ViewHandle<AboutPageView>),
|
||||
Code(ViewHandle<CodeSettingsPageView>),
|
||||
Teams(ViewHandle<TeamsPageView>),
|
||||
OzCloudAPIKeys(ViewHandle<super::platform_page::PlatformPageView>),
|
||||
Privacy(ViewHandle<PrivacyPageView>),
|
||||
Warpify(ViewHandle<WarpifyPageView>),
|
||||
Referrals(ViewHandle<ReferralsPageView>),
|
||||
AI(ViewHandle<AISettingsPageView>),
|
||||
CloudEnvironments(ViewHandle<EnvironmentsPageView>),
|
||||
BillingAndUsage(ViewHandle<BillingAndUsagePageView>),
|
||||
MCPServers(ViewHandle<MCPServersSettingsPageView>),
|
||||
WarpDrive(ViewHandle<WarpDriveSettingsPageView>),
|
||||
@@ -129,17 +120,12 @@ impl SettingsPageViewHandle {
|
||||
Main(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Appearance(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Features(view_handle) => ChildView::new(view_handle).finish(),
|
||||
SharedBlocks(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Keybindings(view_handle) => ChildView::new(view_handle).finish(),
|
||||
About(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Code(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Teams(view_handle) => ChildView::new(view_handle).finish(),
|
||||
OzCloudAPIKeys(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Privacy(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Warpify(view_handle) => ChildView::new(view_handle).finish(),
|
||||
Referrals(view_handle) => ChildView::new(view_handle).finish(),
|
||||
AI(view_handle) => ChildView::new(view_handle).finish(),
|
||||
CloudEnvironments(view_handle) => ChildView::new(view_handle).finish(),
|
||||
BillingAndUsage(view_handle) => ChildView::new(view_handle).finish(),
|
||||
MCPServers(view_handle) => ChildView::new(view_handle).finish(),
|
||||
WarpDrive(view_handle) => ChildView::new(view_handle).finish(),
|
||||
|
||||
@@ -75,10 +75,10 @@ const ITEM_VERTICAL_SPACING: f32 = 24.;
|
||||
const BUILT_IN_TEXT_INPUT_MARGIN: f32 = 10.;
|
||||
const SPACE_AFTER_TEXT_INPUT: f32 = ITEM_VERTICAL_SPACING - BUILT_IN_TEXT_INPUT_MARGIN;
|
||||
|
||||
const SSH_TMUX_WARPIFICATION_DESCRIPTION: &str = "The tmux ssh wrapper works in many situations where the default one does not, but may require you to hit a button to warpify. Takes effect in new tabs.";
|
||||
const SSH_TMUX_WARPIFICATION_DESCRIPTION: &str = "The tmux ssh wrapper works in many situations where the default one does not, but may require you to hit a button to galaxify. Takes effect in new tabs.";
|
||||
|
||||
const SSH_EXTENSION_INSTALL_MODE_DESCRIPTION: &str =
|
||||
"Controls the installation behavior for Warp's SSH extension when a remote host doesn't have it installed.";
|
||||
"Controls the installation behavior for Galaxy's SSH extension when a remote host doesn't have it installed.";
|
||||
|
||||
/// This page lets users configure when they get asked to warpify a session. Some shell commands
|
||||
/// are recognized by default. Users can add new shell commands, or prevent the default ones from
|
||||
@@ -185,7 +185,7 @@ impl WarpifyPageView {
|
||||
{
|
||||
categories.push(
|
||||
Category::new("SSH", vec![Box::new(SSHWidget::default())])
|
||||
.with_subtitle("Warpify your interactive SSH sessions."),
|
||||
.with_subtitle("Galaxify your interactive SSH sessions."),
|
||||
);
|
||||
}
|
||||
PageType::new_categorized(categories, None)
|
||||
@@ -532,7 +532,7 @@ impl TitleWidget {
|
||||
fn render_top_of_page(&self, appearance: &Appearance, _app: &AppContext) -> Box<dyn Element> {
|
||||
let warpify_description = vec![
|
||||
FormattedTextFragment::plain_text(
|
||||
"Configure whether Warp attempts to “Warpify” (add support for blocks, \
|
||||
"Configure whether Galaxy attempts to \u{201c}Galaxify\u{201d} (add support for blocks, \
|
||||
input modes, etc) certain shells. ",
|
||||
),
|
||||
FormattedTextFragment::hyperlink(
|
||||
@@ -556,7 +556,7 @@ impl TitleWidget {
|
||||
.finish();
|
||||
|
||||
Flex::column()
|
||||
.with_child(render_page_title("Warpify", HEADER_FONT_SIZE, appearance))
|
||||
.with_child(render_page_title("Galaxify", HEADER_FONT_SIZE, appearance))
|
||||
.with_child(warpify_description)
|
||||
.finish()
|
||||
}
|
||||
@@ -566,7 +566,7 @@ impl SettingsWidget for TitleWidget {
|
||||
type View = WarpifyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"ssh subshell warpify session"
|
||||
"ssh subshell galaxify session"
|
||||
}
|
||||
|
||||
fn render(
|
||||
@@ -628,7 +628,7 @@ impl SettingsWidget for SubshellsWidget {
|
||||
type View = WarpifyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"warpify subshell"
|
||||
"galaxify subshell"
|
||||
}
|
||||
|
||||
fn render(
|
||||
@@ -655,7 +655,7 @@ impl SettingsWidget for SSHWidget {
|
||||
type View = WarpifyPageView;
|
||||
|
||||
fn search_terms(&self) -> &str {
|
||||
"warpify ssh"
|
||||
"galaxify ssh"
|
||||
}
|
||||
|
||||
fn render(
|
||||
@@ -682,7 +682,7 @@ impl SettingsWidget for SSHWidget {
|
||||
&WarpifySettings::as_ref(app).enable_ssh_warpification,
|
||||
move || {
|
||||
render_body_item::<WarpifyPageAction>(
|
||||
"Warpify SSH Sessions".into(),
|
||||
"Galaxify SSH Sessions".into(),
|
||||
None,
|
||||
LocalOnlyIconState::for_setting(
|
||||
EnableSshWarpification::storage_key(),
|
||||
|
||||
Reference in New Issue
Block a user