From ff278c4d3ee095f33973330eaeaa47e287b43029 Mon Sep 17 00:00:00 2001 From: Logan Yang Date: Sun, 9 Nov 2025 19:56:07 -0800 Subject: [PATCH] Add auto selection to context setting (#2018) * Add setting for auto text selection inclusion and add back manual command * Group chat save settings * Update constants --- src/commands/contextMenu.ts | 9 ++ src/commands/index.ts | 44 +++++++ src/constants.ts | 14 +- src/main.ts | 6 + src/settings/model.ts | 7 + src/settings/v2/components/BasicSettings.tsx | 123 ++++++++++-------- src/settings/v2/components/ModelAddDialog.tsx | 2 +- 7 files changed, 145 insertions(+), 60 deletions(-) diff --git a/src/commands/contextMenu.ts b/src/commands/contextMenu.ts index 15afeac2..14face4d 100644 --- a/src/commands/contextMenu.ts +++ b/src/commands/contextMenu.ts @@ -13,6 +13,15 @@ export function registerContextMenu(menu: Menu) { const submenu = (item as any).submenu; if (!submenu) return; + // Add the main selection command + submenu.addItem((subItem: any) => { + subItem.setTitle("Add selection to chat context").onClick(() => { + (app as any).commands.executeCommandById( + `copilot:${COMMAND_IDS.ADD_SELECTION_TO_CHAT_CONTEXT}` + ); + }); + }); + submenu.addItem((subItem: any) => { subItem.setTitle("Trigger quick command").onClick(() => { (app as any).commands.executeCommandById(`copilot:${COMMAND_IDS.TRIGGER_QUICK_COMMAND}`); diff --git a/src/commands/index.ts b/src/commands/index.ts index 524d571e..5af19c9f 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -19,8 +19,10 @@ import { checkIsPlusUser } from "@/plusUtils"; import CopilotPlugin from "@/main"; import { getAllQAMarkdownContent } from "@/search/searchUtils"; import { CopilotSettings, getSettings, updateSetting } from "@/settings/model"; +import { SelectedTextContext } from "@/types/message"; import { ensureFolderExists, isSourceModeOn } from "@/utils"; import { Editor, MarkdownView, Notice, TFile } from "obsidian"; +import { v4 as uuidv4 } from "uuid"; import { COMMAND_IDS, COMMAND_NAMES, CommandId } from "../constants"; /** @@ -363,6 +365,48 @@ export function registerCommands( new Notice(`Copilot autocomplete ${newValue ? "enabled" : "disabled"}`); }); + // Add selection to chat context command (manual) + addEditorCommand(plugin, COMMAND_IDS.ADD_SELECTION_TO_CHAT_CONTEXT, async (editor: Editor) => { + const selectedText = editor.getSelection(); + if (!selectedText) { + new Notice("No text selected"); + return; + } + + const activeFile = plugin.app.workspace.getActiveFile(); + if (!activeFile) { + new Notice("No active file"); + return; + } + + // Get selection range to determine line numbers + const selectionRange = editor.listSelections()[0]; + if (!selectionRange) { + new Notice("Could not determine selection range"); + return; + } + + const startLine = selectionRange.anchor.line + 1; // Convert to 1-based line numbers + const endLine = selectionRange.head.line + 1; + + // Create selected text context + const selectedTextContext: SelectedTextContext = { + id: uuidv4(), + content: selectedText, + noteTitle: activeFile.basename, + notePath: activeFile.path, + startLine: Math.min(startLine, endLine), + endLine: Math.max(startLine, endLine), + }; + + // Replace selected text contexts (consistent with auto mode behavior) + const { setSelectedTextContexts } = await import("@/aiParams"); + setSelectedTextContexts([selectedTextContext]); + + // Open chat window to show the context was added + plugin.activateView(); + }); + // Add command to create a new custom command addCommand(plugin, COMMAND_IDS.ADD_CUSTOM_COMMAND, async () => { const commands = getCachedCustomCommands(); diff --git a/src/constants.ts b/src/constants.ts index 9e686ef4..28d61317 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -31,7 +31,8 @@ export const DEFAULT_SYSTEM_PROMPT = `You are Obsidian Copilot, a helpful assist 11. Always respond in the language of the user's query. 12. Do NOT mention the additional context provided such as getCurrentTime and getTimeRangeMs if it's irrelevant to the user message. 13. If the user mentions "tags", it most likely means tags in Obsidian note properties. - 14. YouTube URLs: If the user provides YouTube URLs in their message, transcriptions will be automatically fetched and provided to you. You don't need to do anything special - just use the transcription content if available.`; + 14. YouTube URLs: If the user provides YouTube URLs in their message, transcriptions will be automatically fetched and provided to you. You don't need to do anything special - just use the transcription content if available. + 15. For markdown lists, always use '- ' (hyphen followed by exactly one space) for bullet points, with no leading spaces before the hyphen. Never use '*' (asterisk) for bullets.`; export const COMPOSER_OUTPUT_INSTRUCTIONS = `Return the new note content or canvas JSON in tags. @@ -167,21 +168,21 @@ export enum ChatModels { // Model Providers export enum ChatModelProviders { + OPENROUTERAI = "openrouterai", OPENAI = "openai", OPENAI_FORMAT = "3rd party (openai-format)", - AZURE_OPENAI = "azure openai", ANTHROPIC = "anthropic", - COHEREAI = "cohereai", GOOGLE = "google", XAI = "xai", - OPENROUTERAI = "openrouterai", + AMAZON_BEDROCK = "amazon-bedrock", + AZURE_OPENAI = "azure openai", GROQ = "groq", OLLAMA = "ollama", LM_STUDIO = "lm-studio", COPILOT_PLUS = "copilot-plus", MISTRAL = "mistralai", DEEPSEEK = "deepseek", - AMAZON_BEDROCK = "amazon-bedrock", + COHEREAI = "cohereai", SILICONFLOW = "siliconflow", } @@ -682,6 +683,7 @@ export const COMMAND_IDS = { SEARCH_ORAMA_DB: "copilot-search-orama-db", TOGGLE_COPILOT_CHAT_WINDOW: "chat-toggle-window", TOGGLE_AUTOCOMPLETE: "toggle-autocomplete", + ADD_SELECTION_TO_CHAT_CONTEXT: "add-selection-to-chat-context", ADD_CUSTOM_COMMAND: "add-custom-command", APPLY_CUSTOM_COMMAND: "apply-custom-command", OPEN_LOG_FILE: "open-log-file", @@ -709,6 +711,7 @@ export const COMMAND_NAMES: Record = { [COMMAND_IDS.SEARCH_ORAMA_DB]: "Search semantic index (debug)", [COMMAND_IDS.TOGGLE_COPILOT_CHAT_WINDOW]: "Toggle Copilot Chat Window", [COMMAND_IDS.TOGGLE_AUTOCOMPLETE]: "Toggle autocomplete", + [COMMAND_IDS.ADD_SELECTION_TO_CHAT_CONTEXT]: "Add selection to chat context", [COMMAND_IDS.ADD_CUSTOM_COMMAND]: "Add new custom command", [COMMAND_IDS.APPLY_CUSTOM_COMMAND]: "Apply custom command", [COMMAND_IDS.OPEN_LOG_FILE]: "Create log file", @@ -829,6 +832,7 @@ export const DEFAULT_SETTINGS: CopilotSettings = { enableSavedMemory: true, quickCommandModelKey: undefined, quickCommandIncludeNoteContext: true, + autoIncludeTextSelection: true, }; export const EVENT_NAMES = { diff --git a/src/main.ts b/src/main.ts index e78f237a..334f271a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -315,6 +315,12 @@ export default class CopilotPlugin extends Plugin { * Only processes selections from markdown editors */ handleSelectionChange() { + // Check if auto-inclusion is enabled + const settings = getSettings(); + if (!settings.autoIncludeTextSelection) { + return; + } + const activeView = this.app.workspace.getActiveViewOfType(MarkdownView); if (!activeView || !activeView.editor) { return; diff --git a/src/settings/model.ts b/src/settings/model.ts index 749bd3fe..c900ea33 100644 --- a/src/settings/model.ts +++ b/src/settings/model.ts @@ -155,6 +155,8 @@ export interface CopilotSettings { quickCommandModelKey: string | undefined; /** Last checkbox state for including note context in quick command */ quickCommandIncludeNoteContext: boolean; + /** Automatically add text selections to chat context */ + autoIncludeTextSelection: boolean; } export const settingsStore = createStore(); @@ -418,6 +420,11 @@ export function sanitizeSettings(settings: CopilotSettings): CopilotSettings { sanitizedSettings.quickCommandModelKey = DEFAULT_SETTINGS.quickCommandModelKey; } + // Ensure autoIncludeTextSelection has a default value + if (typeof sanitizedSettings.autoIncludeTextSelection !== "boolean") { + sanitizedSettings.autoIncludeTextSelection = DEFAULT_SETTINGS.autoIncludeTextSelection; + } + // Ensure defaultSendShortcut has a valid value if (!Object.values(SEND_SHORTCUT).includes(sanitizedSettings.defaultSendShortcut)) { sanitizedSettings.defaultSendShortcut = DEFAULT_SETTINGS.defaultSendShortcut; diff --git a/src/settings/v2/components/BasicSettings.tsx b/src/settings/v2/components/BasicSettings.tsx index f0bd5024..5e5b4f4c 100644 --- a/src/settings/v2/components/BasicSettings.tsx +++ b/src/settings/v2/components/BasicSettings.tsx @@ -1,9 +1,9 @@ import { ChainType } from "@/chainFactory"; import { Button } from "@/components/ui/button"; +import { HelpTooltip } from "@/components/ui/help-tooltip"; import { Input } from "@/components/ui/input"; import { getModelDisplayWithIcons } from "@/components/ui/model-display"; import { SettingItem } from "@/components/ui/setting-item"; -import { HelpTooltip } from "@/components/ui/help-tooltip"; import { DEFAULT_OPEN_AREA, PLUS_UTM_MEDIUMS, SEND_SHORTCUT } from "@/constants"; import { cn } from "@/lib/utils"; import { createPlusPageUrl } from "@/plusUtils"; @@ -265,6 +265,74 @@ export const BasicSettings: React.FC = () => { ]} /> + { + updateSetting("includeActiveNoteAsContext", checked); + }} + /> + + { + updateSetting("autoIncludeTextSelection", checked); + }} + /> + + { + updateSetting("passMarkdownImages", checked); + }} + /> + + updateSetting("showSuggestedPrompts", checked)} + /> + + updateSetting("showRelevantNotes", checked)} + /> + + + + {/* Saving Conversations Section */} +
+
Saving Conversations
+
+ updateSetting("autosaveChat", checked)} + /> + + updateSetting("generateAIChatTitleOnSave", checked)} + /> + {
- - {/* Feature Toggle Group */} - updateSetting("autosaveChat", checked)} - /> - - updateSetting("generateAIChatTitleOnSave", checked)} - /> - - { - updateSetting("includeActiveNoteAsContext", checked); - }} - /> - - { - updateSetting("passMarkdownImages", checked); - }} - /> - - updateSetting("showSuggestedPrompts", checked)} - /> - - updateSetting("showRelevantNotes", checked)} - />
diff --git a/src/settings/v2/components/ModelAddDialog.tsx b/src/settings/v2/components/ModelAddDialog.tsx index 2de304b5..e8761c53 100644 --- a/src/settings/v2/components/ModelAddDialog.tsx +++ b/src/settings/v2/components/ModelAddDialog.tsx @@ -69,7 +69,7 @@ export const ModelAddDialog: React.FC = ({ const settings = getSettings(); const defaultProvider = isEmbeddingModel ? EmbeddingModelProviders.OPENAI - : ChatModelProviders.OPENAI; + : ChatModelProviders.OPENROUTERAI; const [dialogElement, setDialogElement] = useState(null); const [isOpen, setIsOpen] = useState(false);