Skip to content

feat(notice): show full toast text by default, top-right position, hover-pause dismiss - #558

Open
northwang-lucky wants to merge 1 commit into
agegr:mainfrom
northwang-lucky:notice-hover-expand
Open

feat(notice): show full toast text by default, top-right position, hover-pause dismiss#558
northwang-lucky wants to merge 1 commit into
agegr:mainfrom
northwang-lucky:notice-hover-expand

Conversation

@northwang-lucky

@northwang-lucky northwang-lucky commented Aug 20, 2026

Copy link
Copy Markdown

Problem

Extension notifications (the floating NoticeShelf toasts) currently:

  1. Truncate long messages — the single-line nowrap + ellipsis span inside a fixed 60px / max-620px container gives users no way to read the full text (e.g. pi-fff's multi-field health report).
  2. Swallow embedded newlines — senders like pi-fff compose multi-line messages with lines.join("\n"), but white-space: nowrap/normal folds every \n into a space, so structured output renders as one run-on line.
  3. Auto-dismiss after 5s with no way to pause, so a toast can disappear before the user finishes reading.

Changes

Toasts show their full text by default (components/ChatWindow.tsx, NoticeShelf):

  • Message span uses white-space: pre-line + word-break: break-word: embedded \n are preserved and long lines wrap within the existing max-width: min(100%, 620px) instead of truncating. max-height: 500 remains as a safety cap for pathological messages.
  • Toasts move from top-center to the top-right corner (justify-content: flex-end on the wrapper, align-items: flex-end on the shelf column), so each toast's right edge stays pinned and its width extends leftward as content grows.
  • transformOrigin follows the new anchor (top right).
  • The type indicator dot is top-aligned with the first text line on multi-line toasts (instead of vertically centered), and the font size is reduced from 18 to 14 for a more compact look.
  • Content taller than the 500px cap scrolls inside the text area (thin scrollbar); the type dot stays pinned at the top and does not scroll.

Hover pauses the dismiss timer (hooks/useAgentSession.ts):

  • The hook exposes setNoticeHover(id); while any notice is hovered, no expiry timer runs. Elapsed time is accrued via remainingMs/startedAt refs, so the countdown resumes from the remaining time instead of restarting at 5s. The timer resets whenever the oldest visible notice changes.

Two latent blockers fixed along the way:

  • The floating wrapper is intentionally click-through (pointer-events: "none"), which meant hover events never reached the toast itself; each item now opts back in with pointer-events: "auto" (the rest of the overlay stays click-through).
  • The notice-shelf-in entrance animation ran with fill: both and its to keyframe pins height/min-height/max-height: 60px — a completed forwards-fill animation keeps winning over inline styles and would have clamped expanded toasts forever. Fill is now backwards (entrance visuals unchanged).

Note: an earlier iteration of this PR implemented hover-to-expand instead. It was dropped because expanding changed the toast's width (fit-content shrinks once text wraps), which could eject the cursor and cause an enter/leave flicker loop. Always-expanded has a stable hover area and is simpler; hover now only controls the dismiss timer.

Behavior

before after
long text truncated with full text always visible
embedded \n folded into spaces preserved (pre-line)
position top-center top-right, right edge pinned
5s auto-dismiss always paused while hovered, resumes on leave

Testing

  • tsc --noEmit clean, npm run lint clean.
  • Verified in a real browser (Chrome via CDP against next dev) with pi-fff's 7-line health notice: toast renders fully expanded at the top-right by default, survives well past 5s while hovered, and the countdown resumes and completes after the mouse leaves.

Known accepted edge: when more than MAX_NOTICES are queued, an incoming notice can still force the oldest one out even while hovered (pre-existing queue-overflow behavior, unchanged).

@northwang-lucky northwang-lucky changed the title feat(notice): expand toast on hover and pause dismiss timer feat(notice): show full toast text by default, top-right position, hover-pause dismiss Aug 20, 2026
…ht, hover-pause dismiss

- Toasts render expanded by default: pre-line preserves embedded newlines and long lines wrap within max 620px instead of truncating with an ellipsis
- Toasts move from top-center to the top-right corner; right edges stay pinned (flex-end on both the wrapper and the shelf column) and widths extend leftward
- Content taller than the 500px cap scrolls inside the text area (thin scrollbar); the type dot stays pinned at the top and does not scroll
- Hover no longer drives layout (an earlier hover-to-expand approach could enter an enter/leave flicker loop when the expanded toast narrowed and ejected the cursor); hover now only pauses the dismiss timer via setNoticeHover/onHoverChange
- The dismiss countdown is a true pause: elapsed time is accrued via remainingMs/startedAt refs and the timer resumes from the remaining time; it resets whenever the oldest visible notice changes
- Each toast opts back into pointer events: the floating wrapper is click-through by design (pointer-events: none), so hover events never reached the toast itself
- Entrance animation fill changed from both to backwards: its 60px keyframe would otherwise stick after the animation ends and permanently clamp expanded toasts
- Type indicator dot is top-aligned with the first text line on multi-line toasts; font size reduced from 18 to 14
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