Commit Graph

117 Commits

Author SHA1 Message Date
Nicolas Varrot
d3e8f3143a fix: sync html lang attribute with i18n locale selection
- Set document.documentElement.lang on initial load and locale change
- Ensures screen readers and browser features respect the active language
- Removes duplicate aria-label on MessageSearch input
2026-02-13 15:11:39 +00:00
Nicolas Varrot
a44b6db77d style: add global focus-visible outline for keyboard navigation accessibility
Adds a consistent focus ring (accent-dim color, 2px) on all focusable
elements when navigated via keyboard (:focus-visible). Mouse/touch clicks
no longer show the default browser outline (:focus:not(:focus-visible)).

Elements with existing Tailwind focus:ring-* classes are unaffected since
they already handle their own focus styling.
2026-02-13 14:57:39 +00:00
Nicolas Varrot
1942ba18c7 feat: semantic <time> elements with full-date tooltip on message timestamps
- Replace raw text timestamps with <time> HTML elements
- Add dateTime attribute (ISO 8601) for accessibility and machine readability
- Add title attribute showing full date+time on hover (weekday, date, time with seconds)
- Localized tooltip using the user's language preference
- Helps users see exact timestamps for older messages shown as abbreviated times
2026-02-13 14:43:05 +00:00
Nicolas Varrot
3d707dbd90 perf: lazy-load ReactMarkdown and plugins for faster initial render
Move react-markdown, remark-gfm, remark-breaks, and rehype-highlight
imports from eager to dynamic via a LazyMarkdown wrapper component.
The ~336KB markdown bundle is now loaded on-demand after initial paint
instead of blocking the critical rendering path.

- Create LazyMarkdown component with Suspense fallback
- Pre-load plugins in parallel on module init
- Replace all ReactMarkdown usage in ChatMessage with LazyMarkdown
2026-02-13 14:26:14 +00:00
Nicolas Varrot
388879e14e style: add print stylesheet for conversation export
- Hide sidebar, header, chat input, and scroll button when printing
- Reset backgrounds to white, text to black for readability
- Code blocks get light background with borders
- Tool calls styled for print (compact, bordered)
- External links show their URL in parentheses
- Chat scroll area becomes fully visible (no overflow)
- Messages avoid page-break-inside for clean pagination
- Remove shadows and backdrop filters in print
2026-02-13 14:12:41 +00:00
Nicolas Varrot
52458b6171 test: add GatewayClient WebSocket unit tests (12 tests)
Cover core networking: connect/disconnect, challenge handshake,
event routing, request/response, error handling, timeout,
reconnect behavior, and credential updates.

Adds __APP_VERSION__ define to vitest.config.ts for test env.
Total test count: 95 → 107.
2026-02-13 14:03:16 +00:00
Nicolas Varrot
e230f9791f feat: improve accessibility — semantic HTML landmarks, skip-to-content link, ARIA attributes
- Replace div with <main> for primary chat pane (screen reader landmark)
- Replace div with <section> for split pane
- Add skip-to-content link for keyboard navigation (Tab → jump to chat input)
- Add aria-expanded and aria-label to tool call badge buttons
- Add id='chat-input' to textarea for skip link target
- Add i18n keys for new ARIA labels (EN + FR)
2026-02-13 13:28:02 +00:00
Nicolas Varrot
72e9bce4c5 fix: bust service worker cache on each build with version+timestamp
The SW cache name was hardcoded to 'pinchchat-v1', meaning PWA users
would get stale cached assets forever. Now the cache name includes
the package version and build timestamp, so each build creates a new
cache and old ones are cleaned up on activation.

Also adds:
- Periodic SW update check (every 30 min)
- Auto-reload when new SW version is detected
2026-02-13 11:57:32 +00:00
Nicolas Varrot
13f5c4ac0c fix: textarea overflow and width in highlighted input container 2026-02-13 11:52:39 +00:00
Nicolas Varrot
beced29c91 fix: textarea not expanding in flex layout with syntax highlight enabled 2026-02-13 11:51:55 +00:00
Nicolas Varrot
f8324b760c feat: add C, C++, and Java syntax highlighting support
Add c, cpp, and java to the highlight.js language bundle for code block
rendering. These are among the most commonly used languages in AI coding
conversations and were previously unsupported, falling back to plain text.

Also adds aliases: h (C headers), cc/cxx/c++/hpp/hxx (C++ variants).
2026-02-13 11:30:18 +00:00
Nicolas Varrot
ccabb8699f perf: memoize ChatMessage component and hoist ReactMarkdown plugin arrays
- Wrap ChatMessageComponent with React.memo to skip re-renders when props unchanged
- Hoist remarkPlugins and rehypePlugins arrays to module scope to avoid
  creating new array references on every render (prevents unnecessary
  ReactMarkdown re-processing)
