diff --git a/package.json b/package.json index 4b481f1..8074129 100644 --- a/package.json +++ b/package.json @@ -681,6 +681,11 @@ "title": "Generate Tags for Current Note", "icon": "$(sparkle)" }, + { + "command": "noted.summarizeSelection", + "title": "Summarize Selection", + "icon": "$(sparkle)" + }, { "command": "noted.batchGenerateTags", "title": "Noted: Batch Generate Tags", @@ -806,15 +811,20 @@ "when": "editorHasSelection", "group": "1_edit@1" }, + { + "command": "noted.summarizeSelection", + "when": "editorHasSelection", + "group": "2_ai@1" + }, { "command": "noted.summarizeCurrentNote", "when": "resourcePath =~ /Notes.*\\.(txt|md)$/", - "group": "2_ai@1" + "group": "2_ai@2" }, { "command": "noted.generateTagsCurrentNote", "when": "resourcePath =~ /Notes.*\\.(txt|md)$/", - "group": "2_ai@2" + "group": "2_ai@3" } ], "editor/context": [ diff --git a/src/commands/summarizationCommands.ts b/src/commands/summarizationCommands.ts index 03e70dc..53bf4de 100644 --- a/src/commands/summarizationCommands.ts +++ b/src/commands/summarizationCommands.ts @@ -341,6 +341,66 @@ export class SummarizationCommands { } } + /** + * Handle summarizing selected text and inserting summary below the selection + */ + public async handleSummarizeSelection(): Promise { + const editor = vscode.window.activeTextEditor; + if (!editor) { + vscode.window.showErrorMessage('No editor is currently open'); + return; + } + + const selection = editor.selection; + if (selection.isEmpty) { + vscode.window.showErrorMessage('No text is selected. Please select some text to summarize.'); + return; + } + + const selectedText = editor.document.getText(selection); + if (!selectedText.trim()) { + vscode.window.showErrorMessage('Selected text is empty or contains only whitespace'); + return; + } + + try { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'Summarizing selection...', + cancellable: true + }, + async (progress, token) => { + if (token.isCancellationRequested) { + return; + } + + const summary = await this.summarizationService.summarizeSelection(selectedText, token); + + if (token.isCancellationRequested) { + return; + } + + // Insert summary at the end of the line containing the selection's end point + // to ensure consistent spacing. + const endLine = editor.document.lineAt(selection.end.line); + const insertPosition = endLine.range.end; + + // Format the summary with header (extra blank line before header) + const summaryText = `\n\n## Summary of Selection\n\n${summary}\n`; + + await editor.edit(editBuilder => { + editBuilder.insert(insertPosition, summaryText); + }); + + vscode.window.showInformationMessage('Summary inserted below selection'); + } + ); + } catch (error) { + vscode.window.showErrorMessage(`Failed to generate summary: ${error instanceof Error ? error.message : String(error)}`); + } + } + // ======================================================================== // Private Helper Methods // ======================================================================== diff --git a/src/extension.ts b/src/extension.ts index e50d8c4..fecbf67 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -2081,6 +2081,10 @@ export function activate(context: vscode.ExtensionContext) { await summarizationCommands.handleSummarizeCurrentNote(); }); + let summarizeSelection = vscode.commands.registerCommand('noted.summarizeSelection', async () => { + await summarizationCommands.handleSummarizeSelection(); + }); + let summarizeRecent = vscode.commands.registerCommand('noted.summarizeRecent', async () => { await summarizationCommands.handleSummarizeRecent(); }); @@ -2226,7 +2230,7 @@ export function activate(context: vscode.ExtensionContext) { showPreview, showMarkdownToolbar, undoCommand, redoCommand, showUndoHistory, clearUndoHistory, renameSymbol, - summarizeNote, summarizeCurrentNote, summarizeRecent, summarizeWeek, summarizeMonth, summarizeCustomRange, clearSummaryCache, summarizeSearchResults, + summarizeNote, summarizeCurrentNote, summarizeSelection, summarizeRecent, summarizeWeek, summarizeMonth, summarizeCustomRange, clearSummaryCache, summarizeSearchResults, showSummaryHistory, compareSummaries, restoreSummaryVersion, clearSummaryHistory, showSummaryHistoryStats, createPromptTemplate, editPromptTemplate, deletePromptTemplate, duplicatePromptTemplate, listPromptTemplates, viewTemplateVariables, suggestTags, diff --git a/src/services/summarizationService.ts b/src/services/summarizationService.ts index ba43067..e581646 100644 --- a/src/services/summarizationService.ts +++ b/src/services/summarizationService.ts @@ -526,4 +526,46 @@ ${note.content} } return 'GitHub Copilot'; } + + /** + * Summarize selected text (concise 2-3 sentence summary) + */ + async summarizeSelection(selectedText: string, token: vscode.CancellationToken): Promise { + // Check if AI is enabled + if (!this.isAIEnabled()) { + throw new Error('AI summarization is disabled. Enable it in settings: noted.ai.enabled'); + } + + // Check if Language Model API is available + if (!(await this.isLanguageModelAvailable())) { + throw new Error('Copilot is not available. Please install and enable GitHub Copilot to use AI summarization features.'); + } + + // Truncate if too large + const maxChars = 16000; + const truncatedText = selectedText.length > maxChars + ? selectedText.substring(0, maxChars) + '\n\n[Text truncated for summarization]' + : selectedText; + + // Build a concise summarization prompt + const prompt = `Summarize the following text in 2-3 concise sentences, capturing the main points:\n\n---\n${truncatedText}\n---\n\nProvide only the summary without any preamble or extra formatting.`; + + // Call Language Model API + try { + const model = await selectAIModel(); + const messages = [vscode.LanguageModelChatMessage.User(prompt)]; + + const response = await model.sendRequest(messages, {}, token); + + let summary = ''; + for await (const chunk of response.text) { + summary += chunk; + } + + return summary.trim(); + + } catch (error) { + throw new Error(`Failed to generate summary: ${error instanceof Error ? error.message : String(error)}`); + } + } }