Skip to content

Commit

Permalink
AI Assistant: Add spelling mistake detection to Breve (#38923)
Browse files Browse the repository at this point in the history
* add nspell to jetpack

* add spelling mistake feature

* hide suggestion button for typos

* changelog

* fix for js test
  • Loading branch information
dhasilva authored Aug 16, 2024
1 parent 97e6592 commit eb13dd7
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 1 deletion.
16 changes: 16 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: other

AI Assistant: Add spelling mistake detection to Breve
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ $features-colors: (
'complex-words': rgb( 240, 184, 73 ),
'unconfident-words': rgb( 9, 181, 133 ),
'long-sentences': rgb( 122, 0, 223 ),
'spelling-mistakes': rgb( 214, 54, 56 ),
);

@mixin properties( $feature, $color, $properties, $opacity: false ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import { __ } from '@wordpress/i18n';
/**
* Features
*/
import { getFeatureAvailability } from '../../../../../blocks/ai-assistant/lib/utils/get-feature-availability';
import complexWords, { COMPLEX_WORDS, dictionary as dicComplex } from './complex-words';
import longSentences, { LONG_SENTENCES } from './long-sentences';
import spellingMistakes, { SPELLING_MISTAKES } from './spelling-mistakes';
import unconfidentWords, { UNCONFIDENT_WORDS } from './unconfident-words';
/**
* Types
Expand All @@ -33,4 +35,12 @@ const features: Array< BreveFeature > = [
},
];

if ( getFeatureAvailability( 'ai-breve-typo-support' ) ) {
features.unshift( {
config: SPELLING_MISTAKES,
highlight: spellingMistakes,
description: __( 'Fix spelling mistakes.', 'jetpack' ),
} );
}

export default features;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import nspell from 'nspell';
/**
* Types
*/
import type { BreveFeatureConfig, HighlightedText, SpellChecker } from '../../types';

export const SPELLING_MISTAKES: BreveFeatureConfig = {
name: 'spelling-mistakes',
title: __( 'Spelling mistakes', 'jetpack' ),
tagName: 'span',
className: 'jetpack-ai-breve__has-proofread-highlight--spelling-mistakes',
defaultEnabled: false,
};

const spellcheckers: { [ key: string ]: SpellChecker } = {};
const spellingContexts: {
[ key: string ]: {
affix: string;
dictionary: string;
};
} = {};

const loadContext = ( language: string ) => {
// TODO: Load dictionaries dynamically and save on localStorage
return spellingContexts[ language ];
};

const getSpellchecker = ( { language = 'en' }: { language?: string } = {} ) => {
if ( spellcheckers[ language ] ) {
return spellcheckers[ language ];
}

// Cannot await here as the Rich Text function needs to be synchronous.
// Load of the dictionary in the background if necessary and re-trigger the highlights later.
const spellingContext = loadContext( language );

if ( ! spellingContext ) {
return null;
}

const { affix, dictionary } = spellingContext;
spellcheckers[ language ] = nspell( affix, dictionary );

return spellcheckers[ language ];
};

export default function longSentences( text: string ): Array< HighlightedText > {
const highlightedTexts: Array< HighlightedText > = [];
// Regex to match words, including contractions and hyphenated words
// \p{L} is a Unicode property that matches any letter in any language
// \p{M} is a Unicode property that matches any character intended to be combined with another character
const wordRegex = new RegExp( /[\p{L}\p{M}'-]+/, 'gu' );
const words = text.match( wordRegex ) || [];
const spellchecker = getSpellchecker();

if ( ! spellchecker ) {
return highlightedTexts;
}

words.forEach( ( word: string, index ) => {
if ( ! spellchecker.correct( word ) ) {
const suggestions = spellchecker.suggest( word );

if ( suggestions.length > 0 ) {
highlightedTexts.push( {
text: word,
startIndex: text.indexOf( word, index ),
endIndex: text.indexOf( word, index ) + word.length,
suggestions,
} );
}
}
} );

return highlightedTexts;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { BREVE_FEATURE_NAME } from '../constants';
import features from '../features';
import registerEvents from '../features/events';
import { LONG_SENTENCES } from '../features/long-sentences';
import { SPELLING_MISTAKES } from '../features/spelling-mistakes';
import getBreveAvailability from '../utils/get-availability';
import { getNodeTextIndex } from '../utils/get-node-text-index';
import { getNonLinkAncestor } from '../utils/get-non-link-ancestor';
Expand Down Expand Up @@ -231,7 +232,7 @@ export default function Highlight() {
<div className="jetpack-ai-breve__color" data-breve-type={ feature } />
<div>{ title }</div>
</div>
{ ! hasSuggestions && (
{ ! hasSuggestions && feature !== SPELLING_MISTAKES.name && (
<div className="jetpack-ai-breve__action">
{ loading ? (
<div className="jetpack-ai-breve__loading">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ export type BreveFeature = {
export type HighlightedText = {
text: string;
suggestion?: string;
suggestions?: Array< string >;
startIndex: number;
endIndex: number;
};

export type SpellChecker = {
correct: ( word: string ) => boolean;
suggest: ( word: string ) => Array< string >;
};
1 change: 1 addition & 0 deletions projects/plugins/jetpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
"mapbox-gl": "1.13.0",
"markdown-it": "14.0.0",
"markdown-it-footnote": "3.0.3",
"nspell": "2.1.5",
"photon": "4.0.0",
"postcss-custom-properties": "12.1.7",
"prop-types": "15.7.2",
Expand Down

0 comments on commit eb13dd7

Please sign in to comment.