From fffb97f8dc7c950545ca905d5c322224d5951b01 Mon Sep 17 00:00:00 2001 From: an-lee Date: Fri, 6 Sep 2024 18:32:09 +0800 Subject: [PATCH] Improve: use database to save user settings (#1041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add user settings * fix user setting * migrate dict settings * migrate hotkeys * fix hotkey setting * update library settings * migrate gpt Engine * use user setting key enum * migrate openai * migrate more settings * migrate whisper config * migrate whisper * refactor * clean up * migrate profile * migrate recorder config * refactor * refactor * fix e2e * add api status * fix e2e * fix app init * fetch apiUrl before fetch user * update stt engine enums * update enums * update enums * refactor login flow * Fix warning * Login from remembered users * fix e2e * refactor * add unauthorized alert * feat: 🎸 dict import update (#1040) * rectified. according to Issues. * issue #1025 * feat: add Vietnamese language to support (#1043) * feat: add vietnamese language to support * fix: update Vietnamese language name to native form --------- Co-authored-by: Ryan * upgrade deps * update locales --------- Co-authored-by: divisey <18656007202@163.com> Co-authored-by: xiaolai Co-authored-by: ryan <69750456+ryangwn@users.noreply.github.com> Co-authored-by: Ryan --- 1000-hours/package.json | 6 +- 1000h-portal/package.json | 12 +- enjoy/e2e/main.spec.ts | 6 - enjoy/e2e/renderer.spec.ts | 5 +- enjoy/package.json | 32 +- enjoy/src/api/client.ts | 29 +- enjoy/src/i18n/en.json | 12 +- enjoy/src/i18n/zh-CN.json | 14 +- enjoy/src/main.ts | 1 - enjoy/src/main/db/handlers/audios-handler.ts | 11 + .../main/db/handlers/cache-objects-handler.ts | 8 + .../main/db/handlers/chat-agents-handler.ts | 8 + .../main/db/handlers/chat-members-handler.ts | 9 +- .../main/db/handlers/chat-messages-handler.ts | 8 + enjoy/src/main/db/handlers/chats-handler.ts | 16 +- .../main/db/handlers/conversations-handler.ts | 8 + enjoy/src/main/db/handlers/index.ts | 1 + .../src/main/db/handlers/messages-handler.ts | 10 + enjoy/src/main/db/handlers/notes-handler.ts | 11 + .../pronunciation-assessments-handler.ts | 8 + .../main/db/handlers/recordings-handler.ts | 14 + .../src/main/db/handlers/segments-handler.ts | 7 + .../src/main/db/handlers/speeches-handler.ts | 5 + .../db/handlers/transcriptions-handler.ts | 5 + .../main/db/handlers/user-settings-handler.ts | 79 + enjoy/src/main/db/handlers/videos-handler.ts | 11 + enjoy/src/main/db/index.ts | 111 +- .../1725411577564-create-user-setting.js | 45 + enjoy/src/main/db/models/index.ts | 1 + enjoy/src/main/db/models/user-setting.ts | 176 ++ enjoy/src/main/i18n.ts | 25 +- enjoy/src/main/settings.ts | 173 +- enjoy/src/main/whisper.ts | 54 +- enjoy/src/main/window.ts | 3 +- enjoy/src/preload.ts | 75 +- enjoy/src/renderer/app.tsx | 20 +- .../cables/channels/notifications_channel.ts | 4 + .../renderer/components/chats/chat-form.tsx | 8 +- .../components/medias/media-loading-modal.tsx | 9 +- .../components/misc/bandu-login-form.tsx | 5 + .../src/renderer/components/misc/db-state.tsx | 6 - enjoy/src/renderer/components/misc/layout.tsx | 2 +- .../renderer/components/misc/login-form.tsx | 161 +- .../components/misc/mixin-login-form.tsx | 9 +- .../src/renderer/components/misc/sidebar.tsx | 13 +- .../preferences/api-url-settings.tsx | 4 +- .../renderer/components/preferences/index.ts | 2 +- .../preferences/library-settings.tsx | 14 +- ...{whisper-settings.tsx => stt-settings.tsx} | 44 +- .../components/preferences/user-settings.tsx | 2 +- .../transcription-create-form.tsx | 19 +- .../renderer/components/widgets/sentence.tsx | 4 +- .../renderer/context/ai-settings-provider.tsx | 99 +- .../context/app-settings-provider.tsx | 137 +- enjoy/src/renderer/context/db-provider.tsx | 62 +- enjoy/src/renderer/context/dict-provider.tsx | 25 +- .../context/hotkeys-settings-provider.tsx | 37 +- .../context/media-player-provider.tsx | 3 +- enjoy/src/renderer/hooks/use-transcribe.tsx | 11 +- .../src/renderer/hooks/use-transcriptions.tsx | 15 +- enjoy/src/renderer/pages/home.tsx | 43 +- enjoy/src/renderer/pages/landing.tsx | 2 +- enjoy/src/types/enjoy-app.d.ts | 30 +- enjoy/src/types/enums.ts | 27 + enjoy/src/types/index.d.ts | 2 +- enjoy/src/types/user.d.ts | 2 +- yarn.lock | 1415 ++++++++++++----- 67 files changed, 2185 insertions(+), 1050 deletions(-) create mode 100644 enjoy/src/main/db/handlers/user-settings-handler.ts create mode 100644 enjoy/src/main/db/migrations/1725411577564-create-user-setting.js create mode 100644 enjoy/src/main/db/models/user-setting.ts rename enjoy/src/renderer/components/preferences/{whisper-settings.tsx => stt-settings.tsx} (72%) create mode 100644 enjoy/src/types/enums.ts diff --git a/1000-hours/package.json b/1000-hours/package.json index 285dab8e..b8203c90 100644 --- a/1000-hours/package.json +++ b/1000-hours/package.json @@ -7,11 +7,11 @@ "markdown-it-mathjax3": "^4.3.2", "markdown-it-sub": "^2.0.0", "markdown-it-sup": "^2.0.0", - "mermaid": "^11.0.2", - "sass": "^1.77.8", + "mermaid": "^11.1.0", + "sass": "^1.78.0", "vitepress": "^1.3.4", "vitepress-plugin-mermaid": "^2.0.16", - "vue": "^3.4.38" + "vue": "^3.5.3" }, "scripts": { "dev": "vitepress dev", diff --git a/1000h-portal/package.json b/1000h-portal/package.json index 4c6cf4a5..8021d7ba 100644 --- a/1000h-portal/package.json +++ b/1000h-portal/package.json @@ -10,16 +10,16 @@ "postinstall": "nuxt prepare" }, "dependencies": { - "@nuxtjs/seo": "^2.0.0-rc.19", - "nuxt": "^3.13.0", - "nuxt-og-image": "^3.0.0-rc.65", - "vue": "^3.4.38", + "@nuxtjs/seo": "^2.0.0-rc.21", + "nuxt": "^3.13.1", + "nuxt-og-image": "^3.0.0-rc.66", + "vue": "^3.5.3", "vue-router": "^4.4.3" }, "devDependencies": { "autoprefixer": "^10.4.20", - "postcss": "^8.4.43", - "sass": "^1.77.8", + "postcss": "^8.4.45", + "sass": "^1.78.0", "tailwindcss": "^3.4.10" } } diff --git a/enjoy/e2e/main.spec.ts b/enjoy/e2e/main.spec.ts index 07f5cf44..5c464a42 100644 --- a/enjoy/e2e/main.spec.ts +++ b/enjoy/e2e/main.spec.ts @@ -66,9 +66,6 @@ test("validate whisper command", async () => { }); console.info(res.log); expect(res.success).toBeTruthy(); - - const settings = fs.readJsonSync(path.join(resultDir, "settings.json")); - expect(settings.whisper.service).toBe("azure"); }); test("valid ffmpeg command", async () => { @@ -83,9 +80,6 @@ test("validate echogarden align command", async () => { return window.__ENJOY_APP__.echogarden.check(); }); expect(res).toBeTruthy(); - - const settings = fs.readJsonSync(path.join(resultDir, "settings.json")); - expect(settings.whisper.service).toBe("azure"); }); test("should setup default library path", async () => { diff --git a/enjoy/e2e/renderer.spec.ts b/enjoy/e2e/renderer.spec.ts index eb56df7a..0bc00cae 100644 --- a/enjoy/e2e/renderer.spec.ts +++ b/enjoy/e2e/renderer.spec.ts @@ -61,13 +61,13 @@ test.describe("with login", async () => { settings.user = user; fs.writeJsonSync(path.join(resultDir, "settings.json"), settings); - page.route("**/api/me", (route) => { + await page.route("**/api/me", (route) => { route.fulfill({ json: user, }); }); - page.route("**/api/stories", (route) => { + await page.route("**/api/stories", (route) => { route.fulfill({ json: { stories: [], @@ -199,6 +199,7 @@ test.describe("with login", async () => { // add to library await page.getByTestId("message-start-shadow").click(); + await page.getByTestId("transcribe-continue-button").click(); await page.getByTestId("audio-player").waitFor(); await page .getByTestId("media-player-container") diff --git a/enjoy/package.json b/enjoy/package.json index f5b6f144..143a30d7 100644 --- a/enjoy/package.json +++ b/enjoy/package.json @@ -41,7 +41,7 @@ "@electron-forge/plugin-vite": "^7.4.0", "@electron-forge/publisher-github": "^7.4.0", "@electron/fuses": "^1.8.0", - "@playwright/test": "^1.46.1", + "@playwright/test": "^1.47.0", "@tailwindcss/typography": "^0.5.15", "@types/ahoy.js": "^0.4.2", "@types/autosize": "^4.0.3", @@ -53,7 +53,7 @@ "@types/lodash": "^4.17.7", "@types/mark.js": "^8.11.12", "@types/mustache": "^4.2.5", - "@types/node": "^22.5.2", + "@types/node": "^22.5.4", "@types/prop-types": "^15.7.12", "@types/rails__actioncable": "^6.1.11", "@types/react": "^18.3.5", @@ -61,16 +61,16 @@ "@types/unzipper": "^0.10.10", "@types/validator": "^13.12.1", "@types/wavesurfer.js": "^6.0.12", - "@typescript-eslint/eslint-plugin": "^8.3.0", - "@typescript-eslint/parser": "^8.3.0", + "@typescript-eslint/eslint-plugin": "^8.4.0", + "@typescript-eslint/parser": "^8.4.0", "@vitejs/plugin-react": "^4.3.1", "autoprefixer": "^10.4.20", - "electron": "^32.0.1", + "electron": "^32.0.2", "electron-devtools-installer": "^3.2.0", "electron-playwright-helpers": "^1.7.1", "eslint": "^9.9.1", "eslint-import-resolver-typescript": "^3.6.3", - "eslint-plugin-import": "^2.29.1", + "eslint-plugin-import": "^2.30.0", "flora-colossus": "^2.0.0", "octokit": "^4.0.2", "progress": "^2.0.3", @@ -82,7 +82,7 @@ "ts-node": "^10.9.2", "tslib": "^2.7.0", "typescript": "^5.5.4", - "vite": "^5.4.2", + "vite": "^5.4.3", "vite-plugin-static-copy": "^1.0.6", "zx": "^8.1.5" }, @@ -90,7 +90,7 @@ "@andrkrn/ffprobe-static": "^5.2.0", "@electron-forge/publisher-s3": "^7.4.0", "@hookform/resolvers": "^3.9.0", - "@langchain/community": "^0.2.31", + "@langchain/community": "^0.2.32", "@mozilla/readability": "^0.5.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", @@ -141,7 +141,7 @@ "decamelize-keys": "^2.0.1", "echogarden": "^1.5.0", "electron-context-menu": "^4.0.4", - "electron-log": "^5.1.7", + "electron-log": "^5.2.0", "electron-settings": "^4.0.4", "electron-squirrel-startup": "^1.0.1", "ffmpeg-static": "^5.2.0", @@ -150,19 +150,19 @@ "html-to-text": "^9.0.5", "https-proxy-agent": "^7.0.5", "i18next": "^23.14.0", - "intl-tel-input": "^24.3.6", + "intl-tel-input": "^24.4.0", "js-md5": "^0.8.3", - "langchain": "^0.2.17", + "langchain": "^0.2.18", "lodash": "^4.17.21", - "lru-cache": "^11.0.0", - "lucide-react": "^0.438.0", + "lru-cache": "^11.0.1", + "lucide-react": "^0.439.0", "mark.js": "^8.11.1", "microsoft-cognitiveservices-speech-sdk": "^1.40.0", "mustache": "^4.2.0", "next-themes": "^0.3.0", - "openai": "^4.57.0", + "openai": "^4.58.0", "pitchfinder": "^2.3.2", - "postcss": "^8.4.43", + "postcss": "^8.4.45", "proxy-agent": "^6.4.0", "react": "^18.3.1", "react-activity-calendar": "^2.5.1", @@ -171,7 +171,7 @@ "react-dom": "^18.3.1", "react-frame-component": "^5.2.7", "react-hook-form": "^7.53.0", - "react-hotkeys-hook": "^4.5.0", + "react-hotkeys-hook": "^4.5.1", "react-i18next": "^15.0.1", "react-markdown": "^9.0.1", "react-resizable-panels": "^2.1.2", diff --git a/enjoy/src/api/client.ts b/enjoy/src/api/client.ts index 1eb9f5ce..aa0a8d5b 100644 --- a/enjoy/src/api/client.ts +++ b/enjoy/src/api/client.ts @@ -14,8 +14,17 @@ export class Client { accessToken?: string; logger?: any; locale?: "en" | "zh-CN"; + onError?: (err: any) => void; + onSuccess?: (res: any) => void; }) { - const { baseUrl, accessToken, logger, locale = "en" } = options; + const { + baseUrl, + accessToken, + logger, + locale = "en", + onError, + onSuccess, + } = options; this.baseUrl = baseUrl; this.logger = logger || console; @@ -40,6 +49,10 @@ export class Client { }); this.api.interceptors.response.use( (response) => { + if (onSuccess) { + onSuccess(response); + } + this.logger.debug( response.status, response.config.method.toUpperCase(), @@ -48,14 +61,22 @@ export class Client { return camelcaseKeys(response.data, { deep: true }); }, (err) => { + if (onError) { + onError(err); + } + if (err.response) { this.logger.error( err.response.status, err.response.config.method.toUpperCase(), - err.response.config.baseURL + err.response.config.url + err.response.config.baseURL + err.response.config.url, + err.response.data ); - this.logger.error(err.response.data); - return Promise.reject(new Error(err.response.data)); + + if (err.response.data) { + err.message = err.response.data; + } + return Promise.reject(err); } this.logger.error(err.message); diff --git a/enjoy/src/i18n/en.json b/enjoy/src/i18n/en.json index 38899eed..6c7eff66 100644 --- a/enjoy/src/i18n/en.json +++ b/enjoy/src/i18n/en.json @@ -401,10 +401,10 @@ "sttAiService": "STT AI service", "local": "Local", "localSpeechToTextDescription": "Use local whisper model to transcribe. It is free.", - "azureAi": "Azure AI", - "azureSpeechToTextDescription": "Use Azure AI Speech to transcribe. It is a paid service.", - "cloudflareAi": "Cloudflare AI", - "cloudflareSpeechToTextDescription": "Use Cloudflare AI Worker to transcribe. It is in beta and free for now.", + "enjoyAzure": "Azure AI (by Enjoy)", + "enjoyAzureSpeechToTextDescription": "Use Azure AI Speech to transcribe. It is a paid service from Enjoy.", + "enjoyCloudflare": "Cloudflare AI (by Enjoy)", + "enjoyCloudflareSpeechToTextDescription": "Use Cloudflare AI Worker to transcribe. It is in beta and free for now.", "openaiSpeechToTextDescription": "Use openAI to transcribe using your own key.", "uploadSpeechToTextDescription": "Upload transcript file or input transcript text to align.", "checkingWhisper": "Checking whisper status", @@ -781,5 +781,7 @@ "selectDir": "Select Folder", "selectFile": "Select File", "importMdictFile": "Import the original dictionary file", - "mdictFileTip": "Directly import .mdx .mdd format files (.mdx files are required, .mdd files are optional and can have multiple), but there may be problems with style and usability." + "mdictFileTip": "Directly import .mdx .mdd format files (.mdx files are required, .mdd files are optional and can have multiple), but there may be problems with style and usability.", + "authorizationExpired": "Your authorization has expired. Please login again.", + "selectUser": "Select user" } diff --git a/enjoy/src/i18n/zh-CN.json b/enjoy/src/i18n/zh-CN.json index 6cc0d2b2..c120cfca 100644 --- a/enjoy/src/i18n/zh-CN.json +++ b/enjoy/src/i18n/zh-CN.json @@ -401,11 +401,11 @@ "sttAiService": "语音转文本服务", "local": "本地", "localSpeechToTextDescription": "使用本地 whisper 模型进行语音转文本,不会产生费用", - "azureAi": "Azure AI", - "azureSpeechToTextDescription": "使用 Azure AI Speech 进行语音转文本,收费服务", - "cloudflareAi": "Cloudflare AI", - "cloudflareSpeechToTextDescription": "使用 Cloudflare AI 进行语音转文本,目前免费", - "openaiSpeechToTextDescription": "使用 OpenAI 进行语音转文本(需要 API 密钥)", + "enjoyAzure": "Azure (Enjoy 提供)", + "enjoyAzureSpeechToTextDescription": "使用 Azure AI Speech 进行语音转文本,Enjoy 提供的收费服务", + "enjoyCloudflare": "Cloudflare AI (Enjoy 提供)", + "enjoyCloudflareSpeechToTextDescription": "使用 Cloudflare AI Worker 进行语音转文本,Enjoy 提供的免费服务,有一定的使用限额", + "openaiSpeechToTextDescription": "使用 OpenAI 进行语音转文本(需要自备 API 密钥)", "uploadSpeechToTextDescription": "上传字幕文件或者输入文本进行字幕对齐", "checkingWhisper": "正在检查 Whisper", "pleaseDownloadWhisperModelFirst": "请先下载 Whisper 模型", @@ -781,5 +781,7 @@ "selectDir": "选择文件夹", "selectFile": "选择文件", "importMdictFile": "导入原词典文件", - "mdictFileTip": "直接导入 .mdx .mdd 格式的文件 (.mdx 文件是必须的,.mdd 文件是可选的且可以有多个),不过样式和可用性可能存在问题。" + "mdictFileTip": "直接导入 .mdx .mdd 格式的文件 (.mdx 文件是必须的,.mdd 文件是可选的且可以有多个),不过样式和可用性可能存在问题。", + "authorizationExpired": "您的登录授权已过期,请重新登录。", + "selectUser": "选择用户" } diff --git a/enjoy/src/main.ts b/enjoy/src/main.ts index 5e86c98f..ec0deb1c 100644 --- a/enjoy/src/main.ts +++ b/enjoy/src/main.ts @@ -2,7 +2,6 @@ import { app, BrowserWindow, protocol, net } from "electron"; import path from "path"; import fs from "fs-extra"; import settings from "@main/settings"; -import "@main/i18n"; import log from "@main/logger"; import mainWindow from "@main/window"; import ElectronSquirrelStartup from "electron-squirrel-startup"; diff --git a/enjoy/src/main/db/handlers/audios-handler.ts b/enjoy/src/main/db/handlers/audios-handler.ts index 6bab5d86..1f498def 100644 --- a/enjoy/src/main/db/handlers/audios-handler.ts +++ b/enjoy/src/main/db/handlers/audios-handler.ts @@ -188,6 +188,17 @@ class AudiosHandler { ipcMain.handle("audios-crop", this.crop); ipcMain.handle("audios-clean-up", this.cleanUp); } + + unregister() { + ipcMain.removeHandler("audios-find-all"); + ipcMain.removeHandler("audios-find-one"); + ipcMain.removeHandler("audios-create"); + ipcMain.removeHandler("audios-update"); + ipcMain.removeHandler("audios-destroy"); + ipcMain.removeHandler("audios-upload"); + ipcMain.removeHandler("audios-crop"); + ipcMain.removeHandler("audios-clean-up"); + } } export const audiosHandler = new AudiosHandler(); diff --git a/enjoy/src/main/db/handlers/cache-objects-handler.ts b/enjoy/src/main/db/handlers/cache-objects-handler.ts index 411ebd5c..b1d107ee 100644 --- a/enjoy/src/main/db/handlers/cache-objects-handler.ts +++ b/enjoy/src/main/db/handlers/cache-objects-handler.ts @@ -82,6 +82,14 @@ class CacheObjectsHandler { ipcMain.handle("cache-objects-clear", this.clear); ipcMain.handle("cache-objects-write-file", this.writeFile); } + + unregister() { + ipcMain.removeHandler("cache-objects-get"); + ipcMain.removeHandler("cache-objects-set"); + ipcMain.removeHandler("cache-objects-delete"); + ipcMain.removeHandler("cache-objects-clear"); + ipcMain.removeHandler("cache-objects-write-file"); + } } export const cacheObjectsHandler = new CacheObjectsHandler(); diff --git a/enjoy/src/main/db/handlers/chat-agents-handler.ts b/enjoy/src/main/db/handlers/chat-agents-handler.ts index a4bf3be8..2fd9d445 100644 --- a/enjoy/src/main/db/handlers/chat-agents-handler.ts +++ b/enjoy/src/main/db/handlers/chat-agents-handler.ts @@ -81,6 +81,14 @@ class ChatAgentsHandler { ipcMain.handle("chat-agents-update", this.update); ipcMain.handle("chat-agents-destroy", this.destroy); } + + unregister() { + ipcMain.removeHandler("chat-agents-find-all"); + ipcMain.removeHandler("chat-agents-find-one"); + ipcMain.removeHandler("chat-agents-create"); + ipcMain.removeHandler("chat-agents-update"); + ipcMain.removeHandler("chat-agents-destroy"); + } } export const chatAgentsHandler = new ChatAgentsHandler(); diff --git a/enjoy/src/main/db/handlers/chat-members-handler.ts b/enjoy/src/main/db/handlers/chat-members-handler.ts index e9febb3c..bc2907e3 100644 --- a/enjoy/src/main/db/handlers/chat-members-handler.ts +++ b/enjoy/src/main/db/handlers/chat-members-handler.ts @@ -13,12 +13,15 @@ class ChatMembersHandler { private async findAll( _event: IpcMainEvent, options: FindOptions> & { query?: string } - ) { - } + ) {} register() { ipcMain.handle("chat-members-find-all", this.findAll); } + + unregister() { + ipcMain.removeHandler("chat-members-find-all"); + } } -export const chatMembersHandler = new ChatMembersHandler(); \ No newline at end of file +export const chatMembersHandler = new ChatMembersHandler(); diff --git a/enjoy/src/main/db/handlers/chat-messages-handler.ts b/enjoy/src/main/db/handlers/chat-messages-handler.ts index 07ae1e4c..cde15f10 100644 --- a/enjoy/src/main/db/handlers/chat-messages-handler.ts +++ b/enjoy/src/main/db/handlers/chat-messages-handler.ts @@ -141,6 +141,14 @@ class ChatMessagesHandler { ipcMain.handle("chat-messages-update", this.update); ipcMain.handle("chat-messages-destroy", this.destroy); } + + unregister() { + ipcMain.removeHandler("chat-messages-find-all"); + ipcMain.removeHandler("chat-messages-find-one"); + ipcMain.removeHandler("chat-messages-create"); + ipcMain.removeHandler("chat-messages-update"); + ipcMain.removeHandler("chat-messages-destroy"); + } } export const chatMessagesHandler = new ChatMessagesHandler(); diff --git a/enjoy/src/main/db/handlers/chats-handler.ts b/enjoy/src/main/db/handlers/chats-handler.ts index 10caf7bd..05631891 100644 --- a/enjoy/src/main/db/handlers/chats-handler.ts +++ b/enjoy/src/main/db/handlers/chats-handler.ts @@ -1,10 +1,10 @@ import { ipcMain, IpcMainEvent } from "electron"; -import { Chat, ChatAgent, ChatMember } from "@main/db/models"; +import { Chat, ChatAgent, ChatMember, UserSetting } from "@main/db/models"; import { FindOptions, WhereOptions, Attributes, Op } from "sequelize"; import log from "@main/logger"; import { t } from "i18next"; import db from "@main/db"; -import settings from "@/main/settings"; +import { UserSettingKeyEnum } from "@/types/enums"; const logger = log.scope("db/handlers/chats-handler"); @@ -73,7 +73,9 @@ class ChatsHandler { const transaction = await db.connection.transaction(); if (!chatData.config?.sttEngine) { - chatData.config.sttEngine = settings.whisperConfig().service; + chatData.config.sttEngine = (await UserSetting.get( + UserSettingKeyEnum.STT_ENGINE + )) as string; } const chat = await Chat.create(chatData, { transaction, @@ -195,6 +197,14 @@ class ChatsHandler { ipcMain.handle("chats-update", this.update); ipcMain.handle("chats-destroy", this.destroy); } + + unregister() { + ipcMain.removeHandler("chats-find-all"); + ipcMain.removeHandler("chats-find-one"); + ipcMain.removeHandler("chats-create"); + ipcMain.removeHandler("chats-update"); + ipcMain.removeHandler("chats-destroy"); + } } export const chatsHandler = new ChatsHandler(); diff --git a/enjoy/src/main/db/handlers/conversations-handler.ts b/enjoy/src/main/db/handlers/conversations-handler.ts index f3a25382..a2b0ba20 100644 --- a/enjoy/src/main/db/handlers/conversations-handler.ts +++ b/enjoy/src/main/db/handlers/conversations-handler.ts @@ -120,6 +120,14 @@ class ConversationsHandler { ipcMain.handle("conversations-update", this.update); ipcMain.handle("conversations-destroy", this.destroy); } + + unregister() { + ipcMain.removeHandler("conversations-find-all"); + ipcMain.removeHandler("conversations-find-one"); + ipcMain.removeHandler("conversations-create"); + ipcMain.removeHandler("conversations-update"); + ipcMain.removeHandler("conversations-destroy"); + } } export const conversationsHandler = new ConversationsHandler(); diff --git a/enjoy/src/main/db/handlers/index.ts b/enjoy/src/main/db/handlers/index.ts index afc77bc1..57887a81 100644 --- a/enjoy/src/main/db/handlers/index.ts +++ b/enjoy/src/main/db/handlers/index.ts @@ -12,4 +12,5 @@ export * from "./recordings-handler"; export * from "./speeches-handler"; export * from "./segments-handler"; export * from "./transcriptions-handler"; +export * from "./user-settings-handler"; export * from "./videos-handler"; diff --git a/enjoy/src/main/db/handlers/messages-handler.ts b/enjoy/src/main/db/handlers/messages-handler.ts index 101e49d3..99dfb583 100644 --- a/enjoy/src/main/db/handlers/messages-handler.ts +++ b/enjoy/src/main/db/handlers/messages-handler.ts @@ -177,6 +177,16 @@ class MessagesHandler { ipcMain.handle("messages-destroy", this.destroy); ipcMain.handle("messages-create-speech", this.createSpeech); } + + unregister() { + ipcMain.removeHandler("messages-find-all"); + ipcMain.removeHandler("messages-find-one"); + ipcMain.removeHandler("messages-create"); + ipcMain.removeHandler("messages-create-in-batch"); + ipcMain.removeHandler("messages-update"); + ipcMain.removeHandler("messages-destroy"); + ipcMain.removeHandler("messages-create-speech"); + } } export const messagesHandler = new MessagesHandler(); diff --git a/enjoy/src/main/db/handlers/notes-handler.ts b/enjoy/src/main/db/handlers/notes-handler.ts index 5e53203e..a9e60dcf 100644 --- a/enjoy/src/main/db/handlers/notes-handler.ts +++ b/enjoy/src/main/db/handlers/notes-handler.ts @@ -165,6 +165,17 @@ class NotesHandler { ipcMain.handle("notes-create", this.create); ipcMain.handle("notes-sync", this.sync); } + + unregister() { + ipcMain.removeHandler("notes-group-by-target"); + ipcMain.removeHandler("notes-group-by-segment"); + ipcMain.removeHandler("notes-find-all"); + ipcMain.removeHandler("notes-find"); + ipcMain.removeHandler("notes-update"); + ipcMain.removeHandler("notes-delete"); + ipcMain.removeHandler("notes-create"); + ipcMain.removeHandler("notes-sync"); + } } export const notesHandler = new NotesHandler(); diff --git a/enjoy/src/main/db/handlers/pronunciation-assessments-handler.ts b/enjoy/src/main/db/handlers/pronunciation-assessments-handler.ts index 0e426fa9..7342f4f9 100644 --- a/enjoy/src/main/db/handlers/pronunciation-assessments-handler.ts +++ b/enjoy/src/main/db/handlers/pronunciation-assessments-handler.ts @@ -102,6 +102,14 @@ class PronunciationAssessmentsHandler { ipcMain.handle("pronunciation-assessments-update", this.update); ipcMain.handle("pronunciation-assessments-destroy", this.destroy); } + + unregister() { + ipcMain.removeHandler("pronunciation-assessments-find-all"); + ipcMain.removeHandler("pronunciation-assessments-find-one"); + ipcMain.removeHandler("pronunciation-assessments-create"); + ipcMain.removeHandler("pronunciation-assessments-update"); + ipcMain.removeHandler("pronunciation-assessments-destroy"); + } } export const pronunciationAssessmentsHandler = diff --git a/enjoy/src/main/db/handlers/recordings-handler.ts b/enjoy/src/main/db/handlers/recordings-handler.ts index 0cdfac75..f98127fb 100644 --- a/enjoy/src/main/db/handlers/recordings-handler.ts +++ b/enjoy/src/main/db/handlers/recordings-handler.ts @@ -342,6 +342,20 @@ class RecordingsHandler { ipcMain.handle("recordings-group-by-target", this.groupByTarget); ipcMain.handle("recordings-group-by-segment", this.groupBySegment); } + + unregister() { + ipcMain.removeHandler("recordings-find-all"); + ipcMain.removeHandler("recordings-find-one"); + ipcMain.removeHandler("recordings-sync"); + ipcMain.removeHandler("recordings-sync-all"); + ipcMain.removeHandler("recordings-create"); + ipcMain.removeHandler("recordings-destroy"); + ipcMain.removeHandler("recordings-upload"); + ipcMain.removeHandler("recordings-stats"); + ipcMain.removeHandler("recordings-group-by-date"); + ipcMain.removeHandler("recordings-group-by-target"); + ipcMain.removeHandler("recordings-group-by-segment"); + } } export const recordingsHandler = new RecordingsHandler(); diff --git a/enjoy/src/main/db/handlers/segments-handler.ts b/enjoy/src/main/db/handlers/segments-handler.ts index 178a5547..d1439e61 100644 --- a/enjoy/src/main/db/handlers/segments-handler.ts +++ b/enjoy/src/main/db/handlers/segments-handler.ts @@ -57,6 +57,13 @@ class SegmentsHandler { ipcMain.handle("segments-find-all", this.findAll); ipcMain.handle("segments-sync", this.sync); } + + unregister() { + ipcMain.removeHandler("segments-create"); + ipcMain.removeHandler("segments-find"); + ipcMain.removeHandler("segments-find-all"); + ipcMain.removeHandler("segments-sync"); + } } export const segmentsHandler = new SegmentsHandler(); diff --git a/enjoy/src/main/db/handlers/speeches-handler.ts b/enjoy/src/main/db/handlers/speeches-handler.ts index 4a8c76ba..9e73a7b0 100644 --- a/enjoy/src/main/db/handlers/speeches-handler.ts +++ b/enjoy/src/main/db/handlers/speeches-handler.ts @@ -59,6 +59,11 @@ class SpeechesHandler { ipcMain.handle("speeches-find-one", this.findOne); ipcMain.handle("speeches-create", this.create); } + + unregister() { + ipcMain.removeHandler("speeches-find-one"); + ipcMain.removeHandler("speeches-create"); + } } export const speechesHandler = new SpeechesHandler(); diff --git a/enjoy/src/main/db/handlers/transcriptions-handler.ts b/enjoy/src/main/db/handlers/transcriptions-handler.ts index c1d7fd00..84cc9cde 100644 --- a/enjoy/src/main/db/handlers/transcriptions-handler.ts +++ b/enjoy/src/main/db/handlers/transcriptions-handler.ts @@ -60,6 +60,11 @@ class TranscriptionsHandler { ipcMain.handle("transcriptions-find-or-create", this.findOrCreate); ipcMain.handle("transcriptions-update", this.update); } + + unregister() { + ipcMain.removeHandler("transcriptions-find-or-create"); + ipcMain.removeHandler("transcriptions-update"); + } } export const transcriptionsHandler = new TranscriptionsHandler(); diff --git a/enjoy/src/main/db/handlers/user-settings-handler.ts b/enjoy/src/main/db/handlers/user-settings-handler.ts new file mode 100644 index 00000000..1c8bae24 --- /dev/null +++ b/enjoy/src/main/db/handlers/user-settings-handler.ts @@ -0,0 +1,79 @@ +import { ipcMain, IpcMainEvent } from "electron"; +import { UserSetting } from "@main/db/models"; +import db from "@main/db"; +import { UserSettingKeyEnum } from "@/types/enums"; + +class UserSettingsHandler { + private async get(event: IpcMainEvent, key: UserSettingKeyEnum) { + return UserSetting.get(key) + .then((value) => { + return value; + }) + .catch((err) => { + event.sender.send("on-notification", { + type: "error", + message: err.message, + }); + }); + } + + private async set( + event: IpcMainEvent, + key: UserSettingKeyEnum, + value: string | object + ) { + return UserSetting.set(key, value) + .then(() => { + return; + }) + .catch((err) => { + event.sender.send("on-notification", { + type: "error", + message: err.message, + }); + }); + } + + private async delete(event: IpcMainEvent, key: UserSettingKeyEnum) { + return UserSetting.destroy({ where: { key } }) + .then(() => { + return; + }) + .catch((err) => { + event.sender.send("on-notification", { + type: "error", + message: err.message, + }); + }); + } + + private async clear(event: IpcMainEvent) { + return UserSetting.destroy({ where: {} }) + .then(() => { + db.connection.query("VACUUM"); + return; + }) + .catch((err) => { + event.sender.send("on-notification", { + type: "error", + message: err.message, + }); + }); + } + + register() { + ipcMain.handle("user-settings-get", this.get); + ipcMain.handle("user-settings-set", this.set); + ipcMain.handle("user-settings-delete", this.delete); + ipcMain.handle("user-settings-clear", this.clear); + } + + unregister() { + ipcMain.removeHandler("user-settings-get"); + ipcMain.removeHandler("user-settings-set"); + ipcMain.removeHandler("user-settings-delete"); + ipcMain.removeHandler("user-settings-clear"); + } +} + +export const userSettingsHandler = new UserSettingsHandler(); diff --git a/enjoy/src/main/db/handlers/videos-handler.ts b/enjoy/src/main/db/handlers/videos-handler.ts index e66105ad..e0e52873 100644 --- a/enjoy/src/main/db/handlers/videos-handler.ts +++ b/enjoy/src/main/db/handlers/videos-handler.ts @@ -178,6 +178,17 @@ class VideosHandler { ipcMain.handle("videos-crop", this.crop); ipcMain.handle("videos-clean-up", this.cleanUp); } + + unregister() { + ipcMain.removeHandler("videos-find-all"); + ipcMain.removeHandler("videos-find-one"); + ipcMain.removeHandler("videos-create"); + ipcMain.removeHandler("videos-update"); + ipcMain.removeHandler("videos-destroy"); + ipcMain.removeHandler("videos-upload"); + ipcMain.removeHandler("videos-crop"); + ipcMain.removeHandler("videos-clean-up"); + } } export const videosHandler = new VideosHandler(); diff --git a/enjoy/src/main/db/index.ts b/enjoy/src/main/db/index.ts index c1f9925b..0b785996 100644 --- a/enjoy/src/main/db/index.ts +++ b/enjoy/src/main/db/index.ts @@ -6,6 +6,10 @@ import { Audio, Recording, CacheObject, + Chat, + ChatAgent, + ChatMember, + ChatMessage, Conversation, Message, Note, @@ -14,14 +18,15 @@ import { Speech, Transcription, Video, - Chat, - ChatAgent, - ChatMember, - ChatMessage, + UserSetting, } from "./models"; import { audiosHandler, cacheObjectsHandler, + chatAgentsHandler, + chatMembersHandler, + chatMessagesHandler, + chatsHandler, conversationsHandler, messagesHandler, notesHandler, @@ -31,14 +36,13 @@ import { speechesHandler, transcriptionsHandler, videosHandler, - chatAgentsHandler, - chatMembersHandler, - chatMessagesHandler, - chatsHandler, + userSettingsHandler, } from "./handlers"; import os from "os"; import path from "path"; import url from "url"; +import { i18n } from "@main/i18n"; +import { UserSettingKeyEnum } from "@/types/enums"; const __filename = url.fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -46,6 +50,7 @@ const __dirname = path.dirname(__filename); const db = { connection: null as Sequelize | null, connect: async () => {}, + disconnect: async () => {}, registerIpcHandlers: () => {}, }; @@ -53,12 +58,21 @@ db.connect = async () => { if (db.connection) { return; } + const dbPath = settings.dbPath(); + if (!dbPath) { + throw new Error("Db path is not ready"); + } + const sequelize = new Sequelize({ dialect: "sqlite", - storage: settings.dbPath(), + storage: dbPath, models: [ Audio, CacheObject, + Chat, + ChatAgent, + ChatMember, + ChatMessage, Conversation, Message, Note, @@ -67,11 +81,8 @@ db.connect = async () => { Segment, Speech, Transcription, + UserSetting, Video, - Chat, - ChatAgent, - ChatMember, - ChatMessage, ], }); @@ -143,12 +154,25 @@ db.connect = async () => { }); }); + // migrate settings + await UserSetting.migrateFromSettings(); + + // initialize i18n + const language = (await UserSetting.get( + UserSettingKeyEnum.LANGUAGE + )) as string; + i18n(language); + // vacuum the database await sequelize.query("VACUUM"); // register handlers audiosHandler.register(); cacheObjectsHandler.register(); + chatAgentsHandler.register(); + chatMembersHandler.register(); + chatMessagesHandler.register(); + chatsHandler.register(); conversationsHandler.register(); messagesHandler.register(); notesHandler.register(); @@ -157,28 +181,53 @@ db.connect = async () => { segmentsHandler.register(); speechesHandler.register(); transcriptionsHandler.register(); + userSettingsHandler.register(); videosHandler.register(); - chatAgentsHandler.register(); - chatMembersHandler.register(); - chatMessagesHandler.register(); - chatsHandler.register(); +}; + +db.disconnect = async () => { + // unregister handlers + audiosHandler.unregister(); + cacheObjectsHandler.unregister(); + chatAgentsHandler.unregister(); + chatMembersHandler.unregister(); + chatMessagesHandler.unregister(); + chatsHandler.unregister(); + conversationsHandler.unregister(); + messagesHandler.unregister(); + notesHandler.unregister(); + pronunciationAssessmentsHandler.unregister(); + recordingsHandler.unregister(); + segmentsHandler.unregister(); + speechesHandler.unregister(); + transcriptionsHandler.unregister(); + userSettingsHandler.unregister(); + videosHandler.unregister(); + + await db.connection?.close(); + db.connection = null; }; db.registerIpcHandlers = () => { - ipcMain.handle("db-init", async () => { - return db - .connect() - .then(() => { - return { - state: "connected", - }; - }) - .catch((err) => { - return { - state: "error", - error: err.message, - }; - }); + ipcMain.handle("db-connect", async () => { + try { + await db.connect(); + return { + state: "connected", + path: settings.dbPath(), + error: null, + }; + } catch (err) { + return { + state: "error", + error: err.message, + path: settings.dbPath(), + }; + } + }); + + ipcMain.handle("db-disconnect", async () => { + db.disconnect(); }); }; diff --git a/enjoy/src/main/db/migrations/1725411577564-create-user-setting.js b/enjoy/src/main/db/migrations/1725411577564-create-user-setting.js new file mode 100644 index 00000000..e8f3bb66 --- /dev/null +++ b/enjoy/src/main/db/migrations/1725411577564-create-user-setting.js @@ -0,0 +1,45 @@ +import { DataTypes } from "sequelize"; + +async function up({ context: queryInterface }) { + queryInterface.createTable( + "user_settings", + { + id: { + type: DataTypes.UUID, + defaultValue: DataTypes.UUIDV4, + primaryKey: true, + allowNull: false, + }, + key: { + type: DataTypes.STRING, + allowNull: false, + }, + value: { + type: DataTypes.TEXT, + allowNull: false, + }, + created_at: { + type: DataTypes.DATE, + allowNull: false, + }, + updated_at: { + type: DataTypes.DATE, + allowNull: false, + }, + }, + { + indexes: [ + { + unique: true, + fields: ["key"], + }, + ], + } + ); +} + +async function down({ context: queryInterface }) { + queryInterface.dropTable("user_settings"); +} + +export { up, down }; diff --git a/enjoy/src/main/db/models/index.ts b/enjoy/src/main/db/models/index.ts index 1b3ccbb5..e38189b3 100644 --- a/enjoy/src/main/db/models/index.ts +++ b/enjoy/src/main/db/models/index.ts @@ -11,5 +11,6 @@ export * from "./pronunciation-assessment"; export * from "./recording"; export * from "./segment"; export * from "./speech"; +export * from "./user-setting"; export * from "./transcription"; export * from "./video"; diff --git a/enjoy/src/main/db/models/user-setting.ts b/enjoy/src/main/db/models/user-setting.ts new file mode 100644 index 00000000..fb451f0f --- /dev/null +++ b/enjoy/src/main/db/models/user-setting.ts @@ -0,0 +1,176 @@ +import { + Table, + Column, + Default, + IsUUID, + Model, + DataType, + AllowNull, +} from "sequelize-typescript"; +import log from "@main/logger"; +import settings from "@main/settings"; +import * as i18n from "i18next"; +import { SttEngineOptionEnum, UserSettingKeyEnum } from "@/types/enums"; + +const logger = log.scope("db/userSetting"); + +@Table({ + modelName: "UserSetting", + tableName: "user_settings", + underscored: true, + timestamps: true, +}) +export class UserSetting extends Model { + @IsUUID(4) + @Default(DataType.UUIDV4) + @Column({ primaryKey: true, type: DataType.UUID }) + id: string; + + @Column(DataType.STRING) + key: string; + + @AllowNull(false) + @Column(DataType.TEXT) + value: string; + + static async get( + key: UserSettingKeyEnum + ): Promise { + const setting = await UserSetting.findOne({ where: { key } }); + if (!setting) return null; + + try { + return JSON.parse(setting.value); + } catch { + return setting.value; + } + } + + static async set( + key: UserSettingKeyEnum, + value: object | string + ): Promise { + const setting = await UserSetting.findOne({ where: { key } }); + + if (typeof value === "object") { + value = JSON.stringify(value); + } + + if (setting) { + await setting.update({ value }); + } else { + await UserSetting.create({ key, value }); + } + + // update i18n + if (key === UserSettingKeyEnum.LANGUAGE) { + i18n.changeLanguage(value); + } + } + + static async migrateFromSettings(): Promise { + // hotkeys + const hotkeys = await UserSetting.get(UserSettingKeyEnum.HOTKEYS); + const prevHotkeys = await settings.get("defaultHotkeys"); + if (prevHotkeys && !hotkeys) { + UserSetting.set(UserSettingKeyEnum.HOTKEYS, prevHotkeys as object); + } + + // GPT Engine + const gptEngine = await UserSetting.get(UserSettingKeyEnum.GPT_ENGINE); + const prevGptEngine = await settings.get("engine.gpt"); + if (prevGptEngine && !gptEngine) { + UserSetting.set(UserSettingKeyEnum.GPT_ENGINE, prevGptEngine as object); + } + + // OpenAI API Key + const openai = await UserSetting.get(UserSettingKeyEnum.OPENAI); + const prevOpenai = await settings.get("openai"); + if (prevOpenai && !openai) { + UserSetting.set(UserSettingKeyEnum.OPENAI, prevOpenai as object); + } + + // Language + const language = await UserSetting.get(UserSettingKeyEnum.LANGUAGE); + const prevLanguage = await settings.get("language"); + if (prevLanguage && !language) { + UserSetting.set(UserSettingKeyEnum.LANGUAGE, prevLanguage as string); + } + + // Native Language + const nativeLanguage = await UserSetting.get( + UserSettingKeyEnum.NATIVE_LANGUAGE + ); + const prevNativeLanguage = await settings.get("nativeLanguage"); + if (prevNativeLanguage && !nativeLanguage) { + UserSetting.set( + UserSettingKeyEnum.NATIVE_LANGUAGE, + prevNativeLanguage as string + ); + } + + // Learning Language + const learningLanguage = await UserSetting.get( + UserSettingKeyEnum.LEARNING_LANGUAGE + ); + const prevLearningLanguage = await settings.get("learningLanguage"); + if (prevLearningLanguage && !learningLanguage) { + UserSetting.set( + UserSettingKeyEnum.LEARNING_LANGUAGE, + prevLearningLanguage as string + ); + } + + // STT Engine + const sttEngine = await UserSetting.get(UserSettingKeyEnum.STT_ENGINE); + const prevSttEngine = await settings.get("whisper.service"); + if (prevSttEngine && !sttEngine) { + switch (prevSttEngine) { + case "azure": + UserSetting.set( + UserSettingKeyEnum.STT_ENGINE, + SttEngineOptionEnum.ENJOY_AZURE + ); + break; + case "cloudflare": + UserSetting.set( + UserSettingKeyEnum.STT_ENGINE, + SttEngineOptionEnum.ENJOY_CLOUDFLARE + ); + break; + case "openai": + UserSetting.set( + UserSettingKeyEnum.STT_ENGINE, + SttEngineOptionEnum.OPENAI + ); + break; + default: + break; + } + } + + // Whisper + const whisper = await UserSetting.get(UserSettingKeyEnum.WHISPER); + const prevWhisper = await settings.get("whisper.model"); + if (prevWhisper && !whisper) { + UserSetting.set(UserSettingKeyEnum.WHISPER, prevWhisper as string); + } + + // Profile + const profile = await UserSetting.get(UserSettingKeyEnum.PROFILE); + const prevProfile = (await settings.get("user")) as UserType; + if (prevProfile && !profile) { + UserSetting.set(UserSettingKeyEnum.PROFILE, prevProfile as UserType); + } + + // Recorder Config + const recorderConfig = await UserSetting.get(UserSettingKeyEnum.RECORDER); + const prevRecorderConfig = await settings.get("recorderConfig"); + if (prevRecorderConfig && !recorderConfig) { + UserSetting.set( + UserSettingKeyEnum.RECORDER, + prevRecorderConfig as string + ); + } + } +} diff --git a/enjoy/src/main/i18n.ts b/enjoy/src/main/i18n.ts index 64d5d655..603aded3 100644 --- a/enjoy/src/main/i18n.ts +++ b/enjoy/src/main/i18n.ts @@ -1,7 +1,6 @@ -import * as i18n from "i18next"; +import * as i18next from "i18next"; import en from "@/i18n/en.json"; import zh_CN from "@/i18n/zh-CN.json"; -import settings from "@main/settings"; const resources = { en: { @@ -12,14 +11,14 @@ const resources = { }, }; -i18n.init({ - resources, - lng: settings.language(), - supportedLngs: ["en", "zh-CN"], - fallbackLng: "en", - interpolation: { - escapeValue: false, // react already safes from xss - }, -}); - -export default i18n; +export const i18n = (language: string) => { + i18next.init({ + resources, + lng: language, + supportedLngs: ["en", "zh-CN"], + fallbackLng: "en", + interpolation: { + escapeValue: false, // react already safes from xss + }, + }); +}; diff --git a/enjoy/src/main/settings.ts b/enjoy/src/main/settings.ts index 808a68b9..3217ac41 100644 --- a/enjoy/src/main/settings.ts +++ b/enjoy/src/main/settings.ts @@ -3,7 +3,7 @@ import { LIBRARY_PATH_SUFFIX, DATABASE_NAME, WEB_API_URL } from "@/constants"; import { ipcMain, app } from "electron"; import path from "path"; import fs from "fs-extra"; -import * as i18n from "i18next"; +import { AppSettingsKeyEnum } from "@/types/enums"; if (process.env.SETTINGS_PATH) { settings.configure({ @@ -12,35 +12,23 @@ if (process.env.SETTINGS_PATH) { }); } -const language = () => { - const _language = settings.getSync("language"); - - if (!_language || typeof _language !== "string") { - settings.setSync("language", "en"); - } - - return settings.getSync("language") as string; -}; - -const switchLanguage = (language: string) => { - settings.setSync("language", language); - i18n.changeLanguage(language); -}; - const libraryPath = () => { const _library = settings.getSync("library"); if (!_library || typeof _library !== "string") { settings.setSync( - "library", + AppSettingsKeyEnum.LIBRARY, process.env.LIBRARY_PATH || path.join(app.getPath("documents"), LIBRARY_PATH_SUFFIX) ); } else if (path.parse(_library).base !== LIBRARY_PATH_SUFFIX) { - settings.setSync("library", path.join(_library, LIBRARY_PATH_SUFFIX)); + settings.setSync( + AppSettingsKeyEnum.LIBRARY, + path.join(_library, LIBRARY_PATH_SUFFIX) + ); } - const library = settings.getSync("library") as string; + const library = settings.getSync(AppSettingsKeyEnum.LIBRARY) as string; fs.ensureDirSync(library); return library; @@ -54,165 +42,84 @@ const cachePath = () => { }; const dbPath = () => { + if (!userDataPath()) return null; + const dbName = app.isPackaged ? `${DATABASE_NAME}.sqlite` : `${DATABASE_NAME}_dev.sqlite`; return path.join(userDataPath(), dbName); }; -const whisperConfig = (): WhisperConfigType => { - const model = settings.getSync("whisper.model") as string; - - let service = settings.getSync( - "whisper.service" - ) as WhisperConfigType["service"]; - - if (!service) { - settings.setSync("whisper.service", "azure"); - service = "azure"; - } - - return { - service, - availableModels: settings.getSync( - "whisper.availableModels" - ) as WhisperConfigType["availableModels"], - modelsPath: settings.getSync("whisper.modelsPath") as string, - model, - }; -}; - const userDataPath = () => { - const userData = path.join( - libraryPath(), - settings.getSync("user.id").toString() - ); + const userId = settings.getSync("user.id"); + if (!userId) return null; + + const userData = path.join(libraryPath(), userId.toString()); fs.ensureDirSync(userData); return userData; }; const apiUrl = () => { - const url: string = settings.getSync("apiUrl") as string; + const url: string = settings.getSync(AppSettingsKeyEnum.API_URL) as string; return process.env.WEB_API_URL || url || WEB_API_URL; }; +// scan library directory and get all user data directories +// the name of user data directory is the user id, and they are all numbers and 8 digits +const sessions = () => { + const library = libraryPath(); + const sessions = fs.readdirSync(library).filter((dir) => { + return dir.match(/^\d{8}$/); + }); + return sessions.map((id) => ({ id: parseInt(id), name: id })); +}; + export default { registerIpcHandlers: () => { - ipcMain.handle("settings-get", (_event, key) => { - return settings.getSync(key); - }); - - ipcMain.handle("settings-set", (_event, key, value) => { - settings.setSync(key, value); - }); - - ipcMain.handle("settings-get-library", (_event) => { + ipcMain.handle("app-settings-get-library", (_event) => { libraryPath(); - return settings.getSync("library"); + return settings.getSync(AppSettingsKeyEnum.LIBRARY); }); - ipcMain.handle("settings-set-library", (_event, library) => { + ipcMain.handle("app-settings-set-library", (_event, library) => { if (path.parse(library).base === LIBRARY_PATH_SUFFIX) { - settings.setSync("library", library); + settings.setSync(AppSettingsKeyEnum.LIBRARY, library); } else { const dir = path.join(library, LIBRARY_PATH_SUFFIX); fs.ensureDirSync(dir); - settings.setSync("library", dir); + settings.setSync(AppSettingsKeyEnum.LIBRARY, dir); } }); - ipcMain.handle("settings-get-user", (_event) => { - return settings.getSync("user"); + ipcMain.handle("app-settings-get-user", (_event) => { + return settings.getSync(AppSettingsKeyEnum.USER); }); - ipcMain.handle("settings-set-user", (_event, user) => { - settings.setSync("user", user); + ipcMain.handle("app-settings-set-user", (_event, user) => { + settings.setSync(AppSettingsKeyEnum.USER, user); }); - ipcMain.handle("settings-get-whisper-model", (_event) => { - return settings.getSync("whisper.model"); - }); - - ipcMain.handle("settings-set-whisper-model", (_event, model) => { - settings.setSync("whisper.model", model); - }); - - ipcMain.handle("settings-get-user-data-path", (_event) => { + ipcMain.handle("app-settings-get-user-data-path", (_event) => { return userDataPath(); }); - ipcMain.handle("settings-get-llm", (_event, provider) => { - return settings.getSync(provider); + ipcMain.handle("app-settings-get-api-url", (_event) => { + return settings.getSync(AppSettingsKeyEnum.API_URL); }); - ipcMain.handle("settings-set-llm", (_event, provider, config) => { - return settings.setSync(provider, config); + ipcMain.handle("app-settings-set-api-url", (_event, url) => { + settings.setSync(AppSettingsKeyEnum.API_URL, url); }); - ipcMain.handle("settings-get-language", (_event) => { - return language(); - }); - - ipcMain.handle("settings-switch-language", (_event, language) => { - switchLanguage(language); - }); - - ipcMain.handle("settings-get-default-engine", (_event) => { - return settings.getSync("defaultEngine"); - }); - - ipcMain.handle("settings-set-default-engine", (_event, engine) => { - return settings.setSync("defaultEngine", engine); - }); - - ipcMain.handle("settings-get-gpt-engine", (_event) => { - return settings.getSync("engine.gpt"); - }); - - ipcMain.handle("settings-set-gpt-engine", (_event, engine) => { - return settings.setSync("engine.gpt", engine); - }); - - ipcMain.handle("settings-get-default-hotkeys", (_event) => { - return settings.getSync("defaultHotkeys"); - }); - - ipcMain.handle("settings-set-default-hotkeys", (_event, records) => { - return settings.setSync("defaultHotkeys", records); - }); - - ipcMain.handle("settings-get-dict", (_event) => { - return settings.getSync("dicts"); - }); - - ipcMain.handle("settings-set-dicts", (_event, dict) => { - return settings.setSync("dicts", dict); - }); - - ipcMain.handle("settings-get-api-url", (_event) => { - return settings.getSync("apiUrl"); - }); - - ipcMain.handle("settings-set-api-url", (_event, url) => { - return settings.setSync("apiUrl", url); - }); - - ipcMain.handle("settings-get-vocabulary-config", (_event) => { - return settings.getSync("vocabularyConfig"); - }); - - ipcMain.handle("settings-set-vocabulary-config", (_event, records) => { - return settings.setSync("vocabularyConfig", records); + ipcMain.handle("app-settings-get-sessions", (_event) => { + return sessions(); }); }, cachePath, libraryPath, userDataPath, dbPath, - whisperConfig, - language, - switchLanguage, apiUrl, ...settings, }; diff --git a/enjoy/src/main/whisper.ts b/enjoy/src/main/whisper.ts index a501f7fb..d5022114 100644 --- a/enjoy/src/main/whisper.ts +++ b/enjoy/src/main/whisper.ts @@ -8,6 +8,9 @@ import log from "@main/logger"; import url from "url"; import { enjoyUrlToPath } from "./utils"; import { t } from "i18next"; +import { UserSetting } from "@main/db/models"; +import db from "@main/db"; +import { UserSettingKeyEnum } from "@/types/enums"; const __filename = url.fileURLToPath(import.meta.url); /* @@ -40,7 +43,7 @@ class Whipser { this.initialize(); } - initialize() { + async initialize() { const models = []; const bundledModels = fs.readdirSync(this.bundledModelsDir); @@ -66,9 +69,23 @@ class Whipser { savePath: path.join(dir, file), }); } - settings.setSync("whisper.availableModels", models); - settings.setSync("whisper.modelsPath", dir); - this.config = settings.whisperConfig(); + + if (db.connection) { + const whisperConfig = (await UserSetting.get( + UserSettingKeyEnum.WHISPER + )) as string; + this.config = { + model: whisperConfig || models[0].name, + availableModels: models, + modelsPath: dir, + }; + } else { + this.config = { + model: models[0].name, + availableModels: models, + modelsPath: dir, + }; + } } currentModel() { @@ -82,9 +99,10 @@ class Whipser { } if (!model) { model = this.config.availableModels[0]; + this.config = Object.assign({}, this.config, { model: model.name }); + UserSetting.set(UserSettingKeyEnum.WHISPER, model.name); } - settings.setSync("whisper.model", model.name); return model; } @@ -258,13 +276,13 @@ class Whipser { registerIpcHandlers() { ipcMain.handle("whisper-config", async () => { + await this.initialize(); return this.config; }); ipcMain.handle("whisper-set-model", async (_event, model) => { - const originalModel = settings.getSync("whisper.model"); - settings.setSync("whisper.model", model); - this.config = settings.whisperConfig(); + const originalModel = this.config.model; + this.config.model = model; return this.check() .then(({ success, log }) => { @@ -275,26 +293,14 @@ class Whipser { } }) .catch((err) => { - settings.setSync("whisper.model", originalModel); + this.config.model = originalModel; throw err; + }) + .finally(() => { + UserSetting.set(UserSettingKeyEnum.WHISPER, this.config.model); }); }); - ipcMain.handle("whisper-set-service", async (_event, service) => { - if (service === "local") { - await this.check(); - settings.setSync("whisper.service", service); - this.config.service = service; - return this.config; - } else if (["cloudflare", "azure", "openai"].includes(service)) { - settings.setSync("whisper.service", service); - this.config.service = service; - return this.config; - } else { - throw new Error("Unknown service"); - } - }); - ipcMain.handle("whisper-check", async (_event) => { return await this.check(); }); diff --git a/enjoy/src/main/window.ts b/enjoy/src/main/window.ts index 8691966a..5647e257 100644 --- a/enjoy/src/main/window.ts +++ b/enjoy/src/main/window.ts @@ -14,7 +14,6 @@ import settings from "@main/settings"; import downloader from "@main/downloader"; import whisper from "@main/whisper"; import fs from "fs-extra"; -import "@main/i18n"; import log from "@main/logger"; import { REPO_URL, WS_URL } from "@/constants"; import { AudibleProvider, TedProvider, YoutubeProvider } from "@main/providers"; @@ -42,7 +41,7 @@ const main = { init: () => {}, }; -main.init = () => { +main.init = async () => { if (main.win) { main.win.show(); return; diff --git a/enjoy/src/preload.ts b/enjoy/src/preload.ts index a10b1298..d2e15e9b 100644 --- a/enjoy/src/preload.ts +++ b/enjoy/src/preload.ts @@ -182,75 +182,44 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", { showErrorBox: (title: string, content: string) => ipcRenderer.invoke("dialog-show-error-box", title, content), }, - settings: { + appSettings: { get: (key: string) => { - return ipcRenderer.invoke("settings-get", key); + return ipcRenderer.invoke("app-settings-get", key); }, set: (key: string, value: any) => { - return ipcRenderer.invoke("settings-set", key, value); + return ipcRenderer.invoke("app-settings-set", key, value); }, getLibrary: () => { - return ipcRenderer.invoke("settings-get-library"); + return ipcRenderer.invoke("app-settings-get-library"); }, setLibrary: (library: string) => { - return ipcRenderer.invoke("settings-set-library", library); + return ipcRenderer.invoke("app-settings-set-library", library); + }, + getSessions: () => { + return ipcRenderer.invoke("app-settings-get-sessions"); }, getUser: () => { - return ipcRenderer.invoke("settings-get-user"); + return ipcRenderer.invoke("app-settings-get-user"); }, setUser: (user: UserType) => { - return ipcRenderer.invoke("settings-set-user", user); + return ipcRenderer.invoke("app-settings-set-user", user); }, getUserDataPath: () => { - return ipcRenderer.invoke("settings-get-user-data-path"); - }, - getDefaultEngine: () => { - return ipcRenderer.invoke("settings-get-default-engine"); - }, - setDefaultEngine: (engine: "enjoyai" | "openai") => { - return ipcRenderer.invoke("settings-set-default-engine", engine); - }, - getGptEngine: () => { - return ipcRenderer.invoke("settings-get-gpt-engine"); - }, - setGptEngine: (engine: GptEngineSettingType) => { - return ipcRenderer.invoke("settings-set-gpt-engine", engine); - }, - getLlm: (provider: string) => { - return ipcRenderer.invoke("settings-get-llm", provider); - }, - setLlm: (provider: string, config: LlmProviderType) => { - return ipcRenderer.invoke("settings-set-llm", provider, config); - }, - getLanguage: (language: string) => { - return ipcRenderer.invoke("settings-get-language", language); - }, - switchLanguage: (language: string) => { - return ipcRenderer.invoke("settings-switch-language", language); - }, - getDefaultHotkeys: () => { - return ipcRenderer.invoke("settings-get-default-hotkeys"); - }, - setDefaultHotkeys: (records: Record) => { - return ipcRenderer.invoke("settings-set-default-hotkeys", records); - }, - getDictSettings: () => { - return ipcRenderer.invoke("settings-get-dict"); - }, - setDictSettings: (dict: DictSettingType) => { - return ipcRenderer.invoke("settings-set-dicts", dict); + return ipcRenderer.invoke("app-settings-get-user-data-path"); }, getApiUrl: () => { - return ipcRenderer.invoke("settings-get-api-url"); + return ipcRenderer.invoke("app-settings-get-api-url"); }, setApiUrl: (url: string) => { - return ipcRenderer.invoke("settings-set-api-url", url); + return ipcRenderer.invoke("app-settings-set-api-url", url); }, - getVocabularyConfig: () => { - return ipcRenderer.invoke("settings-get-vocabulary-config"); + }, + userSettings: { + get: (key: string) => { + return ipcRenderer.invoke("user-settings-get", key); }, - setVocabularyConfig: (records: Record) => { - return ipcRenderer.invoke("settings-set-vocabulary-config", records); + set: (key: string, value: any) => { + return ipcRenderer.invoke("user-settings-set", key, value); }, }, path: { @@ -259,7 +228,8 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", { }, }, db: { - init: () => ipcRenderer.invoke("db-init"), + connect: () => ipcRenderer.invoke("db-connect"), + disconnect: () => ipcRenderer.invoke("db-disconnect"), onTransaction: ( callback: ( event: IpcRendererEvent, @@ -516,9 +486,6 @@ contextBridge.exposeInMainWorld("__ENJOY_APP__", { setModel: (model: string) => { return ipcRenderer.invoke("whisper-set-model", model); }, - setService: (service: string) => { - return ipcRenderer.invoke("whisper-set-service", service); - }, check: () => { return ipcRenderer.invoke("whisper-check"); }, diff --git a/enjoy/src/renderer/app.tsx b/enjoy/src/renderer/app.tsx index 37ebdc60..c8c662ed 100644 --- a/enjoy/src/renderer/app.tsx +++ b/enjoy/src/renderer/app.tsx @@ -35,21 +35,21 @@ function App() { return ( - - - - - + + + + + - - - - - + + + + + ); } diff --git a/enjoy/src/renderer/cables/channels/notifications_channel.ts b/enjoy/src/renderer/cables/channels/notifications_channel.ts index 79143704..48fa9962 100644 --- a/enjoy/src/renderer/cables/channels/notifications_channel.ts +++ b/enjoy/src/renderer/cables/channels/notifications_channel.ts @@ -46,4 +46,8 @@ export class NoticiationsChannel { } ); } + + unsubscribe() { + this.consumer.disconnect(); + } } diff --git a/enjoy/src/renderer/components/chats/chat-form.tsx b/enjoy/src/renderer/components/chats/chat-form.tsx index 1d962544..ddf940e8 100644 --- a/enjoy/src/renderer/components/chats/chat-form.tsx +++ b/enjoy/src/renderer/components/chats/chat-form.tsx @@ -192,9 +192,9 @@ export const ChatForm = (props: { {t("local")} - {t("azureAi")} + {t("enjoyAzure")} - {t("cloudflareAi")} + {t("enjoyCloudflare")} OpenAI @@ -203,9 +203,9 @@ export const ChatForm = (props: { {form.watch("config.sttEngine") === "local" && t("localSpeechToTextDescription")} {form.watch("config.sttEngine") === "azure" && - t("azureSpeechToTextDescription")} + t("enjoyAzureSpeechToTextDescription")} {form.watch("config.sttEngine") === "cloudflare" && - t("cloudflareSpeechToTextDescription")} + t("enjoyCloudflareSpeechToTextDescription")} {form.watch("config.sttEngine") === "openai" && t("openaiSpeechToTextDescription")} diff --git a/enjoy/src/renderer/components/medias/media-loading-modal.tsx b/enjoy/src/renderer/components/medias/media-loading-modal.tsx index e64161fe..b71fe057 100644 --- a/enjoy/src/renderer/components/medias/media-loading-modal.tsx +++ b/enjoy/src/renderer/components/medias/media-loading-modal.tsx @@ -14,14 +14,11 @@ import { TabsList, TabsTrigger, } from "@renderer/components/ui"; -import { - CheckCircleIcon, - CircleAlertIcon, - LoaderIcon, -} from "lucide-react"; +import { CheckCircleIcon, CircleAlertIcon, LoaderIcon } from "lucide-react"; import { t } from "i18next"; import { useNavigate } from "react-router-dom"; import { TranscriptionCreateForm, TranscriptionsList } from "../transcriptions"; +import { SttEngineOptionEnum } from "@/types/enums"; export const MediaLoadingModal = () => { const navigate = useNavigate(); @@ -68,7 +65,7 @@ export const MediaLoadingModal = () => { generateTranscription({ originalText: data.text, language: data.language, - service: data.service as WhisperConfigType["service"], + service: data.service as SttEngineOptionEnum | "upload", isolate: data.isolate, }); }} diff --git a/enjoy/src/renderer/components/misc/bandu-login-form.tsx b/enjoy/src/renderer/components/misc/bandu-login-form.tsx index 4be47736..12e08283 100644 --- a/enjoy/src/renderer/components/misc/bandu-login-form.tsx +++ b/enjoy/src/renderer/components/misc/bandu-login-form.tsx @@ -6,6 +6,8 @@ import { Sheet, SheetTrigger, SheetContent, + SheetHeader, + SheetTitle, } from "@renderer/components/ui"; import { useContext, useEffect, useRef, useState } from "react"; import { AppSettingsProviderContext } from "@renderer/context"; @@ -38,6 +40,9 @@ export const BanduLoginButton = () => { className="h-screen" aria-describedby={undefined} > + + 学升登录 +
{open && }
diff --git a/enjoy/src/renderer/components/misc/db-state.tsx b/enjoy/src/renderer/components/misc/db-state.tsx index c0f95de4..1b4f0d83 100644 --- a/enjoy/src/renderer/components/misc/db-state.tsx +++ b/enjoy/src/renderer/components/misc/db-state.tsx @@ -8,12 +8,6 @@ import { t } from "i18next"; export const DbState = () => { const db = useContext(DbProviderContext); - useEffect(() => { - if (db.state === "disconnected") { - db.connect(); - } - }, [db.state]); - if (db.state === "connecting") { return (
diff --git a/enjoy/src/renderer/components/misc/layout.tsx b/enjoy/src/renderer/components/misc/layout.tsx index 64d382ae..2af0227a 100644 --- a/enjoy/src/renderer/components/misc/layout.tsx +++ b/enjoy/src/renderer/components/misc/layout.tsx @@ -25,7 +25,7 @@ export const Layout = () => {
- +
diff --git a/enjoy/src/renderer/components/misc/login-form.tsx b/enjoy/src/renderer/components/misc/login-form.tsx index 930e75ac..1da22fae 100644 --- a/enjoy/src/renderer/components/misc/login-form.tsx +++ b/enjoy/src/renderer/components/misc/login-form.tsx @@ -2,13 +2,10 @@ import { Separator, Card, CardContent, - CardHeader, - CardTitle, Avatar, AvatarImage, AvatarFallback, Button, - toast, Tabs, TabsList, TabsTrigger, @@ -28,40 +25,16 @@ import { NetworkState, } from "@renderer/components"; import { EmailLoginForm } from "./email-login-form"; -import { Client } from "@/api"; export const LoginForm = () => { - const { user, EnjoyApp, login, apiUrl } = useContext( - AppSettingsProviderContext - ); - const [rememberedUser, setRememberedUser] = useState(null); - - const loginWithRememberedUser = async () => { - if (!rememberedUser) return; - const client = new Client({ - baseUrl: apiUrl, - accessToken: rememberedUser.accessToken, - }); - - client - .me() - .then((user) => { - if (user?.id) { - login(Object.assign({}, rememberedUser, user)); - } - }) - .catch((error) => { - toast.error(error.message); - }); - }; + const { user, EnjoyApp, login } = useContext(AppSettingsProviderContext); + const [rememberedUsers, setRememberedUsers] = useState<{ id: string }[]>([]); useEffect(() => { - if (user) return; - - EnjoyApp.settings.getUser().then((user) => { - setRememberedUser(user); + EnjoyApp.appSettings.getSessions().then((sessions) => { + setRememberedUsers(sessions); }); - }, [user]); + }, []); if (user) { return ( @@ -73,77 +46,67 @@ export const LoginForm = () => { ); } - if (rememberedUser) { - return ( - - - {t("login")} - {t("advanced")} - - -
-
-
-
- - - - {rememberedUser.name[0].toUpperCase()} - - -
-
- {rememberedUser.name} -
-
- {rememberedUser.id} -
-
-
-
-
- - -
-
-
-
- - - - - - - - - - - -
- ); - } - return ( - - + 0 ? "selectUser" : "login"} + > + 0 ? 3 : 2 + }`} + > + {rememberedUsers.length > 0 && ( + {t("selectUser")} + )} {t("login")} {t("advanced")} + {rememberedUsers.length > 0 && ( + +
+ {rememberedUsers.map((rememberedUser) => ( +
+
+
+
+ + + + +
+
+
+ {rememberedUser.id} +
+
+
+
+
+ +
+
+
+ ))} +
+
+ )} diff --git a/enjoy/src/renderer/components/misc/mixin-login-form.tsx b/enjoy/src/renderer/components/misc/mixin-login-form.tsx index 9048f727..cd18b246 100644 --- a/enjoy/src/renderer/components/misc/mixin-login-form.tsx +++ b/enjoy/src/renderer/components/misc/mixin-login-form.tsx @@ -6,6 +6,8 @@ import { Sheet, SheetTrigger, SheetContent, + SheetHeader, + SheetTitle, } from "@renderer/components/ui"; import { useContext, useEffect, useState } from "react"; import { AppSettingsProviderContext } from "@renderer/context"; @@ -37,6 +39,9 @@ export const MixinLoginButton = () => { className="h-screen" aria-describedby={undefined} > + + Mixin Messenger Login +
{open && }
@@ -141,7 +146,7 @@ export const MixinLoginForm = () => { setCountdown(120); }) .catch((err) => { - toast.error(err.message); + toast.error(err.response?.data || err.message); }) .finally(() => { setLoading(false); @@ -164,7 +169,7 @@ export const MixinLoginForm = () => { if (user?.id && user?.accessToken) login(user); }) .catch((err) => { - toast.error(err.message); + toast.error(err.response?.data || err.message); }); }} > diff --git a/enjoy/src/renderer/components/misc/sidebar.tsx b/enjoy/src/renderer/components/misc/sidebar.tsx index 1d816948..0adcc3f4 100644 --- a/enjoy/src/renderer/components/misc/sidebar.tsx +++ b/enjoy/src/renderer/components/misc/sidebar.tsx @@ -47,9 +47,15 @@ export const Sidebar = () => { const { EnjoyApp, cable } = useContext(AppSettingsProviderContext); useEffect(() => { + if (!cable) return; + const channel = new NoticiationsChannel(cable); channel.subscribe(); - }, []); + + return () => { + channel.unsubscribe(); + }; + }, [cable]); return (
{
- + {t("sidebar.preferences")} diff --git a/enjoy/src/renderer/components/preferences/api-url-settings.tsx b/enjoy/src/renderer/components/preferences/api-url-settings.tsx index d3c2e205..fe1c9458 100644 --- a/enjoy/src/renderer/components/preferences/api-url-settings.tsx +++ b/enjoy/src/renderer/components/preferences/api-url-settings.tsx @@ -42,7 +42,7 @@ export const ApiUrlSettings = () => { return (
-
+
{t("apiSettings")}
@@ -100,7 +100,7 @@ export const ApiUrlSettings = () => { )}
-
+
{t("reloadIsNeededAfterChanged")}
diff --git a/enjoy/src/renderer/components/preferences/index.ts b/enjoy/src/renderer/components/preferences/index.ts index 22d45d3a..625da100 100644 --- a/enjoy/src/renderer/components/preferences/index.ts +++ b/enjoy/src/renderer/components/preferences/index.ts @@ -13,7 +13,7 @@ export * from "./learning-language-settings"; export * from "./default-engine-settings"; export * from "./openai-settings"; export * from "./library-settings"; -export * from "./whisper-settings"; +export * from "./stt-settings"; export * from "./user-settings"; export * from "./email-settings"; diff --git a/enjoy/src/renderer/components/preferences/library-settings.tsx b/enjoy/src/renderer/components/preferences/library-settings.tsx index 1141033c..3da6f3fd 100644 --- a/enjoy/src/renderer/components/preferences/library-settings.tsx +++ b/enjoy/src/renderer/components/preferences/library-settings.tsx @@ -25,8 +25,8 @@ export const LibrarySettings = () => { }); if (filePaths) { - EnjoyApp.settings.setLibrary(filePaths[0]); - const _library = await EnjoyApp.settings.getLibrary(); + EnjoyApp.appSettings.setLibrary(filePaths[0]); + const _library = await EnjoyApp.appSettings.getLibrary(); if (_library !== libraryPath) { EnjoyApp.app.relaunch(); } @@ -83,9 +83,13 @@ const DiskUsage = () => { const [usage, setUsage] = useState([]); const { EnjoyApp } = useContext(AppSettingsProviderContext); - const openPath = async (path: string) => { - if (path) { - await EnjoyApp.shell.openPath(path); + const openPath = async (filePath: string) => { + console.log(filePath); + + if (filePath?.match(/.+\.json$/)) { + await EnjoyApp.shell.openPath(filePath.split("/").slice(0, -1).join("/")); + } else if (filePath) { + await EnjoyApp.shell.openPath(filePath); } }; diff --git a/enjoy/src/renderer/components/preferences/whisper-settings.tsx b/enjoy/src/renderer/components/preferences/stt-settings.tsx similarity index 72% rename from enjoy/src/renderer/components/preferences/whisper-settings.tsx rename to enjoy/src/renderer/components/preferences/stt-settings.tsx index 4de4f435..eb6ef654 100644 --- a/enjoy/src/renderer/components/preferences/whisper-settings.tsx +++ b/enjoy/src/renderer/components/preferences/stt-settings.tsx @@ -21,11 +21,11 @@ import { } from "@renderer/context"; import { useContext, useEffect, useState } from "react"; import { InfoIcon, AlertCircleIcon } from "lucide-react"; +import { SttEngineOptionEnum } from "@/types/enums"; export const WhisperSettings = () => { - const { whisperConfig, refreshWhisperConfig, setWhisperService } = useContext( - AISettingsProviderContext - ); + const { sttEngine, whisperConfig, refreshWhisperConfig, setSttEngine } = + useContext(AISettingsProviderContext); const { EnjoyApp } = useContext(AppSettingsProviderContext); const [stderr, setStderr] = useState(""); @@ -58,39 +58,47 @@ export const WhisperSettings = () => {
{t("sttAiService")} - {stderr && } + {stderr && ( + + )}
- {whisperConfig?.service === "local" && + {sttEngine === SttEngineOptionEnum.LOCAL && t("localSpeechToTextDescription")} - {whisperConfig?.service === "azure" && - t("azureSpeechToTextDescription")} - {whisperConfig?.service === "cloudflare" && - t("cloudflareSpeechToTextDescription")} - {whisperConfig?.service === "openai" && + {sttEngine === SttEngineOptionEnum.ENJOY_AZURE && + t("enjoyAzureSpeechToTextDescription")} + {sttEngine === SttEngineOptionEnum.ENJOY_CLOUDFLARE && + t("enjoyCloudflareSpeechToTextDescription")} + {sttEngine === SttEngineOptionEnum.OPENAI && t("openaiSpeechToTextDescription")}
- {whisperConfig.service === "local" && ( + {sttEngine === "local" && ( <> )} - diff --git a/enjoy/src/renderer/components/widgets/sentence.tsx b/enjoy/src/renderer/components/widgets/sentence.tsx index 0cdc50ea..d3f850bd 100644 --- a/enjoy/src/renderer/components/widgets/sentence.tsx +++ b/enjoy/src/renderer/components/widgets/sentence.tsx @@ -7,10 +7,10 @@ export const Sentence = ({ sentence }: { sentence: string }) => { {words.map((word, index) => { return ( - <> + {index === words.length - 1 ? " " : " "} - + ); })} diff --git a/enjoy/src/renderer/context/ai-settings-provider.tsx b/enjoy/src/renderer/context/ai-settings-provider.tsx index a7702100..06c41326 100644 --- a/enjoy/src/renderer/context/ai-settings-provider.tsx +++ b/enjoy/src/renderer/context/ai-settings-provider.tsx @@ -1,9 +1,14 @@ import { createContext, useEffect, useState, useContext } from "react"; -import { AppSettingsProviderContext } from "@renderer/context"; +import { + AppSettingsProviderContext, + DbProviderContext, +} from "@renderer/context"; +import { SttEngineOptionEnum, UserSettingKeyEnum } from "@/types/enums"; type AISettingsProviderState = { setWhisperModel?: (name: string) => Promise; - setWhisperService?: (name: string) => Promise; + sttEngine?: SttEngineOptionEnum; + setSttEngine?: (name: string) => Promise; whisperConfig?: WhisperConfigType; refreshWhisperConfig?: () => void; openai?: LlmProviderType; @@ -30,19 +35,26 @@ export const AISettingsProvider = ({ }); const [openai, setOpenai] = useState(null); const [whisperConfig, setWhisperConfig] = useState(null); + const [sttEngine, setSttEngine] = useState( + SttEngineOptionEnum.ENJOY_AZURE + ); const { EnjoyApp, libraryPath, user, apiUrl } = useContext( AppSettingsProviderContext ); + const db = useContext(DbProviderContext); useEffect(() => { + if (db.state !== "connected") return; + fetchSettings(); - }, []); + }, [db.state]); useEffect(() => { + if (db.state !== "connected") return; if (!libraryPath) return; refreshWhisperConfig(); - }, [libraryPath]); + }, [db.state, libraryPath]); const refreshWhisperConfig = async () => { const config = await EnjoyApp.whisper.config(); @@ -56,34 +68,29 @@ export const AISettingsProvider = ({ }); }; - const setWhisperService = async (name: WhisperConfigType["service"]) => { - return EnjoyApp.whisper.setService(name).then((config) => { - if (!config) return; - setWhisperConfig(config); - }); + const handleSetSttEngine = async (name: SttEngineOptionEnum) => { + setSttEngine(name); + return EnjoyApp.userSettings.set(UserSettingKeyEnum.STT_ENGINE, name); }; const fetchSettings = async () => { - const _openai = await EnjoyApp.settings.getLlm("openai"); + const _sttEngine = await EnjoyApp.userSettings.get( + UserSettingKeyEnum.STT_ENGINE + ); + if (_sttEngine) { + setSttEngine(_sttEngine); + } + + const _openai = await EnjoyApp.userSettings.get(UserSettingKeyEnum.OPENAI); if (_openai) { setOpenai(Object.assign({ name: "openai" }, _openai)); } - const _defaultEngine = await EnjoyApp.settings.getDefaultEngine(); - const _gptEngine = await EnjoyApp.settings.getGptEngine(); + const _gptEngine = await EnjoyApp.userSettings.get( + UserSettingKeyEnum.GPT_ENGINE + ); if (_gptEngine) { setGptEngine(_gptEngine); - } else if (_defaultEngine) { - // Migrate default engine to gpt engine - const engine = { - name: _defaultEngine, - models: { - default: "gpt-4o", - }, - }; - EnjoyApp.settings.setGptEngine(engine).then(() => { - setGptEngine(engine); - }); } else if (_openai?.key) { const engine = { name: "openai", @@ -91,9 +98,11 @@ export const AISettingsProvider = ({ default: "gpt-4o", }, }; - EnjoyApp.settings.setGptEngine(engine).then(() => { - setGptEngine(engine); - }); + EnjoyApp.userSettings + .set(UserSettingKeyEnum.GPT_ENGINE, engine) + .then(() => { + setGptEngine(engine); + }); } else { const engine = { name: "enjoyai", @@ -101,35 +110,28 @@ export const AISettingsProvider = ({ default: "gpt-4o", }, }; - EnjoyApp.settings.setGptEngine(engine).then(() => { - setGptEngine(engine); - }); + EnjoyApp.userSettings + .set(UserSettingKeyEnum.GPT_ENGINE, engine) + .then(() => { + setGptEngine(engine); + }); } }; - const handleSetLlm = async ( - name: SupportedLlmProviderType, - config: LlmProviderType - ) => { - await EnjoyApp.settings.setLlm(name, config); - const _config = await EnjoyApp.settings.getLlm(name); - - switch (name) { - case "openai": - setOpenai(Object.assign({ name: "openai" }, _config)); - break; - default: - throw new Error("Unsupported LLM provider"); - } + const handleSetOpenai = async (config: LlmProviderType) => { + await EnjoyApp.userSettings.set(UserSettingKeyEnum.OPENAI, config); + setOpenai(Object.assign({ name: "openai" }, config)); }; return ( { - EnjoyApp.settings.setGptEngine(engine).then(() => { - setGptEngine(engine); - }); + EnjoyApp.userSettings + .set(UserSettingKeyEnum.GPT_ENGINE, engine) + .then(() => { + setGptEngine(engine); + }); }, currentEngine: gptEngine.name === "openai" @@ -142,11 +144,12 @@ export const AISettingsProvider = ({ baseUrl: `${apiUrl}/api/ai`, }), openai, - setOpenai: (config: LlmProviderType) => handleSetLlm("openai", config), + setOpenai: (config: LlmProviderType) => handleSetOpenai(config), whisperConfig, refreshWhisperConfig, setWhisperModel, - setWhisperService, + sttEngine, + setSttEngine: (name: SttEngineOptionEnum) => handleSetSttEngine(name), }} > {children} diff --git a/enjoy/src/renderer/context/app-settings-provider.tsx b/enjoy/src/renderer/context/app-settings-provider.tsx index 7d5764f0..c15e2ede 100644 --- a/enjoy/src/renderer/context/app-settings-provider.tsx +++ b/enjoy/src/renderer/context/app-settings-provider.tsx @@ -1,4 +1,4 @@ -import { createContext, useEffect, useState } from "react"; +import { createContext, useContext, useEffect, useState } from "react"; import { WEB_API_URL, LANGUAGES, IPA_MAPPINGS } from "@/constants"; import { Client } from "@/api"; import i18n from "@renderer/i18n"; @@ -6,6 +6,8 @@ import ahoy from "ahoy.js"; import { type Consumer, createConsumer } from "@rails/actioncable"; import * as Sentry from "@sentry/electron/renderer"; import { SENTRY_DSN } from "@/constants"; +import { DbProviderContext } from "@renderer/context"; +import { UserSettingKeyEnum } from "@/types/enums"; type AppSettingsProviderState = { webApi: Client; @@ -68,6 +70,7 @@ export const AppSettingsProvider = ({ const [ipaMappings, setIpaMappings] = useState<{ [key: string]: string }>( IPA_MAPPINGS ); + const db = useContext(DbProviderContext); const initSentry = () => { EnjoyApp.app.isPackaged().then((isPackaged) => { @@ -80,24 +83,30 @@ export const AppSettingsProvider = ({ }; const fetchLanguages = async () => { - const language = await EnjoyApp.settings.getLanguage(); - setLanguage(language as "en" | "zh-CN"); + const language = await EnjoyApp.userSettings.get( + UserSettingKeyEnum.LANGUAGE + ); + setLanguage((language as "en" | "zh-CN") || "en"); i18n.changeLanguage(language); const _nativeLanguage = - (await EnjoyApp.settings.get("nativeLanguage")) || "zh-CN"; + (await EnjoyApp.userSettings.get(UserSettingKeyEnum.NATIVE_LANGUAGE)) || + "zh-CN"; setNativeLanguage(_nativeLanguage); const _learningLanguage = - (await EnjoyApp.settings.get("learningLanguage")) || "en-US"; + (await EnjoyApp.userSettings.get(UserSettingKeyEnum.LEARNING_LANGUAGE)) || + "en-US"; setLearningLanguage(_learningLanguage); }; const switchLanguage = (language: "en" | "zh-CN") => { - EnjoyApp.settings.switchLanguage(language).then(() => { - i18n.changeLanguage(language); - setLanguage(language); - }); + EnjoyApp.userSettings + .set(UserSettingKeyEnum.LANGUAGE, language) + .then(() => { + i18n.changeLanguage(language); + setLanguage(language); + }); }; const switchNativeLanguage = (lang: string) => { @@ -105,14 +114,14 @@ export const AppSettingsProvider = ({ if (lang == learningLanguage) return; setNativeLanguage(lang); - EnjoyApp.settings.set("nativeLanguage", lang); + EnjoyApp.userSettings.set(UserSettingKeyEnum.NATIVE_LANGUAGE, lang); }; const switchLearningLanguage = (lang: string) => { if (LANGUAGES.findIndex((l) => l.code == lang) < 0) return; if (lang == nativeLanguage) return; - EnjoyApp.settings.set("learningLanguage", lang); + EnjoyApp.userSettings.set(UserSettingKeyEnum.LEARNING_LANGUAGE, lang); setLearningLanguage(lang); }; @@ -121,43 +130,40 @@ export const AppSettingsProvider = ({ setVersion(version); }; - const fetchUser = async () => { + const fetchApiUrl = async () => { const apiUrl = await EnjoyApp.app.apiUrl(); setApiUrl(apiUrl); - - const currentUser = await EnjoyApp.settings.getUser(); - if (!currentUser) return; - - const client = new Client({ - baseUrl: apiUrl, - accessToken: currentUser.accessToken, - }); - - client.me().then((user) => { - if (user?.id) { - login(Object.assign({}, currentUser, user)); - } - }); }; - const login = (user: UserType) => { + const autoLogin = async () => { + const currentUser = await EnjoyApp.appSettings.getUser(); + if (!currentUser) return; + + setUser(currentUser); + }; + + const login = async (user: UserType) => { + if (!user?.id) return; + setUser(user); - EnjoyApp.settings.setUser(user); - createCable(user.accessToken); + if (user.accessToken) { + // Set current user to App settings + EnjoyApp.appSettings.setUser({ id: user.id, name: user.name }); + } }; const logout = () => { setUser(null); - EnjoyApp.settings.setUser(null); + EnjoyApp.appSettings.setUser(null); }; const fetchLibraryPath = async () => { - const dir = await EnjoyApp.settings.getLibrary(); + const dir = await EnjoyApp.appSettings.getLibrary(); setLibraryPath(dir); }; const setLibraryPathHandler = async (dir: string) => { - await EnjoyApp.settings.setLibrary(dir); + await EnjoyApp.appSettings.setLibrary(dir); setLibraryPath(dir); }; @@ -173,19 +179,21 @@ export const AppSettingsProvider = ({ }; const setApiUrlHandler = async (url: string) => { - EnjoyApp.settings.setApiUrl(url).then(() => { + EnjoyApp.appSettings.setApiUrl(url).then(() => { EnjoyApp.app.reload(); }); }; const createCable = async (token: string) => { + if (!token) return; + const wsUrl = await EnjoyApp.app.wsUrl(); const consumer = createConsumer(wsUrl + "/cable?token=" + token); setCable(consumer); }; const fetchRecorderConfig = async () => { - const config = await EnjoyApp.settings.get("recorderConfig"); + const config = await EnjoyApp.userSettings.get(UserSettingKeyEnum.RECORDER); if (config) { setRecorderConfig(config); } else { @@ -201,30 +209,45 @@ export const AppSettingsProvider = ({ }; const setRecorderConfigHandler = async (config: RecorderConfigType) => { - return EnjoyApp.settings.set("recorderConfig", config).then(() => { - setRecorderConfig(config); - }); + return EnjoyApp.userSettings + .set(UserSettingKeyEnum.RECORDER, config) + .then(() => { + setRecorderConfig(config); + }); }; const fetchVocabularyConfig = async () => { - const config = await EnjoyApp.settings.getVocabularyConfig(); - setVocabularyConfig(config || { lookupOnMouseOver: false }); + EnjoyApp.userSettings + .get(UserSettingKeyEnum.VOCABULARY) + .then((config) => { + setVocabularyConfig(config || { lookupOnMouseOver: true }); + }) + .catch((err) => { + console.error(err); + setVocabularyConfig({ lookupOnMouseOver: true }); + }); }; const setVocabularyConfigHandler = async (config: VocabularyConfigType) => { - await EnjoyApp.settings.setVocabularyConfig(config); + await EnjoyApp.userSettings.set(UserSettingKeyEnum.VOCABULARY, config); setVocabularyConfig(config); }; useEffect(() => { - fetchVersion(); - fetchUser(); - fetchLibraryPath(); + if (db.state !== "connected") return; + fetchLanguages(); - fetchProxyConfig(); fetchVocabularyConfig(); initSentry(); fetchRecorderConfig(); + }, [db.state]); + + useEffect(() => { + autoLogin(); + fetchVersion(); + fetchLibraryPath(); + fetchProxyConfig(); + fetchApiUrl(); }, []); useEffect(() => { @@ -235,6 +258,11 @@ export const AppSettingsProvider = ({ baseUrl: apiUrl, accessToken: user?.accessToken, locale: language, + onError: (err) => { + if (user.accessToken && err.status == 401) { + setUser({ ...user, accessToken: null }); + } + }, }) ); }, [user, apiUrl, language]); @@ -255,6 +283,27 @@ export const AppSettingsProvider = ({ }); }, [webApi]); + useEffect(() => { + if (!user) return; + + db.connect().then(async () => { + // Login via API, update profile to DB + if (user.accessToken) { + EnjoyApp.userSettings.set(UserSettingKeyEnum.PROFILE, user); + } else { + // Auto login from local settings, get full profile from DB + const profile = await EnjoyApp.userSettings.get( + UserSettingKeyEnum.PROFILE + ); + setUser(profile); + EnjoyApp.appSettings.setUser({ id: profile.id, name: profile.name }); + } + }); + return () => { + db.disconnect(); + }; + }, [user?.id]); + return ( void; + connect?: () => Promise; + disconnect?: () => Promise; addDblistener?: (callback: (event: CustomEvent) => void) => void; removeDbListener?: (callback: (event: CustomEvent) => void) => void; }; -type DbProviderState = DbState & { - connect?: () => void; -}; const initialState: DbProviderState = { state: "disconnected", @@ -25,20 +22,44 @@ export const DbProvider = ({ children }: { children: React.ReactNode }) => { const [state, setState] = useState("disconnected"); const [path, setPath] = useState(); const [error, setError] = useState(); - const { EnjoyApp } = useContext(AppSettingsProviderContext); + const EnjoyApp = window.__ENJOY_APP__; const connect = async () => { if (["connected", "connecting"].includes(state)) return; - + console.info("--- connecting db ---"); setState("connecting"); - const _db = await EnjoyApp.db.init(); - - setState(_db.state); - setPath(_db.path); - setError(_db.error); + return EnjoyApp.db + .connect() + .then((_db) => { + setState(_db.state); + setPath(_db.path); + setError(_db.error); + }) + .catch((err) => { + setState("error"); + setError(err.message); + }); }; + const disconnect = () => { + console.info("--- disconnecting db ---"); + return EnjoyApp.db.disconnect().then(() => { + setState("disconnected"); + setPath(undefined); + setError(undefined); + }); + }; + + useEffect(() => { + console.info( + "--- db state changed ---\n", + `state: ${state};\n`, + `path: ${path};\n`, + `error: ${error};\n` + ); + }, [state]); + const addDblistener = (callback: (event: CustomEvent) => void) => { document.addEventListener("db-on-transaction", callback); }; @@ -48,14 +69,14 @@ export const DbProvider = ({ children }: { children: React.ReactNode }) => { }; useEffect(() => { - if (state !== "connected") return; + if (state === "connected") { + EnjoyApp.db.onTransaction((_event, state) => { + log.debug("db-on-transaction", state); - EnjoyApp.db.onTransaction((_event, state) => { - log.debug("db-on-transaction", state); - - const event = new CustomEvent("db-on-transaction", { detail: state }); - document.dispatchEvent(event); - }); + const event = new CustomEvent("db-on-transaction", { detail: state }); + document.dispatchEvent(event); + }); + } return () => { EnjoyApp.db.removeListeners(); @@ -69,6 +90,7 @@ export const DbProvider = ({ children }: { children: React.ReactNode }) => { path, error, connect, + disconnect, addDblistener, removeDbListener, }} diff --git a/enjoy/src/renderer/context/dict-provider.tsx b/enjoy/src/renderer/context/dict-provider.tsx index f6e1085e..933db12a 100644 --- a/enjoy/src/renderer/context/dict-provider.tsx +++ b/enjoy/src/renderer/context/dict-provider.tsx @@ -1,6 +1,10 @@ import { createContext, useState, useEffect, useContext, useMemo } from "react"; -import { AppSettingsProviderContext } from "@renderer/context"; +import { + AppSettingsProviderContext, + DbProviderContext, +} from "@renderer/context"; import { t } from "i18next"; +import { UserSettingKeyEnum } from "@/types/enums"; type DictProviderState = { settings: DictSettingType; @@ -48,6 +52,7 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => { }); const [currentDictValue, setCurrentDictValue] = useState(""); const [currentDict, setCurrentDict] = useState(); + const { state: dbState } = useContext(DbProviderContext); const availableDicts = useMemo( () => @@ -93,12 +98,14 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => { }, [availableDicts, settings]); useEffect(() => { + if (dbState !== "connected") return; + fetchSettings(); fetchDicts(); - }, []); + }, [dbState]); const fetchSettings = async () => { - return EnjoyApp.settings.getDictSettings().then((res) => { + return EnjoyApp.userSettings.get(UserSettingKeyEnum.DICTS).then((res) => { res && setSettings(res); }); }; @@ -119,8 +126,8 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => { const setDefault = async (dict: Dict | null) => { const _settings = { ...settings, default: dict?.name ?? "" }; - EnjoyApp.settings - .setDictSettings(_settings) + EnjoyApp.userSettings + .set(UserSettingKeyEnum.DICTS, _settings) .then(() => setSettings(_settings)); }; @@ -129,8 +136,8 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => { const removing = [...(settings.removing ?? []), dict.name]; const _settings = { ...settings, removing }; - EnjoyApp.settings - .setDictSettings(_settings) + EnjoyApp.userSettings + .set(UserSettingKeyEnum.DICTS, _settings) .then(() => setSettings(_settings)); } }; @@ -140,8 +147,8 @@ export const DictProvider = ({ children }: { children: React.ReactNode }) => { settings.removing?.filter((name) => name !== dict.name) ?? []; const _settings = { ...settings, removing }; - EnjoyApp.settings - .setDictSettings(_settings) + EnjoyApp.userSettings + .set(UserSettingKeyEnum.DICTS, _settings) .then(() => setSettings(_settings)); }; diff --git a/enjoy/src/renderer/context/hotkeys-settings-provider.tsx b/enjoy/src/renderer/context/hotkeys-settings-provider.tsx index ec8b82b5..01bfd2f5 100644 --- a/enjoy/src/renderer/context/hotkeys-settings-provider.tsx +++ b/enjoy/src/renderer/context/hotkeys-settings-provider.tsx @@ -6,8 +6,12 @@ import { useState, } from "react"; import { useHotkeys, useRecordHotkeys } from "react-hotkeys-hook"; -import { AppSettingsProviderContext } from "./app-settings-provider"; -import _ from "lodash"; +import { + AppSettingsProviderContext, + DbProviderContext, +} from "@renderer/context"; +import isEmpty from "lodash/isEmpty"; +import { UserSettingKeyEnum } from "@/types/enums"; function isShortcutValid(shortcut: string) { const modifiers = ["ctrl", "alt", "shift", "meta"]; @@ -150,18 +154,25 @@ export const HotKeysSettingsProvider = ({ const [keys, { start, stop, resetKeys, isRecording }] = useRecordHotkeys(); const { EnjoyApp } = useContext(AppSettingsProviderContext); + const { state: dbState } = useContext(DbProviderContext); useEffect(() => { + if (dbState !== "connected") return; + fetchSettings(); - }, []); + }, [dbState]); const fetchSettings = async () => { - const _hotkeys = await EnjoyApp.settings.getDefaultHotkeys(); + const _hotkeys = await EnjoyApp.userSettings.get( + UserSettingKeyEnum.HOTKEYS + ); // During version iterations, there may be added or removed keys. const merged = mergeWithPreference(_hotkeys ?? {}, defaultKeyMap); - await EnjoyApp.settings.setDefaultHotkeys(merged).then(() => { - setCurrentHotkeys(merged); - }); + await EnjoyApp.userSettings + .set(UserSettingKeyEnum.HOTKEYS, merged) + .then(() => { + setCurrentHotkeys(merged); + }); }; const changeHotkey = useCallback( @@ -200,9 +211,11 @@ export const HotKeysSettingsProvider = ({ }; } - await EnjoyApp.settings.setDefaultHotkeys(newMap).then(() => { - setCurrentHotkeys(newMap); - }); + await EnjoyApp.userSettings + .set(UserSettingKeyEnum.HOTKEYS, newMap) + .then(() => { + setCurrentHotkeys(newMap); + }); resetKeys(); }, [currentHotkeys] @@ -230,7 +243,9 @@ export const HotKeysSettingsProvider = ({ changeHotkey, }} > - {_.isEmpty(currentHotkeys) ? null : ( + {isEmpty(currentHotkeys) ? ( + children + ) : ( Promise; transcribing: boolean; diff --git a/enjoy/src/renderer/hooks/use-transcribe.tsx b/enjoy/src/renderer/hooks/use-transcribe.tsx index 5db04258..bddcee9e 100644 --- a/enjoy/src/renderer/hooks/use-transcribe.tsx +++ b/enjoy/src/renderer/hooks/use-transcribe.tsx @@ -18,6 +18,7 @@ import { import take from "lodash/take"; import sortedUniqBy from "lodash/sortedUniqBy"; import { parseText } from "media-captions"; +import { SttEngineOptionEnum } from "@/types/enums"; // test a text string has any punctuations or not // some transcribed text may not have any punctuations @@ -48,7 +49,7 @@ export const useTranscribe = () => { targetType?: string; originalText?: string; language: string; - service: WhisperConfigType["service"] | "upload"; + service: SttEngineOptionEnum | "upload"; isolate?: boolean; align?: boolean; } @@ -100,15 +101,15 @@ export const useTranscribe = () => { text: originalText, }; } - } else if (service === "local") { + } else if (service === SttEngineOptionEnum.LOCAL) { result = await transcribeByLocal(url, language); - } else if (service === "cloudflare") { + } else if (service === SttEngineOptionEnum.ENJOY_CLOUDFLARE) { result = await transcribeByCloudflareAi(blob); - } else if (service === "openai") { + } else if (service === SttEngineOptionEnum.OPENAI) { result = await transcribeByOpenAi( new File([blob], "audio.mp3", { type: "audio/mp3" }) ); - } else if (service === "azure") { + } else if (service === SttEngineOptionEnum.ENJOY_AZURE) { result = await transcribeByAzureAi( new File([blob], "audio.wav", { type: "audio/wav" }), language, diff --git a/enjoy/src/renderer/hooks/use-transcriptions.tsx b/enjoy/src/renderer/hooks/use-transcriptions.tsx index 41bbc4c1..b4c16fd0 100644 --- a/enjoy/src/renderer/hooks/use-transcriptions.tsx +++ b/enjoy/src/renderer/hooks/use-transcriptions.tsx @@ -8,21 +8,18 @@ import { import { toast } from "@renderer/components/ui"; import { TimelineEntry } from "echogarden/dist/utilities/Timeline.d.js"; import { MAGIC_TOKEN_REGEX, END_OF_SENTENCE_REGEX } from "@/constants"; +import { SttEngineOptionEnum } from "@/types/enums"; export const useTranscriptions = (media: AudioType | VideoType) => { - const { whisperConfig } = useContext(AISettingsProviderContext); - const { EnjoyApp, learningLanguage } = useContext( - AppSettingsProviderContext - ); + const { sttEngine } = useContext(AISettingsProviderContext); + const { EnjoyApp, learningLanguage } = useContext(AppSettingsProviderContext); const { addDblistener, removeDbListener } = useContext(DbProviderContext); const [transcription, setTranscription] = useState(null); const { transcribe, output } = useTranscribe(); const [transcribingProgress, setTranscribingProgress] = useState(0); const [transcribing, setTranscribing] = useState(false); const [transcribingOutput, setTranscribingOutput] = useState(""); - const [service, setService] = useState< - WhisperConfigType["service"] | "upload" - >(whisperConfig.service); + const [service, setService] = useState(sttEngine); const onTransactionUpdate = (event: CustomEvent) => { if (!transcription) return; @@ -63,13 +60,13 @@ export const useTranscriptions = (media: AudioType | VideoType) => { const generateTranscription = async (params?: { originalText?: string; language?: string; - service?: WhisperConfigType["service"] | "upload"; + service?: SttEngineOptionEnum | "upload"; isolate?: boolean; }) => { let { originalText, language = learningLanguage, - service = whisperConfig.service, + service = sttEngine, isolate = false, } = params || {}; setService(service); diff --git a/enjoy/src/renderer/pages/home.tsx b/enjoy/src/renderer/pages/home.tsx index eebc7bdb..43a3aef8 100644 --- a/enjoy/src/renderer/pages/home.tsx +++ b/enjoy/src/renderer/pages/home.tsx @@ -5,10 +5,11 @@ import { VideosSegment, YoutubeVideosSegment, EnrollmentSegment, - Vocabulary, } from "@renderer/components"; import { useContext, useEffect, useState } from "react"; import { AppSettingsProviderContext } from "@renderer/context"; +import { Button } from "@renderer/components/ui"; +import { t } from "i18next"; export default () => { const [channels, setChannels] = useState([ @@ -28,17 +29,37 @@ export default () => { }, []); return ( -
-
- - - - - - {channels.map((channel) => ( - - ))} +
+ +
+
+ + + + + + {channels.map((channel) => ( + + ))} +
); }; + +const AuthorizationStatusBar = () => { + const { user, logout } = useContext(AppSettingsProviderContext); + + if (user.accessToken === null) { + return ( +
+ {t("authorizationExpired")} + +
+ ); + } + + return null; +}; diff --git a/enjoy/src/renderer/pages/landing.tsx b/enjoy/src/renderer/pages/landing.tsx index e85756c4..fff4c41b 100644 --- a/enjoy/src/renderer/pages/landing.tsx +++ b/enjoy/src/renderer/pages/landing.tsx @@ -20,7 +20,7 @@ export default () => {
{initialized && ( - + )} diff --git a/enjoy/src/types/enjoy-app.d.ts b/enjoy/src/types/enjoy-app.d.ts index 9c3c6978..8cedc34e 100644 --- a/enjoy/src/types/enjoy-app.d.ts +++ b/enjoy/src/types/enjoy-app.d.ts @@ -115,7 +115,7 @@ type EnjoyAppType = { ) => Promise; showErrorBox: (title: string, content: string) => Promise; }; - settings: { + appSettings: { get: (key: string) => Promise; set: (key: string, value: any) => Promise; getLibrary: () => Promise; @@ -123,25 +123,13 @@ type EnjoyAppType = { getUser: () => Promise; setUser: (user: UserType) => Promise; getUserDataPath: () => Promise; - getDefaultEngine: () => Promise; - setDefaultEngine: (string) => Promise; - getGptEngine: () => Promise; - setGptEngine: (GptEngineSettingType) => Promise; - getLlm: (provider: SupportedLlmProviderType) => Promise; - setLlm: ( - provider: SupportedLlmProviderType, - LlmProviderType - ) => Promise; - getLanguage: () => Promise; - switchLanguage: (language: string) => Promise; - getDefaultHotkeys: () => Promise | undefined>; - setDefaultHotkeys: (records: Record) => Promise; - getDictSettings: () => Promise; - setDictSettings: (dict: DictSettingType) => Promise; getApiUrl: () => Promise; setApiUrl: (url: string) => Promise; - getVocabularyConfig: () => Promise; - setVocabularyConfig: (records: VocabularyConfigType) => Promise; + getSessions: () => Promise<{ id: string }[]>; + }; + userSettings: { + get: (key: UserSettingKeyEnum) => Promise; + set: (key: UserSettingKeyEnum, value: any) => Promise; }; fs: { ensureDir: (path: string) => Promise; @@ -150,7 +138,8 @@ type EnjoyAppType = { join: (...paths: string[]) => Promise; }; db: { - init: () => Promise; + connect: () => Promise; + disconnect: () => Promise; onTransaction: ( callback: (event, state: TransactionStateType) => void ) => Promise; @@ -290,9 +279,6 @@ type EnjoyAppType = { config: () => Promise; check: () => Promise<{ success: boolean; log: string }>; setModel: (model: string) => Promise; - setService: ( - service: WhisperConfigType["service"] - ) => Promise; transcribe: ( params: { file?: string; diff --git a/enjoy/src/types/enums.ts b/enjoy/src/types/enums.ts new file mode 100644 index 00000000..b162ec1a --- /dev/null +++ b/enjoy/src/types/enums.ts @@ -0,0 +1,27 @@ +export enum UserSettingKeyEnum { + PROFILE = "profile", + LANGUAGE = "language", + NATIVE_LANGUAGE = "native_language", + LEARNING_LANGUAGE = "learning_language", + WHISPER = "whisper", + OPENAI = "openai", + HOTKEYS = "hotkeys", + GPT_ENGINE = "gpt_engine", + STT_ENGINE = "stt_engine", + VOCABULARY = "vocabulary", + DICTS = "dicts", + RECORDER = "recorder", +} + +export enum SttEngineOptionEnum { + LOCAL = "local", + ENJOY_AZURE = "enjoy_azure", + ENJOY_CLOUDFLARE = "enjoy_cloudflare", + OPENAI = "openai", +} + +export enum AppSettingsKeyEnum { + LIBRARY = "library", + USER = "user", + API_URL = "api_url", +} diff --git a/enjoy/src/types/index.d.ts b/enjoy/src/types/index.d.ts index d1708a47..5637ef3f 100644 --- a/enjoy/src/types/index.d.ts +++ b/enjoy/src/types/index.d.ts @@ -40,7 +40,7 @@ type NotificationType = { }; type WhisperConfigType = { - service: "local" | "azure" | "cloudflare" | "openai"; + // service: "local" | "azure" | "cloudflare" | "openai"; availableModels: { type: string; name: string; diff --git a/enjoy/src/types/user.d.ts b/enjoy/src/types/user.d.ts index e1f1164f..4f41acab 100644 --- a/enjoy/src/types/user.d.ts +++ b/enjoy/src/types/user.d.ts @@ -1,6 +1,6 @@ type UserType = { id: string; - name: string; + name?: string; email?: string; balance?: number; avatarUrl?: string; diff --git a/yarn.lock b/yarn.lock index 00e66f2a..e62d777b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,11 +15,11 @@ __metadata: markdown-it-mathjax3: "npm:^4.3.2" markdown-it-sub: "npm:^2.0.0" markdown-it-sup: "npm:^2.0.0" - mermaid: "npm:^11.0.2" - sass: "npm:^1.77.8" + mermaid: "npm:^11.1.0" + sass: "npm:^1.78.0" vitepress: "npm:^1.3.4" vitepress-plugin-mermaid: "npm:^2.0.16" - vue: "npm:^3.4.38" + vue: "npm:^3.5.3" languageName: unknown linkType: soft @@ -27,14 +27,14 @@ __metadata: version: 0.0.0-use.local resolution: "1000h-portal@workspace:1000h-portal" dependencies: - "@nuxtjs/seo": "npm:^2.0.0-rc.19" + "@nuxtjs/seo": "npm:^2.0.0-rc.21" autoprefixer: "npm:^10.4.20" - nuxt: "npm:^3.13.0" - nuxt-og-image: "npm:^3.0.0-rc.65" - postcss: "npm:^8.4.43" - sass: "npm:^1.77.8" + nuxt: "npm:^3.13.1" + nuxt-og-image: "npm:^3.0.0-rc.66" + postcss: "npm:^8.4.45" + sass: "npm:^1.78.0" tailwindcss: "npm:^3.4.10" - vue: "npm:^3.4.38" + vue: "npm:^3.5.3" vue-router: "npm:^4.4.3" languageName: unknown linkType: soft @@ -262,6 +262,16 @@ __metadata: languageName: node linkType: hard +"@antfu/install-pkg@npm:^0.4.0": + version: 0.4.1 + resolution: "@antfu/install-pkg@npm:0.4.1" + dependencies: + package-manager-detector: "npm:^0.2.0" + tinyexec: "npm:^0.3.0" + checksum: 10c0/af47a84e77f3f69077ec464e0a9e82791666693380fc8ed9867f388f5c0cd8421e2642b9deefc7d4adb7b8cfb9dd1a715b25f9a974d023b10779cad0885439ef + languageName: node + linkType: hard + "@antfu/utils@npm:^0.7.10, @antfu/utils@npm:^0.7.7": version: 0.7.10 resolution: "@antfu/utils@npm:0.7.10" @@ -2967,6 +2977,28 @@ __metadata: languageName: node linkType: hard +"@iconify/types@npm:^2.0.0": + version: 2.0.0 + resolution: "@iconify/types@npm:2.0.0" + checksum: 10c0/65a3be43500c7ccacf360e136d00e1717f050b7b91da644e94370256ac66f582d59212bdb30d00788aab4fc078262e91c95b805d1808d654b72f6d2072a7e4b2 + languageName: node + linkType: hard + +"@iconify/utils@npm:^2.1.32": + version: 2.1.32 + resolution: "@iconify/utils@npm:2.1.32" + dependencies: + "@antfu/install-pkg": "npm:^0.4.0" + "@antfu/utils": "npm:^0.7.10" + "@iconify/types": "npm:^2.0.0" + debug: "npm:^4.3.6" + kolorist: "npm:^1.8.0" + local-pkg: "npm:^0.5.0" + mlly: "npm:^1.7.1" + checksum: 10c0/a84f14e2faa6ad4d7ec09ca445cf7a08491c2bc29fd61b926dbf2aa2f0d4f55d68e03344aa558574234b793a6e0a6c597a639bd18589c728500c2e67ef0e2d44 + languageName: node + linkType: hard + "@ioredis/commands@npm:^1.1.1": version: 1.2.0 resolution: "@ioredis/commands@npm:1.2.0" @@ -3082,9 +3114,9 @@ __metadata: languageName: node linkType: hard -"@langchain/community@npm:^0.2.31": - version: 0.2.31 - resolution: "@langchain/community@npm:0.2.31" +"@langchain/community@npm:^0.2.32": + version: 0.2.32 + resolution: "@langchain/community@npm:0.2.32" dependencies: "@langchain/core": "npm:>=0.2.21 <0.3.0" "@langchain/openai": "npm:>=0.2.0 <0.3.0" @@ -3459,7 +3491,7 @@ __metadata: optional: true youtubei.js: optional: true - checksum: 10c0/3c3a8acc4013062eecdfa05f984b8c95fa416a033d1684bd1b7c5209904dc9714b29a04dc2fa796aa69856f2e212f766e2d37c5189cfbfe60a2dbe58ab8efcfb + checksum: 10c0/3fca65923e6dafe0e6d9aa1747d53eab5cd88209643ccf2f40a1270abff005479c7ac43b55e0b424bd3a5acce988510f56164038af888490beb49a411f0e6149 languageName: node linkType: hard @@ -3557,12 +3589,12 @@ __metadata: languageName: node linkType: hard -"@mermaid-js/parser@npm:^0.2.0": - version: 0.2.0 - resolution: "@mermaid-js/parser@npm:0.2.0" +"@mermaid-js/parser@npm:^0.3.0": + version: 0.3.0 + resolution: "@mermaid-js/parser@npm:0.3.0" dependencies: langium: "npm:3.0.0" - checksum: 10c0/0893bf909c6b6c9bc6e4a3135f85ee2ef69a0c29a855ff4f5d740bc08a17764af5dbc20dd25d38a331e3639a31dfd6328c1bae7ecd0ef3d4dbe30602594e30f5 + checksum: 10c0/88c08fb20256ce779fea2151500c017bffd8a970b8d2c6ead81b5ff14787877b16c75b43f503dd5365e4eb33d0b7d5a7d9fff852cff56eb67b3b6508f44576b7 languageName: node linkType: hard @@ -3702,7 +3734,20 @@ __metadata: languageName: node linkType: hard -"@nuxt/devtools-kit@npm:1.3.14, @nuxt/devtools-kit@npm:^1.3.9": +"@nuxt/devtools-kit@npm:1.4.1, @nuxt/devtools-kit@npm:^1.4.1": + version: 1.4.1 + resolution: "@nuxt/devtools-kit@npm:1.4.1" + dependencies: + "@nuxt/kit": "npm:^3.13.0" + "@nuxt/schema": "npm:^3.13.0" + execa: "npm:^7.2.0" + peerDependencies: + vite: "*" + checksum: 10c0/c4a001db1a879bc8bf557f9dd05326736750f9131c1db12100a6532d2254dd66c591deef59d5b23a3f30d0cdd2c48371ef6fbc37cebf38838437e8aa25ccaf71 + languageName: node + linkType: hard + +"@nuxt/devtools-kit@npm:^1.3.9": version: 1.3.14 resolution: "@nuxt/devtools-kit@npm:1.3.14" dependencies: @@ -3715,9 +3760,9 @@ __metadata: languageName: node linkType: hard -"@nuxt/devtools-wizard@npm:1.3.14": - version: 1.3.14 - resolution: "@nuxt/devtools-wizard@npm:1.3.14" +"@nuxt/devtools-wizard@npm:1.4.1": + version: 1.4.1 + resolution: "@nuxt/devtools-wizard@npm:1.4.1" dependencies: consola: "npm:^3.2.3" diff: "npm:^5.2.0" @@ -3725,24 +3770,24 @@ __metadata: global-directory: "npm:^4.0.1" magicast: "npm:^0.3.4" pathe: "npm:^1.1.2" - pkg-types: "npm:^1.1.3" + pkg-types: "npm:^1.2.0" prompts: "npm:^2.4.2" rc9: "npm:^2.1.2" semver: "npm:^7.6.3" bin: devtools-wizard: cli.mjs - checksum: 10c0/95eca903ee6fcc56f3d7f94fd1731a5807d702052ed9984649e94762d4b81b6bcdb90890733687f6d494bdde96efd801c60ed988dcd9ddf4bf51a17ed9fef957 + checksum: 10c0/a0f4d1ead81e142c010c68522e99081d9e83b1ee16cede6d1a5ebccc3a2e362c5c6a2901962cd25612b5040c5dfdcf01a19fc596ba12b6576e4696e57dfc2bed languageName: node linkType: hard -"@nuxt/devtools@npm:^1.3.14": - version: 1.3.14 - resolution: "@nuxt/devtools@npm:1.3.14" +"@nuxt/devtools@npm:^1.4.1": + version: 1.4.1 + resolution: "@nuxt/devtools@npm:1.4.1" dependencies: "@antfu/utils": "npm:^0.7.10" - "@nuxt/devtools-kit": "npm:1.3.14" - "@nuxt/devtools-wizard": "npm:1.3.14" - "@nuxt/kit": "npm:^3.12.4" + "@nuxt/devtools-kit": "npm:1.4.1" + "@nuxt/devtools-wizard": "npm:1.4.1" + "@nuxt/kit": "npm:^3.13.0" "@vue/devtools-core": "npm:7.3.3" "@vue/devtools-kit": "npm:7.3.3" birpc: "npm:^0.2.17" @@ -3751,7 +3796,6 @@ __metadata: destr: "npm:^2.0.3" error-stack-parser-es: "npm:^0.1.5" execa: "npm:^7.2.0" - fast-glob: "npm:^3.3.2" fast-npm-meta: "npm:^0.2.2" flatted: "npm:^3.3.1" get-port-please: "npm:^3.1.2" @@ -3761,18 +3805,19 @@ __metadata: launch-editor: "npm:^2.8.1" local-pkg: "npm:^0.5.0" magicast: "npm:^0.3.4" - nypm: "npm:^0.3.9" + nypm: "npm:^0.3.11" ohash: "npm:^1.1.3" pathe: "npm:^1.1.2" perfect-debounce: "npm:^1.0.0" - pkg-types: "npm:^1.1.3" + pkg-types: "npm:^1.2.0" rc9: "npm:^2.1.2" scule: "npm:^1.3.0" semver: "npm:^7.6.3" simple-git: "npm:^3.25.0" sirv: "npm:^2.0.4" - unimport: "npm:^3.10.1" - vite-plugin-inspect: "npm:^0.8.6" + tinyglobby: "npm:^0.2.5" + unimport: "npm:^3.11.1" + vite-plugin-inspect: "npm:^0.8.7" vite-plugin-vue-inspector: "npm:^5.1.3" which: "npm:^3.0.1" ws: "npm:^8.18.0" @@ -3780,11 +3825,39 @@ __metadata: vite: "*" bin: devtools: cli.mjs - checksum: 10c0/5500033977ce657cd2d565b2892d216184bd5477d5b3000a14277d9e0de95c5753a52eb378f5e93a6031917f45485aa0c654cac09759c18899b917537338150e + checksum: 10c0/0fdc43ed224ed70760912ca9ad1b1f90c45de49c49ba0178a5794a22d86712ac5a36085efcd4543e3c055dfd0948f0b3a68e23528d1f36b35839892e3d99c880 languageName: node linkType: hard -"@nuxt/kit@npm:3.13.0, @nuxt/kit@npm:^3.11.2, @nuxt/kit@npm:^3.12.3, @nuxt/kit@npm:^3.12.4": +"@nuxt/kit@npm:3.13.1, @nuxt/kit@npm:^3.13.0": + version: 3.13.1 + resolution: "@nuxt/kit@npm:3.13.1" + dependencies: + "@nuxt/schema": "npm:3.13.1" + c12: "npm:^1.11.2" + consola: "npm:^3.2.3" + defu: "npm:^6.1.4" + destr: "npm:^2.0.3" + globby: "npm:^14.0.2" + hash-sum: "npm:^2.0.0" + ignore: "npm:^5.3.2" + jiti: "npm:^1.21.6" + klona: "npm:^2.0.6" + knitwork: "npm:^1.1.0" + mlly: "npm:^1.7.1" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.2.0" + scule: "npm:^1.3.0" + semver: "npm:^7.6.3" + ufo: "npm:^1.5.4" + unctx: "npm:^2.3.1" + unimport: "npm:^3.11.1" + untyped: "npm:^1.4.2" + checksum: 10c0/b34b06d441cada54d7ae7919f7652e95eb1713b35051e2793a72bafd52e30a1f17a76bcc7905e3596d6f7f0dface0cddab885d0c2ba37ee4077fad9033dbd54a + languageName: node + linkType: hard + +"@nuxt/kit@npm:^3.11.2, @nuxt/kit@npm:^3.12.3, @nuxt/kit@npm:^3.12.4": version: 3.13.0 resolution: "@nuxt/kit@npm:3.13.0" dependencies: @@ -3832,6 +3905,26 @@ __metadata: languageName: node linkType: hard +"@nuxt/schema@npm:3.13.1, @nuxt/schema@npm:^3.13.0": + version: 3.13.1 + resolution: "@nuxt/schema@npm:3.13.1" + dependencies: + compatx: "npm:^0.1.8" + consola: "npm:^3.2.3" + defu: "npm:^6.1.4" + hookable: "npm:^5.5.3" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.2.0" + scule: "npm:^1.3.0" + std-env: "npm:^3.7.0" + ufo: "npm:^1.5.4" + uncrypto: "npm:^0.1.3" + unimport: "npm:^3.11.1" + untyped: "npm:^1.4.2" + checksum: 10c0/606719a9f42ded88f6e0d123af3835014ef0dcdc630c97f551fe3dee5aec31d615532f4e522f18d3623aaf13c41fdb201a95b2326508214c03a552438601545d + languageName: node + linkType: hard + "@nuxt/telemetry@npm:^2.5.4": version: 2.5.4 resolution: "@nuxt/telemetry@npm:2.5.4" @@ -3859,13 +3952,13 @@ __metadata: languageName: node linkType: hard -"@nuxt/vite-builder@npm:3.13.0": - version: 3.13.0 - resolution: "@nuxt/vite-builder@npm:3.13.0" +"@nuxt/vite-builder@npm:3.13.1": + version: 3.13.1 + resolution: "@nuxt/vite-builder@npm:3.13.1" dependencies: - "@nuxt/kit": "npm:3.13.0" + "@nuxt/kit": "npm:3.13.1" "@rollup/plugin-replace": "npm:^5.0.7" - "@vitejs/plugin-vue": "npm:^5.1.2" + "@vitejs/plugin-vue": "npm:^5.1.3" "@vitejs/plugin-vue-jsx": "npm:^4.0.1" autoprefixer: "npm:^10.4.20" clear: "npm:^0.1.0" @@ -3884,83 +3977,83 @@ __metadata: ohash: "npm:^1.1.3" pathe: "npm:^1.1.2" perfect-debounce: "npm:^1.0.0" - pkg-types: "npm:^1.1.3" - postcss: "npm:^8.4.41" + pkg-types: "npm:^1.2.0" + postcss: "npm:^8.4.44" rollup-plugin-visualizer: "npm:^5.12.0" std-env: "npm:^3.7.0" strip-literal: "npm:^2.1.0" ufo: "npm:^1.5.4" unenv: "npm:^1.10.0" - unplugin: "npm:^1.12.2" - vite: "npm:^5.4.2" + unplugin: "npm:^1.12.3" + vite: "npm:^5.4.3" vite-node: "npm:^2.0.5" vite-plugin-checker: "npm:^0.7.2" vue-bundle-renderer: "npm:^2.1.0" peerDependencies: vue: ^3.3.4 - checksum: 10c0/9bec6a61c236bdc2a33bc946d3ba942cc7b2b35d81dcf8690068b95038f2bbca1efaa025fb0e6b53520c5d82bb5b682eabdd1a2963f868ea956e6f0afacb2f01 + checksum: 10c0/002ca44bd8075ffd42c89a1246836a4f36dda8ad9cbc090b63216037d43b0afab10a5a75aaa59ecdd071c02e16a975d2e09a6a98ec483a41cf295787ac49053f languageName: node linkType: hard -"@nuxtjs/robots@npm:^4.1.3": - version: 4.1.3 - resolution: "@nuxtjs/robots@npm:4.1.3" +"@nuxtjs/robots@npm:^4.1.6": + version: 4.1.6 + resolution: "@nuxtjs/robots@npm:4.1.6" dependencies: - "@nuxt/devtools-kit": "npm:^1.3.9" - "@nuxt/kit": "npm:^3.12.4" + "@nuxt/devtools-kit": "npm:^1.4.1" + "@nuxt/kit": "npm:^3.13.0" consola: "npm:^3.2.3" defu: "npm:^6.1.4" - nuxt-site-config: "npm:^2.2.15" - nuxt-site-config-kit: "npm:^2.2.15" + nuxt-site-config: "npm:^2.2.16" + nuxt-site-config-kit: "npm:^2.2.16" pathe: "npm:^1.1.2" - pkg-types: "npm:^1.1.3" + pkg-types: "npm:^1.2.0" sirv: "npm:^2.0.4" std-env: "npm:^3.7.0" ufo: "npm:^1.5.4" - checksum: 10c0/f59debd23f3bd6398a686529ab8d4579901ae5d5b42322e8ce8d64f0054b6803a7b252131f2c9bf7d54bea22cec7ccadc485a12eb10ffda1a249e310df45d389 + checksum: 10c0/4627970b2b091c1f006530c84903ec0bd17f4cd5273568bdd83cc393e2bdbcf670c7e20a250955378fdea02016a4351e257daa1cfc97f9896ff39b0b619d4247 languageName: node linkType: hard -"@nuxtjs/seo@npm:^2.0.0-rc.19": - version: 2.0.0-rc.19 - resolution: "@nuxtjs/seo@npm:2.0.0-rc.19" +"@nuxtjs/seo@npm:^2.0.0-rc.21": + version: 2.0.0-rc.21 + resolution: "@nuxtjs/seo@npm:2.0.0-rc.21" dependencies: - "@nuxt/kit": "npm:^3.12.3" - "@nuxtjs/robots": "npm:^4.1.3" - "@nuxtjs/sitemap": "npm:6.0.0-beta.2" + "@nuxt/kit": "npm:^3.13.0" + "@nuxtjs/robots": "npm:^4.1.6" + "@nuxtjs/sitemap": "npm:6.0.0" defu: "npm:^6.1.4" - nuxt-link-checker: "npm:^3.0.2" - nuxt-og-image: "npm:3.0.0-rc.65" + nuxt-link-checker: "npm:^3.1.1" + nuxt-og-image: "npm:3.0.0-rc.66" nuxt-schema-org: "npm:^3.3.9" - nuxt-seo-experiments: "npm:^4.0.0" - nuxt-site-config: "npm:^2.2.15" - nuxt-site-config-kit: "npm:^2.2.15" - pkg-types: "npm:^1.1.3" + nuxt-seo-experiments: "npm:^4.0.1" + nuxt-site-config: "npm:^2.2.16" + nuxt-site-config-kit: "npm:^2.2.16" + pkg-types: "npm:^1.2.0" ufo: "npm:^1.5.4" - checksum: 10c0/27f136c75cfb342b3d3410a30c4740a3f5d2fec7df0f6ef0b01d64d1403730667bed03fd5df4a872427cc7c1be117d1ed946737c1250693fdcd469cb607cdf32 + checksum: 10c0/ab2ce175fc88204afefe3258c2b81e8880e9a4baed0e931fe2b5bd0930c3cfd304326f8cb61a38cf502136081891aaa0f668458ae0ebc8a160a9bafd461bd6da languageName: node linkType: hard -"@nuxtjs/sitemap@npm:6.0.0-beta.2": - version: 6.0.0-beta.2 - resolution: "@nuxtjs/sitemap@npm:6.0.0-beta.2" +"@nuxtjs/sitemap@npm:6.0.0": + version: 6.0.0 + resolution: "@nuxtjs/sitemap@npm:6.0.0" dependencies: - "@nuxt/devtools-kit": "npm:^1.3.9" - "@nuxt/kit": "npm:^3.12.4" + "@nuxt/devtools-kit": "npm:^1.4.1" + "@nuxt/kit": "npm:^3.13.0" chalk: "npm:^5.3.0" defu: "npm:^6.1.4" h3-compression: "npm:^0.3.2" - nuxt-site-config: "npm:^2.2.15" - nuxt-site-config-kit: "npm:^2.2.15" + nuxt-site-config: "npm:^2.2.16" + nuxt-site-config-kit: "npm:^2.2.16" ofetch: "npm:^1.3.4" pathe: "npm:^1.1.2" - pkg-types: "npm:^1.1.3" + pkg-types: "npm:^1.2.0" radix3: "npm:^1.1.2" semver: "npm:^7.6.3" sirv: "npm:^2.0.4" - site-config-stack: "npm:^2.2.15" + site-config-stack: "npm:^2.2.16" ufo: "npm:^1.5.4" - checksum: 10c0/d1e4396ef6f4c79f239fe5fb111e14621628486dde1e5fbe850f5663966736260c3c98f3eb52620958945d5aa2a2bc0cef591fcc057de40f1cfa92040d6122d6 + checksum: 10c0/3fd183f9edb914801f7b92040f624841db38dd941904842c06a4e4a3c5a2c377eb3efaffb8c5432dab115da44a222f0c810beae28341bfad89cea734a99cbb3c languageName: node linkType: hard @@ -4877,14 +4970,14 @@ __metadata: languageName: node linkType: hard -"@playwright/test@npm:^1.46.1": - version: 1.46.1 - resolution: "@playwright/test@npm:1.46.1" +"@playwright/test@npm:^1.47.0": + version: 1.47.0 + resolution: "@playwright/test@npm:1.47.0" dependencies: - playwright: "npm:1.46.1" + playwright: "npm:1.47.0" bin: playwright: cli.js - checksum: 10c0/b2d33f33bedfa5a5c72cfc5ee212dfbf531d9c46320b0af901e71ab61b96845e43cc636181b33b3faae27f2f87ce2d44017ac65235bf9c95209f476477b16f93 + checksum: 10c0/8fcfcb36b3aef3171d5db6c6b5a505823b50fbb09d571f1b176acc032c08d406e7fe441e3816e6955383a07fb9808f555ab6264fa580f5e7bb15a20b8c2a0f31 languageName: node linkType: hard @@ -6757,6 +6850,13 @@ __metadata: languageName: node linkType: hard +"@rtsao/scc@npm:^1.1.0": + version: 1.1.0 + resolution: "@rtsao/scc@npm:1.1.0" + checksum: 10c0/b5bcfb0d87f7d1c1c7c0f7693f53b07866ed9fec4c34a97a8c948fb9a7c0082e416ce4d3b60beb4f5e167cbe04cdeefbf6771320f3ede059b9ce91188c409a5b + languageName: node + linkType: hard + "@rushstack/node-core-library@npm:5.7.0": version: 5.7.0 resolution: "@rushstack/node-core-library@npm:5.7.0" @@ -8102,12 +8202,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^22.5.2": - version: 22.5.2 - resolution: "@types/node@npm:22.5.2" +"@types/node@npm:^22.5.4": + version: 22.5.4 + resolution: "@types/node@npm:22.5.4" dependencies: undici-types: "npm:~6.19.2" - checksum: 10c0/624a7fd76229eacc6c158eb3b9afd55b811d7f01976c5f92c630d5b9d47047cc218928c343988484a165ac400e5eb6fe70ea300fc7242deeb0e920c7724290f6 + checksum: 10c0/b445daa7eecd761ad4d778b882d6ff7bcc3b4baad2086ea9804db7c5d4a4ab0298b00d7f5315fc640a73b5a1d52bbf9628e09c9fec0cf44dbf9b4df674a8717d languageName: node linkType: hard @@ -8156,7 +8256,7 @@ __metadata: languageName: node linkType: hard -"@types/qs@npm:^6.9.7": +"@types/qs@npm:^6.9.15": version: 6.9.15 resolution: "@types/qs@npm:6.9.15" checksum: 10c0/49c5ff75ca3adb18a1939310042d273c9fc55920861bd8e5100c8a923b3cda90d759e1a95e18334092da1c8f7b820084687770c83a1ccef04fb2c6908117c823 @@ -8305,15 +8405,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/eslint-plugin@npm:8.3.0" +"@typescript-eslint/eslint-plugin@npm:^8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.4.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.3.0" - "@typescript-eslint/type-utils": "npm:8.3.0" - "@typescript-eslint/utils": "npm:8.3.0" - "@typescript-eslint/visitor-keys": "npm:8.3.0" + "@typescript-eslint/scope-manager": "npm:8.4.0" + "@typescript-eslint/type-utils": "npm:8.4.0" + "@typescript-eslint/utils": "npm:8.4.0" + "@typescript-eslint/visitor-keys": "npm:8.4.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -8324,66 +8424,66 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/d5242b16b8602ab5817cf04b35ac7208b6bee530730eeed6eab886667d1f2c5fac1537b3e33c453393090a1c6fcd50f727c07f5168985a00e7d23d1f99576988 + checksum: 10c0/c75e9bb176e9e0277c9f9c4c006bc2c31ac91984e555de1390a9bbe876e3b6787d59d96015b3f0cd083fd22c814aea4ed4858910d3afdd24d64ab79815da31e5 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/parser@npm:8.3.0" +"@typescript-eslint/parser@npm:^8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/parser@npm:8.4.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.3.0" - "@typescript-eslint/types": "npm:8.3.0" - "@typescript-eslint/typescript-estree": "npm:8.3.0" - "@typescript-eslint/visitor-keys": "npm:8.3.0" + "@typescript-eslint/scope-manager": "npm:8.4.0" + "@typescript-eslint/types": "npm:8.4.0" + "@typescript-eslint/typescript-estree": "npm:8.4.0" + "@typescript-eslint/visitor-keys": "npm:8.4.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/8185e7f1f570cded8719cfb1e8147fcbbc5b8796de628d68024d2929ce6fb02d1f6101b741161229e877be1c30c720701e1e1f7c4313dba33d4bb1190a85f705 + checksum: 10c0/19f3358e5bc4bbad693183eefe1a90ea64be054a934bc2c8a972ff4738b94580b55ad4955af5797db42298628caa59b3ba3f9fd960582b5fc2c836da3a4578a5 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/scope-manager@npm:8.3.0" +"@typescript-eslint/scope-manager@npm:8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/scope-manager@npm:8.4.0" dependencies: - "@typescript-eslint/types": "npm:8.3.0" - "@typescript-eslint/visitor-keys": "npm:8.3.0" - checksum: 10c0/24d093505d444a07db88f9ab44af04eb738ce523ac3f98b0a641cf3a3ee38d18aff9f72bbf2b2e2d9f45e57c973f31016f1e224cd8ab773f6e7c3477c5a09ad3 + "@typescript-eslint/types": "npm:8.4.0" + "@typescript-eslint/visitor-keys": "npm:8.4.0" + checksum: 10c0/95188c663df7db106529c6b93c4c7c61647ed34ab6dd48114e41ddf49140ff606c5501ce2ae451a988ec49b5d3874ea96ff212fc102802327b10affd2ff80a37 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/type-utils@npm:8.3.0" +"@typescript-eslint/type-utils@npm:8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/type-utils@npm:8.4.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.3.0" - "@typescript-eslint/utils": "npm:8.3.0" + "@typescript-eslint/typescript-estree": "npm:8.4.0" + "@typescript-eslint/utils": "npm:8.4.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependenciesMeta: typescript: optional: true - checksum: 10c0/0e4b42ff2bfcd1727893bb7fe5fcf1aa808b45b5f690c249c68ce7aff68ddfba3d8b1565de2f08972915df23fa7ab114c09f507668e9b0b63faf1e34a5091706 + checksum: 10c0/ae51100594d9ca61c7577b5aed0bd10c1959725df5c38cd9653eed1fd3dbdfff9146b6e48f3409994b4c8d781b9d95025c36b30f73a5a1b3dbdee6d142cecc87 languageName: node linkType: hard -"@typescript-eslint/types@npm:8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/types@npm:8.3.0" - checksum: 10c0/5cd733af7ffa0cdaa5842f6c5e275b3a5c9b98dc49bf1bb9df1f0b51d346bef2a10a827d886f60492d502218a272e935cef50b4f7c69100217d5b10a2499c7b1 +"@typescript-eslint/types@npm:8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/types@npm:8.4.0" + checksum: 10c0/15e09ced84827c349553530a31822f06ae5bad456c03d561b7d0c64b6ad9b5d7ca795e030bd93e65d5a2cd41bfde36ed08dcd2ff9feaa8b60a67080827f47ecb languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/typescript-estree@npm:8.3.0" +"@typescript-eslint/typescript-estree@npm:8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.4.0" dependencies: - "@typescript-eslint/types": "npm:8.3.0" - "@typescript-eslint/visitor-keys": "npm:8.3.0" + "@typescript-eslint/types": "npm:8.4.0" + "@typescript-eslint/visitor-keys": "npm:8.4.0" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -8393,31 +8493,31 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/dd73aa1a9d7b5c7e6238e766e6ecdb6d87a9b28a24815258b7bbdc59c49fb525d3fe15d9b7c672e2220678f9d5fabdd9615e4cd5ee97a102fd46023ec0735d50 + checksum: 10c0/170702b024121cff9268f53de8054796b0ce025f9a78d6f2bc850a360e5f3f7032ba3ee9d4b7392726308273a5f3ade5ab31b1788b504b514bc15afc07302b37 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/utils@npm:8.3.0" +"@typescript-eslint/utils@npm:8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/utils@npm:8.4.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:8.3.0" - "@typescript-eslint/types": "npm:8.3.0" - "@typescript-eslint/typescript-estree": "npm:8.3.0" + "@typescript-eslint/scope-manager": "npm:8.4.0" + "@typescript-eslint/types": "npm:8.4.0" + "@typescript-eslint/typescript-estree": "npm:8.4.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 - checksum: 10c0/e4e9e820cf4b4775bb66b2293a2a827897edaba88577b63df317b50752a01d542be521cc4842976fbbd93e08b9e273ce9d20e23768d06de68a83d68cc0f68a93 + checksum: 10c0/8c9c36b3aa23f9bcc28cc4b10f0fa2996f1bc6cdd75135f08c2ef734baa30dbd2a8b92f344b90518e1fd07a486936734789fc7e90b780221a7707dad8e9c9364 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.3.0": - version: 8.3.0 - resolution: "@typescript-eslint/visitor-keys@npm:8.3.0" +"@typescript-eslint/visitor-keys@npm:8.4.0": + version: 8.4.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.4.0" dependencies: - "@typescript-eslint/types": "npm:8.3.0" + "@typescript-eslint/types": "npm:8.4.0" eslint-visitor-keys: "npm:^3.4.3" - checksum: 10c0/4c19216636f2cc25026fe20d2832d857f05c262eba78bc4159121c696199e44cac68443565959f9336372f7686a14b452867300cf4deb3c0507b8dbde88ac0e6 + checksum: 10c0/339199b7fbb9ac83b530d03ab25f6bc5ceb688c9cd0ae460112cd14ee78ca7284a845aef5620cdf70170980123475ec875e85ebf595c60255ba3c0d6fe48c714 languageName: node linkType: hard @@ -8438,29 +8538,29 @@ __metadata: languageName: node linkType: hard -"@unhead/addons@npm:^1.9.16": - version: 1.10.0 - resolution: "@unhead/addons@npm:1.10.0" +"@unhead/addons@npm:^1.10.0": + version: 1.10.4 + resolution: "@unhead/addons@npm:1.10.4" dependencies: "@rollup/pluginutils": "npm:^5.1.0" - "@unhead/schema": "npm:1.10.0" - "@unhead/shared": "npm:1.10.0" + "@unhead/schema": "npm:1.10.4" + "@unhead/shared": "npm:1.10.4" magic-string: "npm:^0.30.11" mlly: "npm:^1.7.1" ufo: "npm:^1.5.4" - unplugin: "npm:^1.12.2" + unplugin: "npm:^1.12.3" unplugin-ast: "npm:^0.10.0" - checksum: 10c0/e682c592fa6019e830d2b29cd0213e566e68a2299c597666400d6c3243f3a9f5d7e0e3a36c8fe18cf8235ee0edd130fa6c8f4c6f677ad51e3f82635695308f58 + checksum: 10c0/0c7f368f6d3fb3ffbb4897e68fa184c1f5d3be8c7a8c59d0b49c0011aebf9b8a7fe0acfd7907ea08d05132e4a2aa1342e4fafd0f36c2aa593eff25c4da0626f7 languageName: node linkType: hard -"@unhead/dom@npm:1.10.0, @unhead/dom@npm:^1.10.0": - version: 1.10.0 - resolution: "@unhead/dom@npm:1.10.0" +"@unhead/dom@npm:1.10.4, @unhead/dom@npm:^1.10.4": + version: 1.10.4 + resolution: "@unhead/dom@npm:1.10.4" dependencies: - "@unhead/schema": "npm:1.10.0" - "@unhead/shared": "npm:1.10.0" - checksum: 10c0/f26bd129dc1b9b7d17fea8aad9431b854b009f765ef6f28c6be15c3e3403058e65e3fec46ea99adb6f300cfd9e2720118f52891c894e69456c1539cbb161e9cc + "@unhead/schema": "npm:1.10.4" + "@unhead/shared": "npm:1.10.4" + checksum: 10c0/8f8aef7607a806a13946656a1b874ce7aed8583fd724672dc37f48db860fafe0ff48da58e4a9170aa1a17aec70d91168814cfa53f77a790cd51a437a701f2429 languageName: node linkType: hard @@ -8473,94 +8573,94 @@ __metadata: languageName: node linkType: hard -"@unhead/schema@npm:1.10.0": - version: 1.10.0 - resolution: "@unhead/schema@npm:1.10.0" +"@unhead/schema@npm:1.10.4": + version: 1.10.4 + resolution: "@unhead/schema@npm:1.10.4" dependencies: hookable: "npm:^5.5.3" zhead: "npm:^2.2.4" - checksum: 10c0/0023340256279e76a6e5bb3172ec0d3287f2e91980d6ca0e67a179554a0b1a335ef27e9a5f1b1ceefc6039cab223ff2da5b17b967b48c9ebde21568463e0a2b2 + checksum: 10c0/89ef461aa11eea7e0d52812a739c9573fb4fa47b786d627a069739028ae1186ac2327d356606161c179b89c2ddfeab6564bb22f44519d42c364bb9038a315346 languageName: node linkType: hard -"@unhead/shared@npm:1.10.0": - version: 1.10.0 - resolution: "@unhead/shared@npm:1.10.0" +"@unhead/shared@npm:1.10.4": + version: 1.10.4 + resolution: "@unhead/shared@npm:1.10.4" dependencies: - "@unhead/schema": "npm:1.10.0" - checksum: 10c0/9561af0a2c5abeec301b44c5b64728789a1a8776a2715852787e57260560c5bfc4c2728682ded23bd28218b7aeee12a8d7714e336cdfa5f197b41e290c42949b + "@unhead/schema": "npm:1.10.4" + checksum: 10c0/aed05f046e50f09bd3ac81fc1347a6bc7a8bae187a6c374ea64b805d64a768ee2837ed6a87cdbd9e052fce9c4a7f4952eb3681cd2ae93c46ba48a370eaa54629 languageName: node linkType: hard -"@unhead/ssr@npm:^1.10.0": - version: 1.10.0 - resolution: "@unhead/ssr@npm:1.10.0" +"@unhead/ssr@npm:^1.10.4": + version: 1.10.4 + resolution: "@unhead/ssr@npm:1.10.4" dependencies: - "@unhead/schema": "npm:1.10.0" - "@unhead/shared": "npm:1.10.0" - checksum: 10c0/a5f5efd79d3e457300e55b04b3f3d670b3827a681b390af412fc71081839a449c809860bdfcc00712b46c6d6caf080b5f6ef7cc91d6219894680c68d727c9005 + "@unhead/schema": "npm:1.10.4" + "@unhead/shared": "npm:1.10.4" + checksum: 10c0/7055568bf8195c93f2e497639539e4180e2aef98958ef890900fdf3125c429e805624ff7fba75879ee4ffbbea261396e4e10552456bfc62e17618db56395ad36 languageName: node linkType: hard -"@unhead/vue@npm:^1.10.0": - version: 1.10.0 - resolution: "@unhead/vue@npm:1.10.0" +"@unhead/vue@npm:^1.10.4": + version: 1.10.4 + resolution: "@unhead/vue@npm:1.10.4" dependencies: - "@unhead/schema": "npm:1.10.0" - "@unhead/shared": "npm:1.10.0" + "@unhead/schema": "npm:1.10.4" + "@unhead/shared": "npm:1.10.4" hookable: "npm:^5.5.3" - unhead: "npm:1.10.0" + unhead: "npm:1.10.4" peerDependencies: vue: ">=2.7 || >=3" - checksum: 10c0/5337b15a9250f6039ca988d9bea7ef6c775fb2f4ccf4ce47954ee42057544de48b73fb2eb52335c91036e8492efd060a8d0914fc13a15d4d687893a058d9efaa + checksum: 10c0/3bb9d4cf418f953e766c2eeee2ac1df55c623957aae9f175d6c88367582da8584991468d498e957d501897390c4f11e52a9b8680580d7c19961294cbd07af67e languageName: node linkType: hard -"@unocss/core@npm:0.61.9, @unocss/core@npm:^0.61.4, @unocss/core@npm:^0.61.9": - version: 0.61.9 - resolution: "@unocss/core@npm:0.61.9" - checksum: 10c0/55e357985a3aa84ca0e6165cd191a6789cdbbc9157517695cf521fc848b76ea1bcabaadf8c4e3ac06c336136c32f932628e1e635024d993d2e511668692fa469 +"@unocss/core@npm:0.62.3, @unocss/core@npm:^0.62.3": + version: 0.62.3 + resolution: "@unocss/core@npm:0.62.3" + checksum: 10c0/6add7e010fc73ece0b97db88b95d7e31ce5e47cbf73ccd3f367abfc70cde108b4d05a7cd1a8aef6825edae86718d139213f01c22bc1336c4bad6b5574870ebc5 languageName: node linkType: hard -"@unocss/extractor-arbitrary-variants@npm:0.61.9": - version: 0.61.9 - resolution: "@unocss/extractor-arbitrary-variants@npm:0.61.9" +"@unocss/extractor-arbitrary-variants@npm:0.62.3": + version: 0.62.3 + resolution: "@unocss/extractor-arbitrary-variants@npm:0.62.3" dependencies: - "@unocss/core": "npm:0.61.9" - checksum: 10c0/6c621f8571961ba7e32f9611438f289b74054baf7eec0c0d80902594f62c31832f5a0eb1e53a5c5caa27f9affe00350bcdd0ce6e3b1c1c255b585882d93eb99a + "@unocss/core": "npm:0.62.3" + checksum: 10c0/b43e0da7b7c81e8c188a263b43fff48391033e8bb685a210a9e14424cf3f83a27960c52d9b05443625d8c7cf156ab49f29db0fef9f864ee54ed715c0885ac20c languageName: node linkType: hard -"@unocss/preset-mini@npm:0.61.9": - version: 0.61.9 - resolution: "@unocss/preset-mini@npm:0.61.9" +"@unocss/preset-mini@npm:0.62.3": + version: 0.62.3 + resolution: "@unocss/preset-mini@npm:0.62.3" dependencies: - "@unocss/core": "npm:0.61.9" - "@unocss/extractor-arbitrary-variants": "npm:0.61.9" - "@unocss/rule-utils": "npm:0.61.9" - checksum: 10c0/e611dcc2893a74b6860e00ac127e6b5406fec23e169fb45ff7a6e32822c41f8d8e49240507c3cc30a83940525d61dad417c57d7596490fd3cff0ce4655d25fb8 + "@unocss/core": "npm:0.62.3" + "@unocss/extractor-arbitrary-variants": "npm:0.62.3" + "@unocss/rule-utils": "npm:0.62.3" + checksum: 10c0/0e1988bb208380570cabc573b9d15871056661cc4a7c245d6f4040b5af3ac2c45dd60966ba12e73a6c41d203accbc766e1807c04acadd0f8193355bce705a71b languageName: node linkType: hard -"@unocss/preset-wind@npm:^0.61.4": - version: 0.61.9 - resolution: "@unocss/preset-wind@npm:0.61.9" +"@unocss/preset-wind@npm:^0.62.3": + version: 0.62.3 + resolution: "@unocss/preset-wind@npm:0.62.3" dependencies: - "@unocss/core": "npm:0.61.9" - "@unocss/preset-mini": "npm:0.61.9" - "@unocss/rule-utils": "npm:0.61.9" - checksum: 10c0/9cbd2e834540d0d3e686bf3c95fdec1dd4c3607a55b8110e36c0e74e3cecb055c42d91b870577d15696e3ee434960eddc6f8eebd717b535227f6149afb56b0e5 + "@unocss/core": "npm:0.62.3" + "@unocss/preset-mini": "npm:0.62.3" + "@unocss/rule-utils": "npm:0.62.3" + checksum: 10c0/86fcf01d49ce7b712b43304c4d384d48494c66e503c2e9cd99c20bccb3b353698c73c2b63c7d2fbe69fb410c79277fd1754b66edf9057cc07c040d3500135bb0 languageName: node linkType: hard -"@unocss/rule-utils@npm:0.61.9": - version: 0.61.9 - resolution: "@unocss/rule-utils@npm:0.61.9" +"@unocss/rule-utils@npm:0.62.3": + version: 0.62.3 + resolution: "@unocss/rule-utils@npm:0.62.3" dependencies: - "@unocss/core": "npm:^0.61.9" + "@unocss/core": "npm:^0.62.3" magic-string: "npm:^0.30.11" - checksum: 10c0/3499604aff4a39414d29fc98329449650bae15ef663012bcc978143ae284e69d869271ce6298c3f8db376631b08a441c1391782864c8137fbecc5660dfc90162 + checksum: 10c0/42ddd1a24331f7278b199bddde397d440546ca91dba7e42846b7e66e5feac40a5773ce0e941d207fc1f766c6eb61c3045b75bae77769ec9967523f5b77eee60b languageName: node linkType: hard @@ -8638,6 +8738,16 @@ __metadata: languageName: node linkType: hard +"@vitejs/plugin-vue@npm:^5.1.3": + version: 5.1.3 + resolution: "@vitejs/plugin-vue@npm:5.1.3" + peerDependencies: + vite: ^5.0.0 + vue: ^3.2.25 + checksum: 10c0/dfd575782fb3240b878f96b90ed7b630c27df132cd2faac66f13b3da0beb63626236ca0aed242f6060e390998d4335059289509d4bf05fc1c786e4b835b83300 + languageName: node + linkType: hard + "@vue-macros/common@npm:^1.12.2": version: 1.12.2 resolution: "@vue-macros/common@npm:1.12.2" @@ -8716,6 +8826,19 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-core@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/compiler-core@npm:3.5.3" + dependencies: + "@babel/parser": "npm:^7.25.3" + "@vue/shared": "npm:3.5.3" + entities: "npm:^4.5.0" + estree-walker: "npm:^2.0.2" + source-map-js: "npm:^1.2.0" + checksum: 10c0/e96f51e4ad5bc1e7a8862790a18d3e78bd6ae20fc253a38ddc7d055691e203b9a84184722bd8997f354f8e3399302589e8794dafb62b8c0926eac61b8970d59d + languageName: node + linkType: hard + "@vue/compiler-dom@npm:3.4.38, @vue/compiler-dom@npm:^3.3.4": version: 3.4.38 resolution: "@vue/compiler-dom@npm:3.4.38" @@ -8726,6 +8849,16 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-dom@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/compiler-dom@npm:3.5.3" + dependencies: + "@vue/compiler-core": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + checksum: 10c0/6cd55eeec28ef58f98bcf1880d47f4ed690be7cb9dcdc216e2ea0ce2c59f3f6ee2e8968c8db8be56c2b69574575cef87245834324675b03ceab1e957e951b41f + languageName: node + linkType: hard + "@vue/compiler-sfc@npm:3.4.38, @vue/compiler-sfc@npm:^3.4.15, @vue/compiler-sfc@npm:^3.4.34": version: 3.4.38 resolution: "@vue/compiler-sfc@npm:3.4.38" @@ -8743,6 +8876,23 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-sfc@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/compiler-sfc@npm:3.5.3" + dependencies: + "@babel/parser": "npm:^7.25.3" + "@vue/compiler-core": "npm:3.5.3" + "@vue/compiler-dom": "npm:3.5.3" + "@vue/compiler-ssr": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + estree-walker: "npm:^2.0.2" + magic-string: "npm:^0.30.11" + postcss: "npm:^8.4.44" + source-map-js: "npm:^1.2.0" + checksum: 10c0/880857f837b6f2054568b0ca2420cc443bf9d3510f3574e0c4733675b7e8372f987a37eece1c1a72c5834b31e5bd1fd8255a1ce67d685e3f286b08a8836447f8 + languageName: node + linkType: hard + "@vue/compiler-ssr@npm:3.4.38": version: 3.4.38 resolution: "@vue/compiler-ssr@npm:3.4.38" @@ -8753,6 +8903,16 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-ssr@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/compiler-ssr@npm:3.5.3" + dependencies: + "@vue/compiler-dom": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + checksum: 10c0/b18410704c8de802be41c2d6c99888d964a7e0e2e29c79bd1839429a54dc7dca71b2698fe94fab3a4496f8b5f66c676b1d414219a23fb7ef915f2f1795c9ee7d + languageName: node + linkType: hard + "@vue/devtools-api@npm:^6.6.3": version: 6.6.3 resolution: "@vue/devtools-api@npm:6.6.3" @@ -8831,6 +8991,15 @@ __metadata: languageName: node linkType: hard +"@vue/reactivity@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/reactivity@npm:3.5.3" + dependencies: + "@vue/shared": "npm:3.5.3" + checksum: 10c0/c8571bae56c6924735281a4887bb4e69b54c2ef1a8e96837e24e7f9faff7283b3b7ed4732fc101b2761fb9c19f4aaea8b198ada258bd00efed3533503b2ad451 + languageName: node + linkType: hard + "@vue/runtime-core@npm:3.4.38": version: 3.4.38 resolution: "@vue/runtime-core@npm:3.4.38" @@ -8841,6 +9010,16 @@ __metadata: languageName: node linkType: hard +"@vue/runtime-core@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/runtime-core@npm:3.5.3" + dependencies: + "@vue/reactivity": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + checksum: 10c0/82d11b34e2d37dda8989bb25a0b5f37f44ca16c8ade86e56fb45aa23a1ebec869d1563595bf7e74c0f754044636774be0403dcb350787b0bd8fb429a75a1757d + languageName: node + linkType: hard + "@vue/runtime-dom@npm:3.4.38": version: 3.4.38 resolution: "@vue/runtime-dom@npm:3.4.38" @@ -8853,6 +9032,18 @@ __metadata: languageName: node linkType: hard +"@vue/runtime-dom@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/runtime-dom@npm:3.5.3" + dependencies: + "@vue/reactivity": "npm:3.5.3" + "@vue/runtime-core": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + csstype: "npm:^3.1.3" + checksum: 10c0/714c25dc72466cdc7f0592f2802c01ecd23ddcda5e88e72287e9987b54fa25dfa23e98cc59b863ab6253260dfab7a9436b44675f2081c2c5072a97c0395e5bfb + languageName: node + linkType: hard + "@vue/server-renderer@npm:3.4.38": version: 3.4.38 resolution: "@vue/server-renderer@npm:3.4.38" @@ -8865,6 +9056,18 @@ __metadata: languageName: node linkType: hard +"@vue/server-renderer@npm:3.5.3": + version: 3.5.3 + resolution: "@vue/server-renderer@npm:3.5.3" + dependencies: + "@vue/compiler-ssr": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + peerDependencies: + vue: 3.5.3 + checksum: 10c0/7250624e271f3247abb1c0176329f047286153c641a5685533dee0ae8989d431ec73fb934503a38c3a46bddbb3902d021f3102a9f47a5c80dd560a49c2e1b7bd + languageName: node + linkType: hard + "@vue/shared@npm:3.4.38, @vue/shared@npm:^3.4.38": version: 3.4.38 resolution: "@vue/shared@npm:3.4.38" @@ -8872,7 +9075,14 @@ __metadata: languageName: node linkType: hard -"@vueuse/core@npm:11.0.3, @vueuse/core@npm:^11.0.0, @vueuse/core@npm:^11.0.0-beta.2": +"@vue/shared@npm:3.5.3, @vue/shared@npm:^3.5.0": + version: 3.5.3 + resolution: "@vue/shared@npm:3.5.3" + checksum: 10c0/406e2b8431ef2785ac37857b7560ac88f422be35cf069c0775cae48905912d4781795c3814900aba6f6dbd8255bbbfb85dd065a37de64203ef3ba19cbfd7b735 + languageName: node + linkType: hard + +"@vueuse/core@npm:11.0.3, @vueuse/core@npm:^11.0.0, @vueuse/core@npm:^11.0.3": version: 11.0.3 resolution: "@vueuse/core@npm:11.0.3" dependencies: @@ -9399,7 +9609,7 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.7": +"array-includes@npm:^3.1.8": version: 3.1.8 resolution: "array-includes@npm:3.1.8" dependencies: @@ -9413,7 +9623,7 @@ __metadata: languageName: node linkType: hard -"array.prototype.findlastindex@npm:^1.2.3": +"array.prototype.findlastindex@npm:^1.2.5": version: 1.2.5 resolution: "array.prototype.findlastindex@npm:1.2.5" dependencies: @@ -9979,6 +10189,31 @@ __metadata: languageName: node linkType: hard +"c12@npm:^1.11.2": + version: 1.11.2 + resolution: "c12@npm:1.11.2" + dependencies: + chokidar: "npm:^3.6.0" + confbox: "npm:^0.1.7" + defu: "npm:^6.1.4" + dotenv: "npm:^16.4.5" + giget: "npm:^1.2.3" + jiti: "npm:^1.21.6" + mlly: "npm:^1.7.1" + ohash: "npm:^1.1.3" + pathe: "npm:^1.1.2" + perfect-debounce: "npm:^1.0.0" + pkg-types: "npm:^1.2.0" + rc9: "npm:^2.1.2" + peerDependencies: + magicast: ^0.3.4 + peerDependenciesMeta: + magicast: + optional: true + checksum: 10c0/6c805e563b92109d7c4b7f7526b0fcf91c71ff50a30c316c3f77ccd3a8da8135db343ca9d8720b3ce0472ff1ee451edb8decef6e136a3b3af1ec2ec72b647a60 + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -10277,37 +10512,7 @@ __metadata: languageName: node linkType: hard -"cheerio@npm:1.0.0-rc.10": - version: 1.0.0-rc.10 - resolution: "cheerio@npm:1.0.0-rc.10" - dependencies: - cheerio-select: "npm:^1.5.0" - dom-serializer: "npm:^1.3.2" - domhandler: "npm:^4.2.0" - htmlparser2: "npm:^6.1.0" - parse5: "npm:^6.0.1" - parse5-htmlparser2-tree-adapter: "npm:^6.0.1" - tslib: "npm:^2.2.0" - checksum: 10c0/2bb0fae8b1941949f506ddc4df75e3c2d0e5cc6c05478f918dd64a4d2c5282ec84b243890f6a809052a8eb6214641084922c07f726b5287b5dba114b10e52cb9 - languageName: node - linkType: hard - -"cheerio@npm:1.0.0-rc.12": - version: 1.0.0-rc.12 - resolution: "cheerio@npm:1.0.0-rc.12" - dependencies: - cheerio-select: "npm:^2.1.0" - dom-serializer: "npm:^2.0.0" - domhandler: "npm:^5.0.3" - domutils: "npm:^3.0.1" - htmlparser2: "npm:^8.0.1" - parse5: "npm:^7.0.0" - parse5-htmlparser2-tree-adapter: "npm:^7.0.0" - checksum: 10c0/c85d2f2461e3f024345b78e0bb16ad8e41492356210470dd1e7d5a91391da9fcf6c0a7cb48a9ba8820330153f0cedb4d0a60c7af15d96ecdb3092299b9d9c0cc - languageName: node - linkType: hard - -"cheerio@npm:^1.0.0": +"cheerio@npm:1.0.0, cheerio@npm:^1.0.0": version: 1.0.0 resolution: "cheerio@npm:1.0.0" dependencies: @@ -10326,6 +10531,21 @@ __metadata: languageName: node linkType: hard +"cheerio@npm:1.0.0-rc.10": + version: 1.0.0-rc.10 + resolution: "cheerio@npm:1.0.0-rc.10" + dependencies: + cheerio-select: "npm:^1.5.0" + dom-serializer: "npm:^1.3.2" + domhandler: "npm:^4.2.0" + htmlparser2: "npm:^6.1.0" + parse5: "npm:^6.0.1" + parse5-htmlparser2-tree-adapter: "npm:^6.0.1" + tslib: "npm:^2.2.0" + checksum: 10c0/2bb0fae8b1941949f506ddc4df75e3c2d0e5cc6c05478f918dd64a4d2c5282ec84b243890f6a809052a8eb6214641084922c07f726b5287b5dba114b10e52cb9 + languageName: node + linkType: hard + "chevrotain-allstar@npm:~0.3.0": version: 0.3.1 resolution: "chevrotain-allstar@npm:0.3.1" @@ -11246,7 +11466,7 @@ __metadata: languageName: node linkType: hard -"cytoscape-fcose@npm:^2.1.0": +"cytoscape-fcose@npm:^2.1.0, cytoscape-fcose@npm:^2.2.0": version: 2.2.0 resolution: "cytoscape-fcose@npm:2.2.0" dependencies: @@ -12412,10 +12632,10 @@ __metadata: languageName: node linkType: hard -"electron-log@npm:^5.1.7": - version: 5.1.7 - resolution: "electron-log@npm:5.1.7" - checksum: 10c0/a5572c6db6393064c4cc60d921d06248468aa86c3f53845d5fd0aa3815843b2249f96ea1c27696f828b13ed01dcc190350e9969d27a50ec64dcb11b9c6bd7665 +"electron-log@npm:^5.2.0": + version: 5.2.0 + resolution: "electron-log@npm:5.2.0" + checksum: 10c0/a3925dfe11aabc354ffcd5f47875e0a6c9698e61f5231e4df2ca5afb6e5fa905bba41880a16806adb7c95f441fc1e78f7507331feab0a9dfac0c542c62f2df7e languageName: node linkType: hard @@ -12474,16 +12694,16 @@ __metadata: languageName: node linkType: hard -"electron@npm:^32.0.1": - version: 32.0.1 - resolution: "electron@npm:32.0.1" +"electron@npm:^32.0.2": + version: 32.0.2 + resolution: "electron@npm:32.0.2" dependencies: "@electron/get": "npm:^2.0.0" "@types/node": "npm:^20.9.0" extract-zip: "npm:^2.0.1" bin: electron: cli.js - checksum: 10c0/274412b2da617c2489f4cb0bcbd2316a8429adbc07ef8a469e5f312ca3113558b7e9b25fdc78faea95270ee11ed155c5cf4a5b11083342577defcb9b298012d7 + checksum: 10c0/1c8efa61e70f2a06b83da059aebc055f5e75bb30b3ae4e32e83d238f808e075d6aac43265475e22d46b0a9d44ab0f0464b4d534f24e6dc9f7ef150170dab0602 languageName: node linkType: hard @@ -12585,9 +12805,9 @@ __metadata: "@electron-forge/publisher-s3": "npm:^7.4.0" "@electron/fuses": "npm:^1.8.0" "@hookform/resolvers": "npm:^3.9.0" - "@langchain/community": "npm:^0.2.31" + "@langchain/community": "npm:^0.2.32" "@mozilla/readability": "npm:^0.5.0" - "@playwright/test": "npm:^1.46.1" + "@playwright/test": "npm:^1.47.0" "@radix-ui/react-accordion": "npm:^1.2.0" "@radix-ui/react-alert-dialog": "npm:^1.1.1" "@radix-ui/react-aspect-ratio": "npm:^1.1.0" @@ -12627,7 +12847,7 @@ __metadata: "@types/lodash": "npm:^4.17.7" "@types/mark.js": "npm:^8.11.12" "@types/mustache": "npm:^4.2.5" - "@types/node": "npm:^22.5.2" + "@types/node": "npm:^22.5.4" "@types/prop-types": "npm:^15.7.12" "@types/rails__actioncable": "npm:^6.1.11" "@types/react": "npm:^18.3.5" @@ -12635,8 +12855,8 @@ __metadata: "@types/unzipper": "npm:^0.10.10" "@types/validator": "npm:^13.12.1" "@types/wavesurfer.js": "npm:^6.0.12" - "@typescript-eslint/eslint-plugin": "npm:^8.3.0" - "@typescript-eslint/parser": "npm:^8.3.0" + "@typescript-eslint/eslint-plugin": "npm:^8.4.0" + "@typescript-eslint/parser": "npm:^8.4.0" "@uidotdev/usehooks": "npm:^2.4.1" "@vidstack/react": "npm:^1.12.9" "@vitejs/plugin-react": "npm:^4.3.1" @@ -12659,16 +12879,16 @@ __metadata: decamelize: "npm:^6.0.0" decamelize-keys: "npm:^2.0.1" echogarden: "npm:^1.5.0" - electron: "npm:^32.0.1" + electron: "npm:^32.0.2" electron-context-menu: "npm:^4.0.4" electron-devtools-installer: "npm:^3.2.0" - electron-log: "npm:^5.1.7" + electron-log: "npm:^5.2.0" electron-playwright-helpers: "npm:^1.7.1" electron-settings: "npm:^4.0.4" electron-squirrel-startup: "npm:^1.0.1" eslint: "npm:^9.9.1" eslint-import-resolver-typescript: "npm:^3.6.3" - eslint-plugin-import: "npm:^2.29.1" + eslint-plugin-import: "npm:^2.30.0" ffmpeg-static: "npm:^5.2.0" flora-colossus: "npm:^2.0.0" fluent-ffmpeg: "npm:^2.1.3" @@ -12676,20 +12896,20 @@ __metadata: html-to-text: "npm:^9.0.5" https-proxy-agent: "npm:^7.0.5" i18next: "npm:^23.14.0" - intl-tel-input: "npm:^24.3.6" + intl-tel-input: "npm:^24.4.0" js-md5: "npm:^0.8.3" - langchain: "npm:^0.2.17" + langchain: "npm:^0.2.18" lodash: "npm:^4.17.21" - lru-cache: "npm:^11.0.0" - lucide-react: "npm:^0.438.0" + lru-cache: "npm:^11.0.1" + lucide-react: "npm:^0.439.0" mark.js: "npm:^8.11.1" microsoft-cognitiveservices-speech-sdk: "npm:^1.40.0" mustache: "npm:^4.2.0" next-themes: "npm:^0.3.0" octokit: "npm:^4.0.2" - openai: "npm:^4.57.0" + openai: "npm:^4.58.0" pitchfinder: "npm:^2.3.2" - postcss: "npm:^8.4.43" + postcss: "npm:^8.4.45" progress: "npm:^2.0.3" prop-types: "npm:^15.8.1" proxy-agent: "npm:^6.4.0" @@ -12700,7 +12920,7 @@ __metadata: react-dom: "npm:^18.3.1" react-frame-component: "npm:^5.2.7" react-hook-form: "npm:^7.53.0" - react-hotkeys-hook: "npm:^4.5.0" + react-hotkeys-hook: "npm:^4.5.1" react-i18next: "npm:^15.0.1" react-markdown: "npm:^9.0.1" react-resizable-panels: "npm:^2.1.2" @@ -12724,7 +12944,7 @@ __metadata: umzug: "npm:^3.8.1" unzipper: "npm:^0.12.3" update-electron-app: "npm:^3.0.0" - vite: "npm:^5.4.2" + vite: "npm:^5.4.3" vite-plugin-static-copy: "npm:^1.0.6" wavesurfer.js: "npm:^7.8.4" zod: "npm:^3.23.8" @@ -13247,7 +13467,7 @@ __metadata: languageName: node linkType: hard -"eslint-module-utils@npm:^2.8.0, eslint-module-utils@npm:^2.8.1": +"eslint-module-utils@npm:^2.8.1": version: 2.8.2 resolution: "eslint-module-utils@npm:2.8.2" dependencies: @@ -13259,30 +13479,43 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-import@npm:^2.29.1": - version: 2.29.1 - resolution: "eslint-plugin-import@npm:2.29.1" +"eslint-module-utils@npm:^2.9.0": + version: 2.11.0 + resolution: "eslint-module-utils@npm:2.11.0" dependencies: - array-includes: "npm:^3.1.7" - array.prototype.findlastindex: "npm:^1.2.3" + debug: "npm:^3.2.7" + peerDependenciesMeta: + eslint: + optional: true + checksum: 10c0/c1b02e83429878ab22596f17a5ac138e51a520e96a5ef89a5a6698769a2d174ab28302d45eb563c0fc418d21a5842e328c37a6e8f294bf2e64e675ba55203dd7 + languageName: node + linkType: hard + +"eslint-plugin-import@npm:^2.30.0": + version: 2.30.0 + resolution: "eslint-plugin-import@npm:2.30.0" + dependencies: + "@rtsao/scc": "npm:^1.1.0" + array-includes: "npm:^3.1.8" + array.prototype.findlastindex: "npm:^1.2.5" array.prototype.flat: "npm:^1.3.2" array.prototype.flatmap: "npm:^1.3.2" debug: "npm:^3.2.7" doctrine: "npm:^2.1.0" eslint-import-resolver-node: "npm:^0.3.9" - eslint-module-utils: "npm:^2.8.0" - hasown: "npm:^2.0.0" - is-core-module: "npm:^2.13.1" + eslint-module-utils: "npm:^2.9.0" + hasown: "npm:^2.0.2" + is-core-module: "npm:^2.15.1" is-glob: "npm:^4.0.3" minimatch: "npm:^3.1.2" - object.fromentries: "npm:^2.0.7" - object.groupby: "npm:^1.0.1" - object.values: "npm:^1.1.7" + object.fromentries: "npm:^2.0.8" + object.groupby: "npm:^1.0.3" + object.values: "npm:^1.2.0" semver: "npm:^6.3.1" tsconfig-paths: "npm:^3.15.0" peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: 10c0/5f35dfbf4e8e67f741f396987de9504ad125c49f4144508a93282b4ea0127e052bde65ab6def1f31b6ace6d5d430be698333f75bdd7dca3bc14226c92a083196 + checksum: 10c0/4c9dcb1f27505c4d5dd891d2b551f56c70786d136aa3992a77e785bdc67c9f60200a2c7fb0ce55b7647fe550b12bc433d5dfa59e2c00ab44227791c5ab86badf languageName: node linkType: hard @@ -13533,7 +13766,7 @@ __metadata: languageName: node linkType: hard -"execa@npm:^9.3.0": +"execa@npm:^9.3.1": version: 9.3.1 resolution: "execa@npm:9.3.1" dependencies: @@ -13772,6 +14005,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.2.0": + version: 6.3.0 + resolution: "fdir@npm:6.3.0" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/be91cd6ab2edbc6df457a69b79672ee9345996986821918ef01908ce9619b8cbecd9c6c13d4ca5d0aeb548b162050d68c599f45bb3fbff194a91e16f25e646b5 + languageName: node + linkType: hard + "fflate@npm:^0.7.3": version: 0.7.4 resolution: "fflate@npm:0.7.4" @@ -15015,7 +15260,7 @@ __metadata: languageName: node linkType: hard -"htmlparser2@npm:^8.0.1, htmlparser2@npm:^8.0.2": +"htmlparser2@npm:^8.0.2": version: 8.0.2 resolution: "htmlparser2@npm:8.0.2" dependencies: @@ -15314,6 +15559,19 @@ __metadata: languageName: node linkType: hard +"impound@npm:^0.1.0": + version: 0.1.0 + resolution: "impound@npm:0.1.0" + dependencies: + "@rollup/pluginutils": "npm:^5.1.0" + mlly: "npm:^1.7.1" + pathe: "npm:^1.1.2" + unenv: "npm:^1.10.0" + unplugin: "npm:^1.12.2" + checksum: 10c0/c787fa64309c5d843fcefe806f9a2c5fa86808ba668f2e5f77f6404b1eb3baceca3b3d7b512af89b6aa038a3d4aaba94f07fc4b02d812ecdc4033baae78ac160 + languageName: node + linkType: hard + "imul@npm:^1.0.0": version: 1.0.1 resolution: "imul@npm:1.0.1" @@ -15433,10 +15691,10 @@ __metadata: languageName: node linkType: hard -"intl-tel-input@npm:^24.3.6": - version: 24.3.6 - resolution: "intl-tel-input@npm:24.3.6" - checksum: 10c0/274a45783b08f2a01995dad7defcb7e4ef477f8797e882eb47df8981330dcc1d6e328cbe56cd8a8cc262ad8be55583ddcdf9b28a4623b9e9e96103870353cf07 +"intl-tel-input@npm:^24.4.0": + version: 24.4.0 + resolution: "intl-tel-input@npm:24.4.0" + checksum: 10c0/a87e65e0765c605862c168944f66515101e9f6cae43cf75e0bfb04f7ec9f4631703914340ccd01768536d36d117adee8a46b5c864340fecc6bffd58dbc9ca298 languageName: node linkType: hard @@ -15577,7 +15835,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1, is-core-module@npm:^2.5.0": +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.15.1, is-core-module@npm:^2.5.0": version: 2.15.1 resolution: "is-core-module@npm:2.15.1" dependencies: @@ -16363,7 +16621,207 @@ __metadata: languageName: node linkType: hard -"langchain@npm:^0.2.17, langchain@npm:~0.2.3": +"langchain@npm:^0.2.18": + version: 0.2.18 + resolution: "langchain@npm:0.2.18" + dependencies: + "@langchain/core": "npm:>=0.2.21 <0.3.0" + "@langchain/openai": "npm:>=0.1.0 <0.3.0" + "@langchain/textsplitters": "npm:~0.0.0" + binary-extensions: "npm:^2.2.0" + js-tiktoken: "npm:^1.0.12" + js-yaml: "npm:^4.1.0" + jsonpointer: "npm:^5.0.1" + langsmith: "npm:~0.1.40" + openapi-types: "npm:^12.1.3" + p-retry: "npm:4" + uuid: "npm:^10.0.0" + yaml: "npm:^2.2.1" + zod: "npm:^3.22.4" + zod-to-json-schema: "npm:^3.22.3" + peerDependencies: + "@aws-sdk/client-s3": "*" + "@aws-sdk/client-sagemaker-runtime": "*" + "@aws-sdk/client-sfn": "*" + "@aws-sdk/credential-provider-node": "*" + "@azure/storage-blob": "*" + "@browserbasehq/sdk": "*" + "@gomomento/sdk": "*" + "@gomomento/sdk-core": "*" + "@gomomento/sdk-web": ^1.51.1 + "@langchain/anthropic": "*" + "@langchain/aws": "*" + "@langchain/cohere": "*" + "@langchain/community": "*" + "@langchain/google-genai": "*" + "@langchain/google-vertexai": "*" + "@langchain/groq": "*" + "@langchain/mistralai": "*" + "@langchain/ollama": "*" + "@mendable/firecrawl-js": "*" + "@notionhq/client": "*" + "@pinecone-database/pinecone": "*" + "@supabase/supabase-js": "*" + "@vercel/kv": "*" + "@xata.io/client": "*" + apify-client: "*" + assemblyai: "*" + axios: "*" + cheerio: "*" + chromadb: "*" + convex: "*" + couchbase: "*" + d3-dsv: "*" + epub2: "*" + fast-xml-parser: "*" + handlebars: ^4.7.8 + html-to-text: "*" + ignore: "*" + ioredis: "*" + jsdom: "*" + mammoth: "*" + mongodb: "*" + node-llama-cpp: "*" + notion-to-md: "*" + officeparser: "*" + pdf-parse: "*" + peggy: ^3.0.2 + playwright: "*" + puppeteer: "*" + pyodide: ">=0.24.1 <0.27.0" + redis: "*" + sonix-speech-recognition: "*" + srt-parser-2: "*" + typeorm: "*" + weaviate-ts-client: "*" + web-auth-library: "*" + ws: "*" + youtube-transcript: "*" + youtubei.js: "*" + peerDependenciesMeta: + "@aws-sdk/client-s3": + optional: true + "@aws-sdk/client-sagemaker-runtime": + optional: true + "@aws-sdk/client-sfn": + optional: true + "@aws-sdk/credential-provider-node": + optional: true + "@azure/storage-blob": + optional: true + "@browserbasehq/sdk": + optional: true + "@gomomento/sdk": + optional: true + "@gomomento/sdk-core": + optional: true + "@gomomento/sdk-web": + optional: true + "@langchain/anthropic": + optional: true + "@langchain/aws": + optional: true + "@langchain/cohere": + optional: true + "@langchain/community": + optional: true + "@langchain/google-genai": + optional: true + "@langchain/google-vertexai": + optional: true + "@langchain/groq": + optional: true + "@langchain/mistralai": + optional: true + "@langchain/ollama": + optional: true + "@mendable/firecrawl-js": + optional: true + "@notionhq/client": + optional: true + "@pinecone-database/pinecone": + optional: true + "@supabase/supabase-js": + optional: true + "@vercel/kv": + optional: true + "@xata.io/client": + optional: true + apify-client: + optional: true + assemblyai: + optional: true + axios: + optional: true + cheerio: + optional: true + chromadb: + optional: true + convex: + optional: true + couchbase: + optional: true + d3-dsv: + optional: true + epub2: + optional: true + faiss-node: + optional: true + fast-xml-parser: + optional: true + handlebars: + optional: true + html-to-text: + optional: true + ignore: + optional: true + ioredis: + optional: true + jsdom: + optional: true + mammoth: + optional: true + mongodb: + optional: true + node-llama-cpp: + optional: true + notion-to-md: + optional: true + officeparser: + optional: true + pdf-parse: + optional: true + peggy: + optional: true + playwright: + optional: true + puppeteer: + optional: true + pyodide: + optional: true + redis: + optional: true + sonix-speech-recognition: + optional: true + srt-parser-2: + optional: true + typeorm: + optional: true + weaviate-ts-client: + optional: true + web-auth-library: + optional: true + ws: + optional: true + youtube-transcript: + optional: true + youtubei.js: + optional: true + checksum: 10c0/a6dda5246d42601ae00e24321b16965203c9dcb1b9b45681f8a0bd018e32f75cb4a688d93efd3d9aaa55882136b86fc28a598e66bcd48ce6d9386d41b6c180e2 + languageName: node + linkType: hard + +"langchain@npm:~0.2.3": version: 0.2.17 resolution: "langchain@npm:0.2.17" dependencies: @@ -16933,6 +17391,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^11.0.1": + version: 11.0.1 + resolution: "lru-cache@npm:11.0.1" + checksum: 10c0/8bad6603dc67eb5b03520fba05bce5df6473dbba58ac4c6067ed088d29225a0a04416bb1462acd8c1f819d1fbf37920446a1c36bafd9c384bcc54cee0d3b697a + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -16958,12 +17423,12 @@ __metadata: languageName: node linkType: hard -"lucide-react@npm:^0.438.0": - version: 0.438.0 - resolution: "lucide-react@npm:0.438.0" +"lucide-react@npm:^0.439.0": + version: 0.439.0 + resolution: "lucide-react@npm:0.439.0" peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc - checksum: 10c0/d5e9e9130e301b3231fffd3e5fab85975a7523fb5ab160a8125ad6937de713af46d0ac7a96c1ba532b9ff39a14d940aefa9ea41cb30123ff7f8ce88f44cfe2f1 + checksum: 10c0/153c6da6a1bdcc03c47080b00a50a341b7e485ccbb479a742c0fb606355acf93c136630626dad2d57cafe1f5c691dd4f2fcd0f2b7c813e7c9e0e1f065cf62be0 languageName: node linkType: hard @@ -17417,14 +17882,16 @@ __metadata: languageName: node linkType: hard -"mermaid@npm:^11.0.2": - version: 11.0.2 - resolution: "mermaid@npm:11.0.2" +"mermaid@npm:^11.1.0": + version: 11.1.0 + resolution: "mermaid@npm:11.1.0" dependencies: "@braintree/sanitize-url": "npm:^7.0.1" - "@mermaid-js/parser": "npm:^0.2.0" + "@iconify/utils": "npm:^2.1.32" + "@mermaid-js/parser": "npm:^0.3.0" cytoscape: "npm:^3.29.2" cytoscape-cose-bilkent: "npm:^4.1.0" + cytoscape-fcose: "npm:^2.2.0" d3: "npm:^7.9.0" d3-sankey: "npm:^0.12.3" dagre-d3-es: "npm:7.0.10" @@ -17438,7 +17905,7 @@ __metadata: stylis: "npm:^4.3.1" ts-dedent: "npm:^2.2.0" uuid: "npm:^9.0.1" - checksum: 10c0/8a8cbe8e62178660f8753886407615bd942bcc46f70f5a4a2950ed984290b74f1f79c20a2e51660449eed51945c31cbb41ef71ef4661bb0fb30438176e577cc1 + checksum: 10c0/65cf4eff6a35a9bb5d51e542521c7d95f36d517e4dab28b3d98370ea0fdde11fd2dad7e55ec86e427145a5fe256c7f164341ed0c4ca8c8aaf56476603c8b8f74 languageName: node linkType: hard @@ -18204,6 +18671,13 @@ __metadata: languageName: node linkType: hard +"nanotar@npm:^0.1.1": + version: 0.1.1 + resolution: "nanotar@npm:0.1.1" + checksum: 10c0/51df978ddc57fd0b0fa01d58eb6fc1c17780ad4e9c02664f270e3478ac63424d6e323d0cb2ae7debd04a61d06462fdc996f2c8d455bca41c6f7d301860d1df5f + languageName: node + linkType: hard + "napi-build-utils@npm:^1.0.1": version: 1.0.2 resolution: "napi-build-utils@npm:1.0.2" @@ -18631,9 +19105,9 @@ __metadata: languageName: node linkType: hard -"nuxi@npm:^3.12.0": - version: 3.13.0 - resolution: "nuxi@npm:3.13.0" +"nuxi@npm:^3.13.1": + version: 3.13.1 + resolution: "nuxi@npm:3.13.1" dependencies: fsevents: "npm:~2.3.3" dependenciesMeta: @@ -18644,65 +19118,65 @@ __metadata: nuxi-ng: bin/nuxi.mjs nuxt: bin/nuxi.mjs nuxt-cli: bin/nuxi.mjs - checksum: 10c0/0bbfec5b9aac66b77f9c26588652a7e36a2fa8f65e7c37647621f5541c7a28584e7c9d9304734a2452a0da756ade45bbfb2a54e681fdce667057fc78598a9d8b + checksum: 10c0/595338393c95b811b6aa28e13f7c24f39d03184c5954b0fa3d9d9d0177e5c89158ef28213739147e9285944fac1dba3a7f2d2c726e861f3a13326a5035567b00 languageName: node linkType: hard -"nuxt-link-checker@npm:^3.0.2": - version: 3.1.0 - resolution: "nuxt-link-checker@npm:3.1.0" +"nuxt-link-checker@npm:^3.1.1": + version: 3.1.1 + resolution: "nuxt-link-checker@npm:3.1.1" dependencies: - "@nuxt/devtools-kit": "npm:^1.3.9" - "@nuxt/kit": "npm:^3.12.4" - "@vueuse/core": "npm:^11.0.0-beta.2" + "@nuxt/devtools-kit": "npm:^1.4.1" + "@nuxt/kit": "npm:^3.13.0" + "@vueuse/core": "npm:^11.0.3" chalk: "npm:^5.3.0" - cheerio: "npm:1.0.0-rc.12" + cheerio: "npm:1.0.0" diff: "npm:^5.2.0" fuse.js: "npm:^7.0.0" - magic-string: "npm:^0.30.10" - nuxt-site-config: "npm:2.2.15" - nuxt-site-config-kit: "npm:2.2.15" + magic-string: "npm:^0.30.11" + nuxt-site-config: "npm:2.2.16" + nuxt-site-config-kit: "npm:2.2.16" pathe: "npm:^1.1.2" - pkg-types: "npm:^1.1.3" + pkg-types: "npm:^1.2.0" radix3: "npm:^1.1.2" sirv: "npm:^2.0.4" - site-config-stack: "npm:^2.2.15" + site-config-stack: "npm:^2.2.16" ufo: "npm:^1.5.4" - checksum: 10c0/d145d9039a598e0a8652fa31fd23c2f33729f79e086ad5b5ff7cee14e0af214a9f724fb7c19aa06796798fe70f78c7313624e6a997cf1b973db318c598bfd918 + checksum: 10c0/5bb05896711cb63d34f6c6f6cfa5f41ff7280f36f0edb8a6d8663052cec0ac5a4996b1540505b2364badf5cc10eee0af9598c1b3efbd348791c00e4903c86b34 languageName: node linkType: hard -"nuxt-og-image@npm:3.0.0-rc.65, nuxt-og-image@npm:^3.0.0-rc.65": - version: 3.0.0-rc.65 - resolution: "nuxt-og-image@npm:3.0.0-rc.65" +"nuxt-og-image@npm:3.0.0-rc.66, nuxt-og-image@npm:^3.0.0-rc.66": + version: 3.0.0-rc.66 + resolution: "nuxt-og-image@npm:3.0.0-rc.66" dependencies: - "@nuxt/devtools-kit": "npm:^1.3.9" - "@nuxt/kit": "npm:^3.12.3" + "@nuxt/devtools-kit": "npm:^1.4.1" + "@nuxt/kit": "npm:^3.13.0" "@resvg/resvg-js": "npm:^2.6.2" "@resvg/resvg-wasm": "npm:^2.6.2" - "@unocss/core": "npm:^0.61.4" - "@unocss/preset-wind": "npm:^0.61.4" + "@unocss/core": "npm:^0.62.3" + "@unocss/preset-wind": "npm:^0.62.3" chrome-launcher: "npm:^1.1.2" defu: "npm:^6.1.4" - execa: "npm:^9.3.0" + execa: "npm:^9.3.1" image-size: "npm:^1.1.1" - nuxt-site-config: "npm:^2.2.14" - nuxt-site-config-kit: "npm:^2.2.14" - nypm: "npm:^0.3.9" + nuxt-site-config: "npm:^2.2.16" + nuxt-site-config-kit: "npm:^2.2.16" + nypm: "npm:^0.3.11" ofetch: "npm:^1.3.4" ohash: "npm:^1.1.3" pathe: "npm:^1.1.2" - pkg-types: "npm:^1.1.3" - playwright-core: "npm:^1.45.2" + pkg-types: "npm:^1.2.0" + playwright-core: "npm:^1.46.1" radix3: "npm:^1.1.2" satori: "npm:^0.10.14" satori-html: "npm:^0.3.2" sirv: "npm:^2.0.4" std-env: "npm:^3.7.0" - ufo: "npm:^1.5.3" + ufo: "npm:^1.5.4" unwasm: "npm:^0.3.9" yoga-wasm-web: "npm:^0.3.3" - checksum: 10c0/fc0768148b09d33d2cf34fe1ca6d439d709f699958a15d6b7395ab9c1b31f4931a3de3d12bf7ae0db12e607743a2ac0ebdf4072f40d3bc8e6cea73b7a97d638d + checksum: 10c0/e80d5d9aba4027ddf973e5dab2ac2a7b3ffde2f93e817bd32d776a121cf24dec7518a928d91960cfa9889d8e07b6723409788640fc7c89fd5dc30f1f0286ad3b languageName: node linkType: hard @@ -18721,25 +19195,25 @@ __metadata: languageName: node linkType: hard -"nuxt-seo-experiments@npm:^4.0.0": - version: 4.0.0 - resolution: "nuxt-seo-experiments@npm:4.0.0" +"nuxt-seo-experiments@npm:^4.0.1": + version: 4.0.1 + resolution: "nuxt-seo-experiments@npm:4.0.1" dependencies: - "@nuxt/kit": "npm:^3.12.3" - "@unhead/addons": "npm:^1.9.16" + "@nuxt/kit": "npm:^3.13.0" + "@unhead/addons": "npm:^1.10.0" defu: "npm:^6.1.4" escape-string-regexp: "npm:^5.0.0" fast-glob: "npm:^3.3.2" image-size: "npm:^1.1.1" - nuxt-site-config: "npm:^2.2.14" - nuxt-site-config-kit: "npm:^2.2.14" + nuxt-site-config: "npm:^2.2.15" + nuxt-site-config-kit: "npm:^2.2.15" pathe: "npm:^1.1.2" - ufo: "npm:^1.5.3" - checksum: 10c0/b0ab198a3d77e12a488870c57013280cf3c55a8296cb6eca0c69b3975b57d8bb6c5967512b2bbaa1f3553edd85c56387ff3d0829b6aace186723bbac73199756 + ufo: "npm:^1.5.4" + checksum: 10c0/14988aa40f0bc24c4a0099cb2638398bc2ce35466a301a8fd0f384a657c72d7ffa3259da76105373bd2058822e591b36bc3d77878151f234a27937f43d4cb2e2 languageName: node linkType: hard -"nuxt-site-config-kit@npm:2.2.15, nuxt-site-config-kit@npm:^2.2.14, nuxt-site-config-kit@npm:^2.2.15": +"nuxt-site-config-kit@npm:2.2.15, nuxt-site-config-kit@npm:^2.2.15": version: 2.2.15 resolution: "nuxt-site-config-kit@npm:2.2.15" dependencies: @@ -18753,7 +19227,38 @@ __metadata: languageName: node linkType: hard -"nuxt-site-config@npm:2.2.15, nuxt-site-config@npm:^2.2.14, nuxt-site-config@npm:^2.2.15": +"nuxt-site-config-kit@npm:2.2.16, nuxt-site-config-kit@npm:^2.2.16": + version: 2.2.16 + resolution: "nuxt-site-config-kit@npm:2.2.16" + dependencies: + "@nuxt/kit": "npm:^3.13.0" + "@nuxt/schema": "npm:^3.13.0" + pkg-types: "npm:^1.2.0" + site-config-stack: "npm:2.2.16" + std-env: "npm:^3.7.0" + ufo: "npm:^1.5.4" + checksum: 10c0/6c60873f7b358ca1df5ce468363bb644188f4c78d25a0a2fd485f70e427b9a88e74f39340c0fc1d4e8b30867b519f6975ef10c944f9a9d485471327a47880da1 + languageName: node + linkType: hard + +"nuxt-site-config@npm:2.2.16, nuxt-site-config@npm:^2.2.16": + version: 2.2.16 + resolution: "nuxt-site-config@npm:2.2.16" + dependencies: + "@nuxt/devtools-kit": "npm:^1.4.1" + "@nuxt/kit": "npm:^3.13.0" + "@nuxt/schema": "npm:^3.13.0" + nuxt-site-config-kit: "npm:2.2.16" + pathe: "npm:^1.1.2" + pkg-types: "npm:^1.2.0" + sirv: "npm:^2.0.4" + site-config-stack: "npm:2.2.16" + ufo: "npm:^1.5.4" + checksum: 10c0/a99e144f5e6d0a8df850f28bb231f117b9d4f9a47a5a5d650a70ede9aa365c1748cb905df4162d3903d2ef757e12139c88bfe44b3cd7c9413d3765515ecbe881 + languageName: node + linkType: hard + +"nuxt-site-config@npm:^2.2.15": version: 2.2.15 resolution: "nuxt-site-config@npm:2.2.15" dependencies: @@ -18770,22 +19275,22 @@ __metadata: languageName: node linkType: hard -"nuxt@npm:^3.13.0": - version: 3.13.0 - resolution: "nuxt@npm:3.13.0" +"nuxt@npm:^3.13.1": + version: 3.13.1 + resolution: "nuxt@npm:3.13.1" dependencies: "@nuxt/devalue": "npm:^2.0.2" - "@nuxt/devtools": "npm:^1.3.14" - "@nuxt/kit": "npm:3.13.0" - "@nuxt/schema": "npm:3.13.0" + "@nuxt/devtools": "npm:^1.4.1" + "@nuxt/kit": "npm:3.13.1" + "@nuxt/schema": "npm:3.13.1" "@nuxt/telemetry": "npm:^2.5.4" - "@nuxt/vite-builder": "npm:3.13.0" - "@unhead/dom": "npm:^1.10.0" - "@unhead/ssr": "npm:^1.10.0" - "@unhead/vue": "npm:^1.10.0" - "@vue/shared": "npm:^3.4.38" + "@nuxt/vite-builder": "npm:3.13.1" + "@unhead/dom": "npm:^1.10.4" + "@unhead/ssr": "npm:^1.10.4" + "@unhead/vue": "npm:^1.10.4" + "@vue/shared": "npm:^3.5.0" acorn: "npm:8.12.1" - c12: "npm:^1.11.1" + c12: "npm:^1.11.2" chokidar: "npm:^3.6.0" compatx: "npm:^0.1.8" consola: "npm:^3.2.3" @@ -18801,35 +19306,38 @@ __metadata: h3: "npm:^1.12.0" hookable: "npm:^5.5.3" ignore: "npm:^5.3.2" + impound: "npm:^0.1.0" jiti: "npm:^1.21.6" klona: "npm:^2.0.6" knitwork: "npm:^1.1.0" magic-string: "npm:^0.30.11" mlly: "npm:^1.7.1" + nanotar: "npm:^0.1.1" nitropack: "npm:^2.9.7" - nuxi: "npm:^3.12.0" - nypm: "npm:^0.3.9" + nuxi: "npm:^3.13.1" + nypm: "npm:^0.3.11" ofetch: "npm:^1.3.4" ohash: "npm:^1.1.3" pathe: "npm:^1.1.2" perfect-debounce: "npm:^1.0.0" - pkg-types: "npm:^1.1.3" + pkg-types: "npm:^1.2.0" radix3: "npm:^1.1.2" scule: "npm:^1.3.0" semver: "npm:^7.6.3" std-env: "npm:^3.7.0" strip-literal: "npm:^2.1.0" + tinyglobby: "npm:0.2.5" ufo: "npm:^1.5.4" ultrahtml: "npm:^1.5.3" uncrypto: "npm:^0.1.3" unctx: "npm:^2.3.1" unenv: "npm:^1.10.0" - unimport: "npm:^3.11.0" - unplugin: "npm:^1.12.2" + unimport: "npm:^3.11.1" + unplugin: "npm:^1.12.3" unplugin-vue-router: "npm:^0.10.7" unstorage: "npm:^1.10.2" untyped: "npm:^1.4.2" - vue: "npm:^3.4.38" + vue: "npm:^3.5.0" vue-bundle-renderer: "npm:^2.1.0" vue-devtools-stub: "npm:^0.1.0" vue-router: "npm:^4.4.3" @@ -18844,7 +19352,7 @@ __metadata: bin: nuxi: bin/nuxt.mjs nuxt: bin/nuxt.mjs - checksum: 10c0/37ea5b4c21edf56e0230196b37eabc6e969c10bcef319d5a9ec8aac0ea6b7b88a3de4a4132c9f5b298b3201718ef34c5c7058ccbdd1c70dd0e69c5cc7d7093f7 + checksum: 10c0/86ad07d33f0ecffe104341a0682415556702856cee14689bfcbec2a50454b305fa120350b44831fd0a34496f7eab21cda6d22205ec1673f92881c8c5a40c5665 languageName: node linkType: hard @@ -18855,7 +19363,7 @@ __metadata: languageName: node linkType: hard -"nypm@npm:^0.3.8, nypm@npm:^0.3.9": +"nypm@npm:^0.3.11, nypm@npm:^0.3.8": version: 0.3.11 resolution: "nypm@npm:0.3.11" dependencies: @@ -18911,7 +19419,7 @@ __metadata: languageName: node linkType: hard -"object.fromentries@npm:^2.0.7": +"object.fromentries@npm:^2.0.8": version: 2.0.8 resolution: "object.fromentries@npm:2.0.8" dependencies: @@ -18923,7 +19431,7 @@ __metadata: languageName: node linkType: hard -"object.groupby@npm:^1.0.1": +"object.groupby@npm:^1.0.3": version: 1.0.3 resolution: "object.groupby@npm:1.0.3" dependencies: @@ -18934,7 +19442,7 @@ __metadata: languageName: node linkType: hard -"object.values@npm:^1.1.7": +"object.values@npm:^1.2.0": version: 1.2.0 resolution: "object.values@npm:1.2.0" dependencies: @@ -19101,13 +19609,13 @@ __metadata: languageName: node linkType: hard -"openai@npm:^4.57.0": - version: 4.57.0 - resolution: "openai@npm:4.57.0" +"openai@npm:^4.58.0": + version: 4.58.0 + resolution: "openai@npm:4.58.0" dependencies: "@types/node": "npm:^18.11.18" "@types/node-fetch": "npm:^2.6.4" - "@types/qs": "npm:^6.9.7" + "@types/qs": "npm:^6.9.15" abort-controller: "npm:^3.0.0" agentkeepalive: "npm:^4.2.1" form-data-encoder: "npm:1.7.2" @@ -19121,7 +19629,7 @@ __metadata: optional: true bin: openai: bin/cli - checksum: 10c0/20e2c7c4cfc71ce6a20cbcb76d0ba3a65738f6c80a19a3ee197f0c9d065b707fb93e6bc112f6abf90de049a2ba86a038b4c44b411dffaa727b036bde1568a43c + checksum: 10c0/681a195051deb68e6a13423a090d52616c64eb8198c303b22ca3462a132852cde0067151bb183e7b1b3359b7ea1b8515003cac9e02b35f563c13eadca8aa2392 languageName: node linkType: hard @@ -19358,6 +19866,13 @@ __metadata: languageName: node linkType: hard +"package-manager-detector@npm:^0.2.0": + version: 0.2.0 + resolution: "package-manager-detector@npm:0.2.0" + checksum: 10c0/1ad699098018f9425b0f0a197537e085420ebcb7b6c49ef5a8dcff198f50d8de206f52ed10867624b7cb01bebac76396f5ac020dcff96f44154d59e6a5dcf36a + languageName: node + linkType: hard + "pako@npm:^0.2.5": version: 0.2.9 resolution: "pako@npm:0.2.9" @@ -19779,6 +20294,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc + languageName: node + linkType: hard + "pify@npm:^2.0.0, pify@npm:^2.3.0": version: 2.3.0 resolution: "pify@npm:2.3.0" @@ -19827,27 +20349,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.46.1, playwright-core@npm:^1.45.2": - version: 1.46.1 - resolution: "playwright-core@npm:1.46.1" +"playwright-core@npm:1.47.0, playwright-core@npm:^1.46.1": + version: 1.47.0 + resolution: "playwright-core@npm:1.47.0" bin: playwright-core: cli.js - checksum: 10c0/98e48e271caccaa6c54b3c591cbddbef36a679eef011b38e2af11fbaba0aab1f997b45f9207c4099468a0c79047a1e879bbd9e81bd880ae24501a0ee3c7a33c7 + checksum: 10c0/70fdd2e62849176ff5403ec530a69d0ef07e6bebc8486f1f4bf49bc4b8a34ab27be0f91950f74a448db13b383d2ba6d934041493f286603de190937ebd0e7508 languageName: node linkType: hard -"playwright@npm:1.46.1": - version: 1.46.1 - resolution: "playwright@npm:1.46.1" +"playwright@npm:1.47.0": + version: 1.47.0 + resolution: "playwright@npm:1.47.0" dependencies: fsevents: "npm:2.3.2" - playwright-core: "npm:1.46.1" + playwright-core: "npm:1.47.0" dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 10c0/2a328a2073313136192d74b48b981d9aeb1d4cc54926ed235f638c875f9de59c41370bb20bb2d8cf30126c52c6e25cc02db21ffafeb487c92c84c52c84846912 + checksum: 10c0/fa47134246baf0951db435ae004a0ee98d5d1cfbb675d07a2c5dd3709866e69c092a568f0e8ec652e9e9fcb45e77335a44cd74b03d15369a7acb9c6e7a0a88d0 languageName: node linkType: hard @@ -20302,6 +20824,17 @@ __metadata: languageName: node linkType: hard +"postcss@npm:^8.4.44, postcss@npm:^8.4.45": + version: 8.4.45 + resolution: "postcss@npm:8.4.45" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.1" + source-map-js: "npm:^1.2.0" + checksum: 10c0/ad6f8b9b1157d678560373696109745ab97a947d449f8a997acac41c7f1e4c0f3ca4b092d6df1387f430f2c9a319987b1780dbdc27e35800a88cde9b606c1e8f + languageName: node + linkType: hard + "postgres-array@npm:~2.0.0": version: 2.0.0 resolution: "postgres-array@npm:2.0.0" @@ -20798,13 +21331,13 @@ __metadata: languageName: node linkType: hard -"react-hotkeys-hook@npm:^4.5.0": - version: 4.5.0 - resolution: "react-hotkeys-hook@npm:4.5.0" +"react-hotkeys-hook@npm:^4.5.1": + version: 4.5.1 + resolution: "react-hotkeys-hook@npm:4.5.1" peerDependencies: react: ">=16.8.1" react-dom: ">=16.8.1" - checksum: 10c0/ae3ee47c623aaece4c21ce8027b0fff18caaee8c6cb22fafcbebbb54cee27ef7d0097145f04da1bd67b5af1d89cb4f983ae6a72b32451de81dca193255187480 + checksum: 10c0/d881d8b3dc94e63629467e9e370921d168fee23a683bd02cf52d9c51c488a0fd944d15bb43b5f2b1c99f2d04bac7f58ffb84b57f9c3f84b2f437ea81b439ec10 languageName: node linkType: hard @@ -21659,16 +22192,16 @@ __metadata: languageName: node linkType: hard -"sass@npm:^1.77.8": - version: 1.77.8 - resolution: "sass@npm:1.77.8" +"sass@npm:^1.78.0": + version: 1.78.0 + resolution: "sass@npm:1.78.0" dependencies: chokidar: "npm:>=3.0.0 <4.0.0" immutable: "npm:^4.0.0" source-map-js: "npm:>=0.6.2 <2.0.0" bin: sass: sass.js - checksum: 10c0/2bfd62794070352c804f949e69bd8bb5b4ec846deeb924251b2c3f7b503170fb1ae186f513f0166907749eb34e0277dee747edcb78c886fb471aac01be1e864c + checksum: 10c0/6577a87c00b03a5a50f3a11b4b6592f28abce34e61812e381535a3b712151bd94db3ca06467d20395431e0f38a23f99e616d6859d771fb6d4617c359f590c48c languageName: node linkType: hard @@ -22078,7 +22611,7 @@ __metadata: languageName: node linkType: hard -"site-config-stack@npm:2.2.15, site-config-stack@npm:^2.2.15": +"site-config-stack@npm:2.2.15": version: 2.2.15 resolution: "site-config-stack@npm:2.2.15" dependencies: @@ -22089,6 +22622,17 @@ __metadata: languageName: node linkType: hard +"site-config-stack@npm:2.2.16, site-config-stack@npm:^2.2.16": + version: 2.2.16 + resolution: "site-config-stack@npm:2.2.16" + dependencies: + ufo: "npm:^1.5.4" + peerDependencies: + vue: ^3 + checksum: 10c0/3a307d0819b03fd3840eb4f2bcff56cecbd51f735acdd6d7e83aaec327c201f5c1996a9a6e214d256ad85cb1f918b0255a86ef7814f29908684ebfdead9fcd92 + languageName: node + linkType: hard + "slash@npm:^4.0.0": version: 4.0.0 resolution: "slash@npm:4.0.0" @@ -23001,6 +23545,23 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^0.3.0": + version: 0.3.0 + resolution: "tinyexec@npm:0.3.0" + checksum: 10c0/138a4f4241aea6b6312559508468ab275a31955e66e2f57ed206e0aaabecee622624f208c5740345f0a66e33478fd065e359ed1eb1269eb6fd4fa25d44d0ba3b + languageName: node + linkType: hard + +"tinyglobby@npm:0.2.5, tinyglobby@npm:^0.2.5": + version: 0.2.5 + resolution: "tinyglobby@npm:0.2.5" + dependencies: + fdir: "npm:^6.2.0" + picomatch: "npm:^4.0.2" + checksum: 10c0/a9ebb3fd8fec5a7aee9a8078d84db9209986b077d267bc1da41c312d7a19cea8c5ec4fc660a27adea180efda026c30cb7529ff80ef2aa7737ff404de8d0066c2 + languageName: node + linkType: hard + "tinyld@npm:^1.3.4": version: 1.3.4 resolution: "tinyld@npm:1.3.4" @@ -23485,15 +24046,15 @@ __metadata: languageName: node linkType: hard -"unhead@npm:1.10.0": - version: 1.10.0 - resolution: "unhead@npm:1.10.0" +"unhead@npm:1.10.4": + version: 1.10.4 + resolution: "unhead@npm:1.10.4" dependencies: - "@unhead/dom": "npm:1.10.0" - "@unhead/schema": "npm:1.10.0" - "@unhead/shared": "npm:1.10.0" + "@unhead/dom": "npm:1.10.4" + "@unhead/schema": "npm:1.10.4" + "@unhead/shared": "npm:1.10.4" hookable: "npm:^5.5.3" - checksum: 10c0/c56dc7fb355ddc77529866405177589aab7ee7508082468221713158999a6fdc5a149868208ba4fc25d8f0d0af4bee95418357bbee069d143ebfec182d43920a + checksum: 10c0/e63fece53bdf15459956c7fbccea8faccd0a79f8a7cb6f1bfcccb2bd1356fc1eca565cc21eec31c8fc0e192b0af1f63bdf440a32f6ae3361efecdecb7df8705c languageName: node linkType: hard @@ -23529,7 +24090,7 @@ __metadata: languageName: node linkType: hard -"unimport@npm:^3.10.1, unimport@npm:^3.11.0, unimport@npm:^3.7.2": +"unimport@npm:^3.11.0, unimport@npm:^3.11.1, unimport@npm:^3.7.2": version: 3.11.1 resolution: "unimport@npm:3.11.1" dependencies: @@ -23772,6 +24333,21 @@ __metadata: languageName: node linkType: hard +"unplugin@npm:^1.12.3": + version: 1.13.1 + resolution: "unplugin@npm:1.13.1" + dependencies: + acorn: "npm:^8.12.1" + webpack-virtual-modules: "npm:^0.6.2" + peerDependencies: + webpack-sources: ^3 + peerDependenciesMeta: + webpack-sources: + optional: true + checksum: 10c0/a76fffd0989eb56ac1a45a6fb77cdd313a0560cb2ea18082ec9eb095fe9093e4543e34cbaeeb4e01f5bbb6ca095783df91dd2af5347e0b7cc9ab07058ccf1f1b + languageName: node + linkType: hard + "unstorage@npm:^1.10.2": version: 1.10.2 resolution: "unstorage@npm:1.10.2" @@ -24191,7 +24767,7 @@ __metadata: languageName: node linkType: hard -"vite-plugin-inspect@npm:^0.8.6": +"vite-plugin-inspect@npm:^0.8.7": version: 0.8.7 resolution: "vite-plugin-inspect@npm:0.8.7" dependencies: @@ -24246,7 +24822,7 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.0.0, vite@npm:^5.4.1, vite@npm:^5.4.2": +"vite@npm:^5.0.0, vite@npm:^5.4.1": version: 5.4.2 resolution: "vite@npm:5.4.2" dependencies: @@ -24289,6 +24865,49 @@ __metadata: languageName: node linkType: hard +"vite@npm:^5.4.3": + version: 5.4.3 + resolution: "vite@npm:5.4.3" + dependencies: + esbuild: "npm:^0.21.3" + fsevents: "npm:~2.3.3" + postcss: "npm:^8.4.43" + rollup: "npm:^4.20.0" + peerDependencies: + "@types/node": ^18.0.0 || >=20.0.0 + less: "*" + lightningcss: ^1.21.0 + sass: "*" + sass-embedded: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: 10c0/7afe601bcba82f81980c718fc171ba8f0c45e3bffaeb7ef831b64b84e396f963c3c87818b74da4c8e817d1bce1c179f1efae3bcb14d2e94b4eb635071722c8f2 + languageName: node + linkType: hard + "vitepress-plugin-mermaid@npm:^2.0.16": version: 2.0.16 resolution: "vitepress-plugin-mermaid@npm:2.0.16" @@ -24501,6 +25120,24 @@ __metadata: languageName: node linkType: hard +"vue@npm:^3.5.0, vue@npm:^3.5.3": + version: 3.5.3 + resolution: "vue@npm:3.5.3" + dependencies: + "@vue/compiler-dom": "npm:3.5.3" + "@vue/compiler-sfc": "npm:3.5.3" + "@vue/runtime-dom": "npm:3.5.3" + "@vue/server-renderer": "npm:3.5.3" + "@vue/shared": "npm:3.5.3" + peerDependencies: + typescript: "*" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/b985dfe25e33d415b13d8a80cbe40a7f9c63ea7be11a56551cefd70cc1692a2f498917c77656cf88412b995da6e4824d668767820e28099b92889dfbe9f92c45 + languageName: node + linkType: hard + "w3c-xmlserializer@npm:^5.0.0": version: 5.0.0 resolution: "w3c-xmlserializer@npm:5.0.0"