feat: UI polish, conversation save/unsave, and window positioning#24
Merged
quiet-node merged 11 commits intomainfrom Apr 4, 2026
Merged
feat: UI polish, conversation save/unsave, and window positioning#24quiet-node merged 11 commits intomainfrom
quiet-node merged 11 commits intomainfrom
Conversation
…animated tooltips - Move "New conversation" out of the history panel and into the header bar as an icon-only + button, next to the save bookmark - Replace the text+icon History button with an icon-only button to match - Add an animated Tooltip component (portal-based, Framer Motion entry/exit) with viewport-edge clamping and arrow-offset tracking so tooltips never clip at the window boundary; labels: "Save conversation", "New conversation", "Conversation history" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
When the + button is clicked with an unsaved conversation active,
open the history dropdown and surface a SwitchConfirmation prompt
("Save & Switch" / "Just Switch") instead of silently discarding
the current session. Direct reset still happens immediately when
the conversation is already saved or has no messages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Extract a shared resetForNewConversation() helper called by all three new-conversation paths (direct reset, Just Switch, Save & Switch). The helper mirrors what replayEntranceAnimation already does for the anchor-mode resize state: - isPreExpandedRef.current = false — unblocks the ResizeObserver in anchor mode so it can call set_window_frame with the ask-bar height - outerContainerRef.current.style.minHeight = '' — removes the inline CSS constraint that kept the outer container locked at chat height Without these two resets the window stayed at full chat-mode height after creating a new conversation in any session launched above a text-selection anchor. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…sition After dragging a window that was launched in anchor mode (above a text selection), the mouseup handler was clearing windowAnchorRef and isPreExpandedRef but leaving outerContainerRef.current.style.minHeight set to the height the window had when it was last anchored. That stale minHeight kept the outer container locked at the pre-drag height, which imposed a floor on how high the window could be dragged and caused a snap/jump when the mouse was released near the screen top. Fix: also clear style.minHeight in the mouseup handler, matching what replayEntranceAnimation and requestHideOverlay already do. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
…ely reposition" This reverts commit c31f8d1. Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
When isChatMode flipped to false, max-h-[600px] was immediately removed from the container class — but ConversationView was still in the DOM for its 200ms opacity exit animation. A long conversation's natural height exceeds 600px, so without the cap the container expanded, setSize() grew the window, and the content visually dragged down before snapping back. Extracting max-h-[600px] and overflow-hidden to the static class (always present) keeps the height capped throughout the exit animation. The ask bar never approaches 600px so this has no effect on ask-bar mode layout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Clicking the filled bookmark on a saved conversation now calls delete_conversation + clears conversationId, reverting to the unsaved state without touching in-memory messages. Button label and aria-label switch to "Remove from history" when saved and back on unsave. 100% coverage maintained across all test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Deleting the currently-loaded conversation from the history panel no longer calls reset() (which cleared messages and reverted to ask-bar mode). It now only calls resetHistory() so conversationId is cleared (isSaved → false, save button re-enabled) while messages and chat view remain fully intact. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Changed the no-selection default spawn position from bottom-center (screen_height - margin) to top-center (menu_bar_height + margin). The conversation now grows downward from the top of the screen. Updated three backend tests to reflect the new expected coordinates. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
- Update two Rust test assertions in context.rs to include the +120.0 offset added in 46fc91f (tests were asserting 40.0 but function returns 160.0, causing CI failure) - Add data-history-toggle to the new-conversation (+) button so the click-outside mousedown handler doesn't race-close the history panel that handleNewConversation opens for unsaved conversations - Correct stale doc comment on handleDeleteConversation: messages are kept visible (only resetHistory is called, not reset) - Clarify useConversationHistory.reset() comment to document both the paired usage (full session reset) and the standalone usage (mark unsaved while keeping messages, e.g. after deletion from history) Co-Authored-By: Claude Sonnet 4.6 <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: header UI polish — new conversation button, icon-only history, animated tooltips
- Move "New conversation" out of the history panel and into the header
bar as an icon-only + button, next to the save bookmark
- Replace the text+icon History button with an icon-only button to match
- Add an animated Tooltip component (portal-based, Framer Motion entry/exit)
with viewport-edge clamping and arrow-offset tracking so tooltips never
clip at the window boundary; labels: "Save conversation", "New conversation",
"Conversation history"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: prompt to save before starting new conversation when unsaved
When the + button is clicked with an unsaved conversation active,
open the history dropdown and surface a SwitchConfirmation prompt
("Save & Switch" / "Just Switch") instead of silently discarding
the current session. Direct reset still happens immediately when
the conversation is already saved or has no messages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: reset window to ask-bar height when starting a new conversation
Extract a shared resetForNewConversation() helper called by all three
new-conversation paths (direct reset, Just Switch, Save & Switch).
The helper mirrors what replayEntranceAnimation already does for the
anchor-mode resize state:
- isPreExpandedRef.current = false — unblocks the ResizeObserver in
anchor mode so it can call set_window_frame with the ask-bar height
- outerContainerRef.current.style.minHeight = '' — removes the inline
CSS constraint that kept the outer container locked at chat height
Without these two resets the window stayed at full chat-mode height
after creating a new conversation in any session launched above a
text-selection anchor.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: clear minHeight constraint on drag-end so window can freely reposition
After dragging a window that was launched in anchor mode (above a text
selection), the mouseup handler was clearing windowAnchorRef and
isPreExpandedRef but leaving outerContainerRef.current.style.minHeight
set to the height the window had when it was last anchored.
That stale minHeight kept the outer container locked at the pre-drag
height, which imposed a floor on how high the window could be dragged
and caused a snap/jump when the mouse was released near the screen top.
Fix: also clear style.minHeight in the mouseup handler, matching what
replayEntranceAnimation and requestHideOverlay already do.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* Revert "fix: clear minHeight constraint on drag-end so window can freely reposition"
This reverts commit c31f8d1.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: keep max-h-[600px] and overflow-hidden always on morphing container
When isChatMode flipped to false, max-h-[600px] was immediately removed
from the container class — but ConversationView was still in the DOM for
its 200ms opacity exit animation. A long conversation's natural height
exceeds 600px, so without the cap the container expanded, setSize() grew
the window, and the content visually dragged down before snapping back.
Extracting max-h-[600px] and overflow-hidden to the static class (always
present) keeps the height capped throughout the exit animation. The ask
bar never approaches 600px so this has no effect on ask-bar mode layout.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* feat: toggle save button to unsave active conversation from history
Clicking the filled bookmark on a saved conversation now calls
delete_conversation + clears conversationId, reverting to the unsaved
state without touching in-memory messages. Button label and aria-label
switch to "Remove from history" when saved and back on unsave. 100%
coverage maintained across all test files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: keep active conversation visible when deleted from history panel
Deleting the currently-loaded conversation from the history panel no
longer calls reset() (which cleared messages and reverted to ask-bar
mode). It now only calls resetHistory() so conversationId is cleared
(isSaved → false, save button re-enabled) while messages and chat view
remain fully intact.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* feat: spawn ask bar at top-center instead of bottom-center
Changed the no-selection default spawn position from bottom-center
(screen_height - margin) to top-center (menu_bar_height + margin).
The conversation now grows downward from the top of the screen.
Updated three backend tests to reflect the new expected coordinates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: adjust overlay positioning to account for additional vertical space
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: address pre-PR review findings
- Update two Rust test assertions in context.rs to include the +120.0
offset added in 46fc91f (tests were asserting 40.0 but function
returns 160.0, causing CI failure)
- Add data-history-toggle to the new-conversation (+) button so the
click-outside mousedown handler doesn't race-close the history panel
that handleNewConversation opens for unsaved conversations
- Correct stale doc comment on handleDeleteConversation: messages are
kept visible (only resetHistory is called, not reset)
- Clarify useConversationHistory.reset() comment to document both the
paired usage (full session reset) and the standalone usage (mark
unsaved while keeping messages, e.g. after deletion from history)
Co-Authored-By: Claude Sonnet 4.6 <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: header UI polish — new conversation button, icon-only history, animated tooltips
- Move "New conversation" out of the history panel and into the header
bar as an icon-only + button, next to the save bookmark
- Replace the text+icon History button with an icon-only button to match
- Add an animated Tooltip component (portal-based, Framer Motion entry/exit)
with viewport-edge clamping and arrow-offset tracking so tooltips never
clip at the window boundary; labels: "Save conversation", "New conversation",
"Conversation history"
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: prompt to save before starting new conversation when unsaved
When the + button is clicked with an unsaved conversation active,
open the history dropdown and surface a SwitchConfirmation prompt
("Save & Switch" / "Just Switch") instead of silently discarding
the current session. Direct reset still happens immediately when
the conversation is already saved or has no messages.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: reset window to ask-bar height when starting a new conversation
Extract a shared resetForNewConversation() helper called by all three
new-conversation paths (direct reset, Just Switch, Save & Switch).
The helper mirrors what replayEntranceAnimation already does for the
anchor-mode resize state:
- isPreExpandedRef.current = false — unblocks the ResizeObserver in
anchor mode so it can call set_window_frame with the ask-bar height
- outerContainerRef.current.style.minHeight = '' — removes the inline
CSS constraint that kept the outer container locked at chat height
Without these two resets the window stayed at full chat-mode height
after creating a new conversation in any session launched above a
text-selection anchor.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: clear minHeight constraint on drag-end so window can freely reposition
After dragging a window that was launched in anchor mode (above a text
selection), the mouseup handler was clearing windowAnchorRef and
isPreExpandedRef but leaving outerContainerRef.current.style.minHeight
set to the height the window had when it was last anchored.
That stale minHeight kept the outer container locked at the pre-drag
height, which imposed a floor on how high the window could be dragged
and caused a snap/jump when the mouse was released near the screen top.
Fix: also clear style.minHeight in the mouseup handler, matching what
replayEntranceAnimation and requestHideOverlay already do.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* Revert "fix: clear minHeight constraint on drag-end so window can freely reposition"
This reverts commit c31f8d1.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: keep max-h-[600px] and overflow-hidden always on morphing container
When isChatMode flipped to false, max-h-[600px] was immediately removed
from the container class — but ConversationView was still in the DOM for
its 200ms opacity exit animation. A long conversation's natural height
exceeds 600px, so without the cap the container expanded, setSize() grew
the window, and the content visually dragged down before snapping back.
Extracting max-h-[600px] and overflow-hidden to the static class (always
present) keeps the height capped throughout the exit animation. The ask
bar never approaches 600px so this has no effect on ask-bar mode layout.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* feat: toggle save button to unsave active conversation from history
Clicking the filled bookmark on a saved conversation now calls
delete_conversation + clears conversationId, reverting to the unsaved
state without touching in-memory messages. Button label and aria-label
switch to "Remove from history" when saved and back on unsave. 100%
coverage maintained across all test files.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: keep active conversation visible when deleted from history panel
Deleting the currently-loaded conversation from the history panel no
longer calls reset() (which cleared messages and reverted to ask-bar
mode). It now only calls resetHistory() so conversationId is cleared
(isSaved → false, save button re-enabled) while messages and chat view
remain fully intact.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* feat: spawn ask bar at top-center instead of bottom-center
Changed the no-selection default spawn position from bottom-center
(screen_height - margin) to top-center (menu_bar_height + margin).
The conversation now grows downward from the top of the screen.
Updated three backend tests to reflect the new expected coordinates.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: adjust overlay positioning to account for additional vertical space
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: address pre-PR review findings
- Update two Rust test assertions in context.rs to include the +120.0
offset added in 46fc91f (tests were asserting 40.0 but function
returns 160.0, causing CI failure)
- Add data-history-toggle to the new-conversation (+) button so the
click-outside mousedown handler doesn't race-close the history panel
that handleNewConversation opens for unsaved conversations
- Correct stale doc comment on handleDeleteConversation: messages are
kept visible (only resetHistory is called, not reset)
- Clarify useConversationHistory.reset() comment to document both the
paired usage (full session reset) and the standalone usage (mark
unsaved while keeping messages, e.g. after deletion from history)
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: header UI polish — new conversation button, icon-only history, animated tooltips
- Move "New conversation" out of the history panel and into the header
bar as an icon-only + button, next to the save bookmark
- Replace the text+icon History button with an icon-only button to match
- Add an animated Tooltip component (portal-based, Framer Motion entry/exit)
with viewport-edge clamping and arrow-offset tracking so tooltips never
clip at the window boundary; labels: "Save conversation", "New conversation",
"Conversation history"
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: prompt to save before starting new conversation when unsaved
When the + button is clicked with an unsaved conversation active,
open the history dropdown and surface a SwitchConfirmation prompt
("Save & Switch" / "Just Switch") instead of silently discarding
the current session. Direct reset still happens immediately when
the conversation is already saved or has no messages.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: reset window to ask-bar height when starting a new conversation
Extract a shared resetForNewConversation() helper called by all three
new-conversation paths (direct reset, Just Switch, Save & Switch).
The helper mirrors what replayEntranceAnimation already does for the
anchor-mode resize state:
- isPreExpandedRef.current = false — unblocks the ResizeObserver in
anchor mode so it can call set_window_frame with the ask-bar height
- outerContainerRef.current.style.minHeight = '' — removes the inline
CSS constraint that kept the outer container locked at chat height
Without these two resets the window stayed at full chat-mode height
after creating a new conversation in any session launched above a
text-selection anchor.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: clear minHeight constraint on drag-end so window can freely reposition
After dragging a window that was launched in anchor mode (above a text
selection), the mouseup handler was clearing windowAnchorRef and
isPreExpandedRef but leaving outerContainerRef.current.style.minHeight
set to the height the window had when it was last anchored.
That stale minHeight kept the outer container locked at the pre-drag
height, which imposed a floor on how high the window could be dragged
and caused a snap/jump when the mouse was released near the screen top.
Fix: also clear style.minHeight in the mouseup handler, matching what
replayEntranceAnimation and requestHideOverlay already do.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* Revert "fix: clear minHeight constraint on drag-end so window can freely reposition"
This reverts commit c31f8d1.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: keep max-h-[600px] and overflow-hidden always on morphing container
When isChatMode flipped to false, max-h-[600px] was immediately removed
from the container class — but ConversationView was still in the DOM for
its 200ms opacity exit animation. A long conversation's natural height
exceeds 600px, so without the cap the container expanded, setSize() grew
the window, and the content visually dragged down before snapping back.
Extracting max-h-[600px] and overflow-hidden to the static class (always
present) keeps the height capped throughout the exit animation. The ask
bar never approaches 600px so this has no effect on ask-bar mode layout.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* feat: toggle save button to unsave active conversation from history
Clicking the filled bookmark on a saved conversation now calls
delete_conversation + clears conversationId, reverting to the unsaved
state without touching in-memory messages. Button label and aria-label
switch to "Remove from history" when saved and back on unsave. 100%
coverage maintained across all test files.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: keep active conversation visible when deleted from history panel
Deleting the currently-loaded conversation from the history panel no
longer calls reset() (which cleared messages and reverted to ask-bar
mode). It now only calls resetHistory() so conversationId is cleared
(isSaved → false, save button re-enabled) while messages and chat view
remain fully intact.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* feat: spawn ask bar at top-center instead of bottom-center
Changed the no-selection default spawn position from bottom-center
(screen_height - margin) to top-center (menu_bar_height + margin).
The conversation now grows downward from the top of the screen.
Updated three backend tests to reflect the new expected coordinates.
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: adjust overlay positioning to account for additional vertical space
Signed-off-by: Logan Nguyen <lg.131.dev@gmail.com>
* fix: address pre-PR review findings
- Update two Rust test assertions in context.rs to include the +120.0
offset added in 46fc91f (tests were asserting 40.0 but function
returns 160.0, causing CI failure)
- Add data-history-toggle to the new-conversation (+) button so the
click-outside mousedown handler doesn't race-close the history panel
that handleNewConversation opens for unsaved conversations
- Correct stale doc comment on handleDeleteConversation: messages are
kept visible (only resetHistory is called, not reset)
- Clarify useConversationHistory.reset() comment to document both the
paired usage (full session reset) and the standalone usage (mark
unsaved while keeping messages, e.g. after deletion from history)
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
+icon button with animated tooltips (via newTooltipcomponent) on all three header controls (save, new, history)delete_conversationand resetsconversationIdto unsaved — messages remain visible and can be re-saved+with unsaved messages now surfaces aSwitchConfirmationprompt (Save & Switch / Just Switch) instead of silently discarding workmax-h-[600px] overflow-hiddenmoved to always-present class to prevent content overflow during AnimatePresence exit animationsTest plan
bun run test:all)bun run validate-buildpasses with zero warnings or errors+with unsaved messages shows confirmation; "Just Switch" clears; "Save & Switch" saves then clears🤖 Generated with Claude Code