Skip to content

Beautify UI: improve spacing, padding, and visual hierarchy#27

Open
sharno wants to merge 5 commits intomainfrom
ui/beautify-layout
Open

Beautify UI: improve spacing, padding, and visual hierarchy#27
sharno wants to merge 5 commits intomainfrom
ui/beautify-layout

Conversation

@sharno
Copy link
Owner

@sharno sharno commented Feb 25, 2026

Summary

  • Add card-like section containers with rounded corners and subtle borders for visual grouping (Projects, Global Envs, Collections, Meta, Request, Headers, Auth, Response)
  • Introduce distinct sidebar background and semi-transparent status bar styling via 3 new container styles in theme.rs
  • Tighten pane grid spacing (8px -> 2px) and standardize text sizing, button padding, and alignment across all panels (sidebar, workspace builder, response, headers editor, auth editor)

Changes

File What changed
src/theme.rs Added section_container_style, status_bar_style, sidebar_container_style
src/app/view/mod.rs Updated section() helper to use card-style containers, smaller section titles, tighter pane spacing, styled status bar
src/app/view/sidebar.rs Increased indent, applied sidebar background, improved button/text sizing and alignment
src/app/view/workspace.rs Removed redundant header, reduced padding, standardized button/input sizing
src/app/view/response.rs Improved tab toggle sizing and section spacing
src/app/view/auth.rs Standardized input padding and spacing
src/app/headers.rs Fixed cross-platform button text, improved row alignment and padding

All changes pass cargo clippy --all-targets --all-features -- -D warnings.

Summary by CodeRabbit

  • New Features

    • Configurable icon set with Bootstrap icons and ASCII mode.
    • Automated screenshot publishing to pull request comments.
  • Improvements

    • Unified spacing, typography and token-driven styling across the UI.
    • Updated header editor, sidebar, workspace and response panels with refined layouts, icons and tooltips.
    • New themed button and container styles for clearer visual hierarchy.
  • Bug Fixes

    • Graceful handling when screenshots arrive after an automation run completes.

…l panels

Add card-like section containers with rounded corners and subtle borders,
a distinct sidebar background, tighter pane grid spacing, and consistent
text sizing and button padding throughout the workspace builder, response
panel, headers editor, auth editor, and status bar.
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Walkthrough

This PR adds CI changes to post PR comments with links and metadata for e2e screenshots, adds the iced_fonts dependency, introduces a centralized icons module and Bootstrap icon wrappers, and implements a design-token-driven theme (spacing, radius, typo) with method/status color helpers and several container/button styles. Many UI modules were refactored to use the new theme tokens and icon set handling (including a new IconSet enum and icon helpers). It also handles late-arriving automation screenshots gracefully by making that path a no-op.

Sequence Diagram(s)

sequenceDiagram
    participant Runner as GitHub Actions Runner
    participant Job as e2e-automation job script
    participant ArtifactStore as Actions Artifacts (artifacts/e2e)
    participant GHCLI as gh CLI
    participant GitHub as GitHub PR

    Runner->>Job: start e2e-automation (on PR)
    Job->>ArtifactStore: list/download PNGs in artifacts/e2e
    alt screenshots found
        Job->>Job: count files, gather names & sizes
        Job->>Job: build Markdown body with links and sizes (uses run URL & artifact info)
        Job->>GHCLI: gh pr comment --body "<markdown>"
        GHCLI->>GitHub: POST comment to PR
        GitHub-->>GHCLI: comment created
        GHCLI-->>Job: success
    else no screenshots
        Job->>Job: skip posting (no-op)
    end
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main changes in the PR, which focus on improving UI styling through spacing, padding adjustments, and visual hierarchy enhancements across multiple UI components.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ui/beautify-layout

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

Automation Screenshots

3 screenshot(s) captured during this CI run.

File Size
02-selected.png 84K
04-ready.png 128K
05-send-status.png 124K

Download: automation-e2e-artifacts (see Artifacts section at bottom of the run page)

Unify the app around Bootstrap icon fonts and reusable theme tokens so the interface feels consistent and polished across sidebar, workspace, auth, headers, and response views.
@github-actions
Copy link

