Skip to content

chore(ui): debt cleanup — input, dialogs, viewer, render - #112

Merged
leszek3737 merged 1 commit into
mainfrom
audit/17-debt-ui-input
Jul 31, 2026
Merged

chore(ui): debt cleanup — input, dialogs, viewer, render#112
leszek3737 merged 1 commit into
mainfrom
audit/17-debt-ui-input

Conversation

@leszek3737

@leszek3737 leszek3737 commented Jul 31, 2026

Copy link
Copy Markdown
Owner

chore(ui): debt cleanup — input, dialogs, viewer, render

Audit PR-17 (PR-17-debt-ui-input). P3 maintainability scope: high-value subset of 106 findings — the real bug, dead/redundant code, and one-line style fixes in hot files. No UX/layout change.

Fixed (10 findings)

Bug (reachable from user config):

  • theme.rs #1parse_hex_color panicked on non-ASCII hex config values (&hex[n..m] split a multi-byte codepoint). Now operates on bytes with an is_ascii() guard. Added regression test parse_hex_color_non_ascii_does_not_panic.

Dead / redundant code:

  • command_line.rs #2 — removed dead set_text(String::new()) immediately overwritten by set_text_at_end on history Up.
  • hex.rs #4 — removed redundant needle.len() > haystack.len() early-return in find_bytes (memmem handles it).
  • ui/mod.rs #4 — removed unused pub use theme::{ColorPalette, IconTheme, Theme} re-export (all consumers use full path).
  • panels/tests.rs #5 — removed redundant #![allow(clippy::expect_used)] (no .expect() in file).

Style (hot files, one-liners):

  • theme.rs #8 — removed redundant field-level #[serde(default)] (struct already has it).
  • theme.rs #10 — merged two ratatui::style:: imports.
  • theme.rs #11 — added Eq to ColorPalette derive (fields are all Eq).
  • render.rs #11 — grouped the mid-file use lc::{app, ui} block with top-of-file imports.
  • viewer/render.rs #11 — hoisted Theme::status_bar_with_colors(colors) out of both if/else branches.
  • viewer/toggle.rs #9 — dropped stale "tracked in PR8" comment reference.
  • viewer/tests.rs #11assert!(x == 0)assert_eq!(x, 0).

Deferred (~96 findings, by category)

Deferred to keep this PR surgical (debt tier, no UX change). Each is documented below for follow-up:

Definition of Done

  • cargo fmt
  • cargo clippy --locked --all-targets -- -D warnings
  • cargo test --locked — 982 lib + 302 integration, 0 failed
  • cargo build --release --locked
  • No drive-by changes; all edits trace to an audit finding

No merge — agent opens, user merges.

Summary by Sourcery

Improve UI theming and viewer behavior while cleaning up small bits of technical debt without changing UX.

Bug Fixes:

  • Prevent parse_hex_color from panicking on non-ASCII configuration values by validating ASCII and operating on byte slices.
  • Avoid redundant empty-text reset when starting command-line history navigation, preserving the current draft text correctly.
  • Simplify hex viewer byte search edge-case handling by relying on underlying memmem behavior for long needles.

Enhancements:

  • Consolidate ratatui style imports and derive Eq for ColorPalette to tighten theming types.
  • Remove unused top-level theme re-exports from the ui module to clarify the public surface.
  • Refactor viewer status bar styling to avoid redundant Theme::status_bar_with_colors calls.
  • Trim a stale viewer toggle comment reference in wrap-mode handling.
  • Drop unnecessary clippy allowance in panels tests to match current usage.

Tests:

  • Add regression tests ensuring parse_hex_color safely rejects non-ASCII hex strings and returns None instead of panicking.
  • Tighten a viewer hex-mode search assertion by switching from assert! equality to assert_eq for clearer failures.

@sourcery-ai

sourcery-ai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR performs a small, targeted UI/maintainability cleanup focused on fixing a real hex-color parsing bug, removing a handful of dead/redundant code paths, and applying a few one-line style/sanity improvements in hot UI/rendering files without changing UX or layout.

Sequence diagram for updated hex color parsing in theme config

