Skip to content

Commit 2142c19

Browse files
committed
refactor: switch to native browser spell check
- Remove custom spell check implementation (spellCheck.ts and tests) - Use native spellcheck attribute on textarea element - Remove custom spell check CSS styles - Update translation to reflect native browser spell check usage - Simplify ChatTextArea component by removing spell check logic As requested by @hannesrudolph, this implementation now uses the system's native spell check service instead of a custom dictionary-based approach.
1 parent 8814688 commit 2142c19

File tree

5 files changed

+2
-681
lines changed

5 files changed

+2
-681
lines changed

webview-ui/src/components/chat/ChatTextArea.tsx

Lines changed: 1 addition & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { EXPERIMENT_IDS, experiments as experimentsLib } from "@roo/experiments"
1111
import { vscode } from "@/utils/vscode"
1212
import { useExtensionState } from "@/context/ExtensionStateContext"
1313
import { useAppTranslation } from "@/i18n/TranslationContext"
14-
import { checkSpelling, debounce, SpellCheckResult } from "@/utils/spellCheck"
1514
import {
1615
ContextMenuOptionType,
1716
getContextMenuOptions,
@@ -183,8 +182,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
183182
const contextMenuContainerRef = useRef<HTMLDivElement>(null)
184183
const [isEnhancingPrompt, setIsEnhancingPrompt] = useState(false)
185184
const [isFocused, setIsFocused] = useState(false)
186-
const [spellCheckResults, setSpellCheckResults] = useState<SpellCheckResult[]>([])
187-
const spellCheckLayerRef = useRef<HTMLDivElement>(null)
188185

189186
// Use custom hook for prompt history navigation
190187
const { handleHistoryNavigation, resetHistoryNavigation, resetOnInputChange } = usePromptHistory({
@@ -226,31 +223,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
226223
return experiments && experimentsLib.isEnabled(experiments, EXPERIMENT_IDS.SPELL_CHECK)
227224
}, [experiments])
228225

229-
// Debounced spell check function
230-
const performSpellCheck = useMemo(
231-
() =>
232-
debounce(async (text: string) => {
233-
if (!isSpellCheckEnabled || !text.trim()) {
234-
setSpellCheckResults([])
235-
return
236-
}
237-
238-
try {
239-
const results = await checkSpelling(text)
240-
setSpellCheckResults(results)
241-
} catch (error) {
242-
console.error("Spell check error:", error)
243-
setSpellCheckResults([])
244-
}
245-
}, 300),
246-
[isSpellCheckEnabled],
247-
)
248-
249-
// Perform spell check when input changes
250-
useEffect(() => {
251-
performSpellCheck(inputValue)
252-
}, [inputValue, performSpellCheck])
253-
254226
const allModes = useMemo(() => getAllModes(customModes), [customModes])
255227

256228
const queryItems = useMemo(() => {
@@ -709,52 +681,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
709681
}
710682
}, [])
711683

712-
const updateSpellCheckHighlights = useCallback(() => {
713-
if (!spellCheckLayerRef.current || !isSpellCheckEnabled) return
714-
715-
const text = inputValue
716-
let html = ""
717-
let lastIndex = 0
718-
719-
// Sort spell check results by start index
720-
const sortedResults = [...spellCheckResults].sort((a, b) => a.startIndex - b.startIndex)
721-
722-
sortedResults.forEach((result) => {
723-
// Add text before the misspelled word
724-
const beforeText = text.slice(lastIndex, result.startIndex)
725-
html += beforeText
726-
.replace(/\n$/, "\n\n")
727-
.replace(/[<>&]/g, (c) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;" })[c] || c)
728-
729-
// Add the misspelled word with highlighting
730-
const misspelledWord = text.slice(result.startIndex, result.endIndex)
731-
html += `<span class="spell-check-error">${misspelledWord.replace(
732-
/[<>&]/g,
733-
(c) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;" })[c] || c,
734-
)}</span>`
735-
736-
lastIndex = result.endIndex
737-
})
738-
739-
// Add remaining text
740-
const remainingText = text.slice(lastIndex)
741-
html += remainingText
742-
.replace(/\n$/, "\n\n")
743-
.replace(/[<>&]/g, (c) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;" })[c] || c)
744-
745-
spellCheckLayerRef.current.innerHTML = html
746-
747-
// Sync scroll position
748-
if (textAreaRef.current) {
749-
spellCheckLayerRef.current.scrollTop = textAreaRef.current.scrollTop
750-
spellCheckLayerRef.current.scrollLeft = textAreaRef.current.scrollLeft
751-
}
752-
}, [inputValue, spellCheckResults, isSpellCheckEnabled])
753-
754-
useLayoutEffect(() => {
755-
updateSpellCheckHighlights()
756-
}, [inputValue, spellCheckResults, updateSpellCheckHighlights])
757-
758684
const handleKeyUp = useCallback(
759685
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
760686
if (["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(e.key)) {
@@ -1125,30 +1051,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
11251051
color: "transparent",
11261052
}}
11271053
/>
1128-
{isSpellCheckEnabled && (
1129-
<div
1130-
ref={spellCheckLayerRef}
1131-
className={cn(
1132-
"absolute",
1133-
"inset-0",
1134-
"pointer-events-none",
1135-
"whitespace-pre-wrap",
1136-
"break-words",
1137-
"text-transparent",
1138-
"overflow-hidden",
1139-
"font-vscode-font-family",
1140-
"text-vscode-editor-font-size",
1141-
"leading-vscode-editor-line-height",
1142-
"py-2",
1143-
"px-[9px]",
1144-
"z-[5]",
1145-
"forced-color-adjust-none",
1146-
)}
1147-
style={{
1148-
color: "transparent",
1149-
}}
1150-
/>
1151-
)}
11521054
<DynamicTextArea
11531055
ref={(el) => {
11541056
if (typeof ref === "function") {
@@ -1181,6 +1083,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
11811083
minRows={3}
11821084
maxRows={15}
11831085
autoFocus={true}
1086+
spellCheck={isSpellCheckEnabled}
11841087
className={cn(
11851088
"w-full",
11861089
"text-vscode-input-foreground",
@@ -1213,7 +1116,6 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
12131116
)}
12141117
onScroll={() => {
12151118
updateHighlights()
1216-
updateSpellCheckHighlights()
12171119
}}
12181120
/>
12191121

webview-ui/src/i18n/locales/en/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@
651651
},
652652
"SPELL_CHECK": {
653653
"name": "Enable spell check in chat",
654-
"description": "When enabled, misspelled words in the chat input will be underlined in red. This helps catch typos before sending messages to Roo."
654+
"description": "When enabled, uses the browser's native spell check to underline misspelled words in the chat input. This helps catch typos before sending messages to Roo."
655655
}
656656
},
657657
"promptCaching": {

webview-ui/src/index.css

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -386,14 +386,6 @@ vscode-dropdown::part(listbox) {
386386
box-shadow: 0 0 0 0.5px color-mix(in srgb, var(--vscode-badge-foreground) 30%, transparent);
387387
}
388388

389-
/* Spell check styles */
390-
.spell-check-error {
391-
text-decoration: underline;
392-
text-decoration-color: #ff0000;
393-
text-decoration-style: wavy;
394-
text-underline-offset: 2px;
395-
}
396-
397389
/**
398390
* vscrui Overrides / Hacks
399391
*/

webview-ui/src/utils/__tests__/spellCheck.spec.ts

Lines changed: 0 additions & 142 deletions
This file was deleted.

0 commit comments

Comments
 (0)