ClawSide — SPEC.md
ClawSide — SPEC.md
1. Concept & Vision
ClawSide is a Chrome side panel extension that acts as a bridge between your browser and local OpenClaw instance. When you select text or want to summarize a page, instead of copy-pasting to ChatGPT/Monica/sider, you just use the side panel — and OpenClaw handles it, remembers it, and can build on it later.
The feeling: OpenClaw is always there, inside your browser, watching your back without being creepy.
MVP focus: lightweight, fast, works offline except for LLM calls.
2. Design Language
Aesthetic: Dark terminal-inspired, but refined. Think VS Code meets Arc browser.
Colors:
- Background:
#0d1117(deep dark) - Surface:
#161b22(card/panel bg) - Border:
#30363d - Primary:
#58a6ff(link blue) - Accent:
#f78166(warm orange, matches OpenClaw’s orange) - Text primary:
#e6edf3 - Text muted:
#8b949e - Success:
#3fb950 - Error:
#f85149
Typography:
- UI:
Inter, system-ui fallback - Code/mono:
JetBrains Mono, monospace
Spatial system:
- Base unit: 4px
- Card padding: 16px
- Gap between sections: 12px
Motion:
- Micro: 150ms ease-out for hovers/toggles
- Panel transitions: 200ms ease
3. Architecture
Chrome Extension
├─ Content Script (floating bubble + radial menu)
├─ Side Panel (full UI)
└─ Service Worker (background.js)
↓ HTTP POST (chrome.runtime.sendMessage)
↓ HTTP POST (fetch → 127.0.0.1:18789)
OpenClaw Gateway (/v1/chat/completions)
↓
LLM (configured provider)
No bridge server needed — Chrome extensions can access localhost directly with host_permissions declared in manifest.
4. Features & Interactions
4.1 Three Interaction Modes
Floating Bubble (Primary)
- Trigger: User selects text on any page → floating bubble appears after ~250ms
- Bubble buttons: 🌐 translate, 📄 summarize, 💬 ask
- Flow: Click button → small popup appears inline → result shown directly
- Popup: 320px wide, max 280px tall, scrollable, with copy button
- Auto-hide: Bubble + popup disappear on click outside or Escape
Radial Menu
- Trigger: Long press / right-click on floating bubble → radial menu appears
- Menu items: translate, summarize, ask (arranged radially)
- Flow: Click item → opens side panel to corresponding tab → auto-triggers action
Full Side Panel
- Trigger: Click extension icon or Ctrl+Shift+P
- Tabs: Translate, Summarize, Ask, History, Settings
- Features: Full chat interface, context management, tool prompts
4.2 Translation
- Trigger: Bubble button / Side panel Translate tab / Radial menu
- Side panel flow: Select target language → click Translate → result in card
- Output: Translated text with copy button
- History: Stored in
clawside_chat_{tabId}_{urlHash} - Global page translation (radial menu): Translates all paragraphs on page
- Batch size: 10 paragraphs per request
- Shows loading/error placeholders for each paragraph
- Translation toggle: clicking translate again when translation is showing will hide it; clicking when hidden will re-show without re-requesting LLM
4.3 Page Summarization
- Trigger: Bubble button / Side panel Summarize tab / Radial menu
- Flow: Page URL + content →
/v1/chat/completions→ 3-5 sentence summary - Auto-trigger: When opening via summarize action with no existing result, automatically triggers
- Output: Summary in card with copy button and ask icon (jump to Ask with context)
- Storage:
clawside_summarize_{tabId}_{urlHash}
4.4 Ask / Chat Interface
- Trigger: Bubble button / Side panel Ask tab / Radial menu
- Flow: User question + page context → LLM → answer
- Context: Current page URL, title, content, selected text
- History: Per-tab+URL conversation, max 50 sessions with LRU eviction
- Features: Markdown rendering, streaming responses, Ctrl+Enter to send
4.5 Ask from Summarize
- Trigger: Click ask icon in summarize result header
- Flow: Jump to Ask tab → load summarize result as conversation context → auto-scroll to input
- Context format: User message + assistant message with summary content
4.6 Interaction History
- Trigger: Click “History” tab in side panel
- Output: Chronological list of all translate/summarize/ask interactions, expandable items
- Deduplication: Uses key-based system to prevent duplicate entries
- Ask:
cs_history_ask_{tabId}_{urlHash} - Translate:
cs_history_translate_{textHash} - Summarize:
cs_history_summarize_{tabId}_{urlHash}
- Ask:
4.7 Settings
- Trigger: Click gear icon in action bar
- Options: Gateway port, auth token, language preference, tool prompt customization, appearance theme
- Sub-tabs: Basic, Prompts, About
- Basic: Gateway port, auth token, language, appearance (theme), test connection button
- Prompts: Tool prompt customization (translate/summarize/ask prompt templates with {lang}, {text}, {content} placeholders)
- About: Version info, changelog link, license, support/feedback links, debug info display
5. Component Inventory
Side Panel Layout
┌─────────────────────────────────┐
│ [🌐] [📄] [💬] [⚙️] [📜] │ ← action bar (sticky)
├─────────────────────────────────┤
│ [Context: page info + refresh] │ ← page context
├─────────────────────────────────┤
│ │
│ (content area) │
│ - Result card │
│ - Chat messages │
│ - Input area │
│ │
└─────────────────────────────────┘
Result Card (Translate/Summarize)
┌─────────────────────────────────┐
│ 📝 Translate [📋] [💬] │ ← copy + ask icons
├─────────────────────────────────┤
│ │
│ Result text goes here... │
│ │
└─────────────────────────────────┘
Chat Interface (Ask)
┌─────────────────────────────────┐
│ 👤 User message │
│ 🤖 AI response (streaming) │
├─────────────────────────────────┤
│ [Input... ] [Send] │
└─────────────────────────────────┘
6. Technical Approach
Chrome Extension (Manifest V3)
Key Components:
extension/src/components/sidepanel.js— Main UI, tabs, chat, auto-trigger, debug infoextension/src/components/popup.js— Floating bubble, action dispatchextension/src/components/dock.js— Radial menuextension/src/shared/chat-session.js— Per-tab+URL chat managementextension/src/shared/panel-context.js— Page context managementextension/src/shared/tab-context-manager.js— Tab context with LRU cacheextension/src/shared/settings.js— Unified settings management with {lang} supportextension/background.js— Service worker, Promise chain scan, message routingextension/src/tools/icons.js— SVG icon system with injectSpriteextension/src/tools/openclaw.js— HTTP client for OpenClaw gatewayextension/src/tools/page.js— Page parser & translation injectorextension/src/tools/appearance.js— Theme handling and CSS injectionextension/src/tools/lru-cache.js— Generic LRU cache base classextension/src/tools/chat-lru-cache.js— Chat message persistence with LRU evictionextension/src/tools/context-lru-cache.js— Tab context storage with LRU cache
Storage Keys
| Key Pattern | Purpose |
|---|---|
clawside_settings |
User settings (port, token, language, prompts) |
clawside_chat_{tabId}_{urlHash} |
Chat history per tab+URL (max 50) |
clawside_summarize_{tabId}_{urlHash} |
Summarize results per tab+URL |
cs_history_ask_{tabId}_{urlHash} |
Ask history deduplication key |
cs_history_translate_{textHash} |
Translate history deduplication key (includes targetLang prefix) |
cs_history_summarize_{tabId}_{urlHash} |
Summarize history deduplication key |
_pendingTab, _pendingAction, _pendingMessages |
Panel-open flow (temporary) |
_tabCtxData, _tabCtxVersion |
Tab context persistence (internal) |
LRU Cache
- ChatLRUCache: maxMapSize 50 (tabs), maxLruSize 10 (sessions per tab)
- ContextLRUCache: maxMapSize 50 (tabs), maxLruSize 10 (contexts)
API Design
Extension → OpenClaw Gateway HTTP
POST /v1/chat/completions
Headers: Authorization: Bearer <token>
Body: { model: "openclaw/main", messages: [{role:"user", content: "<prompt>"}] }
Response: streaming { choices: [{delta: {content: "..."}}] }
Two-Phase Connection Test
1. GET /v1/models → Check gateway availability
2. POST /v1/chat/completions → Actual API call
Scan Logic
- Uses Promise chain pattern (.then/.catch)
- Unified 10 second timeout
- Graceful fallback handling
Floating Bubble
- Created by content script on text selection (250ms debounce)
- Positioned at horizontal center of selected text (
rect.left + rect.width/2 - bubbleWidth/2) - Default: appears below selection (
rect.bottom + 6px) - If insufficient space below: appears above selection (
rect.top - 28 - 6) - Horizontal bounds clamped to viewport edges (min 8px, max vw-100px)
- Auto-hides on click outside or selection cleared
- z-index: 2147483647 (max safe integer)
SVG Icons
- Icons stored in
extension/assets/icons/icons.svgas symbols - Loaded via
injectSprite()in sidepanel init - Usage:
<svg><use href="#cs-icon-name"></use></svg> - All icons use consistent stroke-width (1.5)
Language Resolution
- Priority: settings.language → navigator.language → fallback
- Supports “auto” mode (uses browser language)
- {lang} placeholder injected into prompt templates
Debug Info (About Tab)
- Extension ID: chrome.runtime.id
- Extension Version: manifest.json version
- User-Agent: navigator.userAgent (full string)
- OS / Platform: navigator.userAgentData?.platform
- Language: navigator.language
- Connection: navigator.connection.effectiveType + rtt
- Language Setting: settings.language value
- Gateway: 127.0.0.1:port
- Page URL: current tab URL
- Collapsible display with copy button
Two-Phase Connection Test
1. GET /v1/models → Check gateway availability and get available models
2. POST /v1/chat/completions → Test API call with determined model
- If saved model exists in available models, use it
- Otherwise, default to first available model
- Unified 60 second timeout for both phases
7. MVP Out of Scope (Completed)
- ✅ Streaming responses
- ✅ Per-tab+URL chat history
- ✅ Tool prompt customization
- ✅ Auto-trigger summarize
- ✅ Ask from Summarize
- ✅ Radial menu