Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
### 0.14.0

- Fix: enforce font color (chrome bug) on text-area, based on theme. No more dark text on dark bg
- Feat: moved to rich-textarea library to not re-invent the wheel on text area styling.
This also allowed:
- Better styling for error highliht
- Additional tooltip with quick peek of the issue and and quick (up to 4) click to fix resolutions (sidebar gives more options if language tool provides)

### 0.12.0

- Introduced a new environment variable `DISABLE_DICTIONARY`. When this variable is set to true, the functionality for adding words to the dictionary is disabled.
Expand Down
11 changes: 8 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// eslint-disable-next-line no-undef
/* global process */
import express from "express";
import path from "path";
import bodyParser from "body-parser";
Expand Down Expand Up @@ -68,7 +70,10 @@ const handleProxyGET = (url, res, filter) => {
fetch(url)
.then((data) => data.json())
.then((data) => res.send(filter ? filter(data) : data))
.catch((error) => res.send(error));
.catch((error) => {
console.error("[PROXY GET ERROR]", error);
res.status(500).send('Internal server error');
});
};

const handleProxyPost = (url, req, res) => {
Expand All @@ -85,7 +90,7 @@ const handleProxyPost = (url, req, res) => {
})
.catch((error) => {
console.log({ error, url });
res.send(error);
res.status(500).send('Internal server error');
});
};

Expand All @@ -111,7 +116,7 @@ const handleFormDataPost = (url, req, res, filter) => {
})
.catch((error) => {
console.log({ error: error.message, url });
res.send(error);
res.status(500).send('Internal server error');
});
};

Expand Down
14 changes: 12 additions & 2 deletions package-lock.json

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "sth-libretranslate",
"private": true,
"version": "0.13.0",
"version": "0.14.0",
"type": "module",
"scripts": {
"dev": "concurrently \"npm run node\" \"vite\"",
Expand All @@ -21,6 +21,7 @@
"http-proxy-middleware": "^3.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rich-textarea": "^0.26.5",
"styled-components": "^6.1.11"
},
"devDependencies": {
Expand Down
9 changes: 5 additions & 4 deletions src/languagecheck/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { useCallback, useEffect } from "react";
import { Autocomplete, Divider, Stack, TextField } from "@mui/material";
import "./App.css";
import { useDebounce } from "../common/useDebounce";
import { TextBox } from "./Text";
import { useWindowSize } from "../common/useWindowSize";
import { API } from "./API";
import { Resolution } from "./Resolution";
import { LangChoice } from "../translate/types";
import { actions, useGrammar, useInitialiseGrammar } from "../store/grammar";
import { Content } from "./Content";

export const SelectLanguage = ({ label }: { label?: string }) => {
const { languages, language } = useGrammar();
Expand Down Expand Up @@ -37,7 +37,7 @@ export const SelectLanguage = ({ label }: { label?: string }) => {
function App() {
useInitialiseGrammar();
const { question, language } = useGrammar();
const q = useDebounce(question, 1000, () => actions.setTouched(false)) as string;
const q = useDebounce(question, 1000) as string;

const check = useCallback((text: string, language: LangChoice) => {
console.log("check");
Expand All @@ -51,7 +51,7 @@ function App() {
actions.setAnswer(data);
})
.catch((e) => actions.setError(e.message));
}, []);
}, [actions, API]);

useEffect(() => {
if (q.length > 1 && language) {
Expand All @@ -71,7 +71,8 @@ function App() {
className="translation"
>
<div>
<TextBox />
<Content />
{/* <TextBox /> */}
</div>
<Divider orientation="vertical" flexItem />
<div style={{ position: "relative" }}>
Expand Down
62 changes: 62 additions & 0 deletions src/languagecheck/Content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { RichTextarea } from "rich-textarea";
import { actions, useGrammar } from "../store/grammar";
import { Options } from "./Options";

const HighlightText = () => {
const { selection, answer, question: text } = useGrammar();
const highlights = answer?.matches || [];
let lastIndex = 0;
const renderedHighlights = [];
let counter = 0;

for (let i = 0; i < highlights.length; i++) {
const { offset, length, replacements } = highlights[i];
const nCount = text.substring(0, offset).match(/\\[ntrvf]/g)?.length ?? 0;
const nOffset = nCount;
if (offset > lastIndex) {
// Add the text before the current highlight
renderedHighlights.push(
<span key={counter}>{text.substring(lastIndex, offset + nOffset)}</span>
);
counter = counter + 1;
}

renderedHighlights.push(
<Options
key={counter}
replacements={replacements}
isSelected={selection === i}
text={text.substring(nOffset + offset, nOffset + offset + length)}
context={highlights[i]}
index={i}
/>
);
counter = counter + 1;

// Update lastIndex to the end of the current highlight
lastIndex = offset + length + nOffset;
}

// Add any remaining text after the last highlight
if (lastIndex < text.length) {
renderedHighlights.push(
<span key={counter}>{text.substring(lastIndex)}</span>
);
counter = counter + 1;
}
return <div>{renderedHighlights}</div>;
};

export const Content = () => {
const { question } = useGrammar();

return (
<RichTextarea
value={question}
style={{ width: "600px", height: "400px" }}
onChange={(e) => actions.setQuestion(e.target.value)}
>
{() => <HighlightText />}
</RichTextarea>
);
};
86 changes: 86 additions & 0 deletions src/languagecheck/Options.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
Chip,
Divider,
styled,
Tooltip,
tooltipClasses,
TooltipProps,
Typography,
} from "@mui/material";
import { actions } from "../store/grammar";
import Card from "@mui/material/Card";
import CardActions from "@mui/material/CardActions";
import CardContent from "@mui/material/CardContent";
import { Match } from "./API";

const HtmlTooltip = styled(({ className, ...props }: TooltipProps) => (
<Tooltip {...props} classes={{ popper: className }} />
))(() => ({
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: "transparent",
},
}));

type OptionsProps = {
replacements: { value: string }[];
isSelected: boolean;
text: string;
index: number;
context: Match;
};

export const Options = ({
replacements,
isSelected,
text,
index,
context,
}: OptionsProps) => {
const handleFixPos = (value: string) => {
actions.fixPos(value, index);
};

return (
<HtmlTooltip
sx={{ background: "transparent" }}
title={
<div>
<Card sx={{ minWidth: 275 }}>
<CardContent>
<Typography variant="h6" component="div">
{context.shortMessage}
</Typography>
<Typography variant="body2">{context.message}</Typography>
</CardContent>
<Divider />
<CardActions>
{replacements.slice(0, 4).map((elem) => (
<Chip
key={elem.value}
onClick={() => handleFixPos(elem.value)}
label={elem.value}
/>
))}
</CardActions>
</Card>
</div>
}
>
<span
style={{
position: "relative",
background: isSelected ? "rgba(255,0,0,0.3)" : "transparent",
borderBottom: isSelected ? "none" : "2px solid rgba(255,0,0,0.5)",
color: "#fff",
left: 0,
top: 0,
borderRadius: isSelected ? "4px" : 0,
cursor: "pointer",
}}
onClick={() => actions.setSelection(0)}
>
{text}
</span>
</HtmlTooltip>
);
};
37 changes: 25 additions & 12 deletions src/languagecheck/Resolution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,20 @@ const DisplayMatch = ({
context,
replacements,
shortMessage,
offset,
length,
fix,
selected,
setSelection,
idx,
rule,
original,
}: Match & {
fix: Fix;
selected: boolean;
setSelection: () => void;
idx: number;
original: string;
}) => {
const { disableDictionary } = useSystemStatus()
const fixPos = (value: string) => {
const nCount =
original.substring(0, offset).match(/\\[ntrvf]/g)?.length ?? 0;
fix(offset + nCount, length, value, idx);
};
const { disableDictionary } = useSystemStatus();
const { question, answer, selection } = useGrammar();

return (
<Accordion expanded={selected}>
<AccordionSummary
Expand All @@ -71,9 +64,30 @@ const DisplayMatch = ({
<Divider sx={{ mt: 2, mb: 2 }} />
{replacements.map((repl: { value: string }) => (
<Chip
key={repl.value}
size="small"
label={repl.value}
onClick={() => fixPos(repl.value)}
onClick={() => {
if (!answer || selection === null) {
return;
}

const match = answer.matches[selection];
if (!match) {
return;
}

// Count newline-like characters (\n, \t, etc.) before the offset
const nCount = match.context.text.substring(0, match.context.offset).match(/\\[ntrvf]/g)?.length ?? 0;
const adjustedStart = match.context.offset + nCount;

// Apply the fix to the question text
const fixedQuestion = `${question.substring(0, adjustedStart)}${repl.value}${question.substring(adjustedStart + match.context.length)}`;

// Update the state
actions.setQuestion(fixedQuestion);
actions.popAnswer(selection);
}}
sx={{ marginRight: 1, marginBottom: 0.5, marginTop: 0.5 }}
/>
))}
Expand Down Expand Up @@ -153,7 +167,6 @@ export const Resolution = () => {
selected={selection === i}
setSelection={() => actions.setSelection(i)}
idx={i}
original={original}
/>
))}
</div>
Expand Down
Loading