Automation Screenshots

3 screenshot(s) captured during this CI run.

File Size
02-selected.png 84K
04-ready.png 128K
05-send-status.png 124K

Download: automation-e2e-artifacts (see Artifacts section at bottom of the run page)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
src/app/view/workspace.rs (1)

65-65: Consider splitting builder_form to eliminate the #[allow(clippy::too_many_lines)] suppression.

The too_many_lines attribute is a workaround. Extracting the meta, request, and form-assembly logic into dedicated helper functions (e.g., meta_row, request_row) would remove the need for the suppression and improve readability, consistent with the project's strict clippy baseline.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/view/workspace.rs` at line 65, The function builder_form is currently
large and uses #[allow(clippy::too_many_lines)]; split it by extracting logical
sections into smaller helpers: create meta_row(&self, ctx: &Context) for the
metadata rows, request_row(&self, ctx: &Context) for the request-related UI, and
assemble_form(&self, ctx: &Context) (or similar) to compose the final form from
those pieces; move the corresponding blocks of code and any local variables they
need into these helpers (passing minimal arguments) and replace the inlined
blocks in builder_form with calls to meta_row, request_row, and assemble_form so
the suppression can be removed.
src/icons.rs (1)

122-132: Consider removing unused hourglass and lock functions (YAGNI).