- Use proper PluggableList type from unified instead of any
2026-02-13 10:58:11 +00:00
Nicolas Varrot
1c09ccde22 fix: improve accessibility — add ARIA attributes to ThemeSwitcher, ThinkingBlock, ThinkingIndicator, ErrorBoundary, SessionIcon
- ThemeSwitcher: aria-expanded, aria-haspopup, aria-pressed on theme/accent buttons, Escape to close, dialog role
- ThinkingBlock: aria-expanded on toggle, region role on content
- ThinkingIndicator: role=status, aria-label, decorative icon aria-hidden
- ErrorBoundary: role=alert on error state
- SessionIcon: aria-hidden on decorative SVG brand icons
2026-02-13 10:42:10 +00:00
Nicolas Varrot
cbb46115a0 fix: render markdown lists with proper bullet/number styles
Tailwind's preflight resets list-style to none on ul/ol elements.
Add explicit list-style-type rules for markdown-body: disc for ul,
decimal for ol, with circle/square for nested levels.
Also add li margin for spacing.

Closes feedback #61
2026-02-13 09:40:22 +00:00
Nicolas Varrot
5acc5a820d fix: improve light theme readability for tool badges and user bubbles
- Expose resolvedTheme in ThemeContext for theme-aware rendering
- Tool call badges: darker text colors and higher bg opacity in light theme
- User message bubbles: increased bg opacity and border strength in light theme
- Progress bars and send button already use accent CSS variables (no change needed)

Closes feedback #60
2026-02-13 09:27:42 +00:00
Nicolas Varrot
b783ae181b feat: optimistic message rendering with send status indicators
- User messages appear instantly with 'sending' state (dimmed, clock icon)
- Transitions to 'sent' (checkmark) when server acknowledges
- Shows error state (alert icon, retry visible) if send fails
- Applied to both primary and secondary sessions
2026-02-13 09:12:09 +00:00
Nicolas Varrot
85a14224ad fix: theme switcher portal — max z-index, remove backdrop-blur to fix compositing 2026-02-13 08:46:13 +00:00
Nicolas Varrot
885cd0ea22 fix: render theme switcher via portal to escape overflow/stacking context 2026-02-13 08:44:26 +00:00
Nicolas Varrot
f2162c6731 test: add i18n and highlight.js test suites (18 cases, 95 total) 2026-02-13 08:42:59 +00:00
Nicolas Varrot
317096499c fix: theme switcher click handling — prevent click-outside from swallowing button clicks 2026-02-13 08:36:12 +00:00
Nicolas Varrot
3fcaa1eef8 fix: replace remaining hardcoded zinc color classes with theme variables in App.tsx 2026-02-13 08:26:54 +00:00
Nicolas Varrot
e698e64bc8 test: add unit tests for image, utils, and credentials modules (22 cases) 2026-02-13 08:11:17 +00:00
Nicolas Varrot
3fad7b1e0a test: expand exportChat coverage with tool_result and edge case tests 2026-02-13 07:56:54 +00:00
Nicolas Varrot
bb9393c138 test: add systemEvent utility tests (21 cases) 2026-02-13 07:40:45 +00:00
Nicolas Varrot
c4725e65c2 feat: add Vitest unit tests for utility functions
- Set up Vitest with 27 tests across 3 test suites
- relativeTime: edge cases, time buckets, future timestamps
- sessionDisplayName: labels, kinds, channels, UUID truncation
- messagesToMarkdown: roles, blocks, tool calls, system events
- Add test and test:watch npm scripts
- Add test step to CI workflow
2026-02-13 06:58:39 +00:00
Nicolas Varrot
f05db6aa6d refactor: replace console.log with debug logger in gateway client
WebSocket debug logs are now silent by default. Enable with:
  localStorage.setItem('pinchchat:debug', '1')

Reduces console noise in production while keeping full debug
visibility available for developers.
2026-02-13 06:41:26 +00:00
Nicolas Varrot
6b0261f9c1 perf: reduce bundle size by using custom highlight.js subset
Replace highlight.js/lib/common (36 languages) with a curated subset
of 16 languages relevant for coding-assistant chat UIs. Both ToolCall
(direct hljs) and ChatMessage (rehype-highlight) now share the same
custom bundle from src/lib/highlight.ts.

Languages included: bash, css, diff, dockerfile, go, ini, javascript,
json, markdown, python, rust, shell, sql, typescript, xml, yaml.

