Typing Indicator:
- Root cause: streaming placeholder counted as assistant message, making
showTypingIndicator think a response already existed
- Now checks if last assistant message has actual content vs empty placeholder
- iMessage bouncing dots will show reliably after sending
ChatGPT-style Animations:
- New messages fade in with slide-up (message-fade-in 0.4s)
- Streaming messages get subtle orange shimmer glow (streaming-shimmer)
- Glow wraps the message bubble via ::before pseudo-element
Layout:
- Provider usage (Claude MAX pill) moved back to top-right of chat header
- Context bar is now single clean line (context only, no provider)
- Hover popover shows context details (%, tokens, model, warnings)
Mode Dialog:
- Root cause: chat composer useLayoutEffect stole focus on every disabled
change via focusPrompt() — now only triggers on focusKey (session switch)
- Memoized SaveModeDialog with React.memo
- Stabilized onSave/onClose callbacks with useCallback
- Focus trap effect now has stable deps
Context Bar:
- Two stacked thin bars: top = context usage, bottom = provider usage
- Provider bar uses highest % across all limits (orange brand color)
- Both bars: green < amber (50%) < red (75%)
- Hover popover shows full details for both
- Click through to usage details modal
- Context bar now spans entire chat width as thin 6px progress bar
- No text visible by default - clean, minimal appearance
- Hover reveals detailed popover with:
- Context window % + token counts + model name
- Provider usage (Claude MAX) with mini progress bars per limit
- Critical warnings at 90%+
- Click-through to full usage details modal
- Color transitions: emerald < amber (50%) < red (75%+)
- Fills left-to-right like a loading bar as context grows
Typing Indicator:
- Fixed "Thinking" bubble never appearing (hasText incorrectly included isStreaming)
- Show typing dots when waitingForResponse even during streaming with no visible text
- iMessage-style bouncing dots (3 circles with staggered animation)
- TypingIndicator now shows assistant avatar alongside dots
- Added typing-bounce keyframe animation in styles.css
Mode Selector:
- Fixed use-modes.ts accessing settings properties on wrong object level
- Fixed applyMode using non-existent setter methods (now uses updateSettings)
- Fixed TS null-check errors in dialog components
- Fixed outside-click handler closing menu when clicking into save dialog
Misc:
- Removed unused variables in message-item.tsx
Context Bar:
- Merged provider usage pill (Claude MAX) into right side of context bar
- Model name shown in center to fill whitespace
- Removed standalone UsageMeter from ChatHeader
- Clicking provider pill opens full usage details modal
Typing Indicator:
- Fixed 'Thinking' indicator never showing (hasText included isStreaming)
- Keep streaming placeholder in 'streaming' status during history polling
- Users now see 'Thinking...' + cursor while waiting for response
Profile Avatars:
- AssistantAvatar: orange circle with terminal cursor icon
- UserAvatar: neutral circle with person silhouette
- Both shown next to every message bubble (24px)
Auto-Rename:
- Raised message limit from 5 to 50 (tries harder for older sessions)
- Min messages reduced from 3 to 2 (titles sooner)
- Detects generic titles ('A new session...', 'New Session', hash-based) and re-generates
- isGenericTitle() helper with pattern matching
- New /api/context-usage endpoint computes context % from gateway data
- ContextBar component shows thin progress bar below chat header
- Color-coded thresholds: green (<50%), amber (50-75%), red (75%+)
- Shows warning text at 50%/75%/90% with dismiss button
- Token count display (e.g. 134K/200K) on non-compact views
- Polls every 15s, works in both full and compact (panel) mode
- Estimates context from static overhead + cache-per-turn heuristic
- Wrap fs.stat in try/catch to skip broken symlinks (was crashing on
browser-profile/RunningChromeVersion)
- Filter node_modules, .git, .next, .cache, dist, etc from tree
- Cap recursion depth at 8 levels
- Workspace root already defaults to ~/.openclaw/workspace (gateway workspace)
Ported from ibelick/webclaw PRs #24, #10, #14, #13:
- Command palette (⌘K): search and switch sessions instantly
- Conversation export: download as Markdown, JSON, or Plain Text
- Pinned sessions: pin/unpin from context menu, shown at top of sidebar
- Context meter: token usage ring in chat header with hover details
- Keyboard shortcuts: ⌘K search, ⌘⇧O new session
New UI primitives: autocomplete, command, input, preview-card
Attachment button/preview components (composer already has built-in support)
Nav items: justify-center + px-0 when collapsed (was justify-start + px-3)
Header: justify-center when collapsed (was justify-between)
Footer settings button: same center alignment treatment
All icons now align on the same vertical axis in collapsed mode.
Removed from: skills (Back to Chat), tasks (Back to Dashboard),
memory (BackToDashboard). Cleaned up unused imports (Link, ArrowLeft,
HugeiconsIcon, useNavigate) from affected files.
Sidebar handles all navigation now — zero redundant back buttons remain.
Sidebar is now persistent — no need for:
- Dashboard/Studio mode selector dropdown (removed from dashboard header)
- 'Back to Dashboard' buttons (removed from browser, cron, settings, providers, files)
ModeSelector component deleted from dashboard-screen.tsx.
BackToDashboard imports removed from 5 files.
Sub-pages now fit inside WorkspaceShell's content area instead of
fighting for viewport height. Affected: dashboard, tasks, skills,
cron, debug, settings/providers, files.
Architecture change: sidebar is now owned by WorkspaceShell (root layout),
not by ChatScreen. All routes render inside the shell with sidebar always visible.
New files:
- src/components/workspace-shell.tsx — persistent layout with sidebar + Outlet
- src/stores/workspace-store.ts — Zustand store for sidebar/file-explorer state
Changes:
- __root.tsx: RootLayout renders WorkspaceShell instead of bare Outlet
- ChatScreen: removed sidebar ownership, uses workspace store for collapse state
Now renders as just the chat content (header + messages + composer)
- Sub-pages (Dashboard, Skills+, Channels, etc.) all get sidebar for free
Layout: grid grid-cols-[auto_1fr] — sidebar on left, content fills rest.
Sidebar state persisted via Zustand persist (openclaw-workspace-v1).
Also: added Instances to Gateway sidebar section.
Sidebar: added Instances (placeholder — no gateway RPC exists yet)
Gateway API proxy routes (all fetch live from gateway via WebSocket RPC):
- /api/gateway/channels → channels.status
- /api/gateway/sessions → sessions.list (limit 100, with context weight)
- /api/gateway/usage → sessions.usage + usage.cost
- /api/gateway/agents → agents.list
- /api/gateway/nodes → node.list
GatewayDataScreen component: reusable live data viewer with:
- Auto-polling (configurable interval)
- Connection status indicator (green/amber/red dot)
- Last updated timestamp
- Loading spinner → error state with retry button
- Recursive JSON tree renderer for raw gateway data
Routes updated: /channels, /sessions, /usage, /agents, /nodes
all now show live gateway data instead of placeholder.
/instances remains placeholder (no gateway endpoint).
STUDIO section now contains all pages we built rich UI for:
- Dashboard, New Session, Search, Browser, Terminal, Tasks (core)
- Skills+, Cron+, Logs+, Debug+, Files+, Memory+ (Studio-enhanced)
GATEWAY section = raw control plane stubs (pending UI):
- Channels, Sessions, Usage, Agents, Nodes
SETTINGS section = Config + Providers only
Removed AGENT section (merged: enhanced items→Studio, raw items→Gateway)
The + suffix marks pages where we've built beyond default gateway views.
- Notifications: bell icon in header with dropdown popup (replaces widget)
Shows session lifecycle events, red dot on errors
- Moved '+ Widgets' and 'Reset' out of header → inline above grid
Makes it clear these controls belong to the widget area
- Header now: logo + mode selector | time/weather | bell + theme + settings
- Widget controls show as text buttons: '+ Widgets' '↻ Reset'
Old localStorage cache was cramming new tasks widget into tiny cell.
Bumped layout key to v3 and widget visibility to v2 so existing users
get fresh defaults. Tasks widget upgraded to 'L' tier (full width).
- Added Tasks nav item to workspace sidebar (between Cron and Files)
- Dashboard widget: mini-kanban showing all 4 columns with top 3
tasks per status, priority badges, counts, '+N more' overflow
- Widget title shows 'X active · Y done' summary
- /tasks route accessible from both sidebar and widget 'View all'
Integrated task system built into Studio, zero backend dependencies:
- Zustand store (localStorage-backed) with real project data seeded
from Mission Control: Studio tasks, MyAgencyLab, OpenClaw
- Dashboard widget: compact list view with status summary bar,
priority badges, top 5 active tasks, 'View all →' link
- Full /tasks route: 4-column kanban board (Backlog → In Progress →
Review → Done), add/edit/move/delete tasks, detail panel
- Task model: id, title, description, status, priority, project, tags
No new endpoints. No external dependencies. No demo badge.
- Status strip reduced to '● Connected · Opus 4.6' (removed session count + uptime — hero row owns those)
- Status text quieted to primary-400, model to primary-500
- Ambient time/weather: removed pill border + background, reduced to text-[11px] primary-400
- Workspace → CTA remains visually dominant (bordered, far right)
- No new features, data sources, or layout changes
- Left: Logo + flex-col title block (h1 line 1, status p line 2)
- Status strip always visible (not hidden on mobile), uses flex-col
- Right: time/weather text, icon group (theme/reset/settings), Workspace→ CTA
- No new features, data sources, or controls — layout + typography only