mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
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
This commit is contained in:
parent
72f34463c2
commit
ff278c4d3e
7 changed files with 145 additions and 60 deletions
|
|
@ -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}`);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 <writeToFile> 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<CommandId, string> = {
|
|||
[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 = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 = () => {
|
|||
]}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Include Current Note in Context Menu"
|
||||
description="Automatically include the current note in the chat context menu by default when sending messages to the AI."
|
||||
checked={settings.includeActiveNoteAsContext}
|
||||
onCheckedChange={(checked) => {
|
||||
updateSetting("includeActiveNoteAsContext", checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Auto-Add Text Selection to Context"
|
||||
description="Automatically add selected text to chat context when you make a text selection in markdown notes. Disable to use manual command instead."
|
||||
checked={settings.autoIncludeTextSelection}
|
||||
onCheckedChange={(checked) => {
|
||||
updateSetting("autoIncludeTextSelection", checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Images in Markdown"
|
||||
description="Pass embedded images in markdown to the AI along with the text. Only works with multimodal models."
|
||||
checked={settings.passMarkdownImages}
|
||||
onCheckedChange={(checked) => {
|
||||
updateSetting("passMarkdownImages", checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Suggested Prompts"
|
||||
description="Show suggested prompts in the chat view"
|
||||
checked={settings.showSuggestedPrompts}
|
||||
onCheckedChange={(checked) => updateSetting("showSuggestedPrompts", checked)}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Relevant Notes"
|
||||
description="Show relevant notes in the chat view"
|
||||
checked={settings.showRelevantNotes}
|
||||
onCheckedChange={(checked) => updateSetting("showRelevantNotes", checked)}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Saving Conversations Section */}
|
||||
<section>
|
||||
<div className="tw-mb-3 tw-text-xl tw-font-bold">Saving Conversations</div>
|
||||
<div className="tw-space-y-4">
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Autosave Chat"
|
||||
description="Automatically saves the chat after every user message and AI response."
|
||||
checked={settings.autosaveChat}
|
||||
onCheckedChange={(checked) => updateSetting("autosaveChat", checked)}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Generate AI Chat Title on Save"
|
||||
description="When enabled, uses an AI model to generate a concise title for saved chat notes. When disabled, uses the first 10 words of the first user message."
|
||||
checked={settings.generateAIChatTitleOnSave}
|
||||
onCheckedChange={(checked) => updateSetting("generateAIChatTitleOnSave", checked)}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="text"
|
||||
title="Default Conversation Folder Name"
|
||||
|
|
@ -352,59 +420,6 @@ export const BasicSettings: React.FC = () => {
|
|||
</Button>
|
||||
</div>
|
||||
</SettingItem>
|
||||
|
||||
{/* Feature Toggle Group */}
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Autosave Chat"
|
||||
description="Automatically saves the chat after every user message and AI response."
|
||||
checked={settings.autosaveChat}
|
||||
onCheckedChange={(checked) => updateSetting("autosaveChat", checked)}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Generate AI Chat Title on Save"
|
||||
description="When enabled, uses an AI model to generate a concise title for saved chat notes. When disabled, uses the first 10 words of the first user message."
|
||||
checked={settings.generateAIChatTitleOnSave}
|
||||
onCheckedChange={(checked) => updateSetting("generateAIChatTitleOnSave", checked)}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Include Current Note in Context Menu"
|
||||
description="Automatically include the current note in the chat context menu by default when sending messages to the AI."
|
||||
checked={settings.includeActiveNoteAsContext}
|
||||
onCheckedChange={(checked) => {
|
||||
updateSetting("includeActiveNoteAsContext", checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Images in Markdown"
|
||||
description="Pass embedded images in markdown to the AI along with the text. Only works with multimodal models."
|
||||
checked={settings.passMarkdownImages}
|
||||
onCheckedChange={(checked) => {
|
||||
updateSetting("passMarkdownImages", checked);
|
||||
}}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Suggested Prompts"
|
||||
description="Show suggested prompts in the chat view"
|
||||
checked={settings.showSuggestedPrompts}
|
||||
onCheckedChange={(checked) => updateSetting("showSuggestedPrompts", checked)}
|
||||
/>
|
||||
|
||||
<SettingItem
|
||||
type="switch"
|
||||
title="Relevant Notes"
|
||||
description="Show relevant notes in the chat view"
|
||||
checked={settings.showRelevantNotes}
|
||||
onCheckedChange={(checked) => updateSetting("showRelevantNotes", checked)}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export const ModelAddDialog: React.FC<ModelAddDialogProps> = ({
|
|||
const settings = getSettings();
|
||||
const defaultProvider = isEmbeddingModel
|
||||
? EmbeddingModelProviders.OPENAI
|
||||
: ChatModelProviders.OPENAI;
|
||||
: ChatModelProviders.OPENROUTERAI;
|
||||
|
||||
const [dialogElement, setDialogElement] = useState<HTMLDivElement | null>(null);
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue