diff --git a/src/renderer/src/components/RichEditor/index.tsx b/src/renderer/src/components/RichEditor/index.tsx index 0b9e3876ac..83023dab9a 100644 --- a/src/renderer/src/components/RichEditor/index.tsx +++ b/src/renderer/src/components/RichEditor/index.tsx @@ -48,7 +48,8 @@ const RichEditor = ({ enableContentSearch = false, isFullWidth = false, fontFamily = 'default', - fontSize = 16 + fontSize = 16, + enableSpellCheck = false // toolbarItems: _toolbarItems // TODO: Implement custom toolbar items }: RichEditorProps & { ref?: React.RefObject }) => { // Use the rich editor hook for complete editor management @@ -71,6 +72,7 @@ const RichEditor = ({ onBlur, placeholder, editable, + enableSpellCheck, scrollParent: () => scrollContainerRef.current, onShowTableActionMenu: ({ position, actions }) => { const iconMap: Record = { diff --git a/src/renderer/src/components/RichEditor/types.ts b/src/renderer/src/components/RichEditor/types.ts index 8804210aef..48ae5bb112 100644 --- a/src/renderer/src/components/RichEditor/types.ts +++ b/src/renderer/src/components/RichEditor/types.ts @@ -50,6 +50,8 @@ export interface RichEditorProps { fontFamily?: 'default' | 'serif' /** Font size in pixels */ fontSize?: number + /** Whether to enable spell check */ + enableSpellCheck?: boolean } export interface ToolbarItem { diff --git a/src/renderer/src/components/RichEditor/useRichEditor.ts b/src/renderer/src/components/RichEditor/useRichEditor.ts index 7dae176068..1ece36fb00 100644 --- a/src/renderer/src/components/RichEditor/useRichEditor.ts +++ b/src/renderer/src/components/RichEditor/useRichEditor.ts @@ -57,6 +57,8 @@ export interface UseRichEditorOptions { editable?: boolean /** Whether to enable table of contents functionality */ enableTableOfContents?: boolean + /** Whether to enable spell check */ + enableSpellCheck?: boolean /** Show table action menu (row/column) with concrete actions and position */ onShowTableActionMenu?: (payload: { type: 'row' | 'column' @@ -126,6 +128,7 @@ export const useRichEditor = (options: UseRichEditorOptions = {}): UseRichEditor previewLength = 50, placeholder = '', editable = true, + enableSpellCheck = false, onShowTableActionMenu, scrollParent } = options @@ -410,7 +413,9 @@ export const useRichEditor = (options: UseRichEditorOptions = {}): UseRichEditor // Allow text selection even when not editable style: editable ? '' - : 'user-select: text; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text;' + : 'user-select: text; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text;', + // Set spellcheck attribute on the contenteditable element + spellcheck: enableSpellCheck ? 'true' : 'false' } }, onUpdate: ({ editor }) => { diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 0f3d2a3f24..00ab42128b 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1784,6 +1784,8 @@ "sort_updated_asc": "Update time (oldest first)", "sort_updated_desc": "Update time (newest first)", "sort_z2a": "File name (Z-A)", + "spell_check": "Spell Check", + "spell_check_tooltip": "Enable/Disable spell check", "star": "Favorite note", "starred_notes": "Collected notes", "title": "Notes", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index ce2dc5c222..af2389dbe2 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1784,6 +1784,8 @@ "sort_updated_asc": "更新时间(从旧到新)", "sort_updated_desc": "更新时间(从新到旧)", "sort_z2a": "文件名(Z-A)", + "spell_check": "拼写检查", + "spell_check_tooltip": "启用/禁用拼写检查", "star": "收藏笔记", "starred_notes": "收藏的笔记", "title": "笔记", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index e4f45288ee..d0c08124ea 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1784,6 +1784,8 @@ "sort_updated_asc": "更新時間(從舊到新)", "sort_updated_desc": "更新時間(從新到舊)", "sort_z2a": "文件名(Z-A)", + "spell_check": "拼寫檢查", + "spell_check_tooltip": "啟用/禁用拼寫檢查", "star": "收藏筆記", "starred_notes": "收藏的筆記", "title": "筆記", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index a071f27783..57edf11f96 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -334,6 +334,7 @@ "new_topic": "Νέο θέμα {{Command}}", "pause": "Παύση", "placeholder": "Εισάγετε μήνυμα εδώ...", + "placeholder_without_triggers": "Εδώ εισαγάγετε το μήνυμα, πατήστε {{key}} για αποστολή", "send": "Αποστολή", "settings": "Ρυθμίσεις", "thinking": { @@ -1696,6 +1697,12 @@ "provider_settings": "Μετάβαση στις ρυθμίσεις παρόχου" }, "notes": { + "auto_rename": { + "empty_note": "Το σημείωμα είναι κενό, δεν μπορεί να δημιουργηθεί όνομα", + "failed": "Αποτυχία δημιουργίας ονόματος σημείωσης", + "label": "Δημιουργία ονόματος σημείωσης", + "success": "Η δημιουργία του ονόματος σημειώσεων ολοκληρώθηκε με επιτυχία" + }, "characters": "χαρακτήρας", "collapse": "σύμπτυξη", "content_placeholder": "Παρακαλώ εισαγάγετε το περιεχόμενο των σημειώσεων...", @@ -1777,6 +1784,8 @@ "sort_updated_asc": "χρόνος ενημέρωσης (από παλιά στα νέα)", "sort_updated_desc": "χρόνος ενημέρωσης (από νεώτερο σε παλαιότερο)", "sort_z2a": "όνομα αρχείου (Z-A)", + "spell_check": "Έλεγχος ορθογραφίας", + "spell_check_tooltip": "Ενεργοποίηση/Απενεργοποίηση ελέγχου ορθογραφίας", "star": "Αγαπημένες σημειώσεις", "starred_notes": "Σημειώσεις συλλογής", "title": "σημειώσεις", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index 7ae0a1bd3c..739943ffc7 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -334,6 +334,7 @@ "new_topic": "Nuevo tema {{Command}}", "pause": "Pausar", "placeholder": "Escribe aquí tu mensaje...", + "placeholder_without_triggers": "Escriba un mensaje aquí y presione {{key}} para enviar", "send": "Enviar", "settings": "Configuración", "thinking": { @@ -1696,6 +1697,12 @@ "provider_settings": "Ir a la configuración del proveedor" }, "notes": { + "auto_rename": { + "empty_note": "La nota está vacía, no se puede generar un nombre", + "failed": "Error al generar el nombre de la nota", + "label": "Generar nombre de nota", + "success": "Se ha generado correctamente el nombre de la nota" + }, "characters": "carácter", "collapse": "ocultar", "content_placeholder": "Introduzca el contenido de la nota...", @@ -1777,6 +1784,8 @@ "sort_updated_asc": "Fecha de actualización (de más antigua a más reciente)", "sort_updated_desc": "Fecha de actualización (de más nuevo a más antiguo)", "sort_z2a": "Nombre de archivo (Z-A)", + "spell_check": "comprobación ortográfica", + "spell_check_tooltip": "Habilitar/deshabilitar revisión ortográfica", "star": "Notas guardadas", "starred_notes": "notas guardadas", "title": "notas", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 64a788f266..d5eda2e61e 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -334,6 +334,7 @@ "new_topic": "Nouveau sujet {{Command}}", "pause": "Pause", "placeholder": "Entrez votre message ici...", + "placeholder_without_triggers": "Entrez votre message ici, appuyez sur {{key}} pour envoyer", "send": "Envoyer", "settings": "Paramètres", "thinking": { @@ -1696,6 +1697,12 @@ "provider_settings": "Aller aux paramètres du fournisseur" }, "notes": { + "auto_rename": { + "empty_note": "La note est vide, impossible de générer un nom", + "failed": "Échec de la génération du nom de note", + "label": "Générer un nom de note", + "success": "La génération du nom de note a réussi" + }, "characters": "caractère", "collapse": "réduire", "content_placeholder": "Veuillez saisir le contenu de la note...", @@ -1777,6 +1784,8 @@ "sort_updated_asc": "Heure de mise à jour (du plus ancien au plus récent)", "sort_updated_desc": "Date de mise à jour (du plus récent au plus ancien)", "sort_z2a": "Nom de fichier (Z-A)", + "spell_check": "Vérification orthographique", + "spell_check_tooltip": "Activer/Désactiver la vérification orthographique", "star": "Notes enregistrées", "starred_notes": "notes de collection", "title": "notes", diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 23eea05fe6..f5cde82e28 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -334,6 +334,7 @@ "new_topic": "新しいトピック {{Command}}", "pause": "一時停止", "placeholder": "ここにメッセージを入力し、{{key}} を押して送信...", + "placeholder_without_triggers": "ここにメッセージを入力し、{{key}} を押して送信してください", "send": "送信", "settings": "設定", "thinking": { @@ -1696,6 +1697,12 @@ "provider_settings": "プロバイダー設定に移動" }, "notes": { + "auto_rename": { + "empty_note": "ノートが空です。名前を生成できません。", + "failed": "ノート名の生成に失敗しました", + "label": "ノート名の生成", + "success": "ノート名の生成に成功しました" + }, "characters": "文字", "collapse": "閉じる", "content_placeholder": "メモの内容を入力してください...", @@ -1777,6 +1784,8 @@ "sort_updated_asc": "更新日時(古い順)", "sort_updated_desc": "更新日時(新しい順)", "sort_z2a": "ファイル名(Z-A)", + "spell_check": "スペルチェック", + "spell_check_tooltip": "スペルチェックの有効/無効", "star": "お気に入りのノート", "starred_notes": "収集したノート", "title": "ノート", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index ab9bec0e66..befcedf381 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -334,6 +334,7 @@ "new_topic": "Novo tópico {{Command}}", "pause": "Pausar", "placeholder": "Digite sua mensagem aqui...", + "placeholder_without_triggers": "Digite a mensagem aqui, pressione {{key}} para enviar", "send": "Enviar", "settings": "Configurações", "thinking": { @@ -1696,6 +1697,12 @@ "provider_settings": "Ir para as configurações do provedor" }, "notes": { + "auto_rename": { + "empty_note": "A nota está vazia, não é possível gerar um nome", + "failed": "Falha ao gerar o nome da nota", + "label": "Gerar nome da nota", + "success": "Nome da nota gerado com sucesso" + }, "characters": "caractere", "collapse": "[minimizar]", "content_placeholder": "Introduza o conteúdo da nota...", @@ -1777,6 +1784,8 @@ "sort_updated_asc": "Tempo de atualização (do mais antigo para o mais recente)", "sort_updated_desc": "atualização de tempo (do mais novo para o mais antigo)", "sort_z2a": "Nome do arquivo (Z-A)", + "spell_check": "verificação ortográfica", + "spell_check_tooltip": "Ativar/Desativar verificação ortográfica", "star": "Notas favoritas", "starred_notes": "notas salvas", "title": "nota", diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index ccc1f49344..f74529300d 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -334,6 +334,7 @@ "new_topic": "Новый топик {{Command}}", "pause": "Остановить", "placeholder": "Введите ваше сообщение здесь, нажмите {{key}} для отправки...", + "placeholder_without_triggers": "Введите сообщение здесь, нажмите {{key}}, чтобы отправить", "send": "Отправить", "settings": "Настройки", "thinking": { @@ -1696,6 +1697,12 @@ "provider_settings": "Перейти к настройкам поставщика" }, "notes": { + "auto_rename": { + "empty_note": "Заметки пусты, имя невозможно сгенерировать", + "failed": "Создание названия заметки не удалось", + "label": "Создать название заметки", + "success": "Имя заметки успешно создано" + }, "characters": "Символы", "collapse": "Свернуть", "content_placeholder": "Введите содержимое заметки...", @@ -1777,6 +1784,8 @@ "sort_updated_asc": "Время обновления (от старого к новому)", "sort_updated_desc": "Время обновления (от нового к старому)", "sort_z2a": "Имя файла (Я-А)", + "spell_check": "Проверка орфографии", + "spell_check_tooltip": "Включить/отключить проверку орфографии", "star": "Избранные заметки", "starred_notes": "Сохраненные заметки", "title": "заметки", diff --git a/src/renderer/src/pages/notes/NotesEditor.tsx b/src/renderer/src/pages/notes/NotesEditor.tsx index 8bdd44d12c..18c2cfe9d5 100644 --- a/src/renderer/src/pages/notes/NotesEditor.tsx +++ b/src/renderer/src/pages/notes/NotesEditor.tsx @@ -1,11 +1,16 @@ +import ActionIconButton from '@renderer/components/Buttons/ActionIconButton' import CodeEditor from '@renderer/components/CodeEditor' import { HSpaceBetweenStack } from '@renderer/components/Layout' import RichEditor from '@renderer/components/RichEditor' import { RichEditorRef } from '@renderer/components/RichEditor/types' import Selector from '@renderer/components/Selector' import { useNotesSettings } from '@renderer/hooks/useNotesSettings' +import { useSettings } from '@renderer/hooks/useSettings' +import { useAppDispatch } from '@renderer/store' +import { setEnableSpellCheck } from '@renderer/store/settings' import { EditorView } from '@renderer/types' -import { Empty } from 'antd' +import { Empty, Tooltip } from 'antd' +import { SpellCheck } from 'lucide-react' import { FC, memo, RefObject, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -21,7 +26,9 @@ interface NotesEditorProps { const NotesEditor: FC = memo( ({ activeNodeId, currentContent, tokenCount, onMarkdownChange, editorRef }) => { const { t } = useTranslation() + const dispatch = useAppDispatch() const { settings } = useNotesSettings() + const { enableSpellCheck } = useSettings() const currentViewMode = useMemo(() => { if (settings.defaultViewMode === 'edit') { return settings.defaultEditMode @@ -78,6 +85,7 @@ const NotesEditor: FC = memo( isFullWidth={settings.isFullWidth} fontFamily={settings.fontFamily} fontSize={settings.fontSize} + enableSpellCheck={enableSpellCheck} /> )} @@ -92,8 +100,21 @@ const NotesEditor: FC = memo( color: 'var(--color-text-3)', display: 'flex', alignItems: 'center', - gap: 8 + gap: 12 }}> + {tmpViewMode === 'preview' && ( + + { + const newValue = !enableSpellCheck + dispatch(setEnableSpellCheck(newValue)) + window.api.setEnableSpellCheck(newValue) + }}> + + + + )} setTmpViewMode(value)}