Skip to content

Keep the chat at the bottom when a question card is answered - #1174

Merged
alexeyzimarev merged 2 commits into
mainfrom
alexeyzimarev/ai-3243-answering-a-question-card-in-the-desktop-chat-leaves-the
Sep 26, 2026
Merged

alexeyzimarev merged 2 commits into
mainfrom
alexeyzimarev/ai-3243-answering-a-question-card-in-the-desktop-chat-leaves-the

Conversation

@alexeyzimarev

@alexeyzimarev alexeyzimarev commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Closes #1173 — AI-3243

What & why

Answering a question card that has more than one question left the desktop chat where it was, so a taller next question sat below the fold. Follow-tail treats a press or key inside the list as the reader's own gesture and stops following when the change it produces lands above the bottom, which is what the tool-summary expansion click needs; an answer click is the same kind of press, and the card's height change lands in the same layout pass. A press, or Enter or Space, that originates inside a pending card now leaves the gesture flag alone. Wheel, touch-pan and every other key over the card still count as reader scrolling: Page Up from a focused option pages the list.

Where to look

A card that retires clamps back to the bottom on its own, so the existing retire test could not see this; the new series test needs the next question to be taller than the one it replaces.

Verification

dotnet run --project test/Capacitor.App.Tests.Unit/Capacitor.App.Tests.Unit.csproj -- --treenode-filter "/*/*/ChatTabViewSmokeTests/Answering_a_series*" — failed at the final at-bottom assertion before the fix, passes after. The same flow with the option's command invoked directly, no pointer gesture, stayed at the bottom before the fix.

Paging_up_from_a_focused_option_leaves_the_reader_where_they_paged — with every card key exempt, a probe of this flow showed the page-up offset delta of -477 followed by a snap back to the bottom; with the exemption limited to Enter and Space it stays paged.

Full Capacitor.App.Tests.Unit: 2759 passed, 0 failed. Clean rebuild of Capacitor.App: 0 warnings.

🤖 Generated with Claude Code

A card that retires clamps back to the bottom on its own; only a card that
grows in place, the next question in a series, showed the disarmed follow-tail.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Sep 25, 2026

Copy link
Copy Markdown

AI-3243

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T15:11:34.984794Z 3bf560b PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Keep Chat at Bottom When Answering Question Cards

🐞 Bug fix 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Preserve follow-tail for activation events originating inside pending question cards.
• Keep wheel and touch scrolling classified as reader-controlled movement.
• Add regression coverage for taller subsequent questions in multi-question cards.
Diagram

graph TD
    A["Input event"] --> B{"Pending activation?"}
    B -- "Yes" --> C["Preserve follow-tail"] --> E["Card relayout"] --> F{"Follow-tail active?"}
    B -- "No" --> D["Track reader gesture"] --> E
    F -- "Yes" --> G["Maintain bottom"]
    F -- "No" --> H["Keep current offset"]
Loading
High-Level Assessment

The event-filtering approach is appropriate because it fixes the behavior at the shared follow-tail classification boundary while preserving wheel and touch scrolling. Forcing ScrollToEnd from question commands or card layout callbacks was considered but would couple card internals to chat scrolling and require separate handling for pointer and keyboard activation.

Files changed (2) +49 / -0

Bug fix (1) +10 / -0
ChatTabView.axaml.csPreserve follow-tail during pending-card activation +10/-0

Preserve follow-tail during pending-card activation

• Ignores pointer press, pointer release, and key-down activation events originating inside pending cards when classifying reader gestures. This prevents an in-place question-card height change from disabling follow-tail while leaving wheel and touch-pan scrolling unaffected.

src/Capacitor.App/Views/ChatTabView.axaml.cs

Tests (1) +39 / -0
ChatTabViewSmokeTests.csCover bottom pinning across multi-question card growth +39/-0

Cover bottom pinning across multi-question card growth

• Adds an Avalonia smoke test that answers the first question in a series and replaces it with a deliberately taller second question. The test verifies that the chat remains pinned to the bottom after the pointer-driven answer and resulting layout passes.

test/Capacitor.App.Tests.Unit/ChatTabViewSmokeTests.cs

@qodo-code-review