Markdown chunk: 477KB → 336KB (-30%, -45KB gzipped)
2026-02-13 06:11:46 +00:00
Nicolas Varrot
de6976bae8 fix: add missing aria-labels to icon-only buttons for accessibility
- Sidebar close button: aria-label for screen readers
- Sidebar search clear button: aria-label
- ChatMessage raw JSON copy button: aria-label
- Added i18n keys: sidebar.close, sidebar.clearSearch (EN + FR)
2026-02-13 05:42:23 +00:00
Nicolas Varrot
5a4c5ba457 feat: add service worker for PWA support (offline caching, installability)
- Add sw.js with stale-while-revalidate caching for static assets
- Network-first for HTML navigation, skip API/WS requests
- Register service worker in main.tsx on page load
- Enhanced manifest.json with orientation, categories, and all icon sizes
- App is now installable as a standalone PWA on mobile and desktop
2026-02-13 05:11:39 +00:00
Nicolas Varrot
5f3f73e4c9 fix: add loading skeleton and error fallback for images
- Show a pulsing placeholder while images load
- Display a graceful error state with ImageOff icon when images fail to load
- Prevents broken image icons from cluttering the chat
2026-02-13 04:56:47 +00:00
Nicolas Varrot
dfbfc375da fix: open external links in new tab with rel=noopener noreferrer
Markdown links pointing to external URLs (http/https) now open in a
new tab with target=_blank and rel='noopener noreferrer' for security.
Internal/relative links are unaffected.
2026-02-13 04:42:39 +00:00
Nicolas Varrot
2157d7ebd5 feat: add System theme option that follows OS color scheme
Adds a 'System' option to the theme switcher that automatically uses
light or dark theme based on the OS prefers-color-scheme setting.
Dynamically updates when the OS preference changes (e.g. scheduled
dark mode). i18n labels added for EN/FR.
2026-02-13 04:13:01 +00:00
Nicolas Varrot
1770e95d0a fix: resolve all ESLint warnings (setState in useEffect)
Suppress 3 react-hooks/set-state-in-effect warnings with targeted
eslint-disable comments. These are intentional patterns:
- ChatInput: restore draft text on session switch
- MessageSearch: reset active index on query change
- ToolCall: sync open state with global collapse/expand toggle

Lint now passes with 0 errors and 0 warnings.
2026-02-13 03:41:39 +00:00
Nicolas Varrot
f09482e6cb feat: multi-tab split view for 2 sessions side by side
- Add split view button (columns icon) in sidebar session actions
- Click to open any session in a secondary pane alongside the primary
- Resizable divider between panes (drag to resize, persisted in localStorage)
- Secondary pane supports full chat: history, streaming, send, abort
- Close split view via X button or clicking the split icon again
- Each pane has independent scroll, search, and tool collapse
- Keyboard shortcut and i18n support (EN/FR)
2026-02-13 02:44:33 +00:00
Nicolas Varrot
b0492434d0 feat: syntax highlighting in chat input textarea
Real-time markdown syntax coloring while typing using a transparent
textarea overlay approach. Highlights: code blocks, inline code,
bold, italic, headings, and links. Toggle button (highlighter icon)
to enable/disable, persisted in localStorage.
2026-02-13 02:26:15 +00:00
Nicolas Varrot
35652eaeb5 feat: drag & drop session reordering in sidebar
- Drag sessions to reorder within pinned/unpinned groups
- Custom order persists in localStorage
- Visual feedback: dragged item fades, drop target highlights
- Disabled during search filtering
- Works alongside existing pin feature (pinned group stays on top)
2026-02-13 02:10:53 +00:00
Nicolas Varrot
4dfaaff2b5 feat: live markdown preview toggle in chat input
- Eye icon button next to file picker toggles preview on/off
- Shows rendered markdown above textarea in real-time
- Lazy-loads ReactMarkdown (no bundle impact when off)
- Preference persisted in localStorage
- i18n: EN/FR labels for show/hide preview
2026-02-13 01:56:13 +00:00
Nicolas Varrot
82d2e37a27 feat: raw JSON viewer toggle on each message
Add a {⁠} button (visible on hover) that toggles a collapsible panel
showing the full raw gateway JSON payload for any message.
Includes copy-to-clipboard, word-wrap, and i18n (EN/FR).
Useful for debugging and understanding the gateway protocol.

Closes feedback #52
2026-02-13 01:41:04 +00:00
Nicolas Varrot
c7cd47b09a feat: strip webhook/hook scaffolding from user messages
Messages from /hooks/agent containing SECURITY NOTICE blocks and
<<<EXTERNAL_UNTRUSTED_CONTENT>>> delimiters are now cleaned up.
Only the actual user content is displayed, with a small webhook
badge indicator showing the message originated from a webhook.

