Skip to content

fix: address PR #4 follow-ups#5

Merged
Hmbown merged 8 commits into
mainfrom
fix/pr4-followups
Mar 13, 2026
Merged

fix: address PR #4 follow-ups#5
Hmbown merged 8 commits into
mainfrom
fix/pr4-followups

Conversation

@Hmbown
Copy link
Copy Markdown
Owner

@Hmbown Hmbown commented Mar 12, 2026

Summary

  • honor low_motion for tool cells in the default transcript rendering path
  • align composer cursor padding with the rendered empty-input placeholder
  • add focused regression tests for both follow-up fixes

Context

Follow-up to #4 ("Remove Normal mode and consolidate to Agent"). These address the unresolved review findings that remained after merge.

Validation

  • cargo fmt --all --check
  • cargo check
  • cargo test --workspace --all-features
  • cargo check -p deepseek-tui
  • cargo test -p deepseek-tui tool_lines_with_options_respects_low_motion_in_default_path
  • cargo test -p deepseek-tui empty_composer_cursor_matches_placeholder_padding

Open with Devin

Honor low_motion in the default tool transcript path and align composer cursor padding with the rendered placeholder. Add focused regression tests for both behaviors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 12, 2026 17:02
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses several follow-up items identified after the merge of PR #4, which focused on consolidating modes to an Agent-centric workflow. The changes primarily enhance the user interface and experience by correctly applying low_motion preferences to tool output and ensuring precise alignment of the composer's cursor padding. These improvements are validated with new, focused regression tests.

Highlights

  • Tool Cell Motion: Ensured that tool cells in the transcript rendering path correctly honor the low_motion setting.
  • Composer Cursor Padding: Aligned the composer cursor's padding with the empty-input placeholder for a consistent UI.
  • Regression Tests: Added dedicated regression tests to validate both the tool cell motion and composer cursor padding fixes.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • crates/tui/src/tui/history.rs
    • Implemented lines_with_options for HistoryCell::Tool to respect low_motion.
    • Added necessary imports for testing.
    • Introduced a new test case tool_lines_with_options_respects_low_motion_in_default_path to verify low_motion behavior for tool cells.
  • crates/tui/src/tui/widgets/mod.rs
    • Renamed composer_vertical_padding to composer_top_padding and updated its usage in ComposerWidget::render.
    • Refactored the top_padding calculation within ComposerWidget::cursor_pos to utilize the new composer_top_padding function, simplifying the logic for cursor alignment.
    • Added new imports for testing ComposerWidget and App components.
    • Included a new test empty_composer_cursor_matches_placeholder_padding to ensure correct cursor positioning in an empty composer.
Activity
  • This pull request is a direct follow-up to PR Remove Normal mode and consolidate to Agent #4, addressing specific unresolved review findings from that previous work.
  • New regression tests were added to validate the implemented fixes.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refines TUI rendering behavior by standardizing the composer’s vertical padding logic and ensuring transcript tool cells respect the “low motion” rendering option.

Changes:

  • Introduce composer_top_padding() and use it for both composer rendering and cursor positioning.
  • Simplify composer cursor padding logic by using layout_input()’s visible line count.
  • Ensure HistoryCell::Tool respects TranscriptRenderOptions.low_motion in the default rendering path, with a regression test.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
crates/tui/src/tui/widgets/mod.rs Switch composer padding to composer_top_padding() and add a cursor/placeholder alignment test.
crates/tui/src/tui/history.rs Apply low_motion to tool rendering in lines_with_options() and add a test validating symbol behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread crates/tui/src/tui/widgets/mod.rs Outdated
}

let top_padding = composer_vertical_padding(input_lines.len(), input_rows_budget);
let top_padding = composer_top_padding(input_lines.len(), input_rows_budget);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

top_padding is computed from input_lines.len(), but in the empty-input case input_lines contains a single placeholder Line that can wrap to multiple terminal rows (because Paragraph::wrap is enabled). On narrower widths this can undercount the real rendered height, causing the placeholder to be partially clipped and the vertical alignment to be off. Consider basing padding on the wrapped placeholder line count (e.g., wrap the placeholder text to content_width and use that length), or render the placeholder as already-wrapped lines so input_lines.len() reflects the actual row usage.

