- Pin icon appears on hover for each session, filled when pinned
- Pinned sessions sort to top of list (preserved across page reloads via localStorage)
- Subtle divider separates pinned from unpinned sessions
- i18n support for pin/unpin labels (EN + FR)
Shows the model (e.g. claude-opus-4-6) as a subtle chip next to the
session label in the header. Hovering reveals agent ID if available.
Model and agentId are now extracted from sessions.list response.
Display a header bar above code blocks with the detected language
name (e.g. TypeScript, Python, Shell). Pretty-prints common language
identifiers. The copy button remains in the top-right corner.
Shows how long the agent has been thinking (e.g. '5s', '1m 23s').
Timer appears after 2 seconds to avoid flicker on fast responses.
Helps users gauge if a request is still processing or stuck.
Arrow Up/Down to navigate sessions, Enter to select, Escape to close.
Sessions use role='option' with aria-selected for screen reader support.
Mouse hover syncs with keyboard focus index for smooth interaction.
- Add Vite define for __APP_VERSION__ from package.json
- Use dynamic version in gateway connect handshake and userAgent
- Show version number in sidebar footer
- Add globals.d.ts type declaration
Updates document.title dynamically to show the current session label
(e.g. 'main — PinchChat'). Integrates with the existing notification
system so unread badges still work correctly (e.g. '(3) main — PinchChat').
Resets to plain 'PinchChat' when no session is selected.
The Chat component (and its heavy markdown dependencies ~476KB) are now
loaded via React.lazy() + Suspense. Users see the login screen faster
since only ~245KB is needed initially instead of ~750KB.
- Generate 192x192, 512x512, 180x180, 32x32, and 16x16 icons from logo
- Add manifest.json for PWA install support (standalone display)
- Add apple-touch-icon for iOS home screen
- Use sized favicons instead of full 1024x1024 logo.png
Check typeof Notification before accessing .permission or
requestPermission(). Fixes crash on browsers/contexts where
the Notification API is unavailable (e.g. some WebViews).
Show inline hint when the gateway URL doesn't start with ws:// or wss://
and disable the connect button until it's valid. Prevents confusing
connection errors from malformed URLs.
Show a spinner with 'Loading messages…' text while chat history
is being fetched during session switches, instead of briefly
flashing the empty welcome screen. Includes EN/FR i18n.
Show Discord, Telegram, WhatsApp, Signal, Slack brand icons,
clock icon for cron sessions, bot icon for sub-agents, and
globe icon for webchat sessions. Falls back to message bubble
for unknown channels.
Closes feedback #23
- Chat.tsx: replace mutable variable in render IIFE with useMemo+reduce
to satisfy react-hooks/immutability rule
- ConnectionBanner.tsx: move setState calls into a useCallback to avoid
synchronous setState in effect body (react-hooks/set-state-in-effect)
- useGateway.ts: suppress set-state-in-effect for legitimate mount init
Shows a subtle horizontal line with the date label (Today, Yesterday, or
full date) when messages span multiple days. Helps orient users when
scrolling through long conversation histories.
Includes i18n support (EN/FR) for the date labels.
Hover over any user message to reveal a retry button (↻) that resends
the message text. Disabled while a response is generating.
Includes EN/FR i18n strings.
When the tab is not focused:
- Tab title shows unread count: (3) PinchChat
- Browser notification with message preview (if permitted)
- Notifications collapse into one via tag
- Click notification to focus tab
- All clears when tab regains focus
- Permission requested on first user interaction
Disables animations and reduces transition durations to near-zero
when the user has enabled reduced-motion in their OS settings.
Also replaces README screenshot placeholder with link to live demo.
- Add GatewayMessage and JsonPayload interfaces to gateway.ts
- Type WebSocket message parsing with GatewayMessage instead of any
- Use ReturnType<typeof setTimeout> for timer refs
- Type chat event payload destructuring explicitly
- Add ChatPayloadMessage interface for extractText()
- Replace any casts in loadSessions/loadHistory with typed assertions
- Add str() helper in ToolCall.tsx for safe unknown→string extraction
- Use Extract<MessageBlock, ...> type guards instead of `as any` casts
- Type CodeBlock children prop properly
- Catches render errors and shows a styled recovery UI instead of blank white screen
- Try Again button re-renders, Reload button refreshes the page
- Error details shown in a collapsible pre block
- Full i18n support (EN/FR)
- Wraps the entire app in main.tsx
- ImageBlock: wrap clickable image in <button> with aria-label, add
role=dialog and aria-modal to lightbox overlay
- KeyboardShortcuts: add role=dialog and aria-modal to modal overlay
- CodeBlock: add aria-label to copy button
- LanguageSelector: add aria-label with current language