diff --git a/packages/plugin/handlers/commandHandlers.ts b/packages/plugin/handlers/commandHandlers.ts index bbc28685..e924a876 100644 --- a/packages/plugin/handlers/commandHandlers.ts +++ b/packages/plugin/handlers/commandHandlers.ts @@ -1,13 +1,9 @@ import { WorkspaceLeaf } from "obsidian"; import FileOrganizer from "../index"; import { ORGANIZER_VIEW_TYPE, AssistantViewWrapper } from "../views/assistant/view"; -import { App, TFile } from "obsidian"; -import { logger } from "../services/logger"; - - +import { App } from "obsidian"; export function initializeOrganizer(plugin: FileOrganizer) { - plugin.registerView( ORGANIZER_VIEW_TYPE, (leaf: WorkspaceLeaf) => new AssistantViewWrapper(leaf, plugin) @@ -16,11 +12,9 @@ export function initializeOrganizer(plugin: FileOrganizer) { plugin.addRibbonIcon("sparkle", "Fo2k Assistant View", () => { plugin.ensureAssistantView(); }); - } export function initializeFileOrganizationCommands(plugin: FileOrganizer) { - plugin.addCommand({ id: "add-to-inbox", name: "Put in inbox", @@ -32,43 +26,3 @@ export function initializeFileOrganizationCommands(plugin: FileOrganizer) { }, }); } - -export async function addTextToDocument(app: App, content: string, path?: string): Promise { - try { - let targetFile: TFile; - console.log("path", path, 'addTextToDocument'); - - if (path) { - targetFile = app.vault.getAbstractFileByPath(path) as TFile; - if (!targetFile) { - throw new Error(`File not found at path: ${path}`); - } - } else { - // Get the active file - targetFile = app.workspace.getActiveFile(); - if (!targetFile) { - throw new Error("No active file found"); - } - } - - // Get current content - const currentContent = await app.vault.read(targetFile); - - // Get editor if it exists - const editor = app.workspace.activeEditor?.editor; - - if (editor) { - // If we have an editor, insert at cursor position - const cursor = editor.getCursor(); - editor.replaceRange(content, cursor); - } else { - // Otherwise append to the end - await app.vault.modify(targetFile, currentContent + "\n" + content); - } - - logger.info(`Successfully added text to document: ${targetFile.path}`); - } catch (error) { - logger.error("Error adding text to document:", error); - throw error; - } -} diff --git a/packages/plugin/views/assistant/ai-chat/tool-handlers/add-text-handler.tsx b/packages/plugin/views/assistant/ai-chat/tool-handlers/add-text-handler.tsx index cdfe4e9f..c6aa08aa 100644 --- a/packages/plugin/views/assistant/ai-chat/tool-handlers/add-text-handler.tsx +++ b/packages/plugin/views/assistant/ai-chat/tool-handlers/add-text-handler.tsx @@ -1,6 +1,5 @@ import React, { useRef, useState } from "react"; -import { App } from "obsidian"; -import { addTextToDocument } from "../../../../handlers/commandHandlers"; +import { App, TFile } from "obsidian"; import { logger } from "../../../../services/logger"; interface AddTextHandlerProps { @@ -23,7 +22,37 @@ export function AddTextHandler({ hasFetchedRef.current = true; const { content, path } = toolInvocation.args; try { - await addTextToDocument(app, content, path); + let targetFile: TFile; + + if (path) { + targetFile = app.vault.getAbstractFileByPath(path) as TFile; + if (!targetFile) { + throw new Error(`File not found at path: ${path}`); + } + } else { + // Get the active file + targetFile = app.workspace.getActiveFile(); + if (!targetFile) { + throw new Error("No active file found"); + } + } + + // Get current content + const currentContent = await app.vault.read(targetFile); + + // Get editor if it exists + const editor = app.workspace.activeEditor?.editor; + + if (editor) { + // If we have an editor, insert at cursor position + const cursor = editor.getCursor(); + editor.replaceRange(content, cursor); + } else { + // Otherwise append to the end + await app.vault.modify(targetFile, currentContent + "\n" + content); + } + + logger.info(`Successfully added text to document: ${targetFile.path}`); handleAddResult(`Successfully added text to document${path ? `: ${path}` : ""}`); setAddSuccess(true); } catch (error) { diff --git a/packages/plugin/views/assistant/ai-chat/tool-handlers/modify-text-handler.tsx b/packages/plugin/views/assistant/ai-chat/tool-handlers/modify-text-handler.tsx new file mode 100644 index 00000000..4cf5f8c8 --- /dev/null +++ b/packages/plugin/views/assistant/ai-chat/tool-handlers/modify-text-handler.tsx @@ -0,0 +1,81 @@ +import React, { useRef, useState } from "react"; +import { App, TFile } from "obsidian"; +import { logger } from "../../../../services/logger"; + +interface ModifyTextHandlerProps { + toolInvocation: any; + handleAddResult: (result: string) => void; + app: App; +} + +export function ModifyTextHandler({ + toolInvocation, + handleAddResult, + app, +}: ModifyTextHandlerProps) { + const hasFetchedRef = useRef(false); + const [modifySuccess, setModifySuccess] = useState(null); + + React.useEffect(() => { + const handleModifyText = async () => { + if (!hasFetchedRef.current && !("result" in toolInvocation)) { + hasFetchedRef.current = true; + const { content, path } = toolInvocation.args; + try { + let targetFile: TFile; + + if (path) { + targetFile = app.vault.getAbstractFileByPath(path) as TFile; + if (!targetFile) { + throw new Error(`File not found at path: ${path}`); + } + } else { + // Get the active file + targetFile = app.workspace.getActiveFile(); + if (!targetFile) { + throw new Error("No active file found"); + } + } + + // Get editor if it exists + const editor = app.workspace.activeEditor?.editor; + + if (editor) { + // If we have an editor, replace the selected text or entire content + const selection = editor.getSelection(); + if (selection) { + // If there's selected text, replace just that + editor.replaceSelection(content); + } else { + // If no selection, replace entire content + editor.setValue(content); + } + } else { + // If no editor is open, modify the entire file + await app.vault.modify(targetFile, content); + } + + logger.info(`Successfully modified text in document: ${targetFile.path}`); + handleAddResult(`Successfully modified text in document${path ? `: ${path}` : ""}`); + setModifySuccess(true); + } catch (error) { + logger.error("Error modifying text in document:", error); + handleAddResult(`Error: ${error.message}`); + setModifySuccess(false); + } + } + }; + + handleModifyText(); + }, [toolInvocation, handleAddResult, app]); + + if (modifySuccess === null) { + return
Modifying text in document...
; + } + + if (modifySuccess) { + return
Text successfully modified in document
; + } + + return
Failed to modify text in document
; +} \ No newline at end of file diff --git a/packages/plugin/views/assistant/ai-chat/tool-handlers/tool-invocation-handler.tsx b/packages/plugin/views/assistant/ai-chat/tool-handlers/tool-invocation-handler.tsx index 7641d83b..60122a5f 100644 --- a/packages/plugin/views/assistant/ai-chat/tool-handlers/tool-invocation-handler.tsx +++ b/packages/plugin/views/assistant/ai-chat/tool-handlers/tool-invocation-handler.tsx @@ -15,6 +15,7 @@ import { RenameFilesHandler } from "./rename-files-handler"; import { SearchRenameHandler } from "./search-rename-handler"; import { ExecuteActionsHandler } from "./execute-actions-handler"; import { AddTextHandler } from "./add-text-handler"; +import { ModifyTextHandler } from "./modify-text-handler"; interface ToolInvocationHandlerProps { toolInvocation: ToolInvocation; @@ -48,6 +49,7 @@ function ToolInvocationHandler({ searchByName: "Search Files by Name", executeActionsOnFileBasedOnPrompt: "Execute Actions on Files", addTextToDocument: "Adding Text to Document", + modifyDocumentText: "Modifying Document Text", }; return toolTitles[toolName] || "Tool Invocation"; }; @@ -142,6 +144,13 @@ function ToolInvocationHandler({ app={app} /> ), + modifyDocumentText: () => ( + + ), }; return handlers[toolInvocation.toolName]?.() || null; diff --git a/packages/web/app/api/(newai)/chat/route.ts b/packages/web/app/api/(newai)/chat/route.ts index b94d12e9..f77ea9b1 100644 --- a/packages/web/app/api/(newai)/chat/route.ts +++ b/packages/web/app/api/(newai)/chat/route.ts @@ -113,6 +113,13 @@ export async function POST(req: NextRequest) { path: z.string().optional().describe("Optional path to the document. If not provided, uses current document"), }), }, + modifyDocumentText: { + description: "Modifies the text in the current document or selected text according to user's request", + parameters: z.object({ + content: z.string().describe("The new text content to replace the current content or selection"), + path: z.string().optional().describe("Optional path to the document. If not provided, uses current document"), + }), + }, generateSettings: { description: "Generate vault organization settings based on user preferences",