sequenceDiagram
    actor User
    participant Config as ThemeConfig
    participant Theme as parse_color
    participant HexParser as parse_hex_color

    User->>Config: edit hex color value
    Config->>Theme: parse_color(hex_string)
    Theme->>HexParser: parse_hex_color(hex_string)

    alt [hex is ASCII and length is 6 or 3]
        HexParser-->>Theme: Some(Color::Rgb)
        Theme-->>Config: Some(Color)
    else [hex is non-ASCII or invalid length]
        HexParser-->>Theme: None
        Theme-->>Config: None (no panic)
    end
Loading

File-Level Changes

Change Details Files
Harden hex color parsing against non-ASCII config values and add a regression test.
  • Change parse_hex_color to operate on byte slices and gate on hex.is_ascii() to avoid UTF-8 char-boundary panics when slicing.
  • Use std::str::from_utf8 on the sliced byte ranges for both 6-digit and 3-digit hex formats and propagate failures to return None.
  • Add parse_hex_color_non_ascii_does_not_panic test cases covering several non-ASCII inputs reachable via user config.
src/ui/theme.rs
Simplify and tighten theming-related types and imports.
  • Merge separate ratatui::style imports into a single grouped import for Color, Modifier, and Style.
  • Remove redundant #[serde(default)] attribute from ThemeConfig.icon_theme since the struct already has a default.
  • Extend ColorPalette derive to include Eq, matching the Eq nature of all of its fields.
src/ui/theme.rs
Normalize render module imports and keep them together at the top of the file.
  • Move lc::{app, ui} and related type/theme/dialogs/panels/viewer imports from the mid-file block up to the main import section.
  • Preserve existing usage but improve readability and conventional import ordering.
src/render.rs
Slightly refactor viewer status rendering to avoid duplicated theming calls.
  • Hoist Theme::status_bar_with_colors(colors) into a local status_style variable before the warning branch.
  • Reuse status_style and only adjust the foreground color when has_warning is true, reducing repetition.
src/ui/viewer/render.rs
Clean up viewer toggle and tests/panels/hex/command-line minor issues and dead code.
  • Remove a stale PR reference comment in ViewerState::toggle_mode while keeping the behavioral note about horizontal scroll reset.
  • Drop the unused clippy::expect_used allowance in panels tests as the file no longer uses expect.
  • Simplify find_bytes by removing the redundant needle.len() > haystack.len() guard, relying on memmem behavior after the empty-needle check.
  • Convert an assert!(...) equality check in viewer tests to assert_eq!(...) for clearer failure messages.
  • Delete a dead set_text(String::new()) call in command_line history navigation that was immediately overwritten by set_text_at_end.
src/ui/viewer/toggle.rs
src/ui/panels/tests.rs
src/ui/viewer/hex.rs
src/ui/viewer/tests.rs
src/input/command_line.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Greptile Summary

This PR performs targeted UI debt cleanup without changing established behavior.

  • Makes hexadecimal theme-color parsing reject non-ASCII input safely and adds regression coverage.
  • Removes redundant command-history and byte-search operations.
  • Removes unused theme re-exports and redundant lint configuration.
  • Simplifies imports, status-bar styling, comments, derives, and test assertions.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete behavioral, build, or security regressions identified.

The functional changes preserve command-history and byte-search semantics, while hexadecimal color parsing now safely rejects malformed non-ASCII configuration values instead of panicking.

Important Files Changed

Filename Overview
src/ui/theme.rs Replaces potentially unsafe UTF-8 string slicing with guarded byte slicing and adds focused non-ASCII regression tests.
src/input/command_line.rs Removes a redundant clear immediately before history text and cursor state are fully overwritten.
src/ui/viewer/hex.rs Delegates oversized-needle handling to memmem while preserving the existing empty-needle behavior.
src/ui/viewer/render.rs Computes the base status-bar style once and conditionally applies the warning foreground color.
src/ui/mod.rs Removes unused convenience re-exports while retaining the theme types under the public theme module.

