Fix cursor focus and selection in input box, add AWS env var warning box, and remove AWS Bedrock login banner

This commit is contained in:
2026-07-02 14:54:15 -05:00
parent 4770ac06b5
commit 3769646ca6
1194 changed files with 5312 additions and 8032 deletions
@@ -274,3 +274,5 @@ impl From<Option<AccessibilityContent>> for ActionAccessibilityContent {
}
}
}
pub type WarpA11yRole = GalaxyA11yRole;
+1 -1
View File
@@ -97,5 +97,5 @@ impl Element for Clipped {
}
#[cfg(test)]
#[path = "clipped_tests.rs"]
#[path = "clipped_test.rs"]
mod tests;
@@ -467,5 +467,5 @@ impl ScrollableElement for ClippedScrollable {
}
#[cfg(test)]
#[path = "clipped_scrollable_tests.rs"]
#[path = "clipped_scrollable_test.rs"]
mod tests;
@@ -445,5 +445,5 @@ impl SelectableElement for Container {
}
#[cfg(test)]
#[path = "container_tests.rs"]
#[path = "container_test.rs"]
mod tests;
@@ -367,5 +367,5 @@ impl Element for EventHandler {
}
#[cfg(test)]
#[path = "event_handler_tests.rs"]
#[path = "event_handler_test.rs"]
mod tests;
@@ -996,5 +996,5 @@ impl SelectableElement for Expanded {
}
#[cfg(test)]
#[path = "mod_tests.rs"]
#[path = "mod_test.rs"]
mod tests;
@@ -605,5 +605,5 @@ impl RunBuilder {
}
#[cfg(test)]
#[path = "wrap_tests.rs"]
#[path = "wrap_test.rs"]
mod tests;
@@ -1,24 +1,24 @@
use super::{
FormattedTextElement, FrameMouseHandlers, HeadingFontSizeMultipliers, HighlightedHyperlink,
HyperlinkSupport, LaidOutTextFrame, SecretRange,
};
use crate::text::BlockHeaderSize;
use crate::{
elements::{Point, SelectableElement, ZIndex},
fonts::FamilyId,
text_layout::TextFrame,
};
use markdown_parser::{FormattedText, FormattedTextFragment, FormattedTextLine};
use pathfinder_color::ColorU;
use pathfinder_geometry::{rect::RectF, vector::vec2f};
use std::borrow::Cow;
use std::cell::RefCell;
use std::ops::Range;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use markdown_parser::{FormattedText, FormattedTextFragment, FormattedTextLine};
use pathfinder_color::ColorU;
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::vector::vec2f;
use string_offset::ByteOffset;
use super::apply_secret_replacements;
use super::{
apply_secret_replacements, FormattedTextElement, FrameMouseHandlers,
HeadingFontSizeMultipliers, HighlightedHyperlink, HyperlinkSupport, LaidOutTextFrame,
SecretRange,
};
use crate::elements::{Point, SelectableElement, ZIndex};
use crate::fonts::FamilyId;
use crate::text::BlockHeaderSize;
use crate::text_layout::TextFrame;
#[test]
fn test_default_heading_font_size_multipliers() {
+21 -26
View File
@@ -710,27 +710,24 @@ impl Element for Hoverable {
// The double-clicked handler takes precendence. However, we should still fall back to the single-click handler
// on a double-click if there's no double-click handler set.
if matches!(click_count, Some(2)) && self.double_click_handler.is_some() {
let handler = self
.double_click_handler
.as_mut()
.expect("handler should exist");
handler(ctx, app, *position);
ctx.notify();
return true;
} else if click_count.is_some() && self.click_handler.is_some() {
let handler = self.click_handler.as_mut().expect("handler should exist");
handler(ctx, app, *position);
ctx.notify();
return true;
} else if click_count.is_some() && self.click_with_modifiers_handler.is_some() {
let handler = self
.click_with_modifiers_handler
.as_mut()
.expect("handler should exist");
handler(ctx, app, *position, *modifiers);
ctx.notify();
return true;
if matches!(click_count, Some(2)) {
if let Some(handler) = self.double_click_handler.as_mut() {
handler(ctx, app, *position);
ctx.notify();
return true;
}
}
if click_count.is_some() {
if let Some(handler) = self.click_handler.as_mut() {
handler(ctx, app, *position);
ctx.notify();
return true;
}
if let Some(handler) = self.click_with_modifiers_handler.as_mut() {
handler(ctx, app, *position, *modifiers);
ctx.notify();
return true;
}
}
}
Event::MouseMoved {
@@ -742,10 +739,8 @@ impl Element for Hoverable {
return true;
}
}
Event::LeftMouseDragged { .. } => {
if self.suppress_drag && self.state().is_clicked() {
return true;
}
Event::LeftMouseDragged { .. } if self.suppress_drag && self.state().is_clicked() => {
return true;
}
_ => {}
}
@@ -828,5 +823,5 @@ impl SelectableElement for Hoverable {
}
#[cfg(test)]
#[path = "hoverable_tests.rs"]
#[path = "hoverable_test.rs"]
mod tests;
+82
View File
@@ -0,0 +1,82 @@
use std::time::Duration;
use pathfinder_geometry::vector::Vector2F;
use super::{
AfterLayoutContext, AppContext, Element, EventContext, LayoutContext, PaintContext, Point,
SizeConstraint,
};
use crate::event::DispatchedEvent;
/// A wrapper element that triggers periodic repaints at a fixed interval.
///
/// Wrap any child element in `LiveElement` to ensure the view repaints on a
/// timer, which is useful for content that changes over time (e.g. an elapsed
/// duration counter). The repaint cycle is self-sustaining: each `paint` call
/// schedules the next repaint.
pub struct LiveElement {
child: Box<dyn Element>,
repaint_interval: Duration,
size: Option<Vector2F>,
origin: Option<Point>,
}
impl LiveElement {
pub fn new(child: Box<dyn Element>, repaint_interval: Duration) -> Self {
Self {
child,
repaint_interval,
size: None,
origin: None,
}
}
}
impl Element for LiveElement {
fn layout(
&mut self,
constraint: SizeConstraint,
ctx: &mut LayoutContext,
app: &AppContext,
) -> Vector2F {
let size = self.child.layout(constraint, ctx, app);
self.size = Some(size);
size
}
fn after_layout(&mut self, ctx: &mut AfterLayoutContext, app: &AppContext) {
self.child.after_layout(ctx, app);
}
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext) {
self.child.paint(origin, ctx, app);
self.origin = Some(Point::from_vec2f(origin, ctx.scene.z_index()));
ctx.repaint_after(self.repaint_interval);
}
fn size(&self) -> Option<Vector2F> {
self.size
}
fn origin(&self) -> Option<Point> {
self.origin
}
fn dispatch_event(
&mut self,
event: &DispatchedEvent,
ctx: &mut EventContext,
app: &AppContext,
) -> bool {
self.child.dispatch_event(event, ctx, app)
}
fn as_selectable_element(&self) -> Option<&dyn super::SelectableElement> {
self.child.as_selectable_element()
}
#[cfg(any(test, feature = "test-util"))]
fn debug_text_content(&self) -> Option<String> {
self.child.debug_text_content()
}
}
+4 -2
View File
@@ -9,8 +9,6 @@ mod debug;
mod dismiss;
mod drag;
#[cfg(feature = "tui")]
pub mod tui;
pub mod drag_resize;
mod empty;
mod event_handler;
@@ -20,6 +18,7 @@ mod hoverable;
mod icon;
mod image;
mod list;
mod live;
mod min_size;
pub mod new_scrollable;
mod percentage;
@@ -33,6 +32,8 @@ mod size_constraint_switch;
mod stack;
pub mod table;
mod text;
#[cfg(feature = "tui")]
pub mod tui;
mod uniform_list;
mod viewported_list;
@@ -61,6 +62,7 @@ pub use hoverable::*;
pub use icon::*;
pub use image::*;
pub use list::*;
pub use live::*;
pub use min_size::*;
pub use new_scrollable::NewScrollable;
use pathfinder_color::ColorU;
@@ -1586,5 +1586,5 @@ impl ClippedScrollStateHandle {
}
#[cfg(test)]
#[path = "scrollable_tests.rs"]
#[path = "scrollable_test.rs"]
mod tests;
@@ -157,5 +157,5 @@ pub(crate) fn scroll_delta_for_axis(
}
#[cfg(test)]
#[path = "util_tests.rs"]
#[path = "util_test.rs"]
mod tests;
@@ -0,0 +1,154 @@
use super::*;
#[test]
fn test_scroll_delta_for_axis_fully_into_view() {
let mode = ScrollToPositionMode::FullyIntoView;
assert_eq!(
scroll_delta_for_axis(
Axis::Horizontal,
RectF::new(vec2f(100., 0.), vec2f(250., 250.)),
RectF::new(vec2f(400., 50.), vec2f(50., 50.)),
mode,
),
100.
);
assert_eq!(
scroll_delta_for_axis(
Axis::Horizontal,
RectF::new(vec2f(200., 0.), vec2f(250., 250.)),
RectF::new(vec2f(100., 50.), vec2f(50., 50.)),
mode,
),
-100.
);
assert_eq!(
scroll_delta_for_axis(
Axis::Horizontal,
RectF::new(vec2f(100., 0.), vec2f(250., 250.)),
RectF::new(vec2f(325., 50.), vec2f(50., 50.)),
mode,
),
25.
);
assert_eq!(
scroll_delta_for_axis(
Axis::Horizontal,
RectF::new(vec2f(100., 0.), vec2f(250., 250.)),
RectF::new(vec2f(150., 50.), vec2f(50., 50.)),
mode,
),
0.
);
assert_eq!(
scroll_delta_for_axis(
Axis::Horizontal,
RectF::new(vec2f(100., 0.), vec2f(250., 250.)),
RectF::new(vec2f(50., 50.), vec2f(350., 50.)),
mode,
),
0.
);
}
#[test]
fn test_scroll_delta_for_axis_top_into_view() {
let mode = ScrollToPositionMode::TopIntoView;
// --- Element LARGER than the viewport ---
// Element taller than viewport, below viewport: align top with
// viewport top.
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 100.), vec2f(250., 250.)),
RectF::new(vec2f(50., 400.), vec2f(50., 300.)),
mode,
),
300.
);
// Element taller than viewport, above viewport: align top with
// viewport top.
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 200.), vec2f(250., 250.)),
RectF::new(vec2f(50., 100.), vec2f(50., 300.)),
mode,
),
-100.
);
// Element taller than viewport, top at viewport top: align top
// (delta = 0).
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 100.), vec2f(250., 250.)),
RectF::new(vec2f(50., 100.), vec2f(50., 300.)),
mode,
),
0.
);
// Element taller than viewport, top visible but bottom extends
// past: align top with viewport top (shows max content from top).
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 100.), vec2f(250., 250.)),
RectF::new(vec2f(50., 200.), vec2f(50., 300.)),
mode,
),
100.
);
// Element taller than viewport, spans entire viewport (top above,
// bottom below): align top with viewport top.
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 100.), vec2f(250., 250.)),
RectF::new(vec2f(50., 50.), vec2f(50., 400.)),
mode,
),
-50.
);
// --- Element FITS in the viewport (delegates to FullyIntoView) ---
// Small element below viewport: scroll down (bottom to viewport
// bottom).
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 100.), vec2f(250., 250.)),
RectF::new(vec2f(50., 400.), vec2f(50., 50.)),
mode,
),
100.
);
// Small element above viewport: scroll up (top to viewport top).
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 200.), vec2f(250., 250.)),
RectF::new(vec2f(50., 100.), vec2f(50., 50.)),
mode,
),
-100.
);
// Small element fully visible: no scroll.
assert_eq!(
scroll_delta_for_axis(
Axis::Vertical,
RectF::new(vec2f(0., 100.), vec2f(250., 250.)),
RectF::new(vec2f(50., 150.), vec2f(50., 50.)),
mode,
),
0.
);
}
@@ -683,5 +683,5 @@ impl Element for Scrollable {
}
#[cfg(test)]
#[path = "scrollable_tests.rs"]
#[path = "scrollable_test.rs"]
mod tests;
@@ -149,5 +149,5 @@ impl Element for SizeConstraintSwitch {
}
#[cfg(test)]
#[path = "size_constraint_switch_tests.rs"]
#[path = "size_constraint_switch_test.rs"]
mod tests;
@@ -441,5 +441,5 @@ impl Extend<Box<dyn Element>> for Stack {
}
#[cfg(test)]
#[path = "mod_tests.rs"]
#[path = "mod_test.rs"]
mod tests;
@@ -887,5 +887,5 @@ impl Default for PositioningAxis<YAxisAnchor> {
}
#[cfg(test)]
#[path = "offset_positioning_tests.rs"]
#[path = "offset_positioning_test.rs"]
mod tests;
+1 -1
View File
@@ -1436,5 +1436,5 @@ impl PartialClickableElement for Text {
}
#[cfg(test)]
#[path = "text_tests.rs"]
#[path = "text_test.rs"]
mod tests;
@@ -326,5 +326,5 @@ where
}
#[cfg(test)]
#[path = "uniform_list_tests.rs"]
#[path = "uniform_list_test.rs"]
mod tests;
+1
View File
@@ -437,6 +437,7 @@ fn test_binding_description_eq_ignores_dynamic_override() {
mod settings_value_tests {
use settings_value::SettingsValue;
use crate::keymap::Keystroke;
#[test]
fn test_keystroke_to_file_value_is_normalized_string() {
+1 -2
View File
@@ -14,8 +14,6 @@ use std::path::Path;
use std::rc::Rc;
use std::sync::Arc;
use galaxy_util::path::ShellFamily;
use anyhow::Result;
pub use app::AppCallbacks;
use async_task::Runnable;
@@ -24,6 +22,7 @@ pub use file_picker::{
FilePickerCallback, FilePickerConfiguration, FileType, SaveFilePickerCallback,
SaveFilePickerConfiguration,
};
use galaxy_util::path::ShellFamily;
use lazy_static::lazy_static;
use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::vector::{Vector2F, Vector2I};