Both functions carry #[allow(dead_code)], meaning nothing in the codebase currently calls them. Given the project enforces warnings = "deny" and the YAGNI principle, dead icon wrappers should be deleted rather than suppressed. They can be re-added with a concrete use site when actually needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/icons.rs` around lines 122 - 132, Remove the unused icon wrapper
functions hourglass and lock (and their #[allow(dead_code)] attributes) from
src/icons.rs: delete the pub fn hourglass<'a>() -> Text<'a> and pub fn
lock<'a>() -> Text<'a> wrappers (and their doc comments) since nothing calls
them; reintroduce them later with a real use site if needed.
.github/workflows/ci.yml (1)

92-92: Unused listing variable.

listing is populated at line 92 but the body-building loop at line 98 independently re-runs find. The variable is never referenced and can be removed.

🧹 Proposed cleanup
-          listing=$(find artifacts/e2e -name '*.png' -printf '%f\n' 2>/dev/null | sort)
-
           body="## Automation Screenshots"$'\n\n'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml at line 92, Remove the unused variable assignment
"listing=$(find artifacts/e2e -name '*.png' -printf '%f\n' 2>/dev/null | sort)"
since the body-building loop re-runs find independently; delete that line or
instead reuse the variable by replacing the second find in the body-building
loop with iterating over "$listing" (ensure quoting), so either eliminate the
redundant assignment or wire the loop to use the existing "listing" variable.
src/app/view/sidebar.rs (1)

26-33: Normalize env input at parse boundary.

ZAGEL_ICON_SET parsing should trim whitespace before matching to avoid surprising fallbacks from values like " ascii ".

♻️ Small hardening change
 impl IconSet {
     pub fn from_env() -> Self {
         match std::env::var("ZAGEL_ICON_SET")
             .ok()
-            .map(|value| value.to_ascii_lowercase())
+            .map(|value| value.trim().to_ascii_lowercase())
         {
             Some(value) if value == "ascii" => Self::Ascii,
             _ => Self::Bootstrap,
         }
     }
 }

As per coding guidelines, "Parse, don't validate: parse input at the edges so downstream state stays valid and doesn't need repeated validation".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/view/sidebar.rs` around lines 26 - 33, The env parsing in
Sidebar::from_env (reading ZAGEL_ICON_SET) should trim surrounding whitespace
before normalizing to lowercase and matching so inputs like " ascii " don't fall
through; update the map/closure that currently calls to_ascii_lowercase() to
trim the input first (then lowercase) and use the resulting trimmed/lowercased
value for the "ascii" equality check.
src/app/view/response.rs (1)

267-300: Build tab content lazily to avoid unnecessary editor/highlighter work.

body_editor and body_section are currently built even when the Headers tab is active. For large responses, this adds avoidable render cost.

♻️ Suggested refactor (construct only the active tab)
-            let pretty_kind = body.pretty_kind();
-            let syntax = body.syntax();
-            let body_editor = text_editor(content)
-                .height(Length::Fill)
-                .highlight(syntax.as_str(), highlight_theme)
-                .wrapping(Wrapping::None);
-
-            let body_section: Element<'_, Message> = column![
-                text(format!(
-                    "Body ({})",
-                    match (display, pretty_kind) {
-                        (ResponseDisplay::Pretty, Some(PrettyKind::Html)) => {
-                            "pretty (HTML; formatted view)"
-                        }
-                        (ResponseDisplay::Pretty, Some(PrettyKind::Json)) => "pretty (JSON)",
-                        (ResponseDisplay::Pretty, None) => "pretty (raw shown)",
-                        (ResponseDisplay::Raw, _) => "raw",
-                    }
-                ))
-                .size(typo::CAPTION),
-                body_editor,
-            ]
-            .spacing(spacing::XS)
-            .into();
-
-            let headers_section: Element<'_, Message> =
-                column![text("Headers").size(typo::CAPTION), headers_view,]
-                    .spacing(spacing::XS)
-                    .into();
-
-            let tab_view: Element<'_, Message> = match tab {
-                ResponseTab::Body => body_section,
-                ResponseTab::Headers => headers_section,
-            };
+            let tab_view: Element<'_, Message> = match tab {
+                ResponseTab::Body => {
+                    let pretty_kind = body.pretty_kind();
+                    let syntax = body.syntax();
+                    let body_editor = text_editor(content)
+                        .height(Length::Fill)
+                        .highlight(syntax.as_str(), highlight_theme)
+                        .wrapping(Wrapping::None);
+
+                    column![
+                        text(format!(
+                            "Body ({})",
+                            match (display, pretty_kind) {
+                                (ResponseDisplay::Pretty, Some(PrettyKind::Html)) => {
+                                    "pretty (HTML; formatted view)"
+                                }
+                                (ResponseDisplay::Pretty, Some(PrettyKind::Json)) => "pretty (JSON)",
+                                (ResponseDisplay::Pretty, None) => "pretty (raw shown)",
+                                (ResponseDisplay::Raw, _) => "raw",
+                            }
+                        ))
+                        .size(typo::CAPTION),
+                        body_editor,
+                    ]
+                    .spacing(spacing::XS)
+                    .into()
+                }
+                ResponseTab::Headers => column![
+                    text("Headers").size(typo::CAPTION),
+                    headers_view
+                ]
+                .spacing(spacing::XS)
+                .into(),
+            };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/view/response.rs` around lines 267 - 300, The body editor and its
enclosing UI (variables/methods: pretty_kind, syntax, body_editor, body_section,
text_editor) are always constructed even when ResponseTab::Headers is selected;
change the match that builds tab_view so that headers_section is used directly
for ResponseTab::Headers and the body-related values (compute pretty_kind,
syntax and call text_editor to produce body_editor and body_section) are only
computed inside the ResponseTab::Body arm; keep headers_section and its
construction (headers_view) unchanged and ensure ResponseDisplay logic that
formats the "Body (...)" label is evaluated only when building the Body branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 98-102: The for-loop using command substitution with $(find ...)
will break on filenames with spaces; change it to a null-delimited iteration
using find -print0 piped into a read loop (e.g., use find ... -print0 | while
IFS= read -r -d '' img; do ...) so each filename (img) is read safely; inside
the loop keep using basename to set name and du -h "$img" | cut -f1 to set size
and append to body exactly as before, ensuring all uses of img are quoted to
preserve spaces.

In `@src/app/view/sidebar.rs`:
- Around line 87-97: The action buttons (e.g., the add_project button using
icons::plus_circle) hardcode Bootstrap glyphs and ignore IconSet::Ascii; replace
the icon construction with the same match on ctx.icon_set used for tree rows so
ASCII mode uses the ASCII fallback (i.e., when ctx.icon_set == IconSet::Ascii
choose the ascii glyph string/icon, otherwise use
icons::plus_circle().size(...)), and apply this pattern to every action-row icon
occurrence (dash_circle, trash, check_lg, pencil) so all controls honor
IconSet::Ascii and use the documented fallback.

In `@src/theme.rs`:
- Around line 12-22: The module-level doc for spacing is incorrect: it lists
only "4 / 8 / 12 / 16 / 24 / 32" but the spacing module actually defines XXXS,
XXS, XS, SM, MD, LG, XL, XXL (values 2.0, 4.0, 6.0, 8.0, 12.0, 16.0, 24.0,
32.0). Update the comment above pub mod spacing to accurately list the token
scale (e.g. "Spacing tokens (px): 2 / 4 / 6 / 8 / 12 / 16 / 24 / 32") so it
matches the constants XXXS, XXS, XS, SM, MD, LG, XL, and XXL.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Line 92: Remove the unused variable assignment "listing=$(find artifacts/e2e
-name '*.png' -printf '%f\n' 2>/dev/null | sort)" since the body-building loop
re-runs find independently; delete that line or instead reuse the variable by
replacing the second find in the body-building loop with iterating over
"$listing" (ensure quoting), so either eliminate the redundant assignment or
wire the loop to use the existing "listing" variable.

