diff --git a/enjoy/src/constants/dicts.ts b/enjoy/src/constants/dicts.ts
index d3c9ae50d..f55a686bf 100644
--- a/enjoy/src/constants/dicts.ts
+++ b/enjoy/src/constants/dicts.ts
@@ -1,4 +1,27 @@
export const DICTS = [
+ {
+ name: "ccalecd",
+ fileName: "ccalecd.zip",
+ title: "Collins COBUILD Advanced British EN-CN Dictionary",
+ pronunciation: true,
+ lang: "En-CN",
+ downloadUrl: "https://dl.enjoy.bot/dicts/ccalecd.zip",
+ size: "13.879MB",
+ hash: "96940f85e52df4586b287e1859723a39",
+ addition: '',
+ },
+
+ {
+ name: "ccabeld",
+ fileName: "ccabeld.zip",
+ title: "Collins COBUILD Advanced British English Learners Dictionary",
+ pronunciation: true,
+ lang: "En-En",
+ downloadUrl: "https://dl.enjoy.bot/dicts/ccabeld.zip",
+ size: "485.6MB",
+ hash: "5b53498536f3ce3ed173752b7888ca51",
+ addition: '',
+ },
{
name: "ldoce5",
fileName: "ldoce5.zip",
@@ -13,7 +36,7 @@ export const DICTS = [
{
name: "oxford_en_mac",
fileName: "oxford_en_mac.zip",
- title: "Oxford Dictionary of English (Mac)",
+ title: "Oxford Dictionary of English",
pronunciation: false,
lang: "En-En",
downloadUrl: "https://dl.enjoy.bot/dicts/oxford_en_mac.zip",
@@ -24,7 +47,7 @@ export const DICTS = [
{
name: "koen_mac",
fileName: "koen_mac.zip",
- title: "Korean English Dictionary (Mac)",
+ title: "Korean English Dictionary",
pronunciation: false,
lang: "Ko-En",
downloadUrl: "https://dl.enjoy.bot/dicts/koen_mac.zip",
@@ -46,7 +69,7 @@ export const DICTS = [
{
name: "deen_mac",
fileName: "deen_mac.zip",
- title: "German English Dictionary (Mac)",
+ title: "German English Dictionary",
pronunciation: false,
lang: "Ge-En",
downloadUrl: "https://dl.enjoy.bot/dicts/deen_mac.zip",
@@ -57,7 +80,7 @@ export const DICTS = [
{
name: "ruen_mac",
fileName: "ruen_mac.zip",
- title: "Russian English Dictionary (Mac)",
+ title: "Russian English Dictionary",
pronunciation: false,
lang: "Ru-En",
downloadUrl: "https://dl.enjoy.bot/dicts/ruen_mac.zip",
diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json
index 0ed5e3a8a..9a89cbb3a 100644
--- a/enjoy/src/i18n/en.json
+++ b/enjoy/src/i18n/en.json
@@ -751,7 +751,8 @@
"dictFileExist": "{{name}} has already been imported",
"dictFileAddSuccess": "Add {{name}} successfully",
"dictFileRemoveSuccess": "Remove {{name}} successfully",
- "dictFileSetDefaultSuccess": "Set default successfully",
+ "dictFileSetDefaultSuccess": "Successfully set as default dictionary",
+ "dictFileRemoveDefaultSuccess": "Operation successful",
"dictionaries": "Dictionaries",
"import": "Import",
"default": "Default",
@@ -769,5 +770,6 @@
"removing": "Removing",
"removeDictTitle": "Are you sure you want to delete this dictionary? ",
"removeDictDescription": "It will delete the dictionary file from your local computer and you will have to download it again next time.",
- "downloadingDict": "Downloading"
+ "downloadingDict": "Downloading",
+ "removeDefault": "No longer as Default"
}
diff --git a/enjoy/src/i18n/zh-CN.json b/enjoy/src/i18n/zh-CN.json
index 8f614180a..a51cc072c 100644
--- a/enjoy/src/i18n/zh-CN.json
+++ b/enjoy/src/i18n/zh-CN.json
@@ -751,7 +751,8 @@
"dictFileExist": "{{name}} 已经存在了",
"dictFileAddSuccess": "已添加 {{name}}",
"dictFileRemoveSuccess": "已删除 {{name}}",
- "dictFileSetDefaultSuccess": "设置成功",
+ "dictFileSetDefaultSuccess": "成功设置为默认词典",
+ "dictFileRemoveDefaultSuccess": "操作成功",
"dictionaries": "词典",
"import": "导入",
"default": "默认",
@@ -769,5 +770,6 @@
"removing": "正在删除",
"removeDictTitle": "你确定要删除词典吗?",
"removeDictDescription": "此操作将会从本地删除词典文件,下次安装需要重新下载",
- "downloadingDict": "正在下载"
+ "downloadingDict": "正在下载",
+ "removeDefault": "不再设置为默认"
}
diff --git a/enjoy/src/main/downloader.ts b/enjoy/src/main/downloader.ts
index 6720f3596..5bde5f457 100644
--- a/enjoy/src/main/downloader.ts
+++ b/enjoy/src/main/downloader.ts
@@ -130,7 +130,9 @@ class Downloader {
resume(filename: string) {
this.tasks
.filter(
- (t) => t.getFilename() === filename && t.getState() === "progressing"
+ (t) =>
+ t.getFilename() === filename &&
+ ["progressing", "interrupted"].includes(t.getState())
)
.forEach((t) => {
t.resume();
diff --git a/enjoy/src/renderer/components/medias/media-captions/tab-content-translation.tsx b/enjoy/src/renderer/components/medias/media-captions/tab-content-translation.tsx
index 0582ea072..5ea06ba12 100644
--- a/enjoy/src/renderer/components/medias/media-captions/tab-content-translation.tsx
+++ b/enjoy/src/renderer/components/medias/media-captions/tab-content-translation.tsx
@@ -2,6 +2,7 @@ import { useContext } from "react";
import {
AppSettingsProviderContext,
MediaPlayerProviderContext,
+ DictProviderContext,
} from "@renderer/context";
import { TabsContent, Separator } from "@renderer/components/ui";
import { t } from "i18next";
@@ -9,6 +10,7 @@ import { TimelineEntry } from "echogarden/dist/utilities/Timeline.d.js";
import { convertWordIpaToNormal } from "@/utils";
import {
CamdictLookupResult,
+ DictLookupResult,
AiLookupResult,
TranslateResult,
} from "@renderer/components";
@@ -40,6 +42,7 @@ const SelectedWords = (props: {
}) => {
const { selectedIndices, caption } = props;
+ const { currentDictValue } = useContext(DictProviderContext);
const { transcription } = useContext(MediaPlayerProviderContext);
const { learningLanguage, ipaMappings } = useContext(
AppSettingsProviderContext
@@ -100,11 +103,16 @@ const SelectedWords = (props: {
})}
- {learningLanguage.startsWith("en") && (
+ {currentDictValue === "cambridge" ? (
<>
>
+ ) : (
+ <>
+
+
+ >
)}
diff --git a/enjoy/src/renderer/components/preferences/dict-settings/downloading-dict-list.tsx b/enjoy/src/renderer/components/preferences/dict-settings/downloading-dict-list.tsx
index 6124067ba..767b41c78 100644
--- a/enjoy/src/renderer/components/preferences/dict-settings/downloading-dict-list.tsx
+++ b/enjoy/src/renderer/components/preferences/dict-settings/downloading-dict-list.tsx
@@ -5,7 +5,7 @@ import {
import { useContext, useEffect, useState } from "react";
import { Button, toast } from "@renderer/components/ui";
import { t } from "i18next";
-import { LoaderSpin } from "@renderer/components";
+import { LoaderIcon } from "lucide-react";
export const DownloadingDictList = function () {
const { EnjoyApp } = useContext(AppSettingsProviderContext);
@@ -129,7 +129,12 @@ const DownloadingDictItem = function ({ dict }: { dict: Dict }) {
}
function renderActions() {
- if (loading) return ;
+ if (loading)
+ return (
+
+
+
+ );
if (
dict.downloadState?.state === "progressing" &&
diff --git a/enjoy/src/renderer/components/preferences/dict-settings/installed-dict-list.tsx b/enjoy/src/renderer/components/preferences/dict-settings/installed-dict-list.tsx
index dd6d820e5..8ee55dc39 100644
--- a/enjoy/src/renderer/components/preferences/dict-settings/installed-dict-list.tsx
+++ b/enjoy/src/renderer/components/preferences/dict-settings/installed-dict-list.tsx
@@ -62,6 +62,15 @@ const InstalledDictItem = function ({ dict }: { dict: Dict }) {
}
}
+ async function handleRemoveDefault() {
+ try {
+ await setDefault(null);
+ toast.success(t("dictFileRemoveDefaultSuccess"));
+ } catch (err) {
+ toast.error(err.message);
+ }
+ }
+
async function handleRemove() {
setRemoving(true);
@@ -120,9 +129,15 @@ const InstalledDictItem = function ({ dict }: { dict: Dict }) {
-
+ {settings.default === dict.name ? (
+
+ ) : (
+
+ )}
);
}
diff --git a/enjoy/src/renderer/components/widgets/lookup/dict-lookup-result.tsx b/enjoy/src/renderer/components/widgets/lookup/dict-lookup-result.tsx
index 66f9bf7b5..10372be64 100644
--- a/enjoy/src/renderer/components/widgets/lookup/dict-lookup-result.tsx
+++ b/enjoy/src/renderer/components/widgets/lookup/dict-lookup-result.tsx
@@ -23,10 +23,12 @@ const MIME: Record = {
export function DictLookupResult({
word,
+ autoHeight,
onJump,
}: {
word: string;
- onJump: (v: string) => void;
+ autoHeight?: boolean;
+ onJump?: (v: string) => void;
}) {
const { colorScheme } = useContext(ThemeProviderContext);
const initialContent = ``;
@@ -36,6 +38,7 @@ export function DictLookupResult({
const [looking, setLooking] = useState(false);
const [notFound, setNotFound] = useState(false);
const [error, setError] = useState(false);
+ const [height, setHeight] = useState();
useEffect(() => {
if (currentDict && word) {
@@ -72,6 +75,10 @@ export function DictLookupResult({
setError(false);
}
+ function handleResize(h: number) {
+ setHeight(h);
+ }
+
if (looking) {
return (
@@ -100,9 +107,14 @@ export function DictLookupResult({
-
+
);
}
@@ -110,17 +122,38 @@ export function DictLookupResult({
export const DictLookupResultInner = ({
text,
onJump,
+ onResize,
+ autoHeight,
}: {
text: string;
- onJump: (v: string) => void;
+ autoHeight: boolean;
+ onJump?: (v: string) => void;
+ onResize?: (v: number) => void;
}) => {
const { EnjoyApp } = useContext(AppSettingsProviderContext);
const { currentDict } = useContext(DictProviderContext);
const { document: innerDocument } = useFrame();
const [html, setHtml] = useState("");
+ const [hash, setHash] = useState("");
+
+ useEffect(() => {
+ if (autoHeight) {
+ const resizeObserver = new ResizeObserver(() => {
+ const html = innerDocument.getElementsByTagName("html")[0];
+ onResize(html.scrollHeight);
+ });
+
+ resizeObserver.observe(innerDocument.getElementById("inner-dict"));
+ }
+ }, []);
useEffect(() => {
- normalize();
+ normalize().then(() => {
+ if (hash) {
+ handleScroll();
+ setHash("");
+ }
+ });
return () => {
normalizer.revoke();
@@ -166,7 +199,18 @@ export const DictLookupResultInner = ({
function handleJump(el: Element) {
const word = el.getAttribute("data-word");
- onJump(word);
+ const hash = el.getAttribute("data-hash");
+ onJump?.(word);
+ setHash(hash);
+ }
+
+ function handleScroll() {
+ setTimeout(() => {
+ const el = innerDocument.querySelector(`a[name='${hash}']`);
+ if (el) {
+ el.scrollIntoView();
+ }
+ }, 200);
}
const registerAudioHandler = () => {
@@ -199,5 +243,5 @@ export const DictLookupResultInner = ({
};
};
- return
;
+ return
;
};
diff --git a/enjoy/src/renderer/components/widgets/lookup/lookup-widget.tsx b/enjoy/src/renderer/components/widgets/lookup/lookup-widget.tsx
index 4b1e4f41b..b8b733ef4 100644
--- a/enjoy/src/renderer/components/widgets/lookup/lookup-widget.tsx
+++ b/enjoy/src/renderer/components/widgets/lookup/lookup-widget.tsx
@@ -14,6 +14,7 @@ import {
DictLookupResult,
DictSelect,
AiLookupResult,
+ CamdictLookupResult,
} from "@renderer/components";
import { ChevronLeft, ChevronFirst } from "lucide-react";
@@ -41,12 +42,12 @@ export const LookupWidget = () => {
) => {
let word = _word;
let context = _context;
+ let sourceType;
+ let sourceId;
- if (word) {
- if (word.indexOf(" ") > -1) return;
- setSelected({ word, context, position });
- } else {
- const selection = document.getSelection();
+ const selection = document.getSelection();
+
+ if (!word) {
if (!selection?.anchorNode?.parentElement) return;
word = selection
@@ -54,24 +55,23 @@ export const LookupWidget = () => {
.trim()
.replace(/[.,/#!$%^&*;:{}=\-_`~()]+$/, "");
- if (!word) return;
// can only lookup single word
- if (word.indexOf(" ") > -1) return;
+ if (!word || word.indexOf(" ") > -1) return;
+ }
+ if (!context) {
context = selection?.anchorNode.parentElement
.closest(".sentence, h2, p, div")
?.textContent?.trim();
-
- const sourceType = selection?.anchorNode.parentElement
+ sourceType = selection?.anchorNode.parentElement
.closest("[data-source-type]")
?.getAttribute("data-source-type");
- const sourceId = selection?.anchorNode.parentElement
+ sourceId = selection?.anchorNode.parentElement
.closest("[data-source-id]")
?.getAttribute("data-source-id");
-
- setSelected({ word, context, position, sourceType, sourceId });
}
+ setSelected({ word, context, position, sourceType, sourceId });
handleLookup(word);
setOpen(true);
};
@@ -102,7 +102,7 @@ export const LookupWidget = () => {
return (
{
sourceId={selected?.sourceId}
sourceType={selected?.sourceType}
/>
+ ) : currentDictValue === "cambridge" ? (
+
) : (
)}
diff --git a/enjoy/src/renderer/components/widgets/vocabulary.tsx b/enjoy/src/renderer/components/widgets/vocabulary.tsx
index ec95e60b2..a51c1e632 100644
--- a/enjoy/src/renderer/components/widgets/vocabulary.tsx
+++ b/enjoy/src/renderer/components/widgets/vocabulary.tsx
@@ -1,4 +1,4 @@
-import React, { useContext } from "react";
+import React, { useContext, useState } from "react";
import { AppSettingsProviderContext } from "@renderer/context";
export const Vocabulary = ({
@@ -10,18 +10,28 @@ export const Vocabulary = ({
context?: string;
children?: React.ReactNode;
}) => {
- let timeout: ReturnType;
-
+ let [timer, setTimer] = useState>();
const { vocabularyConfig, EnjoyApp } = useContext(AppSettingsProviderContext);
- const handleMouseEnter = (e: React.MouseEvent) => {
- timeout = setTimeout(() => {
- EnjoyApp.lookup(word, context, { x: e.clientX, y: e.clientY });
- }, 500);
+ const handleMouseEnter = (e: any) => {
+ let _timer = setTimeout(() => {
+ if (!context) {
+ context = e.target?.parentElement
+ .closest(".sentence, h2, p, div")
+ ?.textContent?.trim();
+ }
+
+ const { x, bottom: y } = e.target.getBoundingClientRect();
+ const _word = word.replace(/[^\w\s]|_/g, "");
+
+ EnjoyApp.lookup(_word, context, { x, y });
+ }, 1000);
+
+ setTimer(_timer);
};
const handleMouseLeave = () => {
- clearTimeout(timeout);
+ clearTimeout(timer);
};
return vocabularyConfig.lookupOnMouseOver ? (
@@ -30,7 +40,7 @@ export const Vocabulary = ({
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
- {word || children}
+ {word || children}
) : (
{word || children}
diff --git a/enjoy/src/renderer/context/dict-provider.tsx b/enjoy/src/renderer/context/dict-provider.tsx
index a2f1ce9a2..cd692be99 100644
--- a/enjoy/src/renderer/context/dict-provider.tsx
+++ b/enjoy/src/renderer/context/dict-provider.tsx
@@ -23,6 +23,11 @@ const AIDict = {
value: "ai",
};
+const CamDict = {
+ text: t("cambridgeDictionary"),
+ value: "cambridge",
+};
+
const initialState: DictProviderState = {
dicts: [],
downloadingDicts: [],
@@ -39,7 +44,7 @@ export const DictProviderContext =
createContext(initialState);
export const DictProvider = ({ children }: { children: React.ReactNode }) => {
- const { EnjoyApp } = useContext(AppSettingsProviderContext);
+ const { EnjoyApp, learningLanguage } = useContext(AppSettingsProviderContext);
const [dicts, setDicts] = useState([]);
const [settings, setSettings] = useState({
default: "",
@@ -60,14 +65,18 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => {
);
const dictSelectItems = useMemo(() => {
+ const presets = learningLanguage.startsWith("en")
+ ? [CamDict, AIDict]
+ : [AIDict];
+
return [
- AIDict,
+ ...presets,
...availableDicts.map((item) => ({
text: item.title,
value: item.name,
})),
];
- }, [availableDicts]);
+ }, [availableDicts, learningLanguage]);
const downloadingDicts = useMemo(() => {
return dicts.filter(
@@ -84,18 +93,16 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => {
}, [dicts]);
useEffect(() => {
- if (availableDicts.length) {
- const _currentDict = availableDicts.find(
- (dict) => dict.name === settings.default
- );
+ const defaultDict = availableDicts.find(
+ (dict) => dict.name === settings.default
+ );
- if (_currentDict) {
- handleSetCurrentDict(_currentDict.name);
- } else {
- setDefault(availableDicts[0]);
- }
+ if (defaultDict) {
+ handleSetCurrentDict(defaultDict.name);
} else {
- setCurrentDictValue(AIDict.value);
+ setCurrentDictValue(
+ learningLanguage.startsWith("en") ? CamDict.value : AIDict.value
+ );
}
}, [availableDicts, settings]);
@@ -123,7 +130,7 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => {
if (dict) setCurrentDict(dict);
};
- const setDefault = async (dict: Dict) => {
+ const setDefault = async (dict: Dict | null) => {
const _settings = { ...settings, default: dict?.name ?? "" };
EnjoyApp.settings
diff --git a/enjoy/src/renderer/lib/dict.ts b/enjoy/src/renderer/lib/dict.ts
index 48e752ea0..8cb32356c 100644
--- a/enjoy/src/renderer/lib/dict.ts
+++ b/enjoy/src/renderer/lib/dict.ts
@@ -104,9 +104,16 @@ export class DictDefinitionNormalizer {
...this.$('a[href^="bword://"]').toArray(),
].map((link) => {
const $link = this.$(link);
- const word = $link.attr("_href") || $link.attr("href").substring(8);
- $link.attr("data-type", "jump").attr("data-word", word);
+ const href = $link.attr("_href") || $link.attr("href").substring(8);
+ const [word, hash] = href.split("#");
+
+ if (word) {
+ $link
+ .attr("data-type", "jump")
+ .attr("data-word", word)
+ .attr("data-hash", hash);
+ }
})
);
}