Closes feedback #54
2026-02-13 01:12:04 +00:00
Nicolas Varrot
6c19c26b84 feat: message search with Ctrl+F — filter and navigate matches in conversation 2026-02-13 00:57:19 +00:00
Nicolas Varrot
664fc0e109 feat: display thinking/reasoning content during streaming
Extract thinking blocks from delta events and render them inline
using the existing ThinkingBlock component. Previously, thinking
content was only visible after the stream finished (via history
reload). Now it appears in real-time as the agent reasons.

Closes feedback #57.
2026-02-13 00:41:23 +00:00
Nicolas Varrot
b60c0ce3c4 fix: migrate all components to theme-aware CSS variables
Replace ~150 hardcoded Tailwind color classes (bg-zinc-*, text-zinc-*,
border-white/*, text-cyan-*, bg-cyan-*) with CSS custom properties
(--pc-*) across all 17 components.

Add @theme block in index.css for Tailwind v4 theme-aware utility
classes (bg-pc-elevated, text-pc-text, border-pc-border, etc.).

Add --pc-hover, --pc-hover-strong, --pc-separator variables per theme
(white/alpha for dark/OLED, black/alpha for light).

Theme switcher (dark/light/OLED) now actually works — all UI elements
respond to theme changes in real-time.

Fixes #55
2026-02-13 00:29:50 +00:00
Nicolas Varrot
73a46f3ba7 fix: resolve all ESLint errors blocking CI releases
- Extract ThemeContext and ToolCollapseContext definitions into separate
  files to satisfy react-refresh/only-export-components rule
- Move useTheme and useToolCollapse hooks to dedicated hook files
- Fix empty catch block in ThemeContext (add comment)
- Replace Date.now() ref in ThinkingIndicator with useState initializer
- Update all imports across components

Closes feedback #58
2026-02-13 00:13:12 +00:00
Nicolas Varrot
b20bf41bf4 feat: theme switcher — dark, light, OLED modes with configurable accent colors
- Add ThemeContext with CSS custom properties for all base colors
- Three theme modes: Dark (default), Light, OLED Black
- Six accent colors: Cyan, Violet, Emerald, Amber, Rose, Blue
- Theme switcher dropdown in header (palette icon)
- Persisted in localStorage
- CSS variables replace hardcoded hex colors in index.css and components
- i18n support (EN/FR) for theme labels
2026-02-12 23:51:01 +00:00
Nicolas Varrot
bd446aa2e6 feat: word-wrap toggle on tool call content blocks
Add a wrap/nowrap toggle button on tool call parameters and results.
Default: word-wrap enabled (pre-wrap + break-words) so content fits
without horizontal scrolling. Click the toggle to switch to nowrap
mode for raw formatting with horizontal scroll.

Closes feedback #41
2026-02-12 23:46:55 +00:00
Nicolas Varrot
25e63f8d18 feat: improved thinking/reasoning indicator with elapsed time counter
When the agent is reasoning with hidden thinking (thinking=low), show a
pulsing 'Reasoning...' indicator with elapsed time instead of generic
bouncing dots. Once text content starts streaming, falls back to the
regular streaming dots.

Closes feedback #40
2026-02-12 23:42:39 +00:00
Nicolas Varrot
da2e4862dd feat: display agent avatar from OpenClaw identity config
Fetch agent identity via agent.identity.get WS method after connect.
Display avatar in message bubbles (replacing Bot icon) and in the
header (replacing the PinchChat logo when an avatar is configured).
Falls back to default icons when no avatar is set.
2026-02-12 23:29:32 +00:00
Nicolas Varrot
9f67c9e5dc fix: metadata viewer popup clipped by overflow-hidden parent
The MetadataViewer popup was rendered inside the message bubble which has
overflow-hidden, causing the popup to be invisible. Fix by using
createPortal to render the popup directly on document.body with fixed
positioning. Also adds click-outside-to-close behavior.

Closes feedback #46
2026-02-12 23:22:05 +00:00
Nicolas Varrot
b4813f091a feat: add message metadata viewer on hover
Small info button appears on hover of each message bubble.
Click to expand a panel showing raw message metadata (id, role,
timestamp, channel, sender info, etc.) from the gateway.
Useful for debugging and understanding message routing.
Collapsed by default, doesn't clutter the UI.

Closes feedback item #39
2026-02-12 23:17:27 +00:00
Nicolas Varrot
53a8655bb1 docs: release v1.14.2 — textarea scrollbar fix 2026-02-12 23:12:04 +00:00