Skip to content

Commit d51ab56

Browse files
authored
Find similar notes button and command (#704)
* Update Vault QA language * Remove Send Notes to Prompt button * Add command and button for finding similar notes
1 parent 97ea286 commit d51ab56

File tree

9 files changed

+247
-268
lines changed

9 files changed

+247
-268
lines changed

src/components/Chat.tsx

Lines changed: 3 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,13 @@ import {
1818
emojifyPrompt,
1919
fixGrammarSpellingSelectionPrompt,
2020
formatDateTime,
21-
getFileContent,
22-
getFileName,
23-
getNotesFromPath,
24-
getNotesFromTags,
25-
getSendChatContextNotesPrompt,
26-
getTagsFromNote,
2721
glossaryPrompt,
2822
removeUrlsFromSelectionPrompt,
2923
rewriteLongerSelectionPrompt,
3024
rewritePressReleaseSelectionPrompt,
3125
rewriteShorterSelectionPrompt,
3226
rewriteTweetSelectionPrompt,
3327
rewriteTweetThreadSelectionPrompt,
34-
sendNotesContentPrompt,
3528
simplifyPrompt,
3629
summarizePrompt,
3730
tocPrompt,
@@ -243,125 +236,6 @@ ${chatContent}`;
243236
}
244237
};
245238

246-
const handleSendActiveNoteToPrompt = async () => {
247-
if (!app) {
248-
console.error("App instance is not available.");
249-
return;
250-
}
251-
252-
let noteFiles: TFile[] = [];
253-
if (debug) {
254-
console.log("Chat note context path:", settings.chatNoteContextPath);
255-
console.log("Chat note context tags:", settings.chatNoteContextTags);
256-
}
257-
if (settings.chatNoteContextPath) {
258-
// Recursively get all note TFiles in the path
259-
noteFiles = await getNotesFromPath(app.vault, settings.chatNoteContextPath);
260-
}
261-
if (settings.chatNoteContextTags?.length > 0) {
262-
// Get all notes with the specified tags
263-
// If path is provided, get all notes with the specified tags in the path
264-
// If path is not provided, get all notes with the specified tags
265-
noteFiles = await getNotesFromTags(app.vault, settings.chatNoteContextTags, noteFiles);
266-
}
267-
const file = app.workspace.getActiveFile();
268-
// If no note context provided, default to the active note
269-
if (noteFiles.length === 0) {
270-
if (!file) {
271-
new Notice("No active note found.");
272-
console.error("No active note found.");
273-
return;
274-
}
275-
new Notice("No valid Chat context provided. Defaulting to the active note.");
276-
noteFiles = [file];
277-
}
278-
279-
const notes = [];
280-
for (const file of noteFiles) {
281-
// Get the content of the note
282-
const content = await getFileContent(file, app.vault);
283-
const tags = await getTagsFromNote(file, app.vault);
284-
if (content) {
285-
notes.push({ name: getFileName(file), content, tags });
286-
}
287-
}
288-
289-
// Send the content of the note to AI
290-
const promptMessageHidden: ChatMessage = {
291-
message: sendNotesContentPrompt(notes),
292-
sender: USER_SENDER,
293-
isVisible: false,
294-
timestamp: formatDateTime(new Date()),
295-
};
296-
297-
// Visible user message that is not sent to AI
298-
// const sendNoteContentUserMessage = `Please read the following notes [[${activeNoteContent}]] and be ready to answer questions about it.`;
299-
const sendNoteContentUserMessage = getSendChatContextNotesPrompt(
300-
notes,
301-
settings.chatNoteContextPath,
302-
settings.chatNoteContextTags
303-
);
304-
const promptMessageVisible: ChatMessage = {
305-
message: sendNoteContentUserMessage,
306-
sender: USER_SENDER,
307-
isVisible: true,
308-
timestamp: formatDateTime(new Date()),
309-
};
310-
311-
addMessage(promptMessageVisible);
312-
addMessage(promptMessageHidden);
313-
314-
setLoading(true);
315-
await getAIResponse(
316-
promptMessageHidden,
317-
chainManager,
318-
addMessage,
319-
setCurrentAiMessage,
320-
setAbortController,
321-
{ debug }
322-
);
323-
setLoading(false);
324-
};
325-
326-
const forceRebuildActiveNoteContext = async () => {
327-
if (!app) {
328-
console.error("App instance is not available.");
329-
return;
330-
}
331-
332-
const file = app.workspace.getActiveFile();
333-
if (!file) {
334-
new Notice("No active note found.");
335-
console.error("No active note found.");
336-
return;
337-
}
338-
const noteContent = await getFileContent(file, app.vault);
339-
const noteName = getFileName(file);
340-
if (!noteContent) {
341-
new Notice("No note content found.");
342-
console.error("No note content found.");
343-
return;
344-
}
345-
346-
const fileMetadata = app.metadataCache.getFileCache(file);
347-
const noteFile = {
348-
path: file.path,
349-
basename: file.basename,
350-
mtime: file.stat.mtime,
351-
content: noteContent,
352-
metadata: fileMetadata?.frontmatter ?? {},
353-
};
354-
await chainManager.indexFile(noteFile);
355-
const activeNoteOnMessage: ChatMessage = {
356-
sender: AI_SENDER,
357-
message: `Indexing [[${noteName}]]...\n\n Please switch to "QA" in Mode Selection to ask questions about it.`,
358-
isVisible: true,
359-
timestamp: formatDateTime(new Date()),
360-
};
361-
362-
addMessage(activeNoteOnMessage);
363-
};
364-
365239
const refreshVaultContext = async () => {
366240
if (!app) {
367241
console.error("App instance is not available.");
@@ -676,9 +550,10 @@ ${chatContent}`;
676550
clearCurrentAiMessage();
677551
}}
678552
onSaveAsNote={() => handleSaveAsNote(true)}
679-
onSendActiveNoteToPrompt={handleSendActiveNoteToPrompt}
680-
onForceRebuildActiveNoteContext={forceRebuildActiveNoteContext}
681553
onRefreshVaultContext={refreshVaultContext}
554+
onFindSimilarNotes={(content, activeFilePath) =>
555+
plugin.findSimilarNotes(content, activeFilePath)
556+
}
682557
addMessage={addMessage}
683558
settings={settings}
684559
vault={app.vault}