Suggested change
let top_padding = composer_top_padding(input_lines.len(), input_rows_budget);
// Estimate how many terminal rows the input will occupy. For non-empty input,
// use the logical line count. For the empty-input placeholder, assume it may
// occupy up to the entire input_rows_budget so we do not undercount its height
// when it wraps on narrow widths.
let input_visual_rows = if self.app.input.is_empty() {
input_rows_budget
} else {
input_lines.len()
};
let top_padding = composer_top_padding(input_visual_rows, input_rows_budget);

Copilot uses AI. Check for mistakes.
Comment thread crates/tui/src/tui/widgets/mod.rs Outdated
.max(1);
composer_vertical_padding(visible_count.min(input_rows_budget), input_rows_budget)
};
let top_padding = composer_top_padding(visible_lines.len(), input_rows_budget);
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

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

Cursor padding is derived from visible_lines.len(), which is always 1 for an empty input (layout_input injects an empty string). However, the rendered placeholder can wrap to multiple rows. This means the cursor Y position can drift relative to what is actually on-screen at narrow widths. Consider computing cursor padding using the same wrapped-line count logic as rendering (especially for the empty-input placeholder) so cursor placement stays consistent with what the user sees.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request correctly addresses two follow-up items from a previous PR by honoring low_motion for tool cells and aligning the composer cursor padding. The code changes are clean, and the new regression tests are a great addition to prevent future issues. I have one suggestion to improve the robustness of a new test case to avoid potential flakiness in CI environments.

Comment thread crates/tui/src/tui/history.rs Outdated
let animated_symbol = animated[0].spans[0].content.trim();
let low_motion_symbol = low_motion[0].spans[0].content.trim();

