zudo-text

検索したい単語を入力

いつでも検索バーを開ける

Spellcheck

The editor has two independent, opt-in correction sources. Spellcheck delegates word checks to macOS NSSpellChecker instead of browser contenteditable spellcheck and remains macOS desktop only. Japanese typo suggestions use a bundled local matcher for exactly three literal forms on every platform. Both underline findings and share a left-click / Alt+Enter candidate popup. Japanese suggestions do not check grammar, and neither source rewrites text automatically.

Where the code lives

PieceFile
Rust commands (spell_is_supported, spell_check, spell_suggest, spell_learn, spell_ignore)tauri-app/src/commands/spell.rs
Platform-independent DTOs, misspelling loop, request limits (mirrored for Linux CI)tauri-app/core/src/spell.rs
Bridge contract and createUnsupportedSpell()packages/backend-bridge/src/spell.ts
Mock adapter fixturepackages/backend-bridge/src/mock-spell.ts
Tauri/REST adapter wiringpackages/backend-bridge/src/tauri-adapter.ts, packages/backend-bridge/src/rest-adapter.ts
CodeMirror extension (segment extraction, debounce, underline decorations)tauri-app/renderer/extensions/spellcheck.ts
Japanese literal rules and matchertauri-app/renderer/utils/japanese-typo-matcher.ts
Japanese CodeMirror marks and local worker schedulertauri-app/renderer/extensions/japanese-typo-suggestions.ts, tauri-app/renderer/lib/japanese-typo-client.ts, tauri-app/renderer/lib/japanese-typo-worker.ts
Shared correction snapshot, exact edit and popuptauri-app/renderer/extensions/correction-edit.ts, tauri-app/renderer/extensions/correction-popup.ts, tauri-app/renderer/hooks/use-correction-popup.ts
useSpellSupported hookpackages/settings-sections/src/use-spell-supported.ts
Editor context menu "Spelling" grouptauri-app/renderer/components/editor-pane/editor-context-menu.ts
Editor pane wiring (misspelling lookup, suggestion fetch, replace/learn/ignore handlers)tauri-app/renderer/components/editor-pane/index.tsx
Settings row (Settings → Editor → Spellcheck)packages/settings-sections/src/sections/editor-settings.tsx
Command palette entry (editor-toggle-spellcheck, "Toggle Spellcheck")tauri-app/renderer/data/app-commands.tsx
Setting shape, shortcut key, defaults, validationpackages/app-defaults/src/types.ts, packages/app-defaults/src/defaults.ts, packages/app-defaults/src/validate-settings.ts
Help catalog entrytauri-app/renderer/data/help-catalog/index.ts

Bridge contract

See Spell for the full SpellAPI shape, adapter support matrix, and the mock fixture's word list.

Design decisions

  • NSSpellChecker over browser-native spellcheck. WebKit draws its own underlines but exposes suggestions only through its own context menu, which the editor replaces with the app's ContextMenu, and WebKit spellcheck needs a process-level WebContinuousSpellCheckingEnabled NSUserDefault that Tauri never sets. Owning the engine makes the underline and the correction menu agree by construction.

  • NSSpellChecker over Hunspell/WASM or a JS spell-check library. Hunspell needs a bundled dictionary payload and its own suggestion ranking; a JS library like cspell has no browser build. NSSpellChecker reuses the account's system dictionary and user-added words for free, at the cost of being macOS-only — isSupported() gating is what keeps the feature invisible everywhere else.

  • 6-arg checkSpellingOfString:startingAt:language:wrap:inSpellDocumentWithTag:wordCount: with wrap: false and a spell-document tag, not the 2-arg variant. Without a tag, ignoreWord:inSpellDocumentWithTag: has nothing to scope to, so Ignore would never take effect; with wrap: true the search restarts from the beginning of the string instead of terminating, so the misspelling loop would never end. wrap: false plus NSNotFound as the stop condition is what makes check_texts in tauri-app/core/src/spell.rs a bounded loop.

  • spawn_blocking from async Tauri commands, not run_on_main_thread plus an awaited oneshot. A non-async command already runs on the main thread, so a blocking receive of main-thread work from inside one would deadlock. Every spell_* command is async fn and offloads the AppKit call to the blocking pool instead (see the module doc comment at the top of tauri-app/src/commands/spell.rs).

  • Per-segment checking, one paragraph-sized prose segment per array entry, not one concatenated blob. Each segment is passed to NSSpellChecker without an explicit language, leaving language selection to the system service. This does not promise mixed-language detection or grammar checking.

  • Three Japanese literal rules in a bundled worker, separate from the native word-spelling service. The worker checks only うる覚え → うろ覚え, シュミレーション → シミュレーション, and コミニュケーション → コミュニケーション; it does not tokenize text or infer grammar.

  • Replace-within-span decoration semantics. The extension keeps its own StateField<DecorationSet> instead of @codemirror/lint, and a re-check only replaces decorations inside the checked span rather than rebuilding the whole set — so underlines on the line being edited clear immediately while typing, and decorations elsewhere in the document are position-mapped through the edit rather than dropped and redrawn.

  • Per-view refresh limitation. Add to Dictionary and Ignore only refresh the view that triggered them (spellcheckRefresh effect dispatched in tauri-app/renderer/components/editor-pane/index.tsx); other open split views showing the same word catch up on their own next debounced check rather than being broadcast to immediately. Documented as a v1 limitation, not a bug.

  • No automatic correction. A selected candidate replaces only its validated source span in one undoable editor change. The editor never rewrites text while typing.

User-facing behavior

  • Setting: editor.spellcheck (default off), Settings → Editor → Spellcheck. The row itself is hidden when bridge.spell.isSupported() is false.

  • Command palette: Toggle Spellcheck (editor-toggle-spellcheck), disabled with reason "Spellcheck needs the macOS desktop app" when unsupported.

  • Shortcut: shortcuts.toggleSpellcheck, no default binding — rebindable in Settings → Shortcuts.

  • Context menu: right-clicking a misspelled word shows a Spelling group first — up to 5 suggestions, Add to Dictionary, Ignore.

  • Setting: editor.japaneseTypoSuggestions (default off), Settings → Editor → Japanese typo suggestions. It works on every platform and checks locally in a bundled worker without sending document text over the network.

  • Popup: left-click an underlined finding or put the caret in it and press Alt+Enter. Choose the displayed candidate to replace that occurrence; use ArrowUp, ArrowDown, Tab, and Enter to navigate. Escape or the close button only dismisses the popup. Outside clicks, editor/page scrolling, edits, and IME composition dismiss it; scrolling within the candidate list keeps it open. Native right-click Spelling actions remain available for native findings.

  • Scope: exactly the three literals listed above. This is not Japanese grammar, context, or general typo checking; there is no automatic replacement or replace-all action.

Japanese end-user walkthrough: manual/src/content/docs/basics/editor.mdx.