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
@@ -0,0 +1,358 @@
use pathfinder_color::ColorU;
use warp_core::ui::Icon;
use warpui::assets::asset_cache::{AssetCache, AssetSource, AssetState};
use warpui::geometry::rect::RectF;
use warpui::geometry::vector::{vec2f, Vector2F};
use warpui::image_cache::{AnimatedImageBehavior, CacheOption, FitType, Image, ImageCache};
use warpui::{
elements::{CornerRadius, Fill, Point, Radius},
event::DispatchedEvent,
AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, PaintContext,
SingletonEntity as _, SizeConstraint,
};
#[derive(Debug, Clone, Copy)]
pub(crate) struct RectPct {
pub x: f32,
pub y: f32,
pub w: f32,
pub h: f32,
}
impl RectPct {
pub const fn new(x: f32, y: f32, w: f32, h: f32) -> Self {
Self { x, y, w, h }
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Pill {
pub rect: RectPct,
pub color: ColorU,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Rect {
pub rect: RectPct,
pub color: ColorU,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct IconPct {
pub icon: Icon,
pub color: ColorU,
pub center_x: f32,
pub center_y: f32,
pub width_pct: f32,
}
pub(crate) struct OnboardingVisual {
panel_background: ColorU,
pills: Vec<Pill>,
rects: Vec<Rect>,
icons: Vec<IconPct>,
terminal_box: bool,
size: Option<Vector2F>,
origin: Option<Point>,
}
impl OnboardingVisual {
// Padding scale parameters (based on max(x, y) in pixels).
const MIN_PADDING_SIZE_PX: f32 = 500.0;
const MAX_PADDING_SIZE_PX: f32 = 2000.0;
const MIN_PADDING_PCT: f32 = 0.10;
const MAX_PADDING_PCT: f32 = 0.30;
pub fn new(panel_background: ColorU, pills: Vec<Pill>, terminal_box: bool) -> Self {
Self::new_internal(panel_background, pills, terminal_box)
}
fn new_internal(panel_background: ColorU, pills: Vec<Pill>, terminal_box: bool) -> Self {
Self {
panel_background,
pills,
icons: Vec::new(),
rects: Vec::new(),
terminal_box,
size: None,
origin: None,
}
}
pub fn with_icons(mut self, icons: Vec<IconPct>) -> Self {
self.icons = icons;
self
}
pub fn with_rects(mut self, rects: Vec<Rect>) -> Self {
self.rects = rects;
self
}
pub fn compute_contained_size(constraint: SizeConstraint) -> Vector2F {
// Maintain the old images aspect ratio so the right-side layout stays stable.
const ASPECT_RATIO: f32 = 1.5;
let max = constraint.max;
if max.x().is_infinite() && max.y().is_infinite() {
return vec2f(constraint.min.x().max(0.), constraint.min.y().max(0.));
}
let mut width = if max.x().is_finite() {
max.x().max(0.)
} else {
(max.y().max(0.)) * ASPECT_RATIO
};
let mut height = if max.y().is_finite() {
max.y().max(0.)
} else {
width / ASPECT_RATIO
};
// Contain within both axes when both are finite.
if max.x().is_finite() && max.y().is_finite() {
height = width / ASPECT_RATIO;
if height > max.y() {
height = max.y();
width = height * ASPECT_RATIO;
}
}
vec2f(width.max(1.), height.max(1.))
}
fn feature_radius_px(inner_height: f32) -> f32 {
// All features use the same radius.
inner_height * 0.02
}
fn draw_pill(
&self,
rect: RectF,
color: ColorU,
feature_radius_px: f32,
ctx: &mut PaintContext,
) {
ctx.scene
.draw_rect_with_hit_recording(rect)
.with_background(Fill::Solid(color))
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(feature_radius_px)));
}
fn draw_rect(&self, rect: RectF, color: ColorU, ctx: &mut PaintContext) {
ctx.scene
.draw_rect_with_hit_recording(rect)
.with_background(Fill::Solid(color));
}
fn rect_from_pct(inner_origin: Vector2F, inner_size: Vector2F, rect: RectPct) -> RectF {
RectF::new(
vec2f(
inner_origin.x() + inner_size.x() * rect.x,
inner_origin.y() + inner_size.y() * rect.y,
),
vec2f(inner_size.x() * rect.w, inner_size.y() * rect.h),
)
}
fn icon_rect_from_pct(
inner_origin: Vector2F,
inner_size: Vector2F,
center_x: f32,
center_y: f32,
width_pct: f32,
) -> RectF {
let center = vec2f(
inner_origin.x() + inner_size.x() * center_x,
inner_origin.y() + inner_size.y() * center_y,
);
let width_px = inner_size.x() * width_pct;
RectF::new(
center - vec2f(width_px / 2.0, width_px / 2.0),
vec2f(width_px, width_px),
)
}
fn draw_icon(
&self,
rect: RectF,
icon: Icon,
color: ColorU,
ctx: &mut PaintContext,
app: &AppContext,
) {
let bounds = (rect.size() * ctx.scene.scale_factor()).to_i32();
if bounds.x() <= 0 || bounds.y() <= 0 {
return;
}
let path: &'static str = icon.into();
let asset_cache = AssetCache::as_ref(app);
match ImageCache::as_ref(app).image(
AssetSource::Bundled { path },
bounds,
FitType::Contain,
AnimatedImageBehavior::FullAnimation,
CacheOption::BySize,
ctx.max_texture_dimension_2d,
asset_cache,
) {
AssetState::Loaded { data } => match data.as_ref() {
Image::Static(image) => {
let logical_image_size = image.size().to_f32() / ctx.scene.scale_factor();
let origin = rect.origin() + ((rect.size() - logical_image_size) / 2.0);
ctx.scene.draw_icon(
RectF::new(origin, logical_image_size),
image.clone(),
1.0,
color,
);
}
Image::Animated(_) => {
log::info!("Animated icons are currently not supported");
}
},
AssetState::Loading { handle } => {
ctx.repaint_after_load(handle);
}
AssetState::Evicted => {
log::warn!("Unable to render svg because it was evicted");
}
AssetState::FailedToLoad(err) => {
log::warn!("Unable to render svg: {err:#}");
}
}
}
}
impl Element for OnboardingVisual {
fn layout(
&mut self,
constraint: SizeConstraint,
_ctx: &mut LayoutContext,
_app: &AppContext,
) -> Vector2F {
let size = Self::compute_contained_size(constraint);
self.size = Some(size);
size
}
fn after_layout(&mut self, _ctx: &mut AfterLayoutContext, _app: &AppContext) {}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, _app: &AppContext) {
let Some(size) = self.size else {
return;
};
self.origin = Some(Point::from_vec2f(origin, ctx.scene.z_index()));
let max_dimension = size.x().max(size.y());
// Scale padding linearly with size.
let padding_pct = if max_dimension <= Self::MIN_PADDING_SIZE_PX {
Self::MIN_PADDING_PCT
} else if max_dimension >= Self::MAX_PADDING_SIZE_PX {
Self::MAX_PADDING_PCT
} else {
let t = (max_dimension - Self::MIN_PADDING_SIZE_PX)
/ (Self::MAX_PADDING_SIZE_PX - Self::MIN_PADDING_SIZE_PX);
Self::MIN_PADDING_PCT + t * (Self::MAX_PADDING_PCT - Self::MIN_PADDING_PCT)
};
let padding_x: f32 = padding_pct * size.x();
let padding_y: f32 = padding_pct * size.y();
let inner_origin = origin + vec2f(padding_x, padding_y);
let inner_size = vec2f(
(size.x() - 2.0 * padding_x).max(1.),
(size.y() - 2.0 * padding_y).max(1.),
);
let feature_radius_px = Self::feature_radius_px(inner_size.y());
// Background panel.
ctx.scene
.draw_rect_with_hit_recording(RectF::new(inner_origin, inner_size))
.with_background(Fill::Solid(self.panel_background))
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(feature_radius_px)));
if self.terminal_box {
// Bottom container behind the final rows.
const TERMINAL_BOX_Y_PCT: f32 = 0.80;
const TERMINAL_BOX_H_PCT: f32 = 0.25;
let min_x = self
.pills
.iter()
.map(|pill| pill.rect.x)
.fold(0.0_f32, f32::min);
let max_x = self
.pills
.iter()
.map(|pill| pill.rect.x + pill.rect.w)
.fold(1.0_f32, f32::max);
let box_rect = Self::rect_from_pct(
inner_origin,
inner_size,
RectPct::new(min_x, TERMINAL_BOX_Y_PCT, max_x - min_x, TERMINAL_BOX_H_PCT),
);
let box_color = self
.pills
.first()
.map(|pill| pill.color)
.unwrap_or(self.panel_background);
ctx.scene
.draw_rect_with_hit_recording(box_rect)
.with_background(Fill::Solid(box_color))
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(feature_radius_px)));
}
// Rects.
for rect in &self.rects {
let rect_px = Self::rect_from_pct(inner_origin, inner_size, rect.rect);
self.draw_rect(rect_px, rect.color, ctx);
}
// Pills.
for pill in &self.pills {
let rect = Self::rect_from_pct(inner_origin, inner_size, pill.rect);
self.draw_pill(rect, pill.color, feature_radius_px, ctx);
}
// Icons.
for icon in &self.icons {
let rect = Self::icon_rect_from_pct(
inner_origin,
inner_size,
icon.center_x,
icon.center_y,
icon.width_pct,
);
self.draw_icon(rect, icon.icon, icon.color, ctx, _app);
}
}
fn dispatch_event(
&mut self,
_event: &DispatchedEvent,
_ctx: &mut EventContext,
_app: &AppContext,
) -> bool {
false
}
fn size(&self) -> Option<Vector2F> {
self.size
}
fn origin(&self) -> Option<Point> {
self.origin
}
}