src/components/ChatComponents/ChatIcons.tsx

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { CustomModel, SetChainOptions } from "@/aiParams";
2+
import { SimilarNotesModal } from "@/components/SimilarNotesModal";
23
import { AI_SENDER, VAULT_VECTOR_STORE_STRATEGY } from "@/constants";
34
import { CustomError } from "@/error";
45
import { CopilotSettings } from "@/settings/SettingsPage";
@@ -9,9 +10,9 @@ import React, { useEffect, useState } from "react";
910

1011
import { ChainType } from "@/chainFactory";
1112
import {
13+
ConnectionIcon,
1214
RefreshIcon,
1315
SaveAsNoteIcon,
14-
SendActiveNoteToPromptIcon,
1516
UseActiveNoteAsContextIcon,
1617
} from "@/components/Icons";
1718
import { stringToChainType } from "@/utils";
@@ -23,9 +24,8 @@ interface ChatIconsProps {
2324
setCurrentChain: (chain: ChainType, options?: SetChainOptions) => void;
2425
onNewChat: (openNote: boolean) => void;
2526
onSaveAsNote: () => void;
26-
onSendActiveNoteToPrompt: () => void;
27-
onForceRebuildActiveNoteContext: () => void;
2827
onRefreshVaultContext: () => void;
28+
onFindSimilarNotes: (content: string, activeFilePath: string) => Promise<any>;
2929
addMessage: (message: ChatMessage) => void;
3030
settings: CopilotSettings;
3131
vault: Vault;
@@ -40,9 +40,8 @@ const ChatIcons: React.FC<ChatIconsProps> = ({
4040
setCurrentChain,
4141
onNewChat,
4242
onSaveAsNote,
43-
onSendActiveNoteToPrompt,
44-
onForceRebuildActiveNoteContext,
4543
onRefreshVaultContext,
44+
onFindSimilarNotes,
4645
addMessage,
4746
settings,
4847
vault,
@@ -75,7 +74,7 @@ const ChatIcons: React.FC<ChatIconsProps> = ({
7574
}
7675
const activeNoteOnMessage: ChatMessage = {
7776
sender: AI_SENDER,
78-
message: `OK Feel free to ask me questions about your vault: **${app.vault.getName()}**. \n\nIf you have *NEVER* as your auto-index strategy, you must click the *Refresh Index* button below, or run Copilot command: *Index vault for QA* first before you proceed!\n\nPlease note that this is a retrieval-based QA. Specific questions are encouraged. For generic questions like 'give me a summary', 'brainstorm based on the content', Chat mode with *Send Note to Prompt* button used with a *long context model* is a more suitable choice.`,
77+
message: `OK Feel free to ask me questions about your vault: **${app.vault.getName()}**. \n\nIf you have *NEVER* as your auto-index strategy, you must click the *Refresh Index* button below, or run Copilot command: *Index vault for QA* first before you proceed!\n\nPlease note that this is a retrieval-based QA. Specific questions are encouraged. For generic questions like 'give me a summary', 'brainstorm based on the content', Chat mode with direct \`[[note title]]\` mention is a more suitable choice.`,
7978
isVisible: true,
8079
timestamp: formatDateTime(new Date()),
8180
};
@@ -100,6 +99,18 @@ const ChatIcons: React.FC<ChatIconsProps> = ({
10099
handleChainSelection();
101100
}, [selectedChain]);
102101

102+
const handleFindSimilarNotes = async () => {
103+
const activeFile = app.workspace.getActiveFile();
104+
if (!activeFile) {
105+
new Notice("No active file");
106+
return;
107+
}
108+
109+
const activeNoteContent = await app.vault.cachedRead(activeFile);
110+
const similarChunks = await onFindSimilarNotes(activeNoteContent, activeFile.path);
111+
new SimilarNotesModal(app, similarChunks).open();
112+
};
113+
103114
return (
104115
<div className="chat-icons-container">
105116
<div className="chat-icon-selection-tooltip">
@@ -142,34 +153,26 @@ const ChatIcons: React.FC<ChatIconsProps> = ({
142153
onChange={handleChainChange}
143154
>
144155
<option value="llm_chain">Chat</option>
145-
<option value="vault_qa">Vault QA (BETA)</option>
156+
<option value="vault_qa">Vault QA (Basic)</option>
146157
</select>
147158
<span className="tooltip-text">Mode Selection</span>
148159
</div>
149160
</div>
150-
{selectedChain === "llm_chain" && (
151-
<button className="chat-icon-button clickable-icon" onClick={onSendActiveNoteToPrompt}>
152-
<SendActiveNoteToPromptIcon className="icon-scaler" />
153-
<span className="tooltip-text">
154-
Send Note(s) to Prompt
155-
<br />
156-
(Set with Copilot command: <br />
157-
set note context <br />
158-
in Chat mode.
159-
<br />
160-
Default is active note)
161-
</span>
162-
</button>
163-
)}
164161
{selectedChain === "vault_qa" && (
165-
<button className="chat-icon-button clickable-icon" onClick={onRefreshVaultContext}>
166-
<UseActiveNoteAsContextIcon className="icon-scaler" />
167-
<span className="tooltip-text">
168-
Refresh Index
169-
<br />
170-
for Vault
171-
</span>
172-
</button>
162+
<>
163+
<button className="chat-icon-button clickable-icon" onClick={onRefreshVaultContext}>
164+
<UseActiveNoteAsContextIcon className="icon-scaler" />
165+
<span className="tooltip-text">
166+
Refresh Index
167+
<br />
168+
for Vault
169+
</span>
170+
</button>
171+
<button className="chat-icon-button clickable-icon" onClick={handleFindSimilarNotes}>
172+
<ConnectionIcon className="icon-scaler" />
173+
<span className="tooltip-text">Find Similar Notes for Active Note</span>
174+
</button>
175+
</>
173176
)}
174177
</div>
175178
);

src/components/ChatNoteContextModal.tsx

Lines changed: 0 additions & 87 deletions
This file was deleted.

src/components/Icons.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,27 @@ export const DeleteIcon = () => (
220220
</svg>
221221
);
222222

223+
export const ConnectionIcon: React.FC<IconProps> = ({ className }) => (
224+
<svg
225+
className={className}
226+
xmlns="http://www.w3.org/2000/svg"
227+
width="24"
228+
height="24"
229+
viewBox="0 0 24 24"
230+
fill="none"
231+
stroke="currentColor"
232+
strokeWidth="2"
233+
strokeLinecap="round"
234+
strokeLinejoin="round"
235+
>
236+
<circle cx="18" cy="5" r="3" />
237+
<circle cx="6" cy="12" r="3" />
238+
<circle cx="18" cy="19" r="3" />
239+
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
240+
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
241+
</svg>
242+
);
243+
223244
export const InsertIcon: React.FC<IconProps> = ({ className }) => (
224245
<svg
225246
className={className}

0 commit comments

Comments
 (0)