- {exportDialogNote ? (
-
- ) : null}
)
})
diff --git a/ui/web/components/features/notes/NotesShell.tsx b/ui/web/components/features/notes/NotesShell.tsx
index 91725b85700..298467dd51d 100644
--- a/ui/web/components/features/notes/NotesShell.tsx
+++ b/ui/web/components/features/notes/NotesShell.tsx
@@ -245,6 +245,7 @@ function EditorPane({
isAutoSaving={autoSaving}
lastSavedAt={lastSavedAt}
wordpressConfigured={wordpressConfigured}
+ onDelete={selectedNote ? () => handleDeleteNote(selectedNote) : undefined}
/>
)
}
diff --git a/ui/web/components/features/notes/RagIndexPanel.tsx b/ui/web/components/features/notes/RagIndexPanel.tsx
index 683f61e439c..a21cc167ab4 100644
--- a/ui/web/components/features/notes/RagIndexPanel.tsx
+++ b/ui/web/components/features/notes/RagIndexPanel.tsx
@@ -3,6 +3,10 @@
import { useState } from 'react'
import { Database, Trash2, Loader2 } from 'lucide-react'
import { Button } from '@/components/ui/button'
+import {
+ DropdownMenuItem,
+ DropdownMenuLabel,
+} from '@/components/ui/dropdown-menu'
import {
AlertDialog,
AlertDialogAction,
@@ -32,6 +36,9 @@ async function extractErrorMessage(err: unknown, fallback: string): Promise void
}
type Operation = 'indexing' | 'deleting' | null
@@ -42,7 +49,7 @@ function parseChunkCount(data: unknown): number | null {
return typeof value === 'number' && Number.isFinite(value) ? value : null
}
-export function RagIndexPanel({ noteId }: RagIndexPanelProps) {
+export function RagIndexPanel({ noteId, variant = 'inline', onMenuClose }: RagIndexPanelProps) {
const { supabase } = useSupabase()
const { chunkCount, indexedAt, isLoading, refresh } = useRagStatus(noteId)
const [operation, setOperation] = useState(null)
@@ -100,13 +107,90 @@ export function RagIndexPanel({ noteId }: RagIndexPanelProps) {
return 'Not indexed'
}
+ const confirmDialog = (
+ {
+ setDeleteConfirmOpen(open)
+ // After dialog closes (confirm or cancel), let the parent close the dropdown too
+ if (!open) onMenuClose?.()
+ }}
+ >
+
+
+ Remove from AI index?
+
+ This will remove all embeddings for this note. You can re-index it at any time.
+
+
+
+ Cancel
+ { setDeleteConfirmOpen(false); void handleDelete() }}
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
+ >
+ Remove
+
+
+
+
+ )
+
+ if (variant === 'menu') {
+ return (
+ <>
+
+ AI index: {statusText()}
+
+ {/*
+ onSelect preventDefault keeps the dropdown mounted for the duration of the
+ indexing request so operation state survives. onMenuClose is called in finally
+ to close the menu once the request settles, preventing concurrent requests.
+ */}
+ { e.preventDefault(); void handleIndex().finally(() => onMenuClose?.()) }}
+ disabled={isBusy}
+ title={isIndexed ? 'Re-index this note' : 'Index this note for AI search'}
+ >
+ {operation === 'indexing' ? (
+
+ ) : (
+
+ )}
+ {isIndexed ? 'Re-index' : 'Index note'}
+
+ {/*
+ onSelect preventDefault keeps the dropdown mounted while the AlertDialog is open.
+ Without this, DropdownMenuContent unmounts before the dialog can render,
+ losing the deleteConfirmOpen state.
+ */}
+ { e.preventDefault(); setDeleteConfirmOpen(true) }}
+ disabled={isBusy || !isIndexed}
+ data-cy="note-delete-index-button"
+ className="text-red-600 focus:text-red-600 focus:bg-red-50 dark:focus:bg-red-950/20"
+ title="Remove this note from the AI index"
+ >
+ {operation === 'deleting' ? (
+
+ ) : (
+
+ )}
+ Delete index
+
+ {confirmDialog}
+ >
+ )
+ }
+
return (
<>
-
-
-
- Remove from AI index?
-
- This will remove all embeddings for this note. You can re-index it at any time.
-
-
-
- Cancel
- { setDeleteConfirmOpen(false); void handleDelete() }}
- className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
- >
- Remove
-
-
-
-
+ {confirmDialog}
>
)
}