mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 06:47:02 +08:00
feat(core): support ai chat delete action (#13655)
<img width="411" height="205" alt="截屏2025-09-26 10 58 39" src="https://github.com/user-attachments/assets/c3bce144-7847-4794-b766-5a3777cbc00d" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Delete icon added to AI session history with tooltip and confirmation prompt; deleting current session opens a new session. - Session deletion wired end-to-end (toolbar → provider → backend) and shows notifications. - Improvements - Cleanup now supports deleting sessions with or without a document ID (document-specific or workspace-wide). - UI tweaks for cleaner session item layout and safer click handling (delete won’t trigger item click). <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -441,7 +441,7 @@ declare global {
|
||||
) => Promise<AIHistory[] | undefined>;
|
||||
cleanup: (
|
||||
workspaceId: string,
|
||||
docId: string,
|
||||
docId: string | undefined,
|
||||
sessionIds: string[]
|
||||
) => Promise<void>;
|
||||
ids: (
|
||||
|
||||
@@ -130,6 +130,9 @@ export class AIChatPanelTitle extends SignalWatcher(
|
||||
@property({ attribute: false })
|
||||
accessor openDoc!: (docId: string, sessionId: string) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor deleteSession!: (session: BlockSuitePresets.AIRecentSession) => void;
|
||||
|
||||
private readonly openPlayground = () => {
|
||||
const playgroundContent = html`
|
||||
<playground-content
|
||||
@@ -182,6 +185,7 @@ export class AIChatPanelTitle extends SignalWatcher(
|
||||
.onTogglePin=${this.togglePin}
|
||||
.onOpenSession=${this.openSession}
|
||||
.onOpenDoc=${this.openDoc}
|
||||
.onSessionDelete=${this.deleteSession}
|
||||
.docDisplayConfig=${this.docDisplayConfig}
|
||||
.notificationService=${this.notificationService}
|
||||
></ai-chat-toolbar>
|
||||
|
||||
@@ -237,6 +237,31 @@ export class ChatPanel extends SignalWatcher(
|
||||
return this.session;
|
||||
};
|
||||
|
||||
private readonly deleteSession = async (
|
||||
session: BlockSuitePresets.AIRecentSession
|
||||
) => {
|
||||
if (!AIProvider.histories) {
|
||||
return;
|
||||
}
|
||||
const confirm = await this.notificationService.confirm({
|
||||
title: 'Delete this history?',
|
||||
message:
|
||||
'Do you want to delete this AI conversation history? Once deleted, it cannot be recovered.',
|
||||
confirmText: 'Delete',
|
||||
cancelText: 'Cancel',
|
||||
});
|
||||
if (confirm) {
|
||||
await AIProvider.histories.cleanup(
|
||||
session.workspaceId,
|
||||
session.docId || undefined,
|
||||
[session.sessionId]
|
||||
);
|
||||
if (session.sessionId === this.session?.sessionId) {
|
||||
this.newSession();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private readonly updateSession = async (options: UpdateChatSessionInput) => {
|
||||
await AIProvider.session?.updateSession(options);
|
||||
const session = await AIProvider.session?.getSession(
|
||||
@@ -413,6 +438,7 @@ export class ChatPanel extends SignalWatcher(
|
||||
.togglePin=${this.togglePin}
|
||||
.openSession=${this.openSession}
|
||||
.openDoc=${this.openDoc}
|
||||
.deleteSession=${this.deleteSession}
|
||||
></ai-chat-panel-title>
|
||||
${keyed(
|
||||
this.hasPinned ? this.session?.sessionId : this.doc.id,
|
||||
|
||||
@@ -42,6 +42,11 @@ export class AIChatToolbar extends WithDisposable(ShadowlessElement) {
|
||||
@property({ attribute: false })
|
||||
accessor onOpenDoc!: (docId: string, sessionId: string) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onSessionDelete!: (
|
||||
session: BlockSuitePresets.AIRecentSession
|
||||
) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor docDisplayConfig!: DocDisplayConfig;
|
||||
|
||||
@@ -198,7 +203,9 @@ export class AIChatToolbar extends WithDisposable(ShadowlessElement) {
|
||||
.workspaceId=${this.workspaceId}
|
||||
.docDisplayConfig=${this.docDisplayConfig}
|
||||
.onSessionClick=${this.onSessionClick}
|
||||
.onSessionDelete=${this.onSessionDelete}
|
||||
.onDocClick=${this.onDocClick}
|
||||
.notificationService=${this.notificationService}
|
||||
></ai-session-history>
|
||||
`,
|
||||
portalStyles: {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { WithDisposable } from '@blocksuite/affine/global/lit';
|
||||
import { scrollbarStyle } from '@blocksuite/affine/shared/styles';
|
||||
import { unsafeCSSVar, unsafeCSSVarV2 } from '@blocksuite/affine/shared/theme';
|
||||
import { ShadowlessElement } from '@blocksuite/affine/std';
|
||||
import { DeleteIcon } from '@blocksuite/icons/lit';
|
||||
import { css, html, nothing, type PropertyValues } from 'lit';
|
||||
import { property, query, state } from 'lit/decorators.js';
|
||||
|
||||
@@ -62,7 +63,6 @@ export class AISessionHistory extends WithDisposable(ShadowlessElement) {
|
||||
position: relative;
|
||||
display: flex;
|
||||
height: 24px;
|
||||
padding: 2px 4px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
@@ -85,6 +85,7 @@ export class AISessionHistory extends WithDisposable(ShadowlessElement) {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
padding: 2px 4px;
|
||||
color: ${unsafeCSSVarV2('text/primary')};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -94,7 +95,7 @@ export class AISessionHistory extends WithDisposable(ShadowlessElement) {
|
||||
.ai-session-doc {
|
||||
display: flex;
|
||||
width: 120px;
|
||||
padding: 0px 4px;
|
||||
padding: 2px;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
@@ -117,6 +118,36 @@ export class AISessionHistory extends WithDisposable(ShadowlessElement) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-session-item-delete {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: ${unsafeCSSVarV2('layer/background/primary')};
|
||||
border-radius: 2px;
|
||||
padding: 2px;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition:
|
||||
opacity 0.2s ease,
|
||||
visibility 0.2s ease;
|
||||
|
||||
svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: ${unsafeCSSVarV2('icon/primary')};
|
||||
}
|
||||
}
|
||||
|
||||
.ai-session-item:hover .ai-session-item-delete {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
${scrollbarStyle('.ai-session-history')}
|
||||
@@ -134,6 +165,11 @@ export class AISessionHistory extends WithDisposable(ShadowlessElement) {
|
||||
@property({ attribute: false })
|
||||
accessor onSessionClick!: (sessionId: string) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onSessionDelete!: (
|
||||
session: BlockSuitePresets.AIRecentSession
|
||||
) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
accessor onDocClick!: (docId: string, sessionId: string) => void;
|
||||
|
||||
@@ -272,6 +308,16 @@ export class AISessionHistory extends WithDisposable(ShadowlessElement) {
|
||||
${session.docId
|
||||
? this.renderSessionDoc(session.docId, session.sessionId)
|
||||
: nothing}
|
||||
<div
|
||||
class="ai-session-item-delete"
|
||||
@click=${(e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
this.onSessionDelete(session);
|
||||
}}
|
||||
>
|
||||
${DeleteIcon()}
|
||||
<affine-tooltip>Delete</affine-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})}
|
||||
|
||||
@@ -261,7 +261,7 @@ export class CopilotClient {
|
||||
|
||||
async cleanupSessions(input: {
|
||||
workspaceId: string;
|
||||
docId: string;
|
||||
docId: string | undefined;
|
||||
sessionIds: string[];
|
||||
}) {
|
||||
try {
|
||||
|
||||
@@ -794,7 +794,7 @@ Could you make a new website based on these notes and send back just the html fi
|
||||
},
|
||||
cleanup: async (
|
||||
workspaceId: string,
|
||||
docId: string,
|
||||
docId: string | undefined,
|
||||
sessionIds: string[]
|
||||
) => {
|
||||
await client.cleanupSessions({ workspaceId, docId, sessionIds });
|
||||
|
||||
Reference in New Issue
Block a user