Skip to content

feat: add a notification center and keep toasts longer - #230

Merged
kahme247 merged 89 commits into
kahme247:mainfrom
andrebrait:feat/notification-center
Oct 8, 2026
Merged

kahme247 merged 89 commits into
kahme247:mainfrom
andrebrait:feat/notification-center

Conversation

@andrebrait

@andrebrait andrebrait commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Toasts disappeared after 4 seconds, which is too short to read them or use their buttons. Once a toast was gone, it could not be brought back.

What changes

  • Longer toasts. Success and info toasts stay 6 s, errors 10 s. Android's defaults (Toast 2 s / 3.5 s, Snackbar 1.5 s / 2.75 s) are too short for toasts that carry actions. Material recommends 4–10 s, and Windows and macOS banners stay about 5 s. An explicit timeout still wins, including 0 for toasts that stay until dismissed.
  • Notifications tab in the right panel, next to Explorer and Git. It lists the last 100 toasts, newest first, each with its time. Entries keep their action buttons (for example the update toast's Copy), and can be dismissed one at a time or with Clear all. The pinned Explorer and Git tabs now share one PinnedTab component in TabBar.
  • Unread badge on the panel toggle. The toggle keeps its icon. While there are unread notifications it shows their count, and tapping it opens the panel on the Notifications tab; otherwise it opens and closes the panel as before. Unread entries are highlighted while the tab is open and become read when you leave the tab or choose Mark all as read. Re-announcing a notice under the same id keeps its read state, so the update toast that re-fires on every tab focus doesn't re-badge.
  • OS notifications are listed too. Task-completion notifications and the agent notify host tool are recorded with toastHistory.record(). A notify message is recorded even when the browser blocks notifications. A completion now falls back to an in-app toast when the permission prompt is dismissed or the Notification API is missing.
  • No new top-bar button. An earlier revision of this PR added a bell to the top bar; it squeezed the session title on phones (195 px → 118 px at 390 px wide), so it moved into the panel.
  • History is kept in memory only. Persisting it would drop the JSX descriptions that carry the action buttons.

Verification

  • tsc --noEmit and eslint are clean. components/*.test.mjs and components/ui/toast.test.mjs: 210 pass, 0 fail. hooks/useAgentSession.rpc.test.mjs passes with the new notify test.
  • New tests cover the 6 s / 10 s defaults and overrides, the 100-entry cap, replacement by reused id, distinct ids for recorded entries, subscriber notification, unread state, Mark all as read (including no notification when nothing is unread), and read state kept on re-announce.
  • Headless Chromium at 390 px and 1280 px: the badge shows the unread count, tapping the toggle opens the Notifications tab, the session title is back to full width (194 px at 390 px), closing the panel marks entries read and removes the badge, Mark all as read and Clear all work, and keyboard focus stays in the panel after dismissing the last entry.

andrebrait and others added 30 commits October 3, 2026 12:54
The pruning that bounds the job store deletes a `gone` tombstone with no
client-visible signal, so a browser that adopted another browser's recording
could get a 404 and raise an error toast for a job that had in fact been
delivered elsewhere. Only the browser that recorded (or retried) a job holds
the audio to fall back on, so only it may report it as a failure; a follower
that is locked out of the transcript is standing down by design.

The attached-messages toast also blamed the agent for a rule that has nothing
to do with it: `sendQueued` refuses attachments whether or not a run is
active, and `followUp` is dispatched immediately when the agent is idle. The
rule and its explanation are now one predicate, `queueAllowsAttachments`, used
by both `sendQueued` and the dictation branch, so they cannot drift apart.
Stop withdrew queued messages one by one from this client's queue snapshot
before aborting. A steer missing from that snapshot (promoted after the last
queue_update) or claimed by the run but not yet recorded was never withdrawn,
and omp drained it into a new turn right after the abort: the user pressed
Stop and the steer ran anyway.

Stop now sends abort_and_restore_queue, omp's Esc in one step (clearQueue
forInterrupt, then abort), and restores the returned texts to the draft. omp
without the command falls back to the per-entry withdrawal.
Review of the atomic Stop:
- Capture the run fence at the click; a failed request is retried once and
  only while that run is current, so it can never abort a newer run.
- Fall back to per-entry withdrawal only on "Unknown command" (omp without
  abort_and_restore_queue); any other failure retries the same command, and
  warns (new localized queueRestoreUncertain) when nothing came back.
- Narrow the try to the request; drop an image-only "[Image]" label.
- Tests: retry restores, lost response warns, no retry into a newer run,
  overlapping Stops share one request; tidy the server abort branch.
Prepare ompweb for publisher-default Auto effort in can1357/oh-my-pi#14114. Persist the choice through native config, preserve provider settings, and show the control and search result only when Reasoning is auto.
…in browser code

Older browsers lack these APIs: Settings crashed on open, session catch-up stalled, and transcription polling failed. Use AbortController timers and plain promises, keeping the same timeouts and cancellation. Fixes kahme247#196.
Reuse the workspace refresh control and success checkmark. Bypass the server usage cache only for manual refreshes while retaining the existing five-minute automatic polling schedule.
Keep background polling silent, preserve refresh queries on older browsers, and ensure manual refresh is not satisfied by a concurrent background fetch. Cover persisted caching and hook behavior with regression checks.
A manual refresh must attempt cache invalidation even when the automatic fetch it waited behind rejects. Extend the CLI-backed regression to cover this failure path.
The reasoning trigger shows the terminal status line's level glyph instead
of the level name (chevron hidden in narrow toolbars), so the model name
keeps the room on phones. The menu pairs each glyph with its name.

Slow was wired only for saved sessions, so a new session never showed it
even after Fast had spawned omp. Offer it in new sessions too, and refresh
live state after a new-session model change so support follows the model.
Without live omp state (new or idle session), predict omp's slowModeTarget
from the catalog: /api/models marks supportsSlowMode by provider and
reports providers.anthropic.slowMode, read fresh outside the models cache.
Claude shows the shared setting (global scope); flex starts off (session).
Live get_state wins as soon as omp runs, and clicking the toggle spawns
omp like Fast does.
…t code

Older mobile browsers lack these APIs, so calling them crashed the whole
tree into the unexpected-error screen. Use browser-safe helpers.

Fixes kahme247#196

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011RZdyVoycH4WVVDWJNctDR
… again

Handle the session-persistence notice: re-read identity, keep the run
state, re-key the registry (old id stays as an alias) and reuse a live
wrapper that already reports the requested session file.

Fixes kahme247#202

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011RZdyVoycH4WVVDWJNctDR
- auto-name asks omp (generate_title, else /rename) and only falls back to
  the derived title when omp cannot (Fixes kahme247#203)
- provider detail lists the accounts omp reports for the provider (Refs kahme247#197)
- tests and AGENTS.md notes

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011RZdyVoycH4WVVDWJNctDR
fix(queue): take the queue back on Stop through omp's atomic Esc
feat: add a Slow toggle and provider-neutral usage-limit badge
…urce

feat(settings): add Auto thinking source selector
…efresh

feat(usage): add manual provider usage refresh
…-compatibility

fix(client): avoid AbortSignal.timeout/any and Promise.withResolvers
Take main's kahme247#199 client fix instead of the duplicate abort-signal helper.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011RZdyVoycH4WVVDWJNctDR
…edes-297gxo

Support omp session moves and native title generation
Show the branch below the workspace name, keep the activity indicator beside the name, and preserve space for rename and branch controls.
Use OMP live resolution instead of duplicating discovery.
Keep the native notice preference and never resume stopped sessions for
inspection.
Add an icon-only dismiss button that hides the current report until its diagnostics change, and shorten the disable action to "Turn this off" so the notice text keeps its width on phones.
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (3e63bf2) onto upstream/main.

Semantic fixup vs PR kahme247#218: drop the removed completion notifier's record call; log OS-shown notifications from useNotifications in toastHistory.
The top-bar bell crowded the mobile header and squeezed the session
title. Notifications now live in a Notifications tab of the right
panel. Unread entries badge the panel toggle, which then opens that
tab; entries become read when the user leaves it or marks all read.
- Keep keyboard focus in the tab panel after dismissing the last entry;
  Mark all as read uses aria-disabled so it stays focusable
- Badge uses --accent-strong/--on-accent so it stays readable on every
  theme
- Subscribe AppShell and the right panel to the unread count only, so
  toasts no longer re-render the whole shell
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (f988972) onto upstream/main.

Semantic fixup vs PR kahme247#218: drop the removed completion notifier's record call; log OS-shown notifications from useNotifications in toastHistory.
A newer OMP or omp-web version reused the previous toast id, so it
inherited the read state and never badged the panel toggle.
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (a3da881) onto upstream/main.

Semantic fixup vs PR kahme247#218: log OS-shown notifications from useNotifications in toastHistory (reapply on scratch rebuilds).
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (a3da881) onto upstream/main.

Semantic fixup vs PR kahme247#218: log OS-shown notifications from useNotifications in toastHistory (reapply on scratch rebuilds).
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (5df0463) onto upstream/main.

Semantic fixup vs PR kahme247#218 (reapply on scratch rebuilds): useNotifications logs OS-shown notifications in toastHistory with the session onClick; history entries carry the toast onClick; NotificationList rows run it on click (with a test).
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (5df0463) onto upstream/main.

Semantic fixup vs PR kahme247#218 (reapply on scratch rebuilds): useNotifications logs OS-shown notifications in toastHistory with the session onClick; history entries carry the toast onClick; NotificationList rows run it on click (with a test).
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (93acbe7) onto upstream/main.

Semantic fixup vs PR kahme247#218 (reapply on scratch rebuilds): useNotifications logs OS-shown notifications in toastHistory with the session onClick; history entries carry the toast onClick; NotificationList rows run it on click (with a test).
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (93acbe7) onto upstream/main.

Semantic fixup vs PR kahme247#218 (reapply on scratch rebuilds): useNotifications logs OS-shown notifications in toastHistory with the session onClick; history entries carry the toast onClick; NotificationList rows run it on click (with a test).
andrebrait added a commit to andrebrait/ompweb that referenced this pull request Oct 7, 2026
Squashed from origin/feat/notification-center (93acbe7) onto upstream/main.

Semantic fixup vs PR kahme247#218 (reapply on scratch rebuilds): useNotifications logs OS-shown notifications in toastHistory with the session onClick; history entries carry the toast onClick; NotificationList rows run it on click or Enter (focusable when they have one, since there is no Open link), with a test.
@andrebrait
andrebrait force-pushed the feat/notification-center branch from b28fb2a to 6624a8e Compare October 8, 2026 08:35

@kahme247 kahme247 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Review — approve (SHIP)

Verified by independent review in a scratch worktree. Typecheck clean, toast tests 7/7, rpc hook tests 142/142, lint 0 errors.

Notification center (#230). Bounded both layers (4 on-screen, 100 history); no manual timers exist (dismissal owned by the base-ui Toast manager), so nothing to leak. Mark-read correct across tabs; roles correct. Two sub-minor notes for later: no aria-live on the list, and the panel toggle jumps to Notifications while unread. Mutation kills unexecuted (harness interruptions) — the assertions are direct value tests, high confidence, and the merge queue re-runs everything.

Label (#225). Four changed lines: drops the literal "+" from modelsConfig.addProvider in en/ja/zh while addModel keeps its "+"; both provider buttons render a separate <Plus> lucide icon, so the duplicate plus is genuinely gone in all locales. Nothing else to check.

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.

6 participants