mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-25 11:20:07 +08:00
feat(notes): add spell-check control (#10507)
* feat(notes): add spell-check control * feat(notes): add spell-check toggle to preview mode toolbar * feat(settings): move spellcheck to global and use hook
This commit is contained in:
parent
d4b3428160
commit
504531d4d5
@ -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<RichEditorRef | null> }) => {
|
||||
// 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<string, React.ReactNode> = {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 }) => {
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -1784,6 +1784,8 @@
|
||||
"sort_updated_asc": "更新时间(从旧到新)",
|
||||
"sort_updated_desc": "更新时间(从新到旧)",
|
||||
"sort_z2a": "文件名(Z-A)",
|
||||
"spell_check": "拼写检查",
|
||||
"spell_check_tooltip": "启用/禁用拼写检查",
|
||||
"star": "收藏笔记",
|
||||
"starred_notes": "收藏的笔记",
|
||||
"title": "笔记",
|
||||
|
||||
@ -1784,6 +1784,8 @@
|
||||
"sort_updated_asc": "更新時間(從舊到新)",
|
||||
"sort_updated_desc": "更新時間(從新到舊)",
|
||||
"sort_z2a": "文件名(Z-A)",
|
||||
"spell_check": "拼寫檢查",
|
||||
"spell_check_tooltip": "啟用/禁用拼寫檢查",
|
||||
"star": "收藏筆記",
|
||||
"starred_notes": "收藏的筆記",
|
||||
"title": "筆記",
|
||||
|
||||
@ -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": "σημειώσεις",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "ノート",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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": "заметки",
|
||||
|
||||
@ -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<NotesEditorProps> = 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<NotesEditorProps> = memo(
|
||||
isFullWidth={settings.isFullWidth}
|
||||
fontFamily={settings.fontFamily}
|
||||
fontSize={settings.fontSize}
|
||||
enableSpellCheck={enableSpellCheck}
|
||||
/>
|
||||
)}
|
||||
</RichEditorContainer>
|
||||
@ -92,8 +100,21 @@ const NotesEditor: FC<NotesEditorProps> = memo(
|
||||
color: 'var(--color-text-3)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 8
|
||||
gap: 12
|
||||
}}>
|
||||
{tmpViewMode === 'preview' && (
|
||||
<Tooltip placement="top" title={t('notes.spell_check_tooltip')} mouseLeaveDelay={0} arrow>
|
||||
<ActionIconButton
|
||||
active={enableSpellCheck}
|
||||
onClick={() => {
|
||||
const newValue = !enableSpellCheck
|
||||
dispatch(setEnableSpellCheck(newValue))
|
||||
window.api.setEnableSpellCheck(newValue)
|
||||
}}>
|
||||
<SpellCheck size={18} />
|
||||
</ActionIconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Selector
|
||||
value={tmpViewMode as EditorView}
|
||||
onChange={(value: EditorView) => setTmpViewMode(value)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user