Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
pub mod onboarding_callout;
@@ -0,0 +1,400 @@
use std::borrow::Cow;
use pathfinder_color::ColorU;
use pathfinder_geometry::vector::vec2f;
use ui_components::{
button, button::Button as ButtonComponent, Component, MouseEventHandler, Options as _,
};
use warp_core::ui::{
appearance::Appearance,
color::{coloru_with_opacity, contrast::relative_luminance},
theme::{phenomenon::PhenomenonStyle, Fill},
};
use warpui::{
elements::{
Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DropShadow, Flex,
MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, Radius, Rect,
},
fonts::Weight,
keymap::Keystroke,
prelude::*,
ui_components::checkbox::Checkbox as WarpCheckbox,
ui_components::components::{UiComponent as _, UiComponentStyles},
};
const CALLOUT_WIDTH: f32 = 480.;
const CALLOUT_BORDER_WIDTH: f32 = 1.;
const CALLOUT_CORNER_RADIUS: f32 = 8.;
const CALLOUT_PADDING: f32 = 16.;
struct PhenomenonPrimaryButtonTheme;
struct PhenomenonSecondaryButtonTheme;
impl button::Theme for PhenomenonPrimaryButtonTheme {
fn background(&self, button_state: button::State, _appearance: &Appearance) -> Option<Fill> {
let hovered = matches!(
button_state,
button::State::Hovered | button::State::Pressed
);
Some(PhenomenonStyle::primary_button_background(hovered))
}
fn text_color(&self, _background: Option<Fill>, _appearance: &Appearance) -> ColorU {
PhenomenonStyle::primary_button_text()
}
fn keyboard_shortcut_border(&self, text_color: ColorU, _: &Appearance) -> Option<ColorU> {
Some(coloru_with_opacity(text_color, 60))
}
}
impl button::Theme for PhenomenonSecondaryButtonTheme {
fn background(&self, button_state: button::State, _appearance: &Appearance) -> Option<Fill> {
match button_state {
button::State::Default => None,
button::State::Hovered | button::State::Pressed => {
Some(PhenomenonStyle::segmented_control_background())
}
}
}
fn text_color(&self, _background: Option<Fill>, _appearance: &Appearance) -> ColorU {
PhenomenonStyle::foreground()
}
fn border(&self, _appearance: &Appearance) -> Option<ColorU> {
Some(PhenomenonStyle::subtle_border())
}
fn keyboard_shortcut_border(&self, text_color: ColorU, _: &Appearance) -> Option<ColorU> {
Some(coloru_with_opacity(text_color, 60))
}
}
#[derive(Debug, Clone, Copy)]
pub struct StepStatus {
pub current_step: u8,
pub total_steps: u8,
}
impl StepStatus {
pub fn new(current_step: u8, total_steps: u8) -> Self {
Self {
current_step,
total_steps,
}
}
}
pub struct Button {
pub text: Cow<'static, str>,
pub keystroke: Option<Keystroke>,
pub handler: MouseEventHandler,
}
impl Button {
pub fn next(handler: MouseEventHandler) -> Self {
Self {
text: Cow::Borrowed("Next"),
keystroke: Some(Keystroke {
key: "enter".into(),
..Default::default()
}),
handler,
}
}
}
/// A checkbox with a label and click handler.
pub struct Checkbox {
pub label: Cow<'static, str>,
pub checked: bool,
pub handler: MouseEventHandler,
}
#[derive(Default)]
pub struct OnboardingCallout {
right_button: ButtonComponent,
left_button: ButtonComponent,
checkbox_mouse_state: MouseStateHandle,
}
pub struct Params {
/// The title of the callout.
pub title: Cow<'static, str>,
/// The body text of the callout.
pub text: Cow<'static, str>,
/// Current step and total steps.
pub step: StepStatus,
pub right_button: Button,
/// Optional configuration.
pub options: Options,
}
impl ui_components::Params for Params {
type Options<'a> = Options;
}
pub struct Options {
/// Optional left button, typically "Skip".
pub left_button: Option<Button>,
/// Optional checkbox for toggling settings.
pub checkbox: Option<Checkbox>,
}
impl ui_components::Options for Options {
fn default(_appearance: &Appearance) -> Self {
Self {
left_button: None,
checkbox: None,
}
}
}
impl Component for OnboardingCallout {
type Params<'a> = Params;
fn render<'a>(&self, appearance: &Appearance, params: Self::Params<'a>) -> Box<dyn Element> {
let Params {
title,
text,
step,
right_button,
options,
} = params;
let header = self.render_header(appearance, &title);
let body = self.render_body(appearance, &text);
// Take checkbox out before passing options to render_actions
let mut options = options;
let checkbox = options
.checkbox
.take()
.map(|cb| self.render_checkbox(appearance, cb));
let actions = self.render_actions(step, right_button, options, appearance);
let mut content = Flex::column()
.with_main_axis_size(MainAxisSize::Min)
.with_spacing(12.);
content.add_child(header);
content.add_child(body);
if let Some(checkbox_element) = checkbox {
content.add_child(checkbox_element);
}
content.add_child(actions);
let content = content.finish();
let background = PhenomenonStyle::tinted_surface();
let border_color = Fill::Solid(PhenomenonStyle::surface_border());
// Use lighter shadow on dark themes, darker shadow on light themes
let background_luminance = relative_luminance(appearance.theme().background().into_solid());
let is_light_theme = background_luminance > 0.2;
let shadow_opacity = if is_light_theme { 20 } else { 35 };
let shadow = DropShadow {
color: coloru_with_opacity(ColorU::black(), shadow_opacity),
offset: vec2f(0., 10.),
blur_radius: 20.,
spread_radius: 0.,
};
ConstrainedBox::new(
Container::new(content)
.with_uniform_padding(CALLOUT_PADDING)
.with_background(background)
.with_border(Border::all(CALLOUT_BORDER_WIDTH).with_border_fill(border_color))
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(
CALLOUT_CORNER_RADIUS,
)))
.with_drop_shadow(shadow)
.finish(),
)
.with_width(CALLOUT_WIDTH)
.finish()
}
}
impl OnboardingCallout {
fn render_header(&self, appearance: &Appearance, title: &str) -> Box<dyn Element> {
appearance
.ui_builder()
.paragraph(title.to_string())
.with_style(UiComponentStyles {
font_color: Some(PhenomenonStyle::foreground()),
font_size: Some(16.),
font_weight: Some(Weight::Bold),
..Default::default()
})
.build()
.finish()
}
fn render_body(&self, appearance: &Appearance, text: &str) -> Box<dyn Element> {
appearance
.ui_builder()
.paragraph(text.to_string())
.with_style(UiComponentStyles {
font_color: Some(PhenomenonStyle::body_text()),
font_size: Some(13.),
..Default::default()
})
.build()
.finish()
}
fn render_checkbox(&self, appearance: &Appearance, checkbox: Checkbox) -> Box<dyn Element> {
let checkbox_size = Some(12.);
let corner_radius = CornerRadius::with_all(Radius::Pixels(2.));
let foreground_color = PhenomenonStyle::foreground();
let subtle_border = Fill::Solid(PhenomenonStyle::subtle_border());
let checkbox_element = WarpCheckbox::new(
self.checkbox_mouse_state.clone(),
UiComponentStyles {
font_size: checkbox_size,
border_color: Some(Fill::Solid(foreground_color).into()),
font_color: Some(foreground_color),
border_width: Some(1.),
border_radius: Some(corner_radius),
..Default::default()
},
None,
Some(UiComponentStyles {
font_size: checkbox_size,
background: Some(Fill::Solid(foreground_color).into()),
border_color: Some(Fill::Solid(foreground_color).into()),
font_color: Some(PhenomenonStyle::background()),
border_radius: Some(corner_radius),
..Default::default()
}),
Some(UiComponentStyles {
font_size: checkbox_size,
border_color: Some(subtle_border.into()),
font_color: Some(PhenomenonStyle::subtle_border()),
border_width: Some(1.),
border_radius: Some(corner_radius),
..Default::default()
}),
)
.check(checkbox.checked)
.build()
.on_click(checkbox.handler)
.finish();
let label = appearance
.ui_builder()
.paragraph(checkbox.label.to_string())
.with_style(UiComponentStyles {
font_color: Some(PhenomenonStyle::label_text()),
font_size: Some(12.),
..Default::default()
})
.build()
.finish();
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_spacing(8.)
.with_child(checkbox_element)
.with_child(label)
.finish()
}
fn render_status_dots(&self, step: StepStatus) -> Box<dyn Element> {
const DOT_SIZE: f32 = 8.;
const DOT_SPACING: f32 = 4.;
let mut row = Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_spacing(DOT_SPACING);
for i in 0..step.total_steps {
let is_current = i == step.current_step;
let dot_color = if is_current {
PhenomenonStyle::surface_border()
} else {
PhenomenonStyle::subtle_border()
};
let dot = ConstrainedBox::new(
Rect::new()
.with_background_color(dot_color)
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(DOT_SIZE / 2.)))
.finish(),
)
.with_width(DOT_SIZE)
.with_height(DOT_SIZE)
.finish();
row.add_child(dot);
}
row.finish()
}
fn render_actions(
&self,
step: StepStatus,
right_button: Button,
mut options: Options,
appearance: &Appearance,
) -> Box<dyn Element> {
let right_button_text = right_button.text.clone();
let right_button = self.right_button.render(
appearance,
button::Params {
content: button::Content::Label(right_button_text),
theme: &PhenomenonPrimaryButtonTheme,
options: button::Options {
on_click: Some(right_button.handler),
keystroke: right_button.keystroke.clone(),
..button::Options::default(appearance)
},
},
);
let status_dots = self.render_status_dots(step);
let mut buttons_row = Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_main_axis_alignment(MainAxisAlignment::End)
.with_main_axis_size(MainAxisSize::Min);
if let Some(left_button) = options.left_button.take() {
let left_button_text = left_button.text.clone();
let left_button_element = self.left_button.render(
appearance,
button::Params {
content: button::Content::Label(left_button_text),
theme: &PhenomenonSecondaryButtonTheme,
options: button::Options {
on_click: Some(left_button.handler),
keystroke: left_button.keystroke.clone(),
..button::Options::default(appearance)
},
},
);
buttons_row.add_child(
Container::new(left_button_element)
.with_margin_right(8.)
.finish(),
);
}
buttons_row.add_child(right_button);
Flex::row()
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_child(status_dots)
.with_child(buttons_row.finish())
.finish()
}
}