Skip to content

Commit

Permalink
Add ability to ignore suggestions and add suggestions to languagetool…
Browse files Browse the repository at this point in the history
… dictionary (#176)
  • Loading branch information
zachhannum authored Jan 2, 2023
1 parent 30379a7 commit a169373
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 4 deletions.
1 change: 1 addition & 0 deletions app/renderer/components/editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const Editor = () => {
const { signal } = abortController;
checkText(previewContent, signal)
.then((result) => {
console.log(result);
const effects: StateEffect<any>[] = [];

if (result.matches) {
Expand Down
84 changes: 82 additions & 2 deletions app/renderer/components/editor/TooltipView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@ import { EditorView } from '@codemirror/view';
import { useEffect, useRef, useState } from 'react';
import { useResizeObserver } from 'renderer/hooks';
import styled from 'styled-components';
import useStore from 'renderer/store/useStore';
import { DictionaryAddIcon } from 'renderer/icons';
import { addToDictionary } from './language-tool/api';
import { TooltipLocation } from './extensions/proofreadTooltipHelper';
import { clearUnderlinesInRange } from './extensions/proofreadUnderlines';
import {
clearUnderlinesInRange,
proofreadUnderlineField,
} from './extensions/proofreadUnderlines';

type TooltipViewProps = {
tooltip: TooltipLocation | null;
Expand Down Expand Up @@ -46,14 +52,30 @@ const TooltipMessage = styled.div`
padding: 7px;
`;

const TooltipSuggestion = styled.div`
type TooltipSuggestionProps = {
color?: string;
};

const TooltipSuggestion = styled.div<TooltipSuggestionProps>`
color: ${(p) => p.theme.buttonPrimaryBg};
${(p) => p.color && `color: ${p.color};`}
padding: 7px;
&:hover {
background-color: rgba(0, 0, 0, 0.2);
}
border-radius: 7px;
cursor: pointer;
display: flex;
justify-content: flex-start;
align-items: center;
`;

const TooltipBottom = styled.div`
display: flex;
padding-top: 7px;
gap: 5px;
flex-direction: row;
justify-content: flex-start;
`;

const tooltipPadding = 5;
Expand All @@ -69,6 +91,50 @@ export const TooltipView = ({
const [message, setMessage] = useState('');
const [suggestions, setSuggestions] = useState<string[]>([]);
const [tooltipLoc, setTooltipLoc] = useState<TooltipLocation | null>(null);
const [settings] = useStore((s) => [s.settings]);

const handleDismiss = () => {
if (editorView && tooltipLoc && tooltip) {
const clearUnderlineEffect = clearUnderlinesInRange.of({
from: tooltip.from,
to: tooltip.to,
});
editorView.dispatch({
effects: [clearUnderlineEffect],
});
}
};

const handleAddToDictionary = () => {
if (editorView && tooltipLoc && tooltip) {
const word = editorView.state.sliceDoc(tooltip.from, tooltip.to);
addToDictionary(word)
.then((result) => {
if (!result.added) {
console.error('Failed to add word to dictionary');
}
return null;
})
.catch((e) => {
console.error(e);
});

const underlines = editorView.state.field(proofreadUnderlineField);

underlines.between(0, editorView.state.doc.length, (from, to) => {
const underline = editorView.state.sliceDoc(from, to);
if (underline === word) {
const clearUnderlineEffect = clearUnderlinesInRange.of({
from,
to,
});
editorView.dispatch({
effects: [clearUnderlineEffect],
});
}
});
}
};

const calculateTooltipPos = (): { top: number; left: number } => {
if (tooltipRef.current && tooltipRef.current.parentElement && editorView) {
Expand Down Expand Up @@ -274,6 +340,20 @@ export const TooltipView = ({
{suggestion}
</TooltipSuggestion>
))}
<TooltipBottom>
<TooltipSuggestion
color="rgba(255,255,255,0.4)"
onClick={handleDismiss}
>
Dismiss
</TooltipSuggestion>
{tooltipLoc?.match.rule.issueType === 'misspelling' &&
settings.languageToolApiKey !== '' && (
<TooltipSuggestion onClick={handleAddToDictionary}>
<DictionaryAddIcon size="18px" color="rgba(255,255,255,0.4)" />
</TooltipSuggestion>
)}
</TooltipBottom>
</Tooltip>
);
};
54 changes: 54 additions & 0 deletions app/renderer/components/editor/language-tool/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export interface Category {
name: string;
}

export interface LanguageToolDictionaryResponse {
added: boolean;
}

export const checkText = async (
text: string,
abortSignal: AbortSignal
Expand Down Expand Up @@ -131,3 +135,53 @@ export const checkText = async (
}
return body;
};

export const addToDictionary = async (
text: string
): Promise<LanguageToolDictionaryResponse> => {
const { languageToolEndpointUrl, languageToolUsername, languageToolApiKey } =
useStore.getState().settings;

const requestParams: { [key: string]: string } = {
word: text,
};

if (languageToolEndpointUrl === 'https://api.languagetoolplus.com') {
requestParams.username = languageToolUsername;
requestParams.apiKey = languageToolApiKey;
}

let response: Response;

try {
response = await fetch(`${languageToolEndpointUrl}/v2/words/add`, {
method: 'POST',
body: Object.keys(requestParams)
.map((key) => {
return `${encodeURIComponent(key)}=${encodeURIComponent(
requestParams[key]
)}`;
})
.join('&'),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
});
} catch (e) {
return Promise.reject(e);
}

if (!response.ok) {
return Promise.reject(
new Error(`unexpected status ${response.status}, see network tab`)
);
}
let body: { added: boolean };
try {
body = await response.json();
} catch (e) {
return Promise.reject(e);
}
return body;
};
19 changes: 19 additions & 0 deletions app/renderer/icons/DictionaryAddIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Icon from './Icon';
import { IconProps, IconPropDefaults } from './type';

export const DictionaryAddIcon = (props: IconProps) => {
return (
<Icon {...props}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M29 2.5C29.8284 2.5 30.5 3.17157 30.5 4V6.49999H33C33.8284 6.49999 34.5 7.17157 34.5 7.99999C34.5 8.82842 33.8284 9.49999 33 9.49999H30.5V12C30.5 12.8285 29.8284 13.5 29 13.5C28.1716 13.5 27.5 12.8285 27.5 12V9.49999H25C24.1716 9.49999 23.5 8.82842 23.5 7.99999C23.5 7.17157 24.1716 6.49999 25 6.49999H27.5V4C27.5 3.17157 28.1716 2.5 29 2.5ZM13.5379 6.49967L20.0001 6.49999C20.8285 6.50003 21.5 7.17164 21.5 8.00007C21.5 8.8285 20.8284 9.50003 19.9999 9.49999L13.6 9.49967V9.49967C12.4197 9.49967 11.6263 9.50088 11.0151 9.55173C10.4221 9.60105 10.1352 9.68925 9.94305 9.78893C9.45123 10.0441 9.04637 10.4539 8.79101 10.9642C8.68846 11.1691 8.60032 11.4714 8.55139 12.0811C8.50113 12.7075 8.5 13.5192 8.5 14.7197V26.756C9.28143 26.3374 10.1737 26.0997 11.125 26.0997H27.5V17C27.5 16.1716 28.1716 15.5 29 15.5C29.8284 15.5 30.5 16.1716 30.5 17V31.5759C30.5 31.6088 30.5 31.6416 30.5 31.6742V31.7997C30.5 31.8036 30.5 31.8075 30.5 31.8113C30.4995 32.4489 30.4955 33.0083 30.4577 33.4787C30.413 34.0362 30.3138 34.5983 30.0417 35.1421C29.6379 35.949 28.991 36.61 28.1892 37.026C27.646 37.3078 27.084 37.4103 26.5306 37.4563C26.0092 37.4997 25.3783 37.4997 24.6574 37.4997H13.5379C12.4356 37.4997 11.5168 37.4997 10.7664 37.4373C9.98358 37.3722 9.25179 37.2315 8.56153 36.8734C7.50101 36.3232 6.64378 35.448 6.10819 34.3777C5.77457 33.711 5.63538 33.006 5.56872 32.2498C5.52407 32.1077 5.5 31.9565 5.5 31.7997C5.5 31.6452 5.50611 31.492 5.51809 31.3404C5.49998 30.7551 5.49999 30.0903 5.5 29.3401V14.6593C5.49998 13.5345 5.49997 12.6018 5.56101 11.8411C5.62448 11.0501 5.76114 10.3151 6.10819 9.62163C6.64378 8.55135 7.50101 7.67614 8.56153 7.12596C9.25179 6.76786 9.98358 6.62717 10.7664 6.56205C11.5168 6.49963 12.4356 6.49965 13.5379 6.49967ZM8.52323 31.4372C8.53027 31.6099 8.53944 31.7693 8.55139 31.9182C8.60032 32.5279 8.68846 32.8302 8.79101 33.0351C9.04637 33.5454 9.45123 33.9553 9.94305 34.2104C10.1352 34.3101 10.4221 34.3983 11.0151 34.4476C11.6263 34.4985 12.4197 34.4997 13.6 34.4997H24.6C25.3953 34.4997 25.8999 34.4985 26.2818 34.4667C26.6455 34.4364 26.7627 34.3864 26.8077 34.363C27.0408 34.2421 27.2353 34.0464 27.3589 33.7996C27.3865 33.7444 27.4371 33.615 27.4673 33.2387C27.4989 32.8457 27.5 32.3281 27.5 31.5197V29.0997H11.125C9.81628 29.0997 8.69646 30.0985 8.52323 31.4372Z"
fill="black"
/>
</Icon>
);
};

DictionaryAddIcon.defaultProps = {
...IconPropDefaults,
};
4 changes: 2 additions & 2 deletions app/renderer/icons/Icon.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import styled from 'styled-components';
import { IconProps, IconPropDefaults } from './type';

const IconDiv = styled.span<IconProps>`
const IconDiv = styled.div<IconProps>`
height: ${(props) => props.size};
width: ${(props) => props.size};
overflow: hidden;
/* display: inline-block; */
display: inline-block;
`;

const StyledSvg = styled.svg<IconProps>`
Expand Down
1 change: 1 addition & 0 deletions app/renderer/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,4 @@ export { default as InfoIcon } from './InfoIcon';
export { default as CheckIcon } from './CheckIcon';
export { SpellCheckIcon } from './SpellCheckIcon';
export { AnnotationInfoIcon } from './AnnotationInfoIcon';
export { DictionaryAddIcon } from './DictionaryAddIcon';

0 comments on commit a169373

Please sign in to comment.