fix: persist deleted sessions via localStorage blacklist

Deleted sessions would reappear after page refresh because the
gateway sessions.list response still included them. Now maintains
a localStorage blacklist of deleted session keys that filters them
out on every load, regardless of gateway-side deletion support.
This commit is contained in:
Nicolas Varrot
2026-02-12 21:40:07 +00:00
parent 32bc43d54f
commit d9e1b88a70
2 changed files with 95 additions and 4 deletions

View File

@@ -389,3 +389,77 @@
- May need to also hide the horizontal scrollbar entirely (textarea shouldn't need horizontal scroll — use word-wrap/overflow-wrap instead)
- Add `overflow-x: hidden` on the textarea to prevent horizontal scrollbar entirely
- Also check that `resize: none` is set and `word-break: break-word` / `overflow-wrap: break-word` to avoid horizontal overflow
## Item #38
- **Date:** 2026-02-12
- **Priority:** high
- **Status:** pending
- **Description:** Session deletion doesn't persist — deleted sessions reappear after page refresh
- **Details:**
- The delete session feature (v1.7.0) only deletes from local state/localStorage
- On refresh, sessions are reloaded from the gateway via loadHistory/listSessions API
- Need to actually call the gateway API to delete the session server-side
- Check if the gateway WS protocol supports session deletion (DELETE or a command message)
- If not, at minimum maintain a localStorage blacklist of deleted session keys and filter them out on reload
## Item #39
- **Date:** 2026-02-12
- **Priority:** medium
- **Status:** pending
- **Description:** Message metadata viewer — discreet button on each message to show raw metadata
- **Details:**
- Small icon button ( or ⋯) on hover of each message
- Click to expand/toggle a subtle panel showing all available metadata: timestamp, message_id, channel, sender info, session key, etc.
- Should show whatever the gateway sends in the message object (raw dump or formatted key/value pairs)
- Keep it discreet — not visible by default, only on hover, doesn't clutter the UI
- Useful for debugging and understanding message routing
- Collapsed by default, like the tool call details
## Item #40
- **Date:** 2026-02-12
- **Priority:** medium
- **Status:** pending
- **Description:** Better thinking/reasoning indicator when content is hidden
- **Details:**
- When the gateway streams a thinking block without visible content (thinking=low), PinchChat shows "Thinking..." but nothing happens for a while
- Improve the UX: show elapsed time counter, maybe a pulsing animation, and a label like "Reasoning..." or "Thinking (hidden)..."
- If the gateway sends thinking content (thinking=stream), display it in a collapsible block (already works)
- If no content is sent, at least show the user that the agent is actively reasoning and how long it's been
- Keep it purely client-side, no gateway modifications
## Item #41
- **Date:** 2026-02-12
- **Priority:** medium
- **Status:** pending
- **Description:** Tool call payload viewer — word-wrap toggle instead of horizontal scroll
- **Details:**
- Currently tool call JSON payloads/results have long lines that require horizontal scrolling
- Add a toggle button (wrap/nowrap) on the tool call content viewer
- Default to word-wrap (break-word / white-space: pre-wrap) so content fits without horizontal scroll
- Optional toggle to switch to nowrap (pre + overflow-x scroll) for when users want to see raw formatting
- Apply to both tool call parameters and tool results
## Item #42
- **Date:** 2026-02-12
- **Priority:** high
- **Status:** pending
- **Description:** Visual differentiation between user messages and assistant messages/tool calls
- **Details:**
- Currently user and assistant messages look too similar
- Add distinct background color or left border color for user messages (like classic chat apps)
- Keep it subtle and matching the dark theme (e.g. slightly different zinc shade, or a colored left border like cyan for user)
- Assistant messages stay as-is, tool calls already have their own styling
- System events should also be visually distinct (already somewhat handled but could be improved)
- Think WhatsApp/Telegram style: your messages vs their messages are clearly different
- Keep the soft palette for keratoconus — no harsh contrast
## Item #43
- **Date:** 2026-02-12
- **Priority:** medium
- **Status:** pending
- **Description:** Textarea scrollbar always visible — should only show on overflow
- **Details:**
- The vertical scrollbar in the message input textarea is always displayed
- It should only appear when the content exceeds the max height (overflow)
- Use overflow-y: auto instead of overflow-y: scroll on the textarea
- The scrollbar should be hidden when the textarea hasn't reached its max size

View File

@@ -80,12 +80,27 @@ export function useGateway() {
});
}, []);
// Deleted sessions blacklist (persisted in localStorage)
const getDeletedSessions = useCallback((): Set<string> => {
try {
const raw = localStorage.getItem('pinchchat-deleted-sessions');
return raw ? new Set(JSON.parse(raw)) : new Set();
} catch { return new Set(); }
}, []);
const addDeletedSession = useCallback((key: string) => {
const deleted = getDeletedSessions();
deleted.add(key);
localStorage.setItem('pinchchat-deleted-sessions', JSON.stringify([...deleted]));
}, [getDeletedSessions]);
const loadSessions = useCallback(async () => {
try {
const res = await clientRef.current?.send('sessions.list', {});
const sessionList = res?.sessions as Array<Record<string, unknown>> | undefined;
if (sessionList) {
setSessions(sessionList.map((s) => ({
const deleted = getDeletedSessions();
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,
@@ -104,7 +119,7 @@ export function useGateway() {
} catch {
// Silently ignore session list failures (e.g. disconnected)
}
}, []);
}, [getDeletedSessions]);
const loadHistory = useCallback(async (sessionKey: string) => {
setIsLoadingHistory(true);
@@ -394,8 +409,10 @@ export function useGateway() {
try {
await clientRef.current?.send('sessions.delete', { key, deleteTranscript: true });
} catch {
// Ignore delete failures
// Ignore delete failures — blacklist will hide it anyway
}
// Persist to blacklist so it stays hidden after refresh
addDeletedSession(key);
// Remove from local state
setSessions(prev => prev.filter(s => s.key !== key));
// If we deleted the active session, switch to main
@@ -406,7 +423,7 @@ export function useGateway() {
setMessages([]);
loadHistory(mainKey);
}
}, [loadHistory]);
}, [loadHistory, addDeletedSession]);
const logout = useCallback(() => {
if (clientRef.current) {