Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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": [
Expand Down
60 changes: 60 additions & 0 deletions src/commands/summarizationCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,66 @@ export class SummarizationCommands {
}
}

/**
* Handle summarizing selected text and inserting summary below the selection
*/
public async handleSummarizeSelection(): Promise<void> {
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
// ========================================================================
Expand Down
6 changes: 5 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
Expand Down Expand Up @@ -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,
Expand Down
42 changes: 42 additions & 0 deletions src/services/summarizationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
// 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)}`);
}
}
}
Loading