fix: session deletion persistence and action button overlap

- Fix blacklist reconciliation logic that was removing blacklisted keys
  for sessions still on the gateway (inverted filter condition)
- Move message action buttons (copy, bookmark, metadata, retry) above
  the message bubble (-top-3) to prevent overlapping text content

Closes #73, #74
This commit is contained in:
Nicolas Varrot
2026-02-15 02:02:00 +00:00
parent e1a42afd8b
commit e1ba4aaf9d
3 changed files with 19 additions and 7 deletions

View File

@@ -788,3 +788,15 @@
- **Completed:** 2026-02-14 — commit `70d29dc`, tagged `v1.47.0`
- **Source:** Josh (Bardak)
- **Description:** After OpenClaw compaction, all previous messages disappear from the chat UI. This is a bad UX — the user loses their message history visually even though the conversation continues. Ideally PinchChat should keep a local cache/history of previous messages (IndexedDB or localStorage) so the user can still scroll back and see/copy-paste old messages even after compaction. The compaction boundary could be shown with a visual separator ("— context compacted —") but old messages should remain visible above it. This is critical for usability — users need to be able to reference what they said earlier.
## Item #73
- **Date:** 2026-02-14
- **Priority:** medium
- **Status:** open
- **Description:** Message action buttons (copy, view source, etc.) are misaligned when hovering over message text. The buttons overlay/overlap the text content incorrectly. They should be properly positioned outside the text area or in a consistent location that doesn't interfere with reading. (Feedback from Bardak, screenshot: https://i.imgur.com/O9CwAix.png)
## Item #74
- **Date:** 2026-02-14
- **Priority:** high
- **Status:** open
- **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)

View File

@@ -281,7 +281,7 @@ function CopyButton({ text }: { text: string }) {
return (
<button
onClick={handleCopy}
className="absolute top-2 right-2 h-7 w-7 rounded-lg border border-pc-border bg-pc-elevated/80 backdrop-blur-sm flex items-center justify-center text-pc-text-secondary hover:text-pc-accent-light hover:border-[var(--pc-accent-dim)] transition-all opacity-0 group-hover:opacity-100"
className="absolute -top-3 right-2 h-7 w-7 rounded-lg border border-pc-border bg-pc-elevated/80 backdrop-blur-sm flex items-center justify-center text-pc-text-secondary hover:text-pc-accent-light hover:border-[var(--pc-accent-dim)] transition-all opacity-0 group-hover:opacity-100"
title={copied ? t('message.copied') : t('message.copy')}
aria-label={t('message.copy')}
>
@@ -490,7 +490,7 @@ export const ChatMessageComponent = memo(function ChatMessageComponent({ message
{!isUser && !message.isStreaming && getPlainText(message).trim() && (
<CopyButton text={getPlainText(message)} />
)}
<div className={`absolute top-2 ${isUser ? 'left-2' : 'right-10'} flex gap-1 opacity-0 group-hover:opacity-100 transition-all z-10`}>
<div className={`absolute -top-3 ${isUser ? 'left-2' : 'right-10'} flex gap-1 opacity-0 group-hover:opacity-100 transition-all z-10`}>
{onToggleBookmark && (
<button
onClick={(e) => { e.stopPropagation(); onToggleBookmark(); }}
@@ -508,7 +508,7 @@ export const ChatMessageComponent = memo(function ChatMessageComponent({ message
{isUser && onRetry && (
<button
onClick={() => onRetry(getPlainText(message))}
className={`absolute top-2 right-2 h-7 w-7 rounded-lg border border-pc-border bg-pc-elevated/80 backdrop-blur-sm flex items-center justify-center text-pc-text-secondary hover:text-pc-accent-light hover:border-[var(--pc-accent-dim)] transition-all ${message.sendStatus === 'error' ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`}
className={`absolute -top-3 right-2 h-7 w-7 rounded-lg border border-pc-border bg-pc-elevated/80 backdrop-blur-sm flex items-center justify-center text-pc-text-secondary hover:text-pc-accent-light hover:border-[var(--pc-accent-dim)] transition-all ${message.sendStatus === 'error' ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`}
title={t('message.retry')}
aria-label={t('message.retry')}
>

View File

@@ -130,14 +130,14 @@ export function useGateway() {
const sessionList = res?.sessions as Array<Record<string, unknown>> | undefined;
if (sessionList) {
const deleted = getDeletedSessions();
// Reconcile: remove blacklisted keys that still exist on the gateway
// (e.g. permanent sessions like agent:main:main that can't actually be deleted)
// Reconcile: remove blacklisted keys for sessions that no longer exist on the gateway
// (they were successfully deleted, so no need to keep hiding them)
const activeKeys = new Set(sessionList.map((s) => (s.key || s.sessionKey) as string));
const reconciled = new Set([...deleted].filter((k) => !activeKeys.has(k)));
const reconciled = new Set([...deleted].filter((k) => activeKeys.has(k)));
if (reconciled.size !== deleted.size) {
localStorage.setItem('pinchchat-deleted-sessions', JSON.stringify([...reconciled]));
}
setSessions(sessionList.filter((s) => !reconciled.has((s.key || s.sessionKey) as string)).map((s) => ({
setSessions(sessionList.filter((s) => !deleted.has((s.key || s.sessionKey) as string)).map((s) => ({
key: (s.key || s.sessionKey) as string,
label: (s.label || s.key || s.sessionKey) as string,
messageCount: s.messageCount as number | undefined,