Skip to content

refactor: eliminate streamingContent state, inline assistant messages - #75

Merged
quiet-node merged 3 commits into
mainfrom
worktree-cryptic-exploring-curry
Apr 10, 2026
Merged

refactor: eliminate streamingContent state, inline assistant messages#75
quiet-node merged 3 commits into
mainfrom
worktree-cryptic-exploring-curry

Conversation

@quiet-node

Copy link
Copy Markdown
Owner

Summary

  • Remove streamingContent state; tokens now update the assistant message in-place
  • Eliminates component swap (unmount/remount) that triggered re-renders when streaming completed
  • Copy button hidden during streaming, shown only after response completes
  • All tests pass, 100% coverage maintained

Technical Details

Old flow: During streaming, content lived in streamingContent state in a separate bubble (key="streaming"). On Done, that bubble unmounted and a new one mounted from messages. The Streamdown renderer mode switched from "streaming" to "static", causing a re-parse and visible re-render.

New flow: When ask() is called, both user + empty assistant message are added to messages. Tokens update the last message in-place via map(). On Done, isGenerating flips to false. Same component instance, no mode change, no re-render.

Test Updates

  • Updated useOllama tests to reflect immediate placeholder creation
  • Updated ConversationView tests to use messages array instead of streamingContent prop
  • Fixed error handling test to find error message by errorKind
  • Hid empty placeholder from view (TypingIndicator covers that state)

Validation

✓ All 463 tests pass
✓ Lint + format + typecheck all pass
✓ Production build succeeds

quiet-node and others added 3 commits April 10, 2026 15:15
Move streaming content directly into messages array instead of maintaining
separate streamingContent state. Assistant placeholder added immediately
on ask(), tokens update the last message in-place, eliminating the
component swap (unmount/remount) that caused re-renders when streaming
finished. Copy button now hidden during streaming, appears only after done.

Fixes: text component re-rendering after streaming completes
Approach 1: single source of truth (one message array)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…JSDoc

- Add !isGenerating guard to canSave logic to prevent Save button appearing mid-stream
- Fix stale JSDoc on ask() that claimed code avoids array copies (it now does the opposite)
- Prevents user from saving incomplete conversations during active generation

Fixes code review issues from PR #75

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Add isGenerating to auto-scroll effect dependency array so viewport
scrolls to bottom when copy button appears after streaming finishes.
Prevents button from being cut off below the fold.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
@quiet-node
quiet-node merged commit 0bce75a into main Apr 10, 2026
3 checks passed
@quiet-node
quiet-node deleted the worktree-cryptic-exploring-curry branch April 10, 2026 20:37
quiet-node added a commit that referenced this pull request Apr 10, 2026
…#75)

* refactor: eliminate streamingContent state, inline assistant messages

Move streaming content directly into messages array instead of maintaining
separate streamingContent state. Assistant placeholder added immediately
on ask(), tokens update the last message in-place, eliminating the
component swap (unmount/remount) that caused re-renders when streaming
finished. Copy button now hidden during streaming, appears only after done.

Fixes: text component re-rendering after streaming completes
Approach 1: single source of truth (one message array)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

* fix: guard canSave against empty assistant placeholder, update stale JSDoc

- Add !isGenerating guard to canSave logic to prevent Save button appearing mid-stream
- Fix stale JSDoc on ask() that claimed code avoids array copies (it now does the opposite)
- Prevents user from saving incomplete conversations during active generation

Fixes code review issues from PR #75

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

* fix: auto-scroll when streaming ends to reveal copy button

Add isGenerating to auto-scroll effect dependency array so viewport
scrolls to bottom when copy button appears after streaming finishes.
Prevents button from being cut off below the fold.

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
…#75)

* refactor: eliminate streamingContent state, inline assistant messages

Move streaming content directly into messages array instead of maintaining
separate streamingContent state. Assistant placeholder added immediately
on ask(), tokens update the last message in-place, eliminating the
component swap (unmount/remount) that caused re-renders when streaming
finished. Copy button now hidden during streaming, appears only after done.

Fixes: text component re-rendering after streaming completes
Approach 1: single source of truth (one message array)

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

* fix: guard canSave against empty assistant placeholder, update stale JSDoc

- Add !isGenerating guard to canSave logic to prevent Save button appearing mid-stream
- Fix stale JSDoc on ask() that claimed code avoids array copies (it now does the opposite)
- Prevents user from saving incomplete conversations during active generation

Fixes code review issues from PR #75

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

* fix: auto-scroll when streaming ends to reveal copy button

Add isGenerating to auto-scroll effect dependency array so viewport
scrolls to bottom when copy button appears after streaming finishes.
Prevents button from being cut off below the fold.

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
…#75)

* refactor: eliminate streamingContent state, inline assistant messages

Move streaming content directly into messages array instead of maintaining
separate streamingContent state. Assistant placeholder added immediately
on ask(), tokens update the last message in-place, eliminating the
component swap (unmount/remount) that caused re-renders when streaming
finished. Copy button now hidden during streaming, appears only after done.

Fixes: text component re-rendering after streaming completes
Approach 1: single source of truth (one message array)

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

* fix: guard canSave against empty assistant placeholder, update stale JSDoc

- Add !isGenerating guard to canSave logic to prevent Save button appearing mid-stream
- Fix stale JSDoc on ask() that claimed code avoids array copies (it now does the opposite)
- Prevents user from saving incomplete conversations during active generation

Fixes code review issues from PR #75

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

* fix: auto-scroll when streaming ends to reveal copy button

Add isGenerating to auto-scroll effect dependency array so viewport
scrolls to bottom when copy button appears after streaming finishes.
Prevents button from being cut off below the fold.

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>

---------

Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
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