fix: preserve markdown when saving notes

Change the chat text-selection Add to notes action to use the existing markdown selection payload, matching Add to chat formatting behavior while still falling back to plain text when markdown extraction is unavailable.

Stop distilling or summarizing selected text before saving it to project notes. The action now appends the selected content directly so code, lists, emphasis, and other markdown survive.

Increase the project notes text limit from 1000 to 3000 characters to account for saving raw selected text instead of a short generated summary.

Update user-facing copy across locales from distilled insight language to selected text language.
This commit is contained in:
Bohdan Triapitsyn
2026-05-19 02:07:07 +03:00
parent 174fa4e96d
commit f40110698f
9 changed files with 20 additions and 36 deletions
@@ -9,10 +9,9 @@ import { cn } from '@/lib/utils';
import { copyTextToClipboard } from '@/lib/clipboard';
import { toast } from '@/components/ui';
import { Icon } from "@/components/icon/Icon";
import { getProjectNotesAndTodos, saveProjectNotesAndTodos } from '@/lib/openchamberConfig';
import { OPENCHAMBER_PROJECT_NOTES_MAX_LENGTH, getProjectNotesAndTodos, saveProjectNotesAndTodos } from '@/lib/openchamberConfig';
import { resolveProjectForSessionDirectory } from '@/lib/projectResolution';
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
import { summarizeText } from '@/lib/voice/summarize';
import { isVSCodeRuntime } from '@/lib/desktop';
import { useI18n } from '@/lib/i18n';
@@ -33,7 +32,7 @@ interface SelectionPayload {
}
const appendDistilledInsightToNotes = (existingNotes: string, insight: string): string => {
const trimmedInsight = insight.trim().replace(/^[-*+]\s+/, '');
const trimmedInsight = insight.trim().replace(/^[-*+]\s+/, '').slice(0, OPENCHAMBER_PROJECT_NOTES_MAX_LENGTH);
if (!trimmedInsight) {
return existingNotes;
}
@@ -519,18 +518,7 @@ export const TextSelectionMenu: React.FC<TextSelectionMenuProps> = ({ containerR
try {
setIsAddingToNotes(true);
let noteText = selectedText;
let usedSummaryFallback = false;
try {
noteText = await summarizeText(selectedText, {
threshold: 0,
maxLength: 100,
mode: 'note',
});
} catch (summaryError) {
usedSummaryFallback = true;
console.warn('[AddToNotes] Summary failed, saving selected text:', summaryError);
}
const noteText = selectedTextMarkdown || selectedText;
const projectData = await getProjectNotesAndTodos(currentProjectRef);
const nextNotes = appendDistilledInsightToNotes(projectData.notes, noteText);
const saved = await saveProjectNotesAndTodos(currentProjectRef, {
@@ -544,11 +532,7 @@ export const TextSelectionMenu: React.FC<TextSelectionMenuProps> = ({ containerR
window.dispatchEvent(new CustomEvent('openchamber:project-notes-updated', {
detail: { projectId: currentProjectRef.id },
}));
if (usedSummaryFallback) {
toast.warning(t('chat.textSelection.toast.addToNotesSummaryFailed'));
} else {
toast.success(t('chat.textSelection.toast.addToNotesSuccess'));
}
toast.success(t('chat.textSelection.toast.addToNotesSuccess'));
hideMenu();
window.getSelection()?.removeAllRanges();
} catch (error) {
@@ -557,7 +541,7 @@ export const TextSelectionMenu: React.FC<TextSelectionMenuProps> = ({ containerR
} finally {
setIsAddingToNotes(false);
}
}, [currentProjectRef, hideMenu, selectedText, t]);
}, [currentProjectRef, hideMenu, selectedText, selectedTextMarkdown, t]);
if (!position.show) return null;
+2 -2
View File
@@ -1482,7 +1482,7 @@ export const dict = {
'chat.questionCard.copyFailed': 'Failed to copy question',
'chat.textSelection.toast.noProject': 'No project found for this session',
'chat.textSelection.toast.addToNotesFailed': 'Failed to add to notes',
'chat.textSelection.toast.addToNotesSuccess': 'Added distilled insight to notes',
'chat.textSelection.toast.addToNotesSuccess': 'Added selected text to notes',
'chat.textSelection.toast.addToNotesSummaryFailed': 'Could not summarize selection, added selected text to notes',
'chat.textSelection.actions.addToChat': 'Add to chat',
'chat.textSelection.actions.newSession': 'New session',
@@ -1490,7 +1490,7 @@ export const dict = {
'chat.textSelection.actions.addToNotes': 'Add to notes',
'chat.textSelection.title.addToCurrentChat': 'Add to current chat',
'chat.textSelection.title.newSessionWithSelection': 'Create new session with selection',
'chat.textSelection.title.saveInsightToNotes': 'Save distilled insight to notes',
'chat.textSelection.title.saveInsightToNotes': 'Save selected text to notes',
'chat.messageBody.actions.revertAria': 'Revert to this message',
'chat.messageBody.actions.revert': 'Revert from here',
'chat.messageBody.actions.forkAria': 'Fork from this message',
+2 -2
View File
@@ -1448,7 +1448,7 @@ export const dict: Record<I18nKey, string> = {
"chat.questionCard.copyFailed": "No se pudo copiar la pregunta",
"chat.textSelection.toast.noProject": "No se encontró proyecto para esta sesión",
"chat.textSelection.toast.addToNotesFailed": "No se pudo añadir a las notas",
"chat.textSelection.toast.addToNotesSuccess": "Se añadió la información destilada a notas",
"chat.textSelection.toast.addToNotesSuccess": "Texto seleccionado añadido a notas",
"chat.textSelection.toast.addToNotesSummaryFailed": "No se pudo resumir la selección; se añadió el texto seleccionado a las notas",
"chat.textSelection.actions.addToChat": "Añadir al chat",
"chat.textSelection.actions.newSession": "Nueva sesión",
@@ -1456,7 +1456,7 @@ export const dict: Record<I18nKey, string> = {
"chat.textSelection.actions.addToNotes": "Añadir a las notas",
"chat.textSelection.title.addToCurrentChat": "Añadir al chat actual",
"chat.textSelection.title.newSessionWithSelection": "Crear nueva sesión con selección",
"chat.textSelection.title.saveInsightToNotes": "Guardar información destilada en notas",
"chat.textSelection.title.saveInsightToNotes": "Guardar texto seleccionado en notas",
"chat.messageBody.actions.revertAria": "Volver a este mensaje",
"chat.messageBody.actions.revert": "Volver desde aquí",
"chat.messageBody.actions.forkAria": "Bifurcar desde este mensaje",
+2 -2
View File
@@ -1484,7 +1484,7 @@ export const dict: Record<I18nKey, string> = {
'chat.questionCard.copyFailed': '질문 복사에 실패했습니다',
'chat.textSelection.toast.noProject': '이 세션의 프로젝트를 찾을 수 없음',
'chat.textSelection.toast.addToNotesFailed': '메모 추가 실패',
'chat.textSelection.toast.addToNotesSuccess': '정리된 인사이트를 메모에 추가함',
'chat.textSelection.toast.addToNotesSuccess': '선택한 텍스트를 메모에 추가함',
'chat.textSelection.toast.addToNotesSummaryFailed': '선택 영역을 요약할 수 없어 선택한 텍스트를 메모에 추가함',
'chat.textSelection.actions.addToChat': '채팅에 추가',
'chat.textSelection.actions.newSession': '새 세션',
@@ -1492,7 +1492,7 @@ export const dict: Record<I18nKey, string> = {
'chat.textSelection.actions.addToNotes': '메모에 추가',
'chat.textSelection.title.addToCurrentChat': '현재 채팅에 추가',
'chat.textSelection.title.newSessionWithSelection': '선택한 내용으로 새 세션 생성',
'chat.textSelection.title.saveInsightToNotes': '정리된 인사이트를 메모에 저장',
'chat.textSelection.title.saveInsightToNotes': '선택한 텍스트를 메모에 저장',
'chat.messageBody.actions.revertAria': '이 메시지로 되돌리기',
'chat.messageBody.actions.revert': '여기부터 되돌리기',
'chat.messageBody.actions.forkAria': '이 메시지에서 분기',
+2 -2
View File
@@ -529,7 +529,7 @@ export const dict: Record<I18nKey, string> = {
'chat.questionCard.copyFailed': 'Nie udało się skopiować pytania',
'chat.textSelection.toast.noProject': 'Nie znaleziono projektu dla tej sesji',
'chat.textSelection.toast.addToNotesFailed': 'Nie udało się dodać do notatek',
'chat.textSelection.toast.addToNotesSuccess': 'Dodano destylowaną wiedzę do notatek',
'chat.textSelection.toast.addToNotesSuccess': 'Dodano zaznaczony tekst do notatek',
'chat.textSelection.toast.addToNotesSummaryFailed': 'Nie można podsumować zaznaczenia, dodano wybrany tekst do notatek',
'chat.textSelection.actions.addToChat': 'Dodaj do czatu',
'chat.textSelection.actions.newSession': 'Nowa sesja',
@@ -537,7 +537,7 @@ export const dict: Record<I18nKey, string> = {
'chat.textSelection.actions.addToNotes': 'Dodaj do notatek',
'chat.textSelection.title.addToCurrentChat': 'Dodaj do obecnego czatu',
'chat.textSelection.title.newSessionWithSelection': 'Utwórz nową sesję z zaznaczeniem',
'chat.textSelection.title.saveInsightToNotes': 'Zapisz destylowaną wiedzę do notatek',
'chat.textSelection.title.saveInsightToNotes': 'Zapisz zaznaczony tekst do notatek',
'chat.messageBody.actions.revertAria': 'Cofnij do tej wiadomości',
'chat.messageBody.actions.revert': 'Cofnij od tego miejsca',
'chat.messageBody.actions.forkAria': 'Rozwidl z tej wiadomości',
+2 -2
View File
@@ -1448,7 +1448,7 @@ export const dict: Record<I18nKey, string> = {
"chat.questionCard.copyFailed": "Falha ao copiar a pergunta",
"chat.textSelection.toast.noProject": "Não foi encontrado projeto para esta sessão",
"chat.textSelection.toast.addToNotesFailed": "Não foi possível adicionar às notas",
"chat.textSelection.toast.addToNotesSuccess": "Informação destilada adicionada às notas",
"chat.textSelection.toast.addToNotesSuccess": "Texto selecionado adicionado às notas",
"chat.textSelection.toast.addToNotesSummaryFailed": "Não foi possível resumir a seleção; o texto selecionado foi adicionado às notas",
"chat.textSelection.actions.addToChat": "Adicionar ao chat",
"chat.textSelection.actions.newSession": "Nova sessão",
@@ -1456,7 +1456,7 @@ export const dict: Record<I18nKey, string> = {
"chat.textSelection.actions.addToNotes": "Adicionar às notas",
"chat.textSelection.title.addToCurrentChat": "Adicionar ao chat atual",
"chat.textSelection.title.newSessionWithSelection": "Criar nova sessão com seleção",
"chat.textSelection.title.saveInsightToNotes": "Salvar informação destilada em notas",
"chat.textSelection.title.saveInsightToNotes": "Salvar texto selecionado em notas",
"chat.messageBody.actions.revertAria": "Voltar para esta mensagem",
"chat.messageBody.actions.revert": "Voltar daqui",
"chat.messageBody.actions.forkAria": "Bifurcar a partir desta mensagem",
+2 -2
View File
@@ -1448,7 +1448,7 @@ export const dict: Record<I18nKey, string> = {
"chat.questionCard.copyFailed": "Не вдалося скопіювати питання",
"chat.textSelection.toast.noProject": "Для цієї сесії не знайдено жодного проєкту",
"chat.textSelection.toast.addToNotesFailed": "Не вдалося додати до нотаток",
"chat.textSelection.toast.addToNotesSuccess": "Інсайт додано до нотаток",
"chat.textSelection.toast.addToNotesSuccess": "Вибраний текст додано до нотаток",
"chat.textSelection.toast.addToNotesSummaryFailed": "Не вдалося підсумувати виділення, виділений текст додано до нотаток",
"chat.textSelection.actions.addToChat": "Додати в чат",
"chat.textSelection.actions.newSession": "Нова сесія",
@@ -1456,7 +1456,7 @@ export const dict: Record<I18nKey, string> = {
"chat.textSelection.actions.addToNotes": "Додати до нотаток",
"chat.textSelection.title.addToCurrentChat": "Додати до поточного чату",
"chat.textSelection.title.newSessionWithSelection": "Створити нову сесію із виділенням",
"chat.textSelection.title.saveInsightToNotes": "Зберегти стислий інсайт у нотатках",
"chat.textSelection.title.saveInsightToNotes": "Зберегти вибраний текст у нотатках",
"chat.messageBody.actions.revertAria": "Повернутися до цього повідомлення",
"chat.messageBody.actions.revert": "Повернутися звідси",
"chat.messageBody.actions.forkAria": "Відгалузити від цього повідомлення",
+2 -2
View File
@@ -1448,7 +1448,7 @@ export const dict: Record<I18nKey, string> = {
'chat.questionCard.copyFailed': '复制问题失败',
'chat.textSelection.toast.noProject': '未找到此会话对应的项目',
'chat.textSelection.toast.addToNotesFailed': '添加到笔记失败',
'chat.textSelection.toast.addToNotesSuccess': '已将洞察添加到笔记',
'chat.textSelection.toast.addToNotesSuccess': '已将选中文本添加到笔记',
'chat.textSelection.toast.addToNotesSummaryFailed': '无法总结所选内容,已将所选文本添加到笔记',
'chat.textSelection.actions.addToChat': '添加到聊天',
'chat.textSelection.actions.newSession': '新建会话',
@@ -1456,7 +1456,7 @@ export const dict: Record<I18nKey, string> = {
'chat.textSelection.actions.addToNotes': '添加到笔记',
'chat.textSelection.title.addToCurrentChat': '添加到当前聊天',
'chat.textSelection.title.newSessionWithSelection': '使用选中内容创建新会话',
'chat.textSelection.title.saveInsightToNotes': '将洞察保存到笔记',
'chat.textSelection.title.saveInsightToNotes': '将选中文本保存到笔记',
'chat.messageBody.actions.revertAria': '回退到这条消息',
'chat.messageBody.actions.revert': '从此处回退',
'chat.messageBody.actions.forkAria': '从这条消息分叉',
+1 -1
View File
@@ -85,7 +85,7 @@ export interface OpenChamberProjectContextData extends OpenChamberProjectNotesTo
plans: OpenChamberProjectPlanFileLink[];
}
export const OPENCHAMBER_PROJECT_NOTES_MAX_LENGTH = 1000;
export const OPENCHAMBER_PROJECT_NOTES_MAX_LENGTH = 3000;
export const OPENCHAMBER_PROJECT_TODO_TEXT_MAX_LENGTH = 120;
export const OPENCHAMBER_PROJECT_ACTION_NAME_MAX_LENGTH = 80;
export const OPENCHAMBER_PROJECT_ACTION_COMMAND_MAX_LENGTH = 4000;