agent: Refine rules library window design (#31994)

Danilo Leal created

Just polishing up a bit the Rules Library design. I think the most
confusing part here was the icon that was being used to tag a rule as
default; I've heard feedback more than once saying that was confusing,
so I'm now switching to a rather standard star icon, which I'd assume is
well-understood as a "favoriting" affordance.

Release Notes:

- N/A

Change summary

assets/icons/star.svg                     |   0 
assets/icons/star_filled.svg              |   2 
crates/rules_library/src/rules_library.rs | 381 ++++++++++++------------
3 files changed, 184 insertions(+), 199 deletions(-)

Detailed changes

assets/icons/star_filled.svg 🔗

@@ -1 +1,3 @@
-<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M7.22303 0.665992C7.32551 0.419604 7.67454 0.419604 7.77702 0.665992L9.41343 4.60039C9.45663 4.70426 9.55432 4.77523 9.66645 4.78422L13.914 5.12475C14.18 5.14607 14.2878 5.47802 14.0852 5.65162L10.849 8.42374C10.7636 8.49692 10.7263 8.61176 10.7524 8.72118L11.7411 12.866C11.803 13.1256 11.5206 13.3308 11.2929 13.1917L7.6564 10.9705C7.5604 10.9119 7.43965 10.9119 7.34365 10.9705L3.70718 13.1917C3.47945 13.3308 3.19708 13.1256 3.25899 12.866L4.24769 8.72118C4.2738 8.61176 4.23648 8.49692 4.15105 8.42374L0.914889 5.65162C0.712228 5.47802 0.820086 5.14607 1.08608 5.12475L5.3336 4.78422C5.44573 4.77523 5.54342 4.70426 5.58662 4.60039L7.22303 0.665992Z" fill="currentColor"></path></svg>
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">

crates/rules_library/src/rules_library.rs 🔗

@@ -261,6 +261,7 @@ impl PickerDelegate for RulePickerDelegate {
         let rule = self.matches.get(ix)?;
         let default = rule.default;
         let prompt_id = rule.id;
+
         let element = ListItem::new(ix)
             .inset(true)
             .spacing(ListItemSpacing::Sparse)
@@ -272,9 +273,10 @@ impl PickerDelegate for RulePickerDelegate {
                     .child(Label::new(rule.title.clone().unwrap_or("Untitled".into()))),
             )
             .end_slot::<IconButton>(default.then(|| {
-                IconButton::new("toggle-default-rule", IconName::SparkleFilled)
+                IconButton::new("toggle-default-rule", IconName::StarFilled)
                     .toggle_state(true)
                     .icon_color(Color::Accent)
+                    .icon_size(IconSize::Small)
                     .shape(IconButtonShape::Square)
                     .tooltip(Tooltip::text("Remove from Default Rules"))
                     .on_click(cx.listener(move |_, _, _, cx| {
@@ -283,7 +285,7 @@ impl PickerDelegate for RulePickerDelegate {
             }))
             .end_hover_slot(
                 h_flex()
-                    .gap_2()
+                    .gap_1()
                     .child(if prompt_id.is_built_in() {
                         div()
                             .id("built-in-rule")
@@ -299,8 +301,9 @@ impl PickerDelegate for RulePickerDelegate {
                             })
                             .into_any()
                     } else {
-                        IconButton::new("delete-rule", IconName::Trash)
+                        IconButton::new("delete-rule", IconName::TrashAlt)
                             .icon_color(Color::Muted)
+                            .icon_size(IconSize::Small)
                             .shape(IconButtonShape::Square)
                             .tooltip(Tooltip::text("Delete Rule"))
                             .on_click(cx.listener(move |_, _, _, cx| {
@@ -309,16 +312,27 @@ impl PickerDelegate for RulePickerDelegate {
                             .into_any_element()
                     })
                     .child(
-                        IconButton::new("toggle-default-rule", IconName::Sparkle)
+                        IconButton::new("toggle-default-rule", IconName::Star)
                             .toggle_state(default)
-                            .selected_icon(IconName::SparkleFilled)
+                            .selected_icon(IconName::StarFilled)
                             .icon_color(if default { Color::Accent } else { Color::Muted })
+                            .icon_size(IconSize::Small)
                             .shape(IconButtonShape::Square)
-                            .tooltip(Tooltip::text(if default {
-                                "Remove from Default Rules"
-                            } else {
-                                "Add to Default Rules"
-                            }))
+                            .map(|this| {
+                                if default {
+                                    this.tooltip(Tooltip::text("Remove from Default Rules"))
+                                } else {
+                                    this.tooltip(move |window, cx| {
+                                        Tooltip::with_meta(
+                                            "Add to Default Rules",
+                                            None,
+                                            "Always included in every thread.",
+                                            window,
+                                            cx,
+                                        )
+                                    })
+                                }
+                            })
                             .on_click(cx.listener(move |_, _, _, cx| {
                                 cx.emit(RulePickerEvent::ToggledDefault { prompt_id })
                             })),
@@ -1008,216 +1022,180 @@ impl RulesLibrary {
                         .size_full()
                         .relative()
                         .overflow_hidden()
-                        .pl(DynamicSpacing::Base16.rems(cx))
-                        .pt(DynamicSpacing::Base08.rems(cx))
                         .on_click(cx.listener(move |_, _, window, _| {
                             window.focus(&focus_handle);
                         }))
                         .child(
                             h_flex()
                                 .group("active-editor-header")
-                                .pr(DynamicSpacing::Base16.rems(cx))
-                                .pt(DynamicSpacing::Base02.rems(cx))
-                                .pb(DynamicSpacing::Base08.rems(cx))
+                                .pt_2()
+                                .px_2p5()
+                                .gap_2()
                                 .justify_between()
                                 .child(
-                                    h_flex().gap_1().child(
-                                        div()
-                                            .max_w_80()
-                                            .on_action(cx.listener(Self::move_down_from_title))
-                                            .border_1()
-                                            .border_color(transparent_black())
-                                            .rounded_sm()
-                                            .group_hover("active-editor-header", |this| {
-                                                this.border_color(
-                                                    cx.theme().colors().border_variant,
-                                                )
-                                            })
-                                            .child(EditorElement::new(
-                                                &rule_editor.title_editor,
-                                                EditorStyle {
-                                                    background: cx.theme().system().transparent,
-                                                    local_player: cx.theme().players().local(),
-                                                    text: TextStyle {
-                                                        color: cx
-                                                            .theme()
-                                                            .colors()
-                                                            .editor_foreground,
-                                                        font_family: settings
-                                                            .ui_font
-                                                            .family
-                                                            .clone(),
-                                                        font_features: settings
-                                                            .ui_font
-                                                            .features
-                                                            .clone(),
-                                                        font_size: HeadlineSize::Large
-                                                            .rems()
-                                                            .into(),
-                                                        font_weight: settings.ui_font.weight,
-                                                        line_height: relative(
-                                                            settings.buffer_line_height.value(),
-                                                        ),
-                                                        ..Default::default()
-                                                    },
-                                                    scrollbar_width: Pixels::ZERO,
-                                                    syntax: cx.theme().syntax().clone(),
-                                                    status: cx.theme().status().clone(),
-                                                    inlay_hints_style:
-                                                        editor::make_inlay_hints_style(cx),
-                                                    inline_completion_styles:
-                                                        editor::make_suggestion_styles(cx),
-                                                    ..EditorStyle::default()
+                                    div()
+                                        .w_full()
+                                        .on_action(cx.listener(Self::move_down_from_title))
+                                        .border_1()
+                                        .border_color(transparent_black())
+                                        .rounded_sm()
+                                        .group_hover("active-editor-header", |this| {
+                                            this.border_color(cx.theme().colors().border_variant)
+                                        })
+                                        .child(EditorElement::new(
+                                            &rule_editor.title_editor,
+                                            EditorStyle {
+                                                background: cx.theme().system().transparent,
+                                                local_player: cx.theme().players().local(),
+                                                text: TextStyle {
+                                                    color: cx.theme().colors().editor_foreground,
+                                                    font_family: settings.ui_font.family.clone(),
+                                                    font_features: settings
+                                                        .ui_font
+                                                        .features
+                                                        .clone(),
+                                                    font_size: HeadlineSize::Large.rems().into(),
+                                                    font_weight: settings.ui_font.weight,
+                                                    line_height: relative(
+                                                        settings.buffer_line_height.value(),
+                                                    ),
+                                                    ..Default::default()
                                                 },
-                                            )),
-                                    ),
+                                                scrollbar_width: Pixels::ZERO,
+                                                syntax: cx.theme().syntax().clone(),
+                                                status: cx.theme().status().clone(),
+                                                inlay_hints_style: editor::make_inlay_hints_style(
+                                                    cx,
+                                                ),
+                                                inline_completion_styles:
+                                                    editor::make_suggestion_styles(cx),
+                                                ..EditorStyle::default()
+                                            },
+                                        )),
                                 )
                                 .child(
                                     h_flex()
                                         .h_full()
+                                        .flex_shrink_0()
+                                        .gap(DynamicSpacing::Base04.rems(cx))
+                                        .children(rule_editor.token_count.map(|token_count| {
+                                            let token_count: SharedString =
+                                                token_count.to_string().into();
+                                            let label_token_count: SharedString =
+                                                token_count.to_string().into();
+
+                                            div()
+                                                .id("token_count")
+                                                .mr_1()
+                                                .flex_shrink_0()
+                                                .tooltip(move |window, cx| {
+                                                    Tooltip::with_meta(
+                                                        "Token Estimation",
+                                                        None,
+                                                        format!(
+                                                            "Model: {}",
+                                                            model
+                                                                .as_ref()
+                                                                .map(|model| model.name().0)
+                                                                .unwrap_or_default()
+                                                        ),
+                                                        window,
+                                                        cx,
+                                                    )
+                                                })
+                                                .child(
+                                                    Label::new(format!(
+                                                        "{} tokens",
+                                                        label_token_count.clone()
+                                                    ))
+                                                    .color(Color::Muted),
+                                                )
+                                        }))
+                                        .child(if prompt_id.is_built_in() {
+                                            div()
+                                                .id("built-in-rule")
+                                                .child(
+                                                    Icon::new(IconName::FileLock)
+                                                        .color(Color::Muted),
+                                                )
+                                                .tooltip(move |window, cx| {
+                                                    Tooltip::with_meta(
+                                                        "Built-in rule",
+                                                        None,
+                                                        BUILT_IN_TOOLTIP_TEXT,
+                                                        window,
+                                                        cx,
+                                                    )
+                                                })
+                                                .into_any()
+                                        } else {
+                                            IconButton::new("delete-rule", IconName::TrashAlt)
+                                                .icon_size(IconSize::Small)
+                                                .tooltip(move |window, cx| {
+                                                    Tooltip::for_action(
+                                                        "Delete Rule",
+                                                        &DeleteRule,
+                                                        window,
+                                                        cx,
+                                                    )
+                                                })
+                                                .on_click(|_, window, cx| {
+                                                    window
+                                                        .dispatch_action(Box::new(DeleteRule), cx);
+                                                })
+                                                .into_any_element()
+                                        })
                                         .child(
-                                            h_flex()
-                                                .h_full()
-                                                .gap(DynamicSpacing::Base16.rems(cx))
-                                                .child(div()),
+                                            IconButton::new("duplicate-rule", IconName::BookCopy)
+                                                .icon_size(IconSize::Small)
+                                                .tooltip(move |window, cx| {
+                                                    Tooltip::for_action(
+                                                        "Duplicate Rule",
+                                                        &DuplicateRule,
+                                                        window,
+                                                        cx,
+                                                    )
+                                                })
+                                                .on_click(|_, window, cx| {
+                                                    window.dispatch_action(
+                                                        Box::new(DuplicateRule),
+                                                        cx,
+                                                    );
+                                                }),
                                         )
                                         .child(
-                                            h_flex()
-                                                .h_full()
-                                                .gap(DynamicSpacing::Base16.rems(cx))
-                                                .children(rule_editor.token_count.map(
-                                                    |token_count| {
-                                                        let token_count: SharedString =
-                                                            token_count.to_string().into();
-                                                        let label_token_count: SharedString =
-                                                            token_count.to_string().into();
-
-                                                        h_flex()
-                                                            .id("token_count")
-                                                            .tooltip(move |window, cx| {
-                                                                let token_count =
-                                                                    token_count.clone();
-
-                                                                Tooltip::with_meta(
-                                                                    format!(
-                                                                        "{} tokens",
-                                                                        token_count.clone()
-                                                                    ),
-                                                                    None,
-                                                                    format!(
-                                                                        "Model: {}",
-                                                                        model
-                                                                            .as_ref()
-                                                                            .map(|model| model
-                                                                                .name()
-                                                                                .0)
-                                                                            .unwrap_or_default()
-                                                                    ),
-                                                                    window,
-                                                                    cx,
-                                                                )
-                                                            })
-                                                            .child(
-                                                                Label::new(format!(
-                                                                    "{} tokens",
-                                                                    label_token_count.clone()
-                                                                ))
-                                                                .color(Color::Muted),
-                                                            )
-                                                    },
-                                                ))
-                                                .child(if prompt_id.is_built_in() {
-                                                    div()
-                                                        .id("built-in-rule")
-                                                        .child(
-                                                            Icon::new(IconName::FileLock)
-                                                                .color(Color::Muted),
-                                                        )
-                                                        .tooltip(move |window, cx| {
+                                            IconButton::new("toggle-default-rule", IconName::Star)
+                                                .icon_size(IconSize::Small)
+                                                .toggle_state(rule_metadata.default)
+                                                .selected_icon(IconName::StarFilled)
+                                                .icon_color(if rule_metadata.default {
+                                                    Color::Accent
+                                                } else {
+                                                    Color::Muted
+                                                })
+                                                .map(|this| {
+                                                    if rule_metadata.default {
+                                                        this.tooltip(Tooltip::text(
+                                                            "Remove from Default Rules",
+                                                        ))
+                                                    } else {
+                                                        this.tooltip(move |window, cx| {
                                                             Tooltip::with_meta(
-                                                                "Built-in rule",
+                                                                "Add to Default Rules",
                                                                 None,
-                                                                BUILT_IN_TOOLTIP_TEXT,
+                                                                "Always included in every thread.",
                                                                 window,
                                                                 cx,
                                                             )
                                                         })
-                                                        .into_any()
-                                                } else {
-                                                    IconButton::new("delete-rule", IconName::Trash)
-                                                        .size(ButtonSize::Large)
-                                                        .style(ButtonStyle::Transparent)
-                                                        .shape(IconButtonShape::Square)
-                                                        .size(ButtonSize::Large)
-                                                        .tooltip(move |window, cx| {
-                                                            Tooltip::for_action(
-                                                                "Delete Rule",
-                                                                &DeleteRule,
-                                                                window,
-                                                                cx,
-                                                            )
-                                                        })
-                                                        .on_click(|_, window, cx| {
-                                                            window.dispatch_action(
-                                                                Box::new(DeleteRule),
-                                                                cx,
-                                                            );
-                                                        })
-                                                        .into_any_element()
+                                                    }
                                                 })
-                                                .child(
-                                                    IconButton::new(
-                                                        "duplicate-rule",
-                                                        IconName::BookCopy,
-                                                    )
-                                                    .size(ButtonSize::Large)
-                                                    .style(ButtonStyle::Transparent)
-                                                    .shape(IconButtonShape::Square)
-                                                    .size(ButtonSize::Large)
-                                                    .tooltip(move |window, cx| {
-                                                        Tooltip::for_action(
-                                                            "Duplicate Rule",
-                                                            &DuplicateRule,
-                                                            window,
-                                                            cx,
-                                                        )
-                                                    })
-                                                    .on_click(|_, window, cx| {
-                                                        window.dispatch_action(
-                                                            Box::new(DuplicateRule),
-                                                            cx,
-                                                        );
-                                                    }),
-                                                )
-                                                .child(
-                                                    IconButton::new(
-                                                        "toggle-default-rule",
-                                                        IconName::Sparkle,
-                                                    )
-                                                    .style(ButtonStyle::Transparent)
-                                                    .toggle_state(rule_metadata.default)
-                                                    .selected_icon(IconName::SparkleFilled)
-                                                    .icon_color(if rule_metadata.default {
-                                                        Color::Accent
-                                                    } else {
-                                                        Color::Muted
-                                                    })
-                                                    .shape(IconButtonShape::Square)
-                                                    .size(ButtonSize::Large)
-                                                    .tooltip(Tooltip::text(
-                                                        if rule_metadata.default {
-                                                            "Remove from Default Rules"
-                                                        } else {
-                                                            "Add to Default Rules"
-                                                        },
-                                                    ))
-                                                    .on_click(|_, window, cx| {
-                                                        window.dispatch_action(
-                                                            Box::new(ToggleDefaultRule),
-                                                            cx,
-                                                        );
-                                                    }),
-                                                ),
+                                                .on_click(|_, window, cx| {
+                                                    window.dispatch_action(
+                                                        Box::new(ToggleDefaultRule),
+                                                        cx,
+                                                    );
+                                                }),
                                         ),
                                 ),
                         )
@@ -1228,7 +1206,14 @@ impl RulesLibrary {
                                 .on_action(cx.listener(Self::move_up_from_body))
                                 .flex_grow()
                                 .h_full()
-                                .child(rule_editor.body_editor.clone()),
+                                .child(
+                                    h_flex()
+                                        .py_2()
+                                        .pl_2p5()
+                                        .h_full()
+                                        .flex_1()
+                                        .child(rule_editor.body_editor.clone()),
+                                ),
                         ),
                 )
             }))