From 7606a09ba953f5e158e16c2d72f7444fa7f95ee0 Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Sun, 15 Feb 2026 20:07:33 +0000 Subject: [PATCH] fix: clipboard fallback for insecure contexts, session rename via sessions.patch - Add copyToClipboard utility with execCommand fallback for HTTP deployments (#76) - Replace all navigator.clipboard.writeText calls with copyToClipboard - Session rename now persists server-side via sessions.patch (#78) - Pass onRename callback from App to Sidebar --- FEEDBACK.md | 25 ++++++++++++++++++++++++ package-lock.json | 4 ++-- src/App.tsx | 12 ++++++++++++ src/components/ChatMessage.tsx | 11 +++++++---- src/components/CodeBlock.tsx | 9 ++++++--- src/components/Header.tsx | 3 ++- src/components/Sidebar.tsx | 9 +++++++-- src/components/ToolCall.tsx | 9 ++++++--- src/lib/clipboard.ts | 35 ++++++++++++++++++++++++++++++++++ 9 files changed, 102 insertions(+), 15 deletions(-) create mode 100644 src/lib/clipboard.ts diff --git a/FEEDBACK.md b/FEEDBACK.md index 41b9ee5..8832925 100644 --- a/FEEDBACK.md +++ b/FEEDBACK.md @@ -802,3 +802,28 @@ - **Status:** done - **Completed:** 2026-02-15 — commit `e1ba4aa`, tagged `v1.57.1` - **Description:** Session deletion doesn't persist — when deleting a session from the sidebar, it disappears visually but comes back on page reload. The deletion is only client-side/cosmetic and doesn't actually delete the session from the OpenClaw backend. Need to call the proper API endpoint to actually delete/archive the session server-side. (Feedback from Bardak) + +## Item #75 +- **Date:** 2026-02-15 +- **Priority:** high +- **Status:** open +- **Description:** Session info tooltip not clickable — clicking inside the tooltip (e.g. to copy sessionKey) closes it immediately. The click event bubbles up to the parent button that toggles the tooltip. Need stopPropagation on the tooltip container. (Note: fix attempted in v1.63.3 but marlbot-chat had stale files) + +## Item #76 +- **Date:** 2026-02-15 +- **Priority:** high +- **Status:** open +- **Description:** Code/payload copy button doesn't work — clicking the copy button on code blocks and tool call payloads does nothing. Investigate clipboard API calls in CodeBlock and ToolCall components. + +## Item #78 +- **Date:** 2026-02-15 +- **Priority:** medium +- **Status:** open +- **Source:** Josh (Bardak) +- **Description:** Session renaming — allow users to rename sessions from the sidebar. OpenClaw supports `sessions.patch` with a `label` field. Add a rename action (double-click, right-click menu, or edit icon) on session names in the sidebar. Requires `operator.admin` scope (already added in v1.63.3). + +## Item #77 +- **Date:** 2026-02-15 +- **Priority:** high +- **Status:** open +- **Description:** Compact button doesn't work — need to verify the correct OpenClaw API for triggering compaction. Current implementation sends `sessions.compact` which requires `operator.admin` scope (added in v1.63.3). If it still doesn't work, check OpenClaw docs for the correct method name and parameters. May need to use `/compact` slash command equivalent via WS. diff --git a/package-lock.json b/package-lock.json index 1ad0358..5c6a077 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pinchchat", - "version": "1.63.2", + "version": "1.64.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pinchchat", - "version": "1.63.2", + "version": "1.64.0", "license": "MIT", "dependencies": { "@tailwindcss/vite": "^4.1.18", diff --git a/src/App.tsx b/src/App.tsx index 6e35350..283abb5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -59,6 +59,17 @@ export default function App() { } }, [getClient, switchSession]); + const handleRename = useCallback(async (key: string, label: string): Promise => { + const client = getClient(); + if (!client) return false; + try { + await client.send('sessions.patch', { key, label }); + return true; + } catch { + return false; + } + }, [getClient]); + // Split pane drag useEffect(() => { if (!splitDragging) return; @@ -165,6 +176,7 @@ export default function App() { splitSession={splitSession} open={sidebarOpen} onClose={() => setSidebarOpen(false)} + onRename={handleRename} />
{/* Primary pane */} diff --git a/src/components/ChatMessage.tsx b/src/components/ChatMessage.tsx index 435cd94..543fd84 100644 --- a/src/components/ChatMessage.tsx +++ b/src/components/ChatMessage.tsx @@ -10,6 +10,7 @@ import { CodeBlock } from './CodeBlock'; import { ToolCall } from './ToolCall'; import { ImageBlock } from './ImageBlock'; import { buildImageSrc } from '../lib/image'; +import { copyToClipboard } from '../lib/clipboard'; import { Bot, User, Wrench, Copy, Check, CheckCheck, RefreshCw, Zap, Info, Webhook, Braces, Clock, AlertCircle, Bookmark, ChevronDown, Reply } from 'lucide-react'; import { t, getLocale } from '../lib/i18n'; import { useLocale } from '../hooks/useLocale'; @@ -379,9 +380,11 @@ function RawJsonPanel({ message }: { message: ChatMessageType }) { const [copied, setCopied] = useState(false); const json = JSON.stringify(message, null, 2); const handleCopy = useCallback(() => { - navigator.clipboard.writeText(json).then(() => { - setCopied(true); - setTimeout(() => setCopied(false), 2000); + copyToClipboard(json).then((ok) => { + if (ok) { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } }); }, [json]); @@ -557,7 +560,7 @@ export const ChatMessageComponent = memo(function ChatMessageComponent({ message
{!isUser && !message.isStreaming && getPlainText(message).trim() && (