qodo-code-review Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Card keyboard scrolling snaps back ✓ Resolved 🐞 Bug ≡ Correctness
Description
IsActivation classifies every KeyDown from a pending card as an answer action, so
OnReaderGesture returns without setting _readerGesture. When focus is on a card button and Page
Up, Page Down, Home, or End scrolls the enclosing list, OnScrollChanged still sees follow-tail
armed and calls ScrollToEnd, undoing the reader's keyboard scroll.
Code

src/Capacitor.App/Views/ChatTabView.axaml.cs[R213-214]

+    static bool IsActivation(RoutedEventArgs e) =>
+        e.RoutedEvent == PointerPressedEvent || e.RoutedEvent == PointerReleasedEvent || e.RoutedEvent == KeyDownEvent;
Relevance

●●● Strong

Recent accepted keyboard-focus precedents support fixing this concrete regression in pending-card
navigation and scrolling.

PR-#1144
PR-#915

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
ChatItems tunnels all key-down events to OnReaderGesture, and the new guard returns before
_readerGesture is armed for every key originating below .pendingCard. OnScrollChanged
explicitly calls ScrollToEnd when follow-tail remains enabled, while the pending-card templates
provide focusable buttons from which keyboard events originate; existing keyboard tests also confirm
that question-card options hold focus.

src/Capacitor.App/Views/ChatTabView.axaml.cs[43-48]
src/Capacitor.App/Views/ChatTabView.axaml.cs[84-97]
src/Capacitor.App/Views/ChatTabView.axaml.cs[198-217]
src/Capacitor.App/Views/PendingCardTemplates.axaml[141-203]
test/Capacitor.App.Tests.Unit/ChatTabViewSmokeTests.cs[1207-1229]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Every `KeyDown` originating from a pending card is treated as card activation. Scroll-navigation keys must continue to arm the reader-gesture state so follow-tail does not undo keyboard scrolling.

## Fix Focus Areas
- src/Capacitor.App/Views/ChatTabView.axaml.cs[89-92]
- src/Capacitor.App/Views/ChatTabView.axaml.cs[213-217]
- test/Capacitor.App.Tests.Unit/ChatTabViewSmokeTests.cs[1413-1450]

## Recommended Fix
Classify a pending-card key event as activation only when its key actually invokes the focused card control, such as Enter or Space, while leaving Page Up, Page Down, Home, End, and other navigation keys on the normal reader-gesture path. Add coverage that focuses a pending-card button, performs keyboard scrolling, and verifies that the list remains at the reader-selected offset.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 64 rules
✅ Cross-repo context — repo relationships
Review mode: ⚖️ Balanced: This is a localized runtime UI gesture-handling change with meaningful event-routing and scrolling behavior, so it warrants a careful single-pass review.

Grey Divider

Tip of the day
💡 Did you know, you can type 'qodo, fix this' on a finding and the fix lands right on your PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.App/Views/ChatTabView.axaml.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3bf560b2d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// A press or key inside a pending card is answering it — an option, Next, a step chip — and
// the card then changes its own height in this layout pass. Read as the reader scrolling up,
// that would stop following exactly when the next question lands below the fold.
if (IsActivation(e) && OriginatesFromPendingCard(e)) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve reader gestures for non-activation keys

When focus is on a button inside a pending card, keys such as Page Up, Page Down, Home, or End are scrolling gestures rather than activations. This unconditional key-down exemption leaves _readerGesture false, so the resulting ScrollChanged retains _followTail and immediately calls ScrollToEnd(), preventing keyboard users from scrolling back through the chat while a card is pending. Restrict the exemption to keys that actually activate the card, such as Enter and Space.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Restricted the key exemption to Enter and Space in b3906bf. Checked against Avalonia 12.1.2: ScrollViewer.OnKeyDown pages on Page Up and Page Down, and a probe of a Page Up from a focused option showed the -477 offset delta snapping straight back to the bottom under the blanket exemption. Home and End are not handled by the ScrollViewer, but they take the reader path as well now. Pinned by Paging_up_from_a_focused_option_leaves_the_reader_where_they_paged.

ScrollViewer pages on Page Up and Page Down from any focused descendant, so
only Enter and Space, the keys that activate a card control, are an answer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@alexeyzimarev
alexeyzimarev merged commit 80be824 into main Sep 26, 2026
8 checks passed
@alexeyzimarev
alexeyzimarev deleted the alexeyzimarev/ai-3243-answering-a-question-card-in-the-desktop-chat-leaves-the branch September 26, 2026 14:23
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.

Answering a question card in the desktop chat leaves the next question below the fold

1 participant