assert_ne!(animated_symbol, TOOL_RUNNING_SYMBOLS[0]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This assert_ne! could cause flaky tests in a slow CI environment. If test execution is slow, the animation might cycle back to the first frame, causing this assertion to fail. A more robust check would account for this timing possibility.

        if animated_symbol == TOOL_RUNNING_SYMBOLS[0] {
            // This can happen if the test is slow. A full animation cycle takes 7.2s.
            // We start at an offset of 1.8s, so the next time the index is 0 is after an additional 5.4s.
            assert!(
                started_at.unwrap().elapsed() > std::time::Duration::from_secs(5),
                "Animation symbol unexpectedly matched static symbol without significant delay."
            );
        }

devin-ai-integration Bot and others added 6 commits March 12, 2026 17:49
…_app

The test had inner `use` statements for Config, App, TuiOptions, and
PathBuf that duplicated the module-level test imports. It also manually
constructed App instead of calling the existing create_test_app() helper.
…ded settings

Settings::load() may return a non-default composer_density on some CI
environments. Explicitly set ComposerDensity::Comfortable so the
expected cursor position is deterministic across all platforms.
Use a 2× cycle offset so the animated frame index is 2 (maximally
distant from 0), giving 1800 ms of headroom before the animation could
wrap back to index 0. The previous 1× offset left only ~15 ms of
margin, causing flaky failures on Windows where Instant resolution is
approximately 15.6 ms.
- Fix rustfmt line-length issue in history.rs tool animation test
- Settings::path() now respects DEEPSEEK_CONFIG_PATH for Windows test compat
- doctor_check_mcp_server recognizes Unix-style absolute paths on Windows
- Use checked_sub for Instant arithmetic in web_run tests to prevent
  underflow on freshly-booted Windows CI runners

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 9 additional findings in Devin Review.

Open in Devin Review

Comment on lines +818 to +819
fn placeholder_visual_lines(content_width: usize) -> usize {
wrap_text(COMPOSER_PLACEHOLDER, content_width).len().max(1)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 placeholder_visual_lines uses character-level wrapping while rendering uses word-level Paragraph::wrap, causing line-count mismatch

placeholder_visual_lines (crates/tui/src/tui/widgets/mod.rs:818-819) calls wrap_text(COMPOSER_PLACEHOLDER, content_width) which does character-level wrapping. However, the actual rendering at crates/tui/src/tui/widgets/mod.rs:268-270 uses Paragraph::new(lines).wrap(Wrap { trim: false }), which does word-level wrapping. The wrap_text function has a known behavior where text that exactly fills the width produces a trailing empty string (the current_width >= width check at line 1020 flushes and resets, then lines.push(current) at line 1027 appends ""). For COMPOSER_PLACEHOLDER ("Write a task or use /.", exactly 22 ASCII chars), at content_width=22 (terminal width 24 with borders), wrap_text returns ["Write a task or use /.", ""] (2 elements) while Paragraph::wrap renders 1 visual line. The same issue occurs at content_width=11. This causes composer_top_padding to subtract 1 extra row, positioning the placeholder text 1 row higher than intended. Both render() and cursor_pos() use the same estimate, so the cursor remains consistent with the rendered text.

Prompt for agents
In crates/tui/src/tui/widgets/mod.rs, the placeholder_visual_lines function at line 818-819 estimates how many visual rows the placeholder will occupy by calling wrap_text(), which does character-level wrapping. However, the actual rendering (line 268-270) uses Paragraph::wrap(Wrap { trim: false }) which does word-level wrapping. These can disagree on the number of lines, especially when text exactly fills the width (wrap_text produces a trailing empty line due to the flush-then-push pattern at lines 1020-1027).

Two possible fixes:
1. Pre-wrap the placeholder text using wrap_text (the same way non-empty input is handled), creating multiple Line objects instead of relying on Paragraph::wrap. This makes the estimate trivially correct.
2. Strip trailing empty strings from the wrap_text result in placeholder_visual_lines, e.g.: wrap_text(COMPOSER_PLACEHOLDER, content_width).into_iter().filter(|l| !l.is_empty() || ...).count(). However this is fragile.

Option 1 is recommended: in the render() method around line 214-218, instead of creating a single Line from COMPOSER_PLACEHOLDER, wrap it with wrap_text(COMPOSER_PLACEHOLDER, content_width) and create one Line per wrapped segment, just like the non-empty input path at lines 220-225. Then placeholder_visual_lines can be replaced with input_lines.len().
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@Hmbown Hmbown merged commit cf3d61f into main Mar 13, 2026
11 of 12 checks passed
@Hmbown Hmbown deleted the fix/pr4-followups branch March 13, 2026 02:29
Hmbown added a commit that referenced this pull request Apr 25, 2026
* fix: address PR #4 follow-ups

Honor low_motion in the default tool transcript path and align composer cursor padding with the rendered placeholder. Add focused regression tests for both behaviors.


* lint: remove redundant imports in empty_state test, reuse create_test_app

The test had inner `use` statements for Config, App, TuiOptions, and
PathBuf that duplicated the module-level test imports. It also manually
constructed App instead of calling the existing create_test_app() helper.

* fix: replace useless format!("{text}") with text.to_string() in details_affordance_line

* test: pin composer_density in cursor test to avoid sensitivity to loaded settings

Settings::load() may return a non-default composer_density on some CI
environments. Explicitly set ComposerDensity::Comfortable so the
expected cursor position is deterministic across all platforms.

* fix: make tool low_motion test robust against coarse Windows timers

Use a 2× cycle offset so the animated frame index is 2 (maximally
distant from 0), giving 1800 ms of headroom before the animation could
wrap back to index 0. The previous 1× offset left only ~15 ms of
margin, causing flaky failures on Windows where Instant resolution is
approximately 15.6 ms.

* fix: correct headroom comment in tool animation test (3600ms, not 1800ms)

* fix: resolve lint, parity, and Windows test failures

- Fix rustfmt line-length issue in history.rs tool animation test
- Settings::path() now respects DEEPSEEK_CONFIG_PATH for Windows test compat
- doctor_check_mcp_server recognizes Unix-style absolute paths on Windows
- Use checked_sub for Instant arithmetic in web_run tests to prevent
  underflow on freshly-booted Windows CI runners


* fix: expand ~ in DEEPSEEK_CONFIG_PATH when resolving settings path

---------
Hmbown added a commit that referenced this pull request Apr 28, 2026
Sub-area #5 of the v0.6.6 UI redesign (issue #121).

Spout strip:
- Replace box-drawing arc cups (`╭───╮`) with paired crests (`⌒‿`) over
  the existing water-surface (`─`). Crests read as gentle ripples instead
  of hard architectural arches — calmer eye-feel.
- Two crests at independent cadences: A advances every 4 ticks (~600 ms),
  B every 6 ticks (~900 ms). Phase jitter every 17 ticks (~2.5 s) keeps
  the pattern from settling into a strict beat.
- Frame counter cadence in `ui.rs` retimed from 80 ms to 150 ms so the
  4×6×17 tick math lands on the spec'd timings.

Footer left cluster consolidates "what costs you what":
- Cost chip moves from the right-hand parade to the left, between model
  and status: `mode · model · cost · status`.
- Priority drop is now status → cost → truncate model → mode-only. Cost
  outranks status because it's steady info; status is a transient signal.
- Right cluster shrinks to coherence / agents / replay / cache.

Tests: existing strip determinism / position-advances tests are retuned
for the new tick math (12-tick window covers both crests). New tests pin
the cost slot's order on wide widths and confirm cost survives status
drop in the priority cascade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
MMMarcinho pushed a commit to MMMarcinho/DeepSeek-TUI that referenced this pull request May 6, 2026
Sub-area Hmbown#5 of the v0.6.6 UI redesign (issue Hmbown#121).

Spout strip:
- Replace box-drawing arc cups (`╭───╮`) with paired crests (`⌒‿`) over
  the existing water-surface (`─`). Crests read as gentle ripples instead
  of hard architectural arches — calmer eye-feel.
- Two crests at independent cadences: A advances every 4 ticks (~600 ms),
  B every 6 ticks (~900 ms). Phase jitter every 17 ticks (~2.5 s) keeps
  the pattern from settling into a strict beat.
- Frame counter cadence in `ui.rs` retimed from 80 ms to 150 ms so the
  4×6×17 tick math lands on the spec'd timings.

Footer left cluster consolidates "what costs you what":
- Cost chip moves from the right-hand parade to the left, between model
  and status: `mode · model · cost · status`.
- Priority drop is now status → cost → truncate model → mode-only. Cost
  outranks status because it's steady info; status is a transient signal.
- Right cluster shrinks to coherence / agents / replay / cache.

Tests: existing strip determinism / position-advances tests are retuned
for the new tick math (12-tick window covers both crests). New tests pin
the cost slot's order on wide widths and confirm cost survives status
drop in the priority cascade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
YaYII added a commit to YaYII/DeepSeek-TUI that referenced this pull request May 9, 2026
🟡 Hmbown#5 Memory dedup (remember tool):
  - Before writing to vector DB, search for existing memories
    with score > 0.85
  - Skip duplicate writes to prevent vector store pollution
  - Adds dedup check in remember tool execute()

🟡 Hmbown#6 InMemoryBackend OOM prevention:
  - Added MAX_IN_MEMORY_ITEMS = 1000 cap
  - New max_items field with with_max_items() builder
  - Eviction in store_memory(): oldest first, expired-TTL prioritized
  - Summary cap tracking in store_summary()

🟡 Hmbown#10 TTL delete three-tier fallback:
  - Tier 1: SQL CAST(ttl AS INT64) < nanos
  - Tier 2: SQL ttl < nanos (native timestamp compare)
  - Tier 3: InMemoryBackend handles cleanup, LanceDB ages out

Tests: 17/17 vector_db ✓, 62/62 compaction ✓
@Hmbown Hmbown mentioned this pull request May 10, 2026
8 tasks
Yourdaylight added a commit to Yourdaylight/DeepSeek-TUI that referenced this pull request May 15, 2026
… precision

- resolve_path: normalise .. components; reject workspace escapes (Hmbown#1,Hmbown#5)
- tool_read_file: clamp start_line >= 1 to prevent underflow panic (Hmbown#4)
- tool_edit_file: use replacen(.., 1) to avoid mass-replacement bugs (Hmbown#6,Hmbown#8)
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.

2 participants