feat: stream cancellation with stop generating button - #13
Merged
Conversation
Implement CancellationToken-based stream cancellation so users can stop Ollama inference mid-generation. Dropping the HTTP connection signals Ollama to halt via Go's context.Context propagation. - Add tokio-util CancellationToken in GenerationState (Tauri managed state) - Refactor stream_ollama to use tokio::select! for instant cancellation - New cancel_generation Tauri command + StreamChunk::Cancelled variant - Stop button replaces spinner in AskBarView during generation - Partial content preserved as assistant message on cancel - 100% test coverage maintained (144 frontend + 50 backend tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rounded-square stop button now has a CSS spinning border ring during active generation, providing clear visual feedback that inference is in progress and can be interrupted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace broken conic-gradient and miscalculated dash-offset approach with pathLength="100" on SVG rects, making all dash math work in clean percentages. Three layered strokes (head/mid/tail) at staggered offsets with decreasing opacity create a seamless comet-tail that follows the actual rounded-rect border path with no stutter at the wrap point. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The three trace layers had different initial stroke-dashoffset values, causing each to animate a different distance per cycle and drift out of sync. Replacing with animation-delay ensures all layers share the same keyframe distance (0 to -100) and move in lockstep, with the delay creating a fixed spatial offset for the comet-tail effect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `biased;` to tokio::select! so the cancellation branch is always polled first when both futures are ready. Without this, a pre-cancelled token could non-deterministically lose to an immediately-ready stream chunk, making the pre_cancelled test flaky. Update CLAUDE.md architecture to document the new Cancelled variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
The manual Default impl was uncovered by cargo-llvm-cov since no code path called it. Replace with #[derive(Default)] so the generated impl doesn't appear in coverage instrumentation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Revert new() from delegating to Self::default() — the derived Default impl is still instrumented by cargo-llvm-cov and shows as an uncovered line. Direct construction ensures new() (which is tested) covers its own body without depending on the derived impl. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
The cancellation test spawned a TCP server with a 10-second sleep to hold the connection open. When the test cancelled after 100ms, the tokio runtime aborted the spawned task — its closure never ran to completion, leaving the implicit return at the closing }); uncovered. Replace the long sleep with a Notify that the test signals after assertions, so the spawned task exits cleanly and all regions are instrumented by cargo-llvm-cov. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
This was referenced Apr 5, 2026
quiet-node
added a commit
that referenced
this pull request
Apr 10, 2026
* feat: add stream cancellation with stop generating button Implement CancellationToken-based stream cancellation so users can stop Ollama inference mid-generation. Dropping the HTTP connection signals Ollama to halt via Go's context.Context propagation. - Add tokio-util CancellationToken in GenerationState (Tauri managed state) - Refactor stream_ollama to use tokio::select! for instant cancellation - New cancel_generation Tauri command + StreamChunk::Cancelled variant - Stop button replaces spinner in AskBarView during generation - Partial content preserved as assistant message on cancel - 100% test coverage maintained (144 frontend + 50 backend tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat: add spinning border ring animation to stop generating button Rounded-square stop button now has a CSS spinning border ring during active generation, providing clear visual feedback that inference is in progress and can be interrupted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: smooth border-trace animation with pathLength normalization Replace broken conic-gradient and miscalculated dash-offset approach with pathLength="100" on SVG rects, making all dash math work in clean percentages. Three layered strokes (head/mid/tail) at staggered offsets with decreasing opacity create a seamless comet-tail that follows the actual rounded-rect border path with no stutter at the wrap point. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use animation-delay for border trace stagger instead of dashoffset The three trace layers had different initial stroke-dashoffset values, causing each to animate a different distance per cycle and drift out of sync. Replacing with animation-delay ensures all layers share the same keyframe distance (0 to -100) and move in lockstep, with the delay creating a fixed spatial offset for the comet-tail effect. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: add biased select for cancellation priority, update CLAUDE.md Add `biased;` to tokio::select! so the cancellation branch is always polled first when both futures are ready. Without this, a pre-cancelled token could non-deterministically lose to an immediately-ready stream chunk, making the pre_cancelled test flaky. Update CLAUDE.md architecture to document the new Cancelled variant. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: derive Default for GenerationState to satisfy CI coverage The manual Default impl was uncovered by cargo-llvm-cov since no code path called it. Replace with #[derive(Default)] so the generated impl doesn't appear in coverage instrumentation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: use direct construction in GenerationState::new() Revert new() from delegating to Self::default() — the derived Default impl is still instrumented by cargo-llvm-cov and shows as an uncovered line. Direct construction ensures new() (which is tested) covers its own body without depending on the derived impl. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: ensure spawned server task exits cleanly for coverage The cancellation test spawned a TCP server with a 10-second sleep to hold the connection open. When the test cancelled after 100ms, the tokio runtime aborted the spawned task — its closure never ran to completion, leaving the implicit return at the closing }); uncovered. Replace the long sleep with a Notify that the test signals after assertions, so the spawned task exits cleanly and all regions are instrumented by cargo-llvm-cov. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> --------- Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
quiet-node
added a commit
that referenced
this pull request
Apr 10, 2026
* feat: add stream cancellation with stop generating button Implement CancellationToken-based stream cancellation so users can stop Ollama inference mid-generation. Dropping the HTTP connection signals Ollama to halt via Go's context.Context propagation. - Add tokio-util CancellationToken in GenerationState (Tauri managed state) - Refactor stream_ollama to use tokio::select! for instant cancellation - New cancel_generation Tauri command + StreamChunk::Cancelled variant - Stop button replaces spinner in AskBarView during generation - Partial content preserved as assistant message on cancel - 100% test coverage maintained (144 frontend + 50 backend tests) * feat: add spinning border ring animation to stop generating button Rounded-square stop button now has a CSS spinning border ring during active generation, providing clear visual feedback that inference is in progress and can be interrupted. * fix: smooth border-trace animation with pathLength normalization Replace broken conic-gradient and miscalculated dash-offset approach with pathLength="100" on SVG rects, making all dash math work in clean percentages. Three layered strokes (head/mid/tail) at staggered offsets with decreasing opacity create a seamless comet-tail that follows the actual rounded-rect border path with no stutter at the wrap point. * fix: use animation-delay for border trace stagger instead of dashoffset The three trace layers had different initial stroke-dashoffset values, causing each to animate a different distance per cycle and drift out of sync. Replacing with animation-delay ensures all layers share the same keyframe distance (0 to -100) and move in lockstep, with the delay creating a fixed spatial offset for the comet-tail effect. * fix: add biased select for cancellation priority, update CLAUDE.md Add `biased;` to tokio::select! so the cancellation branch is always polled first when both futures are ready. Without this, a pre-cancelled token could non-deterministically lose to an immediately-ready stream chunk, making the pre_cancelled test flaky. Update CLAUDE.md architecture to document the new Cancelled variant. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: derive Default for GenerationState to satisfy CI coverage The manual Default impl was uncovered by cargo-llvm-cov since no code path called it. Replace with #[derive(Default)] so the generated impl doesn't appear in coverage instrumentation. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: use direct construction in GenerationState::new() Revert new() from delegating to Self::default() — the derived Default impl is still instrumented by cargo-llvm-cov and shows as an uncovered line. Direct construction ensures new() (which is tested) covers its own body without depending on the derived impl. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: ensure spawned server task exits cleanly for coverage The cancellation test spawned a TCP server with a 10-second sleep to hold the connection open. When the test cancelled after 100ms, the tokio runtime aborted the spawned task — its closure never ran to completion, leaving the implicit return at the closing }); uncovered. Replace the long sleep with a Notify that the test signals after assertions, so the spawned task exits cleanly and all regions are instrumented by cargo-llvm-cov. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> --------- Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
quiet-node
added a commit
that referenced
this pull request
Apr 11, 2026
* feat: add stream cancellation with stop generating button Implement CancellationToken-based stream cancellation so users can stop Ollama inference mid-generation. Dropping the HTTP connection signals Ollama to halt via Go's context.Context propagation. - Add tokio-util CancellationToken in GenerationState (Tauri managed state) - Refactor stream_ollama to use tokio::select! for instant cancellation - New cancel_generation Tauri command + StreamChunk::Cancelled variant - Stop button replaces spinner in AskBarView during generation - Partial content preserved as assistant message on cancel - 100% test coverage maintained (144 frontend + 50 backend tests) * feat: add spinning border ring animation to stop generating button Rounded-square stop button now has a CSS spinning border ring during active generation, providing clear visual feedback that inference is in progress and can be interrupted. * fix: smooth border-trace animation with pathLength normalization Replace broken conic-gradient and miscalculated dash-offset approach with pathLength="100" on SVG rects, making all dash math work in clean percentages. Three layered strokes (head/mid/tail) at staggered offsets with decreasing opacity create a seamless comet-tail that follows the actual rounded-rect border path with no stutter at the wrap point. * fix: use animation-delay for border trace stagger instead of dashoffset The three trace layers had different initial stroke-dashoffset values, causing each to animate a different distance per cycle and drift out of sync. Replacing with animation-delay ensures all layers share the same keyframe distance (0 to -100) and move in lockstep, with the delay creating a fixed spatial offset for the comet-tail effect. * fix: add biased select for cancellation priority, update CLAUDE.md Add `biased;` to tokio::select! so the cancellation branch is always polled first when both futures are ready. Without this, a pre-cancelled token could non-deterministically lose to an immediately-ready stream chunk, making the pre_cancelled test flaky. Update CLAUDE.md architecture to document the new Cancelled variant. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: derive Default for GenerationState to satisfy CI coverage The manual Default impl was uncovered by cargo-llvm-cov since no code path called it. Replace with #[derive(Default)] so the generated impl doesn't appear in coverage instrumentation. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: use direct construction in GenerationState::new() Revert new() from delegating to Self::default() — the derived Default impl is still instrumented by cargo-llvm-cov and shows as an uncovered line. Direct construction ensures new() (which is tested) covers its own body without depending on the derived impl. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> * fix: ensure spawned server task exits cleanly for coverage The cancellation test spawned a TCP server with a 10-second sleep to hold the connection open. When the test cancelled after 100ms, the tokio runtime aborted the spawned task — its closure never ran to completion, leaving the implicit return at the closing }); uncovered. Replace the long sleep with a Notify that the test signals after assertions, so the spawned task exits cleanly and all regions are instrumented by cargo-llvm-cov. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com> --------- Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
This was referenced Apr 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CancellationToken-based stream cancellation so users can stop Ollama inference mid-generation — dropping the HTTP connection signals Ollama to halt via Go'scontext.Contextpropagationcancel_generationTauri command withGenerationStatemanaged state;stream_ollamarefactored to use biasedtokio::select!for instant, deterministic cancellationTest plan
bun run validate-build— lint, format, typecheck, clippy all clean🤖 Generated with Claude Code