Capture · Roots: roots drop w_full; hosts give the width
145 component roots drop w_full, the early returns included. Three field rows keep it, Input, InputGroup and TagInput: stretched without a width, taffy 0.9 laid the field's flex_1 share at no width in the template picker at 280px, and the placeholder drew nothing. Container keeps it, since mx_auto needs a width to fill. Hosts that relied on a root's width now give it: MessageList boxes each row at the list's width, the splash gives its bar the column's, and test stages that mount a component as the window's root sit in a size_full box. Tests hold a narrow row across the list and the splash bar across its column; each goes red without its fix. The gallery gains --narrow <px>, which lays each page in a padded card at that width and shoots it without its script. The page header wraps: the title takes the room and the theme switcher drops below, where a long summary used to push it past the edge. A file panel step waits for the panel to become key instead of a fixed pause, which lost a race in a capture.
This commit is contained in:
1 parent
3be809a660
commit
81e73b79c1
126 files changed
+275
-276
No files matched your search
@@ -22,6 +22,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
|
||||
|
||||
- `cargo run --example gallery` opens the gallery. `-- --page <slug>` starts on a page.
|
||||
- `cargo run --example gallery -- --capture <dir>` writes PNGs of every page, light and dark, top to bottom, then each page's scripted states, including windows the demos open. macOS only.
|
||||
- `cargo run --example gallery -- --narrow 280 --capture <dir>` lays each page, header and body, at that width in a padded card and shoots it without its script, whose aims are measured at full width. Demos that set their own width run past the card.
|
||||
- `cargo test --lib --features test-support`, `cargo clippy --all-targets --features test-support -- -D warnings`, `cargo fmt --check`. `scripts/check.sh` runs them with the house rules.
|
||||
- `rm -rf target/debug/incremental` and `cargo clean -p ely-gpui-component` after each task item keep the disk lean. Cargo keeps every old build of the crate and its tests under a new hash; by T25 they had filled the disk.
|
||||
- `scripts/icons.sh <lucide-name>...` adds icons. Then add a line to `IconName` in `src/primitives/icon.rs`.
|
||||
@@ -111,7 +112,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
|
||||
- Trackpad momentum arrives as `TouchPhase::Moved` after `Ended`, so a gesture acts only between `Started` and `Ended` (`lists::SwipeableListItem`).
|
||||
- Quartz scroll events carry no window. The harness hands them to the window itself, placed in window points (`Step::Swipe`).
|
||||
- A chevron that opens and closes is `primitives::Disclosure`.
|
||||
- `uniform_list` does not stretch its rows; each row sets `w_full`, or `min_w_full` when the same row also sits in a flex column, where it stretches (`tables::DataTable`).
|
||||
- `uniform_list` does not stretch its rows; each row sets `w_full`, or `min_w_full` when the same row also sits in a flex column, where it stretches (`tables::DataTable`). gpui's `list` lays rows at their content's width too; `chat::MessageList` boxes each row at the list's width.
|
||||
- Chapters import only earlier chapters, so a select of tree nodes lives in `lists` (`TreeSelect`) and reaches `forms::listing`.
|
||||
- Script clicks aim at a probe's corner, so a scripted target must fit in the window; a tall probe puts its top off screen. The log names each probe's bounds as the script aims, and a probe around content with no width of its own measures smaller than what it draws, so a scripted demo sets its width.
|
||||
- `scripts/check.sh` fails by its exit status; a pipe into `tail` hides it.
|
||||
@@ -152,7 +153,7 @@ Ely GPUI Component. A component library for GPUI, in light and dark.
|
||||
- A row that holds text beside fixed parts lets the text give way: the text takes `flex_1().min_w_0()` and an `Ellipsis`; counts, icons, dates and chevrons take `flex_none`. A label that must stay whole wraps in that block instead, as `forms::Checkbox`'s does.
|
||||
- A mark over a text field reads the field at paint time, from a canvas laid over it (`collab::RemoteCursor`): by then `TextInput::bounds_for` holds this frame's layout, and a still page never renders again to catch up.
|
||||
- A callback field holds a named `type` alias; clippy's type_complexity rejects `Option<Rc<dyn Fn(..)>>`. A chapter shares one alias per shape.
|
||||
- A component that fills its container takes no width on its root: a block or a flex column stretches it. Percents fill badly in taffy 0.9. `w_full` in a flex column that a block measures by content keeps its content's width: taffy lays the column out once at unknown width and keeps that layout when the width comes out the same. `min_w_full` resolves against a block's outer width, padding and all (`forms::FormField`). In a flex row the host gives it `flex_1`; as a window's root it sits in a `size_full` box. Roots made before T30d still say `w_full`; T44 takes it off.
|
||||
- A component that fills its container takes no width on its root: a block or a flex column stretches it. Percents fill badly in taffy 0.9. `w_full` in a flex column that a block measures by content keeps its content's width: taffy lays the column out once at unknown width and keeps that layout when the width comes out the same. `min_w_full` resolves against a block's outer width, padding and all (`forms::FormField`). In a flex row the host gives it `flex_1`; as a window's root it sits in a `size_full` box. A root centered under a cap keeps `w_full`, since `mx_auto` needs a width to fill (`layout::Container`). A row that hands a text field a `flex_1` share keeps `w_full` too: stretched without a width, taffy 0.9 can keep a pass that laid the share at no width, and the field draws nothing (`forms::Input`, `InputGroup`, `TagInput`).
|
||||
- A field's `Highlight` sets color, wash, weight, slant and strike per span; measuring and painting share one set of runs, so wrapped heights match.
|
||||
- A block editor keeps one undo for the document: text and structure together, as snapshots through `forms::History`; it takes Undo and Redo before its fields do.
|
||||
- gpui calls focus listeners only while the window is active. A test that needs focus events activates its window with `window.activate_window()`.
|
||||
|
||||
@@ -150,7 +150,7 @@ Per-component lines live in `tasks/`. Tags there:
|
||||
- [x] T43a Tooling · Workbench — `tasks/ch31-43.md`
|
||||
- [x] T43b Tooling · Inspection — `tasks/ch31-43.md`
|
||||
- [x] T43c Tooling · Catalogs — `tasks/ch31-43.md`
|
||||
- [ ] T44a Capture · Roots: roots still on `w_full` drop it; every section checked at 280px and in a padded card
|
||||
- [ ] T44a Capture · Roots: roots still on `w_full` drop it, checked at 280px and in padded cards
|
||||
- [ ] T44b Capture · Sections: every component's section shot on its own, light and dark, with a manifest the site reads
|
||||
- [ ] T44c Capture · Motion: clips of each page's scripted states in motion, for the site
|
||||
- [ ] T45 Website: `frontend/`, Vite 8, pnpm, light and dark: a home page in motion (three.js, smooth scroll), and a components page with every component, categories on the left and the chosen one's detail on the right, after astryx.atmeta.com
|
||||
|
||||
@@ -15,11 +15,17 @@ use crate::{
|
||||
const SETTLE: Duration = Duration::from_millis(700);
|
||||
const SCROLL_SETTLE: Duration = Duration::from_millis(250);
|
||||
|
||||
/// Shoots one page, or all, in both modes, then quits. Exits 1 on failure.
|
||||
pub fn run(window: WindowHandle<Gallery>, dir: PathBuf, only: Option<usize>, cx: &mut App) {
|
||||
/// Shoots one page, or all, in both modes, then quits; exits 1 on failure.
|
||||
pub fn run(
|
||||
window: WindowHandle<Gallery>,
|
||||
dir: PathBuf,
|
||||
only: Option<usize>,
|
||||
scripted: bool,
|
||||
cx: &mut App,
|
||||
) {
|
||||
Theme::update(cx, |theme| theme.reduced_motion = true);
|
||||
cx.spawn(async move |cx| {
|
||||
let outcome = shoot_all(window, &dir, only, cx).await;
|
||||
let outcome = shoot_all(window, &dir, only, scripted, cx).await;
|
||||
if let Err(error) = outcome.and_then(|()| cx.update(|cx| cx.quit())) {
|
||||
log::error!("capture failed: {error:#}");
|
||||
std::process::exit(1);
|
||||
@@ -32,6 +38,7 @@ async fn shoot_all(
|
||||
window: WindowHandle<Gallery>,
|
||||
dir: &Path,
|
||||
only: Option<usize>,
|
||||
scripted: bool,
|
||||
cx: &mut AsyncApp,
|
||||
) -> Result<()> {
|
||||
std::fs::create_dir_all(dir).with_context(|| format!("create {}", dir.display()))?;
|
||||
@@ -70,7 +77,7 @@ async fn shoot_all(
|
||||
window.update(cx, |gallery, _, cx| gallery.scroll_to(offset, cx))?;
|
||||
cx.background_executor().timer(SCROLL_SETTLE).await;
|
||||
}
|
||||
if !page.script.is_empty() {
|
||||
if scripted && !page.script.is_empty() {
|
||||
window.update(cx, |gallery, _, cx| gallery.select(ix, cx))?;
|
||||
cx.background_executor().timer(SETTLE).await;
|
||||
let shot = |step: &str| dir.join(format!("{}-{step}-{name}.png", page.slug));
|
||||
|
||||
@@ -68,12 +68,14 @@ fn choose(window: WindowHandle<shell::Gallery>, choice: shell::Choice, cx: &mut
|
||||
struct Args {
|
||||
page: Option<usize>,
|
||||
capture: Option<PathBuf>,
|
||||
narrow: Option<f32>,
|
||||
}
|
||||
|
||||
fn parse_args() -> Result<Args> {
|
||||
let mut args = Args {
|
||||
page: None,
|
||||
capture: None,
|
||||
narrow: None,
|
||||
};
|
||||
let mut iter = std::env::args().skip(1);
|
||||
while let Some(flag) = iter.next() {
|
||||
@@ -85,6 +87,13 @@ fn parse_args() -> Result<Args> {
|
||||
args.page = Some(pages::find(&value).with_context(|| format!("no page {value}"))?)
|
||||
}
|
||||
"--capture" => args.capture = Some(PathBuf::from(value)),
|
||||
"--narrow" => {
|
||||
args.narrow = Some(
|
||||
value
|
||||
.parse()
|
||||
.with_context(|| format!("bad width {value}"))?,
|
||||
)
|
||||
}
|
||||
other => bail!("unknown flag {other}"),
|
||||
}
|
||||
}
|
||||
@@ -118,13 +127,15 @@ fn main() -> Result<()> {
|
||||
};
|
||||
let window = cx
|
||||
.open_window(options, |window, cx| {
|
||||
cx.new(|cx| shell::Gallery::new(args.page.unwrap_or(0), window, cx))
|
||||
cx.new(|cx| {
|
||||
shell::Gallery::new(args.page.unwrap_or(0), args.narrow, window, cx)
|
||||
})
|
||||
})
|
||||
.expect("gallery window failed to open");
|
||||
menu_actions(window, cx);
|
||||
cx.activate(true);
|
||||
if let Some(dir) = args.capture {
|
||||
capture::run(window, dir, args.page, cx);
|
||||
capture::run(window, dir, args.page, args.narrow.is_none(), cx);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
|
||||
@@ -114,7 +114,6 @@ const SCRIPT: &[Step] = &[
|
||||
Step::Shot("hotkey"),
|
||||
Step::DownAt("path", 380.0, 16.0),
|
||||
Step::UpAt("path", 380.0, 16.0),
|
||||
Step::Wait(1200),
|
||||
Step::CancelPanel,
|
||||
Step::Wait(600),
|
||||
Step::DownAt("checks", 40.0, 66.0),
|
||||
@@ -242,7 +241,6 @@ const SCRIPT: &[Step] = &[
|
||||
Step::Shot("gradient"),
|
||||
Step::DownAt("file-one", 100.0, 14.0),
|
||||
Step::UpAt("file-one", 100.0, 14.0),
|
||||
Step::Wait(1200),
|
||||
Step::CancelPanel,
|
||||
Step::Wait(600),
|
||||
Step::Shot("files"),
|
||||
|
||||
@@ -53,6 +53,8 @@ pub enum Step {
|
||||
}
|
||||
|
||||
const FRAME: Duration = Duration::from_millis(120);
|
||||
/// How long an out-of-process file panel may take to open.
|
||||
const PANEL: Duration = Duration::from_secs(10);
|
||||
const DRAG_STEPS: u32 = 10;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -218,7 +220,15 @@ pub async fn play(
|
||||
let number = window.update(cx, |_, window, _| number(window))??;
|
||||
post_key(number, true, text, code)?;
|
||||
}
|
||||
Step::CancelPanel => cancel_panel()?,
|
||||
Step::CancelPanel => {
|
||||
let mut waited = Duration::ZERO;
|
||||
while !cancel_panel()? {
|
||||
anyhow::ensure!(waited < PANEL, "no file panel became key in {PANEL:?}");
|
||||
cx.background_executor().timer(FRAME).await;
|
||||
waited += FRAME;
|
||||
}
|
||||
log::info!("script: file panel cancelled after {waited:?}");
|
||||
}
|
||||
Step::ExpectClosed(key) => {
|
||||
let handle = opened(key, cx)?;
|
||||
let open = cx.update(|cx| cx.windows().contains(&handle))?;
|
||||
@@ -363,7 +373,8 @@ fn post_key(_: u32, _: bool, _: &str, _: u16) -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn cancel_panel() -> Result<()> {
|
||||
/// Cancels a key file panel; false while none is key.
|
||||
fn cancel_panel() -> Result<bool> {
|
||||
use cocoa::{
|
||||
appkit::NSApp,
|
||||
base::{BOOL, NO, id, nil},
|
||||
@@ -371,16 +382,20 @@ fn cancel_panel() -> Result<()> {
|
||||
use objc::{class, msg_send, sel, sel_impl};
|
||||
unsafe {
|
||||
let key: id = msg_send![NSApp(), keyWindow];
|
||||
anyhow::ensure!(key != nil, "no key window to cancel");
|
||||
if key == nil {
|
||||
return Ok(false);
|
||||
}
|
||||
let panel: BOOL = msg_send![key, isKindOfClass: class!(NSSavePanel)];
|
||||
anyhow::ensure!(panel != NO, "the key window is not a file panel");
|
||||
if panel == NO {
|
||||
return Ok(false);
|
||||
}
|
||||
let _: () = msg_send![key, cancel: nil];
|
||||
}
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn cancel_panel() -> Result<()> {
|
||||
fn cancel_panel() -> Result<bool> {
|
||||
anyhow::bail!("file panels need macOS")
|
||||
}
|
||||
|
||||
|
||||
+63
-30
@@ -5,9 +5,9 @@ use ely_gpui_component::{
|
||||
theme::{ActiveTheme, ControlSize, Mode, Radius, TextSize, Theme},
|
||||
};
|
||||
use gpui::{
|
||||
Bounds, Context, FocusHandle, FontWeight, InteractiveElement, IntoElement, ParentElement,
|
||||
Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled, Subscription,
|
||||
Window, div, point, prelude::*, px,
|
||||
AnyElement, App, Bounds, Context, FocusHandle, FontWeight, InteractiveElement, IntoElement,
|
||||
ParentElement, Pixels, Render, ScrollHandle, SharedString, StatefulInteractiveElement, Styled,
|
||||
Subscription, Window, div, point, prelude::*, px,
|
||||
};
|
||||
|
||||
use crate::pages;
|
||||
@@ -26,11 +26,18 @@ pub struct Gallery {
|
||||
choice: Choice,
|
||||
focus: FocusHandle,
|
||||
scroll: ScrollHandle,
|
||||
/// Narrow check width, if any.
|
||||
narrow: Option<Pixels>,
|
||||
_appearance: Subscription,
|
||||
}
|
||||
|
||||
impl Gallery {
|
||||
pub fn new(page: usize, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
pub fn new(
|
||||
page: usize,
|
||||
narrow: Option<f32>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let appearance = cx.observe_window_appearance(window, |gallery, window, cx| {
|
||||
if gallery.choice == Choice::System {
|
||||
Theme::set_mode(window.appearance().into(), cx);
|
||||
@@ -44,6 +51,7 @@ impl Gallery {
|
||||
choice: Choice::Light,
|
||||
focus,
|
||||
scroll: ScrollHandle::new(),
|
||||
narrow: narrow.map(px),
|
||||
_appearance: appearance,
|
||||
}
|
||||
}
|
||||
@@ -174,6 +182,7 @@ impl Gallery {
|
||||
];
|
||||
let colors = &cx.theme().colors;
|
||||
div()
|
||||
.flex_none()
|
||||
.flex()
|
||||
.gap_0p5()
|
||||
.p_0p5()
|
||||
@@ -198,6 +207,24 @@ impl Gallery {
|
||||
}
|
||||
}
|
||||
|
||||
impl Gallery {
|
||||
/// Header and body; a padded card when narrow.
|
||||
fn card(&self, header: impl IntoElement, body: AnyElement, cx: &App) -> AnyElement {
|
||||
let content = div().child(header).child(body);
|
||||
match self.narrow {
|
||||
// Width plus padding and hairlines.
|
||||
Some(width) => div()
|
||||
.w(width + px(34.0))
|
||||
.p_4()
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors.border)
|
||||
.child(content)
|
||||
.into_any_element(),
|
||||
None => content.into_any_element(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Gallery {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let page = &pages::ALL[self.page];
|
||||
@@ -229,32 +256,38 @@ impl Render for Gallery {
|
||||
.pt(px(56.0))
|
||||
.pb(px(96.0))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.gap_6()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.text_size(theme.text_size(TextSize::Xxl))
|
||||
.font_weight(FontWeight::SEMIBOLD)
|
||||
.child(page.title),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(theme.text_size(TextSize::Md))
|
||||
.text_color(colors.fg_muted)
|
||||
.child(page.summary),
|
||||
),
|
||||
)
|
||||
.child(switcher),
|
||||
)
|
||||
.child(body),
|
||||
self.card(
|
||||
div()
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.items_start()
|
||||
.justify_between()
|
||||
.gap_6()
|
||||
.child(
|
||||
div()
|
||||
.flex_1()
|
||||
.min_w(px(200.0))
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.child(
|
||||
div()
|
||||
.text_size(theme.text_size(TextSize::Xxl))
|
||||
.font_weight(FontWeight::SEMIBOLD)
|
||||
.child(page.title),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(theme.text_size(TextSize::Md))
|
||||
.text_color(colors.fg_muted)
|
||||
.child(page.summary),
|
||||
),
|
||||
)
|
||||
.child(switcher),
|
||||
body,
|
||||
cx,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -119,7 +119,6 @@ impl RenderOnce for FileChangeCard {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -270,7 +269,6 @@ impl RenderOnce for MultiFileDiffReview {
|
||||
)
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -88,7 +88,6 @@ impl RenderOnce for HumanInputRequest {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
@@ -177,7 +176,6 @@ impl RenderOnce for CostBreakdown {
|
||||
)
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -117,7 +117,6 @@ impl RenderOnce for SandboxStatus {
|
||||
let live = matches!(self.state, SandboxState::Starting | SandboxState::Busy);
|
||||
let color = self.state.color(&colors);
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -84,7 +84,6 @@ impl RenderOnce for MemoryPanel {
|
||||
)
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -137,7 +137,6 @@ impl RenderOnce for PermissionPrompt {
|
||||
})
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -118,7 +118,6 @@ impl RenderOnce for AgentPlan {
|
||||
.filter(|(_, state)| *state == StepState::Done)
|
||||
.count();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
@@ -284,7 +283,6 @@ impl RenderOnce for AgentProgress {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -112,7 +112,6 @@ impl RenderOnce for ToolRegistry {
|
||||
let groups = grouped(&self.tools, &found(&self.tools, &self.query));
|
||||
let empty = groups.is_empty();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
|
||||
@@ -109,7 +109,6 @@ impl RenderOnce for ToolCallCard {
|
||||
.child(text)
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
.border_1()
|
||||
.border_color(colors.border)
|
||||
@@ -217,7 +216,6 @@ impl RenderOnce for ToolCallGroup {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -142,7 +142,6 @@ impl RenderOnce for AgendaView {
|
||||
if owners.is_empty() {
|
||||
return div()
|
||||
.debug_selector(|| "agenda-none".into())
|
||||
.w_full()
|
||||
.py_6()
|
||||
.flex()
|
||||
.justify_center()
|
||||
@@ -172,7 +171,6 @@ impl RenderOnce for AgendaView {
|
||||
})
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.child(
|
||||
sections
|
||||
.selected(picked.read(cx).clone())
|
||||
|
||||
@@ -119,7 +119,6 @@ impl RenderOnce for AllDayRow {
|
||||
});
|
||||
div()
|
||||
.relative()
|
||||
.w_full()
|
||||
.h(pad * 2.0 + pitch * lanes as f32 - gap)
|
||||
.border_b_1()
|
||||
.border_color(theme.colors.border)
|
||||
|
||||
@@ -93,7 +93,6 @@ impl RenderOnce for EventChip {
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.h(theme.calendar().chip)
|
||||
.px_1p5()
|
||||
.rounded(round)
|
||||
|
||||
@@ -420,7 +420,6 @@ impl RenderOnce for CalendarMonthView {
|
||||
.collect();
|
||||
let title = shown.strftime("%B %Y").to_string();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -173,7 +173,6 @@ impl RenderOnce for AttendeeList {
|
||||
})
|
||||
.collect();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
|
||||
@@ -300,7 +300,6 @@ impl RenderOnce for YearView {
|
||||
})
|
||||
.collect();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
|
||||
@@ -74,7 +74,6 @@ impl RenderOnce for QuoteReply {
|
||||
let colors = theme.colors.clone();
|
||||
let line = self.text.lines().next().unwrap_or_default().to_string();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
@@ -261,7 +260,6 @@ impl RenderOnce for MessageEditor {
|
||||
self.on_cancel,
|
||||
);
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -105,7 +105,6 @@ impl RenderOnce for SourceCard {
|
||||
let open = self.on_open;
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.w_full()
|
||||
.p_2p5()
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
.border_1()
|
||||
@@ -222,7 +221,6 @@ impl RenderOnce for SourceList {
|
||||
.take(3)
|
||||
.collect();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -94,7 +94,6 @@ impl RenderOnce for CodeBlock {
|
||||
let lines = code.lines().count();
|
||||
let toggle = open.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
|
||||
@@ -179,7 +179,6 @@ impl RenderOnce for ConversationItem {
|
||||
.id(self.id.clone())
|
||||
.group(group.clone())
|
||||
.track_focus(&focus)
|
||||
.w_full()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
|
||||
+14
-3
@@ -105,7 +105,16 @@ impl RenderOnce for MessageList {
|
||||
div()
|
||||
.relative()
|
||||
.size_full()
|
||||
.child(list(state, move |ix, window, cx| render(ix, window, cx)).size_full())
|
||||
// gpui's list lays rows at content width.
|
||||
.child(
|
||||
list(state, move |ix, window, cx| {
|
||||
div()
|
||||
.w_full()
|
||||
.child(render(ix, window, cx))
|
||||
.into_any_element()
|
||||
})
|
||||
.size_full(),
|
||||
)
|
||||
.child(
|
||||
canvas(
|
||||
move |_, window, cx| {
|
||||
@@ -234,8 +243,10 @@ impl RenderOnce for DateSeparator {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
let rule = || div().flex_1().h_px().bg(colors.border);
|
||||
let name = day_name(self.day, self.today);
|
||||
let shown = format!("date-separator-{name}");
|
||||
div()
|
||||
.w_full()
|
||||
.debug_selector(move || shown)
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
@@ -245,7 +256,7 @@ impl RenderOnce for DateSeparator {
|
||||
div()
|
||||
.text_size(theme.text_size(TextSize::Xs))
|
||||
.text_color(colors.fg_subtle)
|
||||
.child(day_name(self.day, self.today)),
|
||||
.child(name),
|
||||
)
|
||||
.child(rule())
|
||||
}
|
||||
|
||||
@@ -72,7 +72,6 @@ impl RenderOnce for ImageMessage {
|
||||
let widest = theme.prose_width().to_pixels(window.rem_size());
|
||||
let open = self.on_open;
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
@@ -235,7 +234,6 @@ impl RenderOnce for FileMessage {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.max_w(theme.prose_width())
|
||||
.flex()
|
||||
.items_center()
|
||||
@@ -339,7 +337,6 @@ impl RenderOnce for LinkPreviewCard {
|
||||
let open = self.on_open;
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.w_full()
|
||||
.max_w(theme.container_width(ContainerSize::Sm))
|
||||
.flex()
|
||||
.gap_3()
|
||||
|
||||
@@ -81,7 +81,6 @@ impl RenderOnce for ParameterPanel {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
@@ -274,7 +273,6 @@ impl RenderOnce for SystemPromptEditor {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -97,7 +97,6 @@ impl RenderOnce for AudioMessage {
|
||||
let playing = self.playing;
|
||||
let tall = theme.control_height(ControlSize::Md);
|
||||
div()
|
||||
.w_full()
|
||||
.max_w(theme.prose_width())
|
||||
.flex()
|
||||
.items_center()
|
||||
|
||||
@@ -62,7 +62,6 @@ impl RenderOnce for ProjectList {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_0p5()
|
||||
@@ -163,7 +162,6 @@ impl RenderOnce for ProjectKnowledgePanel {
|
||||
let used: u64 = self.files.iter().map(|(_, bytes)| bytes).sum();
|
||||
let share = used as f32 / self.capacity as f32;
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -164,7 +164,6 @@ impl RenderOnce for WebResultCard {
|
||||
let open = self.on_open;
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_0p5()
|
||||
@@ -273,7 +272,6 @@ impl RenderOnce for DocumentChunkPreview {
|
||||
..HighlightStyle::default()
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1p5()
|
||||
|
||||
@@ -112,7 +112,6 @@ impl RenderOnce for RateLimitNotice {
|
||||
None => countdown,
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
@@ -198,7 +197,6 @@ impl RenderOnce for FeedbackForm {
|
||||
let ready = !self.picked.is_empty() || !self.note.read(cx).text().trim().is_empty();
|
||||
let (pick, send) = (self.on_pick, self.on_send);
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
@@ -356,7 +354,6 @@ impl RenderOnce for ThinkingBlock {
|
||||
ThinkingDuration::new(self.took).into_any_element()
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
+28
-1
@@ -13,7 +13,8 @@ mod history;
|
||||
mod welcome;
|
||||
|
||||
use super::{
|
||||
ImageMessage, MessageAvatar, MessageBubble, MessageEditor, MessageList, Role, ThinkingBlock,
|
||||
DateSeparator, ImageMessage, MessageAvatar, MessageBubble, MessageEditor, MessageList, Role,
|
||||
ThinkingBlock,
|
||||
};
|
||||
use crate::{forms, forms::TextInput, theme::Theme};
|
||||
|
||||
@@ -361,3 +362,29 @@ fn the_reasoning_opens_from_the_keyboard(cx: &mut TestAppContext) {
|
||||
"Tab and Space open it: {closed:?} to {open:?}"
|
||||
);
|
||||
}
|
||||
|
||||
struct Dated;
|
||||
|
||||
impl Render for Dated {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
let day = jiff::civil::date(2026, 9, 28);
|
||||
div()
|
||||
.size_full()
|
||||
.child(MessageList::new("dated", 1, move |_, _, _| {
|
||||
DateSeparator::new(day, day).into_any_element()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// A row no wider than its words still spans the list.
|
||||
#[gpui::test]
|
||||
fn a_narrow_row_spans_the_list(cx: &mut TestAppContext) {
|
||||
cx.update(Theme::init);
|
||||
let (_, cx) = cx.add_window_view(|_, _| Dated);
|
||||
settle(cx);
|
||||
let row = cx
|
||||
.debug_bounds("date-separator-Today")
|
||||
.expect("the separator draws");
|
||||
let width = cx.update(|window, _| window.viewport_size().width);
|
||||
assert_eq!(row.size.width, width);
|
||||
}
|
||||
@@ -180,7 +180,6 @@ impl RenderOnce for FollowUpSuggestions {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.children(
|
||||
@@ -253,7 +252,6 @@ impl RenderOnce for CapabilityCards {
|
||||
let theme = cx.theme();
|
||||
let colors = theme.colors.clone();
|
||||
div()
|
||||
.w_full()
|
||||
.grid()
|
||||
.grid_cols(3)
|
||||
.gap_3()
|
||||
|
||||
@@ -12,12 +12,28 @@ fn open(
|
||||
forms::bind_keys(cx);
|
||||
crate::documents::bind_keys(cx);
|
||||
});
|
||||
let (editor, cx) = cx.add_window_view(|window, cx| BlockEditor::new(blocks, window, cx));
|
||||
let (full, cx) =
|
||||
cx.add_window_view(|window, cx| Full(cx.new(|cx| BlockEditor::new(blocks, window, cx))));
|
||||
let editor = full.read_with(cx, |full, _| full.0.clone());
|
||||
cx.update(|window, _| window.activate_window());
|
||||
settle(cx);
|
||||
(editor, cx)
|
||||
}
|
||||
|
||||
/// The editor as a window's content, in the box that fills the window.
|
||||
struct Full(Entity<BlockEditor>);
|
||||
|
||||
impl gpui::Render for Full {
|
||||
fn render(
|
||||
&mut self,
|
||||
_: &mut gpui::Window,
|
||||
_: &mut gpui::Context<Self>,
|
||||
) -> impl gpui::IntoElement {
|
||||
use gpui::{ParentElement, Styled};
|
||||
gpui::div().size_full().child(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn settle(cx: &mut VisualTestContext) {
|
||||
cx.run_until_parked();
|
||||
cx.update(|window, _| window.refresh());
|
||||
|
||||
@@ -253,7 +253,6 @@ impl Render for BlockEditor {
|
||||
let start = cx.entity();
|
||||
div()
|
||||
.id(id.clone())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.capture_action(cx.listener(|editor, _: &Undo, window, cx| {
|
||||
|
||||
@@ -108,7 +108,6 @@ impl RenderOnce for PageCover {
|
||||
.id(self.id.clone())
|
||||
.group(group.clone())
|
||||
.relative()
|
||||
.w_full()
|
||||
.h(theme.page_cover())
|
||||
.child(
|
||||
Image::new((self.id.clone(), "picture"), source(&self.source))
|
||||
|
||||
+16
-20
@@ -208,27 +208,23 @@ impl RenderOnce for ReadingProgress {
|
||||
let theme = cx.theme();
|
||||
let (track, bar) = (theme.colors.border, theme.colors.accent);
|
||||
let scroll = self.scroll;
|
||||
div()
|
||||
.w_full()
|
||||
.h(theme.progress_thickness())
|
||||
.bg(track)
|
||||
.child(
|
||||
canvas(
|
||||
|_, _, _| {},
|
||||
move |bounds, _, window, _| {
|
||||
let share = read_share(
|
||||
f32::from(scroll.offset().y),
|
||||
f32::from(scroll.max_offset().height),
|
||||
);
|
||||
let filled = Bounds::new(
|
||||
bounds.origin,
|
||||
size(bounds.size.width * share, bounds.size.height),
|
||||
);
|
||||
window.paint_quad(fill(filled, bar));
|
||||
},
|
||||
)
|
||||
.size_full(),
|
||||
div().h(theme.progress_thickness()).bg(track).child(
|
||||
canvas(
|
||||
|_, _, _| {},
|
||||
move |bounds, _, window, _| {
|
||||
let share = read_share(
|
||||
f32::from(scroll.offset().y),
|
||||
f32::from(scroll.max_offset().height),
|
||||
);
|
||||
let filled = Bounds::new(
|
||||
bounds.origin,
|
||||
size(bounds.size.width * share, bounds.size.height),
|
||||
);
|
||||
window.paint_quad(fill(filled, bar));
|
||||
},
|
||||
)
|
||||
.size_full(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,7 +166,6 @@ impl RenderOnce for Banner {
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.min_h(theme.control_height(ControlSize::Lg))
|
||||
.py_1()
|
||||
.px_10()
|
||||
|
||||
@@ -212,7 +212,6 @@ impl RenderOnce for DuplicateFinder {
|
||||
});
|
||||
div()
|
||||
.debug_selector(|| "duplicate-finder".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
|
||||
@@ -278,7 +278,6 @@ impl RenderOnce for FileExplorer {
|
||||
};
|
||||
div()
|
||||
.debug_selector(|| "file-explorer".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -177,7 +177,6 @@ impl RenderOnce for FileGrid {
|
||||
.id(self.id)
|
||||
.debug_selector(|| "file-grid".into())
|
||||
.relative()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.gap(gap)
|
||||
|
||||
@@ -174,7 +174,6 @@ impl RenderOnce for FileOperationProgress {
|
||||
);
|
||||
div()
|
||||
.debug_selector(|| "file-operation".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -122,7 +122,6 @@ impl RenderOnce for FilePreview {
|
||||
};
|
||||
div()
|
||||
.debug_selector(|| "file-preview".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -140,7 +140,6 @@ impl RenderOnce for RecentFiles {
|
||||
});
|
||||
div()
|
||||
.debug_selector(|| "recent-files".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
|
||||
@@ -122,7 +122,6 @@ impl RenderOnce for FileSearch {
|
||||
};
|
||||
div()
|
||||
.debug_selector(|| "file-search".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -80,7 +80,6 @@ impl RenderOnce for TransferQueue {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.debug_selector(|| "transfer-queue".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
@@ -192,7 +191,6 @@ impl RenderOnce for DownloadManager {
|
||||
});
|
||||
div()
|
||||
.debug_selector(|| "download-manager".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
|
||||
@@ -138,13 +138,9 @@ impl RenderOnce for FontPicker {
|
||||
if let Some(on_change) = self.on_change {
|
||||
combobox = combobox.on_change(move |family, window, cx| on_change(family, window, cx));
|
||||
}
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.child(combobox)
|
||||
.when_some(self.selected, |picker, family| {
|
||||
div().flex().flex_col().gap_2().child(combobox).when_some(
|
||||
self.selected,
|
||||
|picker, family| {
|
||||
picker.child(
|
||||
div()
|
||||
.font_family(family)
|
||||
@@ -152,7 +148,8 @@ impl RenderOnce for FontPicker {
|
||||
.text_color(theme.colors.fg_muted)
|
||||
.child("The quick brown fox jumps over the lazy dog"),
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,7 +173,6 @@ impl RenderOnce for CodeInput {
|
||||
"a code input is one line; JsonInput takes several"
|
||||
);
|
||||
div()
|
||||
.w_full()
|
||||
.font_family(cx.theme().mono_family.clone())
|
||||
.child(Input::new(&self.state).size(self.size))
|
||||
}
|
||||
@@ -205,7 +204,6 @@ impl RenderOnce for JsonInput {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
.font_family(theme.mono_family.clone())
|
||||
|
||||
@@ -275,7 +275,6 @@ impl RenderOnce for GradientEditor {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
|
||||
@@ -375,7 +375,6 @@ impl RenderOnce for ColorPicker {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.w_full()
|
||||
.px(knob * 0.5)
|
||||
.pt(knob * 0.5)
|
||||
.child(plane)
|
||||
|
||||
@@ -214,7 +214,6 @@ impl RenderOnce for Combobox {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.relative()
|
||||
.w_full()
|
||||
.capture_action(move |_: &Up, _, cx| {
|
||||
if open {
|
||||
cx.stop_propagation();
|
||||
|
||||
@@ -276,7 +276,6 @@ impl RenderOnce for ExpressionInput {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
div().font_family(mono).child(
|
||||
Input::new(&self.state)
|
||||
|
||||
@@ -58,7 +58,6 @@ impl RenderOnce for HotkeyInput {
|
||||
.items_center()
|
||||
.justify_between()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.h(theme.control_height(ControlSize::Md))
|
||||
.px(theme.control_padding(ControlSize::Md))
|
||||
.rounded(theme.radius(Radius::Md))
|
||||
|
||||
@@ -129,7 +129,6 @@ impl RenderOnce for InlineEdit {
|
||||
let (escape, enter) = (state.clone(), state.clone());
|
||||
return div()
|
||||
.id(self.id)
|
||||
.w_full()
|
||||
.on_key_down(move |event, window, cx| {
|
||||
if event.keystroke.key == "escape" {
|
||||
cx.stop_propagation();
|
||||
|
||||
@@ -270,7 +270,6 @@ impl RenderOnce for PhoneInput {
|
||||
);
|
||||
div()
|
||||
.relative()
|
||||
.w_full()
|
||||
.child(MaskedInput::new(self.id.clone(), &self.state, mask).prefix(code))
|
||||
.child(
|
||||
canvas(
|
||||
|
||||
@@ -153,7 +153,6 @@ impl RenderOnce for MultiSelect {
|
||||
.flex_wrap()
|
||||
.items_center()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.min_h(theme.control_height(self.size))
|
||||
.pl_1()
|
||||
.pr(theme.control_padding(self.size))
|
||||
|
||||
@@ -158,7 +158,6 @@ impl RenderOnce for FieldArray {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.children(lines)
|
||||
.child(
|
||||
div().flex().child(
|
||||
|
||||
@@ -136,7 +136,6 @@ impl RenderOnce for RegexInput {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(
|
||||
div().font_family(mono).child(
|
||||
Input::new(&self.state)
|
||||
|
||||
@@ -81,7 +81,6 @@ impl RenderOnce for SearchInput {
|
||||
});
|
||||
div()
|
||||
.relative()
|
||||
.w_full()
|
||||
.child(
|
||||
Input::new(&self.state)
|
||||
.size(self.size)
|
||||
|
||||
@@ -260,7 +260,6 @@ impl RenderOnce for SignaturePad {
|
||||
.flex_col()
|
||||
.items_end()
|
||||
.gap_1()
|
||||
.w_full()
|
||||
.child(pad)
|
||||
.child(
|
||||
Button::new((self.id, "clear"), "Clear")
|
||||
|
||||
@@ -55,7 +55,6 @@ impl RenderOnce for Form {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_8()
|
||||
.w_full()
|
||||
.when_some(self.on_submit, |form, submit| {
|
||||
form.capture_action(move |_: &Submit, window, cx| {
|
||||
cx.stop_propagation();
|
||||
@@ -102,7 +101,6 @@ impl RenderOnce for FormSection {
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
@@ -304,7 +302,6 @@ impl RenderOnce for InlineForm {
|
||||
.flex_wrap()
|
||||
.items_end()
|
||||
.gap_3()
|
||||
.w_full()
|
||||
.children(self.children)
|
||||
}
|
||||
}
|
||||
|
||||
+20
-16
@@ -1,6 +1,6 @@
|
||||
use gpui::{
|
||||
Context, Hsla, IntoElement, Modifiers, MouseButton, Render, TestAppContext, Window, point, px,
|
||||
rgb, rgba,
|
||||
Context, Hsla, IntoElement, Modifiers, MouseButton, ParentElement, Render, Styled,
|
||||
TestAppContext, Window, div, point, px, rgb, rgba,
|
||||
};
|
||||
|
||||
use super::setup;
|
||||
@@ -13,14 +13,16 @@ struct Picked {
|
||||
impl Render for Picked {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
ColorPicker::new("picked", self.color)
|
||||
.opaque()
|
||||
.on_change(move |color, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.color = color;
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div()
|
||||
.size_full()
|
||||
.child(ColorPicker::new("picked", self.color).opaque().on_change(
|
||||
move |color, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.color = color;
|
||||
cx.notify();
|
||||
})
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +43,14 @@ struct Gradient {
|
||||
impl Render for Gradient {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
GradientEditor::new("gradient", self.stops.clone()).on_change(move |next, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.stops = next.to_vec();
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div().size_full().child(
|
||||
GradientEditor::new("gradient", self.stops.clone()).on_change(move |next, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.stops = next.to_vec();
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,12 +48,16 @@ struct Pad {
|
||||
impl Render for Pad {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
SignaturePad::new("pad", self.strokes.clone()).on_change(move |next, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.strokes = next.to_vec();
|
||||
cx.notify();
|
||||
})
|
||||
})
|
||||
div()
|
||||
.size_full()
|
||||
.child(
|
||||
SignaturePad::new("pad", self.strokes.clone()).on_change(move |next, _, cx| {
|
||||
view.update(cx, |view, cx| {
|
||||
view.strokes = next.to_vec();
|
||||
cx.notify();
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -358,7 +358,6 @@ impl Render for TextInput {
|
||||
.id("ely-text-input")
|
||||
.key_context(CONTEXT)
|
||||
.track_focus(&self.focus)
|
||||
.w_full()
|
||||
.cursor(if self.disabled {
|
||||
CursorStyle::OperationNotAllowed
|
||||
} else {
|
||||
|
||||
@@ -181,7 +181,6 @@ impl RenderOnce for TransferList {
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.w_full()
|
||||
.child(side("left", available_title, available, left, cx))
|
||||
.child(buttons)
|
||||
.child(side("right", chosen_title, picked, right, cx))
|
||||
|
||||
@@ -152,7 +152,6 @@ impl RenderOnce for AudioGenerationPlayer {
|
||||
}
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -80,7 +80,6 @@ impl RenderOnce for MaskBrush {
|
||||
})
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -168,7 +168,6 @@ impl RenderOnce for ABCompareView {
|
||||
.collect(),
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -256,7 +256,7 @@ impl RenderOnce for ModelDownloadManager {
|
||||
)
|
||||
.children(bar)
|
||||
});
|
||||
div().w_full().flex().flex_col().children(rows)
|
||||
div().flex().flex_col().children(rows)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -313,12 +313,6 @@ impl RenderOnce for EmbeddingVisualizer {
|
||||
cx.notify();
|
||||
})
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.child(legend)
|
||||
.child(area)
|
||||
div().flex().flex_col().gap_3().child(legend).child(area)
|
||||
}
|
||||
}
|
||||
@@ -207,6 +207,6 @@ impl RenderOnce for GenerationGrid {
|
||||
.children((0..short).map(|_| div().flex_1())),
|
||||
);
|
||||
}
|
||||
div().w_full().flex().flex_col().gap_2().children(rows)
|
||||
div().flex().flex_col().gap_2().children(rows)
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,11 @@ impl HardwareMonitor {
|
||||
impl RenderOnce for HardwareMonitor {
|
||||
fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let spark = cx.theme().spark_size();
|
||||
div().w_full().flex().flex_col().gap_3().children(
|
||||
self.readings.into_iter().enumerate().map(|(ix, reading)| {
|
||||
div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
.children(self.readings.into_iter().enumerate().map(|(ix, reading)| {
|
||||
div()
|
||||
.flex()
|
||||
.items_end()
|
||||
@@ -71,7 +74,6 @@ impl RenderOnce for HardwareMonitor {
|
||||
.child(Sparkline::new(reading.history).area()),
|
||||
)
|
||||
})
|
||||
}),
|
||||
)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,6 @@ impl RenderOnce for ModelCard {
|
||||
})
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
@@ -260,7 +259,6 @@ impl RenderOnce for ModelStatus {
|
||||
_ => None,
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -154,7 +154,6 @@ impl RenderOnce for PromptPlayground {
|
||||
)
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -199,7 +199,6 @@ impl RenderOnce for PromptEnhancer {
|
||||
),
|
||||
);
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.child(Input::new(&self.field))
|
||||
|
||||
@@ -76,7 +76,6 @@ impl RenderOnce for AspectRatioPicker {
|
||||
let colors = theme.colors.clone();
|
||||
let glyph = theme.icon_size(IconSize::Md);
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.gap_2()
|
||||
|
||||
@@ -75,7 +75,6 @@ impl RenderOnce for StylePresetPicker {
|
||||
let colors = theme.colors.clone();
|
||||
let side = theme.generative().preset;
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.gap_3()
|
||||
|
||||
@@ -185,7 +185,6 @@ impl RenderOnce for FineTuneJobCard {
|
||||
.h(theme.generative().loss)
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -120,7 +120,6 @@ impl RenderOnce for VariationPicker {
|
||||
})
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -84,7 +84,6 @@ impl RenderOnce for PromptDiff {
|
||||
Some((range, style))
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.text_size(theme.text_size(TextSize::Sm))
|
||||
.text_color(colors.fg)
|
||||
.child(StyledText::new(text).with_highlights(styles))
|
||||
@@ -251,7 +250,6 @@ impl RenderOnce for PromptVersionHistory {
|
||||
},
|
||||
);
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_wrap()
|
||||
.items_start()
|
||||
|
||||
@@ -287,7 +287,6 @@ impl RenderOnce for VideoGenerationTimeline {
|
||||
})
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -224,6 +224,6 @@ impl RenderOnce for TTSVoicePicker {
|
||||
.children(voice.tags.into_iter().map(Badge::new)),
|
||||
)
|
||||
});
|
||||
div().w_full().flex().flex_col().gap_0p5().children(rows)
|
||||
div().flex().flex_col().gap_0p5().children(rows)
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,6 @@ impl RenderOnce for SimpleGrid {
|
||||
let columns = columns_for(*width.read(cx), self.min, self.gap);
|
||||
self.base
|
||||
.relative()
|
||||
.w_full()
|
||||
.grid()
|
||||
.grid_cols(columns)
|
||||
.gap(self.gap)
|
||||
|
||||
@@ -115,6 +115,6 @@ impl RenderOnce for Sections {
|
||||
}),
|
||||
)
|
||||
});
|
||||
div().w_full().flex().flex_col().gap_4().children(sections)
|
||||
div().flex().flex_col().gap_4().children(sections)
|
||||
}
|
||||
}
|
||||
+7
-10
@@ -2,7 +2,7 @@ use std::rc::Rc;
|
||||
|
||||
use gpui::{
|
||||
App, ElementId, InteractiveElement, IntoElement, ParentElement, RenderOnce, SharedString,
|
||||
Styled, Window, div,
|
||||
Window, div,
|
||||
};
|
||||
|
||||
use super::labels::hue_dot;
|
||||
@@ -173,14 +173,11 @@ impl RenderOnce for MailboxList {
|
||||
sections.section(title.clone(), rows)
|
||||
},
|
||||
);
|
||||
div()
|
||||
.debug_selector(|| "mailbox-list".into())
|
||||
.w_full()
|
||||
.child(
|
||||
sections
|
||||
.selected(self.open)
|
||||
.on_select(open.clone())
|
||||
.on_activate(open),
|
||||
)
|
||||
div().debug_selector(|| "mailbox-list".into()).child(
|
||||
sections
|
||||
.selected(self.open)
|
||||
.on_select(open.clone())
|
||||
.on_activate(open),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -330,7 +330,6 @@ impl RenderOnce for MailComposer {
|
||||
});
|
||||
div()
|
||||
.debug_selector(|| "mail-composer".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -240,7 +240,6 @@ impl RenderOnce for LabelPicker {
|
||||
let (up, down, chosen) = (finding.clone(), finding.clone(), finding);
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_2()
|
||||
|
||||
@@ -290,7 +290,6 @@ impl RenderOnce for MailList {
|
||||
if self.mails.is_empty() {
|
||||
return div()
|
||||
.debug_selector(|| "mail-none".into())
|
||||
.w_full()
|
||||
.px_3()
|
||||
.py_4()
|
||||
.text_size(theme.text_size(TextSize::Sm))
|
||||
@@ -320,7 +319,6 @@ impl RenderOnce for MailList {
|
||||
let (id, on_select) = (self.id.clone(), self.on_select);
|
||||
div()
|
||||
.debug_selector(|| "mail-list".into())
|
||||
.w_full()
|
||||
.child(
|
||||
list.selected(self.selected)
|
||||
.on_change(move |keys, window, cx| {
|
||||
|
||||
@@ -344,7 +344,6 @@ impl RenderOnce for MailReader {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.debug_selector(|| "mail-reader".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_4()
|
||||
|
||||
@@ -124,7 +124,6 @@ impl RenderOnce for SignatureEditor {
|
||||
let words = input.read(cx).text().to_string();
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -199,7 +199,6 @@ impl RenderOnce for MailThreadView {
|
||||
);
|
||||
div()
|
||||
.debug_selector(|| "mail-thread".into())
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -428,7 +428,6 @@ impl RenderOnce for ImageAnnotator {
|
||||
})
|
||||
});
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -145,7 +145,6 @@ impl RenderOnce for AudioWaveform {
|
||||
.id(self.id.clone())
|
||||
.debug_selector(|| "audio-waveform".into())
|
||||
.relative()
|
||||
.w_full()
|
||||
.h(sizes.waveform)
|
||||
.border_1()
|
||||
.border_color(transparent_black())
|
||||
@@ -226,7 +225,6 @@ impl RenderOnce for AudioSpectrum {
|
||||
let (color, round) = (theme.colors.fg_muted, theme.radius(Radius::Sm));
|
||||
div()
|
||||
.debug_selector(|| "audio-spectrum".into())
|
||||
.w_full()
|
||||
.h(theme.media().waveform)
|
||||
.flex()
|
||||
.items_end()
|
||||
|
||||
@@ -92,7 +92,6 @@ impl RenderOnce for VolumeControl {
|
||||
None => slider,
|
||||
};
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
|
||||
@@ -200,7 +200,6 @@ impl RenderOnce for ImageCropper {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.relative()
|
||||
.w_full()
|
||||
.p(side / 2.0)
|
||||
.border_1()
|
||||
.border_color(transparent_black())
|
||||
|
||||
@@ -251,7 +251,6 @@ impl RenderOnce for AudioPlayer {
|
||||
};
|
||||
let left = format!("-{}", clock(self.length - self.at));
|
||||
div()
|
||||
.w_full()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.gap_3()
|
||||
|
||||
@@ -255,7 +255,6 @@ impl RenderOnce for MicLevelMeter {
|
||||
let (unlit, round) = (colors.border, theme.radius(Radius::Sm));
|
||||
div()
|
||||
.debug_selector(|| "mic-level".into())
|
||||
.w_full()
|
||||
.h(theme.meter_track())
|
||||
.flex()
|
||||
.gap_0p5()
|
||||
|
||||
@@ -264,7 +264,6 @@ impl RenderOnce for Scrubber {
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.debug_selector(|| "scrubber".into())
|
||||
.w_full()
|
||||
.px(thumb / 2.0)
|
||||
.rounded(theme.radius(Radius::Sm))
|
||||
.border_1()
|
||||
|
||||
@@ -251,7 +251,6 @@ impl RenderOnce for VideoThumbnailStrip {
|
||||
let (pressed_strip, moved_strip) = (strip.clone(), strip.clone());
|
||||
div()
|
||||
.id(self.id.clone())
|
||||
.w_full()
|
||||
.px(handle / 2.0)
|
||||
.border_1()
|
||||
.border_color(transparent_black())
|
||||
|
||||
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user