Reviews (1): Last reviewed commit: "chore(ui): debt — input, dialogs, viewer..." | Re-trigger Greptile

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • In parse_hex_color, you call hex.is_ascii() twice; consider doing a single if !hex.is_ascii() { return None; } early and then branching only on bytes.len() to avoid repeated checks and make the guard logic clearer.
  • The new parse_hex_color_non_ascii_does_not_panic test only asserts None; it might be worth adding at least one positive hex case in this test or nearby to ensure the ASCII path still behaves as expected after the change.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `parse_hex_color`, you call `hex.is_ascii()` twice; consider doing a single `if !hex.is_ascii() { return None; }` early and then branching only on `bytes.len()` to avoid repeated checks and make the guard logic clearer.
- The new `parse_hex_color_non_ascii_does_not_panic` test only asserts `None`; it might be worth adding at least one positive hex case in this test or nearby to ensure the ASCII path still behaves as expected after the change.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@opencode-agent

Copy link
Copy Markdown

CI gate is fully green. Here's my review.

PR Review: chore(ui): debt cleanup

Verdict: High quality, surgical, safe to merge. All CI gates pass (fmt, clippy -D warnings, 981 lib + 302 integration tests, release build). Every change traces to an audit finding with no drive-by edits. I verified each claim independently.

Standout: the parse_hex_color bug fix (theme.rs:220) ✅

This is a real, reachable bug and the fix is correct. &hex[n..m] on a multi-byte UTF-8 config value panics with "byte index is not a char boundary." The is_ascii() guard before indexing is the right call — once it holds, byte indexing is char-boundary safe. The regression test (parse_hex_color_non_ascii_does_not_panic) covers 3 meaningful cases (interior codepoint, leading codepoint, lone codepoint). Good catch documenting reachability via parse_color → parse_hex_color from user config.

Verified all other claims

Change Verification
command_line.rs dead set_text removal set_text_at_end (line 110) immediately overwrites it; draft is preserved separately in command_draft (line 98). Existing comment at lines 122–123 confirms the invariant. ✅
hex.rs redundant guard memchr::memmem::find returns None when needle.len() > haystack.len(); existing test needle_longer_than_haystack confirms. ✅
ui/mod.rs re-export removal No consumer uses ui::Theme/ColorPalette/IconTheme anywhere — all use the full ui::theme:: path. ✅
panels/tests.rs allow removal No .expect() in the file. ✅
theme.rs #[serde(default)] removal Struct-level #[serde(default)] (line 51) already covers all fields. ✅
theme.rs Eq derive All ColorPalette fields (Color, IconTheme) are Eq — sound. ✅
viewer/render.rs hoist Semantically identical; Style is Copy, shadowing pattern is idiomatic. ✅

Minor observations (non-blocking, optional)

  1. parse_hex_color is slightly more verbose than necessary (theme.rs:227–235). After hex.is_ascii() passes, std::str::from_utf8(&bytes[..]).ok()? can never return None (ASCII ⊆ UTF-8), so those ? are dead. A more minimal fix keeps str slicing and just adds the guard:

    if !hex.is_ascii() { return None; }
    // existing &hex[0..2] slicing is now char-boundary-safe

    The byte-based approach chosen is equally correct and arguably more explicit about the safety reasoning, so this is style preference only. Not worth changing given the surgical scope and that it's config-load-time (not hot path).

  2. PR body test-count discrepancy: body states "982 lib + 302 integration" but actual is 981 lib + 302 integration. Cosmetic — the new parse_hex_color_non_ascii_does_not_panic test is present and passing.

Rust best practices / project conventions

  • No println!/eprintln!/dbg! introduced. ✅
  • No state mutation in ui::* draw code. ✅
  • No unsafe. ✅
  • Conventional commit (chore(ui):). ✅
  • assert_eq! over assert!(==). ✅
  • Imports grouped at file top per render.rs convention. ✅

No blocking issues. The deferred-items rationale in the PR body is well-reasoned — the architectural refactors (ValidationResultResult, DialogKind consolidation, etc.) correctly belong in dedicated structural PRs rather than a debt tier.

New%20session%20-%202026-07-31T13%3A26%3A10.454Z
opencode session  |  github run

@leszek3737
leszek3737 merged commit 199a01d into main Jul 31, 2026
7 checks passed
@leszek3737
leszek3737 deleted the audit/17-debt-ui-input branch July 31, 2026 14:20
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