In `@src/app/view/response.rs`:
- Around line 267-300: The body editor and its enclosing UI (variables/methods:
pretty_kind, syntax, body_editor, body_section, text_editor) are always
constructed even when ResponseTab::Headers is selected; change the match that
builds tab_view so that headers_section is used directly for
ResponseTab::Headers and the body-related values (compute pretty_kind, syntax
and call text_editor to produce body_editor and body_section) are only computed
inside the ResponseTab::Body arm; keep headers_section and its construction
(headers_view) unchanged and ensure ResponseDisplay logic that formats the "Body
(...)" label is evaluated only when building the Body branch.

In `@src/app/view/sidebar.rs`:
- Around line 26-33: The env parsing in Sidebar::from_env (reading
ZAGEL_ICON_SET) should trim surrounding whitespace before normalizing to
lowercase and matching so inputs like " ascii " don't fall through; update the
map/closure that currently calls to_ascii_lowercase() to trim the input first
(then lowercase) and use the resulting trimmed/lowercased value for the "ascii"
equality check.

In `@src/app/view/workspace.rs`:
- Line 65: The function builder_form is currently large and uses
#[allow(clippy::too_many_lines)]; split it by extracting logical sections into
smaller helpers: create meta_row(&self, ctx: &Context) for the metadata rows,
request_row(&self, ctx: &Context) for the request-related UI, and
assemble_form(&self, ctx: &Context) (or similar) to compose the final form from
those pieces; move the corresponding blocks of code and any local variables they
need into these helpers (passing minimal arguments) and replace the inlined
blocks in builder_form with calls to meta_row, request_row, and assemble_form so
the suppression can be removed.

