diff --git a/1000-hours/enjoy-app/audios.md b/1000-hours/enjoy-app/audios.md index 13c7ab49..c7595faf 100644 --- a/1000-hours/enjoy-app/audios.md +++ b/1000-hours/enjoy-app/audios.md @@ -25,9 +25,26 @@ Enjoy 支持添加本地的音频资源,和在线资源。在音频页面, 如果第二步语音转文本失败,请检查是否在正在使用本地 whisper 组件进行语音转文本,在某些电脑上可能因为兼容性和未知问题导致无法使用。如果出现这种情况,请在 [语音转文本服务设置](./settings#语音转文本服务) 中改用其他语音转文本的云服务。 ::: +## 播放 + +点击播放键(或者快捷键 Space)即可播放或者暂停音频。 + +Enjoy 会将音频按照句子切分,默认播放模式为“播放单句”,以便逐句反复听练。 + +其他可选的播放模式有: + +- 单句循环 +- 播放所有 + +## 智能断句 + +Enjoy 按照原音的停顿和标点符号,将当前句子分成几个断句,以便逐一反复练习。 + +你也可以通过点击当前句子的任意词语(或同时按下 Shift 进行多选)进行选中单词或者短语,进行听练。 + ## 录音 -Enjoy 会将音频按照句子切分,用户以句子为单位进行跟读练习。在激活的音频句子下,点击下方的录音按钮,即可开始录音,用户可以模仿音频朗读当前句子作为练习。 +Enjoy 会将音频按照句子切分,用户以句子为单位进行跟读练习。在激活的音频句子下,点击红色的录音按钮(或者快捷键 r),即可开始录音,用户可以模仿音频朗读当前句子作为练习。 ![音频播放页面](/images/enjoy/audio-page.png) _\* 音频播放页面_ @@ -36,9 +53,16 @@ _\* 音频播放页面_ 在 Mac 电脑上,首次使用录音功能时,会弹窗请求麦克风的使用权限,请务必点允许,否则无法使用录音功能。 ::: +## 录音对比 + +将录音的 Pitch contour 与原音对比,以便自纠发音。对比状态下,按下播放键,会同时播放录音和原音。 + +![录音对比](/images/enjoy/recording-comparing.png) +_\* 录音于原音对比_ + ## 发音评估 -Enjoy 集成了微软 Azure 的发音评估功能,作为自我的发音检查参考。点击录音右下方的仪表盘图标,即可打开发音评估弹窗。 +Enjoy 集成了微软 Azure 的发音评估功能,作为自我的发音检查参考。 该功能会以**录音时的句子文本作为参考**,评估录音的发音情况,各指标的详细说明可以参考微软的 [官方文档](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/how-to-pronunciation-assessment?pivots=programming-language-javascript#scripted-assessment-results)。 @@ -48,9 +72,7 @@ _\* 发音评估示例_ ::: warning 发音评估的使用建议 该功能是收费功能,每次使用均会在 Enjoy 账户的余额扣费,如果余额不足则需要 [充值](./settings#充值) 后才可继续使用。 -根据实测,该功能对提高学习者的发音作用水平有限,其评估结果仅做参考,不应该过度依赖。 - -另外,我们正在开发一套更有效的发音评估体系,敬请期待。 +值得注意的是,发音评估更侧重于单词的发音是否正确,无法评估音调变化是否正确。 ::: ## 修改音频信息 diff --git a/1000-hours/enjoy-app/faq.md b/1000-hours/enjoy-app/faq.md new file mode 100644 index 00000000..6fe388d9 --- /dev/null +++ b/1000-hours/enjoy-app/faq.md @@ -0,0 +1,23 @@ +# 常见问题 + +## 为什么本地语音转文本服务无法使用? + +Enjoy 集成了 [whipser.cpp](https://github.com/ggerganov/whisper.cpp) 作为本地的语音转文本(STT)服务,但是由于兼容性的问题,某些配置较低或者操作系统版本较低的电脑无法使用。 + +如果您遇到这种情况,Enjoy 提供了其他 STT 的云服务,可以前往 [软件设置](./settings#语音转文本服务) 进行配置。推荐优先使用 Azure AI。 + +## 403 Insufficient balance + +遇到这个报错,说明您正在使用 Enjoy 的付费功能,但是账户余额不足了。 + +Enjoy 内有很多功能都由 AI 驱动,比如[智能助手](./ai-assistant)、智能翻译、句子分析等。如果您在 [软件设置](./settings#默认-ai-引擎) 中的配置了 `OpenAI` 作为默认 AI 引擎,在使用这些功能时候,会使用您配置的 OpenAI 信息进行实现,不会涉及 Enjoy 的扣费。 + +(需要注意的是,[智能助手](./ai-assistant) 的对话一旦创建,AI 引擎无法修改。如果需要切换,比如由 Enjoy AI 换成 Open AI,则需要新建一个对话) + +另外,[发音评估](./audios#发音评估) 是收费功能,并非 OpenAI 提供,所以无论 [默认 AI 引擎](./settings#默认-ai-引擎) 选了什么,使用发音评估时,总是会在 Enjoy 账户中扣费。 + +如果需要充值,请参考[充值](./settings#充值)。 + +## 如何下载音频、录音 + +Enjoy 提供了音频、视频、录音的下载功能,以便可以在其他设备使用。 diff --git a/1000-hours/enjoy-app/install.md b/1000-hours/enjoy-app/install.md index 3fd9e594..13a2b4c6 100644 --- a/1000-hours/enjoy-app/install.md +++ b/1000-hours/enjoy-app/install.md @@ -4,13 +4,13 @@ Enjoy App 是一个跨平台的桌面应用,可以在 Windows、Mac 和 Linux 请根据电脑设备的操作系统,下载相应的版本安装使用。 -当前最新版本:[v0.1.0-alpha.13](https://github.com/xiaolai/everyone-can-use-english/releases/latest) +当前最新版本:[v0.2.0](https://github.com/xiaolai/everyone-can-use-english/releases/latest) ## Windows 支持 Windows 10 以上版本。 -[点击下载](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/Enjoy-0.1.0-alpha.13-Setup.exe) +[点击下载](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-0.2.0.Setup.exe) 下载后,双击即可安装。 @@ -18,8 +18,8 @@ Enjoy App 是一个跨平台的桌面应用,可以在 Windows、Mac 和 Linux 根据使用 Mac 电脑的芯片不同,需要下载不同的版本。 -- [Silicon 芯片版本(arm64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/Enjoy-0.1.0-alpha.13-arm64.dmg) -- [Intel 芯片版本(x64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/Enjoy-0.1.0-alpha.13-x64.dmg) +- [Silicon 芯片版本(arm64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-0.2.0-arm64.dmg) +- [Intel 芯片版本(x64)](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-0.2.0-x64.dmg) ::: info 如何查看本机配置 M1 以后的 Mac 电脑型号(M1、M2、M3),均为 Silicon 芯片。 @@ -37,8 +37,8 @@ M1 以后的 Mac 电脑型号(M1、M2、M3),均为 Silicon 芯片。 请根据不同发行版本选用安装文件。 -- [下载 deb 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/enjoy_0.1.0-alpha.13_amd64.deb) -- [下载 rpm 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.1.0-alpha.13/enjoy-0.1.0.alpha.13-1.x86_64.rpm) +- [下载 deb 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/enjoy_0.2.0_amd64.deb) +- [下载 rpm 版本](https://github.com/xiaolai/everyone-can-use-english/releases/download/v0.2.0/Enjoy-linux-x64-0.2.0.zip) ## 历史版本 diff --git a/1000-hours/enjoy-app/use-case-generate-audio-resources.md b/1000-hours/enjoy-app/use-case-generate-audio-resources.md index b1b1cf06..776cf37a 100644 --- a/1000-hours/enjoy-app/use-case-generate-audio-resources.md +++ b/1000-hours/enjoy-app/use-case-generate-audio-resources.md @@ -1,4 +1,4 @@ -# 利用 AI 生成训练材料 +# 使用案例:利用 AI 生成训练材料 用外语说我们自己想说的话是学习外语的其中一个重要目的。市面上的口语书实际上并不实用,因为那些话大都不是我们想要说的,我们需要创建专属自己的口语书。使用 Enjoy 可以很容易做到这一点。 diff --git a/1000-hours/enjoy-app/videos.md b/1000-hours/enjoy-app/videos.md index 0828e9d5..2e2b9d77 100644 --- a/1000-hours/enjoy-app/videos.md +++ b/1000-hours/enjoy-app/videos.md @@ -5,10 +5,6 @@ ![视频资源播放页面](/images/enjoy/video-page.png) _\* 视频播放页面_ -## 录音 - -在视频播放页面,需要录音时,可以点播放控制栏上的录音图标,视频将会被缩小至小窗,主界面上会显示录音按钮。 - ## 视频大小的限制 过大的视频文件会导致加载卡死而无法使用,目前 Enjoy 将添加视频的大小限制在 100 Mb,超过则会提示添加失败。 diff --git a/1000-hours/public/images/enjoy/audio-page.png b/1000-hours/public/images/enjoy/audio-page.png index 8c086863..074ddf18 100644 Binary files a/1000-hours/public/images/enjoy/audio-page.png and b/1000-hours/public/images/enjoy/audio-page.png differ diff --git a/1000-hours/public/images/enjoy/conversation-add-speech-to-audio.png b/1000-hours/public/images/enjoy/conversation-add-speech-to-audio.png index 4e520a42..f3b830ff 100644 Binary files a/1000-hours/public/images/enjoy/conversation-add-speech-to-audio.png and b/1000-hours/public/images/enjoy/conversation-add-speech-to-audio.png differ diff --git a/1000-hours/public/images/enjoy/recording-comparing.png b/1000-hours/public/images/enjoy/recording-comparing.png new file mode 100644 index 00000000..f0088668 Binary files /dev/null and b/1000-hours/public/images/enjoy/recording-comparing.png differ diff --git a/1000-hours/public/images/enjoy/select-ai-role.png b/1000-hours/public/images/enjoy/select-ai-role.png index a0d06a57..e7cf5871 100644 Binary files a/1000-hours/public/images/enjoy/select-ai-role.png and b/1000-hours/public/images/enjoy/select-ai-role.png differ diff --git a/1000-hours/public/images/enjoy/video-page.png b/1000-hours/public/images/enjoy/video-page.png index ca1aeb9d..50ec78ab 100644 Binary files a/1000-hours/public/images/enjoy/video-page.png and b/1000-hours/public/images/enjoy/video-page.png differ diff --git a/1000-hours/training-tasks/procedures.md b/1000-hours/training-tasks/procedures.md index 54566fd0..3fac70da 100644 --- a/1000-hours/training-tasks/procedures.md +++ b/1000-hours/training-tasks/procedures.md @@ -1,6 +1,6 @@ # 2. 训练方法 -请后继反复认真阅读《[语音塑造](/sounds-of-english/01-basics)》中的每一个章节。 +请后续反复认真阅读《[语音塑造](/sounds-of-english/01-basics)》中的每一个章节。 ## 2.1 搞清每个单词的读音 @@ -61,7 +61,7 @@ 自然语音中有**可换气停顿**的地方,就相当于是乐谱里的小节分界线。分段练习,实在难的小节,还可以继续拆分…… -在跟读的时候,有必要**夸张**一点。声音也要正常地**大** —— 确实不用喊,嗓子的确需要保护。实际上,我们主要需要练的是嘴唇、舌头、气流震动的配合,主要练的还真不是声带。 +在跟读的时候,有必要**夸张**一点。声音也要正常地**大** —— 确实不用喊,嗓子的确需要保护。实际上,我们主要需要练的是嘴唇、舌头、气流振动的配合,主要练的还真不是声带。 跟读的时候,可以戴着耳机。一个比较好的方法是戴单只耳机 —— 这样,一方面录音听得更清楚,另外一方面也不妨碍听清自己的声音,还可以换着耳朵戴。 @@ -119,4 +119,4 @@ TED 上有一个讲座,Benjamin Zander 讲 [The transformative power of classi 一天至少三个小时,每周最多可以中止一天。 -每天结束之前,要花 5~10 分钟做一下**复盘**,回忆并记录一下自己今天遇到的困难、已经克服的困难、尚未克服的困难、以及面对那些困难时所采用的方法…… 想一想还有什么方法可以试试?这些都要写下来。相信我,记录这个东西 ,总是会以想象不到的方式起想象不到的作用。 \ No newline at end of file +每天结束之前,要花 5~10 分钟做一下**复盘**,回忆并记录一下自己今天遇到的困难、已经克服的困难、尚未克服的困难、以及面对那些困难时所采用的方法…… 想一想还有什么方法可以试试?这些都要写下来。相信我,记录这个东西 ,总是会以想象不到的方式起想象不到的作用。 diff --git a/book/chapter2.md b/book/chapter2.md index dde360d8..8e0d904d 100644 --- a/book/chapter2.md +++ b/book/chapter2.md @@ -42,7 +42,7 @@ * [GRE的Issue题目](files/GRE-Analytical-Writing-Argument-Task-Topics.md) * [GRE的Argument题目](files/GRE-Analytical-Writing-Issue-Task-Topics) -在相当长一段时间里,我以为自己 “发现” 了一条真正的捷径,后来才发现 “我并不孤独”。在国内,卖得最好的口语教材,实际上是北京外国语大学的专业教材,作者是北外教授吴桢福老师:一共三本,《英语初级口语》、《英语中级口语》、《英语高级口语》,多年来多次再版,印数均超过 100 万本。如果读者有机会,不妨去书店翻翻这套教材──你会发现其中的大多数课文话题,实际上也许都是从 TOEFL 作文题脱胎而来的。 +在相当长一段时间里,我以为自己 “发现” 了一条真正的捷径,后来才发现 “我并不孤独”。在国内,卖得最好的口语教材,实际上是北京外国语大学的专业教材,作者是北外教授吴祯福老师:一共三本,《英语初级口语》、《英语中级口语》、《英语高级口语》,多年来多次再版,印数均超过 100 万本。如果读者有机会,不妨去书店翻翻这套教材──你会发现其中的大多数课文话题,实际上也许都是从 TOEFL 作文题脱胎而来的。 请允许我重复一遍:**你的问题也许不在于你不会说,而在于你没什么话可说。** diff --git a/enjoy/package.json b/enjoy/package.json index 9732bb57..92386055 100644 --- a/enjoy/package.json +++ b/enjoy/package.json @@ -3,7 +3,7 @@ "private": true, "name": "enjoy", "productName": "Enjoy", - "version": "0.2.0-preview", + "version": "0.2.0", "description": "Enjoy desktop app", "main": ".vite/build/main.js", "types": "./src/types.d.ts", @@ -110,8 +110,11 @@ "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toggle": "^1.0.3", "@radix-ui/react-tooltip": "^1.0.7", + "@sentry/electron": "^4.21.0", + "@types/ahoy.js": "^0.4.2", "@uidotdev/usehooks": "^2.4.1", "@vidstack/react": "^1.10.9", + "ahoy.js": "^0.4.3", "autosize": "^6.0.1", "axios": "^1.6.8", "camelcase": "^8.0.0", diff --git a/enjoy/src/commands/analyze.command.ts b/enjoy/src/commands/analyze.command.ts index edcf69a1..9e5d7ead 100644 --- a/enjoy/src/commands/analyze.command.ts +++ b/enjoy/src/commands/analyze.command.ts @@ -22,32 +22,26 @@ export const analyzeCommand = async ( }, cache: false, verbose: true, + maxRetries: 2, }); const prompt = ChatPromptTemplate.fromMessages([ ["system", SYSTEM_PROMPT], - ["human", TRANSLATION_PROMPT], + ["human", text], ]); - const response = await prompt.pipe(chatModel).invoke({ - native_language: "Chinese", - text, - }); + const response = await prompt.pipe(chatModel).invoke({}); return response.text; }; -const SYSTEM_PROMPT = `You are a language coach of English, and you are helping a student to learn {native_language}.`; -const TRANSLATION_PROMPT = ` -{text} +const SYSTEM_PROMPT = `你是我的英语教练,我将提供英语文本,你将帮助我分析文本的句子结构、语法和词汇/短语,并对文本进行详细解释。请用中文回答,并按以下格式返回结果: -Please analyze the text above, including sentence structure, grammar, and vocabulary/phrases, and provide a detailed explanation of the text. Please reply in {native_language} and return the result only in the following format: + ### 句子结构 + (解释句子的每个元素) - ### Sentence structure - (explain every element of the sentence) + ### 语法 + (解释句子的语法) - ### Grammar - (explain the grammar of the sentence) - - ### Vocabulary/phrases - (explain the key vocabulary and phrases used)`; + ### 词汇/短语 + (解释使用的关键词汇和短语)`; diff --git a/enjoy/src/commands/extract-story.command.ts b/enjoy/src/commands/extract-story.command.ts index 66500fc7..d5f66070 100644 --- a/enjoy/src/commands/extract-story.command.ts +++ b/enjoy/src/commands/extract-story.command.ts @@ -39,6 +39,7 @@ export const extractStoryCommand = async ( }, cache: true, verbose: true, + maxRetries: 2, }).bind({ tools: [ { diff --git a/enjoy/src/commands/ipa.command.ts b/enjoy/src/commands/ipa.command.ts index 88987b9c..c0ce3df1 100644 --- a/enjoy/src/commands/ipa.command.ts +++ b/enjoy/src/commands/ipa.command.ts @@ -59,6 +59,7 @@ export const ipaCommand = async ( }, cache: true, verbose: true, + maxRetries: 2, }); const prompt = ChatPromptTemplate.fromMessages([ diff --git a/enjoy/src/commands/lookup.command.ts b/enjoy/src/commands/lookup.command.ts index 2e21a864..7ab7b6bd 100644 --- a/enjoy/src/commands/lookup.command.ts +++ b/enjoy/src/commands/lookup.command.ts @@ -71,6 +71,7 @@ export const lookupCommand = async ( }, cache: true, verbose: true, + maxRetries: 2, }); const prompt = ChatPromptTemplate.fromMessages([ diff --git a/enjoy/src/commands/translate.command.ts b/enjoy/src/commands/translate.command.ts index ab248493..32c6eeb9 100644 --- a/enjoy/src/commands/translate.command.ts +++ b/enjoy/src/commands/translate.command.ts @@ -22,6 +22,7 @@ export const translateCommand = async ( }, cache: false, verbose: true, + maxRetries: 2, }); const prompt = ChatPromptTemplate.fromMessages([ diff --git a/enjoy/src/constants.ts b/enjoy/src/constants.ts index df6c2970..38495c15 100644 --- a/enjoy/src/constants.ts +++ b/enjoy/src/constants.ts @@ -7,6 +7,8 @@ export const WEB_API_URL = "https://enjoy-web.fly.dev"; export const REPO_URL = "https://github.com/xiaolai/everyone-can-use-english"; +export const SENTRY_DSN = "https://d51056d7af7d14eae446c0c15b4f3d31@o1168905.ingest.us.sentry.io/4506969353289728" + export const MAGIC_TOKEN_REGEX = /\b(Mrs|Ms|Mr|Dr|Prof|St|[a-zA-Z]{1,2}|\d{1,2})\.\b/g; export const END_OF_SENTENCE_REGEX = /[^\.!,\?][\.!\?]/g; diff --git a/enjoy/src/main.ts b/enjoy/src/main.ts index f2e28acf..d9d7c6b3 100644 --- a/enjoy/src/main.ts +++ b/enjoy/src/main.ts @@ -6,6 +6,12 @@ import mainWindow from "@main/window"; import ElectronSquirrelStartup from "electron-squirrel-startup"; import contextMenu from "electron-context-menu"; import { t } from "i18next"; +import * as Sentry from "@sentry/electron"; +import { SENTRY_DSN } from "@/constants"; + +Sentry.init({ + dsn: SENTRY_DSN, +}); app.commandLine.appendSwitch("enable-features", "SharedArrayBuffer"); diff --git a/enjoy/src/renderer.ts b/enjoy/src/renderer.ts index f3c7ec81..d6e184dd 100644 --- a/enjoy/src/renderer.ts +++ b/enjoy/src/renderer.ts @@ -28,6 +28,12 @@ import "./index.css"; import "./renderer/index"; +import * as Sentry from "@sentry/electron"; +import { SENTRY_DSN } from "@/constants"; + +Sentry.init({ + dsn: SENTRY_DSN, +}); declare global { interface Window { diff --git a/enjoy/src/renderer/components/medias/media-caption-tabs.tsx b/enjoy/src/renderer/components/medias/media-caption-tabs.tsx index 8c638835..f3b6438e 100644 --- a/enjoy/src/renderer/components/medias/media-caption-tabs.tsx +++ b/enjoy/src/renderer/components/medias/media-caption-tabs.tsx @@ -348,7 +348,7 @@ const TranslationTabContent = (props: { text: string }) => { setTranslation(result); } }) - .catch((err) => t("translationFailed", { error: err.message })) + .catch((err) => toast.error(err.message)) .finally(() => { setTranslating(false); }); @@ -422,7 +422,7 @@ const AnalysisTabContent = (props: { text: string }) => { setAnalysisResult(result); } }) - .catch((err) => t("analysisFailed", { error: err.message })) + .catch((err) => toast.error(err.message)) .finally(() => { setAnalyzing(false); }); diff --git a/enjoy/src/renderer/components/medias/media-current-recording.tsx b/enjoy/src/renderer/components/medias/media-current-recording.tsx index 7e92bd92..3a2a7316 100644 --- a/enjoy/src/renderer/components/medias/media-current-recording.tsx +++ b/enjoy/src/renderer/components/medias/media-current-recording.tsx @@ -222,7 +222,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => { const calContainerWidth = () => { const w = document - .querySelector(".media-recording-container") + .querySelector(".media-recording-wrapper") ?.getBoundingClientRect()?.width; if (!w) return; @@ -371,10 +371,15 @@ export const MediaCurrentRecording = (props: { height?: number }) => { ]); useEffect(() => { - if (!ref?.current) return; + if (!width) return; - ref.current.style.width = `${width}px`; - }, [width]); + const container: HTMLDivElement = document.querySelector( + ".media-recording-container" + ); + if (!container) return; + + container.style.width = `${width}px`; + }, [width, currentRecording, isRecording]); useEffect(() => { calContainerWidth(); @@ -387,7 +392,7 @@ export const MediaCurrentRecording = (props: { height?: number }) => { calContainerWidth(); }); }; - }, []); + }, [currentRecording, isRecording]); useHotkeys( ["Ctrl+R", "Meta+R"], @@ -428,9 +433,9 @@ export const MediaCurrentRecording = (props: { height?: number }) => { ); return ( -
-
-
+
+
+
{formatDuration(currentTime || 0)} diff --git a/enjoy/src/renderer/components/medias/media-player.tsx b/enjoy/src/renderer/components/medias/media-player.tsx index 1b3ed498..d46134db 100644 --- a/enjoy/src/renderer/components/medias/media-player.tsx +++ b/enjoy/src/renderer/components/medias/media-player.tsx @@ -87,7 +87,7 @@ export const MediaPlayer = () => { const calContainerWidth = () => { const w = document - .querySelector(".media-player-container") + .querySelector(".media-player-wrapper") ?.getBoundingClientRect()?.width; if (!w) return; @@ -122,7 +122,10 @@ export const MediaPlayer = () => { }, [ref]); useEffect(() => { - if (!ref?.current) return; + const container: HTMLDivElement = document.querySelector( + ".media-player-container" + ); + if (!container) return; ref.current.style.width = `${width}px`; }, [width]); @@ -141,11 +144,11 @@ export const MediaPlayer = () => { }, []); return ( -
-
+
+
{formatDuration(currentTime || 0)} diff --git a/enjoy/src/renderer/components/medias/media-recorder.tsx b/enjoy/src/renderer/components/medias/media-recorder.tsx index 068f4566..19469ab1 100644 --- a/enjoy/src/renderer/components/medias/media-recorder.tsx +++ b/enjoy/src/renderer/components/medias/media-recorder.tsx @@ -109,10 +109,10 @@ export const MediaRecorder = (props: { height?: number }) => { setDuration(0); interval = setInterval(() => { setDuration((duration) => { - if (duration >= 300) { + if (duration >= 3000) { setIsRecording(false); } - return duration + 1; + return duration + 0.1; }); }, 100); } else { @@ -135,7 +135,7 @@ export const MediaRecorder = (props: { height?: number }) => {
- {duration / 10} + {duration.toFixed(1)} / 300
diff --git a/enjoy/src/renderer/context/app-settings-provider.tsx b/enjoy/src/renderer/context/app-settings-provider.tsx index 7793f1a6..2de5e73c 100644 --- a/enjoy/src/renderer/context/app-settings-provider.tsx +++ b/enjoy/src/renderer/context/app-settings-provider.tsx @@ -5,6 +5,7 @@ import { Client } from "@/api"; import i18n from "@renderer/i18n"; import { FFmpeg } from "@ffmpeg/ffmpeg"; import { toBlobURL } from "@ffmpeg/util"; +import ahoy from "ahoy.js"; type AppSettingsProviderState = { webApi: Client; @@ -23,6 +24,7 @@ type AppSettingsProviderState = { switchLanguage?: (language: "en" | "zh-CN") => void; proxy?: ProxyConfigType; setProxy?: (config: ProxyConfigType) => Promise; + ahoy?: typeof ahoy; }; const initialState: AppSettingsProviderState = { @@ -71,6 +73,13 @@ export const AppSettingsProvider = ({ locale: language, }) ); + + if (user) { + ahoy.configure({ + urlPrefix: apiUrl, + }); + ahoy.track("logged in", { user: user.id }); + } }, [user, apiUrl, language]); const prepareFfmpeg = async () => { @@ -206,6 +215,7 @@ export const AppSettingsProvider = ({ proxy, setProxy: setProxyConfigHandler, initialized: Boolean(user && libraryPath), + ahoy, }} > {children} diff --git a/enjoy/src/renderer/hooks/use-conversation.tsx b/enjoy/src/renderer/hooks/use-conversation.tsx index 09d25268..99594d54 100644 --- a/enjoy/src/renderer/hooks/use-conversation.tsx +++ b/enjoy/src/renderer/hooks/use-conversation.tsx @@ -39,7 +39,7 @@ export const useConversation = () => { configuration: { baseURL: `${apiUrl}/api/ai`, }, - maxRetries: 3, + maxRetries: 2, modelName: model, temperature, maxTokens, @@ -55,6 +55,7 @@ export const useConversation = () => { configuration: { baseURL: baseUrl || openai.baseUrl, }, + maxRetries: 2, modelName: model, temperature, maxTokens, @@ -69,6 +70,7 @@ export const useConversation = () => { temperature, frequencyPenalty, presencePenalty, + maxRetries: 2, }); } else if (conversation.engine === "googleGenerativeAi") { if (!googleGenerativeAi) throw new Error("Google Generative AI API key is required"); @@ -78,6 +80,7 @@ export const useConversation = () => { modelName: model, temperature: temperature, maxOutputTokens: maxTokens, + maxRetries: 2, }); } }; diff --git a/package.json b/package.json index e841f090..9dee8158 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ ], "scripts": { "start:enjoy": "echo 'Please use `yarn enjoy:start` instead'", + "enjoy:add": "yarn workspace enjoy add", "enjoy:dev": "yarn workspace enjoy dev", "enjoy:start": "yarn workspace enjoy start", "enjoy:test": "yarn workspace enjoy test", diff --git a/yarn.lock b/yarn.lock index 503785a3..e06662e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4678,10 +4678,124 @@ __metadata: languageName: node linkType: hard -"@shikijs/core@npm:1.2.1, @shikijs/core@npm:^1.2.0": - version: 1.2.1 - resolution: "@shikijs/core@npm:1.2.1" - checksum: 10c0/60b5a1cdaf4389c1377b1527657377dac67173d5fd79fe4d9775392c037d6b0fe46c42bb3e3fbe42a88f4f62295f20f5cf3e43cdf1f070f4ccfedf9077379ecd +"@sentry-internal/feedback@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry-internal/feedback@npm:7.107.0" + dependencies: + "@sentry/core": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/b79a68eaefef8fb83f0d423a7aa1e80cf83d82be95396e79170452ed7fe82b0c3b75f6ffa2cef525127c5fe0caf64bdffdab5e374d8912b2496e9aa379e6d500 + languageName: node + linkType: hard + +"@sentry-internal/replay-canvas@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry-internal/replay-canvas@npm:7.107.0" + dependencies: + "@sentry/core": "npm:7.107.0" + "@sentry/replay": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/5223d66bb69cde2ba73049cacd55e5712dda6187c87df8514e8642a26ff13b8c3e34a8ec8f6c59437cb3f52a8785c3dc960eb9e046ca3bb2a74573ca99f8b77f + languageName: node + linkType: hard + +"@sentry-internal/tracing@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry-internal/tracing@npm:7.107.0" + dependencies: + "@sentry/core": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/d87143c548659ce018ba320406b098cc04429904b6194ae03e4626ccc71f2838256cee3390a1c8cd9b0ca945e8349456cf6da460772164545199682962218564 + languageName: node + linkType: hard + +"@sentry/browser@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry/browser@npm:7.107.0" + dependencies: + "@sentry-internal/feedback": "npm:7.107.0" + "@sentry-internal/replay-canvas": "npm:7.107.0" + "@sentry-internal/tracing": "npm:7.107.0" + "@sentry/core": "npm:7.107.0" + "@sentry/replay": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/6cefdb519815f14419135274da22d1748e28d01f9d85d4a77ae8d999dcde08d998b4a7bc417a55c46aa45cd07d5a1abba82701f4f662be15679e6f97b0a26343 + languageName: node + linkType: hard + +"@sentry/core@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry/core@npm:7.107.0" + dependencies: + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/4d28df638aec9f8b88700a09d6a5d71e05b627a4bd6e75e062a523a51d0aed3dc080909859a2a72fc49e1d7637b732ae616468a3e14482900df7c4b55739f537 + languageName: node + linkType: hard + +"@sentry/electron@npm:^4.21.0": + version: 4.21.0 + resolution: "@sentry/electron@npm:4.21.0" + dependencies: + "@sentry/browser": "npm:7.107.0" + "@sentry/core": "npm:7.107.0" + "@sentry/node": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + deepmerge: "npm:4.3.0" + tslib: "npm:^2.5.0" + checksum: 10c0/596fd402d96c26ae4af7b016010231f9748b3c801022529472e25cfc7caa334fce65f5b5cf44055c10232e35676644e53f96afd0d54b162ac171605919ee26b2 + languageName: node + linkType: hard + +"@sentry/node@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry/node@npm:7.107.0" + dependencies: + "@sentry-internal/tracing": "npm:7.107.0" + "@sentry/core": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/a2992e2c54a17cd323c7f7362c7742417b4fdff800f104f8de0506ca7c0c225b6709b4580bb82cebfbb28e2c53642f663d9e2ad7a68f5ef71f7b4bc8e1564501 + languageName: node + linkType: hard + +"@sentry/replay@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry/replay@npm:7.107.0" + dependencies: + "@sentry-internal/tracing": "npm:7.107.0" + "@sentry/core": "npm:7.107.0" + "@sentry/types": "npm:7.107.0" + "@sentry/utils": "npm:7.107.0" + checksum: 10c0/2bb4b0c05bed77a4ad4f7b6b5b653714d02cb4892d403115bddadb1c88ccbe7d2433a7a097973204b89da1587bc9d74e2b4b402dd2e2cff27c558fc762d9b0b9 + languageName: node + linkType: hard + +"@sentry/types@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry/types@npm:7.107.0" + checksum: 10c0/08a2b95a27106a974a42a1e2462c979b8d61d9ae63cef0d7bd36eb37603009e1e8e9bb181586d1a449b7fe52a4b696b01a4228ac13083a7c4088774f16c89421 + languageName: node + linkType: hard + +"@sentry/utils@npm:7.107.0": + version: 7.107.0 + resolution: "@sentry/utils@npm:7.107.0" + dependencies: + "@sentry/types": "npm:7.107.0" + checksum: 10c0/c0a0cd612f4db3d12853311ba8437e0315dee2f842d8ad741afeef75ab030965fc4288d8751c42f0b263d409792388d51257c3d665fba7efdf175527ef8891c4 + languageName: node + linkType: hard + +"@shikijs/core@npm:1.1.7, @shikijs/core@npm:^1.1.5": + version: 1.1.7 + resolution: "@shikijs/core@npm:1.1.7" + checksum: 10c0/ee59b88d4c81422792651c0ca52ff378c3035f9d1e4907b58c1d6da06fad02d530775a2e6f43bb033832cd3d2a5f69c9aa4eb5b6b05311396acc503f15442f37 languageName: node linkType: hard @@ -5274,6 +5388,13 @@ __metadata: languageName: node linkType: hard +"@types/ahoy.js@npm:^0.4.2": + version: 0.4.2 + resolution: "@types/ahoy.js@npm:0.4.2" + checksum: 10c0/2be990ab96b57e749e35bf3f80c37ce375e0f7b58bd0cc75848a70e22adb0f63c5b657c82a1915d40f4272b8e973103e993ac1b125e28e24e208002621010a15 + languageName: node + linkType: hard + "@types/argparse@npm:1.0.38": version: 1.0.38 resolution: "@types/argparse@npm:1.0.38" @@ -6297,6 +6418,13 @@ __metadata: languageName: node linkType: hard +"ahoy.js@npm:^0.4.3": + version: 0.4.3 + resolution: "ahoy.js@npm:0.4.3" + checksum: 10c0/eaacbf267d2094cb80651485af0d58034ec89d48f3755159ec850fb75d62b776222e6e9aa333aeb54d6ba1f0bac58f03653294244f1b01e69458a76fae3c2855 + languageName: node + linkType: hard + "ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -8435,6 +8563,13 @@ __metadata: languageName: node linkType: hard +"deepmerge@npm:4.3.0": + version: 4.3.0 + resolution: "deepmerge@npm:4.3.0" + checksum: 10c0/7ff5c6294b3316c1bc6bca9d3ef2193c1d7beec4e62252db8bcb8a6366d85b924850492eb1a746a5f33d609862e03dfb907ce9fa8769583300f65f20a337cec5 + languageName: node + linkType: hard + "deepmerge@npm:^4.3.1": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" @@ -9151,7 +9286,9 @@ __metadata: "@radix-ui/react-toast": "npm:^1.1.5" "@radix-ui/react-toggle": "npm:^1.0.3" "@radix-ui/react-tooltip": "npm:^1.0.7" + "@sentry/electron": "npm:^4.21.0" "@tailwindcss/typography": "npm:^0.5.10" + "@types/ahoy.js": "npm:^0.4.2" "@types/autosize": "npm:^4.0.3" "@types/command-exists": "npm:^1.2.3" "@types/electron-squirrel-startup": "npm:^1.0.2" @@ -9170,6 +9307,7 @@ __metadata: "@uidotdev/usehooks": "npm:^2.4.1" "@vidstack/react": "npm:^1.10.9" "@vitejs/plugin-react": "npm:^4.2.1" + ahoy.js: "npm:^0.4.3" autoprefixer: "npm:^10.4.19" autosize: "npm:^6.0.1" axios: "npm:^1.6.8"