Repository navigation
feat: implement chat thread deletion - #174
Open
pasan-wijekoon wants to merge 2 commits into
Open
pasan-wijekoon wants to merge 2 commits into
pasan-wijekoon wants to merge 2 commits into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds the ability to delete chat threads (conversations) from the sidebar. Previously, threads could only be created but never removed, leaving users with no way to clean up old or unwanted conversations. This implementation covers the full stack — from the UI trash icon in the sidebar all the way down to transactional database cleanup and CopilotKit Intelligence thread removal.
Motivation
Chat threads accumulate over time with no way to remove them. This change gives users control over their conversation history, keeping the sidebar clean and preventing stale threads from cluttering the workspace.
Changes
UI —
ThreadList.tsxThe thread list sidebar items are restructured to support a hover-revealed delete action.
Before: each thread was a single
<button>that selected the thread on click.After: each thread is wrapped in a
<div class="thread-row">that holds two buttons side-by-side:thread-select— the existing selection button (icon + title + dot name)thread-delete— a new trash icon button, revealed on hover/focus, with a fully descriptivearia-labelfor accessibilityThe component's prop interface gains a new required
onDelete: (id: string) => voidcallback.Styles —
style.cssThree new CSS rule-sets handle the row layout and progressive disclosure of the delete button:
The delete button is invisible by default and only appears when the user hovers over or focuses within the row, keeping the sidebar uncluttered while remaining fully keyboard-accessible via
:focus-within.Client Logic —
App.tsxA new
deleteConversationasync handler is defined insideApp:Key behaviors:
confirm()dialog naming the thread. Cancelling aborts without making any network call.DELETE /conversations/:idvia the existingmutatehelper. Returns early if the request fails (the helper already surfaces errors).undefinedif there are none left), preventing a broken "ghost" view.The handler is wired into
<ThreadList>via the newonDeleteprop:<ThreadList ... onNew={() => void newConversation()} + onDelete={(id) => void deleteConversation(id)} />HTTP Route —
workspace-routes.tsA single new route is added to the Hono router:
Errors thrown by
platform.deleteConversation(e.g., thread not found, Intelligence API failure) propagate to the existing error-handling middleware, returning appropriate HTTP status codes without any additional boilerplate.Platform Service —
platform.tsPlatformgains a newdeleteConversationmethod that orchestrates both the remote and local cleanup in the correct order:The method:
requireReady()).intelligence.deleteThread(...)to remove the thread's message history from CopilotKit Intelligence.workspace.deleteThread(threadId)to remove local records — keeping remote and local state in sync. If the Intelligence call throws, the local DB is left untouched.Data Layer —
workspace.tsWorkspaceStoregains adeleteThreadmethod that wraps all removals in a SQLite transaction:All seven dependent tables are cleaned up before the thread binding itself is removed. The
ownerIdguard on the finalDELETEensures a user cannot delete another owner's thread even if they somehow obtain the ID. Any failure rolls back the entire transaction so the DB is never left in a partial state.Testing
Automated
# Unit tests (workspace data layer) npx vitest run tests/workspace.test.tsManual Verification
confirm()dialog shows the thread title.DELETE /conversations/:idrequest returns{"ok":true}.