mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
commit730d0c82b1Author: Logan Yang <logancyang@gmail.com> Date: Mon Apr 21 20:13:21 2025 -0700 Switch insert and copy button positions (#1463) commit4efdc941f2Author: Logan Yang <logancyang@gmail.com> Date: Mon Apr 21 16:15:50 2025 -0700 Fix canvas read (#1462) commit93e4a04ef1Author: Emt-lin <41323133+Emt-lin@users.noreply.github.com> Date: Tue Apr 22 07:13:47 2025 +0800 fix: Add a new line when press the Enter key on mobile. (#1450) commit3c65d79a74Author: Logan Yang <logancyang@gmail.com> Date: Mon Apr 21 15:38:36 2025 -0700 Implement canvas adaptor (#1461) * Implement canvas parser * Refactor context processing and custom prompt handling - Update `processContextNotes` to exclude notes already processed by custom prompts using a set of excluded note paths. - Modify `extractVariablesFromPrompt` to return both a variables map and a set of included files. - Adjust `processPrompt` to track included files during prompt processing. - Update `Chat` component to handle context notes more efficiently by avoiding duplication of processed notes. - Ensure that the `FileParserManager` is initialized with the vault in `main.ts`. * Include canvas files in note context modal * Fix tests commit6a9085a71cAuthor: Logan Yang <logancyang@gmail.com> Date: Sun Apr 20 16:14:54 2025 -0700 Add a toggle to turn custom prompt templating off (#1460) commit448a64c383Author: Logan Yang <logancyang@gmail.com> Date: Sun Apr 20 15:52:42 2025 -0700 Update dependencies and support gpt 4.1 series, o4-mini and grok 3 (#1459) commit1d46ab90b0Author: Logan Yang <logancyang@gmail.com> Date: Sun Apr 20 12:14:56 2025 -0700 Fix image in note logic (#1457) * Refactor image processing in note context * Add passMarkdownImages setting commitf4bf334c27Author: Felix Haase <felix.haase@feki.de> Date: Fri Apr 18 08:19:35 2025 +0200 Ollama ApiKey support (#1421) * Update @langchain/ollama and ollama * Ollama: support api keys by passing headers commit19d8b50a74Author: Emt-lin <41323133+Emt-lin@users.noreply.github.com> Date: Fri Apr 18 14:13:25 2025 +0800 refactor: Optimize some user experiences. (#1441) commitfe9b9311baAuthor: Zero Liu <zerolxy@gmail.com> Date: Sun Apr 13 17:40:42 2025 -0700 Improve custom command (v3) (#1446) * Trim responses and auto focus replace on finishing * Make textarea auto scroll to bottom when generating * Support custom prompt syntax in custom command * Support follow up instruction * Fix unit test * Allow followup instruction to use custom prompt syntax commit648412a914Author: Zero Liu <zerolxy@gmail.com> Date: Wed Apr 2 21:44:37 2025 -0700 Add update notification (#1415) commitcb4510e920Author: Zero Liu <zerolxy@gmail.com> Date: Wed Apr 2 21:43:26 2025 -0700 Add user_id to broca requests (#1414)
163 lines
5.2 KiB
TypeScript
163 lines
5.2 KiB
TypeScript
import { ChainType } from "@/chainFactory";
|
|
import { FileParserManager } from "@/tools/FileParserManager";
|
|
import { TFile, Vault } from "obsidian";
|
|
|
|
export class ContextProcessor {
|
|
private static instance: ContextProcessor;
|
|
|
|
private constructor() {}
|
|
|
|
static getInstance(): ContextProcessor {
|
|
if (!ContextProcessor.instance) {
|
|
ContextProcessor.instance = new ContextProcessor();
|
|
}
|
|
return ContextProcessor.instance;
|
|
}
|
|
|
|
async processEmbeddedPDFs(
|
|
content: string,
|
|
vault: Vault,
|
|
fileParserManager: FileParserManager
|
|
): Promise<string> {
|
|
const pdfRegex = /!\[\[(.*?\.pdf)\]\]/g;
|
|
const matches = [...content.matchAll(pdfRegex)];
|
|
|
|
for (const match of matches) {
|
|
const pdfName = match[1];
|
|
const pdfFile = vault.getAbstractFileByPath(pdfName);
|
|
|
|
if (pdfFile instanceof TFile) {
|
|
try {
|
|
const pdfContent = await fileParserManager.parseFile(pdfFile, vault);
|
|
content = content.replace(match[0], `\n\nEmbedded PDF (${pdfName}):\n${pdfContent}\n\n`);
|
|
} catch (error) {
|
|
console.error(`Error processing embedded PDF ${pdfName}:`, error);
|
|
content = content.replace(
|
|
match[0],
|
|
`\n\nEmbedded PDF (${pdfName}): [Error: Could not process PDF]\n\n`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* Processes context notes, excluding any already handled by custom prompts.
|
|
*
|
|
* @param excludedNotePaths A set of file paths that should be skipped.
|
|
* @param fileParserManager
|
|
* @param vault
|
|
* @param contextNotes
|
|
* @param includeActiveNote
|
|
* @param activeNote
|
|
* @param currentChain
|
|
* @returns The combined content string of the processed context notes.
|
|
*/
|
|
async processContextNotes(
|
|
excludedNotePaths: Set<string>,
|
|
fileParserManager: FileParserManager,
|
|
vault: Vault,
|
|
contextNotes: TFile[],
|
|
includeActiveNote: boolean,
|
|
activeNote: TFile | null,
|
|
currentChain: ChainType
|
|
): Promise<string> {
|
|
let additionalContext = "";
|
|
|
|
const processNote = async (note: TFile) => {
|
|
try {
|
|
// Check if this note was already processed (via custom prompt)
|
|
if (excludedNotePaths.has(note.path)) {
|
|
console.log(`Skipping note ${note.path} as it was included via custom prompt.`);
|
|
return;
|
|
}
|
|
|
|
console.log(
|
|
`Processing note: ${note.path}, extension: ${note.extension}, chain: ${currentChain}`
|
|
);
|
|
|
|
// 1. Check if the file extension is supported by any parser
|
|
if (!fileParserManager.supportsExtension(note.extension)) {
|
|
console.warn(`Unsupported file type: ${note.extension}`);
|
|
return;
|
|
}
|
|
|
|
// 2. Apply chain restrictions only to supported files that are NOT md or canvas
|
|
if (
|
|
currentChain !== ChainType.COPILOT_PLUS_CHAIN &&
|
|
note.extension !== "md" &&
|
|
note.extension !== "canvas"
|
|
) {
|
|
// This file type is supported, but requires Plus mode (e.g., PDF)
|
|
console.warn(
|
|
`File type ${note.extension} requires Copilot Plus mode for context processing.`
|
|
);
|
|
return;
|
|
}
|
|
|
|
// 3. If we reach here, parse the file (md, canvas, or other supported type in Plus mode)
|
|
let content = await fileParserManager.parseFile(note, vault);
|
|
|
|
// Special handling for embedded PDFs within markdown (only in Plus mode)
|
|
if (note.extension === "md" && currentChain === ChainType.COPILOT_PLUS_CHAIN) {
|
|
content = await this.processEmbeddedPDFs(content, vault, fileParserManager);
|
|
}
|
|
|
|
additionalContext += `\n\nTitle: [[${note.basename}]]\nPath: ${note.path}\n\n${content}`;
|
|
} catch (error) {
|
|
console.error(`Error processing file ${note.path}:`, error);
|
|
additionalContext += `\n\nTitle: [[${note.basename}]]\nPath: ${note.path}\n\n[Error: Could not process file]`;
|
|
}
|
|
};
|
|
|
|
// Process active note if included
|
|
if (includeActiveNote && activeNote) {
|
|
await processNote(activeNote);
|
|
}
|
|
|
|
// Process context notes
|
|
for (const note of contextNotes) {
|
|
await processNote(note);
|
|
}
|
|
|
|
return additionalContext;
|
|
}
|
|
|
|
async hasEmbeddedPDFs(content: string): Promise<boolean> {
|
|
const pdfRegex = /!\[\[(.*?\.pdf)\]\]/g;
|
|
return pdfRegex.test(content);
|
|
}
|
|
|
|
async addNoteToContext(
|
|
note: TFile,
|
|
vault: Vault,
|
|
contextNotes: TFile[],
|
|
activeNote: TFile | null,
|
|
setContextNotes: (notes: TFile[] | ((prev: TFile[]) => TFile[])) => void,
|
|
setIncludeActiveNote: (include: boolean) => void
|
|
): Promise<void> {
|
|
// Only check if the note exists in contextNotes
|
|
if (contextNotes.some((existing) => existing.path === note.path)) {
|
|
return; // Note already exists in context
|
|
}
|
|
|
|
// Read the note content
|
|
const content = await vault.read(note);
|
|
const hasEmbeddedPDFs = await this.hasEmbeddedPDFs(content);
|
|
|
|
// Set includeActiveNote if it's the active note
|
|
if (activeNote && note.path === activeNote.path) {
|
|
setIncludeActiveNote(true);
|
|
}
|
|
|
|
// Add to contextNotes with wasAddedViaReference flag
|
|
setContextNotes((prev: TFile[]) => [
|
|
...prev,
|
|
Object.assign(note, {
|
|
wasAddedViaReference: true,
|
|
hasEmbeddedPDFs,
|
|
}),
|
|
]);
|
|
}
|
|
}
|