In `@src/icons.rs`:
- Around line 122-132: Remove the unused icon wrapper functions hourglass and
lock (and their #[allow(dead_code)] attributes) from src/icons.rs: delete the
pub fn hourglass<'a>() -> Text<'a> and pub fn lock<'a>() -> Text<'a> wrappers
(and their doc comments) since nothing calls them; reintroduce them later with a
real use site if needed.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec73551 and f4a4a44.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .github/workflows/ci.yml
  • Cargo.toml
  • src/app/headers.rs
  • src/app/lifecycle.rs
  • src/app/view/auth.rs
  • src/app/view/mod.rs
  • src/app/view/response.rs
  • src/app/view/sidebar.rs
  • src/app/view/workspace.rs
  • src/icons.rs
  • src/main.rs
  • src/theme.rs

Ignore late screenshot callbacks after automation completion, make sidebar action icons honor ASCII fallback, harden CI screenshot file iteration for spaces, and sync spacing token docs with actual values.
@github-actions
Copy link

Automation Screenshots

3 screenshot(s) captured during this CI run.

File Size
02-selected.png 84K
04-ready.png 128K
05-send-status.png 124K

Download: automation-e2e-artifacts (see Artifacts section at bottom of the run page)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/app/view/sidebar.rs (1)

519-531: Deduplicate up/down move icon rendering.

The same match ctx.icon_set blocks for up/down controls are repeated in both collection and request rows. Extracting move_up_icon / move_down_icon helpers would keep behavior consistent and reduce churn.

Also applies to: 609-621

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/view/sidebar.rs` around lines 519 - 531, Extract the repeated
icon-selection logic into two small helper functions (e.g., move_up_icon(ctx:
&Context) -> Element and move_down_icon(ctx: &Context) -> Element) and replace
the duplicated match blocks that call ctx.icon_set in the collection and request
row code with calls to these helpers; each helper should match
IconSet::Bootstrap to return
icons::arrow_up()/icons::arrow_down().size(typo::CAPTION).into() and
IconSet::Ascii to return Element::from(text("^"/"v").size(typo::CAPTION)),
preserving existing sizing and types so the .padding/.style/.on_press chains
remain unchanged (apply same change for the other duplicated block noted around
lines 609-621).
src/theme.rs (1)

174-277: Extract shared filled-button state logic to avoid style drift.

accent_button_style and destructive_button_style duplicate the same status-state shading flow. A shared helper would reduce maintenance risk.

♻️ Refactor sketch
+fn shift_rgb(c: Color, delta: f32) -> Color {
+    Color::from_rgba(
+        (c.r + delta).clamp(0.0, 1.0),
+        (c.g + delta).clamp(0.0, 1.0),
+        (c.b + delta).clamp(0.0, 1.0),
+        1.0,
+    )
+}
+
+fn filled_button_style(base: Color, text: Color, status: button::Status) -> button::Style {
+    let (bg, text_alpha) = match status {
+        button::Status::Active => (base, 1.0),
+        button::Status::Hovered => (shift_rgb(base, 0.08), 1.0),
+        button::Status::Pressed => (shift_rgb(base, -0.06), 1.0),
+        button::Status::Disabled => (Color::from_rgba(base.r, base.g, base.b, 0.4), 0.5),
+    };
+
+    button::Style {
+        background: Some(bg.into()),
+        text_color: Color::from_rgba(text.r, text.g, text.b, text_alpha),
+        border: border::rounded(radius::SM),
+        ..button::Style::default()
+    }
+}
+
 pub fn accent_button_style(theme: &Theme, status: button::Status) -> button::Style {
     let palette = theme.extended_palette();
-    let accent = palette.primary.strong;
-    match status { ... }
+    filled_button_style(palette.primary.strong.color, palette.primary.strong.text, status)
 }
 
 pub fn destructive_button_style(theme: &Theme, status: button::Status) -> button::Style {
     let palette = theme.extended_palette();
-    let danger = palette.danger.strong;
-    match status { ... }
+    filled_button_style(palette.danger.strong.color, palette.danger.strong.text, status)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/theme.rs` around lines 174 - 277, Both accent_button_style and
destructive_button_style duplicate the same status-based shading/opacity logic;
extract that into a shared helper (e.g. filled_button_style or
style_for_filled_button) that accepts the base Color and text Color (or a struct
like PaletteColor { color, text }) and the button::Status, then return the
button::Style using the same adjustments for Hovered (+0.08 clamped), Pressed
(-0.06 clamped), Active (base color), and Disabled (alpha 0.4 for background,
0.5 for text) and reuse border::rounded(radius::SM) and
..button::Style::default(); update accent_button_style and
destructive_button_style to call this helper with palette.primary.strong and
palette.danger.strong respectively.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/view/sidebar.rs`:
- Around line 287-301: The three section headers (project_section,
global_env_section, collections_section) currently pass hardcoded Bootstrap
glyphs (icons::folder_open(), icons::globe(), icons::collection()) which bypass
the IconSet::Ascii fallback; update these calls to use the project's icon
resolution helper that respects IconSet::Ascii (the same icon helper used
elsewhere in the codebase) instead of the direct icons::* constructors so the
section(...) invocations use the fallback ASCII glyphs when IconSet::Ascii is
selected.

---

Nitpick comments:
In `@src/app/view/sidebar.rs`:
- Around line 519-531: Extract the repeated icon-selection logic into two small
helper functions (e.g., move_up_icon(ctx: &Context) -> Element and
move_down_icon(ctx: &Context) -> Element) and replace the duplicated match
blocks that call ctx.icon_set in the collection and request row code with calls
to these helpers; each helper should match IconSet::Bootstrap to return
icons::arrow_up()/icons::arrow_down().size(typo::CAPTION).into() and
IconSet::Ascii to return Element::from(text("^"/"v").size(typo::CAPTION)),
preserving existing sizing and types so the .padding/.style/.on_press chains
remain unchanged (apply same change for the other duplicated block noted around
lines 609-621).

In `@src/theme.rs`:
- Around line 174-277: Both accent_button_style and destructive_button_style
duplicate the same status-based shading/opacity logic; extract that into a
shared helper (e.g. filled_button_style or style_for_filled_button) that accepts
the base Color and text Color (or a struct like PaletteColor { color, text })
and the button::Status, then return the button::Style using the same adjustments
for Hovered (+0.08 clamped), Pressed (-0.06 clamped), Active (base color), and
Disabled (alpha 0.4 for background, 0.5 for text) and reuse
border::rounded(radius::SM) and ..button::Style::default(); update
accent_button_style and destructive_button_style to call this helper with
palette.primary.strong and palette.danger.strong respectively.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4a4a44 and 5db2764.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • src/app/automation.rs
  • src/app/view/sidebar.rs
  • src/theme.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/ci.yml

Comment on lines +287 to +301
let project_section = section(
"Projects",
icons::folder_open().size(typo::BODY),
project_roots.into(),
);
let global_env_section = section(
"Global Environments",
icons::globe().size(typo::BODY),
global_env_roots.into(),
);
let collections_section = section(
"Collections",
icons::collection().size(typo::BODY),
list.into(),
);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Section header icons still bypass IconSet::Ascii fallback.

These three section icons are hardcoded Bootstrap glyphs, so ASCII mode is still incomplete and can show missing glyphs in fallback environments.

🛠️ Proposed fix
-    let project_section = section(
-        "Projects",
-        icons::folder_open().size(typo::BODY),
-        project_roots.into(),
-    );
+    let project_icon = match ctx.icon_set {
+        IconSet::Bootstrap => icons::folder_open().size(typo::BODY).into(),
+        IconSet::Ascii => text("[P]").size(typo::CAPTION).into(),
+    };
+    let project_section = section("Projects", project_icon, project_roots.into());

-    let global_env_section = section(
-        "Global Environments",
-        icons::globe().size(typo::BODY),
-        global_env_roots.into(),
-    );
+    let global_icon = match ctx.icon_set {
+        IconSet::Bootstrap => icons::globe().size(typo::BODY).into(),
+        IconSet::Ascii => text("[G]").size(typo::CAPTION).into(),
+    };
+    let global_env_section = section("Global Environments", global_icon, global_env_roots.into());

-    let collections_section = section(
-        "Collections",
-        icons::collection().size(typo::BODY),
-        list.into(),
-    );
+    let collections_icon = match ctx.icon_set {
+        IconSet::Bootstrap => icons::collection().size(typo::BODY).into(),
+        IconSet::Ascii => text("[C]").size(typo::CAPTION).into(),
+    };
+    let collections_section = section("Collections", collections_icon, list.into());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/app/view/sidebar.rs` around lines 287 - 301, The three section headers
(project_section, global_env_section, collections_section) currently pass
hardcoded Bootstrap glyphs (icons::folder_open(), icons::globe(),
icons::collection()) which bypass the IconSet::Ascii fallback; update these
calls to use the project's icon resolution helper that respects IconSet::Ascii
(the same icon helper used elsewhere in the codebase) instead of the direct
icons::* constructors so the section(...) invocations use the fallback ASCII
glyphs when IconSet::Ascii is selected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant