feat: Restructured core logic to support automatic page scanning and rich text translation

This commit is contained in:
Gabe
2025-09-21 19:51:57 +08:00
parent 7dc847fca2
commit 943a9e86f0
24 changed files with 2095 additions and 705 deletions

View File

@@ -560,7 +560,12 @@ function ApiAccordion({ translator }) {
return (
<Accordion expanded={expanded} onChange={handleChange}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>
<Typography
sx={{
opacity: api.isDisabled ? 0.5 : 1,
overflowWrap: "anywhere",
}}
>
{api.apiName ? `${translator} (${api.apiName})` : translator}
</Typography>
</AccordionSummary>

View File

@@ -0,0 +1,57 @@
import Box from "@mui/material/Box";
import Stack from "@mui/material/Stack";
import { useI18n } from "../../hooks/I18n";
import ShortcutInput from "./ShortcutInput";
import FormControlLabel from "@mui/material/FormControlLabel";
import Switch from "@mui/material/Switch";
import { useMouseHoverSetting } from "../../hooks/MouseHover";
import { useCallback } from "react";
import Grid from "@mui/material/Grid";
export default function MouseHoverSetting() {
const i18n = useI18n();
const { mouseHoverSetting, updateMouseHoverSetting } = useMouseHoverSetting();
const handleShortcutInput = useCallback(
(val) => {
updateMouseHoverSetting({ mouseHoverKey: val });
},
[updateMouseHoverSetting]
);
const { useMouseHover = true, mouseHoverKey = ["ControlLeft"] } =
mouseHoverSetting;
return (
<Box>
<Stack spacing={3}>
<FormControlLabel
control={
<Switch
size="small"
name="useMouseHover"
checked={useMouseHover}
onChange={() => {
updateMouseHoverSetting({ useMouseHover: !useMouseHover });
}}
/>
}
label={i18n("use_mousehover_translation")}
/>
<Box>
<Grid container spacing={2} columns={12}>
<Grid item xs={12} sm={12} md={4} lg={4}>
<ShortcutInput
value={mouseHoverKey}
onChange={handleShortcutInput}
label={i18n("trigger_trans_shortcut")}
helperText={i18n("mousehover_key_help")}
/>
</Grid>
</Grid>
</Box>
</Stack>
</Box>
);
}

View File

@@ -14,6 +14,7 @@ import ApiIcon from "@mui/icons-material/Api";
import InputIcon from "@mui/icons-material/Input";
import SelectAllIcon from "@mui/icons-material/SelectAll";
import EventNoteIcon from "@mui/icons-material/EventNote";
import MouseIcon from '@mui/icons-material/Mouse';
function LinkItem({ label, url, icon }) {
const match = useMatch(url);
@@ -52,6 +53,12 @@ export default function Navigator(props) {
url: "/tranbox",
icon: <SelectAllIcon />,
},
{
id: "mousehover_translate",
label: i18n("mousehover_translate"),
url: "/mousehover",
icon: <MouseIcon />,
},
{
id: "apis_setting",
label: i18n("apis_setting"),

View File

@@ -16,9 +16,7 @@ import {
OPT_STYLE_USE_COLOR,
URL_KISS_RULES_NEW_ISSUE,
OPT_SYNCTYPE_WORKER,
OPT_TIMING_PAGESCROLL,
DEFAULT_TRANS_TAG,
OPT_TIMING_ALL,
} from "../../config";
import { useState, useEffect, useMemo } from "react";
import { useI18n } from "../../hooks/I18n";
@@ -55,7 +53,6 @@ import HelpButton from "./HelpButton";
import { useSyncCaches } from "../../hooks/Sync";
import DownloadButton from "./DownloadButton";
import UploadButton from "./UploadButton";
import { FIXER_ALL } from "../../libs/webfix";
import AddIcon from "@mui/icons-material/Add";
import EditIcon from "@mui/icons-material/Edit";
import CancelIcon from "@mui/icons-material/Cancel";
@@ -78,6 +75,8 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
pattern,
selector,
keepSelector = "",
rootsSelector = "",
ignoreSelector = "",
terms = "",
selectStyle = "",
parentStyle = "",
@@ -91,17 +90,20 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
bgColor,
textDiyStyle,
transOnly = "false",
transTiming = OPT_TIMING_PAGESCROLL,
autoScan = "true",
hasRichText = "true",
hasShadowroot = "false",
// transTiming = OPT_TIMING_PAGESCROLL,
transTag = DEFAULT_TRANS_TAG,
transTitle = "false",
transSelected = "true",
detectRemote = "false",
skipLangs = [],
fixerSelector = "",
fixerFunc = "-",
// fixerSelector = "",
// fixerFunc = "-",
transStartHook = "",
transEndHook = "",
transRemoveHook = "",
// transRemoveHook = "",
} = formValues;
const hasSamePattern = (str) => {
@@ -236,7 +238,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
helperText={errors.selector || i18n("selector_helper")}
name="selector"
value={selector}
disabled={disabled}
disabled={autoScan === "true" || disabled}
onChange={handleChange}
onFocus={handleFocus}
multiline
@@ -251,6 +253,26 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
onChange={handleChange}
multiline
/>
<TextField
size="small"
label={i18n("root_selector")}
helperText={i18n("root_selector_helper")}
name="rootsSelector"
value={rootsSelector}
disabled={disabled}
onChange={handleChange}
multiline
/>
<TextField
size="small"
label={i18n("ignore_selector")}
helperText={i18n("ignore_selector_helper")}
name="ignoreSelector"
value={ignoreSelector}
disabled={disabled}
onChange={handleChange}
multiline
/>
<Box>
<Grid container spacing={2} columns={12}>
@@ -270,6 +292,126 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
<MenuItem value={"false"}>{i18n("default_disabled")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="autoScan"
value={autoScan}
label={i18n("auto_scan_page")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="hasRichText"
value={hasRichText}
label={i18n("has_rich_text")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="hasShadowroot"
value={hasShadowroot}
label={i18n("has_shadowroot")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transSelected"
value={transSelected}
label={i18n("translate_selected")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transOnly"
value={transOnly}
label={i18n("show_only_translations")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
</Grid>
</Box>
<Box>
<Grid container spacing={2} columns={12}>
{/* <Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTiming"
value={transTiming}
label={i18n("trigger_mode")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
{OPT_TIMING_ALL.map((item) => (
<MenuItem key={item} value={item}>
{i18n(item)}
</MenuItem>
))}
</TextField>
</Grid> */}
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTitle"
value={transTitle}
label={i18n("translate_page_title")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
@@ -378,41 +520,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
<Box>
<Grid container spacing={2} columns={12}>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transOnly"
value={transOnly}
label={i18n("show_only_translations")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTiming"
value={transTiming}
label={i18n("trigger_mode")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
{OPT_TIMING_ALL.map((item) => (
<MenuItem key={item} value={item}>
{i18n(item)}
</MenuItem>
))}
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
@@ -429,38 +536,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
<MenuItem value={"font"}>{`<font>`}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transTitle"
value={transTitle}
label={i18n("translate_page_title")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
size="small"
fullWidth
name="transSelected"
value={transSelected}
label={i18n("translate_selected")}
disabled={disabled}
onChange={handleChange}
>
{GlobalItem}
<MenuItem value={"false"}>{i18n("disable")}</MenuItem>
<MenuItem value={"true"}>{i18n("enable")}</MenuItem>
</TextField>
</Grid>
<Grid item xs={12} sm={6} md={3} lg={2}>
<TextField
select
@@ -482,7 +557,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
{showMore && (
<>
<TextField
{/* <TextField
size="small"
label={i18n("fixer_selector")}
name="fixerSelector"
@@ -508,7 +583,7 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
{item}
</MenuItem>
))}
</TextField>
</TextField> */}
<TextField
select
@@ -542,40 +617,6 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
maxRows={10}
/>
<TextField
size="small"
label={i18n("translate_start_hook")}
helperText={i18n("translate_start_hook_helper")}
name="transStartHook"
value={transStartHook}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/>
<TextField
size="small"
label={i18n("translate_end_hook")}
helperText={i18n("translate_end_hook_helper")}
name="transEndHook"
value={transEndHook}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/>
<TextField
size="small"
label={i18n("translate_remove_hook")}
helperText={i18n("translate_remove_hook_helper")}
name="transRemoveHook"
value={transRemoveHook}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/>
<TextField
size="small"
label={i18n("selector_style")}
@@ -598,6 +639,41 @@ function RuleFields({ rule, rules, setShow, setKeyword }) {
maxRows={10}
multiline
/>
<TextField
size="small"
label={i18n("translate_start_hook")}
helperText={i18n("translate_start_hook_helper")}
name="transStartHook"
value={transStartHook}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/>
<TextField
size="small"
label={i18n("translate_end_hook")}
helperText={i18n("translate_end_hook_helper")}
name="transEndHook"
value={transEndHook}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/>
{/* <TextField
size="small"
label={i18n("translate_remove_hook")}
helperText={i18n("translate_remove_hook_helper")}
name="transRemoveHook"
value={transRemoveHook}
disabled={disabled}
onChange={handleChange}
multiline
maxRows={10}
/> */}
<TextField
size="small"
label={i18n("inject_css")}

View File

@@ -61,7 +61,7 @@ export default function Settings() {
value = limitNumber(value, 0, 5000);
break;
case "transInterval":
value = limitNumber(value, 100, 5000);
value = limitNumber(value, 10, 2000);
break;
case "minLength":
value = limitNumber(value, 1, 100);
@@ -119,7 +119,7 @@ export default function Settings() {
touchTranslate = 2,
blacklist = DEFAULT_BLACKLIST.join(",\n"),
csplist = DEFAULT_CSPLIST.join(",\n"),
transInterval = 500,
transInterval = 200,
langDetector = OPT_TRANS_MICROSOFT,
} = setting;
const { isHide = false } = fab || {};

View File

@@ -16,6 +16,7 @@ import { useCallback } from "react";
import { limitNumber } from "../../libs/utils";
import { useTranbox } from "../../hooks/Tranbox";
import { isExt } from "../../libs/client";
import Alert from "@mui/material/Alert";
export default function Tranbox() {
const i18n = useI18n();
@@ -67,6 +68,7 @@ export default function Tranbox() {
return (
<Box>
<Stack spacing={3}>
<Alert severity="info">{i18n("selected_translation_alert")}</Alert>
<TextField
select
size="small"

View File

@@ -21,6 +21,7 @@ import Apis from "./Apis";
import InputSetting from "./InputSetting";
import Tranbox from "./Tranbox";
import FavWords from "./FavWords";
import MouseHoverSetting from "./MouseHover";
export default function Options() {
const [error, setError] = useState("");
@@ -113,6 +114,7 @@ export default function Options() {
<Route path="rules" element={<Rules />} />
<Route path="input" element={<InputSetting />} />
<Route path="tranbox" element={<Tranbox />} />
<Route path="mousehover" element={<MouseHoverSetting />} />
<Route path="apis" element={<Apis />} />
<Route path="sync" element={<SyncSetting />} />
<Route path="words" element={<FavWords />} />