diff --git a/dashboard/README.md b/dashboard/README.md index e8be32f5..009bbb13 100644 --- a/dashboard/README.md +++ b/dashboard/README.md @@ -1,10 +1,8 @@ # AstrBot React Dashboard -这是 AstrBot Dashboard 的 React + TypeScript 实现。所有启用路由均由 React 提供,新版开发和构建流程不再启动、构建或复制 Vue Dashboard。 +这是 AstrBot Dashboard 的 React + TypeScript 实现,也是桌面端唯一维护的 WebUI。 -旧版 `dashboard/` 仍作为只读的行为与样式参考保留,不属于新版运行时依赖。 - -请从仓库根目录使用统一命令启动或构建,详见 [`../docs/dashboard-react-migration.md`](../docs/dashboard-react-migration.md)。 +请从仓库根目录使用统一命令启动或构建,详见 [`../docs/development.md`](../docs/development.md)。 迁移期 endpoint、存储和响应兼容层的删除条件见 [`../docs/dashboard-compatibility-exit-plan.md`](../docs/dashboard-compatibility-exit-plan.md)。 diff --git a/dashboard/src/api/compat.ts b/dashboard/src/api/compat.ts index d7b86b7a..0cd78207 100644 --- a/dashboard/src/api/compat.ts +++ b/dashboard/src/api/compat.ts @@ -33,7 +33,7 @@ export const compatibilityExitPlan: readonly CompatibilityExitItem[] = [ { id: 'legacy-storage', minimumBackendVersion: 'Not backend-dependent', - removalCondition: 'One stable release has migrated uiTheme and the Vue Dashboard rollback window is closed.', + removalCondition: 'One stable release has migrated uiTheme and no supported release requires the old key.', targetDashboardVersion: '2.0.0', }, { diff --git a/dashboard/src/api/http.test.ts b/dashboard/src/api/http.test.ts index 4755c243..58219921 100644 --- a/dashboard/src/api/http.test.ts +++ b/dashboard/src/api/http.test.ts @@ -5,7 +5,7 @@ import { memoryStorage } from '@/test/storage'; import { ApiError, apiRequest, fetchWithAuth } from './http'; describe('apiRequest', () => { - it('preserves token and locale headers used by the legacy dashboard', async () => { + it('preserves the established token and locale headers', async () => { const fetchMock = vi.fn().mockResolvedValue( jsonResponse({ data: { ready: true }, diff --git a/dashboard/src/routes/configuration/providerIcons.test.ts b/dashboard/src/routes/configuration/providerIcons.test.ts index d7bcd1db..ae6e0747 100644 --- a/dashboard/src/routes/configuration/providerIcons.test.ts +++ b/dashboard/src/routes/configuration/providerIcons.test.ts @@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest'; import { getProviderIcon, PROVIDER_ICON_URLS } from './providerIcons'; describe('provider icons', () => { - it('ports every provider icon mapping from the legacy dashboard', () => { + it('covers every supported provider icon mapping', () => { expect(Object.keys(PROVIDER_ICON_URLS)).toEqual([ 'openai', 'azure', diff --git a/dashboard/src/routes/extensions/extensionModel.test.ts b/dashboard/src/routes/extensions/extensionModel.test.ts index 55d6fde0..dbadc542 100644 --- a/dashboard/src/routes/extensions/extensionModel.test.ts +++ b/dashboard/src/routes/extensions/extensionModel.test.ts @@ -61,7 +61,7 @@ describe('extension model helpers', () => { expect(markdownContent({ content: '# README' })).toBe('# README'); }); - it('normalizes the keyed marketplace response used by the legacy dashboard', () => { + it('normalizes keyed marketplace responses', () => { const payload = { $meta: { name: 'AstrBot', total: 2 }, astrbot_plugin_weather: { author: 'Alice', desc: 'Forecast' }, diff --git a/dashboard/src/styles/features/_extensions.scss b/dashboard/src/styles/features/_extensions.scss index ac31349d..df626454 100644 --- a/dashboard/src/styles/features/_extensions.scss +++ b/dashboard/src/styles/features/_extensions.scss @@ -231,7 +231,7 @@ .extension-market-card footer .button--primary { background: var(--astrbot-primary); color: #fff; } .extension-market-card footer .is-installed { background: var(--astrbot-success); color: #fff; opacity: .72; } -/* Legacy Dashboard MCP and Skills sections */ +/* MCP and Skills sections */ .resource-page { position: relative; min-height: 100%; padding: 8px 4px 100px; } .resource-list { display: flex; flex-direction: column; gap: 12px; } .resource-list-item { display: flex; min-height: 104px; align-items: center; justify-content: space-between; gap: 16px; padding: 16px; border: 1px solid var(--astrbot-border); border-radius: 10px; background: var(--astrbot-surface); cursor: pointer; transition: background .16s; } @@ -419,7 +419,7 @@ .extension-config-editor--detail { height: 60vh; margin-top: 18px; padding: 14px; border: 1px solid var(--astrbot-border); border-radius: 12px; } .extension-config-editor--detail > div:last-child { display: flex; justify-content: flex-end; gap: 8px; margin-top: 10px; } -/* Installed plugin cards mirror the legacy dashboard layout and interactions. */ +/* Installed plugin cards */ .extension-installed__header { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 18px; margin: 16px 0 24px; } .extension-installed__header h2 { margin: 0; font-size: clamp(24px, 2.2vw, 32px); } .extension-installed__search { display: flex; width: min(340px, 100%); min-height: 44px; align-items: center; gap: 8px; padding: 0 12px; border-radius: 10px; background: color-mix(in srgb, var(--astrbot-text) 5%, var(--astrbot-surface)); color: color-mix(in srgb, var(--astrbot-text) 52%, transparent); } diff --git a/docs/dashboard-compatibility-exit-plan.md b/docs/dashboard-compatibility-exit-plan.md index 3dc6e289..07f7b98c 100644 --- a/docs/dashboard-compatibility-exit-plan.md +++ b/docs/dashboard-compatibility-exit-plan.md @@ -6,7 +6,7 @@ 1. 产品声明的最低 AstrBot Core 版本已经包含对应的 v1 API 或稳定数据契约。 2. 至少一个稳定发布周期内未收到旧分支命中报告,并完成旧 Core、升级中断和浏览器存储升级场景回归。 -3. Vue Dashboard 回退窗口已经由维护者明确关闭。 +3. 旧版主题与持久化格式的兼容周期已经由维护者明确结束。 当前仓库只声明兼容 AstrBot Core 4.x,没有更精确的后端最低版本合同。因此,表中的“Core 4.x 兼容基线”是继续保留兼容的最低版本,不代表可以立即删除;维护者提高产品最低 Core 版本时,必须在同一 @@ -18,13 +18,13 @@ PR 中填入已验证的精确版本并执行删除检查。 | --- | --- | --- | --- | --- | | `api-endpoint-fallback` | `dashboard/src/api/compat.ts`、`dashboard/src/api/auth.ts`:认证、版本、统计、更新与 pip 操作先请求 v1,只有 v1 返回 404 或历史 `Missing API key` 响应时请求 unversioned endpoint | Core 4.x 兼容基线 | 产品最低 Core 版本提供登记的全部 v1 endpoint;404/缺 key 回退连续一个稳定周期为零;认证、更新、重启回归通过 | dashboard 2.0.0 | | `legacy-recovery` | `dashboard/src/api/compat.ts`、`dashboard/src/auth/upgradeRecovery.ts`:WebUI 已更新但 Core 进程未重启时,以 token 请求 `/api/stat/version`、`start-time`、`restart-core` | Core 4.x 兼容基线 | 更新流程保证 WebUI/Core 原子切换,或最低 Core 提供不依赖当前进程版本的 v1 恢复端点;升级中断测试通过 | dashboard 2.0.0 | -| `legacy-storage` | `dashboard/src/config/storageKeys.ts`、`preferences.ts`、`stores/layout.ts`:读取并双写 Vue 使用的 `uiTheme`;其他 `token`、`user`、聊天和代理 key 是新旧 Dashboard 的共享持久化合同,不是可直接删除的双写 | 不依赖后端 | `themeMode` 已随一个稳定版本写入用户浏览器;Vue 回退窗口关闭;删除 `uiTheme` 读取、双写和对应测试后,明暗/跟随系统主题回归通过 | dashboard 2.0.0 | +| `legacy-storage` | `dashboard/src/config/storageKeys.ts`、`preferences.ts`、`stores/layout.ts`:读取并双写历史版本使用的 `uiTheme`;其他 `token`、`user`、聊天和代理 key 是跨版本共享的持久化合同,不是可直接删除的双写 | 不依赖后端 | `themeMode` 已随一个稳定版本写入用户浏览器;旧格式兼容周期结束;删除 `uiTheme` 读取、双写和对应测试后,明暗/跟随系统主题回归通过 | dashboard 2.0.0 | | `response-envelope` | `dashboard/src/api/response.ts`、`dashboard/src/desktop/DesktopProvider.tsx`:兼容 Axios 外层 `data`、API envelope 和桌面端历史嵌套 envelope | OpenAPI v1 合同,Core 4.x 兼容基线 | 所有启用 endpoint 和桌面桥使用单一生成类型;移除兼容分支后 API、桌面启动及错误 envelope 测试通过 | dashboard 2.0.0 | | `legacy-chat-files` | `dashboard/src/config/endpoints.ts`、`dashboard/src/routes/chat/ChatMessageList.tsx`:聊天附件仍使用 `/api/chat/attachment` 与 `/api/chat/get_file`,当前没有双请求 | Core 4.x 兼容基线 | 后端提供并发布等价 v1 文件 endpoint,产品最低 Core 版本包含它;历史消息附件回归通过 | 后端 v1 文件 API 发布后的下一个 dashboard major | | `persistence-envelope` | `dashboard/src/config/persistence.ts`:把未版本化 localStorage 值一次性迁移为 `{ data, version }` | 不依赖后端 | 所有受支持浏览器数据至少经历一个稳定迁移周期,且产品接受清除更旧的本地偏好 | dashboard 2.0.0 | -样式中的 `legacy-resource-*`、`legacy-fab-stack` 以及 Vue 迁移说明不承担运行时兼容职责,已在建立本计划时 -改为领域命名;以后不允许以 `legacy` 或 `Vue` 注释代替明确的兼容登记。 +样式中的历史迁移命名不承担运行时兼容职责,已在建立本计划时改为领域命名;以后不允许以模糊的 +`legacy` 注释代替明确的兼容登记。 ## 回退触发合同 diff --git a/docs/dashboard-react-migration.md b/docs/dashboard-react-migration.md deleted file mode 100644 index b9a8dda3..00000000 --- a/docs/dashboard-react-migration.md +++ /dev/null @@ -1,61 +0,0 @@ -# Dashboard React 迁移说明 - -## 当前状态 - -Dashboard 的启用路由已从 Vue 3 + Vuetify 迁移到 React。React 工程现位于 -`dashboard/`,并且是本地开发、桌面打包和 CI 的唯一默认 WebUI。 - -旧版 Vue 工程移动到 `legacy-dashboard/`,仅用于人工行为与视觉对照。它不参与: - -- `pnpm run dev`、`pnpm run build` 或 `make` 默认命令; -- `resources/webui` 资源准备; -- Tauri 的 `beforeDevCommand`、`beforeBuildCommand`; -- Dashboard 质量检查和桌面发布 CI。 - -## 目录职责 - -| 路径 | 职责 | -| --- | --- | -| `dashboard/` | React + TypeScript + Vite 当前工程 | -| `dashboard/src/app/AppRouter.tsx` | React 路由入口和鉴权装配 | -| `legacy-dashboard/` | 只读保留的旧版 Vue 工程,不参与自动化流程 | -| `scripts/run-tauri.mjs` | 唯一 Tauri 开发和构建入口 | -| `scripts/prepare-resources.mjs` | WebUI、后端和版本资源准备入口 | - -## 常用命令 - -```bash -pnpm install -pnpm run install:dashboard -pnpm run dev:dashboard -pnpm run dev -pnpm run typecheck:dashboard -pnpm run prepare:webui -pnpm run prepare:resources -pnpm run build -``` - -生产构建会: - -1. 使用 `dashboard/pnpm-lock.yaml` 安装缺失依赖。 -2. 类型检查并构建 `dashboard/`。 -3. 将 `dashboard/dist` 同步到 `resources/webui`。 -4. 拉取或使用指定 AstrBot 后端源码并准备 `resources/backend`。 -5. 执行 Tauri 打包。 - -## CI - -`.github/workflows/check-dashboard.yml` 对 `dashboard/` 执行类型检查、TypeScript/React -lint、样式 lint、格式检查、覆盖率测试和生产构建。 - -`.github/workflows/build-desktop-tauri.yml` 通过 Tauri 默认 -`beforeBuildCommand = pnpm run prepare:resources` 构建 React Dashboard;Linux、Windows -和 macOS 产物均使用同步到 `resources/webui` 的 React bundle。 - -## 维护约束 - -1. 新功能和修复只进入 `dashboard/`。 -2. 不得在 CI、Makefile、Tauri 配置或默认 package scripts 中引用 `legacy-dashboard/`。 -3. 不再新增 `:new`、`-new` 等并行构建入口。 -4. 新增路由时保持既有 hash 路径、API、存储 key 和桌面桥协议兼容。 -5. `legacy-dashboard/` 删除应作为独立任务处理,不影响当前默认构建链路。 diff --git a/docs/development.md b/docs/development.md index 30f9cd12..2acaa349 100644 --- a/docs/development.md +++ b/docs/development.md @@ -42,7 +42,7 @@ ASTRBOT_SOURCE_GIT_URL=https://github.com/AstrBotDevs/AstrBot.git ASTRBOT_SOURCE_GIT_REF=master ``` -`.env` 仅用于本机且已被 Git 忽略。`pnpm run dev` 会自动把远端源码准备到 `vendor/AstrBot` 后再启动;`pnpm run build` 和资源准备脚本也会自动加载 `.env`。已有的进程环境变量优先级高于 `.env`。仅在明确需要使用现有本地源码时才设置 `ASTRBOT_SOURCE_DIR`。 +`.env` 仅用于本机且已被 Git 忽略。`pnpm run dev` 会自动把远端源码准备到 `vendor/AstrBot` 后再启动;`pnpm run build` 和资源准备脚本也会自动加载 `.env`。已有的进程环境变量优先级高于 `.env`。托管源码同步完成后会删除上游仓库自带的 `dashboard/`,确保桌面端只使用本仓库维护的 React Dashboard。仅在明确需要使用现有本地源码时才设置 `ASTRBOT_SOURCE_DIR`;显式指定的源码目录由用户管理,不会执行上述删除。 推荐直接使用 Makefile: @@ -75,9 +75,8 @@ pnpm --dir dashboard dev 独立前端开发服务默认监听 `1420`,并把 `/api` 代理到本机 AstrBot 后端 `http://127.0.0.1:6185/`。 -React Dashboard 已成为唯一默认前端,目录位于 `dashboard/`。旧版 Vue 工程仅保留在 -`legacy-dashboard/` 供人工对照,不参与默认命令、资源准备或 CI。迁移边界见 -[`dashboard-react-migration.md`](./dashboard-react-migration.md)。 +React Dashboard 是唯一维护和构建的前端,目录位于 `dashboard/`。默认命令、资源准备、 +桌面打包和 CI 均以该目录为准。 本地执行 `pnpm run build` 时,如果未设置 `TAURI_SIGNING_PRIVATE_KEY`,启动脚本会保留 MSI/NSIS 安装包构建,但跳过需要发布私钥的 updater 签名产物。正式发布流水线提供该私钥,因此仍会按 `tauri.conf.json` 生成并签名 updater artifacts。 diff --git a/docs/environment-variables.md b/docs/environment-variables.md index da60140f..627c85a9 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -36,7 +36,7 @@ WebUI 源码固定来自本仓库 `dashboard/`,`pnpm run prepare:webui` 不解 本地开发推荐把这些变量写入仓库根目录 `.env`(从 `.env.example` 复制)。Tauri 的 `dev`/`build` 启动脚本及资源准备入口会自动加载该文件,同时保留外部进程环境变量的更高优先级。 -默认开发流程不设置 `ASTRBOT_SOURCE_DIR`;`pnpm run dev` 会根据 `ASTRBOT_SOURCE_GIT_URL` / `ASTRBOT_SOURCE_GIT_REF` 将源码准备到 `vendor/AstrBot`。`ASTRBOT_SOURCE_DIR` 仅作为显式本地源码覆盖项。 +默认开发流程不设置 `ASTRBOT_SOURCE_DIR`;`pnpm run dev` 会根据 `ASTRBOT_SOURCE_GIT_URL` / `ASTRBOT_SOURCE_GIT_REF` 将源码准备到 `vendor/AstrBot`,并在同步后删除上游源码中的 `dashboard/`。`ASTRBOT_SOURCE_DIR` 仅作为显式本地源码覆盖项,其目录内容不会被资源准备脚本删除。 | 变量 | 用途 | 默认值/行为 | | --- | --- | --- | diff --git a/docs/repository-structure.md b/docs/repository-structure.md index c9876510..b24a5a52 100644 --- a/docs/repository-structure.md +++ b/docs/repository-structure.md @@ -8,8 +8,6 @@ - 桌面壳层核心(Rust + Tauri 配置)。 - `dashboard/` - Desktop 仓库直接维护的 React/Vite WebUI 源码、OpenAPI 快照与前端静态资源。 -- `legacy-dashboard/` - - 只读保留的旧版 Vue Dashboard,仅供人工迁移对照,不参与默认构建与 CI。 - `scripts/` - 构建、资源准备、CI 辅助脚本。 - `resources/` diff --git a/legacy-dashboard/.gitignore b/legacy-dashboard/.gitignore deleted file mode 100644 index 39b1bc9c..00000000 --- a/legacy-dashboard/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -node_modules/ -.DS_Store -dist/ -src/assets/mdi-subset/ \ No newline at end of file diff --git a/legacy-dashboard/LICENSE b/legacy-dashboard/LICENSE deleted file mode 100644 index 7e99c680..00000000 --- a/legacy-dashboard/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2023 CodedThemes - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/legacy-dashboard/README.md b/legacy-dashboard/README.md deleted file mode 100644 index 9193984d..00000000 --- a/legacy-dashboard/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# AstrBot Desktop 管理面板 - -此目录由 AstrBot Desktop 仓库直接维护,基于 CodedThemes/Berry 模板开发。生产构建输出到 `dist/`,随后由桌面资源准备脚本同步到 `resources/webui/`。 - -## 本地开发 - -```bash -pnpm install -pnpm dev -pnpm build -pnpm typecheck -``` - -从仓库根目录启动完整桌面开发模式时,Tauri 会通过 `pnpm run dev:dashboard` 自动启动此处的 Vite 服务。 - -OpenAPI 快照位于 `openapi/openapi-v1.yaml`,更新后可运行 `pnpm generate:api`。T2I Shiki 浏览器运行时由 `pnpm build:t2i-shiki-runtime` 生成到 `public/t2i/`,二者都不依赖本地 AstrBot 源码目录。 diff --git a/legacy-dashboard/env.d.ts b/legacy-dashboard/env.d.ts deleted file mode 100644 index 11f02fe2..00000000 --- a/legacy-dashboard/env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/legacy-dashboard/index.html b/legacy-dashboard/index.html deleted file mode 100644 index 3be38ed4..00000000 --- a/legacy-dashboard/index.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - AstrBot - 仪表盘 - - - -
正在启动 AstrBot 后端…
- - - diff --git a/legacy-dashboard/openapi/openapi-v1.yaml b/legacy-dashboard/openapi/openapi-v1.yaml deleted file mode 100644 index 1c8e7d15..00000000 --- a/legacy-dashboard/openapi/openapi-v1.yaml +++ /dev/null @@ -1,6152 +0,0 @@ -openapi: 3.1.0 -info: - title: AstrBot OpenAPI v1 - version: 0.1.0 - description: > - Target REST contract for migrating AstrBot HTTP APIs to /api/v1. - Dynamic AstrBot configuration payloads are intentionally modeled as open - JSON objects because their schemas are provided at runtime by template - endpoints. - Developer API keys currently support these scopes only: bot, provider, - persona, im, config, chat, data, file, plugin, mcp, skill. The config scope also - grants bot and provider access. -servers: - - url: http://localhost:6185 - description: Local AstrBot server - -security: - - ApiKeyAuth: [] - -tags: - - name: Open API - - name: Auth - - name: API Keys - - name: System Config - - name: Config Profiles - - name: Bot Config Routes - - name: Bots - - name: Provider Sources - - name: Providers - - name: Chat - - name: IM - - name: Files - - name: Plugins - - name: Plugin Sources - - name: Plugin Pages - - name: Commands - - name: Tools - - name: MCP - - name: Skills - - name: Knowledge Base - - name: Personas - - name: Sessions - - name: Conversations - - name: Stats - - name: Backups - - name: Updates - - name: Cron - - name: Logs - - name: T2I - - name: Subagents - - name: Webhooks - -paths: - /api/v1/auth/login: - post: - tags: [Auth] - summary: Login to the dashboard API - operationId: login - security: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/LoginRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - "401": - $ref: "#/components/responses/Error" - - /api/v1/auth/logout: - post: - tags: [Auth] - summary: Logout from the dashboard API - operationId: logout - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/auth/setup-status: - get: - tags: [Auth] - summary: Get first-run setup status - operationId: getAuthSetupStatus - security: [] - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/auth/setup: - post: - tags: [Auth] - summary: Complete first-run dashboard account setup - operationId: setupAuth - security: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SetupAuthRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/auth/totp/setup: - post: - tags: [Auth] - summary: Start or refresh TOTP setup - operationId: setupTotp - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/TotpSetupRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/auth/totp/recovery: - post: - tags: [Auth] - summary: Generate or rotate TOTP recovery codes - operationId: recoverTotp - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/auth/account: - patch: - tags: [Auth] - summary: Update dashboard account credentials - operationId: updateAuthAccount - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateAccountRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/api-keys: - get: - tags: [API Keys] - summary: List OpenAPI keys - operationId: listApiKeys - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [API Keys] - summary: Create an OpenAPI key - operationId: createApiKey - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateApiKeyRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/api-keys/{key_id}/revoke: - post: - tags: [API Keys] - summary: Revoke an OpenAPI key - operationId: revokeApiKey - parameters: - - $ref: "#/components/parameters/KeyId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/api-keys/{key_id}: - delete: - tags: [API Keys] - summary: Delete an OpenAPI key - operationId: deleteApiKey - parameters: - - $ref: "#/components/parameters/KeyId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/system-config/schema: - get: - tags: [System Config] - summary: Get the system configuration schema - operationId: getSystemConfigSchema - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/system-config: - get: - tags: [System Config] - summary: Get the system configuration - operationId: getSystemConfig - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [System Config] - summary: Replace the system configuration - operationId: updateSystemConfig - x-astrbot-scope: config - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/system-config/runtime: - get: - tags: [System Config] - summary: Get runtime-enriched system configuration metadata - operationId: getSystemConfigRuntime - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/config-profiles/schema: - get: - tags: [Config Profiles] - summary: Get the configuration profile schema - operationId: getConfigProfileSchema - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/config-profiles: - get: - tags: [Config Profiles] - summary: List configuration profiles - operationId: listConfigProfiles - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Config Profiles] - summary: Create a configuration profile - operationId: createConfigProfile - x-astrbot-scope: config - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CreateConfigProfileRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/config-profiles/{config_id}: - get: - tags: [Config Profiles] - summary: Get a configuration profile - operationId: getConfigProfile - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/ConfigId" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Config Profiles] - summary: Replace a configuration profile - operationId: updateConfigProfileContent - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/ConfigId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - patch: - tags: [Config Profiles] - summary: Rename a configuration profile - operationId: renameConfigProfile - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/ConfigId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/RenameRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Config Profiles] - summary: Delete a configuration profile - operationId: deleteConfigProfile - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/ConfigId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/config-routes: - get: - tags: [Bot Config Routes] - summary: List UMO to configuration profile bindings - operationId: listConfigRoutes - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Bot Config Routes] - summary: Replace all UMO to configuration profile bindings - operationId: replaceConfigRoutes - x-astrbot-scope: config - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ConfigRoutesReplaceRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/config-routes/{umo}: - put: - tags: [Bot Config Routes] - summary: Bind a UMO to a configuration profile - operationId: upsertConfigRoute - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/Umo" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ConfigRouteUpsertRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Bot Config Routes] - summary: Remove a UMO configuration profile binding - operationId: deleteConfigRoute - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/Umo" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bot-types: - get: - tags: [Bots] - summary: List configurable bot types and their schemas - operationId: listBotTypes - x-astrbot-scope: bot - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bot-types/{bot_type}/registration: - post: - tags: [Bots] - summary: Run a bot type registration flow action - operationId: registerBotType - x-astrbot-scope: bot - parameters: - - name: bot_type - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BotRegistrationRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots: - get: - tags: [Bots] - summary: List created bots and their configurations - operationId: listBots - x-astrbot-scope: bot - parameters: - - name: enabled - in: query - schema: - type: boolean - - name: type - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Bots] - summary: Create a bot from configuration - operationId: createBot - x-astrbot-scope: bot - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BotConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/stats: - get: - tags: [Bots] - summary: List runtime status for all bots - operationId: listBotStats - x-astrbot-scope: bot - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/by-id: - get: - tags: [Bots] - summary: Get a bot configuration by query ID - operationId: getBotById - x-astrbot-scope: bot - parameters: - - name: bot_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Bots] - summary: Replace a bot configuration by body ID - operationId: updateBotById - x-astrbot-scope: bot - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [bot_id, config] - properties: - bot_id: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Bots] - summary: Delete a bot by query ID - operationId: deleteBotById - x-astrbot-scope: bot - parameters: - - name: bot_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/enabled: - patch: - tags: [Bots] - summary: Enable or disable a bot by body ID - operationId: setBotEnabledById - x-astrbot-scope: bot - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [bot_id, enabled] - properties: - bot_id: - type: string - enabled: - type: boolean - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/test: - post: - tags: [Bots] - summary: Test a bot registration or connection by body ID - operationId: testBotById - x-astrbot-scope: bot - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [bot_id] - properties: - bot_id: - type: string - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/{bot_id}: - get: - tags: [Bots] - summary: Get a bot configuration - operationId: getBot - x-astrbot-scope: bot - parameters: - - $ref: "#/components/parameters/BotId" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Bots] - summary: Replace a bot configuration - operationId: updateBot - x-astrbot-scope: bot - parameters: - - $ref: "#/components/parameters/BotId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BotConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Bots] - summary: Delete a bot - operationId: deleteBot - x-astrbot-scope: bot - parameters: - - $ref: "#/components/parameters/BotId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/{bot_id}/enabled: - patch: - tags: [Bots] - summary: Enable or disable a bot - operationId: setBotEnabled - x-astrbot-scope: bot - parameters: - - $ref: "#/components/parameters/BotId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EnabledPatch" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/bots/{bot_id}/test: - post: - tags: [Bots] - summary: Test a bot registration or connection - operationId: testBot - x-astrbot-scope: bot - parameters: - - $ref: "#/components/parameters/BotId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/schema: - get: - tags: [Providers] - summary: Get the overall provider configuration schema - operationId: getProviderSchema - x-astrbot-scope: provider - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources: - get: - tags: [Provider Sources] - summary: List provider sources - operationId: listProviderSources - x-astrbot-scope: provider - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Provider Sources] - summary: Create a provider source - operationId: createProviderSource - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ProviderSourceConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources/by-id: - get: - tags: [Provider Sources] - summary: Get a provider source by query ID - operationId: getProviderSourceById - x-astrbot-scope: provider - parameters: - - name: source_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Provider Sources] - summary: Update a provider source by body ID - operationId: upsertProviderSourceById - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [source_id, config] - properties: - source_id: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Provider Sources] - summary: Delete a provider source by query ID - operationId: deleteProviderSourceById - x-astrbot-scope: provider - parameters: - - name: source_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources/models: - get: - tags: [Provider Sources] - summary: List models available from a provider source by query ID - operationId: listProviderSourceModelsById - x-astrbot-scope: provider - parameters: - - name: source_id - in: query - required: true - schema: - type: string - - name: capability - in: query - schema: - $ref: "#/components/schemas/ProviderCapability" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources/providers: - get: - tags: [Providers] - summary: List providers under a provider source by query ID - operationId: listProvidersBySourceId - x-astrbot-scope: provider - parameters: - - name: source_id - in: query - required: true - schema: - type: string - - name: capability - in: query - schema: - $ref: "#/components/schemas/ProviderCapability" - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Providers] - summary: Create a provider under a provider source by body ID - operationId: createProviderInSourceById - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [source_id, config] - properties: - source_id: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources/{source_id}: - get: - tags: [Provider Sources] - summary: Get a provider source - operationId: getProviderSource - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/SourceId" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Provider Sources] - summary: Update a provider source or create it if missing - operationId: upsertProviderSource - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/SourceId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ProviderSourceConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Provider Sources] - summary: Delete a provider source and providers that reference it - operationId: deleteProviderSource - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/SourceId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources/{source_id}/models: - get: - tags: [Provider Sources] - summary: List models available from a provider source - operationId: listProviderSourceModels - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/SourceId" - - name: capability - in: query - schema: - $ref: "#/components/schemas/ProviderCapability" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/provider-sources/{source_id}/providers: - get: - tags: [Providers] - summary: List providers under a provider source - operationId: listProvidersBySource - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/SourceId" - - name: capability - in: query - schema: - $ref: "#/components/schemas/ProviderCapability" - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Providers] - summary: Create a provider under a provider source - operationId: createProviderInSource - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/SourceId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ProviderConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers: - get: - tags: [Providers] - summary: List providers by capability or source - operationId: listProviders - x-astrbot-scope: provider - parameters: - - name: capability - in: query - schema: - $ref: "#/components/schemas/ProviderCapability" - - name: source_id - in: query - schema: - type: string - - name: enabled - in: query - schema: - type: boolean - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Providers] - summary: Create a standalone provider - operationId: createProvider - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ProviderConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/by-id: - get: - tags: [Providers] - summary: Get a provider configuration by query ID - operationId: getProviderById - x-astrbot-scope: provider - parameters: - - name: provider_id - in: query - required: true - schema: - type: string - - name: merged - in: query - schema: - type: boolean - default: false - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Providers] - summary: Replace a provider configuration by body ID - operationId: updateProviderById - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [provider_id, config] - properties: - provider_id: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Providers] - summary: Delete a provider by query ID - operationId: deleteProviderById - x-astrbot-scope: provider - parameters: - - name: provider_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/enabled: - patch: - tags: [Providers] - summary: Enable or disable a provider by body ID - operationId: setProviderEnabledById - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [provider_id, enabled] - properties: - provider_id: - type: string - enabled: - type: boolean - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/test: - post: - tags: [Providers] - summary: Test a provider by body ID - operationId: testProviderById - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [provider_id] - properties: - provider_id: - type: string - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/embedding-dimension: - post: - tags: [Providers] - summary: Probe embedding dimension for a provider by body ID - operationId: getProviderEmbeddingDimensionById - x-astrbot-scope: provider - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [provider_id] - properties: - provider_id: - type: string - provider_config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/{provider_id}: - get: - tags: [Providers] - summary: Get a provider configuration - operationId: getProvider - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/ProviderId" - - name: merged - in: query - schema: - type: boolean - default: false - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Providers] - summary: Replace a provider configuration - operationId: updateProvider - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/ProviderId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ProviderConfigRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Providers] - summary: Delete a provider - operationId: deleteProvider - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/ProviderId" - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/{provider_id}/enabled: - patch: - tags: [Providers] - summary: Enable or disable a provider - operationId: setProviderEnabled - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/ProviderId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EnabledPatch" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/{provider_id}/test: - post: - tags: [Providers] - summary: Test a provider - operationId: testProvider - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/ProviderId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/providers/{provider_id}/embedding-dimension: - post: - tags: [Providers] - summary: Probe embedding dimension for a provider - operationId: getProviderEmbeddingDimension - x-astrbot-scope: provider - parameters: - - $ref: "#/components/parameters/ProviderId" - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat: - post: - tags: [Chat] - summary: Send a webchat message - operationId: sendChatMessage - x-astrbot-scope: chat - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/ws: - get: - tags: [Chat] - summary: Open a streaming chat WebSocket - operationId: openChatWebSocket - x-websocket: true - x-astrbot-scope: chat - parameters: - - name: api_key - in: query - schema: - type: string - - name: key - in: query - schema: - type: string - responses: - "101": - description: WebSocket upgrade accepted - - /api/v1/live-chat/ws: - get: - tags: [Chat] - summary: Open the live voice chat WebSocket - operationId: openLiveChatWebSocket - x-websocket: true - x-astrbot-scope: chat - parameters: - - name: token - in: query - required: true - schema: - type: string - responses: - "101": - description: WebSocket upgrade accepted - - /api/v1/unified-chat/ws: - get: - tags: [Chat] - summary: Open the unified live/chat WebSocket - operationId: openUnifiedChatWebSocket - x-websocket: true - x-astrbot-scope: chat - parameters: - - name: token - in: query - required: true - schema: - type: string - responses: - "101": - description: WebSocket upgrade accepted - - /api/v1/chat/sessions: - get: - tags: [Chat] - summary: List webchat sessions - operationId: listChatSessions - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: username - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/sessions/new: - get: - tags: [Chat] - summary: Create a webchat session - operationId: createChatSession - x-astrbot-scope: chat - parameters: - - name: platform_id - in: query - schema: - type: string - default: webchat - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/sessions/batch-delete: - post: - tags: [Chat] - summary: Delete multiple webchat sessions - operationId: batchDeleteChatSessions - x-astrbot-scope: chat - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatSessionBatchDeleteRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/sessions/{session_id}: - get: - tags: [Chat] - summary: Get a webchat session and its messages - operationId: getChatSession - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - responses: - "200": - $ref: "#/components/responses/Ok" - patch: - tags: [Chat] - summary: Update a webchat session - operationId: updateChatSession - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatSessionPatchRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Chat] - summary: Delete a webchat session - operationId: deleteChatSession - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/sessions/{session_id}/stop: - post: - tags: [Chat] - summary: Stop active webchat work for a session - operationId: stopChatSession - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/sessions/{session_id}/messages/{message_id}: - patch: - tags: [Chat] - summary: Update the latest user message in a webchat session - operationId: updateChatMessage - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - - name: message_id - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatMessagePatchRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/sessions/{session_id}/messages/{message_id}/regenerate: - post: - tags: [Chat] - summary: Regenerate a bot message in a webchat session - operationId: regenerateChatMessage - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - - name: message_id - in: path - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/ChatMessageRegenerateRequest" - responses: - "200": - description: Server-sent chat stream or an error envelope - - /api/v1/chat/configs: - get: - tags: [Chat] - summary: List chat-selectable configuration profiles - operationId: listChatConfigs - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/threads: - post: - tags: [Chat] - summary: Create or reuse a side thread from a webchat message - operationId: createChatThread - x-astrbot-scope: chat - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatThreadCreateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/threads/{thread_id}: - get: - tags: [Chat] - summary: Get a webchat side thread - operationId: getChatThread - x-astrbot-scope: chat - parameters: - - name: thread_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Chat] - summary: Delete a webchat side thread - operationId: deleteChatThread - x-astrbot-scope: chat - parameters: - - name: thread_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/threads/{thread_id}/messages: - post: - tags: [Chat] - summary: Send a message inside a webchat side thread - operationId: sendChatThreadMessage - x-astrbot-scope: chat - parameters: - - name: thread_id - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatThreadMessageRequest" - responses: - "200": - description: Server-sent chat stream or an error envelope - - /api/v1/chat/projects: - get: - tags: [Chat] - summary: List ChatUI projects - operationId: listChatProjects - x-astrbot-scope: chat - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Chat] - summary: Create a ChatUI project - operationId: createChatProject - x-astrbot-scope: chat - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatProjectRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/projects/{project_id}: - get: - tags: [Chat] - summary: Get a ChatUI project - operationId: getChatProject - x-astrbot-scope: chat - parameters: - - name: project_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - patch: - tags: [Chat] - summary: Update a ChatUI project - operationId: updateChatProject - x-astrbot-scope: chat - parameters: - - name: project_id - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ChatProjectRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Chat] - summary: Delete a ChatUI project - operationId: deleteChatProject - x-astrbot-scope: chat - parameters: - - name: project_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/projects/{project_id}/sessions: - get: - tags: [Chat] - summary: List sessions in a ChatUI project - operationId: listChatProjectSessions - x-astrbot-scope: chat - parameters: - - name: project_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/projects/{project_id}/sessions/{session_id}: - post: - tags: [Chat] - summary: Add a session to a ChatUI project - operationId: addChatProjectSession - x-astrbot-scope: chat - parameters: - - name: project_id - in: path - required: true - schema: - type: string - - $ref: "#/components/parameters/SessionId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/chat/projects/sessions/{session_id}: - delete: - tags: [Chat] - summary: Remove a session from its ChatUI project - operationId: removeChatProjectSession - x-astrbot-scope: chat - parameters: - - $ref: "#/components/parameters/SessionId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/im/messages: - post: - tags: [IM] - summary: Send a message to a UMO - operationId: sendImMessage - x-astrbot-scope: im - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ImMessageRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/im/bots: - get: - tags: [IM] - summary: List active IM bot IDs - operationId: listImBots - x-astrbot-scope: im - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/files: - post: - tags: [Files] - summary: Upload a file - operationId: uploadFile - x-astrbot-scope: file - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/FileUploadRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/file: - post: - tags: [Open API] - summary: Upload a file - operationId: uploadOpenApiFile - x-astrbot-scope: file - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/FileUploadRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - get: - tags: [Open API] - summary: Download an uploaded file by attachment ID - operationId: downloadOpenApiFile - x-astrbot-scope: file - parameters: - - $ref: "#/components/parameters/AttachmentIdQuery" - responses: - "200": - description: File content or an error envelope - content: - application/octet-stream: - schema: - type: string - format: binary - - /api/v1/files/content: - get: - tags: [Files] - summary: Get an uploaded file by stored filename - operationId: getFileByName - x-astrbot-scope: file - parameters: - - name: filename - in: query - required: true - schema: - type: string - responses: - "200": - description: File bytes or an error envelope - content: - application/octet-stream: - schema: - type: string - format: binary - - /api/v1/files/tokens/{file_token}: - get: - tags: [Files] - summary: Get a tokenized public file - operationId: getTokenFile - x-astrbot-scope: file - parameters: - - name: file_token - in: path - required: true - schema: - type: string - responses: - "200": - description: Tokenized file bytes or an error envelope - content: - application/octet-stream: - schema: - type: string - format: binary - - /api/v1/files/{attachment_id}: - get: - tags: [Files] - summary: Get attachment metadata - operationId: getAttachment - x-astrbot-scope: file - parameters: - - $ref: "#/components/parameters/AttachmentId" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Files] - summary: Delete an attachment - operationId: deleteAttachment - x-astrbot-scope: file - parameters: - - $ref: "#/components/parameters/AttachmentId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/files/{attachment_id}/content: - get: - tags: [Files] - summary: Download attachment content - operationId: downloadAttachment - x-astrbot-scope: file - parameters: - - $ref: "#/components/parameters/AttachmentId" - responses: - "200": - description: File content - content: - application/octet-stream: - schema: - type: string - format: binary - - /api/v1/plugins: - get: - tags: [Plugins] - summary: List installed plugins - operationId: listPlugins - x-astrbot-scope: plugin - parameters: - - name: include_reserved - in: query - schema: - type: boolean - default: true - - name: enabled - in: query - schema: - type: boolean - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/by-id: - get: - tags: [Plugins] - summary: Get plugin details by query ID - operationId: getPluginById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Plugins] - summary: Uninstall a plugin by query ID - operationId: uninstallPluginById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - delete_config: - type: boolean - delete_data: - type: boolean - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/config: - get: - tags: [Plugins] - summary: Get plugin configuration by query ID - operationId: getPluginConfigById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Plugins] - summary: Save plugin configuration by body ID - operationId: updatePluginConfigById - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [plugin_id, config] - properties: - plugin_id: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/config/schema: - get: - tags: [Plugins] - summary: Get plugin configuration schema by query ID - operationId: getPluginConfigSchemaById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/config-files: - get: - tags: [Plugins] - summary: List uploaded files for a plugin file configuration item by query ID - operationId: listPluginConfigFilesById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - - name: config_key - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Plugins] - summary: Upload files for a plugin file configuration item by query ID - operationId: uploadPluginConfigFilesById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - - name: config_key - in: query - required: true - schema: - type: string - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Plugins] - summary: Delete a plugin file configuration upload by query ID - operationId: deletePluginConfigFileById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginConfigFileDeleteRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/readme: - get: - tags: [Plugins] - summary: Get plugin README content by query ID - operationId: getPluginReadmeById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Text" - - /api/v1/plugins/changelog: - get: - tags: [Plugins] - summary: Get plugin CHANGELOG content by query ID - operationId: getPluginChangelogById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Text" - - /api/v1/plugins/reload: - post: - tags: [Plugins] - summary: Reload a plugin by body ID - operationId: reloadPluginById - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [plugin_id] - properties: - plugin_id: - type: string - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/enabled: - patch: - tags: [Plugins] - summary: Enable or disable a plugin by body ID - operationId: setPluginEnabledById - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [plugin_id, enabled] - properties: - plugin_id: - type: string - enabled: - type: boolean - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/pages: - get: - tags: [Plugin Pages] - summary: List plugin pages by query ID - operationId: listPluginPagesById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/page: - get: - tags: [Plugin Pages] - summary: Get plugin page entry HTML by query ID - operationId: getPluginPageById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - - name: page_name - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Html" - - /api/v1/plugins/page/assets: - get: - tags: [Plugin Pages] - summary: Get a plugin page asset by query ID - operationId: getPluginPageAssetById - x-astrbot-scope: plugin - parameters: - - name: plugin_id - in: query - required: true - schema: - type: string - - name: page_name - in: query - required: true - schema: - type: string - - name: asset_path - in: query - required: true - schema: - type: string - responses: - "200": - description: Plugin asset content - - /api/v1/plugins/{plugin_id}: - get: - tags: [Plugins] - summary: Get plugin details - operationId: getPlugin - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - delete_config: - type: boolean - delete_data: - type: boolean - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Plugins] - summary: Uninstall a plugin - operationId: uninstallPlugin - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - delete_config: - type: boolean - delete_data: - type: boolean - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/config: - get: - tags: [Plugins] - summary: Get plugin configuration - operationId: getPluginConfig - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Plugins] - summary: Save plugin configuration - operationId: updatePluginConfig - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/config/schema: - get: - tags: [Plugins] - summary: Get plugin configuration schema - operationId: getPluginConfigSchema - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/config-files/{config_key}: - get: - tags: [Plugins] - summary: List uploaded files for a plugin file configuration item - operationId: listPluginConfigFiles - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - - name: config_key - in: path - required: true - description: URL-encoded configuration key path. - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Plugins] - summary: Upload files for a plugin file configuration item - operationId: uploadPluginConfigFiles - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - - name: config_key - in: path - required: true - description: URL-encoded configuration key path. - schema: - type: string - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/config-files: - delete: - tags: [Plugins] - summary: Delete a plugin file configuration upload - operationId: deletePluginConfigFile - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginConfigFileDeleteRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/readme: - get: - tags: [Plugins] - summary: Get plugin README content - operationId: getPluginReadme - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Text" - - /api/v1/plugins/{plugin_id}/changelog: - get: - tags: [Plugins] - summary: Get plugin CHANGELOG content - operationId: getPluginChangelog - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Text" - - /api/v1/plugins/{plugin_id}/reload: - post: - tags: [Plugins] - summary: Reload a plugin - operationId: reloadPlugin - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/source: - post: - tags: [Plugins] - summary: Bind an installed plugin to a marketplace source - operationId: bindPluginSource - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginSourceBindRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/enabled: - patch: - tags: [Plugins] - summary: Enable or disable a plugin - operationId: setPluginEnabled - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EnabledPatch" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/update: - post: - tags: [Plugins] - summary: Update or reinstall a plugin - operationId: updatePlugin - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/PluginUpdateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/update: - post: - tags: [Plugins] - summary: Update multiple plugins - operationId: updatePlugins - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginBatchUpdateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/version-support/check: - post: - tags: [Plugins] - summary: Check whether a plugin version constraint is supported - operationId: checkPluginVersionSupport - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginVersionSupportRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/validate/repo: - post: - tags: [Plugins] - summary: Validate a GitHub plugin repository before installation - operationId: validatePluginRepo - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginValidateRepoRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/failed: - get: - tags: [Plugins] - summary: List failed plugins and errors - operationId: listFailedPlugins - x-astrbot-scope: plugin - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/failed/{plugin_id}: - delete: - tags: [Plugins] - summary: Uninstall a failed plugin - operationId: uninstallFailedPlugin - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - delete_config: - type: boolean - delete_data: - type: boolean - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/failed/{plugin_id}/reload: - post: - tags: [Plugins] - summary: Reload a failed plugin - operationId: reloadFailedPlugin - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/install/github: - post: - tags: [Plugins] - summary: Install a plugin from GitHub - operationId: installPluginFromGithub - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginGithubInstallRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - /api/v1/plugins/install/url: - post: - tags: [Plugins] - summary: Install a plugin from a downloadable ZIP URL - operationId: installPluginFromUrl - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginUrlInstallRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/install/upload: - post: - tags: [Plugins] - summary: Install a plugin from an uploaded ZIP file - operationId: installPluginFromUpload - x-astrbot-scope: plugin - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/PluginUploadInstallRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/market: - get: - tags: [Plugins] - summary: List plugin marketplace entries - operationId: listPluginMarket - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: category - in: query - schema: - type: string - - name: sort - in: query - schema: - type: string - enum: [recommended, downloads, updated, name] - - name: keyword - in: query - schema: - type: string - - name: force_refresh - in: query - schema: - type: boolean - - name: custom_registry - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/market/categories: - get: - tags: [Plugins] - summary: List plugin marketplace categories - operationId: listPluginMarketCategories - x-astrbot-scope: plugin - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugin-sources: - get: - tags: [Plugin Sources] - summary: List plugin sources - operationId: listPluginSources - x-astrbot-scope: plugin - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Plugin Sources] - summary: Add a plugin source - operationId: createPluginSource - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PluginSourceRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Plugin Sources] - summary: Replace plugin sources - operationId: replacePluginSources - x-astrbot-scope: plugin - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [sources] - properties: - sources: - type: array - items: - $ref: "#/components/schemas/PluginSourceRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugin-sources/{source_id}: - delete: - tags: [Plugin Sources] - summary: Delete a plugin source - operationId: deletePluginSource - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/SourceId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugin-sources/by-id: - delete: - tags: [Plugin Sources] - summary: Delete a plugin source by query ID - operationId: deletePluginSourceById - x-astrbot-scope: plugin - parameters: - - name: source_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/pages: - get: - tags: [Plugin Pages] - summary: List plugin pages - operationId: listPluginPages - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/plugins/{plugin_id}/pages/{page_name}: - get: - tags: [Plugin Pages] - summary: Get plugin page entry HTML - operationId: getPluginPage - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - - name: page_name - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Html" - - /api/v1/plugins/{plugin_id}/pages/{page_name}/assets/{asset_path}: - get: - tags: [Plugin Pages] - summary: Get a plugin page asset - operationId: getPluginPageAsset - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/PluginId" - - name: page_name - in: path - required: true - schema: - type: string - - name: asset_path - in: path - required: true - description: URL-encoded relative asset path. - schema: - type: string - responses: - "200": - description: Plugin asset content - - /api/v1/plugins/page-bridge-sdk.js: - get: - tags: [Plugin Pages] - summary: Get the plugin page bridge SDK - operationId: getPluginPageBridgeSdk - x-astrbot-scope: plugin - responses: - "200": - description: JavaScript bridge SDK - content: - application/javascript: - schema: - type: string - - /api/v1/plugins/extensions/{plugin_path}: - get: - tags: [Plugin Pages] - summary: Proxy a plugin extension GET route - operationId: getPluginExtensionRoute - x-astrbot-scope: plugin - parameters: - - name: plugin_path - in: path - required: true - description: Plugin extension path after /api/plug/. It may contain slash-separated segments. - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Plugin Pages] - summary: Proxy a plugin extension POST route - operationId: postPluginExtensionRoute - x-astrbot-scope: plugin - parameters: - - name: plugin_path - in: path - required: true - description: Plugin extension path after /api/plug/. It may contain slash-separated segments. - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - multipart/form-data: - schema: - type: object - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Plugin Pages] - summary: Proxy a plugin extension PUT route - operationId: putPluginExtensionRoute - x-astrbot-scope: plugin - parameters: - - name: plugin_path - in: path - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - patch: - tags: [Plugin Pages] - summary: Proxy a plugin extension PATCH route - operationId: patchPluginExtensionRoute - x-astrbot-scope: plugin - parameters: - - name: plugin_path - in: path - required: true - schema: - type: string - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Plugin Pages] - summary: Proxy a plugin extension DELETE route - operationId: deletePluginExtensionRoute - x-astrbot-scope: plugin - parameters: - - name: plugin_path - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/commands: - get: - tags: [Commands] - summary: List plugin commands - operationId: listCommands - x-astrbot-scope: plugin - parameters: - - name: config_id - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/commands/{command_id}: - patch: - tags: [Commands] - summary: Update command enabled state, alias, or permission group - operationId: updateCommand - x-astrbot-scope: plugin - parameters: - - $ref: "#/components/parameters/CommandId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CommandPatchRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/commands/conflicts: - get: - tags: [Commands] - summary: List command conflicts - operationId: listCommandConflicts - x-astrbot-scope: plugin - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/tools: - get: - tags: [Tools] - summary: List LLM tools - operationId: listTools - x-astrbot-scope: tool - parameters: - - name: origin - in: query - schema: - type: string - enum: [builtin, plugin, mcp] - - name: enabled - in: query - schema: - type: boolean - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/tools/{tool_id}/enabled: - patch: - tags: [Tools] - summary: Enable or disable an LLM tool - operationId: setToolEnabled - x-astrbot-scope: tool - parameters: - - $ref: "#/components/parameters/ToolId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EnabledPatch" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/tools/{tool_id}/permission: - patch: - tags: [Tools] - summary: Update an LLM tool permission - operationId: setToolPermission - x-astrbot-scope: tool - parameters: - - $ref: "#/components/parameters/ToolId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ToolPermissionPatch" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers: - get: - tags: [MCP] - summary: List MCP servers - operationId: listMcpServers - x-astrbot-scope: mcp - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [MCP] - summary: Add an MCP server - operationId: createMcpServer - x-astrbot-scope: mcp - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/McpServerConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers/by-name: - put: - tags: [MCP] - summary: Update an MCP server by body name - operationId: updateMcpServerByName - x-astrbot-scope: mcp - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [server_name] - properties: - server_name: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - enabled: - type: boolean - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [MCP] - summary: Delete an MCP server by query name - operationId: deleteMcpServerByName - x-astrbot-scope: mcp - parameters: - - name: server_name - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers/enabled: - patch: - tags: [MCP] - summary: Enable or disable an MCP server by body name - operationId: setMcpServerEnabledByName - x-astrbot-scope: mcp - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [server_name, enabled] - properties: - server_name: - type: string - enabled: - type: boolean - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers/test: - post: - tags: [MCP] - summary: Test an MCP server connection by body name - operationId: testMcpServerByName - x-astrbot-scope: mcp - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [server_name] - properties: - server_name: - type: string - mcp_server_config: - $ref: "#/components/schemas/DynamicConfig" - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers/{server_name}: - put: - tags: [MCP] - summary: Update an MCP server - operationId: updateMcpServer - x-astrbot-scope: mcp - parameters: - - $ref: "#/components/parameters/ServerName" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/McpServerConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [MCP] - summary: Delete an MCP server - operationId: deleteMcpServer - x-astrbot-scope: mcp - parameters: - - $ref: "#/components/parameters/ServerName" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers/{server_name}/enabled: - patch: - tags: [MCP] - summary: Enable or disable an MCP server - operationId: setMcpServerEnabled - x-astrbot-scope: mcp - parameters: - - $ref: "#/components/parameters/ServerName" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/EnabledPatch" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/servers/{server_name}/test: - post: - tags: [MCP] - summary: Test an MCP server connection - operationId: testMcpServer - x-astrbot-scope: mcp - parameters: - - $ref: "#/components/parameters/ServerName" - requestBody: - required: false - content: - application/json: - schema: - type: object - properties: - mcp_server_config: - type: object - additionalProperties: true - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/mcp/providers/modelscope/sync: - post: - tags: [MCP] - summary: Sync MCP servers from ModelScope - operationId: syncModelScopeMcpServers - x-astrbot-scope: mcp - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/ModelScopeSyncRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills: - get: - tags: [Skills] - summary: List skills - operationId: listSkills - x-astrbot-scope: skill - parameters: - - name: enabled - in: query - schema: - type: boolean - - name: source - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Skills] - summary: Upload or import a skill archive - operationId: uploadSkill - x-astrbot-scope: skill - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/SkillUploadRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/batch: - post: - tags: [Skills] - summary: Upload multiple skill archives - operationId: uploadSkillsBatch - x-astrbot-scope: skill - requestBody: - required: true - content: - multipart/form-data: - schema: - type: object - required: [files] - properties: - files: - type: array - items: - type: string - format: binary - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/by-name: - patch: - tags: [Skills] - summary: Update skill metadata or enabled state by body name - operationId: updateSkillByName - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [skill_name] - properties: - skill_name: - type: string - enabled: - type: boolean - active: - type: boolean - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Skills] - summary: Delete a skill by query name - operationId: deleteSkillByName - x-astrbot-scope: skill - parameters: - - name: skill_name - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/archive: - get: - tags: [Skills] - summary: Download a skill archive by query name - operationId: downloadSkillByName - x-astrbot-scope: skill - parameters: - - name: skill_name - in: query - required: true - schema: - type: string - responses: - "200": - description: Skill archive - content: - application/zip: - schema: - type: string - format: binary - - /api/v1/skills/files: - get: - tags: [Skills] - summary: List files in a skill by query name - operationId: listSkillFilesByName - x-astrbot-scope: skill - parameters: - - name: skill_name - in: query - required: true - schema: - type: string - - name: path - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/file: - get: - tags: [Skills] - summary: Get skill file content by query name and path - operationId: getSkillFileByName - x-astrbot-scope: skill - parameters: - - name: skill_name - in: query - required: true - schema: - type: string - - name: path - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Text" - put: - tags: [Skills] - summary: Update skill file content by body name and path - operationId: updateSkillFileByName - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [skill_name, path, content] - properties: - skill_name: - type: string - path: - type: string - content: - type: string - additionalProperties: false - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/{skill_name}: - patch: - tags: [Skills] - summary: Update skill metadata or enabled state - operationId: updateSkill - x-astrbot-scope: skill - parameters: - - $ref: "#/components/parameters/SkillName" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SkillPatchRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Skills] - summary: Delete a skill - operationId: deleteSkill - x-astrbot-scope: skill - parameters: - - $ref: "#/components/parameters/SkillName" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/{skill_name}/archive: - get: - tags: [Skills] - summary: Download a skill archive - operationId: downloadSkill - x-astrbot-scope: skill - parameters: - - $ref: "#/components/parameters/SkillName" - responses: - "200": - description: Skill archive - content: - application/zip: - schema: - type: string - format: binary - - /api/v1/skills/{skill_name}/files: - get: - tags: [Skills] - summary: List files in a skill - operationId: listSkillFiles - x-astrbot-scope: skill - parameters: - - $ref: "#/components/parameters/SkillName" - - name: path - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/{skill_name}/files/{file_path}: - get: - tags: [Skills] - summary: Get skill file content - operationId: getSkillFile - x-astrbot-scope: skill - parameters: - - $ref: "#/components/parameters/SkillName" - - $ref: "#/components/parameters/FilePath" - responses: - "200": - $ref: "#/components/responses/Text" - put: - tags: [Skills] - summary: Update skill file content - operationId: updateSkillFile - x-astrbot-scope: skill - parameters: - - $ref: "#/components/parameters/SkillName" - - $ref: "#/components/parameters/FilePath" - requestBody: - required: true - content: - text/plain: - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/candidates: - get: - tags: [Skills] - summary: List Shipyard Neo skill candidates - operationId: listNeoSkillCandidates - x-astrbot-scope: skill - parameters: - - name: skill_key - in: query - schema: - type: string - - name: status - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/releases: - get: - tags: [Skills] - summary: List Shipyard Neo skill releases - operationId: listNeoSkillReleases - x-astrbot-scope: skill - parameters: - - name: skill_key - in: query - schema: - type: string - - name: stage - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/payload: - get: - tags: [Skills] - summary: Get a Shipyard Neo skill payload - operationId: getNeoSkillPayload - x-astrbot-scope: skill - parameters: - - name: payload_ref - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/evaluate: - post: - tags: [Skills] - summary: Evaluate a Shipyard Neo skill candidate - operationId: evaluateNeoSkillCandidate - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NeoCandidateActionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/promote: - post: - tags: [Skills] - summary: Promote a Shipyard Neo skill candidate - operationId: promoteNeoSkillCandidate - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NeoCandidateActionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/rollback: - post: - tags: [Skills] - summary: Roll back a Shipyard Neo skill release - operationId: rollbackNeoSkillRelease - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NeoReleaseActionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/sync: - post: - tags: [Skills] - summary: Sync a Shipyard Neo skill release - operationId: syncNeoSkillRelease - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NeoReleaseActionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/candidates/delete: - post: - tags: [Skills] - summary: Delete a Shipyard Neo skill candidate - operationId: deleteNeoSkillCandidate - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NeoCandidateActionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/skills/neo/releases/delete: - post: - tags: [Skills] - summary: Delete a Shipyard Neo skill release - operationId: deleteNeoSkillRelease - x-astrbot-scope: skill - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NeoReleaseActionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases: - get: - tags: [Knowledge Base] - summary: List knowledge bases - operationId: listKnowledgeBases - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: refresh_stats - in: query - schema: - type: boolean - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Knowledge Base] - summary: Create a knowledge base - operationId: createKnowledgeBase - x-astrbot-scope: kb - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/KnowledgeBaseCreateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}: - get: - tags: [Knowledge Base] - summary: Get a knowledge base - operationId: getKnowledgeBase - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Knowledge Base] - summary: Update a knowledge base - operationId: updateKnowledgeBase - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/KnowledgeBaseRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Knowledge Base] - summary: Delete a knowledge base - operationId: deleteKnowledgeBase - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/stats: - get: - tags: [Knowledge Base] - summary: Get knowledge base stats - operationId: getKnowledgeBaseStats - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/documents: - get: - tags: [Knowledge Base] - summary: List knowledge base documents - operationId: listKnowledgeDocuments - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: search - in: query - required: false - schema: - type: string - description: Filter documents by name (case-insensitive partial match). - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Knowledge Base] - summary: Upload a document to a knowledge base - operationId: uploadKnowledgeDocument - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/KnowledgeDocumentUploadRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/documents/import: - post: - tags: [Knowledge Base] - summary: Import documents already available on the server - operationId: importKnowledgeDocuments - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/KnowledgeDocumentImportRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/documents/import-url: - post: - tags: [Knowledge Base] - summary: Import a document from URL - operationId: importKnowledgeDocumentFromUrl - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/KnowledgeDocumentUrlImportRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/documents/{document_id}: - get: - tags: [Knowledge Base] - summary: Get a knowledge base document - operationId: getKnowledgeDocument - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - - $ref: "#/components/parameters/DocumentId" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Knowledge Base] - summary: Delete a knowledge base document - operationId: deleteKnowledgeDocument - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - - $ref: "#/components/parameters/DocumentId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/chunks: - get: - tags: [Knowledge Base] - summary: List document chunks - operationId: listKnowledgeChunks - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - - name: document_id - in: query - schema: - type: string - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/chunks/{chunk_id}: - delete: - tags: [Knowledge Base] - summary: Delete a document chunk - operationId: deleteKnowledgeChunk - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - - $ref: "#/components/parameters/ChunkId" - - name: document_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/{kb_id}/retrieve: - post: - tags: [Knowledge Base] - summary: Retrieve knowledge base chunks - operationId: retrieveKnowledgeBase - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/KbId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/KnowledgeRetrieveRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/knowledge-bases/tasks/{task_id}: - get: - tags: [Knowledge Base] - summary: Get knowledge base import task progress - operationId: getKnowledgeTask - x-astrbot-scope: kb - parameters: - - $ref: "#/components/parameters/TaskId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/personas/tree: - get: - tags: [Personas] - summary: Get persona folder tree - operationId: getPersonaTree - x-astrbot-scope: persona - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/personas: - get: - tags: [Personas] - summary: List personas - operationId: listPersonas - x-astrbot-scope: persona - parameters: - - name: folder_id - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Personas] - summary: Create a persona - operationId: createPersona - x-astrbot-scope: persona - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PersonaRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/personas/by-id: - get: - tags: [Personas] - summary: Get a persona by query ID - operationId: getPersonaById - x-astrbot-scope: persona - parameters: - - name: persona_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Personas] - summary: Update a persona by body ID - operationId: updatePersonaById - x-astrbot-scope: persona - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [persona_id] - properties: - persona_id: - type: string - additionalProperties: true - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Personas] - summary: Delete a persona by query ID - operationId: deletePersonaById - x-astrbot-scope: persona - parameters: - - name: persona_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/personas/{persona_id}: - get: - tags: [Personas] - summary: Get a persona - operationId: getPersona - x-astrbot-scope: persona - parameters: - - $ref: "#/components/parameters/PersonaId" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Personas] - summary: Update a persona - operationId: updatePersona - x-astrbot-scope: persona - parameters: - - $ref: "#/components/parameters/PersonaId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PersonaRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Personas] - summary: Delete a persona - operationId: deletePersona - x-astrbot-scope: persona - parameters: - - $ref: "#/components/parameters/PersonaId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/persona-folders: - get: - tags: [Personas] - summary: List persona folders - operationId: listPersonaFolders - x-astrbot-scope: persona - parameters: - - name: parent_id - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Personas] - summary: Create a persona folder - operationId: createPersonaFolder - x-astrbot-scope: persona - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PersonaFolderRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/persona-folders/{folder_id}: - put: - tags: [Personas] - summary: Update a persona folder - operationId: updatePersonaFolder - x-astrbot-scope: persona - parameters: - - $ref: "#/components/parameters/FolderId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PersonaFolderRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Personas] - summary: Delete a persona folder - operationId: deletePersonaFolder - x-astrbot-scope: persona - parameters: - - $ref: "#/components/parameters/FolderId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/personas/move: - post: - tags: [Personas] - summary: Move a persona or folder - operationId: movePersonaItem - x-astrbot-scope: persona - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PersonaMoveRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/personas/reorder: - post: - tags: [Personas] - summary: Reorder personas or folders - operationId: reorderPersonaItems - x-astrbot-scope: persona - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ReorderRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/sessions: - get: - tags: [Sessions] - summary: List active UMOs and session status - operationId: listSessions - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: search - in: query - schema: - type: string - - name: platform - in: query - schema: - type: string - - name: message_type - in: query - schema: - type: string - enum: [all, group, private] - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/sessions/active-umos: - get: - tags: [Sessions] - summary: List active and known UMOs - operationId: listActiveUmos - x-astrbot-scope: data - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/sessions/rules: - get: - tags: [Sessions] - summary: List session rules - operationId: listSessionRules - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: search - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Sessions] - summary: Update or create a session rule - operationId: upsertSessionRule - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SessionRuleRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/sessions/rules/delete: - post: - tags: [Sessions] - summary: Delete one or more session rules - operationId: deleteSessionRules - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/UmoListRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/sessions/provider: - patch: - tags: [Sessions] - summary: Batch update session provider selection - operationId: batchUpdateSessionProvider - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BatchSessionProviderRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/sessions/service: - patch: - tags: [Sessions] - summary: Batch update session service settings - operationId: batchUpdateSessionService - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BatchSessionServiceRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/session-groups: - get: - tags: [Sessions] - summary: List session groups - operationId: listSessionGroups - x-astrbot-scope: data - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Sessions] - summary: Create a session group - operationId: createSessionGroup - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SessionGroupRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/session-groups/{group_id}: - put: - tags: [Sessions] - summary: Update a session group - operationId: updateSessionGroup - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/GroupId" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/SessionGroupRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Sessions] - summary: Delete a session group - operationId: deleteSessionGroup - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/GroupId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/conversations: - get: - tags: [Conversations] - summary: List conversation history data - operationId: listConversations - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - - name: platform_id - in: query - schema: - type: string - - name: user_id - in: query - schema: - type: string - - name: search - in: query - schema: - type: string - - name: platforms - in: query - schema: - type: string - description: Comma-separated platform IDs. - - name: message_types - in: query - schema: - type: string - description: Comma-separated message types. - - name: exclude_ids - in: query - schema: - type: string - description: Comma-separated user IDs to exclude. - - name: exclude_platforms - in: query - schema: - type: string - description: Comma-separated platforms to exclude. - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/conversations/batch-delete: - post: - tags: [Conversations] - summary: Delete multiple conversations - operationId: batchDeleteConversations - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ConversationBatchDeleteRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/conversations/{conversation_id}: - get: - tags: [Conversations] - summary: Get conversation details and messages - operationId: getConversation - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/ConversationId" - - name: user_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - patch: - tags: [Conversations] - summary: Update conversation metadata - operationId: updateConversation - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/ConversationId" - - name: user_id - in: query - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ConversationPatchRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Conversations] - summary: Delete a conversation - operationId: deleteConversation - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/ConversationId" - - name: user_id - in: query - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/conversations/{conversation_id}/messages: - put: - tags: [Conversations] - summary: Replace conversation message history - operationId: replaceConversationMessages - x-astrbot-scope: data - parameters: - - $ref: "#/components/parameters/ConversationId" - - name: user_id - in: query - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ConversationMessagesReplaceRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/conversations/export: - post: - tags: [Conversations] - summary: Export conversations - operationId: exportConversations - x-astrbot-scope: data - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/ConversationExportRequest" - responses: - "200": - description: Exported conversation data - - /api/v1/stats: - get: - tags: [Stats] - summary: Get runtime statistics - operationId: getStats - x-astrbot-scope: system - parameters: - - name: offset_sec - in: query - schema: - type: integer - default: 86400 - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/provider-tokens: - get: - tags: [Stats] - summary: Get provider token usage statistics - operationId: getProviderTokenStats - x-astrbot-scope: system - parameters: - - name: days - in: query - schema: - type: integer - minimum: 1 - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/version: - get: - tags: [Stats] - summary: Get AstrBot version - operationId: getVersion - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/versions: - get: - tags: [Stats] - summary: Get public WebUI and AstrBot versions - operationId: getPublicVersions - security: [] - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/first-notice: - get: - tags: [Stats] - summary: Get first-run dashboard notice content - operationId: getFirstNotice - x-astrbot-scope: system - parameters: - - name: locale - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/ghproxy/test: - post: - tags: [Stats] - summary: Test a GitHub proxy endpoint - operationId: testGhproxyConnection - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/GhproxyTestRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/changelogs: - get: - tags: [Stats] - summary: List available changelog versions - operationId: listChangelogVersions - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/changelogs/{version}: - get: - tags: [Stats] - summary: Get changelog content for a version - operationId: getChangelog - x-astrbot-scope: system - parameters: - - name: version - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - "404": - $ref: "#/components/responses/Error" - - /api/v1/stats/start-time: - get: - tags: [Stats] - summary: Get runtime start time - operationId: getStartTime - security: [] - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/storage: - get: - tags: [Stats] - summary: Get storage status - operationId: getStorageStatus - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/stats/storage/cleanup: - post: - tags: [Stats] - summary: Clean storage - operationId: cleanupStorage - x-astrbot-scope: system - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/system/restart: - post: - tags: [Stats] - summary: Restart AstrBot core - operationId: restartCore - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups: - get: - tags: [Backups] - summary: List backups - operationId: listBackups - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/Page" - - $ref: "#/components/parameters/PageSize" - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Backups] - summary: Export a backup - operationId: createBackup - x-astrbot-scope: system - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/BackupExportRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/upload: - post: - tags: [Backups] - summary: Upload a backup file - operationId: uploadBackup - x-astrbot-scope: system - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/BackupUploadRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/upload/init: - post: - tags: [Backups] - summary: Initialize chunked backup upload - operationId: initBackupUpload - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BackupUploadInitRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/upload/chunk: - post: - tags: [Backups] - summary: Upload a backup chunk - operationId: uploadBackupChunk - x-astrbot-scope: system - requestBody: - required: true - content: - multipart/form-data: - schema: - $ref: "#/components/schemas/BackupChunkUploadRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/upload/complete: - post: - tags: [Backups] - summary: Complete chunked backup upload - operationId: completeBackupUpload - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BackupUploadSessionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/upload/abort: - post: - tags: [Backups] - summary: Abort chunked backup upload - operationId: abortBackupUpload - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BackupUploadSessionRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/tasks/{task_id}: - get: - tags: [Backups] - summary: Get backup task progress - operationId: getBackupProgress - x-astrbot-scope: system - parameters: - - name: task_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/{filename}: - get: - tags: [Backups] - summary: Download a backup - operationId: downloadBackup - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/Filename" - responses: - "200": - description: Backup archive - patch: - tags: [Backups] - summary: Rename a backup - operationId: renameBackup - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/Filename" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/BackupRenameRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Backups] - summary: Delete a backup - operationId: deleteBackup - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/Filename" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/{filename}/check: - post: - tags: [Backups] - summary: Check a backup before import - operationId: checkBackup - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/Filename" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/backups/{filename}/import: - post: - tags: [Backups] - summary: Import a backup - operationId: importBackup - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/Filename" - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/BackupImportRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/updates/check: - get: - tags: [Updates] - summary: Check for updates - operationId: checkUpdate - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/updates/releases: - get: - tags: [Updates] - summary: List releases - operationId: listReleases - x-astrbot-scope: system - parameters: - - name: type - in: query - schema: - type: string - enum: [core, dashboard] - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/updates/core: - post: - tags: [Updates] - summary: Update AstrBot core - operationId: updateCore - x-astrbot-scope: system - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/updates/dashboard: - post: - tags: [Updates] - summary: Update dashboard assets - operationId: updateDashboard - x-astrbot-scope: system - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/UpdateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/updates/progress/{task_id}: - get: - tags: [Updates] - summary: Get update progress - operationId: getUpdateProgress - x-astrbot-scope: system - parameters: - - $ref: "#/components/parameters/TaskId" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/pip/install: - post: - tags: [Updates] - summary: Install a Python package - operationId: installPipPackage - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/PipInstallRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/cron/jobs: - get: - tags: [Cron] - summary: List cron jobs - operationId: listCronJobs - x-astrbot-scope: system - parameters: - - name: type - in: query - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [Cron] - summary: Create a cron job - operationId: createCronJob - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CronJobRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/cron/jobs/{job_id}: - patch: - tags: [Cron] - summary: Update a cron job - operationId: updateCronJob - x-astrbot-scope: system - parameters: - - name: job_id - in: path - required: true - schema: - type: string - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/CronJobPatchRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [Cron] - summary: Delete a cron job - operationId: deleteCronJob - x-astrbot-scope: system - parameters: - - name: job_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/cron/jobs/{job_id}/run: - post: - tags: [Cron] - summary: Run a cron job immediately - operationId: runCronJob - x-astrbot-scope: system - parameters: - - name: job_id - in: path - required: true - schema: - type: string - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/logs/live: - get: - tags: [Logs] - summary: Stream live logs - operationId: streamLiveLogs - x-astrbot-scope: system - responses: - "200": - description: Server-sent log stream - content: - text/event-stream: - schema: - type: string - - /api/v1/logs/history: - get: - tags: [Logs] - summary: Get log history - operationId: getLogHistory - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Text" - - /api/v1/trace/settings: - get: - tags: [Logs] - summary: Get trace settings - operationId: getTraceSettings - x-astrbot-scope: system - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Logs] - summary: Update trace settings - operationId: updateTraceSettings - x-astrbot-scope: system - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/TraceSettingsRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/t2i/templates: - get: - tags: [T2I] - summary: List text-to-image templates - operationId: listT2iTemplates - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - post: - tags: [T2I] - summary: Create a text-to-image template - operationId: createT2iTemplate - x-astrbot-scope: config - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/T2iTemplateRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/t2i/templates/active: - get: - tags: [T2I] - summary: Get active text-to-image template - operationId: getActiveT2iTemplate - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [T2I] - summary: Set active text-to-image template - operationId: setActiveT2iTemplate - x-astrbot-scope: config - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/NameRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/t2i/templates/default/reset: - post: - tags: [T2I] - summary: Reset the default text-to-image template - operationId: resetDefaultT2iTemplate - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/t2i/templates/{name}: - get: - tags: [T2I] - summary: Get a text-to-image template - operationId: getT2iTemplate - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/Name" - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [T2I] - summary: Update a text-to-image template - operationId: updateT2iTemplate - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/Name" - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/T2iTemplateContentRequest" - responses: - "200": - $ref: "#/components/responses/Ok" - delete: - tags: [T2I] - summary: Delete a text-to-image template - operationId: deleteT2iTemplate - x-astrbot-scope: config - parameters: - - $ref: "#/components/parameters/Name" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/subagents/config: - get: - tags: [Subagents] - summary: Get subagent orchestrator configuration - operationId: getSubagentConfig - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - put: - tags: [Subagents] - summary: Update subagent orchestrator configuration - operationId: updateSubagentConfig - x-astrbot-scope: config - requestBody: - required: true - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/subagents/available-tools: - get: - tags: [Subagents] - summary: List tools available to subagents - operationId: listSubagentAvailableTools - x-astrbot-scope: config - responses: - "200": - $ref: "#/components/responses/Ok" - - /api/v1/webhooks/platforms/{webhook_uuid}: - get: - tags: [Webhooks] - summary: Verify a platform webhook - operationId: verifyPlatformWebhook - security: [] - parameters: - - $ref: "#/components/parameters/WebhookUuid" - responses: - "200": - description: Webhook verification response - post: - tags: [Webhooks] - summary: Receive a platform webhook - operationId: receivePlatformWebhook - security: [] - parameters: - - $ref: "#/components/parameters/WebhookUuid" - requestBody: - required: false - content: - application/json: - schema: - $ref: "#/components/schemas/DynamicConfig" - application/octet-stream: - schema: - type: string - format: binary - responses: - "200": - description: Webhook accepted - -components: - securitySchemes: - ApiKeyAuth: - type: apiKey - in: header - name: X-API-Key - BearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - - responses: - Ok: - description: Standard AstrBot success response - content: - application/json: - schema: - $ref: "#/components/schemas/SuccessEnvelope" - Error: - description: Standard AstrBot error response - content: - application/json: - schema: - $ref: "#/components/schemas/ErrorEnvelope" - Text: - description: Text response - content: - text/plain: - schema: - type: string - application/json: - schema: - $ref: "#/components/schemas/SuccessEnvelope" - Html: - description: HTML response - content: - text/html: - schema: - type: string - - parameters: - AttachmentId: - name: attachment_id - in: path - required: true - schema: - type: string - AttachmentIdQuery: - name: attachment_id - in: query - required: true - schema: - type: string - BotId: - name: bot_id - in: path - required: true - schema: - type: string - ChunkId: - name: chunk_id - in: path - required: true - schema: - type: string - CommandId: - name: command_id - in: path - required: true - description: URL-encoded command handler full name. - schema: - type: string - ConfigId: - name: config_id - in: path - required: true - schema: - type: string - ConversationId: - name: conversation_id - in: path - required: true - schema: - type: string - DocumentId: - name: document_id - in: path - required: true - schema: - type: string - FilePath: - name: file_path - in: path - required: true - description: URL-encoded relative file path. - schema: - type: string - Filename: - name: filename - in: path - required: true - schema: - type: string - FolderId: - name: folder_id - in: path - required: true - schema: - type: string - GroupId: - name: group_id - in: path - required: true - schema: - type: string - KbId: - name: kb_id - in: path - required: true - schema: - type: string - KeyId: - name: key_id - in: path - required: true - schema: - type: string - Name: - name: name - in: path - required: true - schema: - type: string - Page: - name: page - in: query - schema: - type: integer - minimum: 1 - default: 1 - PageSize: - name: page_size - in: query - schema: - type: integer - minimum: 1 - maximum: 200 - default: 20 - PersonaId: - name: persona_id - in: path - required: true - schema: - type: string - PluginId: - name: plugin_id - in: path - required: true - schema: - type: string - ProviderId: - name: provider_id - in: path - required: true - schema: - type: string - SessionId: - name: session_id - in: path - required: true - schema: - type: string - ServerName: - name: server_name - in: path - required: true - schema: - type: string - SkillName: - name: skill_name - in: path - required: true - schema: - type: string - SourceId: - name: source_id - in: path - required: true - schema: - type: string - TaskId: - name: task_id - in: path - required: true - schema: - type: string - ToolId: - name: tool_id - in: path - required: true - schema: - type: string - Umo: - name: umo - in: path - required: true - description: URL-encoded unified message origin. - schema: - type: string - WebhookUuid: - name: webhook_uuid - in: path - required: true - schema: - type: string - - schemas: - SuccessEnvelope: - type: object - required: [status, data] - properties: - status: - type: string - const: ok - message: - type: string - data: {} - additionalProperties: false - - ErrorEnvelope: - type: object - required: [status, message] - properties: - status: - type: string - const: error - message: - type: string - data: {} - additionalProperties: false - - DynamicConfig: - type: object - additionalProperties: true - - JsonSchema: - type: object - additionalProperties: true - - ProviderCapability: - type: string - enum: - - chat - - agent - - stt - - tts - - embedding - - rerank - - LoginRequest: - type: object - required: [username, password] - properties: - username: - type: string - password: - type: string - code: - type: string - description: TOTP code or recovery code when two-factor authentication is required. - trust_device_flag: - type: boolean - additionalProperties: false - - SetupAuthRequest: - type: object - required: [username, password, confirm_password] - properties: - username: - type: string - password: - type: string - confirm_password: - type: string - additionalProperties: false - - UpdateAccountRequest: - type: object - required: [password] - properties: - password: - type: string - new_password: - type: string - confirm_password: - type: string - new_username: - type: string - additionalProperties: false - - TotpSetupRequest: - type: object - properties: - code: - type: string - secret: - type: string - additionalProperties: false - - CreateApiKeyRequest: - type: object - required: [name] - properties: - name: - type: string - scopes: - type: array - items: - type: string - enum: [bot, provider, persona, im, config, chat, data, file, plugin, mcp, skill] - example: [bot, provider, persona, im, config, chat, data, file, plugin, mcp, skill] - expires_at: - type: string - format: date-time - expires_in_days: - type: integer - minimum: 1 - additionalProperties: false - - CreateConfigProfileRequest: - type: object - properties: - name: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - - RenameRequest: - type: object - required: [name] - properties: - name: - type: string - additionalProperties: false - - NameRequest: - type: object - required: [name] - properties: - name: - type: string - additionalProperties: false - - ConfigRoutesReplaceRequest: - type: object - required: [routing] - properties: - routing: - type: object - additionalProperties: - type: string - additionalProperties: false - - ConfigRouteUpsertRequest: - type: object - required: [config_id] - properties: - config_id: - type: string - description: Use "default" to remove a custom route and fall back. - additionalProperties: false - - BotConfigRequest: - type: object - required: [type, config] - properties: - id: - type: string - name: - type: string - type: - type: string - description: Platform adapter type, such as aiocqhttp, telegram, lark. - enabled: - type: boolean - default: true - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - - BotRegistrationRequest: - type: object - required: [action] - properties: - action: - type: string - enum: [start, poll] - platform_config: - $ref: "#/components/schemas/DynamicConfig" - registration_code: - type: string - device_code: - type: string - qrcode: - type: string - additionalProperties: true - - EnabledPatch: - type: object - required: [enabled] - properties: - enabled: - type: boolean - additionalProperties: false - - ToolPermissionPatch: - type: object - required: [permission] - properties: - permission: - type: string - enum: [admin, member] - additionalProperties: false - - ProviderSourceConfigRequest: - type: object - required: [config] - properties: - id: - type: string - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - - ProviderConfigRequest: - type: object - required: [config] - properties: - id: - type: string - provider_source_id: - type: string - capability: - $ref: "#/components/schemas/ProviderCapability" - enabled: - type: boolean - default: true - config: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: false - - ChatRequest: - type: object - required: [message] - properties: - username: - type: string - description: Caller-declared WebChat sender/session owner. This value is used as the message sender identity and may participate in sender-ID-based command permission checks. Treat chat-scoped API keys as trusted backend credentials and map or validate usernames before accepting end-user input. - session_id: - type: string - conversation_id: - type: string - description: Deprecated alias for session_id. - message: - oneOf: - - type: string - - type: array - items: - $ref: "#/components/schemas/MessagePart" - config_id: - type: string - config_name: - type: string - selected_provider: - type: string - selected_model: - type: string - enable_streaming: - type: boolean - default: true - _skip_user_history: - type: boolean - description: Internal WebUI flag for edit/regenerate flows. - _llm_checkpoint_id: - type: string - description: Internal WebUI checkpoint override. - _platform_history_id: - type: string - description: Internal WebUI platform history override. - _thread_selected_text: - type: string - description: Internal WebUI side-thread context. - additionalProperties: false - - ChatSessionBatchDeleteRequest: - type: object - required: [session_ids] - properties: - session_ids: - type: array - items: - type: string - additionalProperties: false - - ChatSessionPatchRequest: - type: object - properties: - display_name: - type: string - additionalProperties: false - - ChatMessagePatchRequest: - type: object - required: [content] - properties: - content: - type: object - additionalProperties: true - additionalProperties: false - - ChatMessageRegenerateRequest: - type: object - properties: - selected_provider: - type: string - selected_model: - type: string - enable_streaming: - type: boolean - default: true - additionalProperties: false - - ChatThreadCreateRequest: - type: object - required: [session_id, parent_message_id, selected_text] - properties: - session_id: - type: string - parent_message_id: - oneOf: - - type: string - - type: integer - selected_text: - type: string - additionalProperties: false - - ChatThreadMessageRequest: - type: object - required: [message] - properties: - message: - oneOf: - - type: string - - type: array - items: - $ref: "#/components/schemas/MessagePart" - selected_provider: - type: string - selected_model: - type: string - enable_streaming: - type: boolean - default: true - additionalProperties: false - - ChatProjectRequest: - type: object - properties: - title: - type: string - emoji: - type: string - description: - type: string - workspace_type: - type: string - enum: [session, project, custom] - workspace_path: - type: string - additionalProperties: false - - MessagePart: - type: object - required: [type] - properties: - type: - type: string - enum: [text, plain, image, file, audio, record, video, reply] - text: - type: string - attachment_id: - type: string - url: - type: string - filename: - type: string - stored_filename: - type: string - mime_type: - type: string - additionalProperties: true - - ImMessageRequest: - type: object - required: [umo, message] - properties: - umo: - type: string - message: - oneOf: - - type: string - - type: array - items: - $ref: "#/components/schemas/MessagePart" - additionalProperties: false - - FileUploadRequest: - type: object - required: [file] - properties: - file: - type: string - format: binary - - PluginUpdateRequest: - type: object - properties: - reinstall: - type: boolean - default: false - additionalProperties: false - - PluginSourceBindRequest: - type: object - properties: - install_method: - type: string - registry_url: - type: string - nullable: true - market_plugin_id: - type: string - additionalProperties: false - - PluginBatchUpdateRequest: - type: object - properties: - plugin_id: - type: string - description: When set, update this single plugin instead of a batch. - plugin_ids: - type: array - items: - type: string - reinstall: - type: boolean - default: false - update_all: - type: boolean - default: false - additionalProperties: true - - PluginVersionSupportRequest: - type: object - properties: - astrbot_version: - type: string - additionalProperties: false - - PluginGithubInstallRequest: - type: object - required: [repository] - properties: - repository: - type: string - description: GitHub URL or owner/repository slug. - ref: - type: string - download_url: - type: string - description: Optional downloadable ZIP URL to use instead of GitHub archive resolution. - proxy: - type: string - ignore_version_check: - type: boolean - install_method: - type: string - registry_url: - type: string - nullable: true - market_plugin_id: - type: string - additionalProperties: false - - PluginUrlInstallRequest: - type: object - required: [url] - properties: - url: - type: string - format: uri - download_url: - type: string - format: uri - description: Optional downloadable ZIP URL when url is the plugin source page or repository. - proxy: - type: string - ignore_version_check: - type: boolean - install_method: - type: string - registry_url: - type: string - nullable: true - market_plugin_id: - type: string - additionalProperties: false - - PluginValidateRepoRequest: - type: object - properties: - repository: - type: string - url: - type: string - proxy: - type: string - additionalProperties: false - - PluginUploadInstallRequest: - type: object - required: [file] - properties: - file: - type: string - format: binary - - PluginConfigFileDeleteRequest: - type: object - required: [path] - properties: - path: - type: string - additionalProperties: false - - PluginSourceRequest: - type: object - required: [url] - properties: - id: - type: string - name: - type: string - url: - type: string - format: uri - additionalProperties: false - - CommandPatchRequest: - type: object - properties: - enabled: - type: boolean - alias: - type: string - aliases: - type: array - items: - type: string - permission_group: - type: string - additionalProperties: false - - McpServerConfig: - type: object - required: [name] - properties: - name: - type: string - enabled: - type: boolean - default: true - transport: - type: string - enum: [stdio, sse, streamable_http] - command: - type: string - args: - type: array - items: - type: string - url: - type: string - headers: - type: object - additionalProperties: - type: string - timeout: - type: integer - additionalProperties: true - - ModelScopeSyncRequest: - type: object - properties: - access_token: - type: string - additionalProperties: false - - SkillUploadRequest: - type: object - required: [file] - properties: - file: - type: string - format: binary - overwrite: - type: boolean - default: false - - SkillPatchRequest: - type: object - properties: - enabled: - type: boolean - display_name: - type: string - description: - type: string - additionalProperties: false - - NeoCandidateActionRequest: - type: object - required: [candidate_id] - properties: - candidate_id: - type: string - additionalProperties: true - - NeoReleaseActionRequest: - type: object - required: [release_id] - properties: - release_id: - type: string - additionalProperties: true - - KnowledgeBaseRequest: - type: object - properties: - kb_name: - type: string - description: - type: string - emoji: - type: string - embedding_provider_id: - type: [string, "null"] - rerank_provider_id: - type: [string, "null"] - chunk_size: - type: integer - chunk_overlap: - type: integer - top_k_dense: - type: integer - top_k_sparse: - type: integer - top_m_final: - type: integer - additionalProperties: false - - KnowledgeBaseCreateRequest: - allOf: - - $ref: "#/components/schemas/KnowledgeBaseRequest" - - type: object - required: [kb_name, embedding_provider_id] - properties: - kb_name: - type: string - embedding_provider_id: - type: string - - KnowledgeDocumentUploadRequest: - type: object - required: [file] - properties: - file: - type: string - format: binary - - KnowledgeDocumentImportRequest: - type: object - required: [paths] - properties: - paths: - type: array - items: - type: string - parser: - type: string - additionalProperties: false - - KnowledgeDocumentUrlImportRequest: - type: object - required: [url] - properties: - url: - type: string - format: uri - parser: - type: string - additionalProperties: false - - KnowledgeRetrieveRequest: - type: object - required: [query] - properties: - query: - type: string - top_k: - type: integer - default: 5 - score_threshold: - type: number - additionalProperties: false - - PersonaRequest: - type: object - required: [persona_id, system_prompt] - properties: - persona_id: - type: string - system_prompt: - type: string - begin_dialogs: - type: array - items: - type: string - folder_id: - type: string - tools: - type: array - items: - type: string - skills: - type: array - items: - type: string - custom_error_message: - type: string - additionalProperties: true - - PersonaFolderRequest: - type: object - properties: - name: - type: string - parent_id: - type: string - description: - type: string - additionalProperties: true - - PersonaMoveRequest: - type: object - required: [persona_id] - properties: - persona_id: - type: string - folder_id: - type: string - additionalProperties: true - - ReorderRequest: - type: object - required: [items] - properties: - items: - type: array - items: - type: object - required: [id, type, sort_order] - properties: - id: - type: string - type: - type: string - enum: [persona, folder] - sort_order: - type: integer - additionalProperties: false - - SessionRuleRequest: - type: object - required: [umo, rule_key] - properties: - umo: - type: string - rule_key: - type: string - rule_value: - $ref: "#/components/schemas/DynamicConfig" - additionalProperties: true - - UmoListRequest: - type: object - properties: - umo: - type: string - umos: - type: array - items: - type: string - scope: - type: string - enum: [all, group, private, custom_group] - group_id: - type: string - rule_key: - type: string - additionalProperties: false - - BatchSessionProviderRequest: - allOf: - - $ref: "#/components/schemas/UmoListRequest" - - type: object - required: [provider_id, provider_type] - properties: - provider_id: - type: string - provider_type: - type: string - enum: [chat_completion, speech_to_text, text_to_speech] - - BatchSessionServiceRequest: - allOf: - - $ref: "#/components/schemas/UmoListRequest" - - type: object - properties: - session_enabled: - type: boolean - llm_enabled: - type: boolean - tts_enabled: - type: boolean - - SessionGroupRequest: - type: object - properties: - name: - type: string - umos: - type: array - items: - type: string - add_umos: - type: array - items: - type: string - remove_umos: - type: array - items: - type: string - additionalProperties: false - - ConversationPatchRequest: - type: object - properties: - title: - type: string - persona_id: - type: string - additionalProperties: false - - ConversationMessagesReplaceRequest: - type: object - properties: - user_id: - type: string - messages: - type: array - items: - type: object - additionalProperties: true - history: - type: array - items: - type: object - additionalProperties: true - additionalProperties: false - - ConversationRef: - type: object - required: [user_id, cid] - properties: - user_id: - type: string - cid: - type: string - additionalProperties: false - - ConversationBatchDeleteRequest: - type: object - required: [conversations] - properties: - conversations: - type: array - items: - $ref: "#/components/schemas/ConversationRef" - additionalProperties: false - - ConversationExportRequest: - type: object - properties: - conversations: - type: array - items: - $ref: "#/components/schemas/ConversationRef" - conversation_ids: - type: array - items: - type: string - format: - type: string - enum: [json, markdown] - default: json - additionalProperties: false - - CronJobRequest: - type: object - properties: - name: - type: string - cron_expression: - type: string - timezone: - type: string - session: - type: string - note: - type: string - description: - type: string - persona_id: - type: string - provider_id: - type: string - enabled: - type: boolean - default: true - run_once: - type: boolean - default: false - run_at: - type: string - format: date-time - payload: - type: object - additionalProperties: true - additionalProperties: true - - CronJobPatchRequest: - allOf: - - $ref: "#/components/schemas/CronJobRequest" - - BackupExportRequest: - type: object - properties: - include: - type: array - items: - type: string - exclude: - type: array - items: - type: string - additionalProperties: false - - BackupUploadRequest: - type: object - required: [file] - properties: - file: - type: string - format: binary - - BackupUploadInitRequest: - type: object - required: [filename, total_size] - properties: - filename: - type: string - total_size: - type: integer - minimum: 1 - additionalProperties: false - - BackupUploadSessionRequest: - type: object - required: [upload_id] - properties: - upload_id: - type: string - additionalProperties: false - - BackupChunkUploadRequest: - type: object - required: [upload_id, chunk_index, chunk] - properties: - upload_id: - type: string - chunk_index: - type: integer - minimum: 0 - chunk: - type: string - format: binary - - BackupRenameRequest: - type: object - required: [new_name] - properties: - new_name: - type: string - additionalProperties: false - - BackupImportRequest: - type: object - properties: - confirmed: - type: boolean - default: true - additionalProperties: false - - UpdateRequest: - type: object - properties: - version: - type: string - proxy: - type: string - reboot: - type: boolean - progress_id: - type: string - additionalProperties: false - - PipInstallRequest: - type: object - required: [package] - properties: - package: - type: string - mirror: - type: string - additionalProperties: false - - GhproxyTestRequest: - type: object - required: [proxy_url] - properties: - proxy_url: - type: string - additionalProperties: false - - TraceSettingsRequest: - type: object - properties: - enabled: - type: boolean - level: - type: string - additionalProperties: true - - T2iTemplateRequest: - type: object - required: [name, content] - properties: - name: - type: string - content: - type: string - additionalProperties: true - - T2iTemplateContentRequest: - type: object - required: [content] - properties: - content: - type: string - additionalProperties: true diff --git a/legacy-dashboard/package.json b/legacy-dashboard/package.json deleted file mode 100644 index 5ea17ecf..00000000 --- a/legacy-dashboard/package.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "name": "astrbot-dashboard", - "version": "1.0.0", - "private": true, - "author": "CodedThemes", - "scripts": { - "dev": "node scripts/subset-mdi-font.mjs && vite --host", - "build:t2i-shiki-runtime": "node scripts/build-t2i-shiki-runtime.mjs", - "build": "node scripts/subset-mdi-font.mjs && vue-tsc --noEmit && vite build", - "build-stage": "node scripts/subset-mdi-font.mjs && vue-tsc --noEmit && vite build --base=/vue/free/stage/", - "build-prod": "node scripts/subset-mdi-font.mjs && vue-tsc --noEmit && vite build --base=/vue/free/", - "preview": "vite preview --port 5050", - "generate:api": "openapi-ts -i ./openapi/openapi-v1.yaml -o src/api/generated/openapi-v1 -c @hey-api/client-axios", - "typecheck": "vue-tsc --noEmit", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" - }, - "dependencies": { - "@guolao/vue-monaco-editor": "^1.5.4", - "@hey-api/client-axios": "0.2.12", - "@lucide/vue": "^1.23.0", - "@tiptap/starter-kit": "2.1.7", - "@tiptap/vue-3": "2.1.7", - "apexcharts": "3.42.0", - "axios": "1.13.5", - "axios-mock-adapter": "^1.22.0", - "chance": "1.1.11", - "date-fns": "2.30.0", - "dompurify": "^3.3.2", - "event-source-polyfill": "^1.0.31", - "highlight.js": "11.11.1", - "katex": "^0.16.27", - "lodash": "4.17.23", - "markdown-it": "^14.1.1", - "markstream-vue": "1.0.5-beta.0", - "mermaid": "^11.12.2", - "monaco-editor": "^0.52.2", - "pinia": "2.1.6", - "pinyin-pro": "^3.26.0", - "qrcode": "^1.5.4", - "shiki": "^3.23.0", - "stream-markdown": "^0.0.16", - "vee-validate": "4.11.3", - "vite-plugin-vuetify": "2.1.3", - "vue": "3.3.4", - "vue-i18n": "^11.1.5", - "vue-router": "4.2.4", - "vue3-apexcharts": "1.4.4", - "vue3-print-nb": "0.1.4", - "vuetify": "3.7.11", - "yup": "1.2.0" - }, - "devDependencies": { - "@hey-api/openapi-ts": "0.60.0", - "@mdi/font": "7.2.96", - "@rushstack/eslint-patch": "1.3.3", - "@types/chance": "1.1.3", - "@types/dompurify": "^3.0.5", - "@types/markdown-it": "^14.1.2", - "@types/node": "^20.5.7", - "@vitejs/plugin-vue": "5.2.4", - "@vue/eslint-config-prettier": "8.0.0", - "@vue/eslint-config-typescript": "11.0.3", - "@vue/tsconfig": "^0.4.0", - "eslint": "8.48.0", - "eslint-plugin-vue": "9.17.0", - "prettier": "3.0.2", - "sass": "1.66.1", - "sass-loader": "13.3.2", - "subset-font": "^2.4.0", - "typescript": "5.1.6", - "vite": "6.4.1", - "vite-plugin-webfont-dl": "^3.12.0", - "vue-cli-plugin-vuetify": "2.5.8", - "vue-tsc": "1.8.8", - "vuetify-loader": "^2.0.0-alpha.9" - }, - "pnpm": { - "overrides": { - "immutable": "4.3.8", - "lodash-es": "4.17.23" - } - } -} diff --git a/legacy-dashboard/pnpm-lock.yaml b/legacy-dashboard/pnpm-lock.yaml deleted file mode 100644 index cae4d6bc..00000000 --- a/legacy-dashboard/pnpm-lock.yaml +++ /dev/null @@ -1,6424 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - immutable: 4.3.8 - lodash-es: 4.17.23 - -importers: - - .: - dependencies: - '@guolao/vue-monaco-editor': - specifier: ^1.5.4 - version: 1.6.0(monaco-editor@0.52.2)(vue@3.3.4) - '@hey-api/client-axios': - specifier: 0.2.12 - version: 0.2.12(axios@1.13.5) - '@lucide/vue': - specifier: ^1.23.0 - version: 1.23.0(vue@3.3.4) - '@tiptap/starter-kit': - specifier: 2.1.7 - version: 2.1.7(@tiptap/pm@2.27.2) - '@tiptap/vue-3': - specifier: 2.1.7 - version: 2.1.7(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(vue@3.3.4) - apexcharts: - specifier: 3.42.0 - version: 3.42.0 - axios: - specifier: 1.13.5 - version: 1.13.5 - axios-mock-adapter: - specifier: ^1.22.0 - version: 1.22.0(axios@1.13.5) - chance: - specifier: 1.1.11 - version: 1.1.11 - date-fns: - specifier: 2.30.0 - version: 2.30.0 - dompurify: - specifier: ^3.3.2 - version: 3.3.2 - event-source-polyfill: - specifier: ^1.0.31 - version: 1.0.31 - highlight.js: - specifier: 11.11.1 - version: 11.11.1 - katex: - specifier: ^0.16.27 - version: 0.16.28 - lodash: - specifier: 4.17.23 - version: 4.17.23 - markdown-it: - specifier: ^14.1.1 - version: 14.1.1 - markstream-vue: - specifier: 1.0.5-beta.0 - version: 1.0.5-beta.0(katex@0.16.28)(mermaid@11.12.2)(stream-markdown@0.0.16(shiki@3.23.0)(vue@3.3.4))(vue-i18n@11.2.8(vue@3.3.4))(vue@3.3.4) - mermaid: - specifier: ^11.12.2 - version: 11.12.2 - monaco-editor: - specifier: ^0.52.2 - version: 0.52.2 - pinia: - specifier: 2.1.6 - version: 2.1.6(typescript@5.1.6)(vue@3.3.4) - pinyin-pro: - specifier: ^3.26.0 - version: 3.28.0 - qrcode: - specifier: ^1.5.4 - version: 1.5.4 - shiki: - specifier: ^3.23.0 - version: 3.23.0 - stream-markdown: - specifier: ^0.0.16 - version: 0.0.16(shiki@3.23.0)(vue@3.3.4) - vee-validate: - specifier: 4.11.3 - version: 4.11.3(vue@3.3.4) - vite-plugin-vuetify: - specifier: 2.1.3 - version: 2.1.3(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0))(vue@3.3.4)(vuetify@3.7.11) - vue: - specifier: 3.3.4 - version: 3.3.4 - vue-i18n: - specifier: ^11.1.5 - version: 11.2.8(vue@3.3.4) - vue-router: - specifier: 4.2.4 - version: 4.2.4(vue@3.3.4) - vue3-apexcharts: - specifier: 1.4.4 - version: 1.4.4(apexcharts@3.42.0)(vue@3.3.4) - vue3-print-nb: - specifier: 0.1.4 - version: 0.1.4 - vuetify: - specifier: 3.7.11 - version: 3.7.11(typescript@5.1.6)(vite-plugin-vuetify@2.1.3)(vue@3.3.4) - yup: - specifier: 1.2.0 - version: 1.2.0 - devDependencies: - '@hey-api/openapi-ts': - specifier: 0.60.0 - version: 0.60.0(typescript@5.1.6) - '@mdi/font': - specifier: 7.2.96 - version: 7.2.96 - '@rushstack/eslint-patch': - specifier: 1.3.3 - version: 1.3.3 - '@types/chance': - specifier: 1.1.3 - version: 1.1.3 - '@types/dompurify': - specifier: ^3.0.5 - version: 3.2.0 - '@types/markdown-it': - specifier: ^14.1.2 - version: 14.1.2 - '@types/node': - specifier: ^20.5.7 - version: 20.19.32 - '@vitejs/plugin-vue': - specifier: 5.2.4 - version: 5.2.4(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0))(vue@3.3.4) - '@vue/eslint-config-prettier': - specifier: 8.0.0 - version: 8.0.0(@types/eslint@9.6.1)(eslint@8.48.0)(prettier@3.0.2) - '@vue/eslint-config-typescript': - specifier: 11.0.3 - version: 11.0.3(eslint-plugin-vue@9.17.0(eslint@8.48.0))(eslint@8.48.0)(typescript@5.1.6) - '@vue/tsconfig': - specifier: ^0.4.0 - version: 0.4.0 - eslint: - specifier: 8.48.0 - version: 8.48.0 - eslint-plugin-vue: - specifier: 9.17.0 - version: 9.17.0(eslint@8.48.0) - prettier: - specifier: 3.0.2 - version: 3.0.2 - sass: - specifier: 1.66.1 - version: 1.66.1 - sass-loader: - specifier: 13.3.2 - version: 13.3.2(sass@1.66.1)(webpack@5.105.0) - subset-font: - specifier: ^2.4.0 - version: 2.4.0 - typescript: - specifier: 5.1.6 - version: 5.1.6 - vite: - specifier: 6.4.1 - version: 6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0) - vite-plugin-webfont-dl: - specifier: ^3.12.0 - version: 3.12.0(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0)) - vue-cli-plugin-vuetify: - specifier: 2.5.8 - version: 2.5.8(sass-loader@13.3.2(sass@1.66.1)(webpack@5.105.0))(vue@3.3.4)(vuetify-loader@2.0.0-alpha.9(@vue/compiler-sfc@3.3.4)(vue@3.3.4)(vuetify@3.7.11)(webpack@5.105.0))(webpack@5.105.0) - vue-tsc: - specifier: 1.8.8 - version: 1.8.8(typescript@5.1.6) - vuetify-loader: - specifier: ^2.0.0-alpha.9 - version: 2.0.0-alpha.9(@vue/compiler-sfc@3.3.4)(vue@3.3.4)(vuetify@3.7.11)(webpack@5.105.0) - -packages: - - '@antfu/install-pkg@1.1.0': - resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - - '@apidevtools/json-schema-ref-parser@11.7.3': - resolution: {integrity: sha512-WApSdLdXEBb/1FUPca2lteASewEfpjEYJ8oXZP+0gExK5qSfsEKBKcA+WjY6Q4wvXwyv0+W6Kvc372pSceib9w==} - engines: {node: '>= 16'} - - '@babel/helper-string-parser@7.27.1': - resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.28.5': - resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.29.0': - resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} - engines: {node: '>=6.9.0'} - - '@braintree/sanitize-url@7.1.2': - resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} - - '@cacheable/memory@2.0.8': - resolution: {integrity: sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==} - - '@cacheable/utils@2.4.0': - resolution: {integrity: sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==} - - '@chenglou/pretext@0.0.5': - resolution: {integrity: sha512-A8GZN10REdFGsyuiUgLV8jjPDDFMg5GmgxGWV0I3igxBOnzj+jgz2VMmVD7g+SFyoctfeqHFxbNatKSzVRWtRg==} - - '@chevrotain/cst-dts-gen@11.0.3': - resolution: {integrity: sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==} - - '@chevrotain/gast@11.0.3': - resolution: {integrity: sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==} - - '@chevrotain/regexp-to-ast@11.0.3': - resolution: {integrity: sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==} - - '@chevrotain/types@11.0.3': - resolution: {integrity: sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==} - - '@chevrotain/utils@11.0.3': - resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} - - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.2': - resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/eslintrc@2.1.4': - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@eslint/js@8.48.0': - resolution: {integrity: sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@floating-ui/core@1.7.5': - resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} - - '@floating-ui/dom@1.7.6': - resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} - - '@floating-ui/utils@0.2.11': - resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - - '@guolao/vue-monaco-editor@1.6.0': - resolution: {integrity: sha512-w2IiJ6eJGGeuIgCK6EKZOAfhHTTUB5aZwslzwGbZ5e89Hb4avx6++GkLTW8p84Sng/arFMjLPPxSBI56cFudyQ==} - peerDependencies: - '@vue/composition-api': ^1.7.2 - monaco-editor: '>=0.43.0' - vue: ^2.6.14 || >=3.0.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - '@hey-api/client-axios@0.2.12': - resolution: {integrity: sha512-lBehVhbnhvm41cFguZuy1FO+4x8NO3Qy/ooL0Jw4bdqTu21n7DmZMPsXEF0gL7/gNdTt4QkJGwaojy+8ExtE8w==} - deprecated: Starting with v0.73.0, this package is bundled directly inside @hey-api/openapi-ts. - peerDependencies: - axios: '>= 1.0.0 < 2' - - '@hey-api/openapi-ts@0.60.0': - resolution: {integrity: sha512-6g7TuIQ40OluuKJOJGi4dVqa/EWGStcPMA1wGaKAWkEfzhepRINBR5FMlYrWB1bOeAQSrlZJyj0MHRQzY5D4sA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - typescript: ^5.x - - '@humanwhocodes/config-array@0.11.14': - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/object-schema@2.0.3': - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead - - '@iconify/types@2.0.0': - resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - - '@iconify/utils@3.1.0': - resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} - - '@intlify/core-base@11.2.8': - resolution: {integrity: sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==} - engines: {node: '>= 16'} - - '@intlify/message-compiler@11.2.8': - resolution: {integrity: sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==} - engines: {node: '>= 16'} - - '@intlify/shared@11.2.8': - resolution: {integrity: sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==} - engines: {node: '>= 16'} - - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@jsdevtools/ono@7.1.3': - resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} - - '@keyv/bigmap@1.3.1': - resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} - engines: {node: '>= 18'} - peerDependencies: - keyv: ^5.6.0 - - '@keyv/serialize@1.1.1': - resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} - - '@lucide/vue@1.23.0': - resolution: {integrity: sha512-9SIYeY5K+R1iv8F8JUKMGSL7Pck/86BJ8djtZz/lnYsoHtKFUj1H2z6+PvKnC/8blZ8tqTDeDXAoTKobuX80hg==} - peerDependencies: - vue: '>=3.0.1' - - '@mdi/font@7.2.96': - resolution: {integrity: sha512-e//lmkmpFUMZKhmCY9zdjRe4zNXfbOIJnn6xveHbaV2kSw5aJ5dLXUxcRt1Gxfi7ZYpFLUWlkG2MGSFAiqAu7w==} - - '@mermaid-js/parser@0.6.3': - resolution: {integrity: sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==} - - '@monaco-editor/loader@1.7.0': - resolution: {integrity: sha512-gIwR1HrJrrx+vfyOhYmCZ0/JcWqG5kbfG7+d3f/C1LXk2EvzAbHSg3MQ5lO2sMlo9izoAZ04shohfKLVT6crVA==} - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - - '@popperjs/core@2.11.8': - resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - - '@remirror/core-constants@3.0.0': - resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} - - '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} - cpu: [arm] - os: [android] - - '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} - cpu: [arm64] - os: [android] - - '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} - cpu: [arm64] - os: [darwin] - - '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} - cpu: [x64] - os: [darwin] - - '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} - cpu: [arm64] - os: [freebsd] - - '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} - cpu: [x64] - os: [freebsd] - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} - cpu: [arm] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} - cpu: [arm] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} - cpu: [loong64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} - cpu: [loong64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} - cpu: [ppc64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} - cpu: [riscv64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} - cpu: [arm64] - os: [win32] - - '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} - cpu: [ia32] - os: [win32] - - '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} - cpu: [x64] - os: [win32] - - '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} - cpu: [x64] - os: [win32] - - '@rushstack/eslint-patch@1.3.3': - resolution: {integrity: sha512-0xd7qez0AQ+MbHatZTlI1gu5vkG8r7MYRUJAHPAHJBmGLs16zpkrpAVLvjQKQOqaXPDUBwOiJzNc00znHSCVBw==} - - '@shikijs/core@3.22.0': - resolution: {integrity: sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA==} - - '@shikijs/core@3.23.0': - resolution: {integrity: sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA==} - - '@shikijs/engine-javascript@3.23.0': - resolution: {integrity: sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA==} - - '@shikijs/engine-oniguruma@3.23.0': - resolution: {integrity: sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==} - - '@shikijs/langs@3.23.0': - resolution: {integrity: sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==} - - '@shikijs/themes@3.23.0': - resolution: {integrity: sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==} - - '@shikijs/types@3.22.0': - resolution: {integrity: sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg==} - - '@shikijs/types@3.23.0': - resolution: {integrity: sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==} - - '@shikijs/vscode-textmate@10.0.2': - resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} - - '@tiptap/core@2.27.2': - resolution: {integrity: sha512-ABL1N6eoxzDzC1bYvkMbvyexHacszsKdVPYqhl5GwHLOvpZcv9VE9QaKwDILTyz5voCA0lGcAAXZp+qnXOk5lQ==} - peerDependencies: - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-blockquote@2.27.2': - resolution: {integrity: sha512-oIGZgiAeA4tG3YxbTDfrmENL4/CIwGuP3THtHsNhwRqwsl9SfMk58Ucopi2GXTQSdYXpRJ0ahE6nPqB5D6j/Zw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-bold@2.27.2': - resolution: {integrity: sha512-bR7J5IwjCGQ0s3CIxyMvOCnMFMzIvsc5OVZKscTN5UkXzFsaY6muUAIqtKxayBUucjtUskm5qZowJITCeCb1/A==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-bubble-menu@2.27.2': - resolution: {integrity: sha512-VkwlCOcr0abTBGzjPXklJ92FCowG7InU8+Od9FyApdLNmn0utRYGRhw0Zno6VgE9EYr1JY4BRnuSa5f9wlR72w==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-bullet-list@2.27.2': - resolution: {integrity: sha512-gmFuKi97u5f8uFc/GQs+zmezjiulZmFiDYTh3trVoLRoc2SAHOjGEB7qxdx7dsqmMN7gwiAWAEVurLKIi1lnnw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-code-block@2.27.2': - resolution: {integrity: sha512-KgvdQHS4jXr79aU3wZOGBIZYYl9vCB7uDEuRFV4so2rYrfmiYMw3T8bTnlNEEGe4RUeAms1i4fdwwvQp9nR1Dw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-code@2.27.2': - resolution: {integrity: sha512-7X9AgwqiIGXoZX7uvdHQsGsjILnN/JaEVtqfXZnPECzKGaWHeK/Ao4sYvIIIffsyZJA8k5DC7ny2/0sAgr2TuA==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-document@2.27.2': - resolution: {integrity: sha512-CFhAYsPnyYnosDC4639sCJnBUnYH4Cat9qH5NZWHVvdgtDwu8GZgZn2eSzaKSYXWH1vJ9DSlCK+7UyC3SNXIBA==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-dropcursor@2.27.2': - resolution: {integrity: sha512-oEu/OrktNoQXq1x29NnH/GOIzQZm8ieTQl3FK27nxfBPA89cNoH4mFEUmBL5/OFIENIjiYG3qWpg6voIqzswNw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-floating-menu@2.27.2': - resolution: {integrity: sha512-GUN6gPIGXS7ngRJOwdSmtBRBDt9Kt9CM/9pSwKebhLJ+honFoNA+Y6IpVyDvvDMdVNgBchiJLs6qA5H97gAePQ==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-gapcursor@2.27.2': - resolution: {integrity: sha512-/c9VF1HBxj+AP54XGVgCmD9bEGYc5w5OofYCFQgM7l7PB1J00A4vOke0oPkHJnqnOOyPlFaxO/7N6l3XwFcnKA==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-hard-break@2.27.2': - resolution: {integrity: sha512-kSRVGKlCYK6AGR0h8xRkk0WOFGXHIIndod3GKgWU49APuIGDiXd8sziXsSlniUsWmqgDmDXcNnSzPcV7AQ8YNg==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-heading@2.27.2': - resolution: {integrity: sha512-iM3yeRWuuQR/IRQ1djwNooJGfn9Jts9zF43qZIUf+U2NY8IlvdNsk2wTOdBgh6E0CamrStPxYGuln3ZS4fuglw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-history@2.27.2': - resolution: {integrity: sha512-+hSyqERoFNTWPiZx4/FCyZ/0eFqB9fuMdTB4AC/q9iwu3RNWAQtlsJg5230bf/qmyO6bZxRUc0k8p4hrV6ybAw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-horizontal-rule@2.27.2': - resolution: {integrity: sha512-WGWUSgX+jCsbtf9Y9OCUUgRZYuwjVoieW5n6mAUohJ9/6gc6sGIOrUpBShf+HHo6WD+gtQjRd+PssmX3NPWMpg==} - peerDependencies: - '@tiptap/core': ^2.7.0 - '@tiptap/pm': ^2.7.0 - - '@tiptap/extension-italic@2.27.2': - resolution: {integrity: sha512-1OFsw2SZqfaqx5Fa5v90iNlPRcqyt+lVSjBwTDzuPxTPFY4Q0mL89mKgkq2gVHYNCiaRkXvFLDxaSvBWbmthgg==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-list-item@2.27.2': - resolution: {integrity: sha512-eJNee7IEGXMnmygM5SdMGDC8m/lMWmwNGf9fPCK6xk0NxuQRgmZHL6uApKcdH6gyNcRPHCqvTTkhEP7pbny/fg==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-ordered-list@2.27.2': - resolution: {integrity: sha512-M7A4tLGJcLPYdLC4CI2Gwl8LOrENQW59u3cMVa+KkwG1hzSJyPsbDpa1DI6oXPC2WtYiTf22zrbq3gVvH+KA2w==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-paragraph@2.27.2': - resolution: {integrity: sha512-elYVn2wHJJ+zB9LESENWOAfI4TNT0jqEN34sMA/hCtA4im1ZG2DdLHwkHIshj/c4H0dzQhmsS/YmNC5Vbqab/A==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-strike@2.27.2': - resolution: {integrity: sha512-HHIjhafLhS2lHgfAsCwC1okqMsQzR4/mkGDm4M583Yftyjri1TNA7lzhzXWRFWiiMfJxKtdjHjUAQaHuteRTZw==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/extension-text@2.27.2': - resolution: {integrity: sha512-Xk7nYcigljAY0GO9hAQpZ65ZCxqOqaAlTPDFcKerXmlkQZP/8ndx95OgUb1Xf63kmPOh3xypurGS2is3v0MXSA==} - peerDependencies: - '@tiptap/core': ^2.7.0 - - '@tiptap/pm@2.27.2': - resolution: {integrity: sha512-kaEg7BfiJPDQMKbjVIzEPO3wlcA+pZb2tlcK9gPrdDnEFaec2QTF1sXz2ak2IIb2curvnIrQ4yrfHgLlVA72wA==} - - '@tiptap/starter-kit@2.1.7': - resolution: {integrity: sha512-z2cmJRSC7ImaTGWrHv+xws9y1wIG0OCPosBYpmpwlEfA3JG3axWFmVRJlWnsQV4eSMi3QY3vaPgBAnrR4IxRhQ==} - - '@tiptap/vue-3@2.1.7': - resolution: {integrity: sha512-JJRXWKLJ8mopb0uZV4JXyOW6vKQnarYoCj0hsy9ZT2LhSuLlPXY0D40NAbFVMMbQssewUtgPUFgVZ/TusMEysQ==} - peerDependencies: - '@tiptap/core': ^2.0.0 - '@tiptap/pm': ^2.0.0 - vue: ^3.0.0 - - '@types/chance@1.1.3': - resolution: {integrity: sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==} - - '@types/d3-array@3.2.2': - resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} - - '@types/d3-axis@3.0.6': - resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} - - '@types/d3-brush@3.0.6': - resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} - - '@types/d3-chord@3.0.6': - resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} - - '@types/d3-color@3.1.3': - resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - - '@types/d3-contour@3.0.6': - resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} - - '@types/d3-delaunay@6.0.4': - resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} - - '@types/d3-dispatch@3.0.7': - resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} - - '@types/d3-drag@3.0.7': - resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - - '@types/d3-dsv@3.0.7': - resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} - - '@types/d3-ease@3.0.2': - resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} - - '@types/d3-fetch@3.0.7': - resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} - - '@types/d3-force@3.0.10': - resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} - - '@types/d3-format@3.0.4': - resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} - - '@types/d3-geo@3.1.0': - resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} - - '@types/d3-hierarchy@3.1.7': - resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} - - '@types/d3-interpolate@3.0.4': - resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - - '@types/d3-path@3.1.1': - resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} - - '@types/d3-polygon@3.0.2': - resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} - - '@types/d3-quadtree@3.0.6': - resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} - - '@types/d3-random@3.0.3': - resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} - - '@types/d3-scale-chromatic@3.1.0': - resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} - - '@types/d3-scale@4.0.9': - resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - - '@types/d3-selection@3.0.11': - resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} - - '@types/d3-shape@3.1.8': - resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} - - '@types/d3-time-format@4.0.3': - resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} - - '@types/d3-time@3.0.4': - resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} - - '@types/d3-timer@3.0.2': - resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - - '@types/d3-transition@3.0.9': - resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} - - '@types/d3-zoom@3.0.8': - resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - - '@types/d3@7.4.3': - resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} - - '@types/dompurify@3.2.0': - resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} - deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. - - '@types/eslint-scope@3.7.7': - resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} - - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - - '@types/geojson@7946.0.16': - resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} - - '@types/hast@3.0.4': - resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/linkify-it@5.0.0': - resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - - '@types/markdown-it@14.1.2': - resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} - - '@types/mdast@4.0.4': - resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} - - '@types/mdurl@2.0.0': - resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} - - '@types/node@20.19.32': - resolution: {integrity: sha512-Ez8QE4DMfhjjTsES9K2dwfV258qBui7qxUsoaixZDiTzbde4U12e1pXGNu/ECsUIOi5/zoCxAQxIhQnaUQ2VvA==} - - '@types/node@20.19.37': - resolution: {integrity: sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==} - - '@types/semver@7.7.1': - resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} - - '@types/trusted-types@2.0.7': - resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} - - '@types/unist@3.0.3': - resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - - '@typescript-eslint/eslint-plugin@5.62.0': - resolution: {integrity: sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - '@typescript-eslint/parser': ^5.0.0 - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/parser@5.62.0': - resolution: {integrity: sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/scope-manager@5.62.0': - resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@typescript-eslint/type-utils@5.62.0': - resolution: {integrity: sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '*' - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/types@5.62.0': - resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@typescript-eslint/typescript-estree@5.62.0': - resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@typescript-eslint/utils@5.62.0': - resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - - '@typescript-eslint/visitor-keys@5.62.0': - resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - deprecated: Potential CWE-502 - Update to 1.3.1 or higher - - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vue: ^3.2.25 - - '@volar/language-core@1.10.10': - resolution: {integrity: sha512-nsV1o3AZ5n5jaEAObrS3MWLBWaGwUj/vAsc15FVNIv+DbpizQRISg9wzygsHBr56ELRH8r4K75vkYNMtsSNNWw==} - - '@volar/source-map@1.10.10': - resolution: {integrity: sha512-GVKjLnifV4voJ9F0vhP56p4+F3WGf+gXlRtjFZsv6v3WxBTWU3ZVeaRaEHJmWrcv5LXmoYYpk/SC25BKemPRkg==} - - '@volar/typescript@1.10.10': - resolution: {integrity: sha512-4a2r5bdUub2m+mYVnLu2wt59fuoYWe7nf0uXtGHU8QQ5LDNfzAR0wK7NgDiQ9rcl2WT3fxT2AA9AylAwFtj50A==} - - '@vue/compiler-core@3.3.4': - resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} - - '@vue/compiler-core@3.5.27': - resolution: {integrity: sha512-gnSBQjZA+//qDZen+6a2EdHqJ68Z7uybrMf3SPjEGgG4dicklwDVmMC1AeIHxtLVPT7sn6sH1KOO+tS6gwOUeQ==} - - '@vue/compiler-dom@3.3.4': - resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==} - - '@vue/compiler-dom@3.5.27': - resolution: {integrity: sha512-oAFea8dZgCtVVVTEC7fv3T5CbZW9BxpFzGGxC79xakTr6ooeEqmRuvQydIiDAkglZEAd09LgVf1RoDnL54fu5w==} - - '@vue/compiler-sfc@3.3.4': - resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} - - '@vue/compiler-ssr@3.3.4': - resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==} - - '@vue/devtools-api@6.6.4': - resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} - - '@vue/eslint-config-prettier@8.0.0': - resolution: {integrity: sha512-55dPqtC4PM/yBjhAr+yEw6+7KzzdkBuLmnhBrDfp4I48+wy+Giqqj9yUr5T2uD/BkBROjjmqnLZmXRdOx/VtQg==} - peerDependencies: - eslint: '>= 8.0.0' - prettier: '>= 3.0.0' - - '@vue/eslint-config-typescript@11.0.3': - resolution: {integrity: sha512-dkt6W0PX6H/4Xuxg/BlFj5xHvksjpSlVjtkQCpaYJBIEuKj2hOVU7r+TIe+ysCwRYFz/lGqvklntRkCAibsbPw==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 - eslint-plugin-vue: ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@vue/language-core@1.8.8': - resolution: {integrity: sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - - '@vue/reactivity-transform@3.3.4': - resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==} - - '@vue/reactivity@3.3.4': - resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==} - - '@vue/reactivity@3.5.27': - resolution: {integrity: sha512-vvorxn2KXfJ0nBEnj4GYshSgsyMNFnIQah/wczXlsNXt+ijhugmW+PpJ2cNPe4V6jpnBcs0MhCODKllWG+nvoQ==} - - '@vue/runtime-core@3.3.4': - resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==} - - '@vue/runtime-dom@3.3.4': - resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==} - - '@vue/server-renderer@3.3.4': - resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==} - peerDependencies: - vue: 3.3.4 - - '@vue/shared@3.3.4': - resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} - - '@vue/shared@3.5.27': - resolution: {integrity: sha512-dXr/3CgqXsJkZ0n9F3I4elY8wM9jMJpP3pvRG52r6m0tu/MsAFIe6JpXVGeNMd/D9F4hQynWT8Rfuj0bdm9kFQ==} - - '@vue/tsconfig@0.4.0': - resolution: {integrity: sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==} - - '@vue/typescript@1.8.8': - resolution: {integrity: sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==} - - '@vuetify/loader-shared@1.7.1': - resolution: {integrity: sha512-kLUvuAed6RCvkeeTNJzuy14pqnkur8lTuner7v7pNE/kVhPR97TuyXwBSBMR1cJeiLiOfu6SF5XlCYbXByEx1g==} - peerDependencies: - vue: ^3.0.0 - vuetify: ^3.0.0-beta.4 - - '@vuetify/loader-shared@2.1.2': - resolution: {integrity: sha512-X+1jBLmXHkpQEnC0vyOb4rtX2QSkBiFhaFXz8yhQqN2A4vQ6k2nChxN4Ol7VAY5KoqMdFoRMnmNdp/1qYXDQig==} - peerDependencies: - vue: ^3.0.0 - vuetify: '>=3' - - '@webassemblyjs/ast@1.14.1': - resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} - - '@webassemblyjs/floating-point-hex-parser@1.13.2': - resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} - - '@webassemblyjs/helper-api-error@1.13.2': - resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} - - '@webassemblyjs/helper-buffer@1.14.1': - resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} - - '@webassemblyjs/helper-numbers@1.13.2': - resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': - resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} - - '@webassemblyjs/helper-wasm-section@1.14.1': - resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} - - '@webassemblyjs/ieee754@1.13.2': - resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} - - '@webassemblyjs/leb128@1.13.2': - resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} - - '@webassemblyjs/utf8@1.13.2': - resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} - - '@webassemblyjs/wasm-edit@1.14.1': - resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} - - '@webassemblyjs/wasm-gen@1.14.1': - resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} - - '@webassemblyjs/wasm-opt@1.14.1': - resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} - - '@webassemblyjs/wasm-parser@1.14.1': - resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} - - '@webassemblyjs/wast-printer@1.14.1': - resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} - - '@xtuc/ieee754@1.2.0': - resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} - - '@xtuc/long@4.2.2': - resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} - - '@yr/monotone-cubic-spline@1.0.3': - resolution: {integrity: sha512-FQXkOta0XBSUPHndIKON2Y9JeQz5ZeMqLYZVVK93FliNBFm7LNMIZmY6FrMEB9XPcDbE2bekMbZD6kzDkxwYjA==} - - acorn-import-phases@1.0.4: - resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} - engines: {node: '>=10.13.0'} - peerDependencies: - acorn: ^8.14.0 - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} - engines: {node: '>=0.4.0'} - hasBin: true - - acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} - hasBin: true - - ajv-formats@2.1.1: - resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - - ajv-keywords@5.1.0: - resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} - peerDependencies: - ajv: ^8.8.2 - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - apexcharts@3.42.0: - resolution: {integrity: sha512-hYhzZqh2Efny9uiutkGU2M/EarJ4Nn8s6dxZ0C7E7N+SV4d1xjTioXi2NLn4UKVJabZkb3HnpXDoumXgtAymwg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - axios-mock-adapter@1.22.0: - resolution: {integrity: sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==} - peerDependencies: - axios: '>= 0.17.0' - - axios@1.13.5: - resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} - engines: {node: '>=6.0.0'} - hasBin: true - - big.js@5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - boolbase@1.0.0: - resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - c12@2.0.1: - resolution: {integrity: sha512-Z4JgsKXHG37C6PYUtIxCfLJZvo6FyhHJoClwwb9ftUkLpPSkuYqn6Tr+vnaN8hymm0kIbcg6Ey3kv/Q71k5w/A==} - peerDependencies: - magicast: ^0.3.5 - peerDependenciesMeta: - magicast: - optional: true - - cacheable@2.3.3: - resolution: {integrity: sha512-iffYMX4zxKp54evOH27fm92hs+DeC1DhXmNVN8Tr94M/iZIV42dqTHSR2Ik4TOSPyOAwKr7Yu3rN9ALoLkbWyQ==} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - callsite@1.0.0: - resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase@5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - - caniuse-lite@1.0.30001778: - resolution: {integrity: sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chance@1.1.11: - resolution: {integrity: sha512-kqTg3WWywappJPqtgrdvbA380VoXO2eu9VCV895JgbyHsaErXdyHK9LOZ911OvAk6L0obK7kDk9CGs8+oBawVA==} - - character-entities-html4@2.1.0: - resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} - - character-entities-legacy@3.0.0: - resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} - - chevrotain-allstar@0.3.1: - resolution: {integrity: sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==} - peerDependencies: - chevrotain: ^11.0.0 - - chevrotain@11.0.3: - resolution: {integrity: sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - chrome-trace-event@1.0.4: - resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} - engines: {node: '>=6.0'} - - citty@0.1.6: - resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} - - clean-css@5.3.3: - resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} - engines: {node: '>= 10.0'} - - cliui@6.0.0: - resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - - commander@7.2.0: - resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} - engines: {node: '>= 10'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - - consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} - - cose-base@1.0.3: - resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} - - cose-base@2.2.0: - resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - - crelt@1.0.6: - resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - csstype@3.2.3: - resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} - - cytoscape-cose-bilkent@4.1.0: - resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape-fcose@2.2.0: - resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} - peerDependencies: - cytoscape: ^3.2.0 - - cytoscape@3.33.1: - resolution: {integrity: sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==} - engines: {node: '>=0.10'} - - d3-array@2.12.1: - resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} - - d3-array@3.2.4: - resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} - engines: {node: '>=12'} - - d3-axis@3.0.0: - resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} - engines: {node: '>=12'} - - d3-brush@3.0.0: - resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} - engines: {node: '>=12'} - - d3-chord@3.0.1: - resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} - engines: {node: '>=12'} - - d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - - d3-contour@4.0.2: - resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} - engines: {node: '>=12'} - - d3-delaunay@6.0.4: - resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} - engines: {node: '>=12'} - - d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - - d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} - - d3-dsv@3.0.1: - resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} - engines: {node: '>=12'} - hasBin: true - - d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - - d3-fetch@3.0.1: - resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} - engines: {node: '>=12'} - - d3-force@3.0.0: - resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} - engines: {node: '>=12'} - - d3-format@3.1.2: - resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} - engines: {node: '>=12'} - - d3-geo@3.1.1: - resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} - engines: {node: '>=12'} - - d3-hierarchy@3.1.2: - resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} - engines: {node: '>=12'} - - d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} - - d3-path@1.0.9: - resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} - - d3-path@3.1.0: - resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} - engines: {node: '>=12'} - - d3-polygon@3.0.1: - resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} - engines: {node: '>=12'} - - d3-quadtree@3.0.1: - resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} - engines: {node: '>=12'} - - d3-random@3.0.1: - resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} - engines: {node: '>=12'} - - d3-sankey@0.12.3: - resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} - - d3-scale-chromatic@3.1.0: - resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} - engines: {node: '>=12'} - - d3-scale@4.0.2: - resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} - engines: {node: '>=12'} - - d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - - d3-shape@1.3.7: - resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} - - d3-shape@3.2.0: - resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} - engines: {node: '>=12'} - - d3-time-format@4.1.0: - resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} - engines: {node: '>=12'} - - d3-time@3.1.0: - resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} - engines: {node: '>=12'} - - d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - - d3-transition@3.0.1: - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 - - d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} - - d3@7.9.0: - resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} - engines: {node: '>=12'} - - dagre-d3-es@7.0.13: - resolution: {integrity: sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==} - - date-fns@2.30.0: - resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} - engines: {node: '>=0.11'} - - dayjs@1.11.19: - resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} - - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decache@4.6.2: - resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - defu@6.1.7: - resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} - - delaunator@5.0.1: - resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - destr@2.0.5: - resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - - devlop@1.1.0: - resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} - - dijkstrajs@1.0.3: - resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - dompurify@3.3.2: - resolution: {integrity: sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==} - engines: {node: '>=20'} - - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - electron-to-chromium@1.5.307: - resolution: {integrity: sha512-5z3uFKBWjiNR44nFcYdkcXjKMbg5KXNdciu7mhTPo9tB7NbqSNP2sSnGR+fqknZSCwKkBN+oxiiajWs4dT6ORg==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emojis-list@3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} - engines: {node: '>= 4'} - - enhanced-resolve@5.20.0: - resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} - engines: {node: '>=10.13.0'} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - entities@7.0.1: - resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} - engines: {node: '>=0.12'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-module-lexer@2.0.0: - resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - eslint-config-prettier@8.10.2: - resolution: {integrity: sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' - - eslint-plugin-prettier@5.5.5: - resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true - - eslint-plugin-vue@9.17.0: - resolution: {integrity: sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - - eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint@8.48.0: - resolution: {integrity: sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - - espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - event-source-polyfill@1.0.31: - resolution: {integrity: sha512-4IJSItgS/41IxN5UVAVuAyczwZF7ZIEsM1XAoUzIHA6A+xzusEZUutdXz2Nr+MQPLxfTiCvqE79/C8HT8fKFvA==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fastq@1.20.1: - resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - - file-loader@6.2.0: - resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-cache-dir@3.3.2: - resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} - engines: {node: '>=8'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flat-cache@6.1.20: - resolution: {integrity: sha512-AhHYqwvN62NVLp4lObVXGVluiABTHapoB57EyegZVmazN+hhGhLTn3uZbOofoTw4DSDvVCadzzyChXhOAvy8uQ==} - - flatted@3.3.3: - resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - fontverter@2.0.0: - resolution: {integrity: sha512-DFVX5hvXuhi1Jven1tbpebYTCT9XYnvx6/Z+HFUPb7ZRMCW+pj2clU9VMhoTPgWKPhAs7JJDSk3CW1jNUvKCZQ==} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - giget@1.2.5: - resolution: {integrity: sha512-r1ekGw/Bgpi3HLV3h1MRBIlSAdHoIMklpaQ3OQLFcRw9PwAj2rqigvIbg+dBUI51OxVI2jsEtDywDBjSiuf7Ug==} - hasBin: true - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob-to-regexp@0.4.1: - resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - hachure-fill@0.5.2: - resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} - - handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true - - harfbuzzjs@0.4.15: - resolution: {integrity: sha512-p1edvnlc+vpRe2kz7OKzcscf0gyFiDZpco+miDxAiiZ67cu1oNlbuOkmP/A/i1l/w938VrkF2FdZ8scNcnkPrQ==} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hashery@1.5.0: - resolution: {integrity: sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==} - engines: {node: '>=20'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hast-util-to-html@9.0.5: - resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} - - hast-util-whitespace@3.0.0: - resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} - - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - highlight.js@11.11.1: - resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} - engines: {node: '>=12.0.0'} - - hookified@1.15.1: - resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} - - html-void-elements@3.0.0: - resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - immutable@4.3.8: - resolution: {integrity: sha512-d/Ld9aLbKpNwyl0KiM2CT1WYvkitQ1TSvmRtkcV8FKStiDoA7Slzgjmb/1G2yhKM1p0XeNOieaTbFZmU1d3Xuw==} - - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - internmap@1.0.1: - resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} - - internmap@2.0.3: - resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} - engines: {node: '>=12'} - - interpret@1.4.0: - resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==} - engines: {node: '>= 0.10'} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-buffer@2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jest-worker@27.5.1: - resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} - engines: {node: '>= 10.13.0'} - - jiti@2.7.0: - resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} - hasBin: true - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - katex@0.16.28: - resolution: {integrity: sha512-YHzO7721WbmAL6Ov1uzN/l5mY5WWWhJBSW+jq4tkfZfsxmo1hu6frS0EOswvjBUnWE6NtjEs48SFn5CQESRLZg==} - hasBin: true - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - keyv@5.6.0: - resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} - - khroma@2.1.0: - resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} - - langium@3.3.1: - resolution: {integrity: sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==} - engines: {node: '>=16.0.0'} - - layout-base@1.0.2: - resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} - - layout-base@2.0.1: - resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - linkify-it@5.0.0: - resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} - - linkify-it@5.0.1: - resolution: {integrity: sha512-wVoTjP4Q6R0NW5hiZkVJaFZPWgtXfoGF+6LucL3/FtiNjmcHhYjEr5f1Kqjirc1nBW07J/ZuRFumqr2oqccEWg==} - - loader-runner@4.3.1: - resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} - engines: {node: '>=6.11.5'} - - loader-utils@2.0.4: - resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} - engines: {node: '>=8.9.0'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash-es@4.17.23: - resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - make-dir@3.1.0: - resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} - engines: {node: '>=8'} - - markdown-it-container@4.0.0: - resolution: {integrity: sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==} - - markdown-it-footnote@4.0.0: - resolution: {integrity: sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==} - - markdown-it-ins@4.0.0: - resolution: {integrity: sha512-sWbjK2DprrkINE4oYDhHdCijGT+MIDhEupjSHLXe5UXeVr5qmVxs/nTUVtgi0Oh/qtF+QKV0tNWDhQBEPxiMew==} - - markdown-it-mark@4.0.0: - resolution: {integrity: sha512-YLhzaOsU9THO/cal0lUjfMjrqSMPjjyjChYM7oyj4DnyaXEzA8gnW6cVJeyCrCVeyesrY2PlEdUYJSPFYL4Nkg==} - - markdown-it-sub@2.0.0: - resolution: {integrity: sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==} - - markdown-it-sup@2.0.0: - resolution: {integrity: sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA==} - - markdown-it-task-checkbox@1.0.6: - resolution: {integrity: sha512-7pxkHuvqTOu3iwVGmDPeYjQg+AIS9VQxzyLP9JCg9lBjgPAJXGEkChK6A2iFuj3tS0GV3HG2u5AMNhcQqwxpJw==} - - markdown-it-ts@1.0.2: - resolution: {integrity: sha512-zba9mN313K2HmKk+BOHqkO/nuZtj9M1TTnUlSbItGrCMpYzc8OHGCm+IaqxWCi2pGcgpiFC8ltxkasYWYpp/YQ==} - engines: {node: '>=18'} - - markdown-it@14.1.1: - resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} - hasBin: true - - marked@16.4.2: - resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} - engines: {node: '>= 20'} - hasBin: true - - markstream-core@1.0.3: - resolution: {integrity: sha512-QXn+yERo1q+RD6YlGDW61zc/af65uhkQEH3K8YvEKkprHbgRJ/JIeBeEQjELCTyBnPoxqtKQ7I565rylY+PePg==} - - markstream-vue@1.0.5-beta.0: - resolution: {integrity: sha512-/gmcNKa7v6qqmwQ/JAn3sWUoiM+EaMkvS0X5YRgqD74d+G22dzua4X8ZViYpbDGR/bl7BTw3juzsRfC0dm15pw==} - peerDependencies: - '@antv/infographic': ^0.2.3 - '@terrastruct/d2': '>=0.1.33' - katex: '>=0.16.22' - mermaid: '>=11' - stream-markdown: '>=0.0.16' - stream-monaco: '>=0.0.45' - vue: '>=3.0.0' - vue-i18n: '>=9' - peerDependenciesMeta: - '@antv/infographic': - optional: true - '@terrastruct/d2': - optional: true - katex: - optional: true - mermaid: - optional: true - stream-markdown: - optional: true - stream-monaco: - optional: true - vue-i18n: - optional: true - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mdast-util-to-hast@13.2.1: - resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} - - mdurl@2.0.0: - resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - mermaid@11.12.2: - resolution: {integrity: sha512-n34QPDPEKmaeCG4WDMGy0OT6PSyxKCfy2pJgShP+Qow2KLrvWjclwbc3yXfSIf4BanqWEhQEpngWwNp/XhZt6w==} - - micromark-util-character@2.1.1: - resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} - - micromark-util-encode@2.0.1: - resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} - - micromark-util-sanitize-uri@2.0.1: - resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - - micromark-util-symbol@2.0.1: - resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} - - micromark-util-types@2.0.2: - resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} - - monaco-editor@0.52.2: - resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - muggle-string@0.3.1: - resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - natural-compare-lite@1.4.0: - resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - - node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} - - node-releases@2.0.36: - resolution: {integrity: sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - nth-check@2.1.1: - resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - - null-loader@4.0.1: - resolution: {integrity: sha512-pxqVbi4U6N26lq+LmgIbB5XATP0VdZKOG25DhHi8btMmJJefGArFyDg1yc4U3hWCJbMqSrw0qyrz1UQX+qYXqg==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - - nypm@0.5.4: - resolution: {integrity: sha512-X0SNNrZiGU8/e/zAB7sCTtdxWTMSIO73q+xuKgglm2Yvzwlo8UoC5FNySQFCvl84uPaeADkqHUZUkWy4aH4xOA==} - engines: {node: ^14.16.0 || >=16.10.0} - hasBin: true - - ohash@1.1.6: - resolution: {integrity: sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg==} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - oniguruma-parser@0.12.1: - resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} - - oniguruma-to-es@4.3.4: - resolution: {integrity: sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA==} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - orderedmap@2.1.1: - resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - package-manager-detector@1.6.0: - resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-data-parser@0.1.0: - resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - - pinia@2.1.6: - resolution: {integrity: sha512-bIU6QuE5qZviMmct5XwCesXelb5VavdOWKWaB17ggk++NUwQWWbP5YnsONTk3b752QkW9sACiR81rorpeOMSvQ==} - peerDependencies: - '@vue/composition-api': ^1.4.0 - typescript: '>=4.4.4' - vue: ^2.6.14 || ^3.3.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - typescript: - optional: true - - pinyin-pro@3.28.0: - resolution: {integrity: sha512-mMRty6RisoyYNphJrTo3pnvp3w8OMZBrXm9YSWkxhAfxKj1KZk2y8T2PDIZlDDRsvZ0No+Hz6FI4sZpA6Ey25g==} - - pkg-dir@4.2.0: - resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} - engines: {node: '>=8'} - - pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - - pngjs@5.0.0: - resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} - engines: {node: '>=10.13.0'} - - points-on-curve@0.2.0: - resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} - - points-on-path@0.2.1: - resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} - engines: {node: ^10 || ^12 || >=14} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier-linter-helpers@1.0.1: - resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} - engines: {node: '>=6.0.0'} - - prettier@3.0.2: - resolution: {integrity: sha512-o2YR9qtniXvwEZlOKbveKfDQVyqxbEIWn48Z8m3ZJjBjcCmUy3xZGIv+7AkaeuaTr6yPXJjwv07ZWlsWbEy1rQ==} - engines: {node: '>=14'} - hasBin: true - - property-expr@2.0.6: - resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} - - property-information@7.1.0: - resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - - prosemirror-changeset@2.4.0: - resolution: {integrity: sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==} - - prosemirror-collab@1.3.1: - resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} - - prosemirror-commands@1.7.1: - resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==} - - prosemirror-dropcursor@1.8.2: - resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==} - - prosemirror-gapcursor@1.4.1: - resolution: {integrity: sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==} - - prosemirror-history@1.5.0: - resolution: {integrity: sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==} - - prosemirror-inputrules@1.5.1: - resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==} - - prosemirror-keymap@1.2.3: - resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} - - prosemirror-markdown@1.13.4: - resolution: {integrity: sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==} - - prosemirror-menu@1.3.0: - resolution: {integrity: sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==} - - prosemirror-model@1.25.4: - resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==} - - prosemirror-schema-basic@1.2.4: - resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==} - - prosemirror-schema-list@1.5.1: - resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==} - - prosemirror-state@1.4.4: - resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} - - prosemirror-tables@1.8.5: - resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} - - prosemirror-trailing-node@3.0.0: - resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} - peerDependencies: - prosemirror-model: ^1.22.1 - prosemirror-state: ^1.4.2 - prosemirror-view: ^1.33.8 - - prosemirror-transform@1.11.0: - resolution: {integrity: sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==} - - prosemirror-view@1.41.6: - resolution: {integrity: sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - punycode.js@2.3.1: - resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} - engines: {node: '>=6'} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - qified@0.6.0: - resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} - engines: {node: '>=20'} - - qrcode@1.5.4: - resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} - engines: {node: '>=10.13.0'} - hasBin: true - - querystring@0.2.1: - resolution: {integrity: sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg==} - engines: {node: '>=0.4.x'} - deprecated: The querystring API is considered Legacy. new code should use the URLSearchParams API instead. - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - rc9@2.1.2: - resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} - - rechoir@0.6.2: - resolution: {integrity: sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==} - engines: {node: '>= 0.10'} - - regex-recursion@6.0.2: - resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} - - regex-utilities@2.3.0: - resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} - - regex@6.1.0: - resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - require-main-filename@2.0.0: - resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} - engines: {node: '>= 0.4'} - hasBin: true - - reusify@1.1.0: - resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - robust-predicates@3.0.2: - resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rope-sequence@1.3.4: - resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==} - - roughjs@4.6.6: - resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rw@1.3.3: - resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - sass-loader@13.3.2: - resolution: {integrity: sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg==} - engines: {node: '>= 14.15.0'} - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - sass: ^1.3.0 - sass-embedded: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - sass-embedded: - optional: true - - sass@1.66.1: - resolution: {integrity: sha512-50c+zTsZOJVgFfTgwwEzkjA3/QACgdNsKueWPyAR0mRINIvLAStVQBbPg14iuqEQ74NPDbXzJARJ/O4SI1zftA==} - engines: {node: '>=14.0.0'} - hasBin: true - - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - - schema-utils@4.3.3: - resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} - engines: {node: '>= 10.13.0'} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - set-blocking@2.0.0: - resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - shelljs@0.8.5: - resolution: {integrity: sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==} - engines: {node: '>=4'} - hasBin: true - - shiki-stream@0.1.4: - resolution: {integrity: sha512-4pz6JGSDmVTTkPJ/ueixHkFAXY4ySCc+unvCaDZV7hqq/sdJZirRxgIXSuNSKgiFlGTgRR97sdu2R8K55sPsrw==} - peerDependencies: - react: ^19.0.0 - solid-js: ^1.9.0 - vue: ^3.2.0 - peerDependenciesMeta: - react: - optional: true - solid-js: - optional: true - vue: - optional: true - - shiki@3.23.0: - resolution: {integrity: sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA==} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - state-local@1.0.7: - resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==} - - stream-markdown-parser@1.0.8: - resolution: {integrity: sha512-+Oo9ik7BtMBxf+Krh19YA0jmk+TIUxfULU5qQpWHo3Z4SBX74/Qwt6lVHcUvEcUQcd1kV35VNmw1SYdhBY4aOA==} - - stream-markdown@0.0.16: - resolution: {integrity: sha512-2WoOxlpc3N5RLc3zGuW+g/w76z6ketWBY0N1YzDYbXds1qw7zrUBv5PTQ3DOtpqgCjLuF3P2iCfjJTEqWGv0NQ==} - peerDependencies: - shiki: '>=3.23.0' - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - stringify-entities@4.0.4: - resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - - subset-font@2.4.0: - resolution: {integrity: sha512-DA/45nIj4NiseVdfHxVdVGL7hvNo3Ol6HjEm3KSYtPyDcsr6jh8Q37vSgz+A722wMfUd6nL8kgsi7uGv9DExXQ==} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - svg.draggable.js@2.2.2: - resolution: {integrity: sha512-JzNHBc2fLQMzYCZ90KZHN2ohXL0BQJGQimK1kGk6AvSeibuKcIdDX9Kr0dT9+UJ5O8nYA0RB839Lhvk4CY4MZw==} - engines: {node: '>= 0.8.0'} - - svg.easing.js@2.0.0: - resolution: {integrity: sha512-//ctPdJMGy22YoYGV+3HEfHbm6/69LJUTAqI2/5qBvaNHZ9uUFVC82B0Pl299HzgH13rKrBgi4+XyXXyVWWthA==} - engines: {node: '>= 0.8.0'} - - svg.filter.js@2.0.2: - resolution: {integrity: sha512-xkGBwU+dKBzqg5PtilaTb0EYPqPfJ9Q6saVldX+5vCRy31P6TlRCP3U9NxH3HEufkKkpNgdTLBJnmhDHeTqAkw==} - engines: {node: '>= 0.8.0'} - - svg.js@2.7.1: - resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==} - - svg.pathmorphing.js@0.1.3: - resolution: {integrity: sha512-49HWI9X4XQR/JG1qXkSDV8xViuTLIWm/B/7YuQELV5KMOPtXjiwH4XPJvr/ghEDibmLQ9Oc22dpWpG0vUDDNww==} - engines: {node: '>= 0.8.0'} - - svg.resize.js@1.4.3: - resolution: {integrity: sha512-9k5sXJuPKp+mVzXNvxz7U0uC9oVMQrrf7cFsETznzUDDm0x8+77dtZkWdMfRlmbkEEYvUn9btKuZ3n41oNA+uw==} - engines: {node: '>= 0.8.0'} - - svg.select.js@2.1.2: - resolution: {integrity: sha512-tH6ABEyJsAOVAhwcCjF8mw4crjXSI1aa7j2VQR8ZuJ37H2MBUbyeqYr5nEO7sSN3cy9AR9DUwNg0t/962HlDbQ==} - engines: {node: '>= 0.8.0'} - - svg.select.js@3.0.1: - resolution: {integrity: sha512-h5IS/hKkuVCbKSieR9uQCj9w+zLHoPh+ce19bBYyqF53g6mnPB8sAtIbe1s9dh2S2fCmYX2xel1Ln3PJBbK4kw==} - engines: {node: '>= 0.8.0'} - - synckit@0.11.12: - resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} - engines: {node: ^14.18.0 || >=16.0.0} - - tapable@2.3.0: - resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} - engines: {node: '>=6'} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - - terser-webpack-plugin@5.4.0: - resolution: {integrity: sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - - terser@5.46.0: - resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} - engines: {node: '>=10'} - hasBin: true - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - tiny-case@1.0.3: - resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} - - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} - engines: {node: '>=18'} - - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} - - tippy.js@6.3.7: - resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - toposort@2.0.2: - resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - ts-dedent@2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tsutils@3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - - typescript@5.1.6: - resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==} - engines: {node: '>=14.17'} - hasBin: true - - uc.micro@2.1.0: - resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} - - uglify-js@3.19.3: - resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} - engines: {node: '>=0.8.0'} - hasBin: true - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unist-util-is@6.0.1: - resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} - - unist-util-position@5.0.0: - resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} - - unist-util-stringify-position@4.0.0: - resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} - - unist-util-visit-parents@6.0.2: - resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} - - unist-util-visit@5.1.0: - resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} - - upath@2.0.1: - resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} - engines: {node: '>=4'} - - update-browserslist-db@1.2.3: - resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - vee-validate@4.11.3: - resolution: {integrity: sha512-YhWORdZRE1GL6vXKj3r9f+Y8fJH5JMwMUJ4DFS44+WcTtiNXggyE3pyJPlZBqS9AgYGZ47EOv4UczkLxHqufiw==} - peerDependencies: - vue: ^3.3.4 - - vfile-message@4.0.3: - resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} - - vfile@6.0.3: - resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - - vite-plugin-vuetify@2.1.3: - resolution: {integrity: sha512-Q4SC/4TqbNvaZIFb9YsfBqkGlYHbJJJ6uU3CnRBZqLUF3s5eCMVZAaV4GkTbehIH/bhSj42lMXztOwc71u6rVw==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: '>=5' - vue: ^3.0.0 - vuetify: '>=3' - - vite-plugin-webfont-dl@3.12.0: - resolution: {integrity: sha512-0jxsr8ycuoK/uV5Y3ytttTRhgvfZo8v3O4JZBlVc4C7QWIws/vCLVR4B3ag+TGVkLNQya6hXfY3UnZge3M8vmA==} - peerDependencies: - vite: ^2 || ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - - vite@6.4.1: - resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - jiti: '>=1.21.0' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vscode-jsonrpc@8.2.0: - resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==} - engines: {node: '>=14.0.0'} - - vscode-languageserver-protocol@3.17.5: - resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==} - - vscode-languageserver-textdocument@1.0.12: - resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} - - vscode-languageserver-types@3.17.5: - resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==} - - vscode-languageserver@9.0.1: - resolution: {integrity: sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==} - hasBin: true - - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - - vue-cli-plugin-vuetify@2.5.8: - resolution: {integrity: sha512-uqi0/URJETJBbWlQHD1l0pnY7JN8Ytu+AL1fw50HFlGByPa8/xx+mq19GkFXA9FcwFT01IqEc/TkxMPugchomg==} - peerDependencies: - sass-loader: '*' - vue: '*' - vuetify-loader: '*' - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - sass-loader: - optional: true - vuetify-loader: - optional: true - - vue-demi@0.14.10: - resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} - engines: {node: '>=12'} - hasBin: true - peerDependencies: - '@vue/composition-api': ^1.0.0-rc.1 - vue: ^3.0.0-0 || ^2.6.0 - peerDependenciesMeta: - '@vue/composition-api': - optional: true - - vue-eslint-parser@9.4.3: - resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} - engines: {node: ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: '>=6.0.0' - - vue-i18n@11.2.8: - resolution: {integrity: sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==} - engines: {node: '>= 16'} - peerDependencies: - vue: ^3.0.0 - - vue-router@4.2.4: - resolution: {integrity: sha512-9PISkmaCO02OzPVOMq2w82ilty6+xJmQrarYZDkjZBfl4RvYAlt4PKnEX21oW4KTtWfa9OuO/b3qk1Od3AEdCQ==} - peerDependencies: - vue: ^3.2.0 - - vue-template-compiler@2.7.16: - resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} - - vue-tsc@1.8.8: - resolution: {integrity: sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==} - hasBin: true - peerDependencies: - typescript: '*' - - vue3-apexcharts@1.4.4: - resolution: {integrity: sha512-TH89uZrxGjaDvkaYAISvj8+k6Bf1rUKFillc8oJirs5XZEPiwM1ELKZQ786wz0rfPqkSHHny2lqqUCK7Rw+LcQ==} - peerDependencies: - apexcharts: '> 3.0.0' - vue: '> 3.0.0' - - vue3-print-nb@0.1.4: - resolution: {integrity: sha512-LExI7viEzplR6ZKQ2b+V4U0cwGYbVD4fut/XHvk3UPGlT5CcvIGs6VlwGp107aKgk6P8Pgx4rco3Rehv2lti3A==} - - vue@3.3.4: - resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==} - - vuetify-loader@2.0.0-alpha.9: - resolution: {integrity: sha512-M4u2XX9coe1U51jLKek54eJTo7wnroNfglh6sQplRTslhQRKQM3k84oh87D0VHqTzoTzlKPHP0sIWdpklwaEEQ==} - engines: {node: '>=12'} - deprecated: vuetify-loader has been renamed to webpack-plugin-vuetify for Vuetify 3 - peerDependencies: - '@vue/compiler-sfc': ^3.2.6 - vuetify: ^3.0.0-alpha.11 - webpack: ^5.0.0 - - vuetify@3.7.11: - resolution: {integrity: sha512-50Z2SNwPXbkGmve4CwxOs4sySZGgLwQYIDsKx+coSrfIBqz8IyXgxRFQdrvgoehIwUjGTNqaPZymuK5rMFkfHA==} - engines: {node: ^12.20 || >=14.13} - peerDependencies: - typescript: '>=4.7' - vite-plugin-vuetify: '>=1.0.0' - vue: ^3.3.0 - webpack-plugin-vuetify: '>=2.0.0' - peerDependenciesMeta: - typescript: - optional: true - vite-plugin-vuetify: - optional: true - webpack-plugin-vuetify: - optional: true - - w3c-keyname@2.2.8: - resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} - - watchpack@2.5.1: - resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} - engines: {node: '>=10.13.0'} - - wawoff2@2.0.1: - resolution: {integrity: sha512-r0CEmvpH63r4T15ebFqeOjGqU4+EgTx4I510NtK35EMciSdcTxCw3Byy3JnBonz7iyIFZ0AbVo0bbFpEVuhCYA==} - hasBin: true - - webpack-sources@3.3.4: - resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} - engines: {node: '>=10.13.0'} - - webpack@5.105.0: - resolution: {integrity: sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - - which-module@2.0.1: - resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - woff2sfnt-sfnt2woff@1.0.0: - resolution: {integrity: sha512-edK4COc1c1EpRfMqCZO1xJOvdUtM5dbVb9iz97rScvnTevqEB3GllnLWCmMVp1MfQBdF1DFg/11I0rSyAdS4qQ==} - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - - y18n@4.0.3: - resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yargs-parser@18.1.3: - resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} - engines: {node: '>=6'} - - yargs@15.4.1: - resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} - engines: {node: '>=8'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yup@1.2.0: - resolution: {integrity: sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@antfu/install-pkg@1.1.0': - dependencies: - package-manager-detector: 1.6.0 - tinyexec: 1.0.2 - - '@apidevtools/json-schema-ref-parser@11.7.3': - dependencies: - '@jsdevtools/ono': 7.1.3 - '@types/json-schema': 7.0.15 - js-yaml: 4.1.1 - - '@babel/helper-string-parser@7.27.1': {} - - '@babel/helper-validator-identifier@7.28.5': {} - - '@babel/parser@7.29.0': - dependencies: - '@babel/types': 7.29.0 - - '@babel/runtime@7.28.6': {} - - '@babel/types@7.29.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.28.5 - - '@braintree/sanitize-url@7.1.2': {} - - '@cacheable/memory@2.0.8': - dependencies: - '@cacheable/utils': 2.4.0 - '@keyv/bigmap': 1.3.1(keyv@5.6.0) - hookified: 1.15.1 - keyv: 5.6.0 - - '@cacheable/utils@2.4.0': - dependencies: - hashery: 1.5.0 - keyv: 5.6.0 - - '@chenglou/pretext@0.0.5': {} - - '@chevrotain/cst-dts-gen@11.0.3': - dependencies: - '@chevrotain/gast': 11.0.3 - '@chevrotain/types': 11.0.3 - lodash-es: 4.17.23 - - '@chevrotain/gast@11.0.3': - dependencies: - '@chevrotain/types': 11.0.3 - lodash-es: 4.17.23 - - '@chevrotain/regexp-to-ast@11.0.3': {} - - '@chevrotain/types@11.0.3': {} - - '@chevrotain/utils@11.0.3': {} - - '@esbuild/aix-ppc64@0.25.12': - optional: true - - '@esbuild/android-arm64@0.25.12': - optional: true - - '@esbuild/android-arm@0.25.12': - optional: true - - '@esbuild/android-x64@0.25.12': - optional: true - - '@esbuild/darwin-arm64@0.25.12': - optional: true - - '@esbuild/darwin-x64@0.25.12': - optional: true - - '@esbuild/freebsd-arm64@0.25.12': - optional: true - - '@esbuild/freebsd-x64@0.25.12': - optional: true - - '@esbuild/linux-arm64@0.25.12': - optional: true - - '@esbuild/linux-arm@0.25.12': - optional: true - - '@esbuild/linux-ia32@0.25.12': - optional: true - - '@esbuild/linux-loong64@0.25.12': - optional: true - - '@esbuild/linux-mips64el@0.25.12': - optional: true - - '@esbuild/linux-ppc64@0.25.12': - optional: true - - '@esbuild/linux-riscv64@0.25.12': - optional: true - - '@esbuild/linux-s390x@0.25.12': - optional: true - - '@esbuild/linux-x64@0.25.12': - optional: true - - '@esbuild/netbsd-arm64@0.25.12': - optional: true - - '@esbuild/netbsd-x64@0.25.12': - optional: true - - '@esbuild/openbsd-arm64@0.25.12': - optional: true - - '@esbuild/openbsd-x64@0.25.12': - optional: true - - '@esbuild/openharmony-arm64@0.25.12': - optional: true - - '@esbuild/sunos-x64@0.25.12': - optional: true - - '@esbuild/win32-arm64@0.25.12': - optional: true - - '@esbuild/win32-ia32@0.25.12': - optional: true - - '@esbuild/win32-x64@0.25.12': - optional: true - - '@eslint-community/eslint-utils@4.9.1(eslint@8.48.0)': - dependencies: - eslint: 8.48.0 - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.2': {} - - '@eslint/eslintrc@2.1.4': - dependencies: - ajv: 6.12.6 - debug: 4.4.3 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.1 - js-yaml: 4.1.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@8.48.0': {} - - '@floating-ui/core@1.7.5': - dependencies: - '@floating-ui/utils': 0.2.11 - - '@floating-ui/dom@1.7.6': - dependencies: - '@floating-ui/core': 1.7.5 - '@floating-ui/utils': 0.2.11 - - '@floating-ui/utils@0.2.11': {} - - '@guolao/vue-monaco-editor@1.6.0(monaco-editor@0.52.2)(vue@3.3.4)': - dependencies: - '@monaco-editor/loader': 1.7.0 - monaco-editor: 0.52.2 - vue: 3.3.4 - vue-demi: 0.14.10(vue@3.3.4) - - '@hey-api/client-axios@0.2.12(axios@1.13.5)': - dependencies: - axios: 1.13.5 - - '@hey-api/openapi-ts@0.60.0(typescript@5.1.6)': - dependencies: - '@apidevtools/json-schema-ref-parser': 11.7.3 - c12: 2.0.1 - commander: 12.1.0 - handlebars: 4.7.8 - typescript: 5.1.6 - transitivePeerDependencies: - - magicast - - '@humanwhocodes/config-array@0.11.14': - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.3 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/object-schema@2.0.3': {} - - '@iconify/types@2.0.0': {} - - '@iconify/utils@3.1.0': - dependencies: - '@antfu/install-pkg': 1.1.0 - '@iconify/types': 2.0.0 - mlly: 1.8.0 - - '@intlify/core-base@11.2.8': - dependencies: - '@intlify/message-compiler': 11.2.8 - '@intlify/shared': 11.2.8 - - '@intlify/message-compiler@11.2.8': - dependencies: - '@intlify/shared': 11.2.8 - source-map-js: 1.2.1 - - '@intlify/shared@11.2.8': {} - - '@jridgewell/gen-mapping@0.3.13': - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@jsdevtools/ono@7.1.3': {} - - '@keyv/bigmap@1.3.1(keyv@5.6.0)': - dependencies: - hashery: 1.5.0 - hookified: 1.15.1 - keyv: 5.6.0 - - '@keyv/serialize@1.1.1': {} - - '@lucide/vue@1.23.0(vue@3.3.4)': - dependencies: - vue: 3.3.4 - - '@mdi/font@7.2.96': {} - - '@mermaid-js/parser@0.6.3': - dependencies: - langium: 3.3.1 - - '@monaco-editor/loader@1.7.0': - dependencies: - state-local: 1.0.7 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.20.1 - - '@pkgr/core@0.2.9': {} - - '@popperjs/core@2.11.8': {} - - '@remirror/core-constants@3.0.0': {} - - '@rollup/rollup-android-arm-eabi@4.59.0': - optional: true - - '@rollup/rollup-android-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-arm64@4.59.0': - optional: true - - '@rollup/rollup-darwin-x64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-arm64@4.59.0': - optional: true - - '@rollup/rollup-freebsd-x64@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.59.0': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-loong64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-loong64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-ppc64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-ppc64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-riscv64-musl@4.59.0': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.59.0': - optional: true - - '@rollup/rollup-linux-x64-musl@4.59.0': - optional: true - - '@rollup/rollup-openbsd-x64@4.59.0': - optional: true - - '@rollup/rollup-openharmony-arm64@4.59.0': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.59.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.59.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.59.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.59.0': - optional: true - - '@rushstack/eslint-patch@1.3.3': {} - - '@shikijs/core@3.22.0': - dependencies: - '@shikijs/types': 3.22.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/core@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - hast-util-to-html: 9.0.5 - - '@shikijs/engine-javascript@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - oniguruma-to-es: 4.3.4 - - '@shikijs/engine-oniguruma@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - - '@shikijs/langs@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - - '@shikijs/themes@3.23.0': - dependencies: - '@shikijs/types': 3.23.0 - - '@shikijs/types@3.22.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/types@3.23.0': - dependencies: - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - '@shikijs/vscode-textmate@10.0.2': {} - - '@tiptap/core@2.27.2(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/pm': 2.27.2 - - '@tiptap/extension-blockquote@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-bold@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-bubble-menu@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - tippy.js: 6.3.7 - - '@tiptap/extension-bullet-list@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-code-block@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - - '@tiptap/extension-code@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-document@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-dropcursor@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - - '@tiptap/extension-floating-menu@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - tippy.js: 6.3.7 - - '@tiptap/extension-gapcursor@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - - '@tiptap/extension-hard-break@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-heading@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-history@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - - '@tiptap/extension-horizontal-rule@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - - '@tiptap/extension-italic@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-list-item@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-ordered-list@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-paragraph@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-strike@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/extension-text@2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - - '@tiptap/pm@2.27.2': - dependencies: - prosemirror-changeset: 2.4.0 - prosemirror-collab: 1.3.1 - prosemirror-commands: 1.7.1 - prosemirror-dropcursor: 1.8.2 - prosemirror-gapcursor: 1.4.1 - prosemirror-history: 1.5.0 - prosemirror-inputrules: 1.5.1 - prosemirror-keymap: 1.2.3 - prosemirror-markdown: 1.13.4 - prosemirror-menu: 1.3.0 - prosemirror-model: 1.25.4 - prosemirror-schema-basic: 1.2.4 - prosemirror-schema-list: 1.5.1 - prosemirror-state: 1.4.4 - prosemirror-tables: 1.8.5 - prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6) - prosemirror-transform: 1.11.0 - prosemirror-view: 1.41.6 - - '@tiptap/starter-kit@2.1.7(@tiptap/pm@2.27.2)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/extension-blockquote': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-bold': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-bullet-list': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-code': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-code-block': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/extension-document': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-dropcursor': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/extension-gapcursor': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/extension-hard-break': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-heading': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-history': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/extension-horizontal-rule': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/extension-italic': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-list-item': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-ordered-list': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-paragraph': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-strike': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - '@tiptap/extension-text': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)) - transitivePeerDependencies: - - '@tiptap/pm' - - '@tiptap/vue-3@2.1.7(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(vue@3.3.4)': - dependencies: - '@tiptap/core': 2.27.2(@tiptap/pm@2.27.2) - '@tiptap/extension-bubble-menu': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/extension-floating-menu': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2) - '@tiptap/pm': 2.27.2 - vue: 3.3.4 - - '@types/chance@1.1.3': {} - - '@types/d3-array@3.2.2': {} - - '@types/d3-axis@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-brush@3.0.6': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-chord@3.0.6': {} - - '@types/d3-color@3.1.3': {} - - '@types/d3-contour@3.0.6': - dependencies: - '@types/d3-array': 3.2.2 - '@types/geojson': 7946.0.16 - - '@types/d3-delaunay@6.0.4': {} - - '@types/d3-dispatch@3.0.7': {} - - '@types/d3-drag@3.0.7': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-dsv@3.0.7': {} - - '@types/d3-ease@3.0.2': {} - - '@types/d3-fetch@3.0.7': - dependencies: - '@types/d3-dsv': 3.0.7 - - '@types/d3-force@3.0.10': {} - - '@types/d3-format@3.0.4': {} - - '@types/d3-geo@3.1.0': - dependencies: - '@types/geojson': 7946.0.16 - - '@types/d3-hierarchy@3.1.7': {} - - '@types/d3-interpolate@3.0.4': - dependencies: - '@types/d3-color': 3.1.3 - - '@types/d3-path@3.1.1': {} - - '@types/d3-polygon@3.0.2': {} - - '@types/d3-quadtree@3.0.6': {} - - '@types/d3-random@3.0.3': {} - - '@types/d3-scale-chromatic@3.1.0': {} - - '@types/d3-scale@4.0.9': - dependencies: - '@types/d3-time': 3.0.4 - - '@types/d3-selection@3.0.11': {} - - '@types/d3-shape@3.1.8': - dependencies: - '@types/d3-path': 3.1.1 - - '@types/d3-time-format@4.0.3': {} - - '@types/d3-time@3.0.4': {} - - '@types/d3-timer@3.0.2': {} - - '@types/d3-transition@3.0.9': - dependencies: - '@types/d3-selection': 3.0.11 - - '@types/d3-zoom@3.0.8': - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - - '@types/d3@7.4.3': - dependencies: - '@types/d3-array': 3.2.2 - '@types/d3-axis': 3.0.6 - '@types/d3-brush': 3.0.6 - '@types/d3-chord': 3.0.6 - '@types/d3-color': 3.1.3 - '@types/d3-contour': 3.0.6 - '@types/d3-delaunay': 6.0.4 - '@types/d3-dispatch': 3.0.7 - '@types/d3-drag': 3.0.7 - '@types/d3-dsv': 3.0.7 - '@types/d3-ease': 3.0.2 - '@types/d3-fetch': 3.0.7 - '@types/d3-force': 3.0.10 - '@types/d3-format': 3.0.4 - '@types/d3-geo': 3.1.0 - '@types/d3-hierarchy': 3.1.7 - '@types/d3-interpolate': 3.0.4 - '@types/d3-path': 3.1.1 - '@types/d3-polygon': 3.0.2 - '@types/d3-quadtree': 3.0.6 - '@types/d3-random': 3.0.3 - '@types/d3-scale': 4.0.9 - '@types/d3-scale-chromatic': 3.1.0 - '@types/d3-selection': 3.0.11 - '@types/d3-shape': 3.1.8 - '@types/d3-time': 3.0.4 - '@types/d3-time-format': 4.0.3 - '@types/d3-timer': 3.0.2 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - - '@types/dompurify@3.2.0': - dependencies: - dompurify: 3.3.2 - - '@types/eslint-scope@3.7.7': - dependencies: - '@types/eslint': 9.6.1 - '@types/estree': 1.0.8 - - '@types/eslint@9.6.1': - dependencies: - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - - '@types/estree@1.0.8': {} - - '@types/geojson@7946.0.16': {} - - '@types/hast@3.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/json-schema@7.0.15': {} - - '@types/linkify-it@5.0.0': {} - - '@types/markdown-it@14.1.2': - dependencies: - '@types/linkify-it': 5.0.0 - '@types/mdurl': 2.0.0 - - '@types/mdast@4.0.4': - dependencies: - '@types/unist': 3.0.3 - - '@types/mdurl@2.0.0': {} - - '@types/node@20.19.32': - dependencies: - undici-types: 6.21.0 - - '@types/node@20.19.37': - dependencies: - undici-types: 6.21.0 - - '@types/semver@7.7.1': {} - - '@types/trusted-types@2.0.7': - optional: true - - '@types/unist@3.0.3': {} - - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.48.0)(typescript@5.1.6))(eslint@8.48.0)(typescript@5.1.6)': - dependencies: - '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 5.62.0(eslint@8.48.0)(typescript@5.1.6) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@8.48.0)(typescript@5.1.6) - '@typescript-eslint/utils': 5.62.0(eslint@8.48.0)(typescript@5.1.6) - debug: 4.4.3 - eslint: 8.48.0 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare-lite: 1.4.0 - semver: 7.7.4 - tsutils: 3.21.0(typescript@5.1.6) - optionalDependencies: - typescript: 5.1.6 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@5.62.0(eslint@8.48.0)(typescript@5.1.6)': - dependencies: - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) - debug: 4.4.3 - eslint: 8.48.0 - optionalDependencies: - typescript: 5.1.6 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@5.62.0': - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - - '@typescript-eslint/type-utils@5.62.0(eslint@8.48.0)(typescript@5.1.6)': - dependencies: - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) - '@typescript-eslint/utils': 5.62.0(eslint@8.48.0)(typescript@5.1.6) - debug: 4.4.3 - eslint: 8.48.0 - tsutils: 3.21.0(typescript@5.1.6) - optionalDependencies: - typescript: 5.1.6 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@5.62.0': {} - - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.1.6)': - dependencies: - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.4.3 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.7.4 - tsutils: 3.21.0(typescript@5.1.6) - optionalDependencies: - typescript: 5.1.6 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@5.62.0(eslint@8.48.0)(typescript@5.1.6)': - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.48.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.7.1 - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/types': 5.62.0 - '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.1.6) - eslint: 8.48.0 - eslint-scope: 5.1.1 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/visitor-keys@5.62.0': - dependencies: - '@typescript-eslint/types': 5.62.0 - eslint-visitor-keys: 3.4.3 - - '@ungap/structured-clone@1.3.0': {} - - '@vitejs/plugin-vue@5.2.4(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0))(vue@3.3.4)': - dependencies: - vite: 6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0) - vue: 3.3.4 - - '@volar/language-core@1.10.10': - dependencies: - '@volar/source-map': 1.10.10 - - '@volar/source-map@1.10.10': - dependencies: - muggle-string: 0.3.1 - - '@volar/typescript@1.10.10': - dependencies: - '@volar/language-core': 1.10.10 - path-browserify: 1.0.1 - - '@vue/compiler-core@3.3.4': - dependencies: - '@babel/parser': 7.29.0 - '@vue/shared': 3.3.4 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-core@3.5.27': - dependencies: - '@babel/parser': 7.29.0 - '@vue/shared': 3.5.27 - entities: 7.0.1 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - - '@vue/compiler-dom@3.3.4': - dependencies: - '@vue/compiler-core': 3.3.4 - '@vue/shared': 3.3.4 - - '@vue/compiler-dom@3.5.27': - dependencies: - '@vue/compiler-core': 3.5.27 - '@vue/shared': 3.5.27 - - '@vue/compiler-sfc@3.3.4': - dependencies: - '@babel/parser': 7.29.0 - '@vue/compiler-core': 3.3.4 - '@vue/compiler-dom': 3.3.4 - '@vue/compiler-ssr': 3.3.4 - '@vue/reactivity-transform': 3.3.4 - '@vue/shared': 3.3.4 - estree-walker: 2.0.2 - magic-string: 0.30.21 - postcss: 8.5.6 - source-map-js: 1.2.1 - - '@vue/compiler-ssr@3.3.4': - dependencies: - '@vue/compiler-dom': 3.3.4 - '@vue/shared': 3.3.4 - - '@vue/devtools-api@6.6.4': {} - - '@vue/eslint-config-prettier@8.0.0(@types/eslint@9.6.1)(eslint@8.48.0)(prettier@3.0.2)': - dependencies: - eslint: 8.48.0 - eslint-config-prettier: 8.10.2(eslint@8.48.0) - eslint-plugin-prettier: 5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@8.48.0))(eslint@8.48.0)(prettier@3.0.2) - prettier: 3.0.2 - transitivePeerDependencies: - - '@types/eslint' - - '@vue/eslint-config-typescript@11.0.3(eslint-plugin-vue@9.17.0(eslint@8.48.0))(eslint@8.48.0)(typescript@5.1.6)': - dependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.48.0)(typescript@5.1.6))(eslint@8.48.0)(typescript@5.1.6) - '@typescript-eslint/parser': 5.62.0(eslint@8.48.0)(typescript@5.1.6) - eslint: 8.48.0 - eslint-plugin-vue: 9.17.0(eslint@8.48.0) - vue-eslint-parser: 9.4.3(eslint@8.48.0) - optionalDependencies: - typescript: 5.1.6 - transitivePeerDependencies: - - supports-color - - '@vue/language-core@1.8.8(typescript@5.1.6)': - dependencies: - '@volar/language-core': 1.10.10 - '@volar/source-map': 1.10.10 - '@vue/compiler-dom': 3.5.27 - '@vue/reactivity': 3.5.27 - '@vue/shared': 3.5.27 - minimatch: 9.0.5 - muggle-string: 0.3.1 - vue-template-compiler: 2.7.16 - optionalDependencies: - typescript: 5.1.6 - - '@vue/reactivity-transform@3.3.4': - dependencies: - '@babel/parser': 7.29.0 - '@vue/compiler-core': 3.3.4 - '@vue/shared': 3.3.4 - estree-walker: 2.0.2 - magic-string: 0.30.21 - - '@vue/reactivity@3.3.4': - dependencies: - '@vue/shared': 3.3.4 - - '@vue/reactivity@3.5.27': - dependencies: - '@vue/shared': 3.5.27 - - '@vue/runtime-core@3.3.4': - dependencies: - '@vue/reactivity': 3.3.4 - '@vue/shared': 3.3.4 - - '@vue/runtime-dom@3.3.4': - dependencies: - '@vue/runtime-core': 3.3.4 - '@vue/shared': 3.3.4 - csstype: 3.2.3 - - '@vue/server-renderer@3.3.4(vue@3.3.4)': - dependencies: - '@vue/compiler-ssr': 3.3.4 - '@vue/shared': 3.3.4 - vue: 3.3.4 - - '@vue/shared@3.3.4': {} - - '@vue/shared@3.5.27': {} - - '@vue/tsconfig@0.4.0': {} - - '@vue/typescript@1.8.8(typescript@5.1.6)': - dependencies: - '@volar/typescript': 1.10.10 - '@vue/language-core': 1.8.8(typescript@5.1.6) - transitivePeerDependencies: - - typescript - - '@vuetify/loader-shared@1.7.1(vue@3.3.4)(vuetify@3.7.11)': - dependencies: - find-cache-dir: 3.3.2 - upath: 2.0.1 - vue: 3.3.4 - vuetify: 3.7.11(typescript@5.1.6)(vite-plugin-vuetify@2.1.3)(vue@3.3.4) - - '@vuetify/loader-shared@2.1.2(vue@3.3.4)(vuetify@3.7.11)': - dependencies: - upath: 2.0.1 - vue: 3.3.4 - vuetify: 3.7.11(typescript@5.1.6)(vite-plugin-vuetify@2.1.3)(vue@3.3.4) - - '@webassemblyjs/ast@1.14.1': - dependencies: - '@webassemblyjs/helper-numbers': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - - '@webassemblyjs/floating-point-hex-parser@1.13.2': {} - - '@webassemblyjs/helper-api-error@1.13.2': {} - - '@webassemblyjs/helper-buffer@1.14.1': {} - - '@webassemblyjs/helper-numbers@1.13.2': - dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.13.2 - '@webassemblyjs/helper-api-error': 1.13.2 - '@xtuc/long': 4.2.2 - - '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} - - '@webassemblyjs/helper-wasm-section@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/wasm-gen': 1.14.1 - - '@webassemblyjs/ieee754@1.13.2': - dependencies: - '@xtuc/ieee754': 1.2.0 - - '@webassemblyjs/leb128@1.13.2': - dependencies: - '@xtuc/long': 4.2.2 - - '@webassemblyjs/utf8@1.13.2': {} - - '@webassemblyjs/wasm-edit@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/helper-wasm-section': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-opt': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - '@webassemblyjs/wast-printer': 1.14.1 - - '@webassemblyjs/wasm-gen@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wasm-opt@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-buffer': 1.14.1 - '@webassemblyjs/wasm-gen': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - - '@webassemblyjs/wasm-parser@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/helper-api-error': 1.13.2 - '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - '@webassemblyjs/ieee754': 1.13.2 - '@webassemblyjs/leb128': 1.13.2 - '@webassemblyjs/utf8': 1.13.2 - - '@webassemblyjs/wast-printer@1.14.1': - dependencies: - '@webassemblyjs/ast': 1.14.1 - '@xtuc/long': 4.2.2 - - '@xtuc/ieee754@1.2.0': {} - - '@xtuc/long@4.2.2': {} - - '@yr/monotone-cubic-spline@1.0.3': {} - - acorn-import-phases@1.0.4(acorn@8.16.0): - dependencies: - acorn: 8.16.0 - - acorn-jsx@5.3.2(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - - acorn@8.15.0: {} - - acorn@8.16.0: {} - - ajv-formats@2.1.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - - ajv-keywords@3.5.2(ajv@6.12.6): - dependencies: - ajv: 6.12.6 - - ajv-keywords@5.1.0(ajv@8.18.0): - dependencies: - ajv: 8.18.0 - fast-deep-equal: 3.1.3 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - apexcharts@3.42.0: - dependencies: - '@yr/monotone-cubic-spline': 1.0.3 - svg.draggable.js: 2.2.2 - svg.easing.js: 2.0.0 - svg.filter.js: 2.0.2 - svg.pathmorphing.js: 0.1.3 - svg.resize.js: 1.4.3 - svg.select.js: 3.0.1 - - argparse@2.0.1: {} - - array-union@2.1.0: {} - - asynckit@0.4.0: {} - - axios-mock-adapter@1.22.0(axios@1.13.5): - dependencies: - axios: 1.13.5 - fast-deep-equal: 3.1.3 - is-buffer: 2.0.5 - - axios@1.13.5: - dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - balanced-match@1.0.2: {} - - baseline-browser-mapping@2.10.0: {} - - big.js@5.2.2: {} - - binary-extensions@2.3.0: {} - - boolbase@1.0.0: {} - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.28.1: - dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001778 - electron-to-chromium: 1.5.307 - node-releases: 2.0.36 - update-browserslist-db: 1.2.3(browserslist@4.28.1) - - buffer-from@1.1.2: {} - - c12@2.0.1: - dependencies: - chokidar: 4.0.3 - confbox: 0.1.8 - defu: 6.1.7 - dotenv: 16.6.1 - giget: 1.2.5 - jiti: 2.7.0 - mlly: 1.8.0 - ohash: 1.1.6 - pathe: 1.1.2 - perfect-debounce: 1.0.0 - pkg-types: 1.3.1 - rc9: 2.1.2 - - cacheable@2.3.3: - dependencies: - '@cacheable/memory': 2.0.8 - '@cacheable/utils': 2.4.0 - hookified: 1.15.1 - keyv: 5.6.0 - qified: 0.6.0 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - callsite@1.0.0: {} - - callsites@3.1.0: {} - - camelcase@5.3.1: {} - - caniuse-lite@1.0.30001778: {} - - ccount@2.0.1: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chance@1.1.11: {} - - character-entities-html4@2.1.0: {} - - character-entities-legacy@3.0.0: {} - - chevrotain-allstar@0.3.1(chevrotain@11.0.3): - dependencies: - chevrotain: 11.0.3 - lodash-es: 4.17.23 - - chevrotain@11.0.3: - dependencies: - '@chevrotain/cst-dts-gen': 11.0.3 - '@chevrotain/gast': 11.0.3 - '@chevrotain/regexp-to-ast': 11.0.3 - '@chevrotain/types': 11.0.3 - '@chevrotain/utils': 11.0.3 - lodash-es: 4.17.23 - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - chokidar@4.0.3: - dependencies: - readdirp: 4.1.2 - - chownr@2.0.0: {} - - chrome-trace-event@1.0.4: {} - - citty@0.1.6: - dependencies: - consola: 3.4.2 - - clean-css@5.3.3: - dependencies: - source-map: 0.6.1 - - cliui@6.0.0: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@2.0.3: {} - - commander@12.1.0: {} - - commander@2.20.3: {} - - commander@7.2.0: {} - - commander@8.3.0: {} - - commondir@1.0.1: {} - - concat-map@0.0.1: {} - - confbox@0.1.8: {} - - consola@3.4.2: {} - - cose-base@1.0.3: - dependencies: - layout-base: 1.0.2 - - cose-base@2.2.0: - dependencies: - layout-base: 2.0.1 - - crelt@1.0.6: {} - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - cssesc@3.0.0: {} - - csstype@3.2.3: {} - - cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1): - dependencies: - cose-base: 1.0.3 - cytoscape: 3.33.1 - - cytoscape-fcose@2.2.0(cytoscape@3.33.1): - dependencies: - cose-base: 2.2.0 - cytoscape: 3.33.1 - - cytoscape@3.33.1: {} - - d3-array@2.12.1: - dependencies: - internmap: 1.0.1 - - d3-array@3.2.4: - dependencies: - internmap: 2.0.3 - - d3-axis@3.0.0: {} - - d3-brush@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3-chord@3.0.1: - dependencies: - d3-path: 3.1.0 - - d3-color@3.1.0: {} - - d3-contour@4.0.2: - dependencies: - d3-array: 3.2.4 - - d3-delaunay@6.0.4: - dependencies: - delaunator: 5.0.1 - - d3-dispatch@3.0.1: {} - - d3-drag@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-selection: 3.0.0 - - d3-dsv@3.0.1: - dependencies: - commander: 7.2.0 - iconv-lite: 0.6.3 - rw: 1.3.3 - - d3-ease@3.0.1: {} - - d3-fetch@3.0.1: - dependencies: - d3-dsv: 3.0.1 - - d3-force@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-quadtree: 3.0.1 - d3-timer: 3.0.1 - - d3-format@3.1.2: {} - - d3-geo@3.1.1: - dependencies: - d3-array: 3.2.4 - - d3-hierarchy@3.1.2: {} - - d3-interpolate@3.0.1: - dependencies: - d3-color: 3.1.0 - - d3-path@1.0.9: {} - - d3-path@3.1.0: {} - - d3-polygon@3.0.1: {} - - d3-quadtree@3.0.1: {} - - d3-random@3.0.1: {} - - d3-sankey@0.12.3: - dependencies: - d3-array: 2.12.1 - d3-shape: 1.3.7 - - d3-scale-chromatic@3.1.0: - dependencies: - d3-color: 3.1.0 - d3-interpolate: 3.0.1 - - d3-scale@4.0.2: - dependencies: - d3-array: 3.2.4 - d3-format: 3.1.2 - d3-interpolate: 3.0.1 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - - d3-selection@3.0.0: {} - - d3-shape@1.3.7: - dependencies: - d3-path: 1.0.9 - - d3-shape@3.2.0: - dependencies: - d3-path: 3.1.0 - - d3-time-format@4.1.0: - dependencies: - d3-time: 3.1.0 - - d3-time@3.1.0: - dependencies: - d3-array: 3.2.4 - - d3-timer@3.0.1: {} - - d3-transition@3.0.1(d3-selection@3.0.0): - dependencies: - d3-color: 3.1.0 - d3-dispatch: 3.0.1 - d3-ease: 3.0.1 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-timer: 3.0.1 - - d3-zoom@3.0.0: - dependencies: - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-interpolate: 3.0.1 - d3-selection: 3.0.0 - d3-transition: 3.0.1(d3-selection@3.0.0) - - d3@7.9.0: - dependencies: - d3-array: 3.2.4 - d3-axis: 3.0.0 - d3-brush: 3.0.0 - d3-chord: 3.0.1 - d3-color: 3.1.0 - d3-contour: 4.0.2 - d3-delaunay: 6.0.4 - d3-dispatch: 3.0.1 - d3-drag: 3.0.0 - d3-dsv: 3.0.1 - d3-ease: 3.0.1 - d3-fetch: 3.0.1 - d3-force: 3.0.0 - d3-format: 3.1.2 - d3-geo: 3.1.1 - d3-hierarchy: 3.1.2 - d3-interpolate: 3.0.1 - d3-path: 3.1.0 - d3-polygon: 3.0.1 - d3-quadtree: 3.0.1 - d3-random: 3.0.1 - d3-scale: 4.0.2 - d3-scale-chromatic: 3.1.0 - d3-selection: 3.0.0 - d3-shape: 3.2.0 - d3-time: 3.1.0 - d3-time-format: 4.1.0 - d3-timer: 3.0.1 - d3-transition: 3.0.1(d3-selection@3.0.0) - d3-zoom: 3.0.0 - - dagre-d3-es@7.0.13: - dependencies: - d3: 7.9.0 - lodash-es: 4.17.23 - - date-fns@2.30.0: - dependencies: - '@babel/runtime': 7.28.6 - - dayjs@1.11.19: {} - - de-indent@1.0.2: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decache@4.6.2: - dependencies: - callsite: 1.0.0 - - decamelize@1.2.0: {} - - deep-is@0.1.4: {} - - defu@6.1.7: {} - - delaunator@5.0.1: - dependencies: - robust-predicates: 3.0.2 - - delayed-stream@1.0.0: {} - - dequal@2.0.3: {} - - destr@2.0.5: {} - - devlop@1.1.0: - dependencies: - dequal: 2.0.3 - - dijkstrajs@1.0.3: {} - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - dompurify@3.3.2: - optionalDependencies: - '@types/trusted-types': 2.0.7 - - dotenv@16.6.1: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - electron-to-chromium@1.5.307: {} - - emoji-regex@8.0.0: {} - - emojis-list@3.0.0: {} - - enhanced-resolve@5.20.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.3.0 - - entities@4.5.0: {} - - entities@7.0.1: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-module-lexer@2.0.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - - escalade@3.2.0: {} - - escape-string-regexp@4.0.0: {} - - eslint-config-prettier@8.10.2(eslint@8.48.0): - dependencies: - eslint: 8.48.0 - - eslint-plugin-prettier@5.5.5(@types/eslint@9.6.1)(eslint-config-prettier@8.10.2(eslint@8.48.0))(eslint@8.48.0)(prettier@3.0.2): - dependencies: - eslint: 8.48.0 - prettier: 3.0.2 - prettier-linter-helpers: 1.0.1 - synckit: 0.11.12 - optionalDependencies: - '@types/eslint': 9.6.1 - eslint-config-prettier: 8.10.2(eslint@8.48.0) - - eslint-plugin-vue@9.17.0(eslint@8.48.0): - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.48.0) - eslint: 8.48.0 - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 6.1.2 - semver: 7.7.4 - vue-eslint-parser: 9.4.3(eslint@8.48.0) - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color - - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - eslint-scope@7.2.2: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint@8.48.0: - dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@8.48.0) - '@eslint-community/regexpp': 4.12.2 - '@eslint/eslintrc': 2.1.4 - '@eslint/js': 8.48.0 - '@humanwhocodes/config-array': 0.11.14 - '@humanwhocodes/module-importer': 1.0.1 - '@nodelib/fs.walk': 1.2.8 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.3 - doctrine: 3.0.0 - escape-string-regexp: 4.0.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.7.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - find-up: 5.0.0 - glob-parent: 6.0.2 - globals: 13.24.0 - graphemer: 1.4.0 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - is-path-inside: 3.0.3 - js-yaml: 4.1.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - strip-ansi: 6.0.1 - text-table: 0.2.0 - transitivePeerDependencies: - - supports-color - - espree@9.6.1: - dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) - eslint-visitor-keys: 3.4.3 - - esquery@1.7.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@4.3.0: {} - - estraverse@5.3.0: {} - - estree-walker@2.0.2: {} - - esutils@2.0.3: {} - - event-source-polyfill@1.0.31: {} - - events@3.3.0: {} - - fast-deep-equal@3.1.3: {} - - fast-diff@1.3.0: {} - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fast-uri@3.1.0: {} - - fastq@1.20.1: - dependencies: - reusify: 1.1.0 - - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - - file-loader@6.2.0(webpack@5.105.0): - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.3.0 - webpack: 5.105.0 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-cache-dir@3.3.2: - dependencies: - commondir: 1.0.1 - make-dir: 3.1.0 - pkg-dir: 4.2.0 - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@3.2.0: - dependencies: - flatted: 3.3.3 - keyv: 4.5.4 - rimraf: 3.0.2 - - flat-cache@6.1.20: - dependencies: - cacheable: 2.3.3 - flatted: 3.3.3 - hookified: 1.15.1 - - flatted@3.3.3: {} - - follow-redirects@1.15.11: {} - - fontverter@2.0.0: - dependencies: - wawoff2: 2.0.1 - woff2sfnt-sfnt2woff: 1.0.0 - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - - fs.realpath@1.0.0: {} - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - get-caller-file@2.0.5: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - giget@1.2.5: - dependencies: - citty: 0.1.6 - consola: 3.4.2 - defu: 6.1.7 - node-fetch-native: 1.6.7 - nypm: 0.5.4 - pathe: 2.0.3 - tar: 6.2.1 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob-to-regexp@0.4.1: {} - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - graphemer@1.4.0: {} - - hachure-fill@0.5.2: {} - - handlebars@4.7.8: - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.19.3 - - harfbuzzjs@0.4.15: {} - - has-flag@4.0.0: {} - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hashery@1.5.0: - dependencies: - hookified: 1.15.1 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - hast-util-to-html@9.0.5: - dependencies: - '@types/hast': 3.0.4 - '@types/unist': 3.0.3 - ccount: 2.0.1 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 3.0.0 - html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.1 - property-information: 7.1.0 - space-separated-tokens: 2.0.2 - stringify-entities: 4.0.4 - zwitch: 2.0.4 - - hast-util-whitespace@3.0.0: - dependencies: - '@types/hast': 3.0.4 - - he@1.2.0: {} - - highlight.js@11.11.1: {} - - hookified@1.15.1: {} - - html-void-elements@3.0.0: {} - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ignore@5.3.2: {} - - immutable@4.3.8: {} - - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - internmap@1.0.1: {} - - internmap@2.0.3: {} - - interpret@1.4.0: {} - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-buffer@2.0.5: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-extglob@2.1.1: {} - - is-fullwidth-code-point@3.0.0: {} - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-number@7.0.0: {} - - is-path-inside@3.0.3: {} - - isexe@2.0.0: {} - - jest-worker@27.5.1: - dependencies: - '@types/node': 20.19.37 - merge-stream: 2.0.0 - supports-color: 8.1.1 - - jiti@2.7.0: {} - - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - json-buffer@3.0.1: {} - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@2.2.3: {} - - katex@0.16.28: - dependencies: - commander: 8.3.0 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - keyv@5.6.0: - dependencies: - '@keyv/serialize': 1.1.1 - - khroma@2.1.0: {} - - langium@3.3.1: - dependencies: - chevrotain: 11.0.3 - chevrotain-allstar: 0.3.1(chevrotain@11.0.3) - vscode-languageserver: 9.0.1 - vscode-languageserver-textdocument: 1.0.12 - vscode-uri: 3.0.8 - - layout-base@1.0.2: {} - - layout-base@2.0.1: {} - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - linkify-it@5.0.0: - dependencies: - uc.micro: 2.1.0 - - linkify-it@5.0.1: - dependencies: - uc.micro: 2.1.0 - - loader-runner@4.3.1: {} - - loader-utils@2.0.4: - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 2.2.3 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash-es@4.17.23: {} - - lodash.merge@4.6.2: {} - - lodash@4.17.23: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - make-dir@3.1.0: - dependencies: - semver: 6.3.1 - - markdown-it-container@4.0.0: {} - - markdown-it-footnote@4.0.0: {} - - markdown-it-ins@4.0.0: {} - - markdown-it-mark@4.0.0: {} - - markdown-it-sub@2.0.0: {} - - markdown-it-sup@2.0.0: {} - - markdown-it-task-checkbox@1.0.6: {} - - markdown-it-ts@1.0.2: - dependencies: - '@types/linkify-it': 5.0.0 - '@types/mdurl': 2.0.0 - entities: 4.5.0 - linkify-it: 5.0.1 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - - markdown-it@14.1.1: - dependencies: - argparse: 2.0.1 - entities: 4.5.0 - linkify-it: 5.0.0 - mdurl: 2.0.0 - punycode.js: 2.3.1 - uc.micro: 2.1.0 - - marked@16.4.2: {} - - markstream-core@1.0.3: {} - - markstream-vue@1.0.5-beta.0(katex@0.16.28)(mermaid@11.12.2)(stream-markdown@0.0.16(shiki@3.23.0)(vue@3.3.4))(vue-i18n@11.2.8(vue@3.3.4))(vue@3.3.4): - dependencies: - '@chenglou/pretext': 0.0.5 - '@floating-ui/dom': 1.7.6 - markstream-core: 1.0.3 - stream-markdown-parser: 1.0.8 - vue: 3.3.4 - optionalDependencies: - katex: 0.16.28 - mermaid: 11.12.2 - stream-markdown: 0.0.16(shiki@3.23.0)(vue@3.3.4) - vue-i18n: 11.2.8(vue@3.3.4) - - math-intrinsics@1.1.0: {} - - mdast-util-to-hast@13.2.1: - dependencies: - '@types/hast': 3.0.4 - '@types/mdast': 4.0.4 - '@ungap/structured-clone': 1.3.0 - devlop: 1.1.0 - micromark-util-sanitize-uri: 2.0.1 - trim-lines: 3.0.1 - unist-util-position: 5.0.0 - unist-util-visit: 5.1.0 - vfile: 6.0.3 - - mdurl@2.0.0: {} - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - mermaid@11.12.2: - dependencies: - '@braintree/sanitize-url': 7.1.2 - '@iconify/utils': 3.1.0 - '@mermaid-js/parser': 0.6.3 - '@types/d3': 7.4.3 - cytoscape: 3.33.1 - cytoscape-cose-bilkent: 4.1.0(cytoscape@3.33.1) - cytoscape-fcose: 2.2.0(cytoscape@3.33.1) - d3: 7.9.0 - d3-sankey: 0.12.3 - dagre-d3-es: 7.0.13 - dayjs: 1.11.19 - dompurify: 3.3.2 - katex: 0.16.28 - khroma: 2.1.0 - lodash-es: 4.17.23 - marked: 16.4.2 - roughjs: 4.6.6 - stylis: 4.3.6 - ts-dedent: 2.2.0 - uuid: 11.1.0 - - micromark-util-character@2.1.1: - dependencies: - micromark-util-symbol: 2.0.1 - micromark-util-types: 2.0.2 - - micromark-util-encode@2.0.1: {} - - micromark-util-sanitize-uri@2.0.1: - dependencies: - micromark-util-character: 2.1.1 - micromark-util-encode: 2.0.1 - micromark-util-symbol: 2.0.1 - - micromark-util-symbol@2.0.1: {} - - micromark-util-types@2.0.2: {} - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - mkdirp@1.0.4: {} - - mlly@1.8.0: - dependencies: - acorn: 8.15.0 - pathe: 2.0.3 - pkg-types: 1.3.1 - ufo: 1.6.3 - - monaco-editor@0.52.2: {} - - ms@2.1.3: {} - - muggle-string@0.3.1: {} - - nanoid@3.3.11: {} - - natural-compare-lite@1.4.0: {} - - natural-compare@1.4.0: {} - - neo-async@2.6.2: {} - - node-fetch-native@1.6.7: {} - - node-releases@2.0.36: {} - - normalize-path@3.0.0: {} - - nth-check@2.1.1: - dependencies: - boolbase: 1.0.0 - - null-loader@4.0.1(webpack@5.105.0): - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.3.0 - webpack: 5.105.0 - - nypm@0.5.4: - dependencies: - citty: 0.1.6 - consola: 3.4.2 - pathe: 2.0.3 - pkg-types: 1.3.1 - tinyexec: 0.3.2 - ufo: 1.6.3 - - ohash@1.1.6: {} - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - oniguruma-parser@0.12.1: {} - - oniguruma-to-es@4.3.4: - dependencies: - oniguruma-parser: 0.12.1 - regex: 6.1.0 - regex-recursion: 6.0.2 - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - orderedmap@2.1.1: {} - - p-limit@2.3.0: - dependencies: - p-try: 2.2.0 - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@4.1.0: - dependencies: - p-limit: 2.3.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-try@2.2.0: {} - - package-manager-detector@1.6.0: {} - - pako@1.0.11: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - path-browserify@1.0.1: {} - - path-data-parser@0.1.0: {} - - path-exists@4.0.0: {} - - path-is-absolute@1.0.1: {} - - path-key@3.1.1: {} - - path-parse@1.0.7: {} - - path-type@4.0.0: {} - - pathe@1.1.2: {} - - pathe@2.0.3: {} - - perfect-debounce@1.0.0: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - picomatch@4.0.3: {} - - pinia@2.1.6(typescript@5.1.6)(vue@3.3.4): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.3.4 - vue-demi: 0.14.10(vue@3.3.4) - optionalDependencies: - typescript: 5.1.6 - - pinyin-pro@3.28.0: {} - - pkg-dir@4.2.0: - dependencies: - find-up: 4.1.0 - - pkg-types@1.3.1: - dependencies: - confbox: 0.1.8 - mlly: 1.8.0 - pathe: 2.0.3 - - pngjs@5.0.0: {} - - points-on-curve@0.2.0: {} - - points-on-path@0.2.1: - dependencies: - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss@8.5.6: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} - - prettier-linter-helpers@1.0.1: - dependencies: - fast-diff: 1.3.0 - - prettier@3.0.2: {} - - property-expr@2.0.6: {} - - property-information@7.1.0: {} - - prosemirror-changeset@2.4.0: - dependencies: - prosemirror-transform: 1.11.0 - - prosemirror-collab@1.3.1: - dependencies: - prosemirror-state: 1.4.4 - - prosemirror-commands@1.7.1: - dependencies: - prosemirror-model: 1.25.4 - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - - prosemirror-dropcursor@1.8.2: - dependencies: - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - prosemirror-view: 1.41.6 - - prosemirror-gapcursor@1.4.1: - dependencies: - prosemirror-keymap: 1.2.3 - prosemirror-model: 1.25.4 - prosemirror-state: 1.4.4 - prosemirror-view: 1.41.6 - - prosemirror-history@1.5.0: - dependencies: - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - prosemirror-view: 1.41.6 - rope-sequence: 1.3.4 - - prosemirror-inputrules@1.5.1: - dependencies: - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - - prosemirror-keymap@1.2.3: - dependencies: - prosemirror-state: 1.4.4 - w3c-keyname: 2.2.8 - - prosemirror-markdown@1.13.4: - dependencies: - '@types/markdown-it': 14.1.2 - markdown-it: 14.1.1 - prosemirror-model: 1.25.4 - - prosemirror-menu@1.3.0: - dependencies: - crelt: 1.0.6 - prosemirror-commands: 1.7.1 - prosemirror-history: 1.5.0 - prosemirror-state: 1.4.4 - - prosemirror-model@1.25.4: - dependencies: - orderedmap: 2.1.1 - - prosemirror-schema-basic@1.2.4: - dependencies: - prosemirror-model: 1.25.4 - - prosemirror-schema-list@1.5.1: - dependencies: - prosemirror-model: 1.25.4 - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - - prosemirror-state@1.4.4: - dependencies: - prosemirror-model: 1.25.4 - prosemirror-transform: 1.11.0 - prosemirror-view: 1.41.6 - - prosemirror-tables@1.8.5: - dependencies: - prosemirror-keymap: 1.2.3 - prosemirror-model: 1.25.4 - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - prosemirror-view: 1.41.6 - - prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6): - dependencies: - '@remirror/core-constants': 3.0.0 - escape-string-regexp: 4.0.0 - prosemirror-model: 1.25.4 - prosemirror-state: 1.4.4 - prosemirror-view: 1.41.6 - - prosemirror-transform@1.11.0: - dependencies: - prosemirror-model: 1.25.4 - - prosemirror-view@1.41.6: - dependencies: - prosemirror-model: 1.25.4 - prosemirror-state: 1.4.4 - prosemirror-transform: 1.11.0 - - proxy-from-env@1.1.0: {} - - punycode.js@2.3.1: {} - - punycode@2.3.1: {} - - qified@0.6.0: - dependencies: - hookified: 1.15.1 - - qrcode@1.5.4: - dependencies: - dijkstrajs: 1.0.3 - pngjs: 5.0.0 - yargs: 15.4.1 - - querystring@0.2.1: {} - - queue-microtask@1.2.3: {} - - rc9@2.1.2: - dependencies: - defu: 6.1.7 - destr: 2.0.5 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - readdirp@4.1.2: {} - - rechoir@0.6.2: - dependencies: - resolve: 1.22.11 - - regex-recursion@6.0.2: - dependencies: - regex-utilities: 2.3.0 - - regex-utilities@2.3.0: {} - - regex@6.1.0: - dependencies: - regex-utilities: 2.3.0 - - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - - require-main-filename@2.0.0: {} - - resolve-from@4.0.0: {} - - resolve@1.22.11: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - reusify@1.1.0: {} - - rimraf@3.0.2: - dependencies: - glob: 7.2.3 - - robust-predicates@3.0.2: {} - - rollup@4.59.0: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.59.0 - '@rollup/rollup-android-arm64': 4.59.0 - '@rollup/rollup-darwin-arm64': 4.59.0 - '@rollup/rollup-darwin-x64': 4.59.0 - '@rollup/rollup-freebsd-arm64': 4.59.0 - '@rollup/rollup-freebsd-x64': 4.59.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 - '@rollup/rollup-linux-arm-musleabihf': 4.59.0 - '@rollup/rollup-linux-arm64-gnu': 4.59.0 - '@rollup/rollup-linux-arm64-musl': 4.59.0 - '@rollup/rollup-linux-loong64-gnu': 4.59.0 - '@rollup/rollup-linux-loong64-musl': 4.59.0 - '@rollup/rollup-linux-ppc64-gnu': 4.59.0 - '@rollup/rollup-linux-ppc64-musl': 4.59.0 - '@rollup/rollup-linux-riscv64-gnu': 4.59.0 - '@rollup/rollup-linux-riscv64-musl': 4.59.0 - '@rollup/rollup-linux-s390x-gnu': 4.59.0 - '@rollup/rollup-linux-x64-gnu': 4.59.0 - '@rollup/rollup-linux-x64-musl': 4.59.0 - '@rollup/rollup-openbsd-x64': 4.59.0 - '@rollup/rollup-openharmony-arm64': 4.59.0 - '@rollup/rollup-win32-arm64-msvc': 4.59.0 - '@rollup/rollup-win32-ia32-msvc': 4.59.0 - '@rollup/rollup-win32-x64-gnu': 4.59.0 - '@rollup/rollup-win32-x64-msvc': 4.59.0 - fsevents: 2.3.3 - - rope-sequence@1.3.4: {} - - roughjs@4.6.6: - dependencies: - hachure-fill: 0.5.2 - path-data-parser: 0.1.0 - points-on-curve: 0.2.0 - points-on-path: 0.2.1 - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rw@1.3.3: {} - - safer-buffer@2.1.2: {} - - sass-loader@13.3.2(sass@1.66.1)(webpack@5.105.0): - dependencies: - neo-async: 2.6.2 - webpack: 5.105.0 - optionalDependencies: - sass: 1.66.1 - - sass@1.66.1: - dependencies: - chokidar: 3.6.0 - immutable: 4.3.8 - source-map-js: 1.2.1 - - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - - schema-utils@4.3.3: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 8.18.0 - ajv-formats: 2.1.1(ajv@8.18.0) - ajv-keywords: 5.1.0(ajv@8.18.0) - - semver@6.3.1: {} - - semver@7.7.4: {} - - set-blocking@2.0.0: {} - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - shelljs@0.8.5: - dependencies: - glob: 7.2.3 - interpret: 1.4.0 - rechoir: 0.6.2 - - shiki-stream@0.1.4(vue@3.3.4): - dependencies: - '@shikijs/core': 3.22.0 - optionalDependencies: - vue: 3.3.4 - - shiki@3.23.0: - dependencies: - '@shikijs/core': 3.23.0 - '@shikijs/engine-javascript': 3.23.0 - '@shikijs/engine-oniguruma': 3.23.0 - '@shikijs/langs': 3.23.0 - '@shikijs/themes': 3.23.0 - '@shikijs/types': 3.23.0 - '@shikijs/vscode-textmate': 10.0.2 - '@types/hast': 3.0.4 - - slash@3.0.0: {} - - source-map-js@1.2.1: {} - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - space-separated-tokens@2.0.2: {} - - state-local@1.0.7: {} - - stream-markdown-parser@1.0.8: - dependencies: - markdown-it-container: 4.0.0 - markdown-it-footnote: 4.0.0 - markdown-it-ins: 4.0.0 - markdown-it-mark: 4.0.0 - markdown-it-sub: 2.0.0 - markdown-it-sup: 2.0.0 - markdown-it-task-checkbox: 1.0.6 - markdown-it-ts: 1.0.2 - - stream-markdown@0.0.16(shiki@3.23.0)(vue@3.3.4): - dependencies: - shiki: 3.23.0 - shiki-stream: 0.1.4(vue@3.3.4) - transitivePeerDependencies: - - react - - solid-js - - vue - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - stringify-entities@4.0.4: - dependencies: - character-entities-html4: 2.1.0 - character-entities-legacy: 3.0.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-json-comments@3.1.1: {} - - stylis@4.3.6: {} - - subset-font@2.4.0: - dependencies: - fontverter: 2.0.0 - harfbuzzjs: 0.4.15 - lodash: 4.17.23 - p-limit: 3.1.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-color@8.1.1: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - svg.draggable.js@2.2.2: - dependencies: - svg.js: 2.7.1 - - svg.easing.js@2.0.0: - dependencies: - svg.js: 2.7.1 - - svg.filter.js@2.0.2: - dependencies: - svg.js: 2.7.1 - - svg.js@2.7.1: {} - - svg.pathmorphing.js@0.1.3: - dependencies: - svg.js: 2.7.1 - - svg.resize.js@1.4.3: - dependencies: - svg.js: 2.7.1 - svg.select.js: 2.1.2 - - svg.select.js@2.1.2: - dependencies: - svg.js: 2.7.1 - - svg.select.js@3.0.1: - dependencies: - svg.js: 2.7.1 - - synckit@0.11.12: - dependencies: - '@pkgr/core': 0.2.9 - - tapable@2.3.0: {} - - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - - terser-webpack-plugin@5.4.0(webpack@5.105.0): - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - jest-worker: 27.5.1 - schema-utils: 4.3.3 - terser: 5.46.0 - webpack: 5.105.0 - - terser@5.46.0: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.16.0 - commander: 2.20.3 - source-map-support: 0.5.21 - - text-table@0.2.0: {} - - tiny-case@1.0.3: {} - - tinyexec@0.3.2: {} - - tinyexec@1.0.2: {} - - tinyglobby@0.2.15: - dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - - tippy.js@6.3.7: - dependencies: - '@popperjs/core': 2.11.8 - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - toposort@2.0.2: {} - - trim-lines@3.0.1: {} - - ts-dedent@2.2.0: {} - - tslib@1.14.1: {} - - tsutils@3.21.0(typescript@5.1.6): - dependencies: - tslib: 1.14.1 - typescript: 5.1.6 - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.20.2: {} - - type-fest@2.19.0: {} - - type-fest@4.41.0: {} - - typescript@5.1.6: {} - - uc.micro@2.1.0: {} - - ufo@1.6.3: {} - - uglify-js@3.19.3: - optional: true - - undici-types@6.21.0: {} - - unist-util-is@6.0.1: - dependencies: - '@types/unist': 3.0.3 - - unist-util-position@5.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-stringify-position@4.0.0: - dependencies: - '@types/unist': 3.0.3 - - unist-util-visit-parents@6.0.2: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - - unist-util-visit@5.1.0: - dependencies: - '@types/unist': 3.0.3 - unist-util-is: 6.0.1 - unist-util-visit-parents: 6.0.2 - - upath@2.0.1: {} - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - util-deprecate@1.0.2: {} - - uuid@11.1.0: {} - - vee-validate@4.11.3(vue@3.3.4): - dependencies: - '@vue/devtools-api': 6.6.4 - type-fest: 4.41.0 - vue: 3.3.4 - - vfile-message@4.0.3: - dependencies: - '@types/unist': 3.0.3 - unist-util-stringify-position: 4.0.0 - - vfile@6.0.3: - dependencies: - '@types/unist': 3.0.3 - vfile-message: 4.0.3 - - vite-plugin-vuetify@2.1.3(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0))(vue@3.3.4)(vuetify@3.7.11): - dependencies: - '@vuetify/loader-shared': 2.1.2(vue@3.3.4)(vuetify@3.7.11) - debug: 4.4.3 - upath: 2.0.1 - vite: 6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0) - vue: 3.3.4 - vuetify: 3.7.11(typescript@5.1.6)(vite-plugin-vuetify@2.1.3)(vue@3.3.4) - transitivePeerDependencies: - - supports-color - - vite-plugin-webfont-dl@3.12.0(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0)): - dependencies: - axios: 1.13.5 - clean-css: 5.3.3 - flat-cache: 6.1.20 - picocolors: 1.1.1 - vite: 6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0) - transitivePeerDependencies: - - debug - - vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0): - dependencies: - esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.59.0 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 20.19.32 - fsevents: 2.3.3 - jiti: 2.7.0 - sass: 1.66.1 - terser: 5.46.0 - - vscode-jsonrpc@8.2.0: {} - - vscode-languageserver-protocol@3.17.5: - dependencies: - vscode-jsonrpc: 8.2.0 - vscode-languageserver-types: 3.17.5 - - vscode-languageserver-textdocument@1.0.12: {} - - vscode-languageserver-types@3.17.5: {} - - vscode-languageserver@9.0.1: - dependencies: - vscode-languageserver-protocol: 3.17.5 - - vscode-uri@3.0.8: {} - - vue-cli-plugin-vuetify@2.5.8(sass-loader@13.3.2(sass@1.66.1)(webpack@5.105.0))(vue@3.3.4)(vuetify-loader@2.0.0-alpha.9(@vue/compiler-sfc@3.3.4)(vue@3.3.4)(vuetify@3.7.11)(webpack@5.105.0))(webpack@5.105.0): - dependencies: - null-loader: 4.0.1(webpack@5.105.0) - semver: 7.7.4 - shelljs: 0.8.5 - vue: 3.3.4 - webpack: 5.105.0 - optionalDependencies: - sass-loader: 13.3.2(sass@1.66.1)(webpack@5.105.0) - vuetify-loader: 2.0.0-alpha.9(@vue/compiler-sfc@3.3.4)(vue@3.3.4)(vuetify@3.7.11)(webpack@5.105.0) - - vue-demi@0.14.10(vue@3.3.4): - dependencies: - vue: 3.3.4 - - vue-eslint-parser@9.4.3(eslint@8.48.0): - dependencies: - debug: 4.4.3 - eslint: 8.48.0 - eslint-scope: 7.2.2 - eslint-visitor-keys: 3.4.3 - espree: 9.6.1 - esquery: 1.7.0 - lodash: 4.17.23 - semver: 7.7.4 - transitivePeerDependencies: - - supports-color - - vue-i18n@11.2.8(vue@3.3.4): - dependencies: - '@intlify/core-base': 11.2.8 - '@intlify/shared': 11.2.8 - '@vue/devtools-api': 6.6.4 - vue: 3.3.4 - - vue-router@4.2.4(vue@3.3.4): - dependencies: - '@vue/devtools-api': 6.6.4 - vue: 3.3.4 - - vue-template-compiler@2.7.16: - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - vue-tsc@1.8.8(typescript@5.1.6): - dependencies: - '@vue/language-core': 1.8.8(typescript@5.1.6) - '@vue/typescript': 1.8.8(typescript@5.1.6) - semver: 7.7.4 - typescript: 5.1.6 - - vue3-apexcharts@1.4.4(apexcharts@3.42.0)(vue@3.3.4): - dependencies: - apexcharts: 3.42.0 - vue: 3.3.4 - - vue3-print-nb@0.1.4: - dependencies: - vue: 3.3.4 - - vue@3.3.4: - dependencies: - '@vue/compiler-dom': 3.3.4 - '@vue/compiler-sfc': 3.3.4 - '@vue/runtime-dom': 3.3.4 - '@vue/server-renderer': 3.3.4(vue@3.3.4) - '@vue/shared': 3.3.4 - - vuetify-loader@2.0.0-alpha.9(@vue/compiler-sfc@3.3.4)(vue@3.3.4)(vuetify@3.7.11)(webpack@5.105.0): - dependencies: - '@vue/compiler-sfc': 3.3.4 - '@vuetify/loader-shared': 1.7.1(vue@3.3.4)(vuetify@3.7.11) - decache: 4.6.2 - file-loader: 6.2.0(webpack@5.105.0) - find-cache-dir: 3.3.2 - loader-utils: 2.0.4 - null-loader: 4.0.1(webpack@5.105.0) - querystring: 0.2.1 - upath: 2.0.1 - vuetify: 3.7.11(typescript@5.1.6)(vite-plugin-vuetify@2.1.3)(vue@3.3.4) - webpack: 5.105.0 - transitivePeerDependencies: - - vue - - vuetify@3.7.11(typescript@5.1.6)(vite-plugin-vuetify@2.1.3)(vue@3.3.4): - dependencies: - vue: 3.3.4 - optionalDependencies: - typescript: 5.1.6 - vite-plugin-vuetify: 2.1.3(vite@6.4.1(@types/node@20.19.32)(jiti@2.7.0)(sass@1.66.1)(terser@5.46.0))(vue@3.3.4)(vuetify@3.7.11) - - w3c-keyname@2.2.8: {} - - watchpack@2.5.1: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - - wawoff2@2.0.1: - dependencies: - argparse: 2.0.1 - - webpack-sources@3.3.4: {} - - webpack@5.105.0: - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 - '@webassemblyjs/ast': 1.14.1 - '@webassemblyjs/wasm-edit': 1.14.1 - '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.16.0 - acorn-import-phases: 1.0.4(acorn@8.16.0) - browserslist: 4.28.1 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.20.0 - es-module-lexer: 2.0.0 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.1 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 4.3.3 - tapable: 2.3.0 - terser-webpack-plugin: 5.4.0(webpack@5.105.0) - watchpack: 2.5.1 - webpack-sources: 3.3.4 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - - which-module@2.0.1: {} - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - woff2sfnt-sfnt2woff@1.0.0: - dependencies: - pako: 1.0.11 - - word-wrap@1.2.5: {} - - wordwrap@1.0.0: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrappy@1.0.2: {} - - xml-name-validator@4.0.0: {} - - y18n@4.0.3: {} - - yallist@4.0.0: {} - - yargs-parser@18.1.3: - dependencies: - camelcase: 5.3.1 - decamelize: 1.2.0 - - yargs@15.4.1: - dependencies: - cliui: 6.0.0 - decamelize: 1.2.0 - find-up: 4.1.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - require-main-filename: 2.0.0 - set-blocking: 2.0.0 - string-width: 4.2.3 - which-module: 2.0.1 - y18n: 4.0.3 - yargs-parser: 18.1.3 - - yocto-queue@0.1.0: {} - - yup@1.2.0: - dependencies: - property-expr: 2.0.6 - tiny-case: 1.0.3 - toposort: 2.0.2 - type-fest: 2.19.0 - - zwitch@2.0.4: {} diff --git a/legacy-dashboard/pnpm-workspace.yaml b/legacy-dashboard/pnpm-workspace.yaml deleted file mode 100644 index a23eae08..00000000 --- a/legacy-dashboard/pnpm-workspace.yaml +++ /dev/null @@ -1,3 +0,0 @@ -allowBuilds: - esbuild: true - vue-demi: true diff --git a/legacy-dashboard/public/_redirects b/legacy-dashboard/public/_redirects deleted file mode 100644 index ad37e2c2..00000000 --- a/legacy-dashboard/public/_redirects +++ /dev/null @@ -1 +0,0 @@ -/* /index.html 200 diff --git a/legacy-dashboard/public/favicon.svg b/legacy-dashboard/public/favicon.svg deleted file mode 100644 index e4f30733..00000000 --- a/legacy-dashboard/public/favicon.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - diff --git a/legacy-dashboard/public/robots.txt b/legacy-dashboard/public/robots.txt deleted file mode 100644 index 1f53798b..00000000 --- a/legacy-dashboard/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/legacy-dashboard/public/t2i/shiki_runtime.iife.js b/legacy-dashboard/public/t2i/shiki_runtime.iife.js deleted file mode 100644 index acf54276..00000000 --- a/legacy-dashboard/public/t2i/shiki_runtime.iife.js +++ /dev/null @@ -1,152 +0,0 @@ -var dl=Object.defineProperty;var gl=Object.getPrototypeOf;var bl=Reflect.get;var ds=k=>{throw TypeError(k)};var hl=(k,q,N)=>q in k?dl(k,q,{enumerable:!0,configurable:!0,writable:!0,value:N}):k[q]=N;var b=(k,q,N)=>hl(k,typeof q!="symbol"?q+"":q,N),pt=(k,q,N)=>q.has(k)||ds("Cannot "+N);var O=(k,q,N)=>(pt(k,q,"read from private field"),N?N.call(k):q.get(k)),xe=(k,q,N)=>q.has(k)?ds("Cannot add the same private member more than once"):q instanceof WeakSet?q.add(k):q.set(k,N),Y=(k,q,N,Xe)=>(pt(k,q,"write to private field"),Xe?Xe.call(k,N):q.set(k,N),N),dt=(k,q,N)=>(pt(k,q,"access private method"),N);var gs=(k,q,N)=>bl(gl(k),N,q);(function(){"use strict";var te,W,le,ae,_e,ue,$e,Ve,bt,me;let k=class extends Error{constructor(e){super(e),this.name="ShikiError"}};function q(n){return N(n)}function N(n){return Array.isArray(n)?Xe(n):n instanceof RegExp?n:typeof n=="object"?fs(n):n}function Xe(n){let e=[];for(let t=0,a=n.length;t{for(let a in t)n[a]=t[a]}),n}function ft(n){const e=~n.lastIndexOf("/")||~n.lastIndexOf("\\");return e===0?n:~e===n.length-1?ft(n.substring(0,n.length-1)):n.substr(~e+1)}var wn=/\$(\d+)|\${(\d+):\/(downcase|upcase)}/g,Ye=class{static hasCaptures(n){return n===null?!1:(wn.lastIndex=0,wn.test(n))}static replaceCaptures(n,e,t){return n.replace(wn,(a,s,i,r)=>{let c=t[parseInt(s||i,10)];if(c){let o=e.substring(c.start,c.end);for(;o[0]===".";)o=o.substring(1);switch(r){case"downcase":return o.toLowerCase();case"upcase":return o.toUpperCase();default:return o}}else return a})}};function yt(n,e){return ne?1:0}function _t(n,e){if(n===null&&e===null)return 0;if(!n)return-1;if(!e)return 1;let t=n.length,a=e.length;if(t===a){for(let s=0;sthis._root.match(n)));this._colorMap=n,this._defaults=e,this._root=t}static createFromRawTheme(n,e){return this.createFromParsedTheme($s(n),e)}static createFromParsedTheme(n,e){return ws(n,e)}getColorMap(){return this._colorMap.getColorMap()}getDefaults(){return this._defaults}match(n){if(n===null)return this._defaults;const e=n.scopeName,a=this._cachedMatchRoot.get(e).find(s=>ys(n.parent,s.parentScopes));return a?new kt(a.fontStyle,a.foreground,a.background):null}},kn=class xn{constructor(e,t){this.parent=e,this.scopeName=t}static push(e,t){for(const a of t)e=new xn(e,a);return e}static from(...e){let t=null;for(let a=0;a"){if(t===e.length-1)return!1;a=e[++t],s=!0}for(;n&&!_s(n.scopeName,a);){if(s)return!1;n=n.parent}if(!n)return!1;n=n.parent}return!0}function _s(n,e){return e===n||n.startsWith(e)&&n[e.length]==="."}var kt=class{constructor(n,e,t){this.fontStyle=n,this.foregroundId=e,this.backgroundId=t}};function $s(n){if(!n)return[];if(!n.settings||!Array.isArray(n.settings))return[];let e=n.settings,t=[],a=0;for(let s=0,i=e.length;s1&&($=d.slice(0,d.length-1),$.reverse()),t[a++]=new xs(f,$,s,o,l,u)}}return t}var xs=class{constructor(n,e,t,a,s,i){this.scope=n,this.parentScopes=e,this.index=t,this.fontStyle=a,this.foreground=s,this.background=i}},z=(n=>(n[n.NotSet=-1]="NotSet",n[n.None=0]="None",n[n.Italic=1]="Italic",n[n.Bold=2]="Bold",n[n.Underline=4]="Underline",n[n.Strikethrough=8]="Strikethrough",n))(z||{});function ws(n,e){n.sort((o,l)=>{let u=yt(o.scope,l.scope);return u!==0||(u=_t(o.parentScopes,l.parentScopes),u!==0)?u:o.index-l.index});let t=0,a="#000000",s="#ffffff";for(;n.length>=1&&n[0].scope==="";){let o=n.shift();o.fontStyle!==-1&&(t=o.fontStyle),o.foreground!==null&&(a=o.foreground),o.background!==null&&(s=o.background)}let i=new ks(e),r=new kt(t,i.getId(a),i.getId(s)),c=new vs(new jn(0,null,-1,0,0),[]);for(let o=0,l=n.length;oe?console.log("how did this happen?"):this.scopeDepth=e,t!==-1&&(this.fontStyle=t),a!==0&&(this.foreground=a),s!==0&&(this.background=s)}},vs=class gt{constructor(e,t=[],a={}){b(this,"_rulesWithParentScopes");this._mainRule=e,this._children=a,this._rulesWithParentScopes=t}static _cmpBySpecificity(e,t){if(e.scopeDepth!==t.scopeDepth)return t.scopeDepth-e.scopeDepth;let a=0,s=0;for(;e.parentScopes[a]===">"&&a++,t.parentScopes[s]===">"&&s++,!(a>=e.parentScopes.length||s>=t.parentScopes.length);){const i=t.parentScopes[s].length-e.parentScopes[a].length;if(i!==0)return i;a++,s++}return t.parentScopes.length-e.parentScopes.length}match(e){if(e!==""){let a=e.indexOf("."),s,i;if(a===-1?(s=e,i=""):(s=e.substring(0,a),i=e.substring(a+1)),this._children.hasOwnProperty(s))return this._children[s].match(i)}const t=this._rulesWithParentScopes.concat(this._mainRule);return t.sort(gt._cmpBySpecificity),t}insert(e,t,a,s,i,r){if(t===""){this._doInsertHere(e,a,s,i,r);return}let c=t.indexOf("."),o,l;c===-1?(o=t,l=""):(o=t.substring(0,c),l=t.substring(c+1));let u;this._children.hasOwnProperty(o)?u=this._children[o]:(u=new gt(this._mainRule.clone(),jn.cloneArr(this._rulesWithParentScopes)),this._children[o]=u),u.insert(e+1,l,a,s,i,r)}_doInsertHere(e,t,a,s,i){if(t===null){this._mainRule.acceptOverwrite(e,a,s,i);return}for(let r=0,c=this._rulesWithParentScopes.length;r>>0}static getTokenType(e){return(e&768)>>>8}static containsBalancedBrackets(e){return(e&1024)!==0}static getFontStyle(e){return(e&30720)>>>11}static getForeground(e){return(e&16744448)>>>15}static getBackground(e){return(e&4278190080)>>>24}static set(e,t,a,s,i,r,c){let o=V.getLanguageId(e),l=V.getTokenType(e),u=V.containsBalancedBrackets(e)?1:0,m=V.getFontStyle(e),p=V.getForeground(e),g=V.getBackground(e);return t!==0&&(o=t),a!==8&&(l=a),s!==null&&(u=s?1:0),i!==-1&&(m=i),r!==0&&(p=r),c!==0&&(g=c),(o<<0|l<<8|u<<10|m<<11|p<<15|g<<24)>>>0}};function fl(n){return n}function yl(n){return n}function Qe(n,e){const t=[],a=Cs(n);let s=a.next();for(;s!==null;){let o=0;if(s.length===2&&s.charAt(1)===":"){switch(s.charAt(0)){case"R":o=1;break;case"L":o=-1;break;default:console.log(`Unknown priority ${s} in scope selector`)}s=a.next()}let l=r();if(t.push({matcher:l,priority:o}),s!==",")break;s=a.next()}return t;function i(){if(s==="-"){s=a.next();const o=i();return l=>!!o&&!o(l)}if(s==="("){s=a.next();const o=c();return s===")"&&(s=a.next()),o}if(jt(s)){const o=[];do o.push(s),s=a.next();while(jt(s));return l=>e(o,l)}return null}function r(){const o=[];let l=i();for(;l;)o.push(l),l=i();return u=>o.every(m=>m(u))}function c(){const o=[];let l=r();for(;l&&(o.push(l),s==="|"||s===",");){do s=a.next();while(s==="|"||s===",");l=r()}return u=>o.some(m=>m(u))}}function jt(n){return!!n&&!!n.match(/[\w\.:]+/)}function Cs(n){let e=/([LR]:|[\w\.:][\w\.:\-]*|[\,\|\-\(\)])/g,t=e.exec(n);return{next:()=>{if(!t)return null;const a=t[0];return t=e.exec(n),a}}}function vt(n){typeof n.dispose=="function"&&n.dispose()}var Re=class{constructor(n){this.scopeName=n}toKey(){return this.scopeName}},Ss=class{constructor(n,e){this.scopeName=n,this.ruleName=e}toKey(){return`${this.scopeName}#${this.ruleName}`}},Es=class{constructor(){b(this,"_references",[]);b(this,"_seenReferenceKeys",new Set);b(this,"visitedRule",new Set)}get references(){return this._references}add(n){const e=n.toKey();this._seenReferenceKeys.has(e)||(this._seenReferenceKeys.add(e),this._references.push(n))}},qs=class{constructor(n,e){b(this,"seenFullScopeRequests",new Set);b(this,"seenPartialScopeRequests",new Set);b(this,"Q");this.repo=n,this.initialScopeName=e,this.seenFullScopeRequests.add(this.initialScopeName),this.Q=[new Re(this.initialScopeName)]}processQueue(){const n=this.Q;this.Q=[];const e=new Es;for(const t of n)As(t,this.initialScopeName,this.repo,e);for(const t of e.references)if(t instanceof Re){if(this.seenFullScopeRequests.has(t.scopeName))continue;this.seenFullScopeRequests.add(t.scopeName),this.Q.push(t)}else{if(this.seenFullScopeRequests.has(t.scopeName)||this.seenPartialScopeRequests.has(t.toKey()))continue;this.seenPartialScopeRequests.add(t.toKey()),this.Q.push(t)}}};function As(n,e,t,a){const s=t.lookup(n.scopeName);if(!s){if(n.scopeName===e)throw new Error(`No grammar provided for <${e}>`);return}const i=t.lookup(e);n instanceof Re?Ke({baseGrammar:i,selfGrammar:s},a):vn(n.ruleName,{baseGrammar:i,selfGrammar:s,repository:s.repository},a);const r=t.injections(n.scopeName);if(r)for(const c of r)a.add(new Re(c))}function vn(n,e,t){if(e.repository&&e.repository[n]){const a=e.repository[n];en([a],e,t)}}function Ke(n,e){n.selfGrammar.patterns&&Array.isArray(n.selfGrammar.patterns)&&en(n.selfGrammar.patterns,{...n,repository:n.selfGrammar.repository},e),n.selfGrammar.injections&&en(Object.values(n.selfGrammar.injections),{...n,repository:n.selfGrammar.repository},e)}function en(n,e,t){for(const a of n){if(t.visitedRule.has(a))continue;t.visitedRule.add(a);const s=a.repository?ht({},e.repository,a.repository):e.repository;Array.isArray(a.patterns)&&en(a.patterns,{...e,repository:s},t);const i=a.include;if(!i)continue;const r=Ct(i);switch(r.kind){case 0:Ke({...e,selfGrammar:e.baseGrammar},t);break;case 1:Ke(e,t);break;case 2:vn(r.ruleName,{...e,repository:s},t);break;case 3:case 4:const c=r.scopeName===e.selfGrammar.scopeName?e.selfGrammar:r.scopeName===e.baseGrammar.scopeName?e.baseGrammar:void 0;if(c){const o={baseGrammar:e.baseGrammar,selfGrammar:c,repository:s};r.kind===4?vn(r.ruleName,o,t):Ke(o,t)}else r.kind===4?t.add(new Ss(r.scopeName,r.ruleName)):t.add(new Re(r.scopeName));break}}}var Ns=class{constructor(){b(this,"kind",0)}},Fs=class{constructor(){b(this,"kind",1)}},Bs=class{constructor(n){b(this,"kind",2);this.ruleName=n}},Gs=class{constructor(n){b(this,"kind",3);this.scopeName=n}},Is=class{constructor(n,e){b(this,"kind",4);this.scopeName=n,this.ruleName=e}};function Ct(n){if(n==="$base")return new Ns;if(n==="$self")return new Fs;const e=n.indexOf("#");if(e===-1)return new Gs(n);if(e===0)return new Bs(n.substring(1));{const t=n.substring(0,e),a=n.substring(e+1);return new Is(t,a)}}var Rs=/\\(\d+)/,St=/\\(\d+)/g,Ps=-1,Et=-2;function _l(n){return n}function $l(n){return n}var Pe=class{constructor(n,e,t,a){b(this,"$location");b(this,"id");b(this,"_nameIsCapturing");b(this,"_name");b(this,"_contentNameIsCapturing");b(this,"_contentName");this.$location=n,this.id=e,this._name=t||null,this._nameIsCapturing=Ye.hasCaptures(this._name),this._contentName=a||null,this._contentNameIsCapturing=Ye.hasCaptures(this._contentName)}get debugName(){const n=this.$location?`${ft(this.$location.filename)}:${this.$location.line}`:"unknown";return`${this.constructor.name}#${this.id} @ ${n}`}getName(n,e){return!this._nameIsCapturing||this._name===null||n===null||e===null?this._name:Ye.replaceCaptures(this._name,n,e)}getContentName(n,e){return!this._contentNameIsCapturing||this._contentName===null?this._contentName:Ye.replaceCaptures(this._contentName,n,e)}},zs=class extends Pe{constructor(e,t,a,s,i){super(e,t,a,s);b(this,"retokenizeCapturedWithRuleId");this.retokenizeCapturedWithRuleId=i}dispose(){}collectPatterns(e,t){throw new Error("Not supported!")}compile(e,t){throw new Error("Not supported!")}compileAG(e,t,a,s){throw new Error("Not supported!")}},Ls=class extends Pe{constructor(e,t,a,s,i){super(e,t,a,null);b(this,"_match");b(this,"captures");b(this,"_cachedCompiledPatterns");this._match=new ze(s,this.id),this.captures=i,this._cachedCompiledPatterns=null}dispose(){this._cachedCompiledPatterns&&(this._cachedCompiledPatterns.dispose(),this._cachedCompiledPatterns=null)}get debugMatchRegExp(){return`${this._match.source}`}collectPatterns(e,t){t.push(this._match)}compile(e,t){return this._getCachedCompiledPatterns(e).compile(e)}compileAG(e,t,a,s){return this._getCachedCompiledPatterns(e).compileAG(e,a,s)}_getCachedCompiledPatterns(e){return this._cachedCompiledPatterns||(this._cachedCompiledPatterns=new Le,this.collectPatterns(e,this._cachedCompiledPatterns)),this._cachedCompiledPatterns}},qt=class extends Pe{constructor(e,t,a,s,i){super(e,t,a,s);b(this,"hasMissingPatterns");b(this,"patterns");b(this,"_cachedCompiledPatterns");this.patterns=i.patterns,this.hasMissingPatterns=i.hasMissingPatterns,this._cachedCompiledPatterns=null}dispose(){this._cachedCompiledPatterns&&(this._cachedCompiledPatterns.dispose(),this._cachedCompiledPatterns=null)}collectPatterns(e,t){for(const a of this.patterns)e.getRule(a).collectPatterns(e,t)}compile(e,t){return this._getCachedCompiledPatterns(e).compile(e)}compileAG(e,t,a,s){return this._getCachedCompiledPatterns(e).compileAG(e,a,s)}_getCachedCompiledPatterns(e){return this._cachedCompiledPatterns||(this._cachedCompiledPatterns=new Le,this.collectPatterns(e,this._cachedCompiledPatterns)),this._cachedCompiledPatterns}},Cn=class extends Pe{constructor(e,t,a,s,i,r,c,o,l,u){super(e,t,a,s);b(this,"_begin");b(this,"beginCaptures");b(this,"_end");b(this,"endHasBackReferences");b(this,"endCaptures");b(this,"applyEndPatternLast");b(this,"hasMissingPatterns");b(this,"patterns");b(this,"_cachedCompiledPatterns");this._begin=new ze(i,this.id),this.beginCaptures=r,this._end=new ze(c||"￿",-1),this.endHasBackReferences=this._end.hasBackReferences,this.endCaptures=o,this.applyEndPatternLast=l||!1,this.patterns=u.patterns,this.hasMissingPatterns=u.hasMissingPatterns,this._cachedCompiledPatterns=null}dispose(){this._cachedCompiledPatterns&&(this._cachedCompiledPatterns.dispose(),this._cachedCompiledPatterns=null)}get debugBeginRegExp(){return`${this._begin.source}`}get debugEndRegExp(){return`${this._end.source}`}getEndWithResolvedBackReferences(e,t){return this._end.resolveBackReferences(e,t)}collectPatterns(e,t){t.push(this._begin)}compile(e,t){return this._getCachedCompiledPatterns(e,t).compile(e)}compileAG(e,t,a,s){return this._getCachedCompiledPatterns(e,t).compileAG(e,a,s)}_getCachedCompiledPatterns(e,t){if(!this._cachedCompiledPatterns){this._cachedCompiledPatterns=new Le;for(const a of this.patterns)e.getRule(a).collectPatterns(e,this._cachedCompiledPatterns);this.applyEndPatternLast?this._cachedCompiledPatterns.push(this._end.hasBackReferences?this._end.clone():this._end):this._cachedCompiledPatterns.unshift(this._end.hasBackReferences?this._end.clone():this._end)}return this._end.hasBackReferences&&(this.applyEndPatternLast?this._cachedCompiledPatterns.setSource(this._cachedCompiledPatterns.length()-1,t):this._cachedCompiledPatterns.setSource(0,t)),this._cachedCompiledPatterns}},nn=class extends Pe{constructor(e,t,a,s,i,r,c,o,l){super(e,t,a,s);b(this,"_begin");b(this,"beginCaptures");b(this,"whileCaptures");b(this,"_while");b(this,"whileHasBackReferences");b(this,"hasMissingPatterns");b(this,"patterns");b(this,"_cachedCompiledPatterns");b(this,"_cachedCompiledWhilePatterns");this._begin=new ze(i,this.id),this.beginCaptures=r,this.whileCaptures=o,this._while=new ze(c,Et),this.whileHasBackReferences=this._while.hasBackReferences,this.patterns=l.patterns,this.hasMissingPatterns=l.hasMissingPatterns,this._cachedCompiledPatterns=null,this._cachedCompiledWhilePatterns=null}dispose(){this._cachedCompiledPatterns&&(this._cachedCompiledPatterns.dispose(),this._cachedCompiledPatterns=null),this._cachedCompiledWhilePatterns&&(this._cachedCompiledWhilePatterns.dispose(),this._cachedCompiledWhilePatterns=null)}get debugBeginRegExp(){return`${this._begin.source}`}get debugWhileRegExp(){return`${this._while.source}`}getWhileWithResolvedBackReferences(e,t){return this._while.resolveBackReferences(e,t)}collectPatterns(e,t){t.push(this._begin)}compile(e,t){return this._getCachedCompiledPatterns(e).compile(e)}compileAG(e,t,a,s){return this._getCachedCompiledPatterns(e).compileAG(e,a,s)}_getCachedCompiledPatterns(e){if(!this._cachedCompiledPatterns){this._cachedCompiledPatterns=new Le;for(const t of this.patterns)e.getRule(t).collectPatterns(e,this._cachedCompiledPatterns)}return this._cachedCompiledPatterns}compileWhile(e,t){return this._getCachedCompiledWhilePatterns(e,t).compile(e)}compileWhileAG(e,t,a,s){return this._getCachedCompiledWhilePatterns(e,t).compileAG(e,a,s)}_getCachedCompiledWhilePatterns(e,t){return this._cachedCompiledWhilePatterns||(this._cachedCompiledWhilePatterns=new Le,this._cachedCompiledWhilePatterns.push(this._while.hasBackReferences?this._while.clone():this._while)),this._while.hasBackReferences&&this._cachedCompiledWhilePatterns.setSource(0,t||"￿"),this._cachedCompiledWhilePatterns}},At=class L{static createCaptureRule(e,t,a,s,i){return e.registerRule(r=>new zs(t,r,a,s,i))}static getCompiledRuleId(e,t,a){return e.id||t.registerRule(s=>{if(e.id=s,e.match)return new Ls(e.$vscodeTextmateLocation,e.id,e.name,e.match,L._compileCaptures(e.captures,t,a));if(typeof e.begin=="undefined"){e.repository&&(a=ht({},a,e.repository));let i=e.patterns;return typeof i=="undefined"&&e.include&&(i=[{include:e.include}]),new qt(e.$vscodeTextmateLocation,e.id,e.name,e.contentName,L._compilePatterns(i,t,a))}return e.while?new nn(e.$vscodeTextmateLocation,e.id,e.name,e.contentName,e.begin,L._compileCaptures(e.beginCaptures||e.captures,t,a),e.while,L._compileCaptures(e.whileCaptures||e.captures,t,a),L._compilePatterns(e.patterns,t,a)):new Cn(e.$vscodeTextmateLocation,e.id,e.name,e.contentName,e.begin,L._compileCaptures(e.beginCaptures||e.captures,t,a),e.end,L._compileCaptures(e.endCaptures||e.captures,t,a),e.applyEndPatternLast,L._compilePatterns(e.patterns,t,a))}),e.id}static _compileCaptures(e,t,a){let s=[];if(e){let i=0;for(const r in e){if(r==="$vscodeTextmateLocation")continue;const c=parseInt(r,10);c>i&&(i=c)}for(let r=0;r<=i;r++)s[r]=null;for(const r in e){if(r==="$vscodeTextmateLocation")continue;const c=parseInt(r,10);let o=0;e[r].patterns&&(o=L.getCompiledRuleId(e[r],t,a)),s[c]=L.createCaptureRule(t,e[r].$vscodeTextmateLocation,e[r].name,e[r].contentName,o)}}return s}static _compilePatterns(e,t,a){let s=[];if(e)for(let i=0,r=e.length;ie.substring(s.start,s.end));return St.lastIndex=0,this.source.replace(St,(s,i)=>xt(a[parseInt(i,10)]||""))}_buildAnchorCache(){if(typeof this.source!="string")throw new Error("This method should only be called if the source is a string");let e=[],t=[],a=[],s=[],i,r,c,o;for(i=0,r=this.source.length;it.source);this._cached=new Nt(n,e,this._items.map(t=>t.ruleId))}return this._cached}compileAG(n,e,t){return this._hasAnchors?e?t?(this._anchorCache.A1_G1||(this._anchorCache.A1_G1=this._resolveAnchors(n,e,t)),this._anchorCache.A1_G1):(this._anchorCache.A1_G0||(this._anchorCache.A1_G0=this._resolveAnchors(n,e,t)),this._anchorCache.A1_G0):t?(this._anchorCache.A0_G1||(this._anchorCache.A0_G1=this._resolveAnchors(n,e,t)),this._anchorCache.A0_G1):(this._anchorCache.A0_G0||(this._anchorCache.A0_G0=this._resolveAnchors(n,e,t)),this._anchorCache.A0_G0):this.compile(n)}_resolveAnchors(n,e,t){let a=this._items.map(s=>s.resolveAnchors(e,t));return new Nt(n,a,this._items.map(s=>s.ruleId))}},Nt=class{constructor(n,e,t){b(this,"scanner");this.regExps=e,this.rules=t,this.scanner=n.createOnigScanner(e)}dispose(){typeof this.scanner.dispose=="function"&&this.scanner.dispose()}toString(){const n=[];for(let e=0,t=this.rules.length;e{const t=this._scopeToLanguage(e),a=this._toStandardTokenType(e);return new Sn(t,a)}));this._defaultAttributes=new Sn(e,8),this._embeddedLanguagesMatcher=new Os(Object.entries(t||{}))}getDefaultAttributes(){return this._defaultAttributes}getBasicScopeAttributes(e){return e===null?te._NULL_SCOPE_METADATA:this._getBasicScopeAttributes.get(e)}_scopeToLanguage(e){return this._embeddedLanguagesMatcher.match(e)||0}_toStandardTokenType(e){const t=e.match(te.STANDARD_TOKEN_TYPE_REGEXP);if(!t)return 8;switch(t[1]){case"comment":return 1;case"string":return 2;case"regex":return 3;case"meta.embedded":return 0}throw new Error("Unexpected match for standard token type!")}},b(te,"_NULL_SCOPE_METADATA",new Sn(0,0)),b(te,"STANDARD_TOKEN_TYPE_REGEXP",/\b(comment|string|regex|meta\.embedded)\b/),te),Os=class{constructor(n){b(this,"values");b(this,"scopesRegExp");if(n.length===0)this.values=null,this.scopesRegExp=null;else{this.values=new Map(n);const e=n.map(([t,a])=>xt(t));e.sort(),e.reverse(),this.scopesRegExp=new RegExp(`^((${e.join(")|(")}))($|\\.)`,"")}}match(n){if(!this.scopesRegExp)return;const e=n.match(this.scopesRegExp);if(e)return this.values.get(e[1])}};typeof process!="undefined"&&process.env.VSCODE_TEXTMATE_DEBUG;var Ft=class{constructor(n,e){this.stack=n,this.stoppedEarly=e}};function Bt(n,e,t,a,s,i,r,c){const o=e.content.length;let l=!1,u=-1;if(r){const g=Ms(n,e,t,a,s,i);s=g.stack,a=g.linePos,t=g.isFirstLine,u=g.anchorPosition}const m=Date.now();for(;!l;){if(c!==0&&Date.now()-m>c)return new Ft(s,!0);p()}return new Ft(s,!1);function p(){const g=Ds(n,e,t,a,s,u);if(!g){i.produce(s,o),l=!0;return}const d=g.captureIndices,f=g.matchedRuleId,$=d&&d.length>0?d[0].end>a:!1;if(f===Ps){const _=s.getRule(n);i.produce(s,d[0].start),s=s.withContentNameScopesList(s.nameScopesList),Te(n,e,t,s,i,_.endCaptures,d),i.produce(s,d[0].end);const x=s;if(s=s.parent,u=x.getAnchorPos(),!$&&x.getEnterPos()===a){s=x,i.produce(s,o),l=!0;return}}else{const _=n.getRule(f);i.produce(s,d[0].start);const x=s,y=_.getName(e.content,d),w=s.contentNameScopesList.pushAttributed(y,n);if(s=s.push(f,a,u,d[0].end===o,null,w,w),_ instanceof Cn){const C=_;Te(n,e,t,s,i,C.beginCaptures,d),i.produce(s,d[0].end),u=d[0].end;const E=C.getContentName(e.content,d),S=w.pushAttributed(E,n);if(s=s.withContentNameScopesList(S),C.endHasBackReferences&&(s=s.withEndRule(C.getEndWithResolvedBackReferences(e.content,d))),!$&&x.hasSameRuleAs(s)){s=s.pop(),i.produce(s,o),l=!0;return}}else if(_ instanceof nn){const C=_;Te(n,e,t,s,i,C.beginCaptures,d),i.produce(s,d[0].end),u=d[0].end;const E=C.getContentName(e.content,d),S=w.pushAttributed(E,n);if(s=s.withContentNameScopesList(S),C.whileHasBackReferences&&(s=s.withEndRule(C.getWhileWithResolvedBackReferences(e.content,d))),!$&&x.hasSameRuleAs(s)){s=s.pop(),i.produce(s,o),l=!0;return}}else if(Te(n,e,t,s,i,_.captures,d),i.produce(s,d[0].end),s=s.pop(),!$){s=s.safePop(),i.produce(s,o),l=!0;return}}d[0].end>a&&(a=d[0].end,t=!1)}}function Ms(n,e,t,a,s,i){let r=s.beginRuleCapturedEOL?0:-1;const c=[];for(let o=s;o;o=o.pop()){const l=o.getRule(n);l instanceof nn&&c.push({rule:l,stack:o})}for(let o=c.pop();o;o=c.pop()){const{ruleScanner:l,findOptions:u}=Hs(o.rule,n,o.stack.endRule,t,a===r),m=l.findNextMatchSync(e,a,u);if(m){if(m.ruleId!==Et){s=o.stack.pop();break}m.captureIndices&&m.captureIndices.length&&(i.produce(o.stack,m.captureIndices[0].start),Te(n,e,t,o.stack,i,o.rule.whileCaptures,m.captureIndices),i.produce(o.stack,m.captureIndices[0].end),r=m.captureIndices[0].end,m.captureIndices[0].end>a&&(a=m.captureIndices[0].end,t=!1))}else{s=o.stack.pop();break}}return{stack:s,linePos:a,anchorPosition:r,isFirstLine:t}}function Ds(n,e,t,a,s,i){const r=Us(n,e,t,a,s,i),c=n.getInjections();if(c.length===0)return r;const o=Zs(c,n,e,t,a,s,i);if(!o)return r;if(!r)return o;const l=r.captureIndices[0].start,u=o.captureIndices[0].start;return u=c)&&(c=y,o=x.captureIndices,l=x.ruleId,u=d.priority,c===s))break}return o?{priorityMatch:u===-1,captureIndices:o,matchedRuleId:l}:null}function Gt(n,e,t,a,s){return{ruleScanner:n.compileAG(e,t,a,s),findOptions:0}}function Hs(n,e,t,a,s){return{ruleScanner:n.compileWhileAG(e,t,a,s),findOptions:0}}function Te(n,e,t,a,s,i,r){if(i.length===0)return;const c=e.content,o=Math.min(i.length,r.length),l=[],u=r[0].end;for(let m=0;mu)break;for(;l.length>0&&l[l.length-1].endPos<=g.start;)s.produceFromScopes(l[l.length-1].scopes,l[l.length-1].endPos),l.pop();if(l.length>0?s.produceFromScopes(l[l.length-1].scopes,g.start):s.produce(a,g.start),p.retokenizeCapturedWithRuleId){const f=p.getName(c,r),$=a.contentNameScopesList.pushAttributed(f,n),_=p.getContentName(c,r),x=$.pushAttributed(_,n),y=a.push(p.retokenizeCapturedWithRuleId,g.start,-1,!1,null,$,x),w=n.createOnigString(c.substring(0,g.end));Bt(n,w,t&&g.start===0,g.start,y,s,!1,0),vt(w);continue}const d=p.getName(c,r);if(d!==null){const $=(l.length>0?l[l.length-1].scopes:a.contentNameScopesList).pushAttributed(d,n);l.push(new Ws($,g.end))}}for(;l.length>0;)s.produceFromScopes(l[l.length-1].scopes,l[l.length-1].endPos),l.pop()}var Ws=class{constructor(n,e){b(this,"scopes");b(this,"endPos");this.scopes=n,this.endPos=e}};function Vs(n,e,t,a,s,i,r,c){return new Ys(n,e,t,a,s,i,r,c)}function It(n,e,t,a,s){const i=Qe(e,tn),r=At.getCompiledRuleId(t,a,s.repository);for(const c of i)n.push({debugSelector:e,matcher:c.matcher,ruleId:r,grammar:s,priority:c.priority})}function tn(n,e){if(e.length{for(let s=t;st&&n.substr(0,t)===e&&n[t]==="."}var Ys=class{constructor(n,e,t,a,s,i,r,c){b(this,"_rootId");b(this,"_lastRuleId");b(this,"_ruleId2desc");b(this,"_includedGrammars");b(this,"_grammarRepository");b(this,"_grammar");b(this,"_injections");b(this,"_basicScopeAttributesProvider");b(this,"_tokenTypeMatchers");if(this._rootScopeName=n,this.balancedBracketSelectors=i,this._onigLib=c,this._basicScopeAttributesProvider=new Ts(t,a),this._rootId=-1,this._lastRuleId=0,this._ruleId2desc=[null],this._includedGrammars={},this._grammarRepository=r,this._grammar=Rt(e,null),this._injections=null,this._tokenTypeMatchers=[],s)for(const o of Object.keys(s)){const l=Qe(o,tn);for(const u of l)this._tokenTypeMatchers.push({matcher:u.matcher,type:s[o]})}}get themeProvider(){return this._grammarRepository}dispose(){for(const n of this._ruleId2desc)n&&n.dispose()}createOnigScanner(n){return this._onigLib.createOnigScanner(n)}createOnigString(n){return this._onigLib.createOnigString(n)}getMetadataForScope(n){return this._basicScopeAttributesProvider.getBasicScopeAttributes(n)}_collectInjections(){const n={lookup:s=>s===this._rootScopeName?this._grammar:this.getExternalGrammar(s),injections:s=>this._grammarRepository.injections(s)},e=[],t=this._rootScopeName,a=n.lookup(t);if(a){const s=a.injections;if(s)for(let r in s)It(e,r,s[r],this,a);const i=this._grammarRepository.injections(t);i&&i.forEach(r=>{const c=this.getExternalGrammar(r);if(c){const o=c.injectionSelector;o&&It(e,o,c,this,c)}})}return e.sort((s,i)=>s.priority-i.priority),e}getInjections(){return this._injections===null&&(this._injections=this._collectInjections()),this._injections}registerRule(n){const e=++this._lastRuleId,t=n(e);return this._ruleId2desc[e]=t,t}getRule(n){return this._ruleId2desc[n]}getExternalGrammar(n,e){if(this._includedGrammars[n])return this._includedGrammars[n];if(this._grammarRepository){const t=this._grammarRepository.lookup(n);if(t)return this._includedGrammars[n]=Rt(t,e&&e.$base),this._includedGrammars[n]}}tokenizeLine(n,e,t=0){const a=this._tokenize(n,e,!1,t);return{tokens:a.lineTokens.getResult(a.ruleStack,a.lineLength),ruleStack:a.ruleStack,stoppedEarly:a.stoppedEarly}}tokenizeLine2(n,e,t=0){const a=this._tokenize(n,e,!0,t);return{tokens:a.lineTokens.getBinaryResult(a.ruleStack,a.lineLength),ruleStack:a.ruleStack,stoppedEarly:a.stoppedEarly}}_tokenize(n,e,t,a){this._rootId===-1&&(this._rootId=At.getCompiledRuleId(this._grammar.repository.$self,this,this._grammar.repository),this.getInjections());let s;if(!e||e===En.NULL){s=!0;const l=this._basicScopeAttributesProvider.getDefaultAttributes(),u=this.themeProvider.getDefaults(),m=we.set(0,l.languageId,l.tokenType,null,u.fontStyle,u.foregroundId,u.backgroundId),p=this.getRule(this._rootId).getName(null,null);let g;p?g=Oe.createRootAndLookUpScopeName(p,m,this):g=Oe.createRoot("unknown",m),e=new En(null,this._rootId,-1,-1,!1,null,g,g)}else s=!1,e.reset();n=n+` -`;const i=this.createOnigString(n),r=i.content.length,c=new Qs(t,n,this._tokenTypeMatchers,this.balancedBracketSelectors),o=Bt(this,i,s,0,e,c,!0,a);return vt(i),{lineLength:r,lineTokens:c,ruleStack:o.stack,stoppedEarly:o.stoppedEarly}}};function Rt(n,e){return n=q(n),n.repository=n.repository||{},n.repository.$self={$vscodeTextmateLocation:n.$vscodeTextmateLocation,patterns:n.patterns,name:n.scopeName},n.repository.$base=e||n.repository.$self,n}var Oe=class J{constructor(e,t,a){this.parent=e,this.scopePath=t,this.tokenAttributes=a}static fromExtension(e,t){var i;let a=e,s=(i=e==null?void 0:e.scopePath)!=null?i:null;for(const r of t)s=kn.push(s,r.scopeNames),a=new J(a,s,r.encodedTokenAttributes);return a}static createRoot(e,t){return new J(null,new kn(null,e),t)}static createRootAndLookUpScopeName(e,t,a){const s=a.getMetadataForScope(e),i=new kn(null,e),r=a.themeProvider.themeMatch(i),c=J.mergeAttributes(t,s,r);return new J(null,i,c)}get scopeName(){return this.scopePath.scopeName}toString(){return this.getScopeNames().join(" ")}equals(e){return J.equals(this,e)}static equals(e,t){do{if(e===t||!e&&!t)return!0;if(!e||!t||e.scopeName!==t.scopeName||e.tokenAttributes!==t.tokenAttributes)return!1;e=e.parent,t=t.parent}while(!0)}static mergeAttributes(e,t,a){let s=-1,i=0,r=0;return a!==null&&(s=a.fontStyle,i=a.foregroundId,r=a.backgroundId),we.set(e,t.languageId,t.tokenType,null,s,i,r)}pushAttributed(e,t){if(e===null)return this;if(e.indexOf(" ")===-1)return J._pushAttributed(this,e,t);const a=e.split(/ /g);let s=this;for(const i of a)s=J._pushAttributed(s,i,t);return s}static _pushAttributed(e,t,a){const s=a.getMetadataForScope(t),i=e.scopePath.push(t),r=a.themeProvider.themeMatch(i),c=J.mergeAttributes(e.tokenAttributes,s,r);return new J(e,i,c)}getScopeNames(){return this.scopePath.getSegments()}getExtensionIfDefined(e){var s,i;const t=[];let a=this;for(;a&&a!==e;)t.push({encodedTokenAttributes:a.tokenAttributes,scopeNames:a.scopePath.getExtensionIfDefined((i=(s=a.parent)==null?void 0:s.scopePath)!=null?i:null)}),a=a.parent;return a===e?t.reverse():void 0}},En=(W=class{constructor(e,t,a,s,i,r,c,o){b(this,"_stackElementBrand");b(this,"_enterPos");b(this,"_anchorPos");b(this,"depth");this.parent=e,this.ruleId=t,this.beginRuleCapturedEOL=i,this.endRule=r,this.nameScopesList=c,this.contentNameScopesList=o,this.depth=this.parent?this.parent.depth+1:1,this._enterPos=a,this._anchorPos=s}equals(e){return e===null?!1:W._equals(this,e)}static _equals(e,t){return e===t?!0:this._structuralEquals(e,t)?Oe.equals(e.contentNameScopesList,t.contentNameScopesList):!1}static _structuralEquals(e,t){do{if(e===t||!e&&!t)return!0;if(!e||!t||e.depth!==t.depth||e.ruleId!==t.ruleId||e.endRule!==t.endRule)return!1;e=e.parent,t=t.parent}while(!0)}clone(){return this}static _reset(e){for(;e;)e._enterPos=-1,e._anchorPos=-1,e=e.parent}reset(){W._reset(this)}pop(){return this.parent}safePop(){return this.parent?this.parent:this}push(e,t,a,s,i,r,c){return new W(this,e,t,a,s,i,r,c)}getEnterPos(){return this._enterPos}getAnchorPos(){return this._anchorPos}getRule(e){return e.getRule(this.ruleId)}toString(){const e=[];return this._writeString(e,0),"["+e.join(",")+"]"}_writeString(e,t){var a,s;return this.parent&&(t=this.parent._writeString(e,t)),e[t++]=`(${this.ruleId}, ${(a=this.nameScopesList)==null?void 0:a.toString()}, ${(s=this.contentNameScopesList)==null?void 0:s.toString()})`,t}withContentNameScopesList(e){return this.contentNameScopesList===e?this:this.parent.push(this.ruleId,this._enterPos,this._anchorPos,this.beginRuleCapturedEOL,this.endRule,this.nameScopesList,e)}withEndRule(e){return this.endRule===e?this:new W(this.parent,this.ruleId,this._enterPos,this._anchorPos,this.beginRuleCapturedEOL,e,this.nameScopesList,this.contentNameScopesList)}hasSameRuleAs(e){let t=this;for(;t&&t._enterPos===e._enterPos;){if(t.ruleId===e.ruleId)return!0;t=t.parent}return!1}toStateStackFrame(){var e,t,a,s,i,r;return{ruleId:this.ruleId,beginRuleCapturedEOL:this.beginRuleCapturedEOL,endRule:this.endRule,nameScopesList:(s=(a=this.nameScopesList)==null?void 0:a.getExtensionIfDefined((t=(e=this.parent)==null?void 0:e.nameScopesList)!=null?t:null))!=null?s:[],contentNameScopesList:(r=(i=this.contentNameScopesList)==null?void 0:i.getExtensionIfDefined(this.nameScopesList))!=null?r:[]}}static pushFrame(e,t){var s,i,r;const a=Oe.fromExtension((s=e==null?void 0:e.nameScopesList)!=null?s:null,t.nameScopesList);return new W(e,t.ruleId,(i=t.enterPos)!=null?i:-1,(r=t.anchorPos)!=null?r:-1,t.beginRuleCapturedEOL,t.endRule,a,Oe.fromExtension(a,t.contentNameScopesList))}},b(W,"NULL",new W(null,0,0,0,!1,null,null,null)),W),Js=class{constructor(n,e){b(this,"balancedBracketScopes");b(this,"unbalancedBracketScopes");b(this,"allowAny",!1);this.balancedBracketScopes=n.flatMap(t=>t==="*"?(this.allowAny=!0,[]):Qe(t,tn).map(a=>a.matcher)),this.unbalancedBracketScopes=e.flatMap(t=>Qe(t,tn).map(a=>a.matcher))}get matchesAlways(){return this.allowAny&&this.unbalancedBracketScopes.length===0}get matchesNever(){return this.balancedBracketScopes.length===0&&!this.allowAny}match(n){for(const e of this.unbalancedBracketScopes)if(e(n))return!1;for(const e of this.balancedBracketScopes)if(e(n))return!0;return this.allowAny}},Qs=class{constructor(n,e,t,a){b(this,"_emitBinaryTokens");b(this,"_lineText");b(this,"_tokens");b(this,"_binaryTokens");b(this,"_lastTokenEndIndex");b(this,"_tokenTypeOverrides");this.balancedBracketSelectors=a,this._emitBinaryTokens=n,this._tokenTypeOverrides=t,this._lineText=null,this._tokens=[],this._binaryTokens=[],this._lastTokenEndIndex=0}produce(n,e){this.produceFromScopes(n.contentNameScopesList,e)}produceFromScopes(n,e){var a,s,i,r;if(this._lastTokenEndIndex>=e)return;if(this._emitBinaryTokens){let c=(a=n==null?void 0:n.tokenAttributes)!=null?a:0,o=!1;if((s=this.balancedBracketSelectors)!=null&&s.matchesAlways&&(o=!0),this._tokenTypeOverrides.length>0||this.balancedBracketSelectors&&!this.balancedBracketSelectors.matchesAlways&&!this.balancedBracketSelectors.matchesNever){const l=(i=n==null?void 0:n.getScopeNames())!=null?i:[];for(const u of this._tokenTypeOverrides)u.matcher(l)&&(c=we.set(c,0,u.type,null,-1,0,0));this.balancedBracketSelectors&&(o=this.balancedBracketSelectors.match(l))}if(o&&(c=we.set(c,0,8,o,-1,0,0)),this._binaryTokens.length>0&&this._binaryTokens[this._binaryTokens.length-1]===c){this._lastTokenEndIndex=e;return}this._binaryTokens.push(this._lastTokenEndIndex),this._binaryTokens.push(c),this._lastTokenEndIndex=e;return}const t=(r=n==null?void 0:n.getScopeNames())!=null?r:[];this._tokens.push({startIndex:this._lastTokenEndIndex,endIndex:e,scopes:t}),this._lastTokenEndIndex=e}getResult(n,e){return this._tokens.length>0&&this._tokens[this._tokens.length-1].startIndex===e-1&&this._tokens.pop(),this._tokens.length===0&&(this._lastTokenEndIndex=-1,this.produce(n,e),this._tokens[this._tokens.length-1].startIndex=0),this._tokens}getBinaryResult(n,e){this._binaryTokens.length>0&&this._binaryTokens[this._binaryTokens.length-2]===e-1&&(this._binaryTokens.pop(),this._binaryTokens.pop()),this._binaryTokens.length===0&&(this._lastTokenEndIndex=-1,this.produce(n,e),this._binaryTokens[this._binaryTokens.length-2]=0);const t=new Uint32Array(this._binaryTokens.length);for(let a=0,s=this._binaryTokens.length;a0;)r.Q.map(c=>this._loadSingleGrammar(c.scopeName)),r.processQueue();return this._grammarForScopeName(e,t,a,s,i)}_loadSingleGrammar(e){this._ensureGrammarCache.has(e)||(this._doLoadSingleGrammar(e),this._ensureGrammarCache.set(e,!0))}_doLoadSingleGrammar(e){const t=this._options.loadGrammar(e);if(t){const a=typeof this._options.getInjections=="function"?this._options.getInjections(e):void 0;this._syncRegistry.addGrammar(t,a)}}addGrammar(e,t=[],a=0,s=null){return this._syncRegistry.addGrammar(e,t),this._grammarForScopeName(e.scopeName,a,s)}_grammarForScopeName(e,t=0,a=null,s=null,i=null){return this._syncRegistry.grammarForScopeName(e,t,a,s,i)}},qn=En.NULL;const ni=["area","base","basefont","bgsound","br","col","command","embed","frame","hr","image","img","input","keygen","link","meta","param","source","track","wbr"];class Me{constructor(e,t,a){this.normal=t,this.property=e,a&&(this.space=a)}}Me.prototype.normal={},Me.prototype.property={},Me.prototype.space=void 0;function Pt(n,e){const t={},a={};for(const s of n)Object.assign(t,s.property),Object.assign(a,s.normal);return new Me(t,a,e)}function An(n){return n.toLowerCase()}class M{constructor(e,t){this.attribute=t,this.property=e}}M.prototype.attribute="",M.prototype.booleanish=!1,M.prototype.boolean=!1,M.prototype.commaOrSpaceSeparated=!1,M.prototype.commaSeparated=!1,M.prototype.defined=!1,M.prototype.mustUseProperty=!1,M.prototype.number=!1,M.prototype.overloadedBoolean=!1,M.prototype.property="",M.prototype.spaceSeparated=!1,M.prototype.space=void 0;let ti=0;const v=de(),B=de(),Nn=de(),h=de(),A=de(),ke=de(),U=de();function de(){return 2**++ti}const Fn=Object.freeze(Object.defineProperty({__proto__:null,boolean:v,booleanish:B,commaOrSpaceSeparated:U,commaSeparated:ke,number:h,overloadedBoolean:Nn,spaceSeparated:A},Symbol.toStringTag,{value:"Module"})),Bn=Object.keys(Fn);class Gn extends M{constructor(e,t,a,s){let i=-1;if(super(e,t),zt(this,"space",s),typeof a=="number")for(;++i4&&t.slice(0,4)==="data"&&ri.test(e)){if(e.charAt(4)==="-"){const i=e.slice(5).replace(Zt,li);a="data"+i.charAt(0).toUpperCase()+i.slice(1)}else{const i=e.slice(4);if(!Zt.test(i)){let r=i.replace(ii,ci);r.charAt(0)!=="-"&&(r="-"+r),e="data"+r}}s=Gn}return new s(a,e)}function ci(n){return"-"+n.toLowerCase()}function li(n){return n.charAt(1).toUpperCase()}const ui=Pt([Lt,ai,Mt,Dt,Ut],"html"),Ht=Pt([Lt,si,Mt,Dt,Ut],"svg"),Wt={}.hasOwnProperty;function mi(n,e){const t=e||{};function a(s,...i){let r=a.invalid;const c=a.handlers;if(s&&Wt.call(s,n)){const o=String(s[n]);r=Wt.call(c,o)?c[o]:a.unknown}if(r)return r.call(this,s,...i)}return a.handlers=t.handlers||{},a.invalid=t.invalid,a.unknown=t.unknown,a}const pi=/["&'<>`]/g,di=/[\uD800-\uDBFF][\uDC00-\uDFFF]/g,gi=/[\x01-\t\v\f\x0E-\x1F\x7F\x81\x8D\x8F\x90\x9D\xA0-\uFFFF]/g,bi=/[|\\{}()[\]^$+*?.]/g,Vt=new WeakMap;function hi(n,e){if(n=n.replace(e.subset?fi(e.subset):pi,a),e.subset||e.escapeOnly)return n;return n.replace(di,t).replace(gi,a);function t(s,i,r){return e.format((s.charCodeAt(0)-55296)*1024+s.charCodeAt(1)-56320+65536,r.charCodeAt(i+2),e)}function a(s,i,r){return e.format(s.charCodeAt(0),r.charCodeAt(i+1),e)}}function fi(n){let e=Vt.get(n);return e||(e=yi(n),Vt.set(n,e)),e}function yi(n){const e=[];let t=-1;for(;++t",OElig:"Œ",oelig:"œ",Scaron:"Š",scaron:"š",Yuml:"Ÿ",circ:"ˆ",tilde:"˜",ensp:" ",emsp:" ",thinsp:" ",zwnj:"‌",zwj:"‍",lrm:"‎",rlm:"‏",ndash:"–",mdash:"—",lsquo:"‘",rsquo:"’",sbquo:"‚",ldquo:"“",rdquo:"”",bdquo:"„",dagger:"†",Dagger:"‡",permil:"‰",lsaquo:"‹",rsaquo:"›",euro:"€"},ji=["cent","copy","divide","gt","lt","not","para","times"],Xt={}.hasOwnProperty,Rn={};let an;for(an in In)Xt.call(In,an)&&(Rn[In[an]]=an);const vi=/[^\dA-Za-z]/;function Ci(n,e,t,a){const s=String.fromCharCode(n);if(Xt.call(Rn,s)){const i=Rn[s],r="&"+i;return t&&ki.includes(i)&&!ji.includes(i)&&(!a||e&&e!==61&&vi.test(String.fromCharCode(e)))?r:r+";"}return""}function Si(n,e,t){let a=$i(n,e,t.omitOptionalSemicolons),s;if((t.useNamedReferences||t.useShortestReferences)&&(s=Ci(n,e,t.omitOptionalSemicolons,t.attribute)),(t.useShortestReferences||!s)&&t.useShortestReferences){const i=wi(n,e,t.omitOptionalSemicolons);i.length|^->||--!>|"],Ai=["<",">"];function Ni(n,e,t,a){return a.settings.bogusComments?"":"";function s(i){return ve(i,Object.assign({},a.settings.characterReferences,{subset:Ai}))}}function Fi(n,e,t,a){return""}function Yt(n,e){const t=String(n);if(typeof e!="string")throw new TypeError("Expected character");let a=0,s=t.indexOf(e);for(;s!==-1;)a++,s=t.indexOf(e,s+e.length);return a}function Bi(n,e){const t=e||{};return(n[n.length-1]===""?[...n,""]:n).join((t.padRight?" ":"")+","+(t.padLeft===!1?"":" ")).trim()}function Gi(n){return n.join(" ").trim()}const Ii=/[ \t\n\f\r]/g;function Pn(n){return typeof n=="object"?n.type==="text"?Jt(n.value):!1:Jt(n)}function Jt(n){return n.replace(Ii,"")===""}const I=Kt(1),Qt=Kt(-1),Ri=[];function Kt(n){return e;function e(t,a,s){const i=t?t.children:Ri;let r=(a||0)+n,c=i[r];if(!s)for(;c&&Pn(c);)r+=n,c=i[r];return c}}const Pi={}.hasOwnProperty;function ea(n){return e;function e(t,a,s){return Pi.call(n,t.tagName)&&n[t.tagName](t,a,s)}}const zn=ea({body:Li,caption:Ln,colgroup:Ln,dd:Di,dt:Mi,head:Ln,html:zi,li:Oi,optgroup:Ui,option:Zi,p:Ti,rp:na,rt:na,tbody:Wi,td:ta,tfoot:Vi,th:ta,thead:Hi,tr:Xi});function Ln(n,e,t){const a=I(t,e,!0);return!a||a.type!=="comment"&&!(a.type==="text"&&Pn(a.value.charAt(0)))}function zi(n,e,t){const a=I(t,e);return!a||a.type!=="comment"}function Li(n,e,t){const a=I(t,e);return!a||a.type!=="comment"}function Ti(n,e,t){const a=I(t,e);return a?a.type==="element"&&(a.tagName==="address"||a.tagName==="article"||a.tagName==="aside"||a.tagName==="blockquote"||a.tagName==="details"||a.tagName==="div"||a.tagName==="dl"||a.tagName==="fieldset"||a.tagName==="figcaption"||a.tagName==="figure"||a.tagName==="footer"||a.tagName==="form"||a.tagName==="h1"||a.tagName==="h2"||a.tagName==="h3"||a.tagName==="h4"||a.tagName==="h5"||a.tagName==="h6"||a.tagName==="header"||a.tagName==="hgroup"||a.tagName==="hr"||a.tagName==="main"||a.tagName==="menu"||a.tagName==="nav"||a.tagName==="ol"||a.tagName==="p"||a.tagName==="pre"||a.tagName==="section"||a.tagName==="table"||a.tagName==="ul"):!t||!(t.type==="element"&&(t.tagName==="a"||t.tagName==="audio"||t.tagName==="del"||t.tagName==="ins"||t.tagName==="map"||t.tagName==="noscript"||t.tagName==="video"))}function Oi(n,e,t){const a=I(t,e);return!a||a.type==="element"&&a.tagName==="li"}function Mi(n,e,t){const a=I(t,e);return!!(a&&a.type==="element"&&(a.tagName==="dt"||a.tagName==="dd"))}function Di(n,e,t){const a=I(t,e);return!a||a.type==="element"&&(a.tagName==="dt"||a.tagName==="dd")}function na(n,e,t){const a=I(t,e);return!a||a.type==="element"&&(a.tagName==="rp"||a.tagName==="rt")}function Ui(n,e,t){const a=I(t,e);return!a||a.type==="element"&&a.tagName==="optgroup"}function Zi(n,e,t){const a=I(t,e);return!a||a.type==="element"&&(a.tagName==="option"||a.tagName==="optgroup")}function Hi(n,e,t){const a=I(t,e);return!!(a&&a.type==="element"&&(a.tagName==="tbody"||a.tagName==="tfoot"))}function Wi(n,e,t){const a=I(t,e);return!a||a.type==="element"&&(a.tagName==="tbody"||a.tagName==="tfoot")}function Vi(n,e,t){return!I(t,e)}function Xi(n,e,t){const a=I(t,e);return!a||a.type==="element"&&a.tagName==="tr"}function ta(n,e,t){const a=I(t,e);return!a||a.type==="element"&&(a.tagName==="td"||a.tagName==="th")}const Yi=ea({body:Ki,colgroup:er,head:Qi,html:Ji,tbody:nr});function Ji(n){const e=I(n,-1);return!e||e.type!=="comment"}function Qi(n){const e=new Set;for(const a of n.children)if(a.type==="element"&&(a.tagName==="base"||a.tagName==="title")){if(e.has(a.tagName))return!1;e.add(a.tagName)}const t=n.children[0];return!t||t.type==="element"}function Ki(n){const e=I(n,-1,!0);return!e||e.type!=="comment"&&!(e.type==="text"&&Pn(e.value.charAt(0)))&&!(e.type==="element"&&(e.tagName==="meta"||e.tagName==="link"||e.tagName==="script"||e.tagName==="style"||e.tagName==="template"))}function er(n,e,t){const a=Qt(t,e),s=I(n,-1,!0);return t&&a&&a.type==="element"&&a.tagName==="colgroup"&&zn(a,t.children.indexOf(a),t)?!1:!!(s&&s.type==="element"&&s.tagName==="col")}function nr(n,e,t){const a=Qt(t,e),s=I(n,-1);return t&&a&&a.type==="element"&&(a.tagName==="thead"||a.tagName==="tbody")&&zn(a,t.children.indexOf(a),t)?!1:!!(s&&s.type==="element"&&s.tagName==="tr")}const sn={name:[[` -\f\r &/=>`.split(""),` -\f\r "&'/=>\``.split("")],[`\0 -\f\r "&'/<=>`.split(""),`\0 -\f\r "&'/<=>\``.split("")]],unquoted:[[` -\f\r &>`.split(""),`\0 -\f\r "&'<=>\``.split("")],[`\0 -\f\r "&'<=>\``.split(""),`\0 -\f\r "&'<=>\``.split("")]],single:[["&'".split(""),"\"&'`".split("")],["\0&'".split(""),"\0\"&'`".split("")]],double:[['"&'.split(""),"\"&'`".split("")],['\0"&'.split(""),"\0\"&'`".split("")]]};function tr(n,e,t,a){const s=a.schema,i=s.space==="svg"?!1:a.settings.omitOptionalTags;let r=s.space==="svg"?a.settings.closeEmptyElements:a.settings.voids.includes(n.tagName.toLowerCase());const c=[];let o;s.space==="html"&&n.tagName==="svg"&&(a.schema=Ht);const l=ar(a,n.properties),u=a.all(s.space==="html"&&n.tagName==="template"?n.content:n);return a.schema=s,u&&(r=!1),(l||!i||!Yi(n,e,t))&&(c.push("<",n.tagName,l?" "+l:""),r&&(s.space==="svg"||a.settings.closeSelfClosing)&&(o=l.charAt(l.length-1),(!a.settings.tightSelfClosing||o==="/"||o&&o!=='"'&&o!=="'")&&c.push(" "),c.push("/")),c.push(">")),c.push(u),!r&&(!i||!zn(n,e,t))&&c.push(""),c.join("")}function ar(n,e){const t=[];let a=-1,s;if(e){for(s in e)if(e[s]!==null&&e[s]!==void 0){const i=sr(n,s,e[s]);i&&t.push(i)}}for(;++aYt(t,n.alternative)&&(r=n.alternative),c=r+ve(t,Object.assign({},n.settings.characterReferences,{subset:(r==="'"?sn.single:sn.double)[s][i],attribute:!0}))+r),o+(c&&"="+c))}const ir=["<","&"];function aa(n,e,t,a){return t&&t.type==="element"&&(t.tagName==="script"||t.tagName==="style")?n.value:ve(n.value,Object.assign({},a.settings.characterReferences,{subset:ir}))}function rr(n,e,t,a){return a.settings.allowDangerousHtml?n.value:aa(n,e,t,a)}function or(n,e,t,a){return a.all(n)}const cr=mi("type",{invalid:lr,unknown:ur,handlers:{comment:Ni,doctype:Fi,element:tr,raw:rr,root:or,text:aa}});function lr(n){throw new Error("Expected node, not `"+n+"`")}function ur(n){const e=n;throw new Error("Cannot compile unknown node `"+e.type+"`")}const mr={},pr={},dr=[];function gr(n,e){const t=e||mr,a=t.quote||'"',s=a==='"'?"'":'"';if(a!=='"'&&a!=="'")throw new Error("Invalid quote `"+a+"`, expected `'` or `\"`");return{one:br,all:hr,settings:{omitOptionalTags:t.omitOptionalTags||!1,allowParseErrors:t.allowParseErrors||!1,allowDangerousCharacters:t.allowDangerousCharacters||!1,quoteSmart:t.quoteSmart||!1,preferUnquoted:t.preferUnquoted||!1,tightAttributes:t.tightAttributes||!1,upperDoctype:t.upperDoctype||!1,tightDoctype:t.tightDoctype||!1,bogusComments:t.bogusComments||!1,tightCommaSeparatedLists:t.tightCommaSeparatedLists||!1,tightSelfClosing:t.tightSelfClosing||!1,collapseEmptyAttributes:t.collapseEmptyAttributes||!1,allowDangerousHtml:t.allowDangerousHtml||!1,voids:t.voids||ni,characterReferences:t.characterReferences||pr,closeSelfClosing:t.closeSelfClosing||!1,closeEmptyElements:t.closeEmptyElements||!1},schema:t.space==="svg"?Ht:ui,quote:a,alternative:s}.one(Array.isArray(n)?{type:"root",children:n}:n,void 0,void 0)}function br(n,e,t){return cr(n,e,t,this)}function hr(n){const e=[],t=n&&n.children||dr;let a=-1;for(;++ae.default||e)}function Tn(n){return!n||["plaintext","txt","text","plain"].includes(n)}function yr(n){return n==="ansi"||Tn(n)}function On(n){return n==="none"}function _r(n){return On(n)}function ia(n,e){var a;if(!e)return n;n.properties||(n.properties={}),(a=n.properties).class||(a.class=[]),typeof n.properties.class=="string"&&(n.properties.class=n.properties.class.split(/\s+/g)),Array.isArray(n.properties.class)||(n.properties.class=[]);const t=Array.isArray(e)?e:e.split(/\s+/g);for(const s of t)s&&!n.properties.class.includes(s)&&n.properties.class.push(s);return n}function on(n,e=!1){var i;if(n.length===0)return[["",0]];const t=n.split(/(\r?\n)/g);let a=0;const s=[];for(let r=0;rs);function t(s){if(s===n.length)return{line:e.length-1,character:e[e.length-1].length};let i=s,r=0;for(const c of e){if(it&&a.push({...n,content:n.content.slice(t,s),offset:n.offset+t}),t=s;return ta-s);return t.length?n.map(a=>a.flatMap(s=>{const i=t.filter(r=>s.offsetr-s.offset).sort((r,c)=>r-c);return i.length?wr(s,i):s})):n}function jr(n,e,t,a,s="css-vars"){const i={content:n.content,explanation:n.explanation,offset:n.offset},r=e.map(u=>cn(n.variants[u])),c=new Set(r.flatMap(u=>Object.keys(u))),o={},l=(u,m)=>{const p=m==="color"?"":m==="background-color"?"-bg":`-${m}`;return t+e[u]+(m==="color"?"":p)};return r.forEach((u,m)=>{for(const p of c){const g=u[p]||"inherit";if(m===0&&a&&xr.includes(p))if(a===Mn&&r.length>1){const d=e.findIndex(x=>x==="light"),f=e.findIndex(x=>x==="dark");if(d===-1||f===-1)throw new k('When using `defaultColor: "light-dark()"`, you must provide both `light` and `dark` themes');const $=r[d][p]||"inherit",_=r[f][p]||"inherit";o[p]=`light-dark(${$}, ${_})`,s==="css-vars"&&(o[l(m,p)]=g)}else o[p]=g;else s==="css-vars"&&(o[l(m,p)]=g)}}),i.htmlStyle=o,i}function cn(n){const e={};if(n.color&&(e.color=n.color),n.bgColor&&(e["background-color"]=n.bgColor),n.fontStyle){n.fontStyle&z.Italic&&(e["font-style"]="italic"),n.fontStyle&z.Bold&&(e["font-weight"]="bold");const t=[];n.fontStyle&z.Underline&&t.push("underline"),n.fontStyle&z.Strikethrough&&t.push("line-through"),t.length&&(e["text-decoration"]=t.join(" "))}return e}function Dn(n){return typeof n=="string"?n:Object.entries(n).map(([e,t])=>`${e}:${t}`).join(";")}const ra=new WeakMap;function ln(n,e){ra.set(n,e)}function De(n){return ra.get(n)}class Ce{constructor(...e){b(this,"_stacks",{});b(this,"lang");if(e.length===2){const[t,a]=e;this.lang=a,this._stacks=t}else{const[t,a,s]=e;this.lang=a,this._stacks={[s]:t}}}get themes(){return Object.keys(this._stacks)}get theme(){return this.themes[0]}get _stack(){return this._stacks[this.theme]}static initial(e,t){return new Ce(Object.fromEntries(fr(t).map(a=>[a,qn])),e)}getInternalStack(e=this.theme){return this._stacks[e]}getScopes(e=this.theme){return vr(this._stacks[e])}toJSON(){return{lang:this.lang,theme:this.theme,themes:this.themes,scopes:this.getScopes()}}}function vr(n){const e=[],t=new Set;function a(s){var r;if(t.has(s))return;t.add(s);const i=(r=s==null?void 0:s.nameScopesList)==null?void 0:r.scopeName;i&&e.push(i),s.parent&&a(s.parent)}return a(n),e}function Cr(n,e){if(!(n instanceof Ce))throw new k("Invalid grammar state");return n.getInternalStack(e)}function Sr(){const n=new WeakMap;function e(t){if(!n.has(t.meta)){let a=function(r){if(typeof r=="number"){if(r<0||r>t.source.length)throw new k(`Invalid decoration offset: ${r}. Code length: ${t.source.length}`);return{...s.indexToPos(r),offset:r}}else{const c=s.lines[r.line];if(c===void 0)throw new k(`Invalid decoration position ${JSON.stringify(r)}. Lines length: ${s.lines.length}`);let o=r.character;if(o<0&&(o=c.length+o),o<0||o>c.length)throw new k(`Invalid decoration position ${JSON.stringify(r)}. Line ${r.line} length: ${c.length}`);return{...r,character:o,offset:s.posToIndex(r.line,o)}}};const s=$r(t.source),i=(t.options.decorations||[]).map(r=>({...r,start:a(r.start),end:a(r.end)}));Er(i),n.set(t.meta,{decorations:i,converter:s,source:t.source})}return n.get(t.meta)}return{name:"shiki:decorations",tokens(t){var r;if(!((r=this.options.decorations)!=null&&r.length))return;const s=e(this).decorations.flatMap(c=>[c.start.offset,c.end.offset]);return kr(t,s)},code(t){var u;if(!((u=this.options.decorations)!=null&&u.length))return;const a=e(this),s=Array.from(t.children).filter(m=>m.type==="element"&&m.tagName==="span");if(s.length!==a.converter.lines.length)throw new k(`Number of lines in code element (${s.length}) does not match the number of lines in the source (${a.converter.lines.length}). Failed to apply decorations.`);function i(m,p,g,d){const f=s[m];let $="",_=-1,x=-1;if(p===0&&(_=0),g===0&&(x=0),g===Number.POSITIVE_INFINITY&&(x=f.children.length),_===-1||x===-1)for(let w=0;w_);return m.tagName=p.tagName||"span",m.properties={...m.properties,...d,class:m.properties.class},($=p.properties)!=null&&$.class&&ia(m,p.properties.class),m=f(m,g)||m,m}const o=[],l=a.decorations.sort((m,p)=>p.start.offset-m.start.offset||m.end.offset-p.end.offset);for(const m of l){const{start:p,end:g}=m;if(p.line===g.line)i(p.line,p.character,g.character,m);else if(p.liner(d,m));i(g.line,0,g.character,m)}}o.forEach(m=>m())}}}function Er(n){for(let e=0;et.end.offset)throw new k(`Invalid decoration range: ${JSON.stringify(t.start)} - ${JSON.stringify(t.end)}`);for(let a=e+1;aNumber.parseInt(a));return t.length!==3||t.some(a=>Number.isNaN(a))?void 0:{type:"rgb",rgb:t}}else if(e==="5"){const t=n.shift();if(t)return{type:"table",index:Number(t)}}}function Fr(n){const e=[];for(;n.length>0;){const t=n.shift();if(!t)continue;const a=Number.parseInt(t);if(!Number.isNaN(a))if(a===0)e.push({type:"resetAll"});else if(a<=9)Un[a]&&e.push({type:"setDecoration",value:Un[a]});else if(a<=29){const s=Un[a-20];s&&(e.push({type:"resetDecoration",value:s}),s==="dim"&&e.push({type:"resetDecoration",value:"bold"}))}else if(a<=37)e.push({type:"setForegroundColor",value:{type:"named",name:ge[a-30]}});else if(a===38){const s=ca(n);s&&e.push({type:"setForegroundColor",value:s})}else if(a===39)e.push({type:"resetForegroundColor"});else if(a<=47)e.push({type:"setBackgroundColor",value:{type:"named",name:ge[a-40]}});else if(a===48){const s=ca(n);s&&e.push({type:"setBackgroundColor",value:s})}else a===49?e.push({type:"resetBackgroundColor"}):a===53?e.push({type:"setDecoration",value:"overline"}):a===55?e.push({type:"resetDecoration",value:"overline"}):a>=90&&a<=97?e.push({type:"setForegroundColor",value:{type:"named",name:ge[a-90+8]}}):a>=100&&a<=107&&e.push({type:"setBackgroundColor",value:{type:"named",name:ge[a-100+8]}})}return e}function Br(){let n=null,e=null,t=new Set;return{parse(a){const s=[];let i=0;do{const r=Nr(a,i),c=r.sequence?a.substring(i,r.startPosition):a.substring(i);if(c.length>0&&s.push({value:c,foreground:n,background:e,decorations:new Set(t)}),r.sequence){const o=Fr(r.sequence);for(const l of o)l.type==="resetAll"?(n=null,e=null,t.clear()):l.type==="resetForegroundColor"?n=null:l.type==="resetBackgroundColor"?e=null:l.type==="resetDecoration"&&t.delete(l.value);for(const l of o)l.type==="setForegroundColor"?n=l.value:l.type==="setBackgroundColor"?e=l.value:l.type==="setDecoration"&&t.add(l.value)}i=r.position}while(iMath.max(0,Math.min(o,255)).toString(16).padStart(2,"0")).join("")}`}let a;function s(){if(a)return a;a=[];for(let l=0;l{var m;const l=`terminal.ansi${o[0].toUpperCase()}${o.substring(1)}`,u=(m=n.colors)==null?void 0:m[l];return[o,u||Rr[o]]})),r=Ir(i),c=Br();return s.map(o=>c.parse(o[0]).map(l=>{let u,m;l.decorations.has("reverse")?(u=l.background?r.value(l.background):n.bg,m=l.foreground?r.value(l.foreground):n.fg):(u=l.foreground?r.value(l.foreground):n.fg,m=l.background?r.value(l.background):void 0),u=re(u,a),m=re(m,a),l.decorations.has("dim")&&(u=zr(u));let p=z.None;return l.decorations.has("bold")&&(p|=z.Bold),l.decorations.has("italic")&&(p|=z.Italic),l.decorations.has("underline")&&(p|=z.Underline),l.decorations.has("strikethrough")&&(p|=z.Strikethrough),{content:l.value,offset:o[1],color:u,bgColor:m,fontStyle:p}}))}function zr(n){const e=n.match(/#([0-9a-f]{3,8})/i);if(e){const a=e[1];if(a.length===8){const s=Math.round(Number.parseInt(a.slice(6,8),16)/2).toString(16).padStart(2,"0");return`#${a.slice(0,6)}${s}`}else{if(a.length===6)return`#${a}80`;if(a.length===4){const s=a[0],i=a[1],r=a[2],c=a[3],o=Math.round(Number.parseInt(`${c}${c}`,16)/2).toString(16).padStart(2,"0");return`#${s}${s}${i}${i}${r}${r}${o}`}else if(a.length===3){const s=a[0],i=a[1],r=a[2];return`#${s}${s}${i}${i}${r}${r}80`}}}const t=n.match(/var\((--[\w-]+-ansi-[\w-]+)\)/);return t?`var(${t[1]}-dim)`:n}function Zn(n,e,t={}){const{theme:a=n.getLoadedThemes()[0]}=t,s=n.resolveLangAlias(t.lang||"text");if(Tn(s)||On(a))return on(e).map(o=>[{content:o[0],offset:o[1]}]);const{theme:i,colorMap:r}=n.setTheme(a);if(s==="ansi")return Pr(i,e,t);const c=n.getLanguage(t.lang||"text");if(t.grammarState){if(t.grammarState.lang!==c.name)throw new k(`Grammar state language "${t.grammarState.lang}" does not match highlight language "${c.name}"`);if(!t.grammarState.themes.includes(i.name))throw new k(`Grammar state themes "${t.grammarState.themes}" do not contain highlight theme "${i.name}"`)}return Tr(e,c,i,r,t)}function Lr(...n){if(n.length===2)return De(n[1]);const[e,t,a={}]=n,{lang:s="text",theme:i=e.getLoadedThemes()[0]}=a;if(Tn(s)||On(i))throw new k("Plain language does not have grammar state");if(s==="ansi")throw new k("ANSI language does not have grammar state");const{theme:r,colorMap:c}=e.setTheme(i),o=e.getLanguage(s);return new Ce(Hn(t,o,r,c,a).stateStack,o.name,r.name)}function Tr(n,e,t,a,s){const i=Hn(n,e,t,a,s),r=new Ce(i.stateStack,e.name,t.name);return ln(i.tokens,r),i.tokens}function Hn(n,e,t,a,s){var p;const i=rn(t,s),{tokenizeMaxLineLength:r=0,tokenizeTimeLimit:c=500}=s,o=on(n);let l=s.grammarState?(p=Cr(s.grammarState,t.name))!=null?p:qn:s.grammarContextCode!=null?Hn(s.grammarContextCode,e,t,a,{...s,grammarState:void 0,grammarContextCode:void 0}).stateStack:qn,u=[];const m=[];for(let g=0,d=o.length;g0&&f.length>=r){u=[],m.push([{content:f,offset:$,color:"",fontStyle:0}]);continue}let _,x,y;s.includeExplanation&&(_=e.tokenizeLine(f,l,c),x=_.tokens,y=0);const w=e.tokenizeLine2(f,l,c),C=w.tokens.length/2;for(let E=0;Emt.trim());break;case"object":Ie=ie.scope;break;default:continue}pe.push({settings:ie,selectors:Ie.map(mt=>mt.split(/ /))})}se.explanation=[];let ps=0;for(;S+ps({scopeName:e}))}function Mr(n,e){const t=[];for(let a=0,s=e.length;a=0&&s>=0;)la(n[a],t[s])&&(a-=1),s-=1;return a===-1}function Ur(n,e,t){const a=[];for(const{selectors:s,settings:i}of n)for(const r of s)if(Dr(r,e,t)){a.push(i);break}return a}function ua(n,e,t){const a=Object.entries(t.themes).filter(o=>o[1]).map(o=>({color:o[0],theme:o[1]})),s=a.map(o=>{const l=Zn(n,e,{...t,theme:o.theme}),u=De(l),m=typeof o.theme=="string"?o.theme:o.theme.name;return{tokens:l,state:u,theme:m}}),i=Zr(...s.map(o=>o.tokens)),r=i[0].map((o,l)=>o.map((u,m)=>{const p={content:u.content,variants:{},offset:u.offset};return"includeExplanation"in t&&t.includeExplanation&&(p.explanation=u.explanation),i.forEach((g,d)=>{const{content:f,explanation:$,offset:_,...x}=g[l][m];p.variants[a[d].color]=x}),p})),c=s[0].state?new Ce(Object.fromEntries(s.map(o=>{var l;return[o.theme,(l=o.state)==null?void 0:l.getInternalStack(o.theme)]})),s[0].state.lang):void 0;return c&&ln(r,c),r}function Zr(...n){const e=n.map(()=>[]),t=n.length;for(let a=0;ao[a]),i=e.map(()=>[]);e.forEach((o,l)=>o.push(i[l]));const r=s.map(()=>0),c=s.map(o=>o[0]);for(;c.every(o=>o);){const o=Math.min(...c.map(l=>l.content.length));for(let l=0;l_[1]).map(_=>({color:_[0],theme:_[1]})).sort((_,x)=>_.color===l?-1:x.color===l?1:0);if(p.length===0)throw new k("`themes` option must not be empty");const g=ua(n,e,t);if(o=De(g),l&&Mn!==l&&!p.find(_=>_.color===l))throw new k(`\`themes\` option must contain the defaultColor key \`${l}\``);const d=p.map(_=>n.getTheme(_.theme)),f=p.map(_=>_.color);i=g.map(_=>_.map(x=>jr(x,f,u,l,m))),o&&ln(i,o);const $=p.map(_=>rn(_.theme,t));s=ma(p,d,$,u,l,"fg",m),a=ma(p,d,$,u,l,"bg",m),r=`shiki-themes ${d.map(_=>_.name).join(" ")}`,c=l?void 0:[s,a].join(";")}else if("theme"in t){const l=rn(t.theme,t);i=Zn(n,e,t);const u=n.getTheme(t.theme);a=re(u.bg,l),s=re(u.fg,l),r=u.name,o=De(i)}else throw new k("Invalid options, either `theme` or `themes` must be provided");return{tokens:i,fg:s,bg:a,themeName:r,rootStyle:c,grammarState:o}}function ma(n,e,t,a,s,i,r){return n.map((c,o)=>{const l=re(e[o][i],t[o])||"inherit",u=`${a+c.color}${i==="bg"?"-bg":""}:${l}`;if(o===0&&s){if(s===Mn&&n.length>1){const m=n.findIndex(f=>f.color==="light"),p=n.findIndex(f=>f.color==="dark");if(m===-1||p===-1)throw new k('When using `defaultColor: "light-dark()"`, you must provide both `light` and `dark` themes');const g=re(e[m][i],t[m])||"inherit",d=re(e[p][i],t[p])||"inherit";return`light-dark(${g}, ${d});${u}`}return l}return r==="css-vars"?u:null}).filter(c=>!!c).join(";")}function pn(n,e,t,a={meta:{},options:t,codeToHast:(s,i)=>pn(n,s,i),codeToTokens:(s,i)=>mn(n,s,i)}){var d,f,$;let s=e;for(const _ of un(t))s=((d=_.preprocess)==null?void 0:d.call(a,s,t))||s;let{tokens:i,fg:r,bg:c,themeName:o,rootStyle:l,grammarState:u}=mn(n,s,t);const{mergeWhitespaces:m=!0,mergeSameStyleTokens:p=!1}=t;m===!0?i=Wr(i):m==="never"&&(i=Vr(i)),p&&(i=Xr(i));const g={...a,get source(){return s}};for(const _ of un(t))i=((f=_.tokens)==null?void 0:f.call(g,i))||i;return Hr(i,{...t,fg:r,bg:c,themeName:o,rootStyle:t.rootStyle===!1?!1:($=t.rootStyle)!=null?$:l},g,u)}function Hr(n,e,t,a=De(n)){var f,$,_,x;const s=un(e),i=[],r={type:"root",children:[]},{structure:c="classic",tabindex:o="0"}=e,l={class:`shiki ${e.themeName||""}`};e.rootStyle!==!1&&(e.rootStyle!=null?l.style=e.rootStyle:l.style=`background-color:${e.bg};color:${e.fg}`),o!==!1&&o!=null&&(l.tabindex=o.toString());for(const[y,w]of Object.entries(e.meta||{}))y.startsWith("_")||(l[y]=w);let u={type:"element",tagName:"pre",properties:l,children:[],data:e.data},m={type:"element",tagName:"code",properties:{},children:i};const p=[],g={...t,structure:c,addClassToHast:ia,get source(){return t.source},get tokens(){return n},get options(){return e},get root(){return r},get pre(){return u},get code(){return m},get lines(){return p}};if(n.forEach((y,w)=>{var S,D;w&&(c==="inline"?r.children.push({type:"element",tagName:"br",properties:{},children:[]}):c==="classic"&&i.push({type:"text",value:` -`}));let C={type:"element",tagName:"span",properties:{class:"line"},children:[]},E=0;for(const G of y){let T={type:"element",tagName:"span",properties:{...G.htmlAttrs},children:[{type:"text",value:G.content}]};const Ge=Dn(G.htmlStyle||cn(G));Ge&&(T.properties.style=Ge);for(const se of s)T=((S=se==null?void 0:se.span)==null?void 0:S.call(g,T,w+1,E,C,G))||T;c==="inline"?r.children.push(T):c==="classic"&&C.children.push(T),E+=G.content.length}if(c==="classic"){for(const G of s)C=((D=G==null?void 0:G.line)==null?void 0:D.call(g,C,w+1))||C;p.push(C),i.push(C)}else c==="inline"&&p.push(C)}),c==="classic"){for(const y of s)m=((f=y==null?void 0:y.code)==null?void 0:f.call(g,m))||m;u.children.push(m);for(const y of s)u=(($=y==null?void 0:y.pre)==null?void 0:$.call(g,u))||u;r.children.push(u)}else if(c==="inline"){const y=[];let w={type:"element",tagName:"span",properties:{class:"line"},children:[]};for(const S of r.children)S.type==="element"&&S.tagName==="br"?(y.push(w),w={type:"element",tagName:"span",properties:{class:"line"},children:[]}):(S.type==="element"||S.type==="text")&&w.children.push(S);y.push(w);let E={type:"element",tagName:"code",properties:{},children:y};for(const S of s)E=((_=S==null?void 0:S.code)==null?void 0:_.call(g,E))||E;r.children=[];for(let S=0;S0&&r.children.push({type:"element",tagName:"br",properties:{},children:[]});const D=E.children[S];D.type==="element"&&r.children.push(...D.children)}}let d=r;for(const y of s)d=((x=y==null?void 0:y.root)==null?void 0:x.call(g,d))||d;return a&&ln(d,a),d}function Wr(n){return n.map(e=>{const t=[];let a="",s;return e.forEach((i,r)=>{const o=!(i.fontStyle&&(i.fontStyle&z.Underline||i.fontStyle&z.Strikethrough));o&&i.content.match(/^\s+$/)&&e[r+1]?(s===void 0&&(s=i.offset),a+=i.content):a?(o?t.push({...i,offset:s,content:a+i.content}):t.push({content:a,offset:s},i),s=void 0,a=""):t.push(i)}),t})}function Vr(n){return n.map(e=>e.flatMap(t=>{if(t.content.match(/^\s+$/))return t;const a=t.content.match(/^(\s*)(.*?)(\s*)$/);if(!a)return t;const[,s,i,r]=a;if(!s&&!r)return t;const c=[{...t,offset:t.offset+s.length,content:i}];return s&&c.unshift({content:s,offset:t.offset}),r&&c.push({content:r,offset:t.offset+s.length+i.length}),c}))}function Xr(n){return n.map(e=>{const t=[];for(const a of e){if(t.length===0){t.push({...a});continue}const s=t[t.length-1],i=Dn(s.htmlStyle||cn(s)),r=Dn(a.htmlStyle||cn(a)),c=s.fontStyle&&(s.fontStyle&z.Underline||s.fontStyle&z.Strikethrough),o=a.fontStyle&&(a.fontStyle&z.Underline||a.fontStyle&z.Strikethrough);!c&&!o&&i===r?s.content+=a.content:t.push({...a})}return t})}const Yr=gr;function Jr(n,e,t){var i;const a={meta:{},options:t,codeToHast:(r,c)=>pn(n,r,c),codeToTokens:(r,c)=>mn(n,r,c)};let s=Yr(pn(n,e,t,a));for(const r of un(t))s=((i=r.postprocess)==null?void 0:i.call(a,s,t))||s;return s}const pa={light:"#333333",dark:"#bbbbbb"},da={light:"#fffffe",dark:"#1e1e1e"},ga="__shiki_resolved";function Wn(n){var c,o,l,u,m;if(n!=null&&n[ga])return n;const e={...n};e.tokenColors&&!e.settings&&(e.settings=e.tokenColors,delete e.tokenColors),e.type||(e.type="dark"),e.colorReplacements={...e.colorReplacements},e.settings||(e.settings=[]);let{bg:t,fg:a}=e;if(!t||!a){const p=e.settings?e.settings.find(g=>!g.name&&!g.scope):void 0;(c=p==null?void 0:p.settings)!=null&&c.foreground&&(a=p.settings.foreground),(o=p==null?void 0:p.settings)!=null&&o.background&&(t=p.settings.background),!a&&((l=e==null?void 0:e.colors)!=null&&l["editor.foreground"])&&(a=e.colors["editor.foreground"]),!t&&((u=e==null?void 0:e.colors)!=null&&u["editor.background"])&&(t=e.colors["editor.background"]),a||(a=e.type==="light"?pa.light:pa.dark),t||(t=e.type==="light"?da.light:da.dark),e.fg=a,e.bg=t}e.settings[0]&&e.settings[0].settings&&!e.settings[0].scope||e.settings.unshift({settings:{foreground:e.fg,background:e.bg}});let s=0;const i=new Map;function r(p){var d;if(i.has(p))return i.get(p);s+=1;const g=`#${s.toString(16).padStart(8,"0").toLowerCase()}`;return(d=e.colorReplacements)!=null&&d[`#${g}`]?r(p):(i.set(p,g),g)}e.settings=e.settings.map(p=>{var $,_;const g=(($=p.settings)==null?void 0:$.foreground)&&!p.settings.foreground.startsWith("#"),d=((_=p.settings)==null?void 0:_.background)&&!p.settings.background.startsWith("#");if(!g&&!d)return p;const f={...p,settings:{...p.settings}};if(g){const x=r(p.settings.foreground);e.colorReplacements[x]=p.settings.foreground,f.settings.foreground=x}if(d){const x=r(p.settings.background);e.colorReplacements[x]=p.settings.background,f.settings.background=x}return f});for(const p of Object.keys(e.colors||{}))if((p==="editor.foreground"||p==="editor.background"||p.startsWith("terminal.ansi"))&&!((m=e.colors[p])!=null&&m.startsWith("#"))){const g=r(e.colors[p]);e.colorReplacements[g]=e.colors[p],e.colors[p]=g}return Object.defineProperty(e,ga,{enumerable:!1,writable:!1,value:!0}),e}async function Qr(n){return Array.from(new Set((await Promise.all(n.filter(e=>!yr(e)).map(async e=>await sa(e).then(t=>Array.isArray(t)?t:[t])))).flat()))}async function Kr(n){return(await Promise.all(n.map(async t=>_r(t)?null:Wn(await sa(t))))).filter(t=>!!t)}class Se extends Error{constructor(e){super(e),this.name="ShikiError"}}function ba(n,e){if(!e)return n;if(e[n]){const t=new Set([n]);for(;e[n];){if(n=e[n],t.has(n))throw new Se(`Circular alias \`${Array.from(t).join(" -> ")} -> ${n}\``);t.add(n)}}return n}class eo extends ei{constructor(t,a,s,i={}){super(t);b(this,"_resolvedThemes",new Map);b(this,"_resolvedGrammars",new Map);b(this,"_langMap",new Map);b(this,"_langGraph",new Map);b(this,"_textmateThemeCache",new WeakMap);b(this,"_loadedThemesCache",null);b(this,"_loadedLanguagesCache",null);this._resolver=t,this._themes=a,this._langs=s,this._alias=i,this._themes.map(r=>this.loadTheme(r)),this.loadLanguages(this._langs)}getTheme(t){return typeof t=="string"?this._resolvedThemes.get(t):this.loadTheme(t)}loadTheme(t){const a=Wn(t);return a.name&&(this._resolvedThemes.set(a.name,a),this._loadedThemesCache=null),a}getLoadedThemes(){return this._loadedThemesCache||(this._loadedThemesCache=[...this._resolvedThemes.keys()]),this._loadedThemesCache}setTheme(t){let a=this._textmateThemeCache.get(t);a||(a=Je.createFromRawTheme(t),this._textmateThemeCache.set(t,a)),this._syncRegistry.setTheme(a)}getGrammar(t){return t=ba(t,this._alias),this._resolvedGrammars.get(t)}loadLanguage(t){var r,c,o,l;if(this.getGrammar(t.name))return;const a=new Set([...this._langMap.values()].filter(u=>{var m;return(m=u.embeddedLangsLazy)==null?void 0:m.includes(t.name)}));this._resolver.addLanguage(t);const s={balancedBracketSelectors:t.balancedBracketSelectors||["*"],unbalancedBracketSelectors:t.unbalancedBracketSelectors||[]};this._syncRegistry._rawGrammars.set(t.scopeName,t);const i=this.loadGrammarWithConfiguration(t.scopeName,1,s);if(i.name=t.name,this._resolvedGrammars.set(t.name,i),t.aliases&&t.aliases.forEach(u=>{this._alias[u]=t.name}),this._loadedLanguagesCache=null,a.size)for(const u of a)this._resolvedGrammars.delete(u.name),this._loadedLanguagesCache=null,(c=(r=this._syncRegistry)==null?void 0:r._injectionGrammars)==null||c.delete(u.scopeName),(l=(o=this._syncRegistry)==null?void 0:o._grammars)==null||l.delete(u.scopeName),this.loadLanguage(this._langMap.get(u.name))}dispose(){super.dispose(),this._resolvedThemes.clear(),this._resolvedGrammars.clear(),this._langMap.clear(),this._langGraph.clear(),this._loadedThemesCache=null}loadLanguages(t){for(const i of t)this.resolveEmbeddedLanguages(i);const a=Array.from(this._langGraph.entries()),s=a.filter(([i,r])=>!r);if(s.length){const i=a.filter(([r,c])=>{if(!c)return!1;const o=c.embeddedLanguages||c.embeddedLangs;return o==null?void 0:o.some(l=>s.map(([u])=>u).includes(l))}).filter(r=>!s.includes(r));throw new Se(`Missing languages ${s.map(([r])=>`\`${r}\``).join(", ")}, required by ${i.map(([r])=>`\`${r}\``).join(", ")}`)}for(const[i,r]of a)this._resolver.addLanguage(r);for(const[i,r]of a)this.loadLanguage(r)}getLoadedLanguages(){return this._loadedLanguagesCache||(this._loadedLanguagesCache=[...new Set([...this._resolvedGrammars.keys(),...Object.keys(this._alias)])]),this._loadedLanguagesCache}resolveEmbeddedLanguages(t){var s;this._langMap.set(t.name,t),this._langGraph.set(t.name,t);const a=(s=t.embeddedLanguages)!=null?s:t.embeddedLangs;if(a)for(const i of a)this._langGraph.set(i,this._langMap.get(i))}}class no{constructor(e,t){b(this,"_langs",new Map);b(this,"_scopeToLang",new Map);b(this,"_injections",new Map);b(this,"_onigLib");this._onigLib={createOnigScanner:a=>e.createScanner(a),createOnigString:a=>e.createString(a)},t.forEach(a=>this.addLanguage(a))}get onigLib(){return this._onigLib}getLangRegistration(e){return this._langs.get(e)}loadGrammar(e){return this._scopeToLang.get(e)}addLanguage(e){this._langs.set(e.name,e),e.aliases&&e.aliases.forEach(t=>{this._langs.set(t,e)}),this._scopeToLang.set(e.scopeName,e),e.injectTo&&e.injectTo.forEach(t=>{this._injections.get(t)||this._injections.set(t,[]),this._injections.get(t).push(e.scopeName)})}getInjections(e){const t=e.split(".");let a=[];for(let s=1;s<=t.length;s++){const i=t.slice(0,s).join(".");a=[...a,...this._injections.get(i)||[]]}return a}}let Ue=0;function to(n){Ue+=1,n.warnings!==!1&&Ue>=10&&Ue%10===0&&console.warn(`[Shiki] ${Ue} instances have been created. Shiki is supposed to be used as a singleton, consider refactoring your code to cache your highlighter instance; Or call \`highlighter.dispose()\` to release unused instances.`);let e=!1;if(!n.engine)throw new Se("`engine` option is required for synchronous mode");const t=(n.langs||[]).flat(1),a=(n.themes||[]).flat(1).map(Wn),s=new no(n.engine,t),i=new eo(s,a,t,n.langAlias);let r;function c(y){return ba(y,n.langAlias)}function o(y){_();const w=i.getGrammar(typeof y=="string"?y:y.name);if(!w)throw new Se(`Language \`${y}\` not found, you may need to load it first`);return w}function l(y){if(y==="none")return{bg:"",fg:"",name:"none",settings:[],type:"dark"};_();const w=i.getTheme(y);if(!w)throw new Se(`Theme \`${y}\` not found, you may need to load it first`);return w}function u(y){_();const w=l(y);r!==y&&(i.setTheme(w),r=y);const C=i.getColorMap();return{theme:w,colorMap:C}}function m(){return _(),i.getLoadedThemes()}function p(){return _(),i.getLoadedLanguages()}function g(...y){_(),i.loadLanguages(y.flat(1))}async function d(...y){return g(await Qr(y))}function f(...y){_();for(const w of y.flat(1))i.loadTheme(w)}async function $(...y){return _(),f(await Kr(y))}function _(){if(e)throw new Se("Shiki instance has been disposed")}function x(){e||(e=!0,i.dispose(),Ue-=1)}return{setTheme:u,getTheme:l,getLanguage:o,getLoadedThemes:m,getLoadedLanguages:p,resolveLangAlias:c,loadLanguage:d,loadLanguageSync:g,loadTheme:$,loadThemeSync:f,dispose:x,[Symbol.dispose]:x}}function ao(n){const e=to(n);return{getLastGrammarState:(...t)=>Lr(e,...t),codeToTokensBase:(t,a)=>Zn(e,t,a),codeToTokensWithThemes:(t,a)=>ua(e,t,a),codeToTokens:(t,a)=>mn(e,t,a),codeToHast:(t,a)=>pn(e,t,a),codeToHtml:(t,a)=>Jr(e,t,a),getBundledLanguages:()=>({}),getBundledThemes:()=>({}),...e,getInternalContext:()=>e}}function Ee(n){if([...n].length!==1)throw new Error(`Expected "${n}" to be a single code point`);return n.codePointAt(0)}function so(n,e,t){return n.has(e)||n.set(e,t),n.get(e)}const Vn=new Set(["alnum","alpha","ascii","blank","cntrl","digit","graph","lower","print","punct","space","upper","word","xdigit"]),P=String.raw;function qe(n,e){if(n==null)throw new Error(e!=null?e:"Value expected");return n}const ha=P`\[\^?`,fa=`c.? | C(?:-.?)?|${P`[pP]\{(?:\^?[-\x20_]*[A-Za-z][-\x20\w]*\})?`}|${P`x[89A-Fa-f]\p{AHex}(?:\\x[89A-Fa-f]\p{AHex})*`}|${P`u(?:\p{AHex}{4})? | x\{[^\}]*\}? | x\p{AHex}{0,2}`}|${P`o\{[^\}]*\}?`}|${P`\d{1,3}`}`,Xn=/[?*+][?+]?|\{(?:\d+(?:,\d*)?|,\d+)\}\??/,dn=new RegExp(P` - \\ (?: - ${fa} - | [gk]<[^>]*>? - | [gk]'[^']*'? - | . - ) - | \( (?: - \? (?: - [:=!>({] - | <[=!] - | <[^>]*> - | '[^']*' - | ~\|? - | #(?:[^)\\]|\\.?)* - | [^:)]*[:)] - )? - | \*[^\)]*\)? - )? - | (?:${Xn.source})+ - | ${ha} - | . -`.replace(/\s+/g,""),"gsu"),Yn=new RegExp(P` - \\ (?: - ${fa} - | . - ) - | \[:(?:\^?\p{Alpha}+|\^):\] - | ${ha} - | && - | . -`.replace(/\s+/g,""),"gsu");function io(n,e={}){const t={flags:"",...e,rules:{captureGroup:!1,singleline:!1,...e.rules}};if(typeof n!="string")throw new Error("String expected as pattern");const a=vo(t.flags),s=[a.extended],i={captureGroup:t.rules.captureGroup,getCurrentModX(){return s.at(-1)},numOpenGroups:0,popModX(){s.pop()},pushModX(m){s.push(m)},replaceCurrentModX(m){s[s.length-1]=m},singleline:t.rules.singleline};let r=[],c;for(dn.lastIndex=0;c=dn.exec(n);){const m=ro(i,n,c[0],dn.lastIndex);m.tokens?r.push(...m.tokens):m.token&&r.push(m.token),m.lastIndex!==void 0&&(dn.lastIndex=m.lastIndex)}const o=[];let l=0;r.filter(m=>m.type==="GroupOpen").forEach(m=>{m.kind==="capturing"?m.number=++l:m.raw==="("&&o.push(m)}),l||o.forEach((m,p)=>{m.kind="capturing",m.number=p+1});const u=l||o.length;return{tokens:r.map(m=>m.type==="EscapedNumber"?So(m,u):m).flat(),flags:a}}function ro(n,e,t,a){const[s,i]=t;if(t==="["||t==="[^"){const r=oo(e,t,a);return{tokens:r.tokens,lastIndex:r.lastIndex}}if(s==="\\"){if("AbBGyYzZ".includes(i))return{token:_a(t,t)};if(/^\\g[<']/.test(t)){if(!/^\\g(?:<[^>]+>|'[^']+')$/.test(t))throw new Error(`Invalid group name "${t}"`);return{token:yo(t)}}if(/^\\k[<']/.test(t)){if(!/^\\k(?:<[^>]+>|'[^']+')$/.test(t))throw new Error(`Invalid group name "${t}"`);return{token:$a(t)}}if(i==="K")return{token:wa("keep",t)};if(i==="N"||i==="R")return{token:be("newline",t,{negate:i==="N"})};if(i==="O")return{token:be("any",t)};if(i==="X")return{token:be("text_segment",t)};const r=ya(t,{inCharClass:!1});return Array.isArray(r)?{tokens:r}:{token:r}}if(s==="("){if(i==="*")return{token:wo(t)};if(t==="(?{")throw new Error(`Unsupported callout "${t}"`);if(t.startsWith("(?#")){if(e[a]!==")")throw new Error('Unclosed comment group "(?#"');return{lastIndex:a+1}}if(/^\(\?[-imx]+[:)]$/.test(t))return{token:xo(t,n)};if(n.pushModX(n.getCurrentModX()),n.numOpenGroups++,t==="("&&!n.captureGroup||t==="(?:")return{token:Ae("group",t)};if(t==="(?>")return{token:Ae("atomic",t)};if(t==="(?="||t==="(?!"||t==="(?<="||t==="(?")||t.startsWith("(?'")&&t.endsWith("'"))return{token:Ae("capturing",t,{...t!=="("&&{name:t.slice(3,-1)}})};if(t.startsWith("(?~")){if(t==="(?~|")throw new Error(`Unsupported absence function kind "${t}"`);return{token:Ae("absence_repeater",t)}}throw t==="(?("?new Error(`Unsupported conditional "${t}"`):new Error(`Invalid or unsupported group option "${t}"`)}if(t===")"){if(n.popModX(),n.numOpenGroups--,n.numOpenGroups<0)throw new Error('Unmatched ")"');return{token:bo(t)}}if(n.getCurrentModX()){if(t==="#"){const r=e.indexOf(` -`,a);return{lastIndex:r===-1?e.length:r}}if(/^\s$/.test(t)){const r=/\s+/y;return r.lastIndex=a,{lastIndex:r.exec(e)?r.lastIndex:a}}}if(t===".")return{token:be("dot",t)};if(t==="^"||t==="$"){const r=n.singleline?{"^":P`\A`,$:P`\Z`}[t]:t;return{token:_a(r,t)}}return t==="|"?{token:lo(t)}:Xn.test(t)?{tokens:Eo(t)}:{token:Q(Ee(t),t)}}function oo(n,e,t){const a=[xa(e[1]==="^",e)];let s=1,i;for(Yn.lastIndex=t;i=Yn.exec(n);){const r=i[0];if(r[0]==="["&&r[1]!==":")s++,a.push(xa(r[1]==="^",r));else if(r==="]"){if(a.at(-1).type==="CharacterClassOpen")a.push(Q(93,r));else if(s--,a.push(uo(r)),!s)break}else{const c=co(r);Array.isArray(c)?a.push(...c):a.push(c)}}return{tokens:a,lastIndex:Yn.lastIndex||n.length}}function co(n){if(n[0]==="\\")return ya(n,{inCharClass:!0});if(n[0]==="["){const e=/\[:(?\^?)(?[a-z]+):\]/.exec(n);if(!e||!Vn.has(e.groups.name))throw new Error(`Invalid POSIX class "${n}"`);return be("posix",n,{value:e.groups.name,negate:!!e.groups.negate})}return n==="-"?mo(n):n==="&&"?po(n):Q(Ee(n),n)}function ya(n,{inCharClass:e}){const t=n[1];if(t==="c"||t==="C")return $o(n);if("dDhHsSwW".includes(t))return ko(n);if(n.startsWith(P`\o{`))throw new Error(`Incomplete, invalid, or unsupported octal code point "${n}"`);if(/^\\[pP]\{/.test(n)){if(n.length===3)throw new Error(`Incomplete or invalid Unicode property "${n}"`);return jo(n)}if(/^\\x[89A-Fa-f]\p{AHex}/u.test(n))try{const a=n.split(/\\x/).slice(1).map(r=>parseInt(r,16)),s=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}).decode(new Uint8Array(a)),i=new TextEncoder;return[...s].map(r=>{const c=[...i.encode(r)].map(o=>`\\x${o.toString(16)}`).join("");return Q(Ee(r),c)})}catch(a){throw new Error(`Multibyte code "${n}" incomplete or invalid in Oniguruma`)}if(t==="u"||t==="x")return Q(Co(n),n);if(ka.has(t))return Q(ka.get(t),n);if(/\d/.test(t))return go(e,n);if(n==="\\")throw new Error(P`Incomplete escape "\"`);if(t==="M")throw new Error(`Unsupported meta "${n}"`);if([...n].length===2)return Q(n.codePointAt(1),n);throw new Error(`Unexpected escape "${n}"`)}function lo(n){return{type:"Alternator",raw:n}}function _a(n,e){return{type:"Assertion",kind:n,raw:e}}function $a(n){return{type:"Backreference",raw:n}}function Q(n,e){return{type:"Character",value:n,raw:e}}function uo(n){return{type:"CharacterClassClose",raw:n}}function mo(n){return{type:"CharacterClassHyphen",raw:n}}function po(n){return{type:"CharacterClassIntersector",raw:n}}function xa(n,e){return{type:"CharacterClassOpen",negate:n,raw:e}}function be(n,e,t={}){return{type:"CharacterSet",kind:n,...t,raw:e}}function wa(n,e,t={}){return n==="keep"?{type:"Directive",kind:n,raw:e}:{type:"Directive",kind:n,flags:qe(t.flags),raw:e}}function go(n,e){return{type:"EscapedNumber",inCharClass:n,raw:e}}function bo(n){return{type:"GroupClose",raw:n}}function Ae(n,e,t={}){return{type:"GroupOpen",kind:n,...t,raw:e}}function ho(n,e,t,a){return{type:"NamedCallout",kind:n,tag:e,arguments:t,raw:a}}function fo(n,e,t,a){return{type:"Quantifier",kind:n,min:e,max:t,raw:a}}function yo(n){return{type:"Subroutine",raw:n}}const _o=new Set(["COUNT","CMP","ERROR","FAIL","MAX","MISMATCH","SKIP","TOTAL_COUNT"]),ka=new Map([["a",7],["b",8],["e",27],["f",12],["n",10],["r",13],["t",9],["v",11]]);function $o(n){const e=n[1]==="c"?n[2]:n[3];if(!e||!/[A-Za-z]/.test(e))throw new Error(`Unsupported control character "${n}"`);return Q(Ee(e.toUpperCase())-64,n)}function xo(n,e){let{on:t,off:a}=/^\(\?(?[imx]*)(?:-(?[-imx]*))?/.exec(n).groups;a!=null||(a="");const s=(e.getCurrentModX()||t.includes("x"))&&!a.includes("x"),i=va(t),r=va(a),c={};if(i&&(c.enable=i),r&&(c.disable=r),n.endsWith(")"))return e.replaceCurrentModX(s),wa("flags",n,{flags:c});if(n.endsWith(":"))return e.pushModX(s),e.numOpenGroups++,Ae("group",n,{...(i||r)&&{flags:c}});throw new Error(`Unexpected flag modifier "${n}"`)}function wo(n){var u;const e=/\(\*(?[A-Za-z_]\w*)?(?:\[(?(?:[A-Za-z_]\w*)?)\])?(?:\{(?[^}]*)\})?\)/.exec(n);if(!e)throw new Error(`Incomplete or invalid named callout "${n}"`);const{name:t,tag:a,args:s}=e.groups;if(!t)throw new Error(`Invalid named callout "${n}"`);if(a==="")throw new Error(`Named callout tag with empty value not allowed "${n}"`);const i=s?s.split(",").filter(m=>m!=="").map(m=>/^[+-]?\d+$/.test(m)?+m:m):[],[r,c,o]=i,l=_o.has(t)?t.toLowerCase():"custom";switch(l){case"fail":case"mismatch":case"skip":if(i.length>0)throw new Error(`Named callout arguments not allowed "${i}"`);break;case"error":if(i.length>1)throw new Error(`Named callout allows only one argument "${i}"`);if(typeof r=="string")throw new Error(`Named callout argument must be a number "${r}"`);break;case"max":if(!i.length||i.length>2)throw new Error(`Named callout must have one or two arguments "${i}"`);if(typeof r=="string"&&!/^[A-Za-z_]\w*$/.test(r))throw new Error(`Named callout argument one must be a tag or number "${r}"`);if(i.length===2&&(typeof c=="number"||!/^[<>X]$/.test(c)))throw new Error(`Named callout optional argument two must be '<', '>', or 'X' "${c}"`);break;case"count":case"total_count":if(i.length>1)throw new Error(`Named callout allows only one argument "${i}"`);if(i.length===1&&(typeof r=="number"||!/^[<>X]$/.test(r)))throw new Error(`Named callout optional argument must be '<', '>', or 'X' "${r}"`);break;case"cmp":if(i.length!==3)throw new Error(`Named callout must have three arguments "${i}"`);if(typeof r=="string"&&!/^[A-Za-z_]\w*$/.test(r))throw new Error(`Named callout argument one must be a tag or number "${r}"`);if(typeof c=="number"||!/^(?:[<>!=]=|[<>])$/.test(c))throw new Error(`Named callout argument two must be '==', '!=', '>', '<', '>=', or '<=' "${c}"`);if(typeof o=="string"&&!/^[A-Za-z_]\w*$/.test(o))throw new Error(`Named callout argument three must be a tag or number "${o}"`);break;case"custom":throw new Error(`Undefined callout name "${t}"`);default:throw new Error(`Unexpected named callout kind "${l}"`)}return ho(l,a!=null?a:null,(u=s==null?void 0:s.split(","))!=null?u:null,n)}function ja(n){let e=null,t,a;if(n[0]==="{"){const{minStr:s,maxStr:i}=/^\{(?\d*)(?:,(?\d*))?/.exec(n).groups,r=1e5;if(+s>r||i&&+i>r)throw new Error("Quantifier value unsupported in Oniguruma");if(t=+s,a=i===void 0?+s:i===""?1/0:+i,t>a&&(e="possessive",[t,a]=[a,t]),n.endsWith("?")){if(e==="possessive")throw new Error('Unsupported possessive interval quantifier chain with "?"');e="lazy"}else e||(e="greedy")}else t=n[0]==="+"?1:0,a=n[0]==="?"?1:1/0,e=n[1]==="+"?"possessive":n[1]==="?"?"lazy":"greedy";return fo(e,t,a,n)}function ko(n){const e=n[1].toLowerCase();return be({d:"digit",h:"hex",s:"space",w:"word"}[e],n,{negate:n[1]!==e})}function jo(n){const{p:e,neg:t,value:a}=/^\\(?

[pP])\{(?\^?)(?[^}]+)/.exec(n).groups;return be("property",n,{value:a,negate:e==="P"&&!t||e==="p"&&!!t})}function va(n){const e={};return n.includes("i")&&(e.ignoreCase=!0),n.includes("m")&&(e.dotAll=!0),n.includes("x")&&(e.extended=!0),Object.keys(e).length?e:null}function vo(n){const e={ignoreCase:!1,dotAll:!1,extended:!1,digitIsAscii:!1,posixIsAscii:!1,spaceIsAscii:!1,wordIsAscii:!1,textSegmentMode:null};for(let t=0;t\p{AHex}+)/u.exec(n).groups.hex:n.slice(2);return parseInt(e,16)}function So(n,e){const{raw:t,inCharClass:a}=n,s=t.slice(1);if(!a&&(s!=="0"&&s.length===1||s[0]!=="0"&&+s<=e))return[$a(t)];const i=[],r=s.match(/^[0-7]+|\d/g);for(let c=0;c127)throw new Error(P`Octal encoded byte above 177 unsupported "${t}"`)}else l=Ee(o);i.push(Q(l,(c===0?"\\":"")+o))}return i}function Eo(n){const e=[],t=new RegExp(Xn,"gy");let a;for(;a=t.exec(n);){const s=a[0];if(s[0]==="{"){const i=/^\{(?\d+),(?\d+)\}\??$/.exec(s);if(i){const{min:r,max:c}=i.groups;if(+r>+c&&s.endsWith("?")){t.lastIndex--,e.push(ja(s.slice(0,-1)));continue}}}e.push(ja(s))}return e}function Ca(n,e){if(!Array.isArray(n.body))throw new Error("Expected node with body array");if(n.body.length!==1)return!1;const t=n.body[0];return!e||Object.keys(e).every(a=>e[a]===t[a])}function qo(n){return Ao.has(n.type)}const Ao=new Set(["AbsenceFunction","Backreference","CapturingGroup","Character","CharacterClass","CharacterSet","Group","Quantifier","Subroutine"]);function Sa(n,e={}){const t={flags:"",normalizeUnknownPropertyNames:!1,skipBackrefValidation:!1,skipLookbehindValidation:!1,skipPropertyNameValidation:!1,unicodePropertyMap:null,...e,rules:{captureGroup:!1,singleline:!1,...e.rules}},a=io(n,{flags:t.flags,rules:{captureGroup:t.rules.captureGroup,singleline:t.rules.singleline}}),s=(p,g)=>{const d=a.tokens[i.nextIndex];switch(i.parent=p,i.nextIndex++,d.type){case"Alternator":return he();case"Assertion":return No(d);case"Backreference":return Fo(d,i);case"Character":return gn(d.value,{useLastValid:!!g.isCheckingRangeEnd});case"CharacterClassHyphen":return Bo(d,i,g);case"CharacterClassOpen":return Go(d,i,g);case"CharacterSet":return Io(d,i);case"Directive":return Oo(d.kind,{flags:d.flags});case"GroupOpen":return Ro(d,i,g);case"NamedCallout":return Do(d.kind,d.tag,d.arguments);case"Quantifier":return Po(d,i);case"Subroutine":return zo(d,i);default:throw new Error(`Unexpected token type "${d.type}"`)}},i={capturingGroups:[],hasNumberedRef:!1,namedGroupsByName:new Map,nextIndex:0,normalizeUnknownPropertyNames:t.normalizeUnknownPropertyNames,parent:null,skipBackrefValidation:t.skipBackrefValidation,skipLookbehindValidation:t.skipLookbehindValidation,skipPropertyNameValidation:t.skipPropertyNameValidation,subroutines:[],tokens:a.tokens,unicodePropertyMap:t.unicodePropertyMap,walk:s},r=Zo(Mo(a.flags));let c=r.body[0];for(;i.nextIndexo.length)throw new Error("Subroutine uses a group number that's not defined");p&&(o[p-1].isSubroutined=!0)}else if(u.has(p)){if(u.get(p).length>1)throw new Error(P`Subroutine uses a duplicate group name "\g<${p}>"`);u.get(p)[0].isSubroutined=!0}else throw new Error(P`Subroutine uses a group name that's not defined "\g<${p}>"`);return r}function No({kind:n}){return Jn(qe({"^":"line_start",$:"line_end","\\A":"string_start","\\b":"word_boundary","\\B":"word_boundary","\\G":"search_start","\\y":"text_segment_boundary","\\Y":"text_segment_boundary","\\z":"string_end","\\Z":"string_end_newline"}[n],`Unexpected assertion kind "${n}"`),{negate:n===P`\B`||n===P`\Y`})}function Fo({raw:n},e){const t=/^\\k[<']/.test(n),a=t?n.slice(3,-1):n.slice(1),s=(i,r=!1)=>{const c=e.capturingGroups.length;let o=!1;if(i>c)if(e.skipBackrefValidation)o=!0;else throw new Error(`Not enough capturing groups defined to the left "${n}"`);return e.hasNumberedRef=!0,Qn(r?c+1-i:i,{orphan:o})};if(t){const i=/^(?-?)0*(?[1-9]\d*)$/.exec(a);if(i)return s(+i.groups.num,!!i.groups.sign);if(/[-+]/.test(a))throw new Error(`Invalid backref name "${n}"`);if(!e.namedGroupsByName.has(a))throw new Error(`Group name not defined to the left "${n}"`);return Qn(a)}return s(+a)}function Bo(n,e,t){const{tokens:a,walk:s}=e,i=e.parent,r=i.body.at(-1),c=a[e.nextIndex];if(!t.isCheckingRangeEnd&&r&&r.type!=="CharacterClass"&&r.type!=="CharacterClassRange"&&c&&c.type!=="CharacterClassOpen"&&c.type!=="CharacterClassClose"&&c.type!=="CharacterClassIntersector"){const o=s(i,{...t,isCheckingRangeEnd:!0});if(r.type==="Character"&&o.type==="Character")return i.body.pop(),To(r,o);throw new Error("Invalid character class range")}return gn(Ee("-"))}function Go({negate:n},e,t){const{tokens:a,walk:s}=e,i=a[e.nextIndex],r=[bn()];let c=Ga(i);for(;c.type!=="CharacterClassClose";){if(c.type==="CharacterClassIntersector")r.push(bn()),e.nextIndex++;else{const l=r.at(-1);l.body.push(s(l,t))}c=Ga(a[e.nextIndex],i)}const o=bn({negate:n});return r.length===1?o.body=r[0].body:(o.kind="intersection",o.body=r.map(l=>l.body.length===1?l.body[0]:l)),e.nextIndex++,o}function Io({kind:n,negate:e,value:t},a){const{normalizeUnknownPropertyNames:s,skipPropertyNameValidation:i,unicodePropertyMap:r}=a;if(n==="property"){const c=hn(t);if(Vn.has(c)&&!(r!=null&&r.has(c)))n="posix",t=c;else return Ne(t,{negate:e,normalizeUnknownPropertyNames:s,skipPropertyNameValidation:i,unicodePropertyMap:r})}return n==="posix"?Uo(t,{negate:e}):Kn(n,{negate:e})}function Ro(n,e,t){const{tokens:a,capturingGroups:s,namedGroupsByName:i,skipLookbehindValidation:r,walk:c}=e,o=Ho(n),l=o.type==="AbsenceFunction",u=Ba(o),m=u&&o.negate;if(o.type==="CapturingGroup"&&(s.push(o),o.name&&so(i,o.name,[]).push(o)),l&&t.isInAbsenceFunction)throw new Error("Nested absence function not supported by Oniguruma");let p=Ia(a[e.nextIndex]);for(;p.type!=="GroupClose";){if(p.type==="Alternator")o.body.push(he()),e.nextIndex++;else{const g=o.body.at(-1),d=c(g,{...t,isInAbsenceFunction:t.isInAbsenceFunction||l,isInLookbehind:t.isInLookbehind||u,isInNegLookbehind:t.isInNegLookbehind||m});if(g.body.push(d),(u||t.isInLookbehind)&&!r){const f="Lookbehind includes a pattern not allowed by Oniguruma";if(m||t.isInNegLookbehind){if(Fa(d)||d.type==="CapturingGroup")throw new Error(f)}else if(Fa(d)||Ba(d)&&d.negate)throw new Error(f)}}p=Ia(a[e.nextIndex])}return e.nextIndex++,o}function Po({kind:n,min:e,max:t},a){const s=a.parent,i=s.body.at(-1);if(!i||!qo(i))throw new Error("Quantifier requires a repeatable token");const r=qa(n,e,t,i);return s.body.pop(),r}function zo({raw:n},e){const{capturingGroups:t,subroutines:a}=e;let s=n.slice(3,-1);const i=/^(?[-+]?)0*(?[1-9]\d*)$/.exec(s);if(i){const c=+i.groups.num,o=t.length;if(e.hasNumberedRef=!0,s={"":c,"+":o+c,"-":o+1-c}[i.groups.sign],s<1)throw new Error("Invalid subroutine number")}else s==="0"&&(s=0);const r=Aa(s);return a.push(r),r}function Lo(n,e){return{type:"AbsenceFunction",kind:n,body:Ze(e==null?void 0:e.body)}}function he(n){return{type:"Alternative",body:Na(n==null?void 0:n.body)}}function Jn(n,e){const t={type:"Assertion",kind:n};return(n==="word_boundary"||n==="text_segment_boundary")&&(t.negate=!!(e!=null&&e.negate)),t}function Qn(n,e){const t=!!(e!=null&&e.orphan);return{type:"Backreference",ref:n,...t&&{orphan:t}}}function Ea(n,e){const t={name:void 0,isSubroutined:!1,...e};if(t.name!==void 0&&!Wo(t.name))throw new Error(`Group name "${t.name}" invalid in Oniguruma`);return{type:"CapturingGroup",number:n,...t.name&&{name:t.name},...t.isSubroutined&&{isSubroutined:t.isSubroutined},body:Ze(e==null?void 0:e.body)}}function gn(n,e){const t={useLastValid:!1,...e};if(n>1114111){const a=n.toString(16);if(t.useLastValid)n=1114111;else throw n>1310719?new Error(`Invalid code point out of range "\\x{${a}}"`):new Error(`Invalid code point out of range in JS "\\x{${a}}"`)}return{type:"Character",value:n}}function bn(n){const e={kind:"union",negate:!1,...n};return{type:"CharacterClass",kind:e.kind,negate:e.negate,body:Na(n==null?void 0:n.body)}}function To(n,e){if(e.valuet)throw new Error("Invalid reversed quantifier range");return{type:"Quantifier",kind:n,min:e,max:t,body:a}}function Zo(n,e){return{type:"Regex",body:Ze(e==null?void 0:e.body),flags:n}}function Aa(n){return{type:"Subroutine",ref:n}}function Ne(n,e){var s;const t={negate:!1,normalizeUnknownPropertyNames:!1,skipPropertyNameValidation:!1,unicodePropertyMap:null,...e};let a=(s=t.unicodePropertyMap)==null?void 0:s.get(hn(n));if(!a){if(t.normalizeUnknownPropertyNames)a=Vo(n);else if(t.unicodePropertyMap&&!t.skipPropertyNameValidation)throw new Error(P`Invalid Unicode property "\p{${n}}"`)}return{type:"CharacterSet",kind:"property",value:a!=null?a:n,negate:t.negate}}function Ho({flags:n,kind:e,name:t,negate:a,number:s}){switch(e){case"absence_repeater":return Lo("repeater");case"atomic":return X({atomic:!0});case"capturing":return Ea(s,{name:t});case"group":return X({flags:n});case"lookahead":case"lookbehind":return fe({behind:e==="lookbehind",negate:a});default:throw new Error(`Unexpected group kind "${e}"`)}}function Ze(n){if(n===void 0)n=[he()];else if(!Array.isArray(n)||!n.length||!n.every(e=>e.type==="Alternative"))throw new Error("Invalid body; expected array of one or more Alternative nodes");return n}function Na(n){if(n===void 0)n=[];else if(!Array.isArray(n)||!n.every(e=>!!e.type))throw new Error("Invalid body; expected array of nodes");return n}function Fa(n){return n.type==="LookaroundAssertion"&&n.kind==="lookahead"}function Ba(n){return n.type==="LookaroundAssertion"&&n.kind==="lookbehind"}function Wo(n){return/^[\p{Alpha}\p{Pc}][^)]*$/u.test(n)}function Vo(n){return n.trim().replace(/[- _]+/g,"_").replace(/[A-Z][a-z]+(?=[A-Z])/g,"$&_").replace(/[A-Za-z]+/g,e=>e[0].toUpperCase()+e.slice(1).toLowerCase())}function hn(n){return n.replace(/[- _]+/g,"").toLowerCase()}function Ga(n,e){return qe(n,`${(e==null?void 0:e.type)==="Character"&&e.value===93?"Empty":"Unclosed"} character class`)}function Ia(n){return qe(n,"Unclosed group")}function He(n,e,t=null){function a(i,r){for(let c=0;cA-Za-z\-]|<[=!]|\(DEFINE\))`;function Yo(n,e){for(let t=0;t=e&&n[t]++}function Jo(n,e,t,a){return n.slice(0,e)+a+n.slice(e+t.length)}const Z=Object.freeze({DEFAULT:"DEFAULT",CHAR_CLASS:"CHAR_CLASS"});function et(n,e,t,a){const s=new RegExp(String.raw`${e}|(?<$skip>\[\^?|\\?.)`,"gsu"),i=[!1];let r=0,c="";for(const o of n.matchAll(s)){const{0:l,groups:{$skip:u}}=o;if(!u&&(!a||a===Z.DEFAULT==!r)){t instanceof Function?c+=t(o,{context:r?Z.CHAR_CLASS:Z.DEFAULT,negated:i[i.length-1]}):c+=t;continue}l[0]==="["?(r++,i.push(l[1]==="^")):l==="]"&&r&&(r--,i.pop()),c+=l}return c}function Ra(n,e,t,a){et(n,e,t,a)}function Qo(n,e,t=0,a){if(!new RegExp(e,"su").test(n))return null;const s=new RegExp(`${e}|(?<$skip>\\\\?.)`,"gsu");s.lastIndex=t;let i=0,r;for(;r=s.exec(n);){const{0:c,groups:{$skip:o}}=r;if(!o&&(!a||a===Z.DEFAULT==!i))return r;c==="["?i++:c==="]"&&i&&i--,s.lastIndex==r.index&&s.lastIndex++}return null}function yn(n,e,t){return!!Qo(n,e,0,t)}function Ko(n,e){const t=/\\?./gsu;t.lastIndex=e;let a=n.length,s=0,i=1,r;for(;r=t.exec(n);){const[c]=r;if(c==="[")s++;else if(s)c==="]"&&s--;else if(c==="(")i++;else if(c===")"&&(i--,!i)){a=r.index;break}}return n.slice(e,a)}const Pa=new RegExp(String.raw`(?${Xo})|(?\((?:\?<[^>]+>)?)|\\?.`,"gsu");function ec(n,e){var p,g;const t=(p=e==null?void 0:e.hiddenCaptures)!=null?p:[];let a=(g=e==null?void 0:e.captureTransfers)!=null?g:new Map;if(!/\(\?>/.test(n))return{pattern:n,captureTransfers:a,hiddenCaptures:t};const s="(?>",i="(?:(?=(",r=[0],c=[];let o=0,l=0,u=NaN,m;do{m=!1;let d=0,f=0,$=!1,_;for(Pa.lastIndex=Number.isNaN(u)?0:u+i.length;_=Pa.exec(n);){const{0:x,index:y,groups:{capturingStart:w,noncapturingStart:C}}=_;if(x==="[")d++;else if(d)x==="]"&&d--;else if(x===s&&!$)u=y,$=!0;else if($&&C)f++;else if(w)$?f++:(o++,r.push(o+l));else if(x===")"&&$){if(!f){l++;const E=o+l;if(n=`${n.slice(0,u)}${i}${n.slice(u+s.length,y)}))<$$${E}>)${n.slice(y+1)}`,m=!0,c.push(E),Yo(t,E),a.size){const S=new Map;a.forEach((D,G)=>{S.set(G>=E?G+1:G,D.map(T=>T>=E?T+1:T))}),a=S}break}f--}}}while(m);return t.push(...c),n=et(n,String.raw`\\(?[1-9]\d*)|<\$\$(?\d+)>`,({0:d,groups:{backrefNum:f,wrappedBackrefNum:$}})=>{if(f){const _=+f;if(_>r.length-1)throw new Error(`Backref "${d}" greater than number of captures`);return`\\${r[_]}`}return`\\${$}`},Z.DEFAULT),{pattern:n,captureTransfers:a,hiddenCaptures:t}}const za=String.raw`(?:[?*+]|\{\d+(?:,\d*)?\})`,nt=new RegExp(String.raw` -\\(?: \d+ - | c[A-Za-z] - | [gk]<[^>]+> - | [pPu]\{[^\}]+\} - | u[A-Fa-f\d]{4} - | x[A-Fa-f\d]{2} - ) -| \((?: \? (?: [:=!>] - | <(?:[=!]|[^>]+>) - | [A-Za-z\-]+: - | \(DEFINE\) - ))? -| (?${za})(?[?+]?)(?[?*+\{]?) -| \\?. -`.replace(/\s+/g,""),"gsu");function nc(n){if(!new RegExp(`${za}\\+`).test(n))return{pattern:n};const e=[];let t=null,a=null,s="",i=0,r;for(nt.lastIndex=0;r=nt.exec(n);){const{0:c,index:o,groups:{qBase:l,qMod:u,invalidQ:m}}=r;if(c==="[")i||(a=o),i++;else if(c==="]")i?i--:a=null;else if(!i)if(u==="+"&&s&&!s.startsWith("(")){if(m)throw new Error(`Invalid quantifier "${c}"`);let p=-1;if(/^\{\d+\}$/.test(l))n=Jo(n,o+l.length,u,"");else{if(s===")"||s==="]"){const g=s===")"?t:a;if(g===null)throw new Error(`Invalid unmatched "${s}"`);n=`${n.slice(0,g)}(?>${n.slice(g,o)}${l})${n.slice(o+c.length)}`}else n=`${n.slice(0,o-s.length)}(?>${s}${l})${n.slice(o+c.length)}`;p+=4}nt.lastIndex+=p}else c[0]==="("?e.push(o):c===")"&&(t=e.length?e.pop():null);s=c}return{pattern:n}}const H=String.raw,tc=H`\\g<(?[^>&]+)&R=(?[^>]+)>`,tt=H`\(\?R=(?[^\)]+)\)|${tc}`,_n=H`\(\?<(?![=!])(?[^>]+)>`,La=H`${_n}|(?\()(?!\?)`,ye=new RegExp(H`${_n}|${tt}|\(\?|\\?.`,"gsu"),at="Cannot use multiple overlapping recursions";function ac(n,e){var g;const{hiddenCaptures:t,mode:a}={hiddenCaptures:[],mode:"plugin",...e};let s=(g=e==null?void 0:e.captureTransfers)!=null?g:new Map;if(!new RegExp(tt,"su").test(n))return{pattern:n,captureTransfers:s,hiddenCaptures:t};if(a==="plugin"&&yn(n,H`\(\?\(DEFINE\)`,Z.DEFAULT))throw new Error("DEFINE groups cannot be used with recursion");const i=[],r=yn(n,H`\\[1-9]`,Z.DEFAULT),c=new Map,o=[];let l=!1,u=0,m=0,p;for(ye.lastIndex=0;p=ye.exec(n);){const{0:d,groups:{captureName:f,rDepth:$,gRNameOrNum:_,gRDepth:x}}=p;if(d==="[")u++;else if(u)d==="]"&&u--;else if($){if(Ta($),l)throw new Error(at);if(r)throw new Error(`${a==="external"?"Backrefs":"Numbered backrefs"} cannot be used with global recursion`);const y=n.slice(0,p.index),w=n.slice(ye.lastIndex);if(yn(w,tt,Z.DEFAULT))throw new Error(at);const C=+$-1;n=Oa(y,w,C,!1,t,i,m),s=Da(s,y,C,i.length,0,m);break}else if(_){Ta(x);let y=!1;for(const pe of o)if(pe.name===_||pe.num===+_){if(y=!0,pe.hasRecursedWithin)throw new Error(at);break}if(!y)throw new Error(H`Recursive \g cannot be used outside the referenced group "${a==="external"?_:H`\g<${_}&R=${x}>`}"`);const w=c.get(_),C=Ko(n,w);if(r&&yn(C,H`${_n}|\((?!\?)`,Z.DEFAULT))throw new Error(`${a==="external"?"Backrefs":"Numbered backrefs"} cannot be used with recursion of capturing groups`);const E=n.slice(w,p.index),S=C.slice(E.length+d.length),D=i.length,G=+x-1,T=Oa(E,S,G,!0,t,i,m);s=Da(s,E,G,i.length-D,D,m);const Ge=n.slice(0,w),se=n.slice(w+C.length);n=`${Ge}${T}${se}`,ye.lastIndex+=T.length-d.length-E.length-S.length,o.forEach(pe=>pe.hasRecursedWithin=!0),l=!0}else if(f)m++,c.set(String(m),ye.lastIndex),c.set(f,ye.lastIndex),o.push({num:m,name:f});else if(d[0]==="("){const y=d==="(";y&&(m++,c.set(String(m),ye.lastIndex)),o.push(y?{num:m}:{})}else d===")"&&o.pop()}return t.push(...i),{pattern:n,captureTransfers:s,hiddenCaptures:t}}function Ta(n){const e=`Max depth must be integer between 2 and 100; used ${n}`;if(!/^[1-9]\d*$/.test(n))throw new Error(e);if(n=+n,n<2||n>100)throw new Error(e)}function Oa(n,e,t,a,s,i,r){const c=new Set;a&&Ra(n+e,_n,({groups:{captureName:l}})=>{c.add(l)},Z.DEFAULT);const o=[t,a?c:null,s,i,r];return`${n}${Ma(`(?:${n}`,"forward",...o)}(?:)${Ma(`${e})`,"backward",...o)}${e}`}function Ma(n,e,t,a,s,i,r){const o=u=>e==="forward"?u+2:t-u+2-1;let l="";for(let u=0;u[^>]+)>`,({0:p,groups:{captureName:g,unnamed:d,backref:f}})=>{if(f&&a&&!a.has(f))return p;const $=`_$${m}`;if(d||g){const _=r+i.length+1;return i.push(_),sc(s,_),d?p:`(?<${g}${$}>`}return H`\k<${f}${$}>`},Z.DEFAULT)}return l}function sc(n,e){for(let t=0;t=e&&n[t]++}function Da(n,e,t,a,s,i){if(n.size&&a){let r=0;Ra(e,La,()=>r++,Z.DEFAULT);const c=i-r+s,o=new Map;return n.forEach((l,u)=>{const m=(a-r*t)/t,p=r*t,g=u>c+r?u+a:u,d=[];for(const f of l)if(f<=c)d.push(f);else if(f>c+r+m)d.push(f+a);else if(f<=c+r)for(let $=0;$<=t;$++)d.push(f+r*$);else for(let $=0;$<=t;$++)d.push(f+p+m*$);o.set(g,d)}),o}return n}var R=String.fromCodePoint,j=String.raw,K={flagGroups:(()=>{try{new RegExp("(?i:)")}catch(n){return!1}return!0})(),unicodeSets:(()=>{try{new RegExp("[[]]","v")}catch(n){return!1}return!0})()};K.bugFlagVLiteralHyphenIsRange=K.unicodeSets?(()=>{try{new RegExp(j`[\d\-a]`,"v")}catch(n){return!0}return!1})():!1,K.bugNestedClassIgnoresNegation=K.unicodeSets&&new RegExp("[[^a]]","v").test("a");function $n(n,{enable:e,disable:t}){return{dotAll:!(t!=null&&t.dotAll)&&!!(e!=null&&e.dotAll||n.dotAll),ignoreCase:!(t!=null&&t.ignoreCase)&&!!(e!=null&&e.ignoreCase||n.ignoreCase)}}function We(n,e,t){return n.has(e)||n.set(e,t),n.get(e)}function st(n,e){return Ua[n]>=Ua[e]}function ic(n,e){if(n==null)throw new Error(e!=null?e:"Value expected");return n}var Ua={ES2025:2025,ES2024:2024,ES2018:2018},rc={auto:"auto",ES2025:"ES2025",ES2024:"ES2024",ES2018:"ES2018"};function Za(n={}){if({}.toString.call(n)!=="[object Object]")throw new Error("Unexpected options");if(n.target!==void 0&&!rc[n.target])throw new Error(`Unexpected target "${n.target}"`);const e={accuracy:"default",avoidSubclass:!1,flags:"",global:!1,hasIndices:!1,lazyCompileLength:1/0,target:"auto",verbose:!1,...n,rules:{allowOrphanBackrefs:!1,asciiWordBoundaries:!1,captureGroup:!1,recursionLimit:20,singleline:!1,...n.rules}};return e.target==="auto"&&(e.target=K.flagGroups?"ES2025":K.unicodeSets?"ES2024":"ES2018"),e}var oc="[ -\r ]",cc=new Set([R(304),R(305)]),ee=j`[\p{L}\p{M}\p{N}\p{Pc}]`;function Ha(n){if(cc.has(n))return[n];const e=new Set,t=n.toLowerCase(),a=t.toUpperCase(),s=mc.get(t),i=lc.get(t),r=uc.get(t);return[...a].length===1&&e.add(a),r&&e.add(r),s&&e.add(s),e.add(t),i&&e.add(i),[...e]}var it=new Map(`C Other -Cc Control cntrl -Cf Format -Cn Unassigned -Co Private_Use -Cs Surrogate -L Letter -LC Cased_Letter -Ll Lowercase_Letter -Lm Modifier_Letter -Lo Other_Letter -Lt Titlecase_Letter -Lu Uppercase_Letter -M Mark Combining_Mark -Mc Spacing_Mark -Me Enclosing_Mark -Mn Nonspacing_Mark -N Number -Nd Decimal_Number digit -Nl Letter_Number -No Other_Number -P Punctuation punct -Pc Connector_Punctuation -Pd Dash_Punctuation -Pe Close_Punctuation -Pf Final_Punctuation -Pi Initial_Punctuation -Po Other_Punctuation -Ps Open_Punctuation -S Symbol -Sc Currency_Symbol -Sk Modifier_Symbol -Sm Math_Symbol -So Other_Symbol -Z Separator -Zl Line_Separator -Zp Paragraph_Separator -Zs Space_Separator -ASCII -ASCII_Hex_Digit AHex -Alphabetic Alpha -Any -Assigned -Bidi_Control Bidi_C -Bidi_Mirrored Bidi_M -Case_Ignorable CI -Cased -Changes_When_Casefolded CWCF -Changes_When_Casemapped CWCM -Changes_When_Lowercased CWL -Changes_When_NFKC_Casefolded CWKCF -Changes_When_Titlecased CWT -Changes_When_Uppercased CWU -Dash -Default_Ignorable_Code_Point DI -Deprecated Dep -Diacritic Dia -Emoji -Emoji_Component EComp -Emoji_Modifier EMod -Emoji_Modifier_Base EBase -Emoji_Presentation EPres -Extended_Pictographic ExtPict -Extender Ext -Grapheme_Base Gr_Base -Grapheme_Extend Gr_Ext -Hex_Digit Hex -IDS_Binary_Operator IDSB -IDS_Trinary_Operator IDST -ID_Continue IDC -ID_Start IDS -Ideographic Ideo -Join_Control Join_C -Logical_Order_Exception LOE -Lowercase Lower -Math -Noncharacter_Code_Point NChar -Pattern_Syntax Pat_Syn -Pattern_White_Space Pat_WS -Quotation_Mark QMark -Radical -Regional_Indicator RI -Sentence_Terminal STerm -Soft_Dotted SD -Terminal_Punctuation Term -Unified_Ideograph UIdeo -Uppercase Upper -Variation_Selector VS -White_Space space -XID_Continue XIDC -XID_Start XIDS`.split(/\s/).map(n=>[hn(n),n])),lc=new Map([["s",R(383)],[R(383),"s"]]),uc=new Map([[R(223),R(7838)],[R(107),R(8490)],[R(229),R(8491)],[R(969),R(8486)]]),mc=new Map([oe(453),oe(456),oe(459),oe(498),...rt(8072,8079),...rt(8088,8095),...rt(8104,8111),oe(8124),oe(8140),oe(8188)]),pc=new Map([["alnum",j`[\p{Alpha}\p{Nd}]`],["alpha",j`\p{Alpha}`],["ascii",j`\p{ASCII}`],["blank",j`[\p{Zs}\t]`],["cntrl",j`\p{Cc}`],["digit",j`\p{Nd}`],["graph",j`[\P{space}&&\P{Cc}&&\P{Cn}&&\P{Cs}]`],["lower",j`\p{Lower}`],["print",j`[[\P{space}&&\P{Cc}&&\P{Cn}&&\P{Cs}]\p{Zs}]`],["punct",j`[\p{P}\p{S}]`],["space",j`\p{space}`],["upper",j`\p{Upper}`],["word",j`[\p{Alpha}\p{M}\p{Nd}\p{Pc}]`],["xdigit",j`\p{AHex}`]]);function dc(n,e){const t=[];for(let a=n;a<=e;a++)t.push(a);return t}function oe(n){const e=R(n);return[e.toLowerCase(),e]}function rt(n,e){return dc(n,e).map(t=>oe(t))}var Wa=new Set(["Lower","Lowercase","Upper","Uppercase","Ll","Lowercase_Letter","Lt","Titlecase_Letter","Lu","Uppercase_Letter"]);function gc(n,e){const t={accuracy:"default",asciiWordBoundaries:!1,avoidSubclass:!1,bestEffortTarget:"ES2025",...e};Va(n);const a={accuracy:t.accuracy,asciiWordBoundaries:t.asciiWordBoundaries,avoidSubclass:t.avoidSubclass,flagDirectivesByAlt:new Map,jsGroupNameMap:new Map,minTargetEs2024:st(t.bestEffortTarget,"ES2024"),passedLookbehind:!1,strategy:null,subroutineRefMap:new Map,supportedGNodes:new Set,digitIsAscii:n.flags.digitIsAscii,spaceIsAscii:n.flags.spaceIsAscii,wordIsAscii:n.flags.wordIsAscii};He(n,bc,a);const s={dotAll:n.flags.dotAll,ignoreCase:n.flags.ignoreCase},i={currentFlags:s,prevFlags:null,globalFlags:s,groupOriginByCopy:new Map,groupsByName:new Map,multiplexCapturesToLeftByRef:new Map,openRefs:new Map,reffedNodesByReferencer:new Map,subroutineRefMap:a.subroutineRefMap};He(n,hc,i);const r={groupsByName:i.groupsByName,highestOrphanBackref:0,numCapturesToLeft:0,reffedNodesByReferencer:i.reffedNodesByReferencer};return He(n,fc,r),n._originMap=i.groupOriginByCopy,n._strategy=a.strategy,n}var bc={AbsenceFunction({node:n,parent:e,replaceWith:t}){const{body:a,kind:s}=n;if(s==="repeater"){const i=X();i.body[0].body.push(fe({negate:!0,body:a}),Ne("Any"));const r=X();r.body[0].body.push(qa("greedy",0,1/0,i)),t(F(r,e),{traverse:!0})}else throw new Error('Unsupported absence function "(?~|"')},Alternative:{enter({node:n,parent:e,key:t},{flagDirectivesByAlt:a}){const s=n.body.filter(i=>i.kind==="flags");for(let i=t+1;i\r\n|${s?j`\p{RGI_Emoji}`:p}|\P{M}\p{M}*)`,{skipPropertyNameValidation:!0}),e))}else if(o==="hex")t(ce(Ne("AHex",{negate:l}),e));else if(o==="newline")t(F(ne(l?`[^ -]`:`(?>\r -?|[ -\v\f…\u2028\u2029])`),e));else if(o==="posix")if(!s&&(u==="graph"||u==="print")){if(a==="strict")throw new Error(`POSIX class "${u}" requires min target ES2024 or non-strict accuracy`);let m={graph:"!-~",print:" -~"}[u];l&&(m=`\0-${R(m.codePointAt(0)-1)}${R(m.codePointAt(2)+1)}-􏿿`),t(F(ne(`[${m}]`),e))}else t(F(lt(ne(pc.get(u)),l),e));else if(o==="property")it.has(hn(u))||(n.key="sc");else if(o==="space")t(ce(Ne("space",{negate:l}),e));else if(o==="word")t(F(lt(ne(ee),l),e));else throw new Error(`Unexpected character set kind "${o}"`)},Directive({node:n,parent:e,root:t,remove:a,replaceWith:s,removeAllPrevSiblings:i,removeAllNextSiblings:r}){const{kind:c,flags:o}=n;if(c==="flags")if(!o.enable&&!o.disable)a();else{const l=X({flags:o});l.body[0].body=r(),s(F(l,e),{traverse:!0})}else if(c==="keep"){const l=t.body[0],m=t.body.length===1&&Ca(l,{type:"Group"})&&l.body[0].body.length===1?l.body[0]:t;if(e.parent!==m||m.body.length>1)throw new Error(j`Uses "\K" in a way that's unsupported`);const p=fe({behind:!0});p.body[0].body=i(),s(F(p,e))}else throw new Error(`Unexpected directive kind "${c}"`)},Flags({node:n,parent:e}){var t;if(n.posixIsAscii)throw new Error('Unsupported flag "P"');if(n.textSegmentMode==="word")throw new Error('Unsupported flag "y{w}"');["digitIsAscii","extended","posixIsAscii","spaceIsAscii","wordIsAscii","textSegmentMode"].forEach(a=>delete n[a]),Object.assign(n,{global:!1,hasIndices:!1,multiline:!1,sticky:(t=n.sticky)!=null?t:!1}),e.options={disable:{x:!0,n:!0},force:{v:!0}}},Group({node:n}){if(!n.flags)return;const{enable:e,disable:t}=n.flags;e!=null&&e.extended&&delete e.extended,t!=null&&t.extended&&delete t.extended,e!=null&&e.dotAll&&(t!=null&&t.dotAll)&&delete e.dotAll,e!=null&&e.ignoreCase&&(t!=null&&t.ignoreCase)&&delete e.ignoreCase,e&&!Object.keys(e).length&&delete n.flags.enable,t&&!Object.keys(t).length&&delete n.flags.disable,!n.flags.enable&&!n.flags.disable&&delete n.flags},LookaroundAssertion({node:n},e){const{kind:t}=n;t==="lookbehind"&&(e.passedLookbehind=!0)},NamedCallout({node:n,parent:e,replaceWith:t}){const{kind:a}=n;if(a==="fail")t(F(fe({negate:!0}),e));else throw new Error(`Unsupported named callout "(*${a.toUpperCase()}"`)},Quantifier({node:n}){if(n.body.type==="Quantifier"){const e=X();e.body[0].body.push(n.body),n.body=F(e,n)}},Regex:{enter({node:n},{supportedGNodes:e}){const t=[];let a=!1,s=!1;for(const i of n.body)if(i.body.length===1&&i.body[0].kind==="search_start")i.body.pop();else{const r=Ka(i.body);r?(a=!0,Array.isArray(r)?t.push(...r):t.push(r)):s=!0}a&&!s&&t.forEach(i=>e.add(i))},exit(n,{accuracy:e,passedLookbehind:t,strategy:a}){if(e==="strict"&&t&&a)throw new Error(j`Uses "\G" in a way that requires non-strict accuracy`)}},Subroutine({node:n},{jsGroupNameMap:e}){let{ref:t}=n;typeof t=="string"&&!ct(t)&&(t=ot(t,e),n.ref=t)}},hc={Backreference({node:n},{multiplexCapturesToLeftByRef:e,reffedNodesByReferencer:t}){const{orphan:a,ref:s}=n;a||t.set(n,[...e.get(s).map(({node:i})=>i)])},CapturingGroup:{enter({node:n,parent:e,replaceWith:t,skip:a},{groupOriginByCopy:s,groupsByName:i,multiplexCapturesToLeftByRef:r,openRefs:c,reffedNodesByReferencer:o}){var m;const l=s.get(n);if(l&&c.has(n.number)){const p=ce(Ya(n.number),e);o.set(p,c.get(n.number)),t(p);return}c.set(n.number,n),r.set(n.number,[]),n.name&&We(r,n.name,[]);const u=r.get((m=n.name)!=null?m:n.number);for(let p=0;pp.type==="Group"&&!!p.flags)),m=u?$n(a.globalFlags,u):a.globalFlags;yc(m,a.currentFlags)||(l=X({flags:xc(m)}),l.body[0].body.push(o))}t(F(l,e),{traverse:!c})}},fc={Backreference({node:n,parent:e,replaceWith:t},a){if(n.orphan){a.highestOrphanBackref=Math.max(a.highestOrphanBackref,n.ref);return}const i=a.reffedNodesByReferencer.get(n).filter(r=>_c(r,n));if(!i.length)t(F(fe({negate:!0}),e));else if(i.length>1){const r=X({atomic:!0,body:i.reverse().map(c=>he({body:[Qn(c.number)]}))});t(F(r,e))}else n.ref=i[0].number},CapturingGroup({node:n},e){n.number=++e.numCapturesToLeft,n.name&&e.groupsByName.get(n.name).get(n).hasDuplicateNameToRemove&&delete n.name},Regex:{exit({node:n},e){const t=Math.max(e.highestOrphanBackref-e.numCapturesToLeft,0);for(let a=0;a{e.forEach(s=>{var i,r;(i=a.enable)!=null&&i[s]&&(delete t.disable[s],t.enable[s]=!0),(r=a.disable)!=null&&r[s]&&(t.disable[s]=!0)})}),Object.keys(t.enable).length||delete t.enable,Object.keys(t.disable).length||delete t.disable,t.enable||t.disable?t:null}function xc({dotAll:n,ignoreCase:e}){const t={};return(n||e)&&(t.enable={},n&&(t.enable.dotAll=!0),e&&(t.enable.ignoreCase=!0)),(!n||!e)&&(t.disable={},!n&&(t.disable.dotAll=!0),!e&&(t.disable.ignoreCase=!0)),t}function Qa(n){if(!n)throw new Error("Node expected");const{body:e}=n;return Array.isArray(e)?e:e?[e]:null}function Ka(n){const e=n.find(t=>t.kind==="search_start"||jc(t,{negate:!1})||!wc(t));if(!e)return null;if(e.kind==="search_start")return e;if(e.type==="LookaroundAssertion")return e.body[0].body[0];if(e.type==="CapturingGroup"||e.type==="Group"){const t=[];for(const a of e.body){const s=Ka(a.body);if(!s)return null;Array.isArray(s)?t.push(...s):t.push(s)}return t}return null}function es(n,e){var a;const t=(a=Qa(n))!=null?a:[];for(const s of t)if(s===e||es(s,e))return!0;return!1}function wc({type:n}){return n==="Assertion"||n==="Directive"||n==="LookaroundAssertion"}function kc(n){const e=["Character","CharacterClass","CharacterSet"];return e.includes(n.type)||n.type==="Quantifier"&&n.min&&e.includes(n.body.type)}function jc(n,e){const t={negate:null,...e};return n.type==="LookaroundAssertion"&&(t.negate===null||n.negate===t.negate)&&n.body.length===1&&Ca(n.body[0],{type:"Assertion",kind:"search_start"})}function ct(n){return/^[$_\p{IDS}][$\u200C\u200D\p{IDC}]*$/u.test(n)}function ne(n,e){const a=Sa(n,{...e,unicodePropertyMap:it}).body;return a.length>1||a[0].body.length>1?X({body:a}):a[0].body[0]}function lt(n,e){return n.negate=e,n}function ce(n,e){return n.parent=e,n}function F(n,e){return Va(n),n.parent=e,n}function vc(n,e){const t=Za(e),a=st(t.target,"ES2024"),s=st(t.target,"ES2025"),i=t.rules.recursionLimit;if(!Number.isInteger(i)||i<2||i>20)throw new Error("Invalid recursionLimit; use 2-20");let r=null,c=null;if(!s){const g=[n.flags.ignoreCase];He(n,Cc,{getCurrentModI:()=>g.at(-1),popModI(){g.pop()},pushModI(d){g.push(d)},setHasCasedChar(){g.at(-1)?r=!0:c=!0}})}const o={dotAll:n.flags.dotAll,ignoreCase:!!((n.flags.ignoreCase||r)&&!c)};let l=n;const u={accuracy:t.accuracy,appliedGlobalFlags:o,captureMap:new Map,currentFlags:{dotAll:n.flags.dotAll,ignoreCase:n.flags.ignoreCase},inCharClass:!1,lastNode:l,originMap:n._originMap,recursionLimit:i,useAppliedIgnoreCase:!!(!s&&r&&c),useFlagMods:s,useFlagV:a,verbose:t.verbose};function m(g){return u.lastNode=l,l=g,ic(Sc[g.type],`Unexpected node type "${g.type}"`)(g,u,m)}const p={pattern:n.body.map(m).join("|"),flags:m(n.flags),options:{...n.options}};return a||(delete p.options.force.v,p.options.disable.v=!0,p.options.unicodeSetsPlugin=null),p._captureTransfers=new Map,p._hiddenCaptures=[],u.captureMap.forEach((g,d)=>{g.hidden&&p._hiddenCaptures.push(d),g.transferTo&&We(p._captureTransfers,g.transferTo,[]).push(d)}),p}var Cc={"*":{enter({node:n},e){if(as(n)){const t=e.getCurrentModI();e.pushModI(n.flags?$n({ignoreCase:t},n.flags).ignoreCase:t)}},exit({node:n},e){as(n)&&e.popModI()}},Backreference(n,e){e.setHasCasedChar()},Character({node:n},e){ut(R(n.value))&&e.setHasCasedChar()},CharacterClassRange({node:n,skip:e},t){e(),ts(n,{firstOnly:!0}).length&&t.setHasCasedChar()},CharacterSet({node:n},e){n.kind==="property"&&Wa.has(n.value)&&e.setHasCasedChar()}},Sc={Alternative({body:n},e,t){return n.map(t).join("")},Assertion({kind:n,negate:e}){if(n==="string_end")return"$";if(n==="string_start")return"^";if(n==="word_boundary")return e?j`\B`:j`\b`;throw new Error(`Unexpected assertion kind "${n}"`)},Backreference({ref:n},e){if(typeof n!="number")throw new Error("Unexpected named backref in transformed AST");if(!e.useFlagMods&&e.accuracy==="strict"&&e.currentFlags.ignoreCase&&!e.captureMap.get(n).ignoreCase)throw new Error("Use of case-insensitive backref to case-sensitive group requires target ES2025 or non-strict accuracy");return"\\"+n},CapturingGroup(n,e,t){const{body:a,name:s,number:i}=n,r={ignoreCase:e.currentFlags.ignoreCase},c=e.originMap.get(n);return c&&(r.hidden=!0,i>c.number&&(r.transferTo=c.number)),e.captureMap.set(i,r),`(${s?`?<${s}>`:""}${a.map(t).join("|")})`},Character({value:n},e){const t=R(n),a=Be(n,{escDigit:e.lastNode.type==="Backreference",inCharClass:e.inCharClass,useFlagV:e.useFlagV});if(a!==t)return a;if(e.useAppliedIgnoreCase&&e.currentFlags.ignoreCase&&ut(t)){const s=Ha(t);return e.inCharClass?s.join(""):s.length>1?`[${s.join("")}]`:s[0]}return t},CharacterClass(n,e,t){const{kind:a,negate:s,parent:i}=n;let{body:r}=n;if(a==="intersection"&&!e.useFlagV)throw new Error("Use of character class intersection requires min target ES2024");K.bugFlagVLiteralHyphenIsRange&&e.useFlagV&&r.some(ss)&&(r=[gn(45),...r.filter(l=>!ss(l))]);const c=()=>`[${s?"^":""}${r.map(t).join(a==="intersection"?"&&":"")}]`;if(!e.inCharClass){if((!e.useFlagV||K.bugNestedClassIgnoresNegation)&&!s){const u=r.filter(m=>m.type==="CharacterClass"&&m.kind==="union"&&m.negate);if(u.length){const m=X(),p=m.body[0];return m.parent=i,p.parent=m,r=r.filter(g=>!u.includes(g)),n.body=r,r.length?(n.parent=p,p.body.push(n)):m.body.pop(),u.forEach(g=>{const d=he({body:[g]});g.parent=d,d.parent=m,m.body.push(d)}),t(m)}}e.inCharClass=!0;const l=c();return e.inCharClass=!1,l}const o=r[0];if(a==="union"&&!s&&o&&((!e.useFlagV||!e.verbose)&&i.kind==="union"&&!(K.bugFlagVLiteralHyphenIsRange&&e.useFlagV)||!e.verbose&&i.kind==="intersection"&&r.length===1&&o.type!=="CharacterClassRange"))return r.map(t).join("");if(!e.useFlagV&&i.type==="CharacterClass")throw new Error("Uses nested character class in a way that requires min target ES2024");return c()},CharacterClassRange(n,e){const t=n.min.value,a=n.max.value,s={escDigit:!1,inCharClass:!0,useFlagV:e.useFlagV},i=Be(t,s),r=Be(a,s),c=new Set;if(e.useAppliedIgnoreCase&&e.currentFlags.ignoreCase){const o=ts(n);Fc(o).forEach(u=>{c.add(Array.isArray(u)?`${Be(u[0],s)}-${Be(u[1],s)}`:Be(u,s))})}return`${i}-${r}${[...c].join("")}`},CharacterSet({kind:n,negate:e,value:t,key:a},s){if(n==="dot")return s.currentFlags.dotAll?s.appliedGlobalFlags.dotAll||s.useFlagMods?".":"[^]":j`[^\n]`;if(n==="digit")return e?j`\D`:j`\d`;if(n==="property"){if(s.useAppliedIgnoreCase&&s.currentFlags.ignoreCase&&Wa.has(t))throw new Error(`Unicode property "${t}" can't be case-insensitive when other chars have specific case`);return`${e?j`\P`:j`\p`}{${a?`${a}=`:""}${t}}`}if(n==="word")return e?j`\W`:j`\w`;throw new Error(`Unexpected character set kind "${n}"`)},Flags(n,e){return(e.appliedGlobalFlags.ignoreCase?"i":"")+(n.dotAll?"s":"")+(n.sticky?"y":"")},Group({atomic:n,body:e,flags:t,parent:a},s,i){const r=s.currentFlags;t&&(s.currentFlags=$n(r,t));const c=e.map(i).join("|"),o=!s.verbose&&e.length===1&&a.type!=="Quantifier"&&!n&&(!s.useFlagMods||!t)?c:`(?${Bc(n,t,s.useFlagMods)}${c})`;return s.currentFlags=r,o},LookaroundAssertion({body:n,kind:e,negate:t},a,s){return`(?${`${e==="lookahead"?"":"<"}${t?"!":"="}`}${n.map(s).join("|")})`},Quantifier(n,e,t){return t(n.body)+Gc(n)},Subroutine({isRecursive:n,ref:e},t){if(!n)throw new Error("Unexpected non-recursive subroutine in transformed AST");const a=t.recursionLimit;return e===0?`(?R=${a})`:j`\g<${e}&R=${a}>`}},Ec=new Set(["$","(",")","*","+",".","?","[","\\","]","^","{","|","}"]),qc=new Set(["-","\\","]","^","["]),Ac=new Set(["(",")","-","/","[","\\","]","^","{","|","}","!","#","$","%","&","*","+",",",".",":",";","<","=",">","?","@","`","~"]),ns=new Map([[9,j`\t`],[10,j`\n`],[11,j`\v`],[12,j`\f`],[13,j`\r`],[8232,j`\u2028`],[8233,j`\u2029`],[65279,j`\uFEFF`]]),Nc=/^\p{Cased}$/u;function ut(n){return Nc.test(n)}function ts(n,e){const t=!!(e!=null&&e.firstOnly),a=n.min.value,s=n.max.value,i=[];if(a<65&&(s===65535||s>=131071)||a===65536&&s>=131071)return i;for(let r=a;r<=s;r++){const c=R(r);if(!ut(c))continue;const o=Ha(c).filter(l=>{const u=l.codePointAt(0);return us});if(o.length&&(i.push(...o),t))break}return i}function Be(n,{escDigit:e,inCharClass:t,useFlagV:a}){if(ns.has(n))return ns.get(n);if(n<32||n>126&&n<160||n>262143||e&&Ic(n))return n>255?`\\u{${n.toString(16).toUpperCase()}}`:`\\x${n.toString(16).toUpperCase().padStart(2,"0")}`;const s=t?a?Ac:qc:Ec,i=R(n);return(s.has(i)?"\\":"")+i}function Fc(n){const e=n.map(s=>s.codePointAt(0)).sort((s,i)=>s-i),t=[];let a=null;for(let s=0;s";let a="";if(e&&t){const{enable:s,disable:i}=e;a=(s!=null&&s.ignoreCase?"i":"")+(s!=null&&s.dotAll?"s":"")+(i?"-":"")+(i!=null&&i.ignoreCase?"i":"")+(i!=null&&i.dotAll?"s":"")}return`${a}:`}function Gc({kind:n,max:e,min:t}){let a;return!t&&e===1?a="?":!t&&e===1/0?a="*":t===1&&e===1/0?a="+":t===e?a=`{${t}}`:a=`{${t},${e===1/0?"":e}}`,a+{greedy:"",lazy:"?",possessive:"+"}[n]}function as({type:n}){return n==="CapturingGroup"||n==="Group"||n==="LookaroundAssertion"}function Ic(n){return n>47&&n<58}function ss({type:n,value:e}){return n==="Character"&&e===45}var Rc=(me=class extends RegExp{constructor(t,a,s){var e=(...Gl)=>(super(...Gl),xe(this,Ve),xe(this,le,new Map),xe(this,ae,null),xe(this,_e),xe(this,ue,null),xe(this,$e,null),b(this,"rawOptions",{}),this);const i=!!(s!=null&&s.lazyCompile);if(t instanceof RegExp){if(s)throw new Error("Cannot provide options when copying a regexp");const r=t;e(r,a),Y(this,_e,r.source),r instanceof me&&(Y(this,le,O(r,le)),Y(this,ue,O(r,ue)),Y(this,$e,O(r,$e)),this.rawOptions=r.rawOptions)}else{const r={hiddenCaptures:[],strategy:null,transfers:[],...s};e(i?"":t,a),Y(this,_e,t),Y(this,le,zc(r.hiddenCaptures,r.transfers)),Y(this,$e,r.strategy),this.rawOptions=s!=null?s:{}}i||Y(this,ae,this)}get source(){return O(this,_e)||"(?:)"}exec(t){if(!O(this,ae)){const{lazyCompile:i,...r}=this.rawOptions;Y(this,ae,new me(O(this,_e),this.flags,r))}const a=this.global||this.sticky,s=this.lastIndex;if(O(this,$e)==="clip_search"&&a&&s){this.lastIndex=0;const i=dt(this,Ve,bt).call(this,t.slice(s));return i&&(Pc(i,s,t,this.hasIndices),this.lastIndex+=s),i}return dt(this,Ve,bt).call(this,t)}},le=new WeakMap,ae=new WeakMap,_e=new WeakMap,ue=new WeakMap,$e=new WeakMap,Ve=new WeakSet,bt=function(t){var c;O(this,ae).lastIndex=this.lastIndex;const a=gs(me.prototype,this,"exec").call(O(this,ae),t);if(this.lastIndex=O(this,ae).lastIndex,!a||!O(this,le).size)return a;const s=[...a];a.length=1;let i;this.hasIndices&&(i=[...a.indices],a.indices.length=1);const r=[0];for(let o=1;o{const c=i[r];c&&(i[r]=[c[0]+e,c[1]+e])})}}function zc(n,e){const t=new Map;for(const a of n)t.set(a,{hidden:!0});for(const[a,s]of e)for(const i of s)We(t,i,{}).transferTo=a;return t}function Lc(n){const e=/(?\((?:\?<(?![=!])(?[^>]+)>|(?!\?)))|\\?./gsu,t=new Map;let a=0,s=0,i;for(;i=e.exec(n);){const{0:r,groups:{capture:c,name:o}}=i;r==="["?a++:a?r==="]"&&a--:c&&(s++,o&&t.set(s,o))}return t}function Tc(n,e){const t=Oc(n,e);return t.options?new Rc(t.pattern,t.flags,t.options):new RegExp(t.pattern,t.flags)}function Oc(n,e){const t=Za(e),a=Sa(n,{flags:t.flags,normalizeUnknownPropertyNames:!0,rules:{captureGroup:t.rules.captureGroup,singleline:t.rules.singleline},skipBackrefValidation:t.rules.allowOrphanBackrefs,unicodePropertyMap:it}),s=gc(a,{accuracy:t.accuracy,asciiWordBoundaries:t.rules.asciiWordBoundaries,avoidSubclass:t.avoidSubclass,bestEffortTarget:t.target}),i=vc(s,t),r=ac(i.pattern,{captureTransfers:i._captureTransfers,hiddenCaptures:i._hiddenCaptures,mode:"external"}),c=nc(r.pattern),o=ec(c.pattern,{captureTransfers:r.captureTransfers,hiddenCaptures:r.hiddenCaptures}),l={pattern:o.pattern,flags:`${t.hasIndices?"d":""}${t.global?"g":""}${i.flags}${i.options.disable.v?"u":"v"}`};if(t.avoidSubclass){if(t.lazyCompileLength!==1/0)throw new Error("Lazy compilation requires subclass")}else{const u=o.hiddenCaptures.sort((d,f)=>d-f),m=Array.from(o.captureTransfers),p=s._strategy,g=l.pattern.length>=t.lazyCompileLength;(u.length||m.length||p||g)&&(l.options={...u.length&&{hiddenCaptures:u},...m.length&&{transfers:m},...p&&{strategy:p},...g&&{lazyCompile:g}})}return l}const is=4294967295;class Mc{constructor(e,t={}){b(this,"regexps");this.patterns=e,this.options=t;const{forgiving:a=!1,cache:s,regexConstructor:i}=t;if(!i)throw new Error("Option `regexConstructor` is not provided");this.regexps=e.map(r=>{if(typeof r!="string")return r;const c=s==null?void 0:s.get(r);if(c){if(c instanceof RegExp)return c;if(a)return null;throw c}try{const o=i(r);return s==null||s.set(r,o),o}catch(o){if(s==null||s.set(r,o),a)return null;throw o}})}findNextMatchSync(e,t,a){const s=typeof e=="string"?e:e.content,i=[];function r(c,o,l=0){return{index:c,captureIndices:o.indices.map(u=>u==null?{start:is,end:is,length:0}:{start:u[0]+l,end:u[1]+l,length:u[1]-u[0]})}}for(let c=0;co[1].index));for(const[o,l,u]of i)if(l.index===c)return r(o,l,u)}return null}}function Dc(n,e){return Tc(n,{global:!0,hasIndices:!0,lazyCompileLength:3e3,rules:{allowOrphanBackrefs:!0,asciiWordBoundaries:!0,captureGroup:!0,recursionLimit:5,singleline:!0},...e})}function Uc(n={}){const e=Object.assign({target:"auto",cache:new Map},n);return e.regexConstructor||(e.regexConstructor=t=>Dc(t,{target:e.target})),{createScanner(t){return new Mc(t,e)},createString(t){return{content:t}}}}const Zc=[Object.freeze(JSON.parse(`{"displayName":"Shell","name":"shellscript","patterns":[{"include":"#initial_context"}],"repository":{"alias_statement":{"begin":"[\\\\t ]*+(alias)[\\\\t ]*+((?:((?\\\\\\\\\`|]+(?!>))"},{"include":"#normal_context"}]},"arithmetic_double":{"patterns":[{"begin":"\\\\(\\\\(","beginCaptures":{"0":{"name":"punctuation.section.arithmetic.double.shell"}},"end":"\\\\)\\\\s*\\\\)","endCaptures":{"0":{"name":"punctuation.section.arithmetic.double.shell"}},"name":"meta.arithmetic.shell","patterns":[{"include":"#math"},{"include":"#string"}]}]},"arithmetic_no_dollar":{"patterns":[{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.section.arithmetic.single.shell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.section.arithmetic.single.shell"}},"name":"meta.arithmetic.shell","patterns":[{"include":"#math"},{"include":"#string"}]}]},"array_access_inline":{"captures":{"1":{"name":"punctuation.section.array.shell"},"2":{"patterns":[{"include":"#special_expansion"},{"include":"#string"},{"include":"#variable"}]},"3":{"name":"punctuation.section.array.shell"}},"match":"(\\\\[)([^]\\\\[]+)(])"},"array_value":{"begin":"[\\\\t ]*+((?\\\\[{|]|$|[\\\\t ;])(?!nocorrect |nocorrect\\\\t|nocorrect$|readonly |readonly\\\\t|readonly$|function |function\\\\t|function$|foreach |foreach\\\\t|foreach$|coproc |coproc\\\\t|coproc$|logout |logout\\\\t|logout$|export |export\\\\t|export$|select |select\\\\t|select$|repeat |repeat\\\\t|repeat$|pushd |pushd\\\\t|pushd$|until |until\\\\t|until$|while |while\\\\t|while$|local |local\\\\t|local$|case |case\\\\t|case$|done |done\\\\t|done$|elif |elif\\\\t|elif$|else |else\\\\t|else$|esac |esac\\\\t|esac$|popd |popd\\\\t|popd$|then |then\\\\t|then$|time |time\\\\t|time$|for |for\\\\t|for$|end |end\\\\t|end$|fi |fi\\\\t|fi$|do |do\\\\t|do$|in |in\\\\t|in$|if |if\\\\t|if$)(?:((?<=^|[\\\\t \\\\&;])(?:readonly|declare|typeset|export|local)(?=[\\\\t \\\\&;]|$))|((?![\\"']|\\\\\\\\\\\\n?$)[^\\\\t\\\\n\\\\r !\\"'<>]+?))(?:(?=[\\\\t ])|(?=[\\\\n\\\\&);\`{|}]|[\\\\t ]*#|])(?\`{|]+)"},{"begin":"(?:\\\\G|(?\\\\[{|]|$|[\\\\t ;])(?!nocorrect |nocorrect\\\\t|nocorrect$|readonly |readonly\\\\t|readonly$|function |function\\\\t|function$|foreach |foreach\\\\t|foreach$|coproc |coproc\\\\t|coproc$|logout |logout\\\\t|logout$|export |export\\\\t|export$|select |select\\\\t|select$|repeat |repeat\\\\t|repeat$|pushd |pushd\\\\t|pushd$|until |until\\\\t|until$|while |while\\\\t|while$|local |local\\\\t|local$|case |case\\\\t|case$|done |done\\\\t|done$|elif |elif\\\\t|elif$|else |else\\\\t|else$|esac |esac\\\\t|esac$|popd |popd\\\\t|popd$|then |then\\\\t|then$|time |time\\\\t|time$|for |for\\\\t|for$|end |end\\\\t|end$|fi |fi\\\\t|fi$|do |do\\\\t|do$|in |in\\\\t|in$|if |if\\\\t|if$)(?!\\\\\\\\\\\\n?$)","beginCaptures":{},"end":"(?=[\\\\n\\\\&);\`{|}]|[\\\\t ]*#|])(?]|&&|\\\\|\\\\|","name":"keyword.operator.logical.shell"},{"match":"(?[=>]?|==|!=|^|\\\\|{1,2}|&{1,2}|[,:=?]|[-%\\\\&*+/^|]=|<<=|>>=","name":"keyword.operator.arithmetic.shell"},{"match":"0[Xx]\\\\h+","name":"constant.numeric.hex.shell"},{"match":";","name":"punctuation.separator.semicolon.range"},{"match":"0\\\\d+","name":"constant.numeric.octal.shell"},{"match":"\\\\d{1,2}#[0-9@-Z_a-z]+","name":"constant.numeric.other.shell"},{"match":"\\\\d+","name":"constant.numeric.integer.shell"},{"match":"(?[=>]?|==|!=|^|\\\\|{1,2}|&{1,2}|[,:=?]|[-%\\\\&*+/^|]=|<<=|>>=","name":"keyword.operator.arithmetic.shell"},{"match":"0[Xx]\\\\h+","name":"constant.numeric.hex.shell"},{"match":"0\\\\d+","name":"constant.numeric.octal.shell"},{"match":"\\\\d{1,2}#[0-9@-Z_a-z]+","name":"constant.numeric.other.shell"},{"match":"\\\\d+","name":"constant.numeric.integer.shell"}]},"misc_ranges":{"patterns":[{"include":"#logical_expression_single"},{"include":"#logical_expression_double"},{"include":"#subshell_dollar"},{"begin":"(?\\\\[{|]|$|[\\\\t ;]))","beginCaptures":{"1":{"name":"string.unquoted.argument.shell constant.other.option.dash.shell"},"2":{"name":"string.unquoted.argument.shell constant.other.option.shell"}},"contentName":"string.unquoted.argument constant.other.option","end":"(?=[\\\\t ])|(?=[\\\\n\\\\&);\`{|}]|[\\\\t ]*#|])(?>?)[\\\\t ]*+([^\\\\t\\\\n \\"$\\\\&-);<>\\\\\\\\\`|]+)"},"redirect_number":{"captures":{"1":{"name":"keyword.operator.redirect.stdout.shell"},"2":{"name":"keyword.operator.redirect.stderr.shell"},"3":{"name":"keyword.operator.redirect.$3.shell"}},"match":"(?<=[\\\\t ])(?:(1)|(2)|(\\\\d+))(?=>)"},"redirection":{"patterns":[{"begin":"[<>]\\\\(","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.shell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.string.end.shell"}},"name":"string.interpolated.process-substitution.shell","patterns":[{"include":"#initial_context"}]},{"match":"(?])(&>|\\\\d*>&\\\\d*|\\\\d*(>>|[<>])|\\\\d*<&|\\\\d*<>)(?![<>])","name":"keyword.operator.redirect.shell"}]},"regex_comparison":{"match":"=~","name":"keyword.operator.logical.regex.shell"},"regexp":{"patterns":[{"match":".+"}]},"simple_options":{"captures":{"0":{"patterns":[{"captures":{"1":{"name":"string.unquoted.argument.shell constant.other.option.dash.shell"},"2":{"name":"string.unquoted.argument.shell constant.other.option.shell"}},"match":"[\\\\t ]++(-)(\\\\w+)"}]}},"match":"(?:[\\\\t ]++-\\\\w+)*"},"simple_unquoted":{"match":"[^\\\\t\\\\n \\"$\\\\&-);<>\\\\\\\\\`|]","name":"string.unquoted.shell"},"special_expansion":{"match":"!|:[-=?]?|[*@]|##?|%%|[%/]","name":"keyword.operator.expansion.shell"},"start_of_command":{"match":"[\\\\t ]*+(?![\\\\n!#\\\\&()<>\\\\[{|]|$|[\\\\t ;])(?!nocorrect |nocorrect\\\\t|nocorrect$|readonly |readonly\\\\t|readonly$|function |function\\\\t|function$|foreach |foreach\\\\t|foreach$|coproc |coproc\\\\t|coproc$|logout |logout\\\\t|logout$|export |export\\\\t|export$|select |select\\\\t|select$|repeat |repeat\\\\t|repeat$|pushd |pushd\\\\t|pushd$|until |until\\\\t|until$|while |while\\\\t|while$|local |local\\\\t|local$|case |case\\\\t|case$|done |done\\\\t|done$|elif |elif\\\\t|elif$|else |else\\\\t|else$|esac |esac\\\\t|esac$|popd |popd\\\\t|popd$|then |then\\\\t|then$|time |time\\\\t|time$|for |for\\\\t|for$|end |end\\\\t|end$|fi |fi\\\\t|fi$|do |do\\\\t|do$|in |in\\\\t|in$|if |if\\\\t|if$)(?!\\\\\\\\\\\\n?$)"},"string":{"patterns":[{"match":"\\\\\\\\.","name":"constant.character.escape.shell"},{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.shell"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.shell"}},"name":"string.quoted.single.shell"},{"begin":"\\\\$?\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.shell"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.shell"}},"name":"string.quoted.double.shell","patterns":[{"match":"\\\\\\\\[\\\\n\\"$\\\\\\\\\`]","name":"constant.character.escape.shell"},{"include":"#variable"},{"include":"#interpolation"}]},{"begin":"\\\\$'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.shell"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.shell"}},"name":"string.quoted.single.dollar.shell","patterns":[{"match":"\\\\\\\\['\\\\\\\\abefnrtv]","name":"constant.character.escape.ansi-c.shell"},{"match":"\\\\\\\\[0-9]{3}\\"","name":"constant.character.escape.octal.shell"},{"match":"\\\\\\\\x\\\\h{2}\\"","name":"constant.character.escape.hex.shell"},{"match":"\\\\\\\\c.\\"","name":"constant.character.escape.control-char.shell"}]}]},"subshell_dollar":{"patterns":[{"begin":"\\\\$\\\\(","beginCaptures":{"0":{"name":"punctuation.definition.subshell.single.shell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.subshell.single.shell"}},"name":"meta.scope.subshell","patterns":[{"include":"#parenthese"},{"include":"#initial_context"}]}]},"support":{"patterns":[{"match":"(?<=^|[\\\\&;\\\\s])[.:](?=[\\\\&;\\\\s]|$)","name":"support.function.builtin.shell"}]},"typical_statements":{"patterns":[{"include":"#assignment_statement"},{"include":"#case_statement"},{"include":"#for_statement"},{"include":"#while_statement"},{"include":"#function_definition"},{"include":"#command_statement"},{"include":"#line_continuation"},{"include":"#arithmetic_double"},{"include":"#normal_context"}]},"variable":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.variable.shell variable.parameter.positional.all.shell"},"2":{"name":"variable.parameter.positional.all.shell"}},"match":"(\\\\$)(@(?!\\\\w))"},{"captures":{"1":{"name":"punctuation.definition.variable.shell variable.parameter.positional.shell"},"2":{"name":"variable.parameter.positional.shell"}},"match":"(\\\\$)([0-9](?!\\\\w))"},{"captures":{"1":{"name":"punctuation.definition.variable.shell variable.language.special.shell"},"2":{"name":"variable.language.special.shell"}},"match":"(\\\\$)([-!#$*0?_](?!\\\\w))"},{"begin":"(\\\\$)(\\\\{)[\\\\t ]*+(?=\\\\d)","beginCaptures":{"1":{"name":"punctuation.definition.variable.shell variable.parameter.positional.shell"},"2":{"name":"punctuation.section.bracket.curly.variable.begin.shell punctuation.definition.variable.shell variable.parameter.positional.shell"}},"contentName":"meta.parameter-expansion","end":"}","endCaptures":{"0":{"name":"punctuation.section.bracket.curly.variable.end.shell punctuation.definition.variable.shell variable.parameter.positional.shell"}},"patterns":[{"include":"#special_expansion"},{"include":"#array_access_inline"},{"match":"[0-9]+","name":"variable.parameter.positional.shell"},{"match":"(?>>","name":"invalid.deprecated.combinator.css"},{"match":">>|[+>~]","name":"keyword.operator.combinator.css"}]},"commas":{"match":",","name":"punctuation.separator.list.comma.css"},"comment-block":{"begin":"/\\\\*","beginCaptures":{"0":{"name":"punctuation.definition.comment.begin.css"}},"end":"\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.end.css"}},"name":"comment.block.css"},"escapes":{"patterns":[{"match":"\\\\\\\\\\\\h{1,6}","name":"constant.character.escape.codepoint.css"},{"begin":"\\\\\\\\$\\\\s*","end":"^(?]|/\\\\*)"},"media-query":{"begin":"\\\\G","end":"(?=\\\\s*[;{])","patterns":[{"include":"#comment-block"},{"include":"#escapes"},{"include":"#media-types"},{"match":"(?i)(?<=\\\\s|^|,|\\\\*/)(only|not)(?=[{\\\\s]|/\\\\*|$)","name":"keyword.operator.logical.$1.media.css"},{"match":"(?i)(?<=\\\\s|^|\\\\*/|\\\\))and(?=\\\\s|/\\\\*|$)","name":"keyword.operator.logical.and.media.css"},{"match":",(?:(?:\\\\s*,)+|(?=\\\\s*[);{]))","name":"invalid.illegal.comma.css"},{"include":"#commas"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.definition.parameters.begin.bracket.round.css"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.bracket.round.css"}},"patterns":[{"include":"#media-features"},{"include":"#media-feature-keywords"},{"match":":","name":"punctuation.separator.key-value.css"},{"match":">=|<=|[<=>]","name":"keyword.operator.comparison.css"},{"captures":{"1":{"name":"constant.numeric.css"},"2":{"name":"keyword.operator.arithmetic.css"},"3":{"name":"constant.numeric.css"}},"match":"(\\\\d+)\\\\s*(/)\\\\s*(\\\\d+)","name":"meta.ratio.css"},{"include":"#numeric-values"},{"include":"#comment-block"}]}]},"media-query-list":{"begin":"(?=\\\\s*[^;{])","end":"(?=\\\\s*[;{])","patterns":[{"include":"#media-query"}]},"media-types":{"captures":{"1":{"name":"support.constant.media.css"},"2":{"name":"invalid.deprecated.constant.media.css"}},"match":"(?i)(?<=^|[,\\\\s]|\\\\*/)(?:(all|print|screen|speech)|(aural|braille|embossed|handheld|projection|tty|tv))(?=$|[,;{\\\\s]|/\\\\*)"},"numeric-values":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.constant.css"}},"match":"(#)(?:\\\\h{3,4}|\\\\h{6}|\\\\h{8})\\\\b","name":"constant.other.color.rgb-value.hex.css"},{"captures":{"1":{"name":"keyword.other.unit.percentage.css"},"2":{"name":"keyword.other.unit.\${2:/downcase}.css"}},"match":"(?i)(?\\\\[{|~\\\\s]|/\\\\*)|(?:[-0-9A-Z_a-z[^\\\\x00-\\\\x7F]]|\\\\\\\\(?:\\\\h{1,6}|.))*(?:[]!\\"%-(*;\\\\[{|~\\\\s]|/\\\\*)","name":"entity.other.attribute-name.class.css"},{"captures":{"1":{"name":"punctuation.definition.entity.css"},"2":{"patterns":[{"include":"#escapes"}]}},"match":"(#)(-?(?![0-9])(?:[-0-9A-Z_a-z[^\\\\x00-\\\\x7F]]|\\\\\\\\(?:\\\\h{1,6}|.))+)(?=$|[#)+,.:>\\\\[{|~\\\\s]|/\\\\*)","name":"entity.other.attribute-name.id.css"},{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.entity.begin.bracket.square.css"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.entity.end.bracket.square.css"}},"name":"meta.attribute-selector.css","patterns":[{"include":"#comment-block"},{"include":"#string"},{"captures":{"1":{"name":"storage.modifier.ignore-case.css"}},"match":"(?<=[\\"'\\\\s]|^|\\\\*/)\\\\s*([Ii])\\\\s*(?=[]\\\\s]|/\\\\*|$)"},{"captures":{"1":{"name":"string.unquoted.attribute-value.css","patterns":[{"include":"#escapes"}]}},"match":"(?<==)\\\\s*((?!/\\\\*)(?:[^]\\"'\\\\\\\\\\\\s]|\\\\\\\\.)+)"},{"include":"#escapes"},{"match":"[$*^|~]?=","name":"keyword.operator.pattern.css"},{"match":"\\\\|","name":"punctuation.separator.css"},{"captures":{"1":{"name":"entity.other.namespace-prefix.css","patterns":[{"include":"#escapes"}]}},"match":"(-?(?!\\\\d)(?:[-\\\\w[^\\\\x00-\\\\x7F]]|\\\\\\\\(?:\\\\h{1,6}|.))+|\\\\*)(?=\\\\|(?![=\\\\s]|$|])(?:-?(?!\\\\d)|[-\\\\\\\\\\\\w[^\\\\x00-\\\\x7F]]))"},{"captures":{"1":{"name":"entity.other.attribute-name.css","patterns":[{"include":"#escapes"}]}},"match":"(-?(?!\\\\d)(?>[-\\\\w[^\\\\x00-\\\\x7F]]|\\\\\\\\(?:\\\\h{1,6}|.))+)\\\\s*(?=[]$*=^|~]|/\\\\*)"}]},{"include":"#pseudo-classes"},{"include":"#pseudo-elements"},{"include":"#functional-pseudo-classes"},{"match":"(?\\\\[{|~\\\\s]|/\\\\*|$)","name":"entity.name.tag.css"},"unicode-range":{"captures":{"0":{"name":"constant.other.unicode-range.css"},"1":{"name":"punctuation.separator.dash.unicode-range.css"}},"match":"(??\\\\[]|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^yield|[^$._[:alnum:]]yield|^throw|[^$._[:alnum:]]throw|^in|[^$._[:alnum:]]in|^of|[^$._[:alnum:]]of|^typeof|[^$._[:alnum:]]typeof|&&|\\\\|\\\\||\\\\*)\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"punctuation.definition.block.js"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.js"}},"name":"meta.objectliteral.js","patterns":[{"include":"#object-member"}]},"array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.js"},"2":{"name":"punctuation.definition.binding-pattern.array.js"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.js"}},"patterns":[{"include":"#binding-element"},{"include":"#punctuation-comma"}]},"array-binding-pattern-const":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.js"},"2":{"name":"punctuation.definition.binding-pattern.array.js"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.js"}},"patterns":[{"include":"#binding-element-const"},{"include":"#punctuation-comma"}]},"array-literal":{"begin":"\\\\s*(\\\\[)","beginCaptures":{"1":{"name":"meta.brace.square.js"}},"end":"]","endCaptures":{"0":{"name":"meta.brace.square.js"}},"name":"meta.array.literal.js","patterns":[{"include":"#expression"},{"include":"#punctuation-comma"}]},"arrow-function":{"patterns":[{"captures":{"1":{"name":"storage.modifier.async.js"},"2":{"name":"variable.parameter.js"}},"match":"(?:(?)","name":"meta.arrow.js"},{"begin":"(?:(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))","beginCaptures":{"1":{"name":"storage.modifier.async.js"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.arrow.js","patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"include":"#function-parameters"},{"include":"#arrow-return-type"},{"include":"#possibly-arrow-return-type"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.js"}},"end":"((?<=[}\\\\S])(?)|((?!\\\\{)(?=\\\\S)))(?!/[*/])","name":"meta.arrow.js","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#decl-block"},{"include":"#expression"}]}]},"arrow-return-type":{"begin":"(?<=\\\\))\\\\s*(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.js"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.return.type.arrow.js","patterns":[{"include":"#arrow-return-type-body"}]},"arrow-return-type-body":{"patterns":[{"begin":"(?<=:)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"async-modifier":{"match":"(?\\\\s*$)","beginCaptures":{"1":{"name":"punctuation.definition.comment.js"}},"end":"(?=$)","name":"comment.line.triple-slash.directive.js","patterns":[{"begin":"(<)(reference|amd-dependency|amd-module)","beginCaptures":{"1":{"name":"punctuation.definition.tag.directive.js"},"2":{"name":"entity.name.tag.directive.js"}},"end":"/>","endCaptures":{"0":{"name":"punctuation.definition.tag.directive.js"}},"name":"meta.tag.js","patterns":[{"match":"path|types|no-default-lib|lib|name|resolution-mode","name":"entity.other.attribute-name.directive.js"},{"match":"=","name":"keyword.operator.assignment.js"},{"include":"#string"}]}]},"docblock":{"patterns":[{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.access-type.jsdoc"}},"match":"((@)a(?:ccess|pi))\\\\s+(p(?:rivate|rotected|ublic))\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"5":{"name":"constant.other.email.link.underline.jsdoc"},"6":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"match":"((@)author)\\\\s+([^*/<>@\\\\s](?:[^*/<>@]|\\\\*[^/])*)(?:\\\\s*(<)([^>\\\\s]+)(>))?"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"keyword.operator.control.jsdoc"},"5":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)borrows)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)\\\\s+(as)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)"},{"begin":"((@)example)\\\\s+","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=@|\\\\*/)","name":"meta.example.jsdoc","patterns":[{"match":"^\\\\s\\\\*\\\\s+"},{"begin":"\\\\G(<)caption(>)","beginCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"contentName":"constant.other.description.jsdoc","end":"()|(?=\\\\*/)","endCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}}},{"captures":{"0":{"name":"source.embedded.js"}},"match":"[^*@\\\\s](?:[^*]|\\\\*[^/])*"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.symbol-type.jsdoc"}},"match":"((@)kind)\\\\s+(class|constant|event|external|file|function|member|mixin|module|namespace|typedef)\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.link.underline.jsdoc"},"4":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)see)\\\\s+(?:((?=https?://)(?:[^*\\\\s]|\\\\*[^/])+)|((?!https?://|(?:\\\\[[^]\\\\[]*])?\\\\{@(?:link|linkcode|linkplain|tutorial)\\\\b)(?:[^*/@\\\\s]|\\\\*[^/])+))"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)template)\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*(?:\\\\s*,\\\\s*[$A-Z_a-z][]$.\\\\[\\\\w]*)*)"},{"begin":"((@)template)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:arg|argument|const|constant|member|namespace|param|var))\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*)"},{"begin":"((@)typedef)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"(?:[^*/@\\\\s]|\\\\*[^/])+","name":"entity.name.type.instance.jsdoc"}]},{"begin":"((@)(?:arg|argument|const|constant|member|namespace|param|prop|property|var))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"},{"captures":{"1":{"name":"punctuation.definition.optional-value.begin.bracket.square.jsdoc"},"2":{"name":"keyword.operator.assignment.jsdoc"},"3":{"name":"source.embedded.js"},"4":{"name":"punctuation.definition.optional-value.end.bracket.square.jsdoc"},"5":{"name":"invalid.illegal.syntax.jsdoc"}},"match":"(\\\\[)\\\\s*[$\\\\w]+(?:(?:\\\\[])?\\\\.[$\\\\w]+)*(?:\\\\s*(=)\\\\s*((?>\\"(?:\\\\*(?!/)|\\\\\\\\(?!\\")|[^*\\\\\\\\])*?\\"|'(?:\\\\*(?!/)|\\\\\\\\(?!')|[^*\\\\\\\\])*?'|\\\\[(?:\\\\*(?!/)|[^*])*?]|(?:\\\\*(?!/)|\\\\s(?!\\\\s*])|\\\\[.*?(?:]|(?=\\\\*/))|[^]*\\\\[\\\\s])*)*))?\\\\s*(?:(])((?:[^*\\\\s]|\\\\*[^/\\\\s])+)?|(?=\\\\*/))","name":"variable.other.jsdoc"}]},{"begin":"((@)(?:define|enum|exception|export|extends|lends|implements|modifies|namespace|private|protected|returns?|satisfies|suppress|this|throws|type|yields?))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)(?:alias|augments|callback|constructs|emits|event|fires|exports?|extends|external|function|func|host|lends|listens|interface|memberof!?|method|module|mixes|mixin|name|requires|see|this|typedef|uses))\\\\s+((?:[^*@{}\\\\s]|\\\\*[^/])+)"},{"begin":"((@)(?:default(?:value)?|license|version))\\\\s+(([\\"']))","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"},"4":{"name":"punctuation.definition.string.begin.jsdoc"}},"contentName":"variable.other.jsdoc","end":"(\\\\3)|(?=$|\\\\*/)","endCaptures":{"0":{"name":"variable.other.jsdoc"},"1":{"name":"punctuation.definition.string.end.jsdoc"}}},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:default(?:value)?|license|tutorial|variation|version))\\\\s+([^*\\\\s]+)"},{"captures":{"1":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"(@)(?:abstract|access|alias|api|arg|argument|async|attribute|augments|author|beta|borrows|bubbles|callback|chainable|class|classdesc|code|config|const|constant|constructor|constructs|copyright|default|defaultvalue|define|deprecated|desc|description|dict|emits|enum|event|example|exception|exports?|extends|extension(?:_?for)?|external|externs|file|fileoverview|final|fires|for|func|function|generator|global|hideconstructor|host|ignore|implements|implicitCast|inherit[Dd]oc|inner|instance|interface|internal|kind|lends|license|listens|main|member|memberof!?|method|mixes|mixins?|modifies|module|name|namespace|noalias|nocollapse|nocompile|nosideeffects|override|overview|package|param|polymer(?:Behavior)?|preserve|private|prop|property|protected|public|read[Oo]nly|record|require[ds]|returns?|see|since|static|struct|submodule|summary|suppress|template|this|throws|todo|tutorial|type|typedef|unrestricted|uses|var|variation|version|virtual|writeOnce|yields?)\\\\b","name":"storage.type.class.jsdoc"},{"include":"#inline-tags"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"((@)[$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s+)"}]},"enum-declaration":{"begin":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.js"},"2":{"name":"keyword.operator.rest.js"},"3":{"name":"variable.parameter.js variable.language.this.js"},"4":{"name":"variable.parameter.js"},"5":{"name":"keyword.operator.optional.js"}},"match":"(?:(??}]|\\\\|\\\\||&&|!==|$|((?>>??|\\\\|)=","name":"keyword.operator.assignment.compound.bitwise.js"},{"match":"<<|>>>?","name":"keyword.operator.bitwise.shift.js"},{"match":"[!=]==?","name":"keyword.operator.comparison.js"},{"match":"<=|>=|<>|[<>]","name":"keyword.operator.relational.js"},{"captures":{"1":{"name":"keyword.operator.logical.js"},"2":{"name":"keyword.operator.assignment.compound.js"},"3":{"name":"keyword.operator.arithmetic.js"}},"match":"(?<=[$_[:alnum:]])(!)\\\\s*(?:(/=)|(/)(?![*/]))"},{"match":"!|&&|\\\\|\\\\||\\\\?\\\\?","name":"keyword.operator.logical.js"},{"match":"[\\\\&^|~]","name":"keyword.operator.bitwise.js"},{"match":"=","name":"keyword.operator.assignment.js"},{"match":"--","name":"keyword.operator.decrement.js"},{"match":"\\\\+\\\\+","name":"keyword.operator.increment.js"},{"match":"[-%*+/]","name":"keyword.operator.arithmetic.js"},{"begin":"(?<=[]$)_[:alnum:]])\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)+(?:(/=)|(/)(?![*/])))","end":"(/=)|(/)(?!\\\\*([^*]|(\\\\*[^/]))*\\\\*/)","endCaptures":{"1":{"name":"keyword.operator.assignment.compound.js"},"2":{"name":"keyword.operator.arithmetic.js"}},"patterns":[{"include":"#comment"}]},{"captures":{"1":{"name":"keyword.operator.assignment.compound.js"},"2":{"name":"keyword.operator.arithmetic.js"}},"match":"(?<=[]$)_[:alnum:]])\\\\s*(?:(/=)|(/)(?![*/]))"}]},"expressionPunctuations":{"patterns":[{"include":"#punctuation-comma"},{"include":"#punctuation-accessor"}]},"expressionWithoutIdentifiers":{"patterns":[{"include":"#jsx"},{"include":"#string"},{"include":"#regex"},{"include":"#comment"},{"include":"#function-expression"},{"include":"#class-expression"},{"include":"#arrow-function"},{"include":"#paren-expression-possibly-arrow"},{"include":"#cast"},{"include":"#ternary-expression"},{"include":"#new-expr"},{"include":"#instanceof-expr"},{"include":"#object-literal"},{"include":"#expression-operators"},{"include":"#function-call"},{"include":"#literal"},{"include":"#support-objects"},{"include":"#paren-expression"}]},"field-declaration":{"begin":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"match":"#?[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.property.js variable.object.property.js"},{"match":"\\\\?","name":"keyword.operator.optional.js"},{"match":"!","name":"keyword.operator.definiteassignment.js"}]},"for-loop":{"begin":"(?\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\\\\())","end":"(?<=\\\\))(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\\\\())","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\\\\())","name":"meta.function-call.js","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"},{"include":"#paren-expression"}]},{"begin":"(?=(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","end":"(?<=>)(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*[(\\\\[{]\\\\s*)$)","name":"meta.function-call.js","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"}]}]},"function-call-optionals":{"patterns":[{"match":"\\\\?\\\\.","name":"meta.function-call.js punctuation.accessor.optional.js"},{"match":"!","name":"meta.function-call.js keyword.operator.definiteassignment.js"}]},"function-call-target":{"patterns":[{"include":"#support-function-call-identifiers"},{"match":"(#?[$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.js"}]},"function-declaration":{"begin":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))"},{"captures":{"1":{"name":"punctuation.accessor.js"},"2":{"name":"punctuation.accessor.optional.js"},"3":{"name":"variable.other.constant.property.js"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])"},{"captures":{"1":{"name":"punctuation.accessor.js"},"2":{"name":"punctuation.accessor.optional.js"},"3":{"name":"variable.other.property.js"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*)"},{"match":"(\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])","name":"variable.other.constant.js"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"variable.other.readwrite.js"}]},"if-statement":{"patterns":[{"begin":"(??}]|\\\\|\\\\||&&|!==|$|([!=]==?)|(([\\\\&^|~]\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s+instanceof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|((?))","end":"(/>)|()","endCaptures":{"1":{"name":"punctuation.definition.tag.end.js"},"2":{"name":"punctuation.definition.tag.begin.js"},"3":{"name":"entity.name.tag.namespace.js"},"4":{"name":"punctuation.separator.namespace.js"},"5":{"name":"entity.name.tag.js"},"6":{"name":"support.class.component.js"},"7":{"name":"punctuation.definition.tag.end.js"}},"name":"meta.tag.js","patterns":[{"begin":"(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.js"},"2":{"name":"entity.name.tag.namespace.js"},"3":{"name":"punctuation.separator.namespace.js"},"4":{"name":"entity.name.tag.js"},"5":{"name":"support.class.component.js"}},"end":"(?=/?>)","patterns":[{"include":"#comment"},{"include":"#type-arguments"},{"include":"#jsx-tag-attributes"}]},{"begin":"(>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.end.js"}},"contentName":"meta.jsx.children.js","end":"(?=|/\\\\*|//)"},"jsx-tag-attributes":{"begin":"\\\\s+","end":"(?=/?>)","name":"meta.tag.attributes.js","patterns":[{"include":"#comment"},{"include":"#jsx-tag-attribute-name"},{"include":"#jsx-tag-attribute-assignment"},{"include":"#jsx-string-double-quoted"},{"include":"#jsx-string-single-quoted"},{"include":"#jsx-evaluated-code"},{"include":"#jsx-tag-attributes-illegal"}]},"jsx-tag-attributes-illegal":{"match":"\\\\S+","name":"invalid.illegal.attribute.js"},"jsx-tag-in-expression":{"begin":"(??\\\\[{]|&&|\\\\|\\\\||\\\\?|\\\\*/|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^default|[^$._[:alnum:]]default|^yield|[^$._[:alnum:]]yield|^)\\\\s*(?!<\\\\s*[$_[:alpha:]][$_[:alnum:]]*((\\\\s+extends\\\\s+[^=>])|,))(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","end":"(?!(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","patterns":[{"include":"#jsx-tag"}]},"jsx-tag-without-attributes":{"begin":"(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.js"},"2":{"name":"entity.name.tag.namespace.js"},"3":{"name":"punctuation.separator.namespace.js"},"4":{"name":"entity.name.tag.js"},"5":{"name":"support.class.component.js"},"6":{"name":"punctuation.definition.tag.end.js"}},"contentName":"meta.jsx.children.js","end":"()","endCaptures":{"1":{"name":"punctuation.definition.tag.begin.js"},"2":{"name":"entity.name.tag.namespace.js"},"3":{"name":"punctuation.separator.namespace.js"},"4":{"name":"entity.name.tag.js"},"5":{"name":"support.class.component.js"},"6":{"name":"punctuation.definition.tag.end.js"}},"name":"meta.tag.without-attributes.js","patterns":[{"include":"#jsx-children"}]},"jsx-tag-without-attributes-in-expression":{"begin":"(??\\\\[{]|&&|\\\\|\\\\||\\\\?|\\\\*/|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^default|[^$._[:alnum:]]default|^yield|[^$._[:alnum:]]yield|^)\\\\s*(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","end":"(?!(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","patterns":[{"include":"#jsx-tag-without-attributes"}]},"label":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)(?=\\\\s*\\\\{)","beginCaptures":{"1":{"name":"entity.name.label.js"},"2":{"name":"punctuation.separator.label.js"}},"end":"(?<=})","patterns":[{"include":"#decl-block"}]},{"captures":{"1":{"name":"entity.name.label.js"},"2":{"name":"punctuation.separator.label.js"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)"}]},"literal":{"patterns":[{"include":"#numeric-literal"},{"include":"#boolean-literal"},{"include":"#null-literal"},{"include":"#undefined-literal"},{"include":"#numericConstant-literal"},{"include":"#array-literal"},{"include":"#this-literal"},{"include":"#super-literal"}]},"method-declaration":{"patterns":[{"begin":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.js"},"2":{"name":"storage.modifier.js"},"3":{"name":"storage.modifier.js"},"4":{"name":"storage.modifier.async.js"},"5":{"name":"keyword.operator.new.js"},"6":{"name":"keyword.generator.asterisk.js"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.js","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]},{"begin":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.js"},"2":{"name":"storage.modifier.js"},"3":{"name":"storage.modifier.js"},"4":{"name":"storage.modifier.async.js"},"5":{"name":"storage.type.property.js"},"6":{"name":"keyword.generator.asterisk.js"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.js","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]}]},"method-declaration-name":{"begin":"(?=(\\\\b((??}]|\\\\|\\\\||&&|!==|$|((?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.js"},"2":{"name":"storage.type.property.js"},"3":{"name":"keyword.generator.asterisk.js"}},"end":"(?=[,;}])|(?<=})","name":"meta.method.declaration.js","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"},{"begin":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.js"},"2":{"name":"storage.type.property.js"},"3":{"name":"keyword.generator.asterisk.js"}},"end":"(?=[(<])","patterns":[{"include":"#method-declaration-name"}]}]},"object-member":{"patterns":[{"include":"#comment"},{"include":"#object-literal-method-declaration"},{"begin":"(?=\\\\[)","end":"(?=:)|((?<=])(?=\\\\s*[(<]))","name":"meta.object.member.js meta.object-literal.key.js","patterns":[{"include":"#comment"},{"include":"#array-literal"}]},{"begin":"(?=[\\"'\`])","end":"(?=:)|((?<=[\\"'\`])(?=((\\\\s*[(,<}])|(\\\\s+(as|satisifies)\\\\s+))))","name":"meta.object.member.js meta.object-literal.key.js","patterns":[{"include":"#comment"},{"include":"#string"}]},{"begin":"(?=\\\\b((?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))","name":"meta.object.member.js"},{"captures":{"0":{"name":"meta.object-literal.key.js"}},"match":"[$_[:alpha:]][$_[:alnum:]]*\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object.member.js"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.js"}},"end":"(?=[,}])","name":"meta.object.member.js","patterns":[{"include":"#expression"}]},{"captures":{"1":{"name":"variable.other.readwrite.js"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=[,}]|$|//|/\\\\*)","name":"meta.object.member.js"},{"captures":{"1":{"name":"keyword.control.as.js"},"2":{"name":"storage.modifier.js"}},"match":"(??}]|\\\\|\\\\||&&|!==|$|^|((?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.js"}},"end":"(?<=\\\\))","patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.js"},"2":{"name":"meta.brace.round.js"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(?=<\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.js"}},"end":"(?<=>)","patterns":[{"include":"#type-parameters"}]},{"begin":"(?<=>)\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"meta.brace.round.js"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"include":"#possibly-arrow-return-type"},{"include":"#expression"}]},{"include":"#punctuation-comma"},{"include":"#decl-block"}]},"parameter-array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.js"},"2":{"name":"punctuation.definition.binding-pattern.array.js"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.js"}},"patterns":[{"include":"#parameter-binding-element"},{"include":"#punctuation-comma"}]},"parameter-binding-element":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#parameter-object-binding-pattern"},{"include":"#parameter-array-binding-pattern"},{"include":"#destructuring-parameter-rest"},{"include":"#variable-initializer"}]},"parameter-name":{"patterns":[{"captures":{"1":{"name":"storage.modifier.js"}},"match":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.js"},"2":{"name":"keyword.operator.rest.js"},"3":{"name":"variable.parameter.js variable.language.this.js"},"4":{"name":"variable.parameter.js"},"5":{"name":"keyword.operator.optional.js"}},"match":"(?:(?])","name":"meta.type.annotation.js","patterns":[{"include":"#type"}]}]},"paren-expression":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js"}},"patterns":[{"include":"#expression"}]},"paren-expression-possibly-arrow":{"patterns":[{"begin":"(?<=[(,=])\\\\s*(async)?(?=\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.js"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"begin":"(?<=[(,=]|=>|^return|[^$._[:alnum:]]return)\\\\s*(async)?(?=\\\\s*((((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()|(<)|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)))\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.js"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"include":"#possibly-arrow-return-type"}]},"paren-expression-possibly-arrow-with-typeparameters":{"patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},"possibly-arrow-return-type":{"begin":"(?<=\\\\)|^)\\\\s*(:)(?=\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*=>)","beginCaptures":{"1":{"name":"meta.arrow.js meta.return.type.arrow.js keyword.operator.type.annotation.js"}},"contentName":"meta.arrow.js meta.return.type.arrow.js","end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","patterns":[{"include":"#arrow-return-type-body"}]},"property-accessor":{"match":"(?|&&|\\\\|\\\\||\\\\*/)\\\\s*(/)(?![*/])(?=(?:[^()/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)+]|\\\\(([^)\\\\\\\\]|\\\\\\\\.)+\\\\))+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.js"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.js"},"2":{"name":"keyword.other.js"}},"name":"string.regexp.js","patterns":[{"include":"#regexp"}]},{"begin":"((?)"},{"match":"[*+?]|\\\\{(\\\\d+,\\\\d+|\\\\d+,|,\\\\d+|\\\\d+)}\\\\??","name":"keyword.operator.quantifier.regexp"},{"match":"\\\\|","name":"keyword.operator.or.regexp"},{"begin":"(\\\\()((\\\\?=)|(\\\\?!)|(\\\\?<=)|(\\\\?)?","beginCaptures":{"0":{"name":"punctuation.definition.group.regexp"},"1":{"name":"punctuation.definition.group.no-capture.regexp"},"2":{"name":"variable.other.regexp"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.group.regexp"}},"name":"meta.group.regexp","patterns":[{"include":"#regexp"}]},{"begin":"(\\\\[)(\\\\^)?","beginCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"},"2":{"name":"keyword.operator.negation.regexp"}},"end":"(])","endCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"}},"name":"constant.other.character-class.set.regexp","patterns":[{"captures":{"1":{"name":"constant.character.numeric.regexp"},"2":{"name":"constant.character.control.regexp"},"3":{"name":"constant.character.escape.backslash.regexp"},"4":{"name":"constant.character.numeric.regexp"},"5":{"name":"constant.character.control.regexp"},"6":{"name":"constant.character.escape.backslash.regexp"}},"match":"(?:.|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))-(?:[^]\\\\\\\\]|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))","name":"constant.other.character-class.range.regexp"},{"include":"#regex-character-class"}]},{"include":"#regex-character-class"}]},"return-type":{"patterns":[{"begin":"(?<=\\\\))\\\\s*(:)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.js"}},"end":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\()|(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\\\b(?!\\\\$))"},{"captures":{"1":{"name":"support.type.object.module.js"},"2":{"name":"support.type.object.module.js"},"3":{"name":"punctuation.accessor.js"},"4":{"name":"punctuation.accessor.optional.js"},"5":{"name":"support.type.object.module.js"}},"match":"(?\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\`)","end":"(?=\`)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*\\\\s*\\\\??\\\\.\\\\s*)*|(\\\\??\\\\.\\\\s*)?)([$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\`)","patterns":[{"include":"#support-function-call-identifiers"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.tagged-template.js"}]},{"include":"#type-arguments"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?\\\\s*(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.js"}},"end":"(?=\`)","patterns":[{"include":"#type-arguments"}]}]},"template-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.js"}},"contentName":"meta.embedded.line.js","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.js"}},"name":"meta.template.expression.js","patterns":[{"include":"#expression"}]},"template-type":{"patterns":[{"include":"#template-call"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?(\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.js"},"2":{"name":"string.template.js punctuation.definition.string.template.begin.js"}},"contentName":"string.template.js","end":"\`","endCaptures":{"0":{"name":"string.template.js punctuation.definition.string.template.end.js"}},"patterns":[{"include":"#template-type-substitution-element"},{"include":"#string-character-escape"}]}]},"template-type-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.js"}},"contentName":"meta.embedded.line.js","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.js"}},"name":"meta.template.expression.js","patterns":[{"include":"#type"}]},"ternary-expression":{"begin":"(?!\\\\?\\\\.\\\\s*\\\\D)(\\\\?)(?!\\\\?)","beginCaptures":{"1":{"name":"keyword.operator.ternary.js"}},"end":"\\\\s*(:)","endCaptures":{"1":{"name":"keyword.operator.ternary.js"}},"patterns":[{"include":"#expression"}]},"this-literal":{"match":"(?])|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.js","patterns":[{"include":"#type"}]},{"begin":"(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.js"}},"end":"(?])|(?=^\\\\s*$)|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.js","patterns":[{"include":"#type"}]}]},"type-arguments":{"begin":"<","beginCaptures":{"0":{"name":"punctuation.definition.typeparameters.begin.js"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.typeparameters.end.js"}},"name":"meta.type.parameters.js","patterns":[{"include":"#type-arguments-body"}]},"type-arguments-body":{"patterns":[{"captures":{"0":{"name":"keyword.operator.type.js"}},"match":"(?)","patterns":[{"include":"#comment"},{"include":"#type-parameters"}]},{"begin":"(?))))))","end":"(?<=\\\\))","name":"meta.type.function.js","patterns":[{"include":"#function-parameters"}]}]},"type-function-return-type":{"patterns":[{"begin":"(=>)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"storage.type.function.arrow.js"}},"end":"(?)(??{}]|//|$)","name":"meta.type.function.return.js","patterns":[{"include":"#type-function-return-type-core"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.js"}},"end":"(?)(??{}]|//|^\\\\s*$)|((?<=\\\\S)(?=\\\\s*$)))","name":"meta.type.function.return.js","patterns":[{"include":"#type-function-return-type-core"}]}]},"type-function-return-type-core":{"patterns":[{"include":"#comment"},{"begin":"(?<==>)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"type-infer":{"patterns":[{"captures":{"1":{"name":"keyword.operator.expression.infer.js"},"2":{"name":"entity.name.type.js"},"3":{"name":"keyword.operator.expression.extends.js"}},"match":"(?)","endCaptures":{"1":{"name":"meta.type.parameters.js punctuation.definition.typeparameters.end.js"}},"patterns":[{"include":"#type-arguments-body"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(<)","beginCaptures":{"1":{"name":"entity.name.type.js"},"2":{"name":"meta.type.parameters.js punctuation.definition.typeparameters.begin.js"}},"contentName":"meta.type.parameters.js","end":"(>)","endCaptures":{"1":{"name":"meta.type.parameters.js punctuation.definition.typeparameters.end.js"}},"patterns":[{"include":"#type-arguments-body"}]},{"captures":{"1":{"name":"entity.name.type.module.js"},"2":{"name":"punctuation.accessor.js"},"3":{"name":"punctuation.accessor.optional.js"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"entity.name.type.js"}]},"type-object":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.js"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.js"}},"name":"meta.object.type.js","patterns":[{"include":"#comment"},{"include":"#method-declaration"},{"include":"#indexer-declaration"},{"include":"#indexer-mapped-type-declaration"},{"include":"#field-declaration"},{"include":"#type-annotation"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.js"}},"end":"(?=[,;}]|$)|(?<=})","patterns":[{"include":"#type"}]},{"include":"#punctuation-comma"},{"include":"#punctuation-semicolon"},{"include":"#type"}]},"type-operators":{"patterns":[{"include":"#typeof-operator"},{"include":"#type-infer"},{"begin":"([\\\\&|])(?=\\\\s*\\\\{)","beginCaptures":{"0":{"name":"keyword.operator.type.js"}},"end":"(?<=})","patterns":[{"include":"#type-object"}]},{"begin":"[\\\\&|]","beginCaptures":{"0":{"name":"keyword.operator.type.js"}},"end":"(?=\\\\S)"},{"match":"(?)","endCaptures":{"1":{"name":"punctuation.definition.typeparameters.end.js"}},"name":"meta.type.parameters.js","patterns":[{"include":"#comment"},{"match":"(?)","name":"keyword.operator.assignment.js"}]},"type-paren-or-function-parameters":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js"}},"name":"meta.type.paren.cover.js","patterns":[{"captures":{"1":{"name":"storage.modifier.js"},"2":{"name":"keyword.operator.rest.js"},"3":{"name":"entity.name.function.js variable.language.this.js"},"4":{"name":"entity.name.function.js"},"5":{"name":"keyword.operator.optional.js"}},"match":"(?:(?)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))))"},{"captures":{"1":{"name":"storage.modifier.js"},"2":{"name":"keyword.operator.rest.js"},"3":{"name":"variable.parameter.js variable.language.this.js"},"4":{"name":"variable.parameter.js"},"5":{"name":"keyword.operator.optional.js"}},"match":"(?:(??{|}]|(extends\\\\s+)|$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type-arguments"},{"include":"#expression"}]},"undefined-literal":{"match":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.js variable.other.constant.js entity.name.function.js"}},"end":"(?=$|^|[,;=}]|((?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.js entity.name.function.js"},"2":{"name":"keyword.operator.definiteassignment.js"}},"end":"(?=$|^|[,;=}]|((?\\\\s*$)","beginCaptures":{"1":{"name":"keyword.operator.assignment.js"}},"end":"(?=$|^|[]),;}]|((?)","patterns":[{"begin":"(?=[^/<=>\`\\\\s]|/(?!>))","end":"(?!\\\\G)","name":"meta.embedded.line.css","patterns":[{"captures":{"0":{"name":"source.css"}},"match":"([^\\"'/<=>\`\\\\s]|/(?!>))+","name":"string.unquoted.html"},{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.html"}},"contentName":"source.css","end":"(\\")","endCaptures":{"0":{"name":"punctuation.definition.string.end.html"},"1":{"name":"source.css"}},"name":"string.quoted.double.html","patterns":[{"include":"#entities"}]},{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.html"}},"contentName":"source.css","end":"(')","endCaptures":{"0":{"name":"punctuation.definition.string.end.html"},"1":{"name":"source.css"}},"name":"string.quoted.single.html","patterns":[{"include":"#entities"}]}]},{"match":"=","name":"invalid.illegal.unexpected-equals-sign.html"}]}]},{"begin":"on(s(croll|t(orage|alled)|u(spend|bmit)|e(curitypolicyviolation|ek(ing|ed)|lect))|hashchange|c(hange|o(ntextmenu|py)|u(t|echange)|l(ick|ose)|an(cel|play(through)?))|t(imeupdate|oggle)|in(put|valid)|o((?:n|ff)line)|d(urationchange|r(op|ag(start|over|e(n(ter|d)|xit)|leave)?)|blclick)|un(handledrejection|load)|p(opstate|lay(ing)?|a(ste|use|ge(show|hide))|rogress)|e(nded|rror|mptied)|volumechange|key(down|up|press)|focus|w(heel|aiting)|l(oad(start|e(nd|d((?:|meta)data)))?|anguagechange)|a(uxclick|fterprint|bort)|r(e(s(ize|et)|jectionhandled)|atechange)|m(ouse(o(ut|ver)|down|up|enter|leave|move)|essage(error)?)|b(efore(unload|print)|lur))(?![-:\\\\w])","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.event-handler.$1.html","patterns":[{"begin":"=","beginCaptures":{"0":{"name":"punctuation.separator.key-value.html"}},"end":"(?<=[^=\\\\s])(?!\\\\s*=)|(?=/?>)","patterns":[{"begin":"(?=[^/<=>\`\\\\s]|/(?!>))","end":"(?!\\\\G)","name":"meta.embedded.line.js","patterns":[{"captures":{"0":{"name":"source.js"},"1":{"patterns":[{"include":"source.js"}]}},"match":"(([^\\"'/<=>\`\\\\s]|/(?!>))+)","name":"string.unquoted.html"},{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.html"}},"contentName":"source.js","end":"(\\")","endCaptures":{"0":{"name":"punctuation.definition.string.end.html"},"1":{"name":"source.js"}},"name":"string.quoted.double.html","patterns":[{"captures":{"0":{"patterns":[{"include":"source.js"}]}},"match":"([^\\\\n\\"/]|/(?![*/]))+"},{"begin":"//","beginCaptures":{"0":{"name":"punctuation.definition.comment.js"}},"end":"(?=\\")|\\\\n","name":"comment.line.double-slash.js"},{"begin":"/\\\\*","beginCaptures":{"0":{"name":"punctuation.definition.comment.begin.js"}},"end":"(?=\\")|\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.end.js"}},"name":"comment.block.js"}]},{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.html"}},"contentName":"source.js","end":"(')","endCaptures":{"0":{"name":"punctuation.definition.string.end.html"},"1":{"name":"source.js"}},"name":"string.quoted.single.html","patterns":[{"captures":{"0":{"patterns":[{"include":"source.js"}]}},"match":"([^\\\\n'/]|/(?![*/]))+"},{"begin":"//","beginCaptures":{"0":{"name":"punctuation.definition.comment.js"}},"end":"(?=')|\\\\n","name":"comment.line.double-slash.js"},{"begin":"/\\\\*","beginCaptures":{"0":{"name":"punctuation.definition.comment.begin.js"}},"end":"(?=')|\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.end.js"}},"name":"comment.block.js"}]}]},{"match":"=","name":"invalid.illegal.unexpected-equals-sign.html"}]}]},{"begin":"(data-[-a-z]+)(?![-:\\\\w])","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.data-x.$1.html","patterns":[{"include":"#attribute-interior"}]},{"begin":"(align|bgcolor|border)(?![-:\\\\w])","beginCaptures":{"0":{"name":"invalid.deprecated.entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.$1.html","patterns":[{"include":"#attribute-interior"}]},{"begin":"([^\\\\x00- \\"'/<=>\\\\x7F-\\\\x{9F}﷐-﷯￾￿🿾🿿𯿾𯿿𿿾𿿿\\\\x{4FFFE}\\\\x{4FFFF}\\\\x{5FFFE}\\\\x{5FFFF}\\\\x{6FFFE}\\\\x{6FFFF}\\\\x{7FFFE}\\\\x{7FFFF}\\\\x{8FFFE}\\\\x{8FFFF}\\\\x{9FFFE}\\\\x{9FFFF}\\\\x{AFFFE}\\\\x{AFFFF}\\\\x{BFFFE}\\\\x{BFFFF}\\\\x{CFFFE}\\\\x{CFFFF}\\\\x{DFFFE}\\\\x{DFFFF}\\\\x{EFFFE}\\\\x{EFFFF}\\\\x{FFFFE}\\\\x{FFFFF}\\\\x{10FFFE}\\\\x{10FFFF}]+)","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.unrecognized.$1.html","patterns":[{"include":"#attribute-interior"}]},{"match":"[^>\\\\s]+","name":"invalid.illegal.character-not-allowed-here.html"}]},"attribute-interior":{"patterns":[{"begin":"=","beginCaptures":{"0":{"name":"punctuation.separator.key-value.html"}},"end":"(?<=[^=\\\\s])(?!\\\\s*=)|(?=/?>)","patterns":[{"match":"([^\\"'/<=>\`\\\\s]|/(?!>))+","name":"string.unquoted.html"},{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.html"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.html"}},"name":"string.quoted.double.html","patterns":[{"include":"#entities"}]},{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.html"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.html"}},"name":"string.quoted.single.html","patterns":[{"include":"#entities"}]},{"match":"=","name":"invalid.illegal.unexpected-equals-sign.html"}]}]},"cdata":{"begin":"","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.cdata.html"},"comment":{"begin":"","name":"comment.block.html","patterns":[{"match":"\\\\G-?>","name":"invalid.illegal.characters-not-allowed-here.html"},{"match":")|(?=-->))","name":"invalid.illegal.characters-not-allowed-here.html"},{"match":"--!>","name":"invalid.illegal.characters-not-allowed-here.html"}]},"core-minus-invalid":{"patterns":[{"include":"#xml-processing"},{"include":"#comment"},{"include":"#doctype"},{"include":"#cdata"},{"include":"#tags-valid"},{"include":"#entities"}]},"doctype":{"begin":"","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.doctype.html","patterns":[{"match":"\\\\G(?i:DOCTYPE)","name":"entity.name.tag.html"},{"begin":"\\"","end":"\\"","name":"string.quoted.double.html"},{"match":"[^>\\\\s]+","name":"entity.other.attribute-name.html"}]},"entities":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.entity.html"},"912":{"name":"punctuation.definition.entity.html"}},"match":"(&)(?=[A-Za-z])((a(s(ymp(eq)?|cr|t)|n(d(slope|[dv]|and)?|g(s(t|ph)|zarr|e|le|rt(vb(d)?)?|msd(a([a-h]))?)?)|c(y|irc|d|ute|E)?|tilde|o(pf|gon)|uml|p(id|os|prox(eq)?|[Ee]|acir)?|elig|f(r)?|w((?:con|)int)|l(pha|e(ph|fsym))|acute|ring|grave|m(p|a(cr|lg))|breve)|A(s(sign|cr)|nd|MP|c(y|irc)|tilde|o(pf|gon)|uml|pplyFunction|fr|Elig|lpha|acute|ring|grave|macr|breve))|(B(scr|cy|opf|umpeq|e(cause|ta|rnoullis)|fr|a(ckslash|r(v|wed))|reve)|b(s(cr|im(e)?|ol(hsub|b)?|emi)|n(ot|e(quiv)?)|c(y|ong)|ig(s(tar|qcup)|c(irc|up|ap)|triangle(down|up)|o(times|dot|plus)|uplus|vee|wedge)|o(t(tom)?|pf|wtie|x(h([DUdu])?|times|H([DUdu])?|d([LRlr])|u([LRlr])|plus|D([LRlr])|v([HLRhlr])?|U([LRlr])|V([HLRhlr])?|minus|box))|Not|dquo|u(ll(et)?|mp(e(q)?|E)?)|prime|e(caus(e)?|t(h|ween|a)|psi|rnou|mptyv)|karow|fr|l(ock|k(1([24])|34)|a(nk|ck(square|triangle(down|left|right)?|lozenge)))|a(ck(sim(eq)?|cong|prime|epsilon)|r(vee|wed(ge)?))|r(eve|vbar)|brk(tbrk)?))|(c(s(cr|u(p(e)?|b(e)?))|h(cy|i|eck(mark)?)|ylcty|c(irc|ups(sm)?|edil|a(ps|ron))|tdot|ir(scir|c(eq|le(d(R|circ|S|dash|ast)|arrow(left|right)))?|e|fnint|E|mid)?|o(n(int|g(dot)?)|p(y(sr)?|f|rod)|lon(e(q)?)?|m(p(fn|le(xes|ment))?|ma(t)?))|dot|u(darr([lr])|p(s|c([au]p)|or|dot|brcap)?|e(sc|pr)|vee|wed|larr(p)?|r(vearrow(left|right)|ly(eq(succ|prec)|vee|wedge)|arr(m)?|ren))|e(nt(erdot)?|dil|mptyv)|fr|w((?:con|)int)|lubs(uit)?|a(cute|p(s|c([au]p)|dot|and|brcup)?|r(on|et))|r(oss|arr))|C(scr|hi|c(irc|onint|edil|aron)|ircle(Minus|Times|Dot|Plus)|Hcy|o(n(tourIntegral|int|gruent)|unterClockwiseContourIntegral|p(f|roduct)|lon(e)?)|dot|up(Cap)?|OPY|e(nterDot|dilla)|fr|lo(seCurly((?:Double|)Quote)|ckwiseContourIntegral)|a(yleys|cute|p(italDifferentialD)?)|ross))|(d(s(c([ry])|trok|ol)|har([lr])|c(y|aron)|t(dot|ri(f)?)|i(sin|e|v(ide(ontimes)?|onx)?|am(s|ond(suit)?)?|gamma)|Har|z(cy|igrarr)|o(t(square|plus|eq(dot)?|minus)?|ublebarwedge|pf|wn(harpoon(left|right)|downarrows|arrow)|llar)|d(otseq|a(rr|gger))?|u(har|arr)|jcy|e(lta|g|mptyv)|f(isht|r)|wangle|lc(orn|rop)|a(sh(v)?|leth|rr|gger)|r(c(orn|rop)|bkarow)|b(karow|lac)|Arr)|D(s(cr|trok)|c(y|aron)|Scy|i(fferentialD|a(critical(Grave|Tilde|Do(t|ubleAcute)|Acute)|mond))|o(t(Dot|Equal)?|uble(Right(Tee|Arrow)|ContourIntegral|Do(t|wnArrow)|Up((?:Down|)Arrow)|VerticalBar|L(ong(RightArrow|Left((?:Right|)Arrow))|eft(RightArrow|Tee|Arrow)))|pf|wn(Right(TeeVector|Vector(Bar)?)|Breve|Tee(Arrow)?|arrow|Left(RightVector|TeeVector|Vector(Bar)?)|Arrow(Bar|UpArrow)?))|Zcy|el(ta)?|D(otrahd)?|Jcy|fr|a(shv|rr|gger)))|(e(s(cr|im|dot)|n(sp|g)|c(y|ir(c)?|olon|aron)|t([ah])|o(pf|gon)|dot|u(ro|ml)|p(si(v|lon)?|lus|ar(sl)?)|e|D(D??ot)|q(s(im|lant(less|gtr))|c(irc|olon)|u(iv(DD)?|est|als)|vparsl)|f(Dot|r)|l(s(dot)?|inters|l)?|a(ster|cute)|r(Dot|arr)|g(s(dot)?|rave)?|x(cl|ist|p(onentiale|ectation))|m(sp(1([34]))?|pty(set|v)?|acr))|E(s(cr|im)|c(y|irc|aron)|ta|o(pf|gon)|NG|dot|uml|TH|psilon|qu(ilibrium|al(Tilde)?)|fr|lement|acute|grave|x(ists|ponentialE)|m(pty((?:|Very)SmallSquare)|acr)))|(f(scr|nof|cy|ilig|o(pf|r(k(v)?|all))|jlig|partint|emale|f(ilig|l(l??ig)|r)|l(tns|lig|at)|allingdotseq|r(own|a(sl|c(1([2-68])|78|2([35])|3([458])|45|5([68])))))|F(scr|cy|illed((?:|Very)SmallSquare)|o(uriertrf|pf|rAll)|fr))|(G(scr|c(y|irc|edil)|t|opf|dot|T|Jcy|fr|amma(d)?|reater(Greater|SlantEqual|Tilde|Equal(Less)?|FullEqual|Less)|g|breve)|g(s(cr|im([el])?)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|irc)|t(c(c|ir)|dot|quest|lPar|r(sim|dot|eq(q?less)|less|a(pprox|rr)))?|imel|opf|dot|jcy|e(s(cc|dot(o(l)?)?|l(es)?)?|q(slant|q)?|l)?|v(nE|ertneqq)|fr|E(l)?|l([Eaj])?|a(cute|p|mma(d)?)|rave|g(g)?|breve))|(h(s(cr|trok|lash)|y(phen|bull)|circ|o(ok((?:lef|righ)tarrow)|pf|arr|rbar|mtht)|e(llip|arts(uit)?|rcon)|ks([ew]arow)|fr|a(irsp|lf|r(dcy|r(cir|w)?)|milt)|bar|Arr)|H(s(cr|trok)|circ|ilbertSpace|o(pf|rizontalLine)|ump(DownHump|Equal)|fr|a(cek|t)|ARDcy))|(i(s(cr|in(s(v)?|dot|[Ev])?)|n(care|t(cal|prod|e(rcal|gers)|larhk)?|odot|fin(tie)?)?|c(y|irc)?|t(ilde)?|i(nfin|i(i??nt)|ota)?|o(cy|ta|pf|gon)|u(kcy|ml)|jlig|prod|e(cy|xcl)|quest|f([fr])|acute|grave|m(of|ped|a(cr|th|g(part|e|line))))|I(scr|n(t(e(rsection|gral))?|visible(Comma|Times))|c(y|irc)|tilde|o(ta|pf|gon)|dot|u(kcy|ml)|Ocy|Jlig|fr|Ecy|acute|grave|m(plies|a(cr|ginaryI))?))|(j(s(cr|ercy)|c(y|irc)|opf|ukcy|fr|math)|J(s(cr|ercy)|c(y|irc)|opf|ukcy|fr))|(k(scr|hcy|c(y|edil)|opf|jcy|fr|appa(v)?|green)|K(scr|c(y|edil)|Hcy|opf|Jcy|fr|appa))|(l(s(h|cr|trok|im([eg])?|q(uo(r)?|b)|aquo)|h(ar(d|u(l)?)|blk)|n(sim|e(q(q)?)?|E|ap(prox)?)|c(y|ub|e(d??il)|aron)|Barr|t(hree|c(c|ir)|imes|dot|quest|larr|r(i([ef])?|Par))?|Har|o(ng(left((?:|right)arrow)|rightarrow|mapsto)|times|z(enge|f)?|oparrow(left|right)|p(f|lus|ar)|w(ast|bar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|r((?:d|us)har))|ur((?:ds|u)har)|jcy|par(lt)?|e(s(s(sim|dot|eq(q?gtr)|approx|gtr)|cc|dot(o(r)?)?|g(es)?)?|q(slant|q)?|ft(harpoon(down|up)|threetimes|leftarrows|arrow(tail)?|right(squigarrow|harpoons|arrow(s)?))|g)?|v(nE|ertneqq)|f(isht|loor|r)|E(g)?|l(hard|corner|tri|arr)?|a(ng(d|le)?|cute|t(e(s)?|ail)?|p|emptyv|quo|rr(sim|hk|tl|pl|fs|lp|b(fs)?)?|gran|mbda)|r(har(d)?|corner|tri|arr|m)|g(E)?|m(idot|oust(ache)?)|b(arr|r(k(sl([du])|e)|ac([ek]))|brk)|A(tail|arr|rr))|L(s(h|cr|trok)|c(y|edil|aron)|t|o(ng(RightArrow|left((?:|right)arrow)|rightarrow|Left((?:Right|)Arrow))|pf|wer((?:Righ|Lef)tArrow))|T|e(ss(Greater|SlantEqual|Tilde|EqualGreater|FullEqual|Less)|ft(Right(Vector|Arrow)|Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|rightarrow|Floor|A(ngleBracket|rrow(RightArrow|Bar)?)))|Jcy|fr|l(eftarrow)?|a(ng|cute|placetrf|rr|mbda)|midot))|(M(scr|cy|inusPlus|opf|u|e(diumSpace|llintrf)|fr|ap)|m(s(cr|tpos)|ho|nplus|c(y|omma)|i(nus(d(u)?|b)?|cro|d(cir|dot|ast)?)|o(dels|pf)|dash|u((?:lti|)map)?|p|easuredangle|DDot|fr|l(cp|dr)|a(cr|p(sto(down|up|left)?)?|l(t(ese)?|e)|rker)))|(n(s(hort(parallel|mid)|c(cue|[er])?|im(e(q)?)?|u(cc(eq)?|p(set(eq(q)?)?|[Ee])?|b(set(eq(q)?)?|[Ee])?)|par|qsu([bp]e)|mid)|Rightarrow|h(par|arr|Arr)|G(t(v)?|g)|c(y|ong(dot)?|up|edil|a(p|ron))|t(ilde|lg|riangle(left(eq)?|right(eq)?)|gl)|i(s(d)?|v)?|o(t(ni(v([abc]))?|in(dot|v([abc])|E)?)?|pf)|dash|u(m(sp|ero)?)?|jcy|p(olint|ar(sl|t|allel)?|r(cue|e(c(eq)?)?)?)|e(s(im|ear)|dot|quiv|ar(hk|r(ow)?)|xist(s)?|Arr)?|v(sim|infin|Harr|dash|Dash|l(t(rie)?|e|Arr)|ap|r(trie|Arr)|g([et]))|fr|w(near|ar(hk|r(ow)?)|Arr)|V([Dd]ash)|l(sim|t(ri(e)?)?|dr|e(s(s)?|q(slant|q)?|ft((?:|right)arrow))?|E|arr|Arr)|a(ng|cute|tur(al(s)?)?|p(id|os|prox|E)?|bla)|r(tri(e)?|ightarrow|arr([cw])?|Arr)|g(sim|t(r)?|e(s|q(slant|q)?)?|E)|mid|L(t(v)?|eft((?:|right)arrow)|l)|b(sp|ump(e)?))|N(scr|c(y|edil|aron)|tilde|o(nBreakingSpace|Break|t(R(ightTriangle(Bar|Equal)?|everseElement)|Greater(Greater|SlantEqual|Tilde|Equal|FullEqual|Less)?|S(u(cceeds(SlantEqual|Tilde|Equal)?|perset(Equal)?|bset(Equal)?)|quareSu(perset(Equal)?|bset(Equal)?))|Hump(DownHump|Equal)|Nested(GreaterGreater|LessLess)|C(ongruent|upCap)|Tilde(Tilde|Equal|FullEqual)?|DoubleVerticalBar|Precedes((?:Slant|)Equal)?|E(qual(Tilde)?|lement|xists)|VerticalBar|Le(ss(Greater|SlantEqual|Tilde|Equal|Less)?|ftTriangle(Bar|Equal)?))?|pf)|u|e(sted(GreaterGreater|LessLess)|wLine|gative(MediumSpace|Thi((?:n|ck)Space)|VeryThinSpace))|Jcy|fr|acute))|(o(s(cr|ol|lash)|h(m|bar)|c(y|ir(c)?)|ti(lde|mes(as)?)|S|int|opf|d(sold|iv|ot|ash|blac)|uml|p(erp|lus|ar)|elig|vbar|f(cir|r)|l(c(ir|ross)|t|ine|arr)|a(st|cute)|r(slope|igof|or|d(er(of)?|[fm])?|v|arr)?|g(t|on|rave)|m(i(nus|cron|d)|ega|acr))|O(s(cr|lash)|c(y|irc)|ti(lde|mes)|opf|dblac|uml|penCurly((?:Double|)Quote)|ver(B(ar|rac(e|ket))|Parenthesis)|fr|Elig|acute|r|grave|m(icron|ega|acr)))|(p(s(cr|i)|h(i(v)?|one|mmat)|cy|i(tchfork|v)?|o(intint|und|pf)|uncsp|er(cnt|tenk|iod|p|mil)|fr|l(us(sim|cir|two|d([ou])|e|acir|mn|b)?|an(ck(h)?|kv))|ar(s(im|l)|t|a(llel)?)?|r(sim|n(sim|E|ap)|cue|ime(s)?|o(d|p(to)?|f(surf|line|alar))|urel|e(c(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?)?|E|ap)?|m)|P(s(cr|i)|hi|cy|i|o(incareplane|pf)|fr|lusMinus|artialD|r(ime|o(duct|portion(al)?)|ecedes(SlantEqual|Tilde|Equal)?)?))|(q(scr|int|opf|u(ot|est(eq)?|at(int|ernions))|prime|fr)|Q(scr|opf|UOT|fr))|(R(s(h|cr)|ho|c(y|edil|aron)|Barr|ight(Ceiling|T(ee(Vector|Arrow)?|riangle(Bar|Equal)?)|Do(ubleBracket|wn(TeeVector|Vector(Bar)?))|Up(TeeVector|DownVector|Vector(Bar)?)|Vector(Bar)?|arrow|Floor|A(ngleBracket|rrow(Bar|LeftArrow)?))|o(undImplies|pf)|uleDelayed|e(verse(UpEquilibrium|E(quilibrium|lement)))?|fr|EG|a(ng|cute|rr(tl)?)|rightarrow)|r(s(h|cr|q(uo(r)?|b)|aquo)|h(o(v)?|ar(d|u(l)?))|nmid|c(y|ub|e(d??il)|aron)|Barr|t(hree|imes|ri([ef]|ltri)?)|i(singdotseq|ng|ght(squigarrow|harpoon(down|up)|threetimes|left(harpoons|arrows)|arrow(tail)?|rightarrows))|Har|o(times|p(f|lus|ar)|a(ng|rr)|brk)|d(sh|ca|quo(r)?|ldhar)|uluhar|p(polint|ar(gt)?)|e(ct|al(s|ine|part)?|g)|f(isht|loor|r)|l(har|arr|m)|a(ng([de]|le)?|c(ute|e)|t(io(nals)?|ail)|dic|emptyv|quo|rr(sim|hk|c|tl|pl|fs|w|lp|ap|b(fs)?)?)|rarr|x|moust(ache)?|b(arr|r(k(sl([du])|e)|ac([ek]))|brk)|A(tail|arr|rr)))|(s(s(cr|tarf|etmn|mile)|h(y|c(hcy|y)|ort(parallel|mid)|arp)|c(sim|y|n(sim|E|ap)|cue|irc|polint|e(dil)?|E|a(p|ron))?|t(ar(f)?|r(ns|aight(phi|epsilon)))|i(gma([fv])?|m(ne|dot|plus|e(q)?|l(E)?|rarr|g(E)?)?)|zlig|o(pf|ftcy|l(b(ar)?)?)|dot([be])?|u(ng|cc(sim|n(sim|eqq|approx)|curlyeq|eq|approx)?|p(s(im|u([bp])|et(neq(q)?|eq(q)?)?)|hs(ol|ub)|1|n([Ee])|2|d(sub|ot)|3|plus|e(dot)?|E|larr|mult)?|m|b(s(im|u([bp])|et(neq(q)?|eq(q)?)?)|n([Ee])|dot|plus|e(dot)?|E|rarr|mult)?)|pa(des(uit)?|r)|e(swar|ct|tm(n|inus)|ar(hk|r(ow)?)|xt|mi|Arr)|q(su(p(set(eq)?|e)?|b(set(eq)?|e)?)|c(up(s)?|ap(s)?)|u(f|ar([ef]))?)|fr(own)?|w(nwar|ar(hk|r(ow)?)|Arr)|larr|acute|rarr|m(t(e(s)?)?|i(d|le)|eparsl|a(shp|llsetminus))|bquo)|S(scr|hort((?:Right|Down|Up|Left)Arrow)|c(y|irc|edil|aron)?|tar|igma|H(cy|CHcy)|opf|u(c(hThat|ceeds(SlantEqual|Tilde|Equal)?)|p(set|erset(Equal)?)?|m|b(set(Equal)?)?)|OFTcy|q(uare(Su(perset(Equal)?|bset(Equal)?)|Intersection|Union)?|rt)|fr|acute|mallCircle))|(t(s(hcy|c([ry])|trok)|h(i(nsp|ck(sim|approx))|orn|e(ta(sym|v)?|re(4|fore))|k(sim|ap))|c(y|edil|aron)|i(nt|lde|mes(d|b(ar)?)?)|o(sa|p(cir|f(ork)?|bot)?|ea)|dot|prime|elrec|fr|w(ixt|ohead((?:lef|righ)tarrow))|a(u|rget)|r(i(sb|time|dot|plus|e|angle(down|q|left(eq)?|right(eq)?)?|minus)|pezium|ade)|brk)|T(s(cr|trok)|RADE|h(i((?:n|ck)Space)|e(ta|refore))|c(y|edil|aron)|S(H??cy)|ilde(Tilde|Equal|FullEqual)?|HORN|opf|fr|a([bu])|ripleDot))|(u(scr|h(ar([lr])|blk)|c(y|irc)|t(ilde|dot|ri(f)?)|Har|o(pf|gon)|d(har|arr|blac)|u(arr|ml)|p(si(h|lon)?|harpoon(left|right)|downarrow|uparrows|lus|arrow)|f(isht|r)|wangle|l(c(orn(er)?|rop)|tri)|a(cute|rr)|r(c(orn(er)?|rop)|tri|ing)|grave|m(l|acr)|br(cy|eve)|Arr)|U(scr|n(ion(Plus)?|der(B(ar|rac(e|ket))|Parenthesis))|c(y|irc)|tilde|o(pf|gon)|dblac|uml|p(si(lon)?|downarrow|Tee(Arrow)?|per((?:Righ|Lef)tArrow)|DownArrow|Equilibrium|arrow|Arrow(Bar|DownArrow)?)|fr|a(cute|rr(ocir)?)|ring|grave|macr|br(cy|eve)))|(v(s(cr|u(pn([Ee])|bn([Ee])))|nsu([bp])|cy|Bar(v)?|zigzag|opf|dash|prop|e(e(eq|bar)?|llip|r(t|bar))|Dash|fr|ltri|a(ngrt|r(s(igma|u(psetneq(q)?|bsetneq(q)?))|nothing|t(heta|riangle(left|right))|p(hi|i|ropto)|epsilon|kappa|r(ho)?))|rtri|Arr)|V(scr|cy|opf|dash(l)?|e(e|r(yThinSpace|t(ical(Bar|Separator|Tilde|Line))?|bar))|Dash|vdash|fr|bar))|(w(scr|circ|opf|p|e(ierp|d(ge(q)?|bar))|fr|r(eath)?)|W(scr|circ|opf|edge|fr))|(X(scr|i|opf|fr)|x(s(cr|qcup)|h([Aa]rr)|nis|c(irc|up|ap)|i|o(time|dot|p(f|lus))|dtri|u(tri|plus)|vee|fr|wedge|l([Aa]rr)|r([Aa]rr)|map))|(y(scr|c(y|irc)|icy|opf|u(cy|ml)|en|fr|ac(y|ute))|Y(scr|c(y|irc)|opf|uml|Icy|Ucy|fr|acute|Acy))|(z(scr|hcy|c(y|aron)|igrarr|opf|dot|e(ta|etrf)|fr|w(n?j)|acute)|Z(scr|c(y|aron)|Hcy|opf|dot|e(ta|roWidthSpace)|fr|acute)))(;)","name":"constant.character.entity.named.$2.html"},{"captures":{"1":{"name":"punctuation.definition.entity.html"},"3":{"name":"punctuation.definition.entity.html"}},"match":"(&)#[0-9]+(;)","name":"constant.character.entity.numeric.decimal.html"},{"captures":{"1":{"name":"punctuation.definition.entity.html"},"3":{"name":"punctuation.definition.entity.html"}},"match":"(&)#[Xx]\\\\h+(;)","name":"constant.character.entity.numeric.hexadecimal.html"},{"match":"&(?=[0-9A-Za-z]+;)","name":"invalid.illegal.ambiguous-ampersand.html"}]},"math":{"patterns":[{"begin":"(?i)(<)(math)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.structure.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()","endCaptures":{"0":{"name":"meta.tag.structure.$2.end.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.element.structure.$2.html","patterns":[{"begin":"(?)\\\\G","end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]}],"repository":{"attribute":{"patterns":[{"begin":"(s(hift|ymmetric|cript(sizemultiplier|level|minsize)|t(ackalign|retchy)|ide|u([bp]scriptshift)|e(parator(s)?|lection)|rc)|h(eight|ref)|n(otation|umalign)|c(haralign|olumn(spa(n|cing)|width|lines|align)|lose|rossout)|i(n(dent(shift(first|last)?|target|align(first|last)?)|fixlinebreakstyle)|d)|o(pen|verflow)|d(i(splay(style)?|r)|e(nomalign|cimalpoint|pth))|position|e(dge|qual(columns|rows))|voffset|f(orm|ence|rame(spacing)?)|width|l(space|ine(thickness|leading|break(style|multchar)?)|o(ngdivstyle|cation)|ength|quote|argeop)|a(c(cent(under)?|tiontype)|l(t(text|img(-(height|valign|width))?)|ign(mentscope)?))|r(space|ow(spa(n|cing)|lines|align)|quote)|groupalign|x(link:href|mlns)|m(in(size|labelspacing)|ovablelimits|a(th(size|color|variant|background)|xsize))|bevelled)(?![-:\\\\w])","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.$1.html","patterns":[{"include":"#attribute-interior"}]},{"begin":"([^\\\\x00- \\"'/<=>\\\\x7F-\\\\x{9F}﷐-﷯￾￿🿾🿿𯿾𯿿𿿾𿿿\\\\x{4FFFE}\\\\x{4FFFF}\\\\x{5FFFE}\\\\x{5FFFF}\\\\x{6FFFE}\\\\x{6FFFF}\\\\x{7FFFE}\\\\x{7FFFF}\\\\x{8FFFE}\\\\x{8FFFF}\\\\x{9FFFE}\\\\x{9FFFF}\\\\x{AFFFE}\\\\x{AFFFF}\\\\x{BFFFE}\\\\x{BFFFF}\\\\x{CFFFE}\\\\x{CFFFF}\\\\x{DFFFE}\\\\x{DFFFF}\\\\x{EFFFE}\\\\x{EFFFF}\\\\x{FFFFE}\\\\x{FFFFF}\\\\x{10FFFE}\\\\x{10FFFF}]+)","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.unrecognized.$1.html","patterns":[{"include":"#attribute-interior"}]},{"match":"[^>\\\\s]+","name":"invalid.illegal.character-not-allowed-here.html"}]},"tags":{"patterns":[{"include":"#comment"},{"include":"#cdata"},{"captures":{"0":{"name":"meta.tag.structure.math.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(annotation|annotation-xml|semantics|menclose|merror|mfenced|mfrac|mpadded|mphantom|mroot|mrow|msqrt|mstyle|mmultiscripts|mover|mprescripts|msub|msubsup|msup|munder|munderover|none|mlabeledtr|mtable|mtd|mtr|mlongdiv|mscarries|mscarry|msgroup|msline|msrow|mstack|maction)(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.structure.math.$2.html"},{"begin":"(?i)(<)(annotation|annotation-xml|semantics|menclose|merror|mfenced|mfrac|mpadded|mphantom|mroot|mrow|msqrt|mstyle|mmultiscripts|mover|mprescripts|msub|msubsup|msup|munder|munderover|none|mlabeledtr|mtable|mtd|mtr|mlongdiv|mscarries|mscarry|msgroup|msline|msrow|mstack|maction)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.structure.math.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.inline.math.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(m(?:[inos]|space|text|aligngroup|alignmark))(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.inline.math.$2.html"},{"begin":"(?i)(<)(m(?:[inos]|space|text|aligngroup|alignmark))(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.inline.math.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.object.math.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(mglyph)(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.object.math.$2.html"},{"begin":"(?i)(<)(mglyph)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.object.math.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.other.invalid.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.illegal.unrecognized-tag.html"},"4":{"patterns":[{"include":"#attribute"}]},"6":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(([:\\\\w]+))(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.other.invalid.html"},{"begin":"(?i)(<)((\\\\w[^>\\\\s]*))(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.other.invalid.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.illegal.unrecognized-tag.html"},"4":{"patterns":[{"include":"#attribute"}]},"6":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.other.invalid.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"include":"#tags-invalid"}]}}},"svg":{"patterns":[{"begin":"(?i)(<)(svg)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.structure.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()","endCaptures":{"0":{"name":"meta.tag.structure.$2.end.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.element.structure.$2.html","patterns":[{"begin":"(?)\\\\G","end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]}],"repository":{"attribute":{"patterns":[{"begin":"(s(hape-rendering|ystemLanguage|cale|t(yle|itchTiles|op-(color|opacity)|dDeviation|em([hv])|artOffset|r(i(ng|kethrough-(thickness|position))|oke(-(opacity|dash(offset|array)|width|line(cap|join)|miterlimit))?))|urfaceScale|p(e(cular(Constant|Exponent)|ed)|acing|readMethod)|eed|lope)|h(oriz-(origin-x|adv-x)|eight|anging|ref(lang)?)|y([12]|ChannelSelector)?|n(umOctaves|ame)|c(y|o(ntentS((?:cript|tyle)Type)|lor(-(interpolation(-filters)?|profile|rendering))?)|ursor|l(ip(-(path|rule)|PathUnits)?|ass)|a(p-height|lcMode)|x)|t(ype|o|ext(-(decoration|anchor|rendering)|Length)|a(rget([XY])?|b(index|leValues))|ransform)|i(n(tercept|2)?|d(eographic)?|mage-rendering)|z(oomAndPan)?|o(p(erator|acity)|ver(flow|line-(thickness|position))|ffset|r(i(ent(ation)?|gin)|der))|d(y|i(splay|visor|ffuseConstant|rection)|ominant-baseline|ur|e(scent|celerate)|x)?|u(1|n(i(code(-(range|bidi))?|ts-per-em)|derline-(thickness|position))|2)|p(ing|oint(s(At([XYZ]))?|er-events)|a(nose-1|t(h(Length)?|tern(ContentUnits|Transform|Units))|int-order)|r(imitiveUnits|eserveA(spectRatio|lpha)))|e(n(d|able-background)|dgeMode|levation|x(ternalResourcesRequired|ponent))|v(i(sibility|ew(Box|Target))|-(hanging|ideographic|alphabetic|mathematical)|e(ctor-effect|r(sion|t-(origin-([xy])|adv-y)))|alues)|k([123]|e(y(Splines|Times|Points)|rn(ing|el(Matrix|UnitLength)))|4)?|f(y|il(ter(Res|Units)?|l(-(opacity|rule))?)|o(nt-(s(t(yle|retch)|ize(-adjust)?)|variant|family|weight)|rmat)|lood-(color|opacity)|r(om)?|x)|w(idth(s)?|ord-spacing|riting-mode)|l(i(ghting-color|mitingConeAngle)|ocal|e(ngthAdjust|tter-spacing)|ang)|a(scent|cc(umulate|ent-height)|ttribute(Name|Type)|zimuth|dditive|utoReverse|l(ignment-baseline|phabetic|lowReorder)|rabic-form|mplitude)|r(y|otate|e(s(tart|ult)|ndering-intent|peat(Count|Dur)|quired(Extensions|Features)|f([XY]|errerPolicy)|l)|adius|x)?|g([12]|lyph(Ref|-(name|orientation-(horizontal|vertical)))|radient(Transform|Units))|x([12]|ChannelSelector|-height|link:(show|href|t(ype|itle)|a(ctuate|rcrole)|role)|ml:(space|lang|base))?|m(in|ode|e(thod|dia)|a(sk((?:Content|)Units)?|thematical|rker(Height|-(start|end|mid)|Units|Width)|x))|b(y|ias|egin|ase(Profile|line-shift|Frequency)|box))(?![-:\\\\w])","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.$1.html","patterns":[{"include":"#attribute-interior"}]},{"begin":"([^\\\\x00- \\"'/<=>\\\\x7F-\\\\x{9F}﷐-﷯￾￿🿾🿿𯿾𯿿𿿾𿿿\\\\x{4FFFE}\\\\x{4FFFF}\\\\x{5FFFE}\\\\x{5FFFF}\\\\x{6FFFE}\\\\x{6FFFF}\\\\x{7FFFE}\\\\x{7FFFF}\\\\x{8FFFE}\\\\x{8FFFF}\\\\x{9FFFE}\\\\x{9FFFF}\\\\x{AFFFE}\\\\x{AFFFF}\\\\x{BFFFE}\\\\x{BFFFF}\\\\x{CFFFE}\\\\x{CFFFF}\\\\x{DFFFE}\\\\x{DFFFF}\\\\x{EFFFE}\\\\x{EFFFF}\\\\x{FFFFE}\\\\x{FFFFF}\\\\x{10FFFE}\\\\x{10FFFF}]+)","beginCaptures":{"0":{"name":"entity.other.attribute-name.html"}},"end":"(?=\\\\s*+[^=\\\\s])","name":"meta.attribute.unrecognized.$1.html","patterns":[{"include":"#attribute-interior"}]},{"match":"[^>\\\\s]+","name":"invalid.illegal.character-not-allowed-here.html"}]},"tags":{"patterns":[{"include":"#comment"},{"include":"#cdata"},{"captures":{"0":{"name":"meta.tag.metadata.svg.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(color-profile|desc|metadata|script|style|title)(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.metadata.svg.$2.html"},{"begin":"(?i)(<)(color-profile|desc|metadata|script|style|title)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.metadata.svg.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.structure.svg.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(animateMotion|clipPath|defs|feComponentTransfer|feDiffuseLighting|feMerge|feSpecularLighting|filter|g|hatch|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|pattern|radialGradient|switch|text|textPath)(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.structure.svg.$2.html"},{"begin":"(?i)(<)(animateMotion|clipPath|defs|feComponentTransfer|feDiffuseLighting|feMerge|feSpecularLighting|filter|g|hatch|linearGradient|marker|mask|mesh|meshgradient|meshpatch|meshrow|pattern|radialGradient|switch|text|textPath)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.structure.svg.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.inline.svg.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(a|animate|discard|feBlend|feColorMatrix|feComposite|feConvolveMatrix|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feMergeNode|feMorphology|feOffset|fePointLight|feSpotLight|feTile|feTurbulence|hatchPath|mpath|set|solidcolor|stop|tspan)(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.inline.svg.$2.html"},{"begin":"(?i)(<)(a|animate|discard|feBlend|feColorMatrix|feComposite|feConvolveMatrix|feDisplacementMap|feDistantLight|feDropShadow|feFlood|feFuncA|feFuncB|feFuncG|feFuncR|feGaussianBlur|feMergeNode|feMorphology|feOffset|fePointLight|feSpotLight|feTile|feTurbulence|hatchPath|mpath|set|solidcolor|stop|tspan)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.inline.svg.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.object.svg.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(circle|ellipse|feImage|foreignObject|image|line|path|polygon|polyline|rect|symbol|use|view)(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.object.svg.$2.html"},{"begin":"(?i)(<)(a|circle|ellipse|feImage|foreignObject|image|line|path|polygon|polyline|rect|symbol|use|view)(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.object.svg.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"patterns":[{"include":"#attribute"}]},"5":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.other.svg.$2.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"},"4":{"patterns":[{"include":"#attribute"}]},"6":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)((altGlyph|altGlyphDef|altGlyphItem|animateColor|animateTransform|cursor|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|glyph|glyphRef|hkern|missing-glyph|tref|vkern))(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.other.svg.$2.html"},{"begin":"(?i)(<)((altGlyph|altGlyphDef|altGlyphItem|animateColor|animateTransform|cursor|font|font-face|font-face-format|font-face-name|font-face-src|font-face-uri|glyph|glyphRef|hkern|missing-glyph|tref|vkern))(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.other.svg.$2.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"},"4":{"patterns":[{"include":"#attribute"}]},"6":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.other.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"captures":{"0":{"name":"meta.tag.other.invalid.void.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.illegal.unrecognized-tag.html"},"4":{"patterns":[{"include":"#attribute"}]},"6":{"name":"punctuation.definition.tag.end.html"}},"match":"(?i)(<)(([:\\\\w]+))(?=\\\\s|/?>)(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(/>)","name":"meta.element.other.invalid.html"},{"begin":"(?i)(<)((\\\\w[^>\\\\s]*))(?=\\\\s|/?>)(?:(([^\\"'>]|\\"[^\\"]*\\"|'[^']*')*)(>))?","beginCaptures":{"0":{"name":"meta.tag.other.invalid.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.illegal.unrecognized-tag.html"},"4":{"patterns":[{"include":"#attribute"}]},"6":{"name":"punctuation.definition.tag.end.html"}},"end":"(?i)()|(/>)|(?=)\\\\G","end":"(?=/>)|>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.other.invalid.start.html","patterns":[{"include":"#attribute"}]},{"include":"#tags"}]},{"include":"#tags-invalid"}]}}},"tags-invalid":{"patterns":[{"begin":"(\\\\s]*))(?)","endCaptures":{"1":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.other.$2.html","patterns":[{"include":"#attribute"}]}]},"tags-valid":{"patterns":[{"begin":"(^[\\\\t ]+)?(?=<(?i:style)\\\\b(?!-))","beginCaptures":{"1":{"name":"punctuation.whitespace.embedded.leading.html"}},"end":"(?!\\\\G)([\\\\t ]*$\\\\n?)?","endCaptures":{"1":{"name":"punctuation.whitespace.embedded.trailing.html"}},"patterns":[{"begin":"(?i)(<)(style)(?=\\\\s|/?>)","beginCaptures":{"0":{"name":"meta.tag.metadata.style.start.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":"(?i)((<)/)(style)\\\\s*(>)","endCaptures":{"0":{"name":"meta.tag.metadata.style.end.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"source.css-ignored-vscode"},"3":{"name":"entity.name.tag.html"},"4":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.embedded.block.html","patterns":[{"begin":"\\\\G","captures":{"1":{"name":"punctuation.definition.tag.end.html"}},"end":"(>)","name":"meta.tag.metadata.style.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?!\\\\G)","end":"(?=)","endCaptures":{"0":{"name":"meta.tag.metadata.script.end.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.embedded.block.html","patterns":[{"begin":"\\\\G","end":"(?=/)","patterns":[{"begin":"(>)","beginCaptures":{"0":{"name":"meta.tag.metadata.script.start.html"},"1":{"name":"punctuation.definition.tag.end.html"}},"end":"((<))(?=/(?i:script))","endCaptures":{"0":{"name":"meta.tag.metadata.script.end.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"source.js-ignored-vscode"}},"patterns":[{"begin":"\\\\G","end":"(?=|type(?=[=\\\\s])(?!\\\\s*=\\\\s*(''|\\"\\"|([\\"']?)(text/(javascript(1\\\\.[0-5])?|x-javascript|jscript|livescript|(x-)?ecmascript|babel)|application/((?:(x-)?jav|(x-)?ecm)ascript)|module)[\\"'>\\\\s]))))","name":"meta.tag.metadata.script.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i:(?=type\\\\s*=\\\\s*([\\"']?)text/(x-handlebars|(x-(handlebars-)?|ng-)?template|html)[\\"'>\\\\s]))","end":"((<))(?=/(?i:script))","endCaptures":{"0":{"name":"meta.tag.metadata.script.end.html"},"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"text.html.basic"}},"patterns":[{"begin":"\\\\G","end":"(>)","endCaptures":{"1":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.script.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?!\\\\G)","end":"(?=)","endCaptures":{"1":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.script.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?!\\\\G)","end":"(?=)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.$2.void.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(noscript|title)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(col|hr|input)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.$2.void.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(address|article|aside|blockquote|body|button|caption|colgroup|datalist|dd|details|dialog|div|dl|dt|fieldset|figcaption|figure|footer|form|head|header|hgroup|html|h[1-6]|label|legend|li|main|map|menu|meter|nav|ol|optgroup|option|output|p|pre|progress|section|select|slot|summary|table|tbody|td|template|textarea|tfoot|th|thead|tr|ul)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(area|br|wbr)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.$2.void.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(a|abbr|b|bdi|bdo|cite|code|data|del|dfn|em|i|ins|kbd|mark|q|rp|rt|ruby|s|samp|small|span|strong|sub|sup|time|u|var)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(embed|img|param|source|track)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.$2.void.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)(audio|canvas|iframe|object|picture|video)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)((basefont|isindex))(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.metadata.$2.void.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)((center|frameset|noembed|noframes))(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.structure.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)((acronym|big|blink|font|strike|tt|xmp))(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.inline.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)((frame))(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.$2.void.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)((applet))(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.deprecated.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.object.$2.end.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)(<)((dir|keygen|listing|menuitem|plaintext|spacer))(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.illegal.no-longer-supported.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.other.$2.start.html","patterns":[{"include":"#attribute"}]},{"begin":"(?i)()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"},"3":{"name":"invalid.illegal.no-longer-supported.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.other.$2.end.html","patterns":[{"include":"#attribute"}]},{"include":"#math"},{"include":"#svg"},{"begin":"(<)([A-Za-z][.0-9A-Z_a-z·À-ÖØ-öø-ͽͿ-῿‌‍‿⁀⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�𐀀-\\\\x{EFFFF}]*-[-.0-9A-Z_a-z·À-ÖØ-öø-ͽͿ-῿‌‍‿⁀⁰-↏Ⰰ-⿯、-퟿豈-﷏ﷰ-�𐀀-\\\\x{EFFFF}]*)(?=\\\\s|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":"/?>","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.custom.start.html","patterns":[{"include":"#attribute"}]},{"begin":"()","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.html"},"2":{"name":"entity.name.tag.html"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.tag.end.html"}},"name":"meta.tag.custom.end.html","patterns":[{"include":"#attribute"}]}]},"xml-processing":{"begin":"(<\\\\?)(xml)","captures":{"1":{"name":"punctuation.definition.tag.html"},"2":{"name":"entity.name.tag.html"}},"end":"(\\\\?>)","name":"meta.tag.metadata.processing.xml.html","patterns":[{"include":"#attribute"}]}},"scopeName":"text.html.basic","embeddedLangs":["javascript","css"]}`)),Wc=[...os,...rs,Hc],Vc=[Object.freeze(JSON.parse('{"displayName":"JSON","name":"json","patterns":[{"include":"#value"}],"repository":{"array":{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.array.begin.json"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.array.end.json"}},"name":"meta.structure.array.json","patterns":[{"include":"#value"},{"match":",","name":"punctuation.separator.array.json"},{"match":"[^]\\\\s]","name":"invalid.illegal.expected-array-separator.json"}]},"comments":{"patterns":[{"begin":"/\\\\*\\\\*(?!/)","captures":{"0":{"name":"punctuation.definition.comment.json"}},"end":"\\\\*/","name":"comment.block.documentation.json"},{"begin":"/\\\\*","captures":{"0":{"name":"punctuation.definition.comment.json"}},"end":"\\\\*/","name":"comment.block.json"},{"captures":{"1":{"name":"punctuation.definition.comment.json"}},"match":"(//).*$\\\\n?","name":"comment.line.double-slash.js"}]},"constant":{"match":"\\\\b(?:true|false|null)\\\\b","name":"constant.language.json"},"number":{"match":"-?(?:0|[1-9]\\\\d*)(?:(?:\\\\.\\\\d+)?(?:[Ee][-+]?\\\\d+)?)?","name":"constant.numeric.json"},"object":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.dictionary.begin.json"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.dictionary.end.json"}},"name":"meta.structure.dictionary.json","patterns":[{"include":"#objectkey"},{"include":"#comments"},{"begin":":","beginCaptures":{"0":{"name":"punctuation.separator.dictionary.key-value.json"}},"end":"(,)|(?=})","endCaptures":{"1":{"name":"punctuation.separator.dictionary.pair.json"}},"name":"meta.structure.dictionary.value.json","patterns":[{"include":"#value"},{"match":"[^,\\\\s]","name":"invalid.illegal.expected-dictionary-separator.json"}]},{"match":"[^}\\\\s]","name":"invalid.illegal.expected-dictionary-separator.json"}]},"objectkey":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.support.type.property-name.begin.json"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.support.type.property-name.end.json"}},"name":"string.json support.type.property-name.json","patterns":[{"include":"#stringcontent"}]},"string":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.json"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.json"}},"name":"string.quoted.double.json","patterns":[{"include":"#stringcontent"}]},"stringcontent":{"patterns":[{"match":"\\\\\\\\(?:[\\"/\\\\\\\\bfnrt]|u\\\\h{4})","name":"constant.character.escape.json"},{"match":"\\\\\\\\.","name":"invalid.illegal.unrecognized-string-escape.json"}]},"value":{"patterns":[{"include":"#constant"},{"include":"#number"},{"include":"#string"},{"include":"#array"},{"include":"#object"},{"include":"#comments"}]}},"scopeName":"source.json"}'))],Xc=[Object.freeze(JSON.parse(`{"displayName":"JSX","name":"jsx","patterns":[{"include":"#directives"},{"include":"#statements"},{"include":"#shebang"}],"repository":{"access-modifier":{"match":"(??\\\\[]|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^yield|[^$._[:alnum:]]yield|^throw|[^$._[:alnum:]]throw|^in|[^$._[:alnum:]]in|^of|[^$._[:alnum:]]of|^typeof|[^$._[:alnum:]]typeof|&&|\\\\|\\\\||\\\\*)\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"punctuation.definition.block.js.jsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.js.jsx"}},"name":"meta.objectliteral.js.jsx","patterns":[{"include":"#object-member"}]},"array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.js.jsx"},"2":{"name":"punctuation.definition.binding-pattern.array.js.jsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.js.jsx"}},"patterns":[{"include":"#binding-element"},{"include":"#punctuation-comma"}]},"array-binding-pattern-const":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.js.jsx"},"2":{"name":"punctuation.definition.binding-pattern.array.js.jsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.js.jsx"}},"patterns":[{"include":"#binding-element-const"},{"include":"#punctuation-comma"}]},"array-literal":{"begin":"\\\\s*(\\\\[)","beginCaptures":{"1":{"name":"meta.brace.square.js.jsx"}},"end":"]","endCaptures":{"0":{"name":"meta.brace.square.js.jsx"}},"name":"meta.array.literal.js.jsx","patterns":[{"include":"#expression"},{"include":"#punctuation-comma"}]},"arrow-function":{"patterns":[{"captures":{"1":{"name":"storage.modifier.async.js.jsx"},"2":{"name":"variable.parameter.js.jsx"}},"match":"(?:(?)","name":"meta.arrow.js.jsx"},{"begin":"(?:(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.arrow.js.jsx","patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"include":"#function-parameters"},{"include":"#arrow-return-type"},{"include":"#possibly-arrow-return-type"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.js.jsx"}},"end":"((?<=[}\\\\S])(?)|((?!\\\\{)(?=\\\\S)))(?!/[*/])","name":"meta.arrow.js.jsx","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#decl-block"},{"include":"#expression"}]}]},"arrow-return-type":{"begin":"(?<=\\\\))\\\\s*(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.js.jsx"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.return.type.arrow.js.jsx","patterns":[{"include":"#arrow-return-type-body"}]},"arrow-return-type-body":{"patterns":[{"begin":"(?<=:)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"async-modifier":{"match":"(?\\\\s*$)","beginCaptures":{"1":{"name":"punctuation.definition.comment.js.jsx"}},"end":"(?=$)","name":"comment.line.triple-slash.directive.js.jsx","patterns":[{"begin":"(<)(reference|amd-dependency|amd-module)","beginCaptures":{"1":{"name":"punctuation.definition.tag.directive.js.jsx"},"2":{"name":"entity.name.tag.directive.js.jsx"}},"end":"/>","endCaptures":{"0":{"name":"punctuation.definition.tag.directive.js.jsx"}},"name":"meta.tag.js.jsx","patterns":[{"match":"path|types|no-default-lib|lib|name|resolution-mode","name":"entity.other.attribute-name.directive.js.jsx"},{"match":"=","name":"keyword.operator.assignment.js.jsx"},{"include":"#string"}]}]},"docblock":{"patterns":[{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.access-type.jsdoc"}},"match":"((@)a(?:ccess|pi))\\\\s+(p(?:rivate|rotected|ublic))\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"5":{"name":"constant.other.email.link.underline.jsdoc"},"6":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"match":"((@)author)\\\\s+([^*/<>@\\\\s](?:[^*/<>@]|\\\\*[^/])*)(?:\\\\s*(<)([^>\\\\s]+)(>))?"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"keyword.operator.control.jsdoc"},"5":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)borrows)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)\\\\s+(as)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)"},{"begin":"((@)example)\\\\s+","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=@|\\\\*/)","name":"meta.example.jsdoc","patterns":[{"match":"^\\\\s\\\\*\\\\s+"},{"begin":"\\\\G(<)caption(>)","beginCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"contentName":"constant.other.description.jsdoc","end":"()|(?=\\\\*/)","endCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}}},{"captures":{"0":{"name":"source.embedded.js.jsx"}},"match":"[^*@\\\\s](?:[^*]|\\\\*[^/])*"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.symbol-type.jsdoc"}},"match":"((@)kind)\\\\s+(class|constant|event|external|file|function|member|mixin|module|namespace|typedef)\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.link.underline.jsdoc"},"4":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)see)\\\\s+(?:((?=https?://)(?:[^*\\\\s]|\\\\*[^/])+)|((?!https?://|(?:\\\\[[^]\\\\[]*])?\\\\{@(?:link|linkcode|linkplain|tutorial)\\\\b)(?:[^*/@\\\\s]|\\\\*[^/])+))"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)template)\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*(?:\\\\s*,\\\\s*[$A-Z_a-z][]$.\\\\[\\\\w]*)*)"},{"begin":"((@)template)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:arg|argument|const|constant|member|namespace|param|var))\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*)"},{"begin":"((@)typedef)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"(?:[^*/@\\\\s]|\\\\*[^/])+","name":"entity.name.type.instance.jsdoc"}]},{"begin":"((@)(?:arg|argument|const|constant|member|namespace|param|prop|property|var))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"},{"captures":{"1":{"name":"punctuation.definition.optional-value.begin.bracket.square.jsdoc"},"2":{"name":"keyword.operator.assignment.jsdoc"},"3":{"name":"source.embedded.js.jsx"},"4":{"name":"punctuation.definition.optional-value.end.bracket.square.jsdoc"},"5":{"name":"invalid.illegal.syntax.jsdoc"}},"match":"(\\\\[)\\\\s*[$\\\\w]+(?:(?:\\\\[])?\\\\.[$\\\\w]+)*(?:\\\\s*(=)\\\\s*((?>\\"(?:\\\\*(?!/)|\\\\\\\\(?!\\")|[^*\\\\\\\\])*?\\"|'(?:\\\\*(?!/)|\\\\\\\\(?!')|[^*\\\\\\\\])*?'|\\\\[(?:\\\\*(?!/)|[^*])*?]|(?:\\\\*(?!/)|\\\\s(?!\\\\s*])|\\\\[.*?(?:]|(?=\\\\*/))|[^]*\\\\[\\\\s])*)*))?\\\\s*(?:(])((?:[^*\\\\s]|\\\\*[^/\\\\s])+)?|(?=\\\\*/))","name":"variable.other.jsdoc"}]},{"begin":"((@)(?:define|enum|exception|export|extends|lends|implements|modifies|namespace|private|protected|returns?|satisfies|suppress|this|throws|type|yields?))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)(?:alias|augments|callback|constructs|emits|event|fires|exports?|extends|external|function|func|host|lends|listens|interface|memberof!?|method|module|mixes|mixin|name|requires|see|this|typedef|uses))\\\\s+((?:[^*@{}\\\\s]|\\\\*[^/])+)"},{"begin":"((@)(?:default(?:value)?|license|version))\\\\s+(([\\"']))","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"},"4":{"name":"punctuation.definition.string.begin.jsdoc"}},"contentName":"variable.other.jsdoc","end":"(\\\\3)|(?=$|\\\\*/)","endCaptures":{"0":{"name":"variable.other.jsdoc"},"1":{"name":"punctuation.definition.string.end.jsdoc"}}},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:default(?:value)?|license|tutorial|variation|version))\\\\s+([^*\\\\s]+)"},{"captures":{"1":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"(@)(?:abstract|access|alias|api|arg|argument|async|attribute|augments|author|beta|borrows|bubbles|callback|chainable|class|classdesc|code|config|const|constant|constructor|constructs|copyright|default|defaultvalue|define|deprecated|desc|description|dict|emits|enum|event|example|exception|exports?|extends|extension(?:_?for)?|external|externs|file|fileoverview|final|fires|for|func|function|generator|global|hideconstructor|host|ignore|implements|implicitCast|inherit[Dd]oc|inner|instance|interface|internal|kind|lends|license|listens|main|member|memberof!?|method|mixes|mixins?|modifies|module|name|namespace|noalias|nocollapse|nocompile|nosideeffects|override|overview|package|param|polymer(?:Behavior)?|preserve|private|prop|property|protected|public|read[Oo]nly|record|require[ds]|returns?|see|since|static|struct|submodule|summary|suppress|template|this|throws|todo|tutorial|type|typedef|unrestricted|uses|var|variation|version|virtual|writeOnce|yields?)\\\\b","name":"storage.type.class.jsdoc"},{"include":"#inline-tags"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"((@)[$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s+)"}]},"enum-declaration":{"begin":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.js.jsx"},"2":{"name":"keyword.operator.rest.js.jsx"},"3":{"name":"variable.parameter.js.jsx variable.language.this.js.jsx"},"4":{"name":"variable.parameter.js.jsx"},"5":{"name":"keyword.operator.optional.js.jsx"}},"match":"(?:(??}]|\\\\|\\\\||&&|!==|$|((?>>??|\\\\|)=","name":"keyword.operator.assignment.compound.bitwise.js.jsx"},{"match":"<<|>>>?","name":"keyword.operator.bitwise.shift.js.jsx"},{"match":"[!=]==?","name":"keyword.operator.comparison.js.jsx"},{"match":"<=|>=|<>|[<>]","name":"keyword.operator.relational.js.jsx"},{"captures":{"1":{"name":"keyword.operator.logical.js.jsx"},"2":{"name":"keyword.operator.assignment.compound.js.jsx"},"3":{"name":"keyword.operator.arithmetic.js.jsx"}},"match":"(?<=[$_[:alnum:]])(!)\\\\s*(?:(/=)|(/)(?![*/]))"},{"match":"!|&&|\\\\|\\\\||\\\\?\\\\?","name":"keyword.operator.logical.js.jsx"},{"match":"[\\\\&^|~]","name":"keyword.operator.bitwise.js.jsx"},{"match":"=","name":"keyword.operator.assignment.js.jsx"},{"match":"--","name":"keyword.operator.decrement.js.jsx"},{"match":"\\\\+\\\\+","name":"keyword.operator.increment.js.jsx"},{"match":"[-%*+/]","name":"keyword.operator.arithmetic.js.jsx"},{"begin":"(?<=[]$)_[:alnum:]])\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)+(?:(/=)|(/)(?![*/])))","end":"(/=)|(/)(?!\\\\*([^*]|(\\\\*[^/]))*\\\\*/)","endCaptures":{"1":{"name":"keyword.operator.assignment.compound.js.jsx"},"2":{"name":"keyword.operator.arithmetic.js.jsx"}},"patterns":[{"include":"#comment"}]},{"captures":{"1":{"name":"keyword.operator.assignment.compound.js.jsx"},"2":{"name":"keyword.operator.arithmetic.js.jsx"}},"match":"(?<=[]$)_[:alnum:]])\\\\s*(?:(/=)|(/)(?![*/]))"}]},"expressionPunctuations":{"patterns":[{"include":"#punctuation-comma"},{"include":"#punctuation-accessor"}]},"expressionWithoutIdentifiers":{"patterns":[{"include":"#jsx"},{"include":"#string"},{"include":"#regex"},{"include":"#comment"},{"include":"#function-expression"},{"include":"#class-expression"},{"include":"#arrow-function"},{"include":"#paren-expression-possibly-arrow"},{"include":"#cast"},{"include":"#ternary-expression"},{"include":"#new-expr"},{"include":"#instanceof-expr"},{"include":"#object-literal"},{"include":"#expression-operators"},{"include":"#function-call"},{"include":"#literal"},{"include":"#support-objects"},{"include":"#paren-expression"}]},"field-declaration":{"begin":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"match":"#?[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.property.js.jsx variable.object.property.js.jsx"},{"match":"\\\\?","name":"keyword.operator.optional.js.jsx"},{"match":"!","name":"keyword.operator.definiteassignment.js.jsx"}]},"for-loop":{"begin":"(?\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\\\\())","end":"(?<=\\\\))(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\\\\())","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\\\\())","name":"meta.function-call.js.jsx","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"},{"include":"#paren-expression"}]},{"begin":"(?=(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","end":"(?<=>)(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*[(\\\\[{]\\\\s*)$)","name":"meta.function-call.js.jsx","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"}]}]},"function-call-optionals":{"patterns":[{"match":"\\\\?\\\\.","name":"meta.function-call.js.jsx punctuation.accessor.optional.js.jsx"},{"match":"!","name":"meta.function-call.js.jsx keyword.operator.definiteassignment.js.jsx"}]},"function-call-target":{"patterns":[{"include":"#support-function-call-identifiers"},{"match":"(#?[$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.js.jsx"}]},"function-declaration":{"begin":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))"},{"captures":{"1":{"name":"punctuation.accessor.js.jsx"},"2":{"name":"punctuation.accessor.optional.js.jsx"},"3":{"name":"variable.other.constant.property.js.jsx"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])"},{"captures":{"1":{"name":"punctuation.accessor.js.jsx"},"2":{"name":"punctuation.accessor.optional.js.jsx"},"3":{"name":"variable.other.property.js.jsx"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*)"},{"match":"(\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])","name":"variable.other.constant.js.jsx"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"variable.other.readwrite.js.jsx"}]},"if-statement":{"patterns":[{"begin":"(??}]|\\\\|\\\\||&&|!==|$|([!=]==?)|(([\\\\&^|~]\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s+instanceof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|((?))","end":"(/>)|()","endCaptures":{"1":{"name":"punctuation.definition.tag.end.js.jsx"},"2":{"name":"punctuation.definition.tag.begin.js.jsx"},"3":{"name":"entity.name.tag.namespace.js.jsx"},"4":{"name":"punctuation.separator.namespace.js.jsx"},"5":{"name":"entity.name.tag.js.jsx"},"6":{"name":"support.class.component.js.jsx"},"7":{"name":"punctuation.definition.tag.end.js.jsx"}},"name":"meta.tag.js.jsx","patterns":[{"begin":"(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.js.jsx"},"2":{"name":"entity.name.tag.namespace.js.jsx"},"3":{"name":"punctuation.separator.namespace.js.jsx"},"4":{"name":"entity.name.tag.js.jsx"},"5":{"name":"support.class.component.js.jsx"}},"end":"(?=/?>)","patterns":[{"include":"#comment"},{"include":"#type-arguments"},{"include":"#jsx-tag-attributes"}]},{"begin":"(>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.end.js.jsx"}},"contentName":"meta.jsx.children.js.jsx","end":"(?=|/\\\\*|//)"},"jsx-tag-attributes":{"begin":"\\\\s+","end":"(?=/?>)","name":"meta.tag.attributes.js.jsx","patterns":[{"include":"#comment"},{"include":"#jsx-tag-attribute-name"},{"include":"#jsx-tag-attribute-assignment"},{"include":"#jsx-string-double-quoted"},{"include":"#jsx-string-single-quoted"},{"include":"#jsx-evaluated-code"},{"include":"#jsx-tag-attributes-illegal"}]},"jsx-tag-attributes-illegal":{"match":"\\\\S+","name":"invalid.illegal.attribute.js.jsx"},"jsx-tag-in-expression":{"begin":"(??\\\\[{]|&&|\\\\|\\\\||\\\\?|\\\\*/|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^default|[^$._[:alnum:]]default|^yield|[^$._[:alnum:]]yield|^)\\\\s*(?!<\\\\s*[$_[:alpha:]][$_[:alnum:]]*((\\\\s+extends\\\\s+[^=>])|,))(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","end":"(?!(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","patterns":[{"include":"#jsx-tag"}]},"jsx-tag-without-attributes":{"begin":"(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.js.jsx"},"2":{"name":"entity.name.tag.namespace.js.jsx"},"3":{"name":"punctuation.separator.namespace.js.jsx"},"4":{"name":"entity.name.tag.js.jsx"},"5":{"name":"support.class.component.js.jsx"},"6":{"name":"punctuation.definition.tag.end.js.jsx"}},"contentName":"meta.jsx.children.js.jsx","end":"()","endCaptures":{"1":{"name":"punctuation.definition.tag.begin.js.jsx"},"2":{"name":"entity.name.tag.namespace.js.jsx"},"3":{"name":"punctuation.separator.namespace.js.jsx"},"4":{"name":"entity.name.tag.js.jsx"},"5":{"name":"support.class.component.js.jsx"},"6":{"name":"punctuation.definition.tag.end.js.jsx"}},"name":"meta.tag.without-attributes.js.jsx","patterns":[{"include":"#jsx-children"}]},"jsx-tag-without-attributes-in-expression":{"begin":"(??\\\\[{]|&&|\\\\|\\\\||\\\\?|\\\\*/|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^default|[^$._[:alnum:]]default|^yield|[^$._[:alnum:]]yield|^)\\\\s*(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","end":"(?!(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?))","patterns":[{"include":"#jsx-tag-without-attributes"}]},"label":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)(?=\\\\s*\\\\{)","beginCaptures":{"1":{"name":"entity.name.label.js.jsx"},"2":{"name":"punctuation.separator.label.js.jsx"}},"end":"(?<=})","patterns":[{"include":"#decl-block"}]},{"captures":{"1":{"name":"entity.name.label.js.jsx"},"2":{"name":"punctuation.separator.label.js.jsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)"}]},"literal":{"patterns":[{"include":"#numeric-literal"},{"include":"#boolean-literal"},{"include":"#null-literal"},{"include":"#undefined-literal"},{"include":"#numericConstant-literal"},{"include":"#array-literal"},{"include":"#this-literal"},{"include":"#super-literal"}]},"method-declaration":{"patterns":[{"begin":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.js.jsx"},"2":{"name":"storage.modifier.js.jsx"},"3":{"name":"storage.modifier.js.jsx"},"4":{"name":"storage.modifier.async.js.jsx"},"5":{"name":"keyword.operator.new.js.jsx"},"6":{"name":"keyword.generator.asterisk.js.jsx"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.js.jsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]},{"begin":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.js.jsx"},"2":{"name":"storage.modifier.js.jsx"},"3":{"name":"storage.modifier.js.jsx"},"4":{"name":"storage.modifier.async.js.jsx"},"5":{"name":"storage.type.property.js.jsx"},"6":{"name":"keyword.generator.asterisk.js.jsx"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.js.jsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]}]},"method-declaration-name":{"begin":"(?=(\\\\b((??}]|\\\\|\\\\||&&|!==|$|((?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"},"2":{"name":"storage.type.property.js.jsx"},"3":{"name":"keyword.generator.asterisk.js.jsx"}},"end":"(?=[,;}])|(?<=})","name":"meta.method.declaration.js.jsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"},{"begin":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"},"2":{"name":"storage.type.property.js.jsx"},"3":{"name":"keyword.generator.asterisk.js.jsx"}},"end":"(?=[(<])","patterns":[{"include":"#method-declaration-name"}]}]},"object-member":{"patterns":[{"include":"#comment"},{"include":"#object-literal-method-declaration"},{"begin":"(?=\\\\[)","end":"(?=:)|((?<=])(?=\\\\s*[(<]))","name":"meta.object.member.js.jsx meta.object-literal.key.js.jsx","patterns":[{"include":"#comment"},{"include":"#array-literal"}]},{"begin":"(?=[\\"'\`])","end":"(?=:)|((?<=[\\"'\`])(?=((\\\\s*[(,<}])|(\\\\s+(as|satisifies)\\\\s+))))","name":"meta.object.member.js.jsx meta.object-literal.key.js.jsx","patterns":[{"include":"#comment"},{"include":"#string"}]},{"begin":"(?=\\\\b((?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))","name":"meta.object.member.js.jsx"},{"captures":{"0":{"name":"meta.object-literal.key.js.jsx"}},"match":"[$_[:alpha:]][$_[:alnum:]]*\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object.member.js.jsx"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.js.jsx"}},"end":"(?=[,}])","name":"meta.object.member.js.jsx","patterns":[{"include":"#expression"}]},{"captures":{"1":{"name":"variable.other.readwrite.js.jsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=[,}]|$|//|/\\\\*)","name":"meta.object.member.js.jsx"},{"captures":{"1":{"name":"keyword.control.as.js.jsx"},"2":{"name":"storage.modifier.js.jsx"}},"match":"(??}]|\\\\|\\\\||&&|!==|$|^|((?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"},"2":{"name":"meta.brace.round.js.jsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(?=<\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"}},"end":"(?<=>)","patterns":[{"include":"#type-parameters"}]},{"begin":"(?<=>)\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"meta.brace.round.js.jsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"include":"#possibly-arrow-return-type"},{"include":"#expression"}]},{"include":"#punctuation-comma"},{"include":"#decl-block"}]},"parameter-array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.js.jsx"},"2":{"name":"punctuation.definition.binding-pattern.array.js.jsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.js.jsx"}},"patterns":[{"include":"#parameter-binding-element"},{"include":"#punctuation-comma"}]},"parameter-binding-element":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#parameter-object-binding-pattern"},{"include":"#parameter-array-binding-pattern"},{"include":"#destructuring-parameter-rest"},{"include":"#variable-initializer"}]},"parameter-name":{"patterns":[{"captures":{"1":{"name":"storage.modifier.js.jsx"}},"match":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.js.jsx"},"2":{"name":"keyword.operator.rest.js.jsx"},"3":{"name":"variable.parameter.js.jsx variable.language.this.js.jsx"},"4":{"name":"variable.parameter.js.jsx"},"5":{"name":"keyword.operator.optional.js.jsx"}},"match":"(?:(?])","name":"meta.type.annotation.js.jsx","patterns":[{"include":"#type"}]}]},"paren-expression":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"patterns":[{"include":"#expression"}]},"paren-expression-possibly-arrow":{"patterns":[{"begin":"(?<=[(,=])\\\\s*(async)?(?=\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"begin":"(?<=[(,=]|=>|^return|[^$._[:alnum:]]return)\\\\s*(async)?(?=\\\\s*((((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()|(<)|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)))\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.js.jsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"include":"#possibly-arrow-return-type"}]},"paren-expression-possibly-arrow-with-typeparameters":{"patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},"possibly-arrow-return-type":{"begin":"(?<=\\\\)|^)\\\\s*(:)(?=\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*=>)","beginCaptures":{"1":{"name":"meta.arrow.js.jsx meta.return.type.arrow.js.jsx keyword.operator.type.annotation.js.jsx"}},"contentName":"meta.arrow.js.jsx meta.return.type.arrow.js.jsx","end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","patterns":[{"include":"#arrow-return-type-body"}]},"property-accessor":{"match":"(?|&&|\\\\|\\\\||\\\\*/)\\\\s*(/)(?![*/])(?=(?:[^()/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)+]|\\\\(([^)\\\\\\\\]|\\\\\\\\.)+\\\\))+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.js.jsx"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.js.jsx"},"2":{"name":"keyword.other.js.jsx"}},"name":"string.regexp.js.jsx","patterns":[{"include":"#regexp"}]},{"begin":"((?)"},{"match":"[*+?]|\\\\{(\\\\d+,\\\\d+|\\\\d+,|,\\\\d+|\\\\d+)}\\\\??","name":"keyword.operator.quantifier.regexp"},{"match":"\\\\|","name":"keyword.operator.or.regexp"},{"begin":"(\\\\()((\\\\?=)|(\\\\?!)|(\\\\?<=)|(\\\\?)?","beginCaptures":{"0":{"name":"punctuation.definition.group.regexp"},"1":{"name":"punctuation.definition.group.no-capture.regexp"},"2":{"name":"variable.other.regexp"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.group.regexp"}},"name":"meta.group.regexp","patterns":[{"include":"#regexp"}]},{"begin":"(\\\\[)(\\\\^)?","beginCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"},"2":{"name":"keyword.operator.negation.regexp"}},"end":"(])","endCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"}},"name":"constant.other.character-class.set.regexp","patterns":[{"captures":{"1":{"name":"constant.character.numeric.regexp"},"2":{"name":"constant.character.control.regexp"},"3":{"name":"constant.character.escape.backslash.regexp"},"4":{"name":"constant.character.numeric.regexp"},"5":{"name":"constant.character.control.regexp"},"6":{"name":"constant.character.escape.backslash.regexp"}},"match":"(?:.|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))-(?:[^]\\\\\\\\]|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))","name":"constant.other.character-class.range.regexp"},{"include":"#regex-character-class"}]},{"include":"#regex-character-class"}]},"return-type":{"patterns":[{"begin":"(?<=\\\\))\\\\s*(:)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.js.jsx"}},"end":"(?]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\()|(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\\\b(?!\\\\$))"},{"captures":{"1":{"name":"support.type.object.module.js.jsx"},"2":{"name":"support.type.object.module.js.jsx"},"3":{"name":"punctuation.accessor.js.jsx"},"4":{"name":"punctuation.accessor.optional.js.jsx"},"5":{"name":"support.type.object.module.js.jsx"}},"match":"(?\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\`)","end":"(?=\`)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*\\\\s*\\\\??\\\\.\\\\s*)*|(\\\\??\\\\.\\\\s*)?)([$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)?\`)","patterns":[{"include":"#support-function-call-identifiers"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.tagged-template.js.jsx"}]},{"include":"#type-arguments"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?\\\\s*(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?))*(?)*(?\\\\s*)\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.js.jsx"}},"end":"(?=\`)","patterns":[{"include":"#type-arguments"}]}]},"template-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.js.jsx"}},"contentName":"meta.embedded.line.js.jsx","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.js.jsx"}},"name":"meta.template.expression.js.jsx","patterns":[{"include":"#expression"}]},"template-type":{"patterns":[{"include":"#template-call"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?(\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.js.jsx"},"2":{"name":"string.template.js.jsx punctuation.definition.string.template.begin.js.jsx"}},"contentName":"string.template.js.jsx","end":"\`","endCaptures":{"0":{"name":"string.template.js.jsx punctuation.definition.string.template.end.js.jsx"}},"patterns":[{"include":"#template-type-substitution-element"},{"include":"#string-character-escape"}]}]},"template-type-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.js.jsx"}},"contentName":"meta.embedded.line.js.jsx","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.js.jsx"}},"name":"meta.template.expression.js.jsx","patterns":[{"include":"#type"}]},"ternary-expression":{"begin":"(?!\\\\?\\\\.\\\\s*\\\\D)(\\\\?)(?!\\\\?)","beginCaptures":{"1":{"name":"keyword.operator.ternary.js.jsx"}},"end":"\\\\s*(:)","endCaptures":{"1":{"name":"keyword.operator.ternary.js.jsx"}},"patterns":[{"include":"#expression"}]},"this-literal":{"match":"(?])|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.js.jsx","patterns":[{"include":"#type"}]},{"begin":"(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.js.jsx"}},"end":"(?])|(?=^\\\\s*$)|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.js.jsx","patterns":[{"include":"#type"}]}]},"type-arguments":{"begin":"<","beginCaptures":{"0":{"name":"punctuation.definition.typeparameters.begin.js.jsx"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.typeparameters.end.js.jsx"}},"name":"meta.type.parameters.js.jsx","patterns":[{"include":"#type-arguments-body"}]},"type-arguments-body":{"patterns":[{"captures":{"0":{"name":"keyword.operator.type.js.jsx"}},"match":"(?)","patterns":[{"include":"#comment"},{"include":"#type-parameters"}]},{"begin":"(?))))))","end":"(?<=\\\\))","name":"meta.type.function.js.jsx","patterns":[{"include":"#function-parameters"}]}]},"type-function-return-type":{"patterns":[{"begin":"(=>)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"storage.type.function.arrow.js.jsx"}},"end":"(?)(??{}]|//|$)","name":"meta.type.function.return.js.jsx","patterns":[{"include":"#type-function-return-type-core"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.js.jsx"}},"end":"(?)(??{}]|//|^\\\\s*$)|((?<=\\\\S)(?=\\\\s*$)))","name":"meta.type.function.return.js.jsx","patterns":[{"include":"#type-function-return-type-core"}]}]},"type-function-return-type-core":{"patterns":[{"include":"#comment"},{"begin":"(?<==>)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"type-infer":{"patterns":[{"captures":{"1":{"name":"keyword.operator.expression.infer.js.jsx"},"2":{"name":"entity.name.type.js.jsx"},"3":{"name":"keyword.operator.expression.extends.js.jsx"}},"match":"(?)","endCaptures":{"1":{"name":"meta.type.parameters.js.jsx punctuation.definition.typeparameters.end.js.jsx"}},"patterns":[{"include":"#type-arguments-body"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(<)","beginCaptures":{"1":{"name":"entity.name.type.js.jsx"},"2":{"name":"meta.type.parameters.js.jsx punctuation.definition.typeparameters.begin.js.jsx"}},"contentName":"meta.type.parameters.js.jsx","end":"(>)","endCaptures":{"1":{"name":"meta.type.parameters.js.jsx punctuation.definition.typeparameters.end.js.jsx"}},"patterns":[{"include":"#type-arguments-body"}]},{"captures":{"1":{"name":"entity.name.type.module.js.jsx"},"2":{"name":"punctuation.accessor.js.jsx"},"3":{"name":"punctuation.accessor.optional.js.jsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"entity.name.type.js.jsx"}]},"type-object":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.js.jsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.js.jsx"}},"name":"meta.object.type.js.jsx","patterns":[{"include":"#comment"},{"include":"#method-declaration"},{"include":"#indexer-declaration"},{"include":"#indexer-mapped-type-declaration"},{"include":"#field-declaration"},{"include":"#type-annotation"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.js.jsx"}},"end":"(?=[,;}]|$)|(?<=})","patterns":[{"include":"#type"}]},{"include":"#punctuation-comma"},{"include":"#punctuation-semicolon"},{"include":"#type"}]},"type-operators":{"patterns":[{"include":"#typeof-operator"},{"include":"#type-infer"},{"begin":"([\\\\&|])(?=\\\\s*\\\\{)","beginCaptures":{"0":{"name":"keyword.operator.type.js.jsx"}},"end":"(?<=})","patterns":[{"include":"#type-object"}]},{"begin":"[\\\\&|]","beginCaptures":{"0":{"name":"keyword.operator.type.js.jsx"}},"end":"(?=\\\\S)"},{"match":"(?)","endCaptures":{"1":{"name":"punctuation.definition.typeparameters.end.js.jsx"}},"name":"meta.type.parameters.js.jsx","patterns":[{"include":"#comment"},{"match":"(?)","name":"keyword.operator.assignment.js.jsx"}]},"type-paren-or-function-parameters":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.js.jsx"}},"name":"meta.type.paren.cover.js.jsx","patterns":[{"captures":{"1":{"name":"storage.modifier.js.jsx"},"2":{"name":"keyword.operator.rest.js.jsx"},"3":{"name":"entity.name.function.js.jsx variable.language.this.js.jsx"},"4":{"name":"entity.name.function.js.jsx"},"5":{"name":"keyword.operator.optional.js.jsx"}},"match":"(?:(?)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))))"},{"captures":{"1":{"name":"storage.modifier.js.jsx"},"2":{"name":"keyword.operator.rest.js.jsx"},"3":{"name":"variable.parameter.js.jsx variable.language.this.js.jsx"},"4":{"name":"variable.parameter.js.jsx"},"5":{"name":"keyword.operator.optional.js.jsx"}},"match":"(?:(??{|}]|(extends\\\\s+)|$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type-arguments"},{"include":"#expression"}]},"undefined-literal":{"match":"(?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.js.jsx variable.other.constant.js.jsx entity.name.function.js.jsx"}},"end":"(?=$|^|[,;=}]|((?)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.js.jsx entity.name.function.js.jsx"},"2":{"name":"keyword.operator.definiteassignment.js.jsx"}},"end":"(?=$|^|[,;=}]|((?\\\\s*$)","beginCaptures":{"1":{"name":"keyword.operator.assignment.js.jsx"}},"end":"(?=$|^|[]),;}]|((?) ?","captures":{"2":{"name":"punctuation.definition.quote.begin.markdown"}},"name":"markup.quote.markdown","patterns":[{"include":"#block"}],"while":"(^|\\\\G)\\\\s*(>) ?"},"bold":{"begin":"(?(\\\\*\\\\*(?=\\\\w)|(?]*+>|(?`+)([^`]|(?!(?(?!`))`)*+\\\\k|\\\\\\\\[-\\\\]!#(-+.>\\\\[\\\\\\\\_`{}]?+|\\\\[((?[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g*+])*+](( ?\\\\[[^]]*+])|(\\\\([\\\\t ]*+?[\\\\t ]*+((?[\\"\'])(.*?)\\\\k<title>)?\\\\))))|(?!(?<=\\\\S)\\\\k<open>).)++(?<=\\\\S)(?=__\\\\b|\\\\*\\\\*)\\\\k<open>)","captures":{"1":{"name":"punctuation.definition.bold.markdown"}},"end":"(?<=\\\\S)(\\\\1)","name":"markup.bold.markdown","patterns":[{"applyEndPatternLast":1,"begin":"(?=<[^>]*?>)","end":"(?<=>)","patterns":[{"include":"text.html.derivative"}]},{"include":"#escape"},{"include":"#ampersand"},{"include":"#bracket"},{"include":"#raw"},{"include":"#bold"},{"include":"#italic"},{"include":"#image-inline"},{"include":"#link-inline"},{"include":"#link-inet"},{"include":"#link-email"},{"include":"#image-ref"},{"include":"#link-ref-literal"},{"include":"#link-ref"},{"include":"#link-ref-shortcut"},{"include":"#strikethrough"}]},"bracket":{"match":"<(?![!$/?A-Za-z])","name":"meta.other.valid-bracket.markdown"},"escape":{"match":"\\\\\\\\[-\\\\]!#(-+.>\\\\[\\\\\\\\_`{}]","name":"constant.character.escape.markdown"},"fenced_code_block":{"patterns":[{"include":"#fenced_code_block_css"},{"include":"#fenced_code_block_basic"},{"include":"#fenced_code_block_ini"},{"include":"#fenced_code_block_java"},{"include":"#fenced_code_block_lua"},{"include":"#fenced_code_block_makefile"},{"include":"#fenced_code_block_perl"},{"include":"#fenced_code_block_r"},{"include":"#fenced_code_block_ruby"},{"include":"#fenced_code_block_php"},{"include":"#fenced_code_block_sql"},{"include":"#fenced_code_block_vs_net"},{"include":"#fenced_code_block_xml"},{"include":"#fenced_code_block_xsl"},{"include":"#fenced_code_block_yaml"},{"include":"#fenced_code_block_dosbatch"},{"include":"#fenced_code_block_clojure"},{"include":"#fenced_code_block_coffee"},{"include":"#fenced_code_block_c"},{"include":"#fenced_code_block_cpp"},{"include":"#fenced_code_block_diff"},{"include":"#fenced_code_block_dockerfile"},{"include":"#fenced_code_block_git_commit"},{"include":"#fenced_code_block_git_rebase"},{"include":"#fenced_code_block_go"},{"include":"#fenced_code_block_groovy"},{"include":"#fenced_code_block_pug"},{"include":"#fenced_code_block_ignore"},{"include":"#fenced_code_block_js"},{"include":"#fenced_code_block_js_regexp"},{"include":"#fenced_code_block_json"},{"include":"#fenced_code_block_jsonc"},{"include":"#fenced_code_block_jsonl"},{"include":"#fenced_code_block_less"},{"include":"#fenced_code_block_objc"},{"include":"#fenced_code_block_swift"},{"include":"#fenced_code_block_scss"},{"include":"#fenced_code_block_perl6"},{"include":"#fenced_code_block_powershell"},{"include":"#fenced_code_block_python"},{"include":"#fenced_code_block_julia"},{"include":"#fenced_code_block_regexp_python"},{"include":"#fenced_code_block_rust"},{"include":"#fenced_code_block_scala"},{"include":"#fenced_code_block_shell"},{"include":"#fenced_code_block_ts"},{"include":"#fenced_code_block_tsx"},{"include":"#fenced_code_block_csharp"},{"include":"#fenced_code_block_fsharp"},{"include":"#fenced_code_block_dart"},{"include":"#fenced_code_block_handlebars"},{"include":"#fenced_code_block_markdown"},{"include":"#fenced_code_block_log"},{"include":"#fenced_code_block_erlang"},{"include":"#fenced_code_block_elixir"},{"include":"#fenced_code_block_latex"},{"include":"#fenced_code_block_bibtex"},{"include":"#fenced_code_block_twig"},{"include":"#fenced_code_block_yang"},{"include":"#fenced_code_block_abap"},{"include":"#fenced_code_block_restructuredtext"},{"include":"#fenced_code_block_unknown"}]},"fenced_code_block_abap":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(abap)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.abap","patterns":[{"include":"source.abap"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_basic":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(html?|shtml|xhtml|inc|tmpl|tpl)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.html","patterns":[{"include":"text.html.basic"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_bibtex":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(bibtex)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.bibtex","patterns":[{"include":"text.bibtex"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_c":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:([ch])((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.c","patterns":[{"include":"source.c"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_clojure":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(cl(?:js??|ojure))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.clojure","patterns":[{"include":"source.clojure"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_coffee":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(coffee|Cakefile|coffee.erb)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.coffee","patterns":[{"include":"source.coffee"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_cpp":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(c(?:pp|\\\\+\\\\+|xx))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.cpp source.cpp","patterns":[{"include":"source.cpp"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_csharp":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(c(?:s|sharp|#))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.csharp","patterns":[{"include":"source.cs"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_css":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(css(?:|.erb))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.css","patterns":[{"include":"source.css"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_dart":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(dart)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.dart","patterns":[{"include":"source.dart"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_diff":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(patch|diff|rej)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.diff","patterns":[{"include":"source.diff"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_dockerfile":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:([Dd]ockerfile)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.dockerfile","patterns":[{"include":"source.dockerfile"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_dosbatch":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(bat(?:|ch))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.dosbatch","patterns":[{"include":"source.batchfile"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_elixir":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(elixir)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.elixir","patterns":[{"include":"source.elixir"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_erlang":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(erlang)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.erlang","patterns":[{"include":"source.erlang"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_fsharp":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(f(?:s|sharp|#))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.fsharp","patterns":[{"include":"source.fsharp"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_git_commit":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:((?:COMMIT_EDIT|MERGE_)MSG)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.git_commit","patterns":[{"include":"text.git-commit"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_git_rebase":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(git-rebase-todo)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.git_rebase","patterns":[{"include":"text.git-rebase"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_go":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(go(?:|lang))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.go","patterns":[{"include":"source.go"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_groovy":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(g(?:roovy|vy))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.groovy","patterns":[{"include":"source.groovy"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_handlebars":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(h(?:andlebars|bs))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.handlebars","patterns":[{"include":"text.html.handlebars"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_ignore":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:((?:git|)ignore)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.ignore","patterns":[{"include":"source.ignore"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_ini":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(ini|conf)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.ini","patterns":[{"include":"source.ini"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_java":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(java|bsh)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.java","patterns":[{"include":"source.java"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_js":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(jsx??|javascript|es6|mjs|cjs|dataviewjs|\\\\{\\\\.js.+?})((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.javascript","patterns":[{"include":"source.js"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_js_regexp":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(regexp)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.js_regexp","patterns":[{"include":"source.js.regexp"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_json":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(json5??|sublime-settings|sublime-menu|sublime-keymap|sublime-mousemap|sublime-theme|sublime-build|sublime-project|sublime-completions)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.json","patterns":[{"include":"source.json"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_jsonc":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(jsonc)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.jsonc","patterns":[{"include":"source.json.comments"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_jsonl":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(jsonl(?:|ines))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.jsonl","patterns":[{"include":"source.json.lines"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_julia":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(julia|\\\\{\\\\.julia.+?})((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.julia","patterns":[{"include":"source.julia"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_latex":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:((?:la|)tex)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.latex","patterns":[{"include":"text.tex.latex"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_less":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(less)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.less","patterns":[{"include":"source.css.less"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_log":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(log)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.log","patterns":[{"include":"text.log"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_lua":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(lua)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.lua","patterns":[{"include":"source.lua"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_makefile":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:((?:[Mm]|GNUm|OCamlM)akefile)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.makefile","patterns":[{"include":"source.makefile"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_markdown":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(m(?:arkdown|d))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.markdown","patterns":[{"include":"text.html.markdown"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_objc":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(objectivec|objective-c|mm|objc|obj-c|[hm])((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.objc","patterns":[{"include":"source.objc"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_perl":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(perl|pl|pm|pod|t|PL|psgi|vcl)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.perl","patterns":[{"include":"source.perl"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_perl6":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(perl6|p6|pl6|pm6|nqp)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.perl6","patterns":[{"include":"source.perl.6"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_php":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(php3??|php4|php5|phpt|phtml|aw|ctp)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.php","patterns":[{"include":"text.html.basic"},{"include":"source.php"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_powershell":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(p(?:owershell|s1|sm1|sd1|wsh))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.powershell","patterns":[{"include":"source.powershell"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_pug":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(jade|pug)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.pug","patterns":[{"include":"text.pug"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_python":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(python|py3??|rpy|pyw|cpy|SConstruct|Sconstruct|sconstruct|SConscript|gypi??|\\\\{\\\\.python.+?})((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.python","patterns":[{"include":"source.python"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_r":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:([RSrs]|Rprofile|\\\\{\\\\.r.+?})((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.r","patterns":[{"include":"source.r"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_regexp_python":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(re)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.regexp_python","patterns":[{"include":"source.regexp.python"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_restructuredtext":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(r(?:estructuredtext|st))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.restructuredtext","patterns":[{"include":"source.rst"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_ruby":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(ruby|rbx??|rjs|Rakefile|rake|cgi|fcgi|gemspec|irbrc|Capfile|ru|prawn|Cheffile|Gemfile|Guardfile|Hobofile|Vagrantfile|Appraisals|Rantfile|Berksfile|Berksfile.lock|Thorfile|Puppetfile)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.ruby","patterns":[{"include":"source.ruby"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_rust":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(rust|rs|\\\\{\\\\.rust.+?})((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.rust","patterns":[{"include":"source.rust"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_scala":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(s(?:cala|bt))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.scala","patterns":[{"include":"source.scala"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_scss":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(scss)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.scss","patterns":[{"include":"source.css.scss"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_shell":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(shell|sh|bash|zsh|bashrc|bash_profile|bash_login|profile|bash_logout|.textmate_init|\\\\{\\\\.bash.+?})((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.shellscript","patterns":[{"include":"source.shell"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_sql":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(sql|ddl|dml)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.sql","patterns":[{"include":"source.sql"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_swift":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(swift)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.swift","patterns":[{"include":"source.swift"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_ts":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(t(?:ypescript|s))((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.typescript","patterns":[{"include":"source.ts"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_tsx":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(tsx)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.typescriptreact","patterns":[{"include":"source.tsx"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_twig":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(twig)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.twig","patterns":[{"include":"source.twig"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_unknown":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?=([^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown"},"fenced_code_block_vs_net":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(vb)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.vs_net","patterns":[{"include":"source.asp.vb.net"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_xml":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(xml|xsd|tld|jsp|pt|cpt|dtml|rss|opml)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.xml","patterns":[{"include":"text.xml"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_xsl":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(xslt??)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.xsl","patterns":[{"include":"text.xml.xsl"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_yaml":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(ya?ml)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.yaml","patterns":[{"include":"source.yaml"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"fenced_code_block_yang":{"begin":"(^|\\\\G)(\\\\s*)(`{3,}|~{3,})\\\\s*(?i:(yang)((\\\\s+|[,:?{])[^`]*)?$)","beginCaptures":{"3":{"name":"punctuation.definition.markdown"},"4":{"name":"fenced_code.block.language.markdown"},"5":{"name":"fenced_code.block.language.attributes.markdown"}},"end":"(^|\\\\G)(\\\\2|\\\\s{0,3})(\\\\3)\\\\s*$","endCaptures":{"3":{"name":"punctuation.definition.markdown"}},"name":"markup.fenced_code.block.markdown","patterns":[{"begin":"(^|\\\\G)(\\\\s*)(.*)","contentName":"meta.embedded.block.yang","patterns":[{"include":"source.yang"}],"while":"(^|\\\\G)(?!\\\\s*([`~]{3,})\\\\s*$)"}]},"frontMatter":{"applyEndPatternLast":1,"begin":"\\\\A(?=(-{3,}))","end":"^(?: {0,3}\\\\1-*[\\\\t ]*|[\\\\t ]*\\\\.{3})$","endCaptures":{"0":{"name":"punctuation.definition.end.frontmatter"}},"patterns":[{"begin":"\\\\A(-{3,})(.*)$","beginCaptures":{"1":{"name":"punctuation.definition.begin.frontmatter"},"2":{"name":"comment.frontmatter"}},"contentName":"meta.embedded.block.frontmatter","patterns":[{"include":"source.yaml"}],"while":"^(?!(?: {0,3}\\\\1-*[\\\\t ]*|[\\\\t ]*\\\\.{3})$)"}]},"heading":{"captures":{"1":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.heading.markdown"},"2":{"name":"entity.name.section.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"}]},"3":{"name":"punctuation.definition.heading.markdown"}},"match":"(#{6})\\\\s+(.*?)(?:\\\\s+(#+))?\\\\s*$","name":"heading.6.markdown"},{"captures":{"1":{"name":"punctuation.definition.heading.markdown"},"2":{"name":"entity.name.section.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"}]},"3":{"name":"punctuation.definition.heading.markdown"}},"match":"(#{5})\\\\s+(.*?)(?:\\\\s+(#+))?\\\\s*$","name":"heading.5.markdown"},{"captures":{"1":{"name":"punctuation.definition.heading.markdown"},"2":{"name":"entity.name.section.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"}]},"3":{"name":"punctuation.definition.heading.markdown"}},"match":"(#{4})\\\\s+(.*?)(?:\\\\s+(#+))?\\\\s*$","name":"heading.4.markdown"},{"captures":{"1":{"name":"punctuation.definition.heading.markdown"},"2":{"name":"entity.name.section.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"}]},"3":{"name":"punctuation.definition.heading.markdown"}},"match":"(#{3})\\\\s+(.*?)(?:\\\\s+(#+))?\\\\s*$","name":"heading.3.markdown"},{"captures":{"1":{"name":"punctuation.definition.heading.markdown"},"2":{"name":"entity.name.section.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"}]},"3":{"name":"punctuation.definition.heading.markdown"}},"match":"(#{2})\\\\s+(.*?)(?:\\\\s+(#+))?\\\\s*$","name":"heading.2.markdown"},{"captures":{"1":{"name":"punctuation.definition.heading.markdown"},"2":{"name":"entity.name.section.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"}]},"3":{"name":"punctuation.definition.heading.markdown"}},"match":"(#{1})\\\\s+(.*?)(?:\\\\s+(#+))?\\\\s*$","name":"heading.1.markdown"}]}},"match":"(?:^|\\\\G) {0,3}(#{1,6}\\\\s+(.*?)(\\\\s+#{1,6})?\\\\s*)$","name":"markup.heading.markdown"},"heading-setext":{"patterns":[{"match":"^(={3,})(?=[\\\\t ]*$\\\\n?)","name":"markup.heading.setext.1.markdown"},{"match":"^(-{3,})(?=[\\\\t ]*$\\\\n?)","name":"markup.heading.setext.2.markdown"}]},"html":{"patterns":[{"begin":"(^|\\\\G)\\\\s*(<!--)","captures":{"1":{"name":"punctuation.definition.comment.html"},"2":{"name":"punctuation.definition.comment.html"}},"end":"(-->)","name":"comment.block.html"},{"begin":"(?i)(^|\\\\G)\\\\s*(?=<(script|style|pre)(\\\\s|$|>)(?!.*?</(script|style|pre)>))","end":"(?i)(.*)((</)(script|style|pre)(>))","endCaptures":{"1":{"patterns":[{"include":"text.html.derivative"}]},"2":{"name":"meta.tag.structure.$4.end.html"},"3":{"name":"punctuation.definition.tag.begin.html"},"4":{"name":"entity.name.tag.html"},"5":{"name":"punctuation.definition.tag.end.html"}},"patterns":[{"begin":"(\\\\s*|$)","patterns":[{"include":"text.html.derivative"}],"while":"(?i)^(?!.*</(script|style|pre)>)"}]},{"begin":"(?i)(^|\\\\G)\\\\s*(?=</?[A-Za-z]+[^\\\\&/;gt\\\\s]*(\\\\s|$|/?>))","patterns":[{"include":"text.html.derivative"}],"while":"^(?!\\\\s*$)"},{"begin":"(^|\\\\G)\\\\s*(?=(<(?:[-0-9A-Za-z](/?>|\\\\s.*?>)|/[-0-9A-Za-z]>))\\\\s*$)","patterns":[{"include":"text.html.derivative"}],"while":"^(?!\\\\s*$)"}]},"image-inline":{"captures":{"1":{"name":"punctuation.definition.link.description.begin.markdown"},"2":{"name":"string.other.link.description.markdown"},"4":{"name":"punctuation.definition.link.description.end.markdown"},"5":{"name":"punctuation.definition.metadata.markdown"},"7":{"name":"punctuation.definition.link.markdown"},"8":{"name":"markup.underline.link.image.markdown"},"9":{"name":"punctuation.definition.link.markdown"},"10":{"name":"markup.underline.link.image.markdown"},"12":{"name":"string.other.link.description.title.markdown"},"13":{"name":"punctuation.definition.string.begin.markdown"},"14":{"name":"punctuation.definition.string.end.markdown"},"15":{"name":"string.other.link.description.title.markdown"},"16":{"name":"punctuation.definition.string.begin.markdown"},"17":{"name":"punctuation.definition.string.end.markdown"},"18":{"name":"string.other.link.description.title.markdown"},"19":{"name":"punctuation.definition.string.begin.markdown"},"20":{"name":"punctuation.definition.string.end.markdown"},"21":{"name":"punctuation.definition.metadata.markdown"}},"match":"(!\\\\[)((?<square>[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g<square>*+])*+)(])(\\\\()[\\\\t ]*((<)((?:\\\\\\\\[<>]|[^\\\\n<>])*)(>)|((?<url>(?>[^()\\\\s]+)|\\\\(\\\\g<url>*\\\\))*))[\\\\t ]*(?:((\\\\().+?(\\\\)))|((\\").+?(\\"))|((\').+?(\')))?\\\\s*(\\\\))","name":"meta.image.inline.markdown"},"image-ref":{"captures":{"1":{"name":"punctuation.definition.link.description.begin.markdown"},"2":{"name":"string.other.link.description.markdown"},"4":{"name":"punctuation.definition.link.description.end.markdown"},"5":{"name":"punctuation.definition.constant.markdown"},"6":{"name":"constant.other.reference.link.markdown"},"7":{"name":"punctuation.definition.constant.markdown"}},"match":"(!\\\\[)((?<square>[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g<square>*+])*+)(]) ?(\\\\[)(.*?)(])","name":"meta.image.reference.markdown"},"inline":{"patterns":[{"include":"#ampersand"},{"include":"#bracket"},{"include":"#bold"},{"include":"#italic"},{"include":"#raw"},{"include":"#strikethrough"},{"include":"#escape"},{"include":"#image-inline"},{"include":"#image-ref"},{"include":"#link-email"},{"include":"#link-inet"},{"include":"#link-inline"},{"include":"#link-ref"},{"include":"#link-ref-literal"},{"include":"#link-ref-shortcut"}]},"italic":{"begin":"(?<open>(\\\\*(?=\\\\w)|(?<!\\\\w)\\\\*|(?<!\\\\w)\\\\b_))(?=\\\\S)(?=(<[^>]*+>|(?<raw>`+)([^`]|(?!(?<!`)\\\\k<raw>(?!`))`)*+\\\\k<raw>|\\\\\\\\[-\\\\]!#(-+.>\\\\[\\\\\\\\_`{}]?+|\\\\[((?<square>[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g<square>*+])*+](( ?\\\\[[^]]*+])|(\\\\([\\\\t ]*+<?(.*?)>?[\\\\t ]*+((?<title>[\\"\'])(.*?)\\\\k<title>)?\\\\))))|\\\\k<open>\\\\k<open>|(?!(?<=\\\\S)\\\\k<open>).)++(?<=\\\\S)(?=_\\\\b|\\\\*)\\\\k<open>)","captures":{"1":{"name":"punctuation.definition.italic.markdown"}},"end":"(?<=\\\\S)(\\\\1)((?!\\\\1)|(?=\\\\1\\\\1))","name":"markup.italic.markdown","patterns":[{"applyEndPatternLast":1,"begin":"(?=<[^>]*?>)","end":"(?<=>)","patterns":[{"include":"text.html.derivative"}]},{"include":"#escape"},{"include":"#ampersand"},{"include":"#bracket"},{"include":"#raw"},{"include":"#bold"},{"include":"#image-inline"},{"include":"#link-inline"},{"include":"#link-inet"},{"include":"#link-email"},{"include":"#image-ref"},{"include":"#link-ref-literal"},{"include":"#link-ref"},{"include":"#link-ref-shortcut"},{"include":"#strikethrough"}]},"link-def":{"captures":{"1":{"name":"punctuation.definition.constant.markdown"},"2":{"name":"constant.other.reference.link.markdown"},"3":{"name":"punctuation.definition.constant.markdown"},"4":{"name":"punctuation.separator.key-value.markdown"},"5":{"name":"punctuation.definition.link.markdown"},"6":{"name":"markup.underline.link.markdown"},"7":{"name":"punctuation.definition.link.markdown"},"8":{"name":"markup.underline.link.markdown"},"9":{"name":"string.other.link.description.title.markdown"},"10":{"name":"punctuation.definition.string.begin.markdown"},"11":{"name":"punctuation.definition.string.end.markdown"},"12":{"name":"string.other.link.description.title.markdown"},"13":{"name":"punctuation.definition.string.begin.markdown"},"14":{"name":"punctuation.definition.string.end.markdown"},"15":{"name":"string.other.link.description.title.markdown"},"16":{"name":"punctuation.definition.string.begin.markdown"},"17":{"name":"punctuation.definition.string.end.markdown"}},"match":"\\\\s*(\\\\[)([^]]+?)(])(:)[\\\\t ]*(?:(<)((?:\\\\\\\\[<>]|[^\\\\n<>])*)(>)|(\\\\S+?))[\\\\t ]*(?:((\\\\().+?(\\\\)))|((\\").+?(\\"))|((\').+?(\')))?\\\\s*$","name":"meta.link.reference.def.markdown"},"link-email":{"captures":{"1":{"name":"punctuation.definition.link.markdown"},"2":{"name":"markup.underline.link.markdown"},"4":{"name":"punctuation.definition.link.markdown"}},"match":"(<)((?:mailto:)?[!#-\'*+\\\\--9=?A-Z^-~]+@[-0-9A-Za-z]+(?:\\\\.[-0-9A-Za-z]+)*)(>)","name":"meta.link.email.lt-gt.markdown"},"link-inet":{"captures":{"1":{"name":"punctuation.definition.link.markdown"},"2":{"name":"markup.underline.link.markdown"},"3":{"name":"punctuation.definition.link.markdown"}},"match":"(<)((?:https?|ftp)://.*?)(>)","name":"meta.link.inet.markdown"},"link-inline":{"captures":{"1":{"name":"punctuation.definition.link.title.begin.markdown"},"2":{"name":"string.other.link.title.markdown","patterns":[{"include":"#raw"},{"include":"#bold"},{"include":"#italic"},{"include":"#strikethrough"},{"include":"#image-inline"}]},"4":{"name":"punctuation.definition.link.title.end.markdown"},"5":{"name":"punctuation.definition.metadata.markdown"},"7":{"name":"punctuation.definition.link.markdown"},"8":{"name":"markup.underline.link.markdown"},"9":{"name":"punctuation.definition.link.markdown"},"10":{"name":"markup.underline.link.markdown"},"12":{"name":"string.other.link.description.title.markdown"},"13":{"name":"punctuation.definition.string.begin.markdown"},"14":{"name":"punctuation.definition.string.end.markdown"},"15":{"name":"string.other.link.description.title.markdown"},"16":{"name":"punctuation.definition.string.begin.markdown"},"17":{"name":"punctuation.definition.string.end.markdown"},"18":{"name":"string.other.link.description.title.markdown"},"19":{"name":"punctuation.definition.string.begin.markdown"},"20":{"name":"punctuation.definition.string.end.markdown"},"21":{"name":"punctuation.definition.metadata.markdown"}},"match":"(\\\\[)((?<square>[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g<square>*+])*+)(])(\\\\()[\\\\t ]*((<)((?:\\\\\\\\[<>]|[^\\\\n<>])*)(>)|((?<url>(?>[^()\\\\s]+)|\\\\(\\\\g<url>*\\\\))*))[\\\\t ]*(?:((\\\\()[^()]*(\\\\)))|((\\")[^\\"]*(\\"))|((\')[^\']*(\')))?\\\\s*(\\\\))","name":"meta.link.inline.markdown"},"link-ref":{"captures":{"1":{"name":"punctuation.definition.link.title.begin.markdown"},"2":{"name":"string.other.link.title.markdown","patterns":[{"include":"#raw"},{"include":"#bold"},{"include":"#italic"},{"include":"#strikethrough"},{"include":"#image-inline"}]},"4":{"name":"punctuation.definition.link.title.end.markdown"},"5":{"name":"punctuation.definition.constant.begin.markdown"},"6":{"name":"constant.other.reference.link.markdown"},"7":{"name":"punctuation.definition.constant.end.markdown"}},"match":"(?<![]\\\\\\\\])(\\\\[)((?<square>[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g<square>*+])*+)(])(\\\\[)([^]]*+)(])","name":"meta.link.reference.markdown"},"link-ref-literal":{"captures":{"1":{"name":"punctuation.definition.link.title.begin.markdown"},"2":{"name":"string.other.link.title.markdown"},"4":{"name":"punctuation.definition.link.title.end.markdown"},"5":{"name":"punctuation.definition.constant.begin.markdown"},"6":{"name":"punctuation.definition.constant.end.markdown"}},"match":"(?<![]\\\\\\\\])(\\\\[)((?<square>[^]\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[\\\\g<square>*+])*+)(]) ?(\\\\[)(])","name":"meta.link.reference.literal.markdown"},"link-ref-shortcut":{"captures":{"1":{"name":"punctuation.definition.link.title.begin.markdown"},"2":{"name":"string.other.link.title.markdown"},"3":{"name":"punctuation.definition.link.title.end.markdown"}},"match":"(?<![]\\\\\\\\])(\\\\[)((?:[^]\\\\[\\\\\\\\\\\\s]|\\\\\\\\[]\\\\[])+?)((?<!\\\\\\\\)])","name":"meta.link.reference.markdown"},"list_paragraph":{"begin":"(^|\\\\G)(?=\\\\S)(?![*->]\\\\s|[0-9]+\\\\.\\\\s)","name":"meta.paragraph.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"},{"include":"#heading-setext"}],"while":"(^|\\\\G)(?!\\\\s*$|#| {0,3}([-*>_] {2,}){3,}[\\\\t ]*$\\\\n?| {0,3}[*->]| {0,3}[0-9]+\\\\.)"},"lists":{"patterns":[{"begin":"(^|\\\\G)( {0,3})([-*+])([\\\\t ])","beginCaptures":{"3":{"name":"punctuation.definition.list.begin.markdown"}},"name":"markup.list.unnumbered.markdown","patterns":[{"include":"#block"},{"include":"#list_paragraph"}],"while":"((^|\\\\G)( {2,4}|\\\\t))|^([\\\\t ]*)$"},{"begin":"(^|\\\\G)( {0,3})([0-9]+[).])([\\\\t ])","beginCaptures":{"3":{"name":"punctuation.definition.list.begin.markdown"}},"name":"markup.list.numbered.markdown","patterns":[{"include":"#block"},{"include":"#list_paragraph"}],"while":"((^|\\\\G)( {2,4}|\\\\t))|^([\\\\t ]*)$"}]},"paragraph":{"begin":"(^|\\\\G) {0,3}(?=[^\\\\t\\\\n ])","name":"meta.paragraph.markdown","patterns":[{"include":"#inline"},{"include":"text.html.derivative"},{"include":"#heading-setext"}],"while":"(^|\\\\G)((?=\\\\s*[-=]{3,}\\\\s*$)| {4,}(?=[^\\\\t\\\\n ]))"},"raw":{"captures":{"1":{"name":"punctuation.definition.raw.markdown"},"3":{"name":"punctuation.definition.raw.markdown"}},"match":"(`+)((?:[^`]|(?!(?<!`)\\\\1(?!`))`)*+)(\\\\1)","name":"markup.inline.raw.string.markdown"},"raw_block":{"begin":"(^|\\\\G)( {4}|\\\\t)","name":"markup.raw.block.markdown","while":"(^|\\\\G)( {4}|\\\\t)"},"separator":{"match":"(^|\\\\G) {0,3}([-*_])( {0,2}\\\\2){2,}[\\\\t ]*$\\\\n?","name":"meta.separator.markdown"},"strikethrough":{"captures":{"1":{"name":"punctuation.definition.strikethrough.markdown"},"2":{"patterns":[{"applyEndPatternLast":1,"begin":"(?=<[^>]*?>)","end":"(?<=>)","patterns":[{"include":"text.html.derivative"}]},{"include":"#escape"},{"include":"#ampersand"},{"include":"#bracket"},{"include":"#raw"},{"include":"#bold"},{"include":"#italic"},{"include":"#image-inline"},{"include":"#link-inline"},{"include":"#link-inet"},{"include":"#link-email"},{"include":"#image-ref"},{"include":"#link-ref-literal"},{"include":"#link-ref"},{"include":"#link-ref-shortcut"}]},"3":{"name":"punctuation.definition.strikethrough.markdown"}},"match":"(?<!\\\\\\\\)(~{2,})(?!(?<=\\\\w~~)_)((?:[^~]|(?!(?<![\\\\\\\\~])\\\\1(?!~))~)*+)(\\\\1)(?!(?<=_\\\\1)\\\\w)","name":"markup.strikethrough.markdown"},"table":{"begin":"(^|\\\\G)(\\\\|)(?=[^|].+\\\\|\\\\s*$)","beginCaptures":{"2":{"name":"punctuation.definition.table.markdown"}},"name":"markup.table.markdown","patterns":[{"match":"\\\\|","name":"punctuation.definition.table.markdown"},{"captures":{"1":{"name":"punctuation.separator.table.markdown"}},"match":"(?<=\\\\|)\\\\s*(:?-+:?)\\\\s*(?=\\\\|)"},{"captures":{"1":{"patterns":[{"include":"#inline"}]}},"match":"(?<=\\\\|)\\\\s*(?=\\\\S)((\\\\\\\\\\\\||[^|])+)(?<=\\\\S)\\\\s*(?=\\\\|)"}],"while":"(^|\\\\G)(?=\\\\|)"}},"scopeName":"text.html.markdown","embeddedLangs":[],"aliases":["md"],"embeddedLangsLazy":["css","html","ini","java","lua","make","perl","r","ruby","php","sql","vb","xml","xsl","yaml","bat","clojure","coffee","c","cpp","diff","docker","git-commit","git-rebase","go","groovy","pug","javascript","json","jsonc","jsonl","less","objective-c","swift","scss","raku","powershell","python","julia","regexp","rust","scala","shellscript","typescript","tsx","csharp","fsharp","dart","handlebars","log","erlang","elixir","latex","bibtex","abap","rst","html-derivative"]}'))],Jc=[Object.freeze(JSON.parse('{"displayName":"PowerShell","name":"powershell","patterns":[{"begin":"<#","beginCaptures":{"0":{"name":"punctuation.definition.comment.block.begin.powershell"}},"end":"#>","endCaptures":{"0":{"name":"punctuation.definition.comment.block.end.powershell"}},"name":"comment.block.powershell","patterns":[{"include":"#commentEmbeddedDocs"}]},{"match":"[2-6]>&1|>>?|<<|[<>]|>\\\\||[1-6]>|[1-6]>>","name":"keyword.operator.redirection.powershell"},{"include":"#commands"},{"include":"#commentLine"},{"include":"#variable"},{"include":"#subexpression"},{"include":"#function"},{"include":"#attribute"},{"include":"#UsingDirective"},{"include":"#type"},{"include":"#hashtable"},{"include":"#doubleQuotedString"},{"include":"#scriptblock"},{"include":"#doubleQuotedStringEscapes"},{"applyEndPatternLast":true,"begin":"[\'‘-‛]","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.powershell"}},"end":"[\'‘-‛]","endCaptures":{"0":{"name":"punctuation.definition.string.end.powershell"}},"name":"string.quoted.single.powershell","patterns":[{"match":"[\'‘-‛]{2}","name":"constant.character.escape.powershell"}]},{"begin":"(@[\\"“”„])\\\\s*$","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.powershell"}},"end":"^[\\"“”„]@","endCaptures":{"0":{"name":"punctuation.definition.string.end.powershell"}},"name":"string.quoted.double.heredoc.powershell","patterns":[{"include":"#variableNoProperty"},{"include":"#doubleQuotedStringEscapes"},{"include":"#interpolation"}]},{"begin":"(@[\'‘-‛])\\\\s*$","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.powershell"}},"end":"^[\'‘-‛]@","endCaptures":{"0":{"name":"punctuation.definition.string.end.powershell"}},"name":"string.quoted.single.heredoc.powershell"},{"include":"#numericConstant"},{"begin":"(@)(\\\\()","beginCaptures":{"1":{"name":"keyword.other.array.begin.powershell"},"2":{"name":"punctuation.section.group.begin.powershell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.section.group.end.powershell"}},"name":"meta.group.array-expression.powershell","patterns":[{"include":"$self"}]},{"begin":"((\\\\$))(\\\\()","beginCaptures":{"1":{"name":"keyword.other.substatement.powershell"},"2":{"name":"punctuation.definition.subexpression.powershell"},"3":{"name":"punctuation.section.group.begin.powershell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.section.group.end.powershell"}},"name":"meta.group.complex.subexpression.powershell","patterns":[{"include":"$self"}]},{"match":"\\\\b((([-.0-9A-Z_a-z]+)\\\\.(?i:exe|com|cmd|bat)))\\\\b","name":"support.function.powershell"},{"match":"(?<![-.\\\\w])((?i:begin|break|catch|clean|continue|data|default|define|do|dynamicparam|else|elseif|end|exit|finally|for|from|if|in|inlinescript|parallel|param|process|return|sequence|switch|throw|trap|try|until|var|while)|[%?])(?!\\\\w)","name":"keyword.control.powershell"},{"match":"(?<![-\\\\w]|[^)]\\\\.)((?i:(foreach|where)(?!-object))|[%?])(?!\\\\w)","name":"keyword.control.powershell"},{"begin":"(?<!\\\\w)(--%)(?!\\\\w)","beginCaptures":{"1":{"name":"keyword.control.powershell"}},"end":"$","patterns":[{"match":".+","name":"string.unquoted.powershell"}]},{"match":"(?<!\\\\w)((?i:hidden|static))(?!\\\\w)","name":"storage.modifier.powershell"},{"captures":{"1":{"name":"storage.type.powershell"},"2":{"name":"entity.name.function"}},"match":"(?<![-\\\\w])((?i:class)|[%?])\\\\s+([-_\\\\p{L}\\\\d]?{1,})\\\\b"},{"match":"(?<!\\\\w)-(?i:is(?:not)?|as)\\\\b","name":"keyword.operator.comparison.powershell"},{"match":"(?<!\\\\w)-(?i:[ci]?(?:eq|ne|[gl][et]|(?:not)?(?:like|match|contains|in)|replace))(?!\\\\p{L})","name":"keyword.operator.comparison.powershell"},{"match":"(?<!\\\\w)-(?i:join|split)(?!\\\\p{L})|!","name":"keyword.operator.unary.powershell"},{"match":"(?<!\\\\w)-(?i:and|or|not|xor)(?!\\\\p{L})|!","name":"keyword.operator.logical.powershell"},{"match":"(?<!\\\\w)-(?i:band|bor|bnot|bxor|shl|shr)(?!\\\\p{L})","name":"keyword.operator.bitwise.powershell"},{"match":"(?<!\\\\w)-(?i:f)(?!\\\\p{L})","name":"keyword.operator.string-format.powershell"},{"match":"[-%*+/]?=|[-%*+/]","name":"keyword.operator.assignment.powershell"},{"match":"\\\\|{2}|&{2}|;","name":"punctuation.terminator.statement.powershell"},{"match":"&|(?<!\\\\w)\\\\.(?= )|[,`|]","name":"keyword.operator.other.powershell"},{"match":"(?<!\\\\s|^)\\\\.\\\\.(?=-?\\\\d|[$(])","name":"keyword.operator.range.powershell"}],"repository":{"RequiresDirective":{"begin":"(?<=#)(?i:(requires))\\\\s","beginCaptures":{"0":{"name":"keyword.control.requires.powershell"}},"end":"$","name":"meta.requires.powershell","patterns":[{"match":"-(?i:Modules|PSSnapin|RunAsAdministrator|ShellId|Version|Assembly|PSEdition)","name":"keyword.other.powershell"},{"match":"(?<!-)\\\\b\\\\p{L}+|\\\\d+(?:\\\\.\\\\d+)*","name":"variable.parameter.powershell"},{"include":"#hashtable"}]},"UsingDirective":{"captures":{"1":{"name":"keyword.control.using.powershell"},"2":{"name":"keyword.other.powershell"},"3":{"name":"variable.parameter.powershell"}},"match":"(?<!\\\\w)(?i:(using))\\\\s+(?i:(namespace|module))\\\\s+(?i:((?:\\\\w+\\\\.?)+))"},"attribute":{"begin":"(\\\\[)\\\\s*\\\\b(?i)(cmdletbinding|alias|outputtype|parameter|validatenotnull|validatenotnullorempty|validatecount|validateset|allownull|allowemptycollection|allowemptystring|validatescript|validaterange|validatepattern|validatelength|supportswildcards)\\\\b","beginCaptures":{"1":{"name":"punctuation.section.bracket.begin.powershell"},"2":{"name":"support.function.attribute.powershell"}},"end":"(])","endCaptures":{"1":{"name":"punctuation.section.bracket.end.powershell"}},"name":"meta.attribute.powershell","patterns":[{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.section.group.begin.powershell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.section.group.end.powershell"}},"patterns":[{"include":"$self"},{"captures":{"1":{"name":"variable.parameter.attribute.powershell"},"2":{"name":"keyword.operator.assignment.powershell"}},"match":"(?i)\\\\b(mandatory|valuefrompipeline|valuefrompipelinebypropertyname|valuefromremainingarguments|position|parametersetname|defaultparametersetname|supportsshouldprocess|supportspaging|positionalbinding|helpuri|confirmimpact|helpmessage)\\\\b\\\\s+{0,1}(=)?"}]}]},"commands":{"patterns":[{"match":"(?:([-:\\\\\\\\_\\\\p{L}\\\\d])*\\\\\\\\)?\\\\b(?i:Add|Approve|Assert|Backup|Block|Build|Checkpoint|Clear|Close|Compare|Complete|Compress|Confirm|Connect|Convert|ConvertFrom|ConvertTo|Copy|Debug|Deny|Deploy|Disable|Disconnect|Dismount|Edit|Enable|Enter|Exit|Expand|Export|Find|Format|Get|Grant|Group|Hide|Import|Initialize|Install|Invoke|Join|Limit|Lock|Measure|Merge|Mount|Move|New|Open|Optimize|Out|Ping|Pop|Protect|Publish|Push|Read|Receive|Redo|Register|Remove|Rename|Repair|Request|Reset|Resize|Resolve|Restart|Restore|Resume|Revoke|Save|Search|Select|Send|Set|Show|Skip|Split|Start|Step|Stop|Submit|Suspend|Switch|Sync|Test|Trace|Unblock|Undo|Uninstall|Unlock|Unprotect|Unpublish|Unregister|Update|Use|Wait|Watch|Write)-.+?(?:\\\\.(?i:exe|cmd|bat|ps1))?\\\\b","name":"support.function.powershell"},{"match":"(?<!\\\\w)(?i:foreach-object)(?!\\\\w)","name":"support.function.powershell"},{"match":"(?<!\\\\w)(?i:where-object)(?!\\\\w)","name":"support.function.powershell"},{"match":"(?<!\\\\w)(?i:sort-object)(?!\\\\w)","name":"support.function.powershell"},{"match":"(?<!\\\\w)(?i:tee-object)(?!\\\\w)","name":"support.function.powershell"}]},"commentEmbeddedDocs":{"patterns":[{"captures":{"1":{"name":"constant.string.documentation.powershell"},"2":{"name":"keyword.operator.documentation.powershell"}},"match":"(?:^|\\\\G)(?i:\\\\s*(\\\\.)(COMPONENT|DESCRIPTION|EXAMPLE|FUNCTIONALITY|INPUTS|LINK|NOTES|OUTPUTS|ROLE|SYNOPSIS))\\\\s*$","name":"comment.documentation.embedded.powershell"},{"captures":{"1":{"name":"constant.string.documentation.powershell"},"2":{"name":"keyword.operator.documentation.powershell"},"3":{"name":"keyword.operator.documentation.powershell"}},"match":"(?:^|\\\\G)(?i:\\\\s*(\\\\.)(EXTERNALHELP|FORWARDHELP(?:CATEGORY|TARGETNAME)|PARAMETER|REMOTEHELPRUNSPACE))\\\\s+(.+?)\\\\s*$","name":"comment.documentation.embedded.powershell"}]},"commentLine":{"begin":"(?<![-\\\\\\\\`])(#)#*","captures":{"1":{"name":"punctuation.definition.comment.powershell"}},"end":"$\\\\n?","name":"comment.line.powershell","patterns":[{"include":"#commentEmbeddedDocs"},{"include":"#RequiresDirective"}]},"doubleQuotedString":{"applyEndPatternLast":true,"begin":"[\\"“”„]","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.powershell"}},"end":"[\\"“”„]","endCaptures":{"0":{"name":"punctuation.definition.string.end.powershell"}},"name":"string.quoted.double.powershell","patterns":[{"match":"(?i)\\\\b[-%+.0-9A-Z_]+@[-.0-9A-Z]+\\\\.[A-Z]{2,64}\\\\b"},{"include":"#variableNoProperty"},{"include":"#doubleQuotedStringEscapes"},{"match":"[\\"“”„]{2}","name":"constant.character.escape.powershell"},{"include":"#interpolation"},{"match":"`\\\\s*$","name":"keyword.other.powershell"}]},"doubleQuotedStringEscapes":{"patterns":[{"match":"`[\\"$\'0`abefnrtv‘-„]","name":"constant.character.escape.powershell"},{"include":"#unicodeEscape"}]},"function":{"begin":"^\\\\s*+(?i)(function|filter|configuration|workflow)\\\\s+(?:(global|local|script|private):)?([-._\\\\p{L}\\\\d]+)","beginCaptures":{"0":{"name":"meta.function.powershell"},"1":{"name":"storage.type.powershell"},"2":{"name":"storage.modifier.scope.powershell"},"3":{"name":"entity.name.function.powershell"}},"end":"(?=[({])","patterns":[{"include":"#commentLine"}]},"hashtable":{"begin":"(@)(\\\\{)","beginCaptures":{"1":{"name":"keyword.other.hashtable.begin.powershell"},"2":{"name":"punctuation.section.braces.begin.powershell"}},"end":"(})","endCaptures":{"1":{"name":"punctuation.section.braces.end.powershell"}},"name":"meta.hashtable.powershell","patterns":[{"captures":{"1":{"name":"punctuation.definition.string.begin.powershell"},"2":{"name":"variable.other.readwrite.powershell"},"3":{"name":"punctuation.definition.string.end.powershell"},"4":{"name":"keyword.operator.assignment.powershell"}},"match":"\\\\b([\\"\']?)(\\\\w+)([\\"\']?)\\\\s+{0,1}(=)\\\\s+{0,1}","name":"meta.hashtable.assignment.powershell"},{"include":"#scriptblock"},{"include":"$self"}]},"interpolation":{"begin":"(((\\\\$)))((\\\\())","beginCaptures":{"1":{"name":"keyword.other.substatement.powershell"},"2":{"name":"punctuation.definition.substatement.powershell"},"3":{"name":"punctuation.section.embedded.substatement.begin.powershell"},"4":{"name":"punctuation.section.group.begin.powershell"},"5":{"name":"punctuation.section.embedded.substatement.begin.powershell"}},"contentName":"interpolated.complex.source.powershell","end":"(\\\\))","endCaptures":{"0":{"name":"punctuation.section.group.end.powershell"},"1":{"name":"punctuation.section.embedded.substatement.end.powershell"}},"name":"meta.embedded.substatement.powershell","patterns":[{"include":"$self"}]},"numericConstant":{"patterns":[{"captures":{"1":{"name":"constant.numeric.hex.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?0[Xx][_\\\\h]+(?:[LUlu]|UL|Ul|uL|ul|LU|Lu|lU|lu)?)((?i:[gkmpt]b)?)\\\\b"},{"captures":{"1":{"name":"constant.numeric.integer.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?[0-9_]+{0,1}\\\\.[0-9_]+(?:[Ee][0-9]+)?[DFMdfm]?)((?i:[gkmpt]b)?)\\\\b"},{"captures":{"1":{"name":"constant.numeric.octal.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?0[Bb][01_]+(?:[LUlu]|UL|Ul|uL|ul|LU|Lu|lU|lu)?)((?i:[gkmpt]b)?)\\\\b"},{"captures":{"1":{"name":"constant.numeric.integer.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?[0-9_]+[Ee][0-9_]?+[DFMdfm]?)((?i:[gkmpt]b)?)\\\\b"},{"captures":{"1":{"name":"constant.numeric.integer.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?[0-9_]+\\\\.[Ee][0-9_]?+[DFMdfm]?)((?i:[gkmpt]b)?)\\\\b"},{"captures":{"1":{"name":"constant.numeric.integer.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?[0-9_]+\\\\.?[DFMdfm])((?i:[gkmpt]b)?)\\\\b"},{"captures":{"1":{"name":"constant.numeric.integer.powershell"},"2":{"name":"keyword.other.powershell"}},"match":"(?<!\\\\w)([-+]?[0-9_]+\\\\.?(?:[LUlu]|UL|Ul|uL|ul|LU|Lu|lU|lu)?)((?i:[gkmpt]b)?)\\\\b"}]},"scriptblock":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.braces.begin.powershell"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.braces.end.powershell"}},"name":"meta.scriptblock.powershell","patterns":[{"include":"$self"}]},"subexpression":{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.section.group.begin.powershell"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.section.group.end.powershell"}},"name":"meta.group.simple.subexpression.powershell","patterns":[{"include":"$self"}]},"type":{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.section.bracket.begin.powershell"}},"end":"]","endCaptures":{"0":{"name":"punctuation.section.bracket.end.powershell"}},"patterns":[{"match":"(?!\\\\d+|\\\\.)[.\\\\p{L}\\\\p{N}]+","name":"storage.type.powershell"},{"include":"$self"}]},"unicodeEscape":{"patterns":[{"match":"`u\\\\{(?:(?:10)?(\\\\h){1,4}|0?\\\\g<1>{1,5})}","name":"constant.character.escape.powershell"},{"match":"`u(?:\\\\{\\\\h{0,6}.)?","name":"invalid.character.escape.powershell"}]},"variable":{"patterns":[{"captures":{"0":{"name":"constant.language.powershell"},"1":{"name":"punctuation.definition.variable.powershell"}},"match":"(\\\\$)(?i:(False|Null|True))\\\\b"},{"captures":{"0":{"name":"support.constant.variable.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"3":{"name":"variable.other.member.powershell"}},"match":"(\\\\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?\\\\b"},{"captures":{"0":{"name":"support.variable.automatic.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"3":{"name":"variable.other.member.powershell"}},"match":"(\\\\$)([$?^]|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\\\\b)((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?"},{"captures":{"0":{"name":"variable.language.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"3":{"name":"variable.other.member.powershell"}},"match":"(\\\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|InformationPreference|LogCommandHealthEvent|LogCommandLifecycleEvent|LogEngineHealthEvent|LogEngineLifecycleEvent|LogProviderHealthEvent|LogProviderLifecycleEvent|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|PSCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoLoadingPreference|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|ProgressPreference|VerbosePreference|WarningPreference|WhatIfPreference))((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?\\\\b"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"storage.modifier.scope.powershell"},"4":{"name":"variable.other.member.powershell"}},"match":"(?i:([$@])(global|local|private|script|using|workflow):([_\\\\p{L}\\\\d]+))((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"punctuation.section.braces.begin.powershell"},"3":{"name":"storage.modifier.scope.powershell"},"5":{"name":"punctuation.section.braces.end.powershell"},"6":{"name":"variable.other.member.powershell"}},"match":"(?i:(\\\\$)(\\\\{)(global|local|private|script|using|workflow):([^}]*[^`}])(}))((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"support.variable.drive.powershell"},"4":{"name":"variable.other.member.powershell"}},"match":"(?i:([$@])([_\\\\p{L}\\\\d]+:)?([_\\\\p{L}\\\\d]+))((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"punctuation.section.braces.begin.powershell"},"3":{"name":"support.variable.drive.powershell"},"5":{"name":"punctuation.section.braces.end.powershell"},"6":{"name":"variable.other.member.powershell"}},"match":"(?i:(\\\\$)(\\\\{)([_\\\\p{L}\\\\d]+:)?([^}]*[^`}])(}))((?:\\\\.[_\\\\p{L}\\\\d]+)*\\\\b)?"}]},"variableNoProperty":{"patterns":[{"captures":{"0":{"name":"constant.language.powershell"},"1":{"name":"punctuation.definition.variable.powershell"}},"match":"(\\\\$)(?i:(False|Null|True))\\\\b"},{"captures":{"0":{"name":"support.constant.variable.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"3":{"name":"variable.other.member.powershell"}},"match":"(\\\\$)(?i:(Error|ExecutionContext|Host|Home|PID|PsHome|PsVersionTable|ShellID))\\\\b"},{"captures":{"0":{"name":"support.variable.automatic.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"3":{"name":"variable.other.member.powershell"}},"match":"(\\\\$)([$?^]|(?i:_|Args|ConsoleFileName|Event|EventArgs|EventSubscriber|ForEach|Input|LastExitCode|Matches|MyInvocation|NestedPromptLevel|Profile|PSBoundParameters|PsCmdlet|PsCulture|PSDebugContext|PSItem|PSCommandPath|PSScriptRoot|PsUICulture|Pwd|Sender|SourceArgs|SourceEventArgs|StackTrace|Switch|This)\\\\b)"},{"captures":{"0":{"name":"variable.language.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"3":{"name":"variable.other.member.powershell"}},"match":"(\\\\$)(?i:(ConfirmPreference|DebugPreference|ErrorActionPreference|ErrorView|FormatEnumerationLimit|InformationPreference|LogCommandHealthEvent|LogCommandLifecycleEvent|LogEngineHealthEvent|LogEngineLifecycleEvent|LogProviderHealthEvent|LogProviderLifecycleEvent|MaximumAliasCount|MaximumDriveCount|MaximumErrorCount|MaximumFunctionCount|MaximumHistoryCount|MaximumVariableCount|OFS|OutputEncoding|PSCulture|PSDebugContext|PSDefaultParameterValues|PSEmailServer|PSItem|PSModuleAutoLoadingPreference|PSModuleAutoloadingPreference|PSSenderInfo|PSSessionApplicationName|PSSessionConfigurationName|PSSessionOption|ProgressPreference|VerbosePreference|WarningPreference|WhatIfPreference))\\\\b"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"storage.modifier.scope.powershell"},"4":{"name":"variable.other.member.powershell"}},"match":"(?i:(\\\\$)(global|local|private|script|using|workflow):([_\\\\p{L}\\\\d]+))"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"storage.modifier.scope.powershell"},"4":{"name":"keyword.other.powershell"},"5":{"name":"variable.other.member.powershell"}},"match":"(?i:(\\\\$)(\\\\{)(global|local|private|script|using|workflow):([^}]*[^`}])(}))"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"support.variable.drive.powershell"},"4":{"name":"variable.other.member.powershell"}},"match":"(?i:(\\\\$)([_\\\\p{L}\\\\d]+:)?([_\\\\p{L}\\\\d]+))"},{"captures":{"0":{"name":"variable.other.readwrite.powershell"},"1":{"name":"punctuation.definition.variable.powershell"},"2":{"name":"punctuation.section.braces.begin"},"3":{"name":"support.variable.drive.powershell"},"5":{"name":"punctuation.section.braces.end"}},"match":"(?i:(\\\\$)(\\\\{)([_\\\\p{L}\\\\d]+:)?([^}]*[^`}])(}))"}]}},"scopeName":"source.powershell","aliases":["ps","ps1"]}'))],Qc=[Object.freeze(JSON.parse(`{"displayName":"Python","name":"python","patterns":[{"include":"#statement"},{"include":"#expression"}],"repository":{"annotated-parameter":{"begin":"\\\\b([_[:alpha:]]\\\\w*)\\\\s*(:)","beginCaptures":{"1":{"name":"variable.parameter.function.language.python"},"2":{"name":"punctuation.separator.annotation.python"}},"end":"(,)|(?=\\\\))","endCaptures":{"1":{"name":"punctuation.separator.parameters.python"}},"patterns":[{"include":"#expression"},{"match":"=(?!=)","name":"keyword.operator.assignment.python"}]},"assignment-operator":{"match":"<<=|>>=|//=|\\\\*\\\\*=|\\\\+=|-=|/=|@=|\\\\*=|%=|~=|\\\\^=|&=|\\\\|=|=(?!=)","name":"keyword.operator.assignment.python"},"backticks":{"begin":"\`","end":"\`|(?<!\\\\\\\\)(\\\\n)","name":"invalid.deprecated.backtick.python","patterns":[{"include":"#expression"}]},"builtin-callables":{"patterns":[{"include":"#illegal-names"},{"include":"#illegal-object-name"},{"include":"#builtin-exceptions"},{"include":"#builtin-functions"},{"include":"#builtin-types"}]},"builtin-exceptions":{"match":"(?<!\\\\.)\\\\b((Arithmetic|Assertion|Attribute|Buffer|BlockingIO|BrokenPipe|ChildProcess|(Connection(Aborted|Refused|Reset)?)|EOF|Environment|FileExists|FileNotFound|FloatingPoint|IO|Import|Indentation|Index|Interrupted|IsADirectory|NotADirectory|Permission|ProcessLookup|Timeout|Key|Lookup|Memory|Name|NotImplemented|OS|Overflow|Reference|Runtime|Recursion|Syntax|System|Tab|Type|UnboundLocal|Unicode(Encode|Decode|Translate)?|Value|Windows|ZeroDivision|ModuleNotFound)Error|((Pending)?Deprecation|Runtime|Syntax|User|Future|Import|Unicode|Bytes|Resource)?Warning|SystemExit|Stop(Async)?Iteration|KeyboardInterrupt|GeneratorExit|(Base)?Exception)\\\\b","name":"support.type.exception.python"},"builtin-functions":{"patterns":[{"match":"(?<!\\\\.)\\\\b(__import__|abs|aiter|all|any|anext|ascii|bin|breakpoint|callable|chr|compile|copyright|credits|delattr|dir|divmod|enumerate|eval|exec|exit|filter|format|getattr|globals|hasattr|hash|help|hex|id|input|isinstance|issubclass|iter|len|license|locals|map|max|memoryview|min|next|oct|open|ord|pow|print|quit|range|reload|repr|reversed|round|setattr|sorted|sum|vars|zip)\\\\b","name":"support.function.builtin.python"},{"match":"(?<!\\\\.)\\\\b(file|reduce|intern|raw_input|unicode|cmp|basestring|execfile|long|xrange)\\\\b","name":"variable.legacy.builtin.python"}]},"builtin-possible-callables":{"patterns":[{"include":"#builtin-callables"},{"include":"#magic-names"}]},"builtin-types":{"match":"(?<!\\\\.)\\\\b(bool|bytearray|bytes|classmethod|complex|dict|float|frozenset|int|list|object|property|set|slice|staticmethod|str|tuple|type|super)\\\\b","name":"support.type.python"},"call-wrapper-inheritance":{"begin":"\\\\b(?=([_[:alpha:]]\\\\w*)\\\\s*(\\\\())","end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.arguments.end.python"}},"name":"meta.function-call.python","patterns":[{"include":"#inheritance-name"},{"include":"#function-arguments"}]},"class-declaration":{"patterns":[{"begin":"\\\\s*(class)\\\\s+(?=[_[:alpha:]]\\\\w*\\\\s*([(:]))","beginCaptures":{"1":{"name":"storage.type.class.python"}},"end":"(:)","endCaptures":{"1":{"name":"punctuation.section.class.begin.python"}},"name":"meta.class.python","patterns":[{"include":"#class-name"},{"include":"#class-inheritance"}]}]},"class-inheritance":{"begin":"(\\\\()","beginCaptures":{"1":{"name":"punctuation.definition.inheritance.begin.python"}},"end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.inheritance.end.python"}},"name":"meta.class.inheritance.python","patterns":[{"match":"(\\\\*\\\\*?)","name":"keyword.operator.unpacking.arguments.python"},{"match":",","name":"punctuation.separator.inheritance.python"},{"match":"=(?!=)","name":"keyword.operator.assignment.python"},{"match":"\\\\bmetaclass\\\\b","name":"support.type.metaclass.python"},{"include":"#illegal-names"},{"include":"#class-kwarg"},{"include":"#call-wrapper-inheritance"},{"include":"#expression-base"},{"include":"#member-access-class"},{"include":"#inheritance-identifier"}]},"class-kwarg":{"captures":{"1":{"name":"entity.other.inherited-class.python variable.parameter.class.python"},"2":{"name":"keyword.operator.assignment.python"}},"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\s*(=)(?!=)"},"class-name":{"patterns":[{"include":"#illegal-object-name"},{"include":"#builtin-possible-callables"},{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b","name":"entity.name.type.class.python"}]},"codetags":{"captures":{"1":{"name":"keyword.codetag.notation.python"}},"match":"\\\\b(NOTE|XXX|HACK|FIXME|BUG|TODO)\\\\b"},"comments":{"patterns":[{"begin":"#\\\\s*(type:)\\\\s*+(?!$|#)","beginCaptures":{"0":{"name":"meta.typehint.comment.python"},"1":{"name":"comment.typehint.directive.notation.python"}},"contentName":"meta.typehint.comment.python","end":"$|(?=#)","name":"comment.line.number-sign.python","patterns":[{"match":"\\\\Gignore(?=\\\\s*(?:$|#))","name":"comment.typehint.ignore.notation.python"},{"match":"(?<!\\\\.)\\\\b(bool|bytes|float|int|object|str|List|Dict|Iterable|Sequence|Set|FrozenSet|Callable|Union|Tuple|Any|None)\\\\b","name":"comment.typehint.type.notation.python"},{"match":"([]()*,.=\\\\[]|(->))","name":"comment.typehint.punctuation.notation.python"},{"match":"([_[:alpha:]]\\\\w*)","name":"comment.typehint.variable.notation.python"}]},{"include":"#comments-base"}]},"comments-base":{"begin":"(#)","beginCaptures":{"1":{"name":"punctuation.definition.comment.python"}},"end":"$()","name":"comment.line.number-sign.python","patterns":[{"include":"#codetags"}]},"comments-string-double-three":{"begin":"(#)","beginCaptures":{"1":{"name":"punctuation.definition.comment.python"}},"end":"($|(?=\\"\\"\\"))","name":"comment.line.number-sign.python","patterns":[{"include":"#codetags"}]},"comments-string-single-three":{"begin":"(#)","beginCaptures":{"1":{"name":"punctuation.definition.comment.python"}},"end":"($|(?='''))","name":"comment.line.number-sign.python","patterns":[{"include":"#codetags"}]},"curly-braces":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.dict.begin.python"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.dict.end.python"}},"patterns":[{"match":":","name":"punctuation.separator.dict.python"},{"include":"#expression"}]},"decorator":{"begin":"^\\\\s*((@))\\\\s*(?=[_[:alpha:]]\\\\w*)","beginCaptures":{"1":{"name":"entity.name.function.decorator.python"},"2":{"name":"punctuation.definition.decorator.python"}},"end":"(\\\\))(.*?)(?=\\\\s*(?:#|$))|(?=[\\\\n#])","endCaptures":{"1":{"name":"punctuation.definition.arguments.end.python"},"2":{"name":"invalid.illegal.decorator.python"}},"name":"meta.function.decorator.python","patterns":[{"include":"#decorator-name"},{"include":"#function-arguments"}]},"decorator-name":{"patterns":[{"include":"#builtin-callables"},{"include":"#illegal-object-name"},{"captures":{"2":{"name":"punctuation.separator.period.python"}},"match":"([_[:alpha:]]\\\\w*)|(\\\\.)","name":"entity.name.function.decorator.python"},{"include":"#line-continuation"},{"captures":{"1":{"name":"invalid.illegal.decorator.python"}},"match":"\\\\s*([^#(.\\\\\\\\_[:alpha:]\\\\s].*?)(?=#|$)","name":"invalid.illegal.decorator.python"}]},"docstring":{"patterns":[{"begin":"('''|\\"\\"\\")","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\1)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"}},"name":"string.quoted.docstring.multi.python","patterns":[{"include":"#docstring-prompt"},{"include":"#codetags"},{"include":"#docstring-guts-unicode"}]},{"begin":"([Rr])('''|\\"\\"\\")","beginCaptures":{"1":{"name":"storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\2)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"}},"name":"string.quoted.docstring.raw.multi.python","patterns":[{"include":"#string-consume-escape"},{"include":"#docstring-prompt"},{"include":"#codetags"}]},{"begin":"([\\"'])","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\1)|(\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.docstring.single.python","patterns":[{"include":"#codetags"},{"include":"#docstring-guts-unicode"}]},{"begin":"([Rr])([\\"'])","beginCaptures":{"1":{"name":"storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\2)|(\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.docstring.raw.single.python","patterns":[{"include":"#string-consume-escape"},{"include":"#codetags"}]}]},"docstring-guts-unicode":{"patterns":[{"include":"#escape-sequence-unicode"},{"include":"#escape-sequence"},{"include":"#string-line-continuation"}]},"docstring-prompt":{"captures":{"1":{"name":"keyword.control.flow.python"}},"match":"(?:^|\\\\G)\\\\s*((?:>>>|\\\\.\\\\.\\\\.)\\\\s)(?=\\\\s*\\\\S)"},"docstring-statement":{"begin":"^(?=\\\\s*[Rr]?('''|\\"\\"\\"|[\\"']))","end":"((?<=\\\\1)|^)(?!\\\\s*[Rr]?('''|\\"\\"\\"|[\\"']))","patterns":[{"include":"#docstring"}]},"double-one-regexp-character-set":{"patterns":[{"match":"\\\\[\\\\^?](?!.*?])"},{"begin":"(\\\\[)(\\\\^)?(])?","beginCaptures":{"1":{"name":"punctuation.character.set.begin.regexp constant.other.set.regexp"},"2":{"name":"keyword.operator.negation.regexp"},"3":{"name":"constant.character.set.regexp"}},"end":"(]|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"punctuation.character.set.end.regexp constant.other.set.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.character.set.regexp","patterns":[{"include":"#regexp-charecter-set-escapes"},{"match":"\\\\N","name":"constant.character.set.regexp"}]}]},"double-one-regexp-comments":{"begin":"\\\\(\\\\?#","beginCaptures":{"0":{"name":"punctuation.comment.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"punctuation.comment.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"comment.regexp","patterns":[{"include":"#codetags"}]},"double-one-regexp-conditional":{"begin":"(\\\\()\\\\?\\\\((\\\\w+(?:\\\\s+\\\\p{alnum}+)?|\\\\d+)\\\\)","beginCaptures":{"0":{"name":"keyword.operator.conditional.regexp"},"1":{"name":"punctuation.parenthesis.conditional.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.conditional.negative.regexp punctuation.parenthesis.conditional.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-expression":{"patterns":[{"include":"#regexp-base-expression"},{"include":"#double-one-regexp-character-set"},{"include":"#double-one-regexp-comments"},{"include":"#regexp-flags"},{"include":"#double-one-regexp-named-group"},{"include":"#regexp-backreference"},{"include":"#double-one-regexp-lookahead"},{"include":"#double-one-regexp-lookahead-negative"},{"include":"#double-one-regexp-lookbehind"},{"include":"#double-one-regexp-lookbehind-negative"},{"include":"#double-one-regexp-conditional"},{"include":"#double-one-regexp-parentheses-non-capturing"},{"include":"#double-one-regexp-parentheses"}]},"double-one-regexp-lookahead":{"begin":"(\\\\()\\\\?=","beginCaptures":{"0":{"name":"keyword.operator.lookahead.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-lookahead-negative":{"begin":"(\\\\()\\\\?!","beginCaptures":{"0":{"name":"keyword.operator.lookahead.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-lookbehind":{"begin":"(\\\\()\\\\?<=","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-lookbehind-negative":{"begin":"(\\\\()\\\\?<!","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.negative.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-named-group":{"begin":"(\\\\()(\\\\?P<\\\\w+(?:\\\\s+\\\\p{alnum}+)?>)","beginCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"},"2":{"name":"entity.name.tag.named.group.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.named.regexp","patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-parentheses":{"begin":"\\\\(","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-one-regexp-parentheses-non-capturing":{"begin":"\\\\(\\\\?:","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.begin.regexp"}},"end":"(\\\\)|(?=\\"))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-one-regexp-expression"}]},"double-three-regexp-character-set":{"patterns":[{"match":"\\\\[\\\\^?](?!.*?])"},{"begin":"(\\\\[)(\\\\^)?(])?","beginCaptures":{"1":{"name":"punctuation.character.set.begin.regexp constant.other.set.regexp"},"2":{"name":"keyword.operator.negation.regexp"},"3":{"name":"constant.character.set.regexp"}},"end":"(]|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"punctuation.character.set.end.regexp constant.other.set.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.character.set.regexp","patterns":[{"include":"#regexp-charecter-set-escapes"},{"match":"\\\\N","name":"constant.character.set.regexp"}]}]},"double-three-regexp-comments":{"begin":"\\\\(\\\\?#","beginCaptures":{"0":{"name":"punctuation.comment.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"punctuation.comment.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"comment.regexp","patterns":[{"include":"#codetags"}]},"double-three-regexp-conditional":{"begin":"(\\\\()\\\\?\\\\((\\\\w+(?:\\\\s+\\\\p{alnum}+)?|\\\\d+)\\\\)","beginCaptures":{"0":{"name":"keyword.operator.conditional.regexp"},"1":{"name":"punctuation.parenthesis.conditional.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"keyword.operator.conditional.negative.regexp punctuation.parenthesis.conditional.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-expression":{"patterns":[{"include":"#regexp-base-expression"},{"include":"#double-three-regexp-character-set"},{"include":"#double-three-regexp-comments"},{"include":"#regexp-flags"},{"include":"#double-three-regexp-named-group"},{"include":"#regexp-backreference"},{"include":"#double-three-regexp-lookahead"},{"include":"#double-three-regexp-lookahead-negative"},{"include":"#double-three-regexp-lookbehind"},{"include":"#double-three-regexp-lookbehind-negative"},{"include":"#double-three-regexp-conditional"},{"include":"#double-three-regexp-parentheses-non-capturing"},{"include":"#double-three-regexp-parentheses"},{"include":"#comments-string-double-three"}]},"double-three-regexp-lookahead":{"begin":"(\\\\()\\\\?=","beginCaptures":{"0":{"name":"keyword.operator.lookahead.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-lookahead-negative":{"begin":"(\\\\()\\\\?!","beginCaptures":{"0":{"name":"keyword.operator.lookahead.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-lookbehind":{"begin":"(\\\\()\\\\?<=","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-lookbehind-negative":{"begin":"(\\\\()\\\\?<!","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.negative.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-named-group":{"begin":"(\\\\()(\\\\?P<\\\\w+(?:\\\\s+\\\\p{alnum}+)?>)","beginCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"},"2":{"name":"entity.name.tag.named.group.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.named.regexp","patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-parentheses":{"begin":"\\\\(","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"double-three-regexp-parentheses-non-capturing":{"begin":"\\\\(\\\\?:","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.begin.regexp"}},"end":"(\\\\)|(?=\\"\\"\\"))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#double-three-regexp-expression"},{"include":"#comments-string-double-three"}]},"ellipsis":{"match":"\\\\.\\\\.\\\\.","name":"constant.other.ellipsis.python"},"escape-sequence":{"match":"\\\\\\\\(x\\\\h{2}|[0-7]{1,3}|[\\"'\\\\\\\\abfnrtv])","name":"constant.character.escape.python"},"escape-sequence-unicode":{"patterns":[{"match":"\\\\\\\\(u\\\\h{4}|U\\\\h{8}|N\\\\{[\\\\w\\\\s]+?})","name":"constant.character.escape.python"}]},"expression":{"patterns":[{"include":"#expression-base"},{"include":"#member-access"},{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b"}]},"expression-bare":{"patterns":[{"include":"#backticks"},{"include":"#illegal-anno"},{"include":"#literal"},{"include":"#regexp"},{"include":"#string"},{"include":"#lambda"},{"include":"#generator"},{"include":"#illegal-operator"},{"include":"#operator"},{"include":"#curly-braces"},{"include":"#item-access"},{"include":"#list"},{"include":"#odd-function-call"},{"include":"#round-braces"},{"include":"#function-call"},{"include":"#builtin-functions"},{"include":"#builtin-types"},{"include":"#builtin-exceptions"},{"include":"#magic-names"},{"include":"#special-names"},{"include":"#illegal-names"},{"include":"#special-variables"},{"include":"#ellipsis"},{"include":"#punctuation"},{"include":"#line-continuation"}]},"expression-base":{"patterns":[{"include":"#comments"},{"include":"#expression-bare"},{"include":"#line-continuation"}]},"f-expression":{"patterns":[{"include":"#expression-bare"},{"include":"#member-access"},{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b"}]},"fregexp-base-expression":{"patterns":[{"include":"#fregexp-quantifier"},{"include":"#fstring-formatting-braces"},{"match":"\\\\{.*?}"},{"include":"#regexp-base-common"}]},"fregexp-quantifier":{"match":"\\\\{\\\\{(\\\\d+|\\\\d+,(\\\\d+)?|,\\\\d+)}}","name":"keyword.operator.quantifier.regexp"},"fstring-fnorm-quoted-multi-line":{"begin":"\\\\b([Ff])([BUbu])?('''|\\"\\"\\")","beginCaptures":{"1":{"name":"string.interpolated.python string.quoted.multi.python storage.type.string.python"},"2":{"name":"invalid.illegal.prefix.python"},"3":{"name":"punctuation.definition.string.begin.python string.interpolated.python string.quoted.multi.python"}},"end":"(\\\\3)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python string.interpolated.python string.quoted.multi.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.fstring.python","patterns":[{"include":"#fstring-guts"},{"include":"#fstring-illegal-multi-brace"},{"include":"#fstring-multi-brace"},{"include":"#fstring-multi-core"}]},"fstring-fnorm-quoted-single-line":{"begin":"\\\\b([Ff])([BUbu])?(([\\"']))","beginCaptures":{"1":{"name":"string.interpolated.python string.quoted.single.python storage.type.string.python"},"2":{"name":"invalid.illegal.prefix.python"},"3":{"name":"punctuation.definition.string.begin.python string.interpolated.python string.quoted.single.python"}},"end":"(\\\\3)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python string.interpolated.python string.quoted.single.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.fstring.python","patterns":[{"include":"#fstring-guts"},{"include":"#fstring-illegal-single-brace"},{"include":"#fstring-single-brace"},{"include":"#fstring-single-core"}]},"fstring-formatting":{"patterns":[{"include":"#fstring-formatting-braces"},{"include":"#fstring-formatting-singe-brace"}]},"fstring-formatting-braces":{"patterns":[{"captures":{"1":{"name":"constant.character.format.placeholder.other.python"},"2":{"name":"invalid.illegal.brace.python"},"3":{"name":"constant.character.format.placeholder.other.python"}},"match":"(\\\\{)(\\\\s*?)(})"},{"match":"(\\\\{\\\\{|}})","name":"constant.character.escape.python"}]},"fstring-formatting-singe-brace":{"match":"(}(?!}))","name":"invalid.illegal.brace.python"},"fstring-guts":{"patterns":[{"include":"#escape-sequence-unicode"},{"include":"#escape-sequence"},{"include":"#string-line-continuation"},{"include":"#fstring-formatting"}]},"fstring-illegal-multi-brace":{"patterns":[{"include":"#impossible"}]},"fstring-illegal-single-brace":{"begin":"(\\\\{)(?=[^\\\\n}]*$\\\\n?)","beginCaptures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"end":"(})|(?=\\\\n)","endCaptures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"patterns":[{"include":"#fstring-terminator-single"},{"include":"#f-expression"}]},"fstring-multi-brace":{"begin":"(\\\\{)","beginCaptures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"end":"(})","endCaptures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"patterns":[{"include":"#fstring-terminator-multi"},{"include":"#f-expression"}]},"fstring-multi-core":{"match":"(.+?)($(\\\\n?)|(?=[\\\\\\\\{}]|'''|\\"\\"\\"))|\\\\n","name":"string.interpolated.python string.quoted.multi.python"},"fstring-normf-quoted-multi-line":{"begin":"\\\\b([BUbu])([Ff])('''|\\"\\"\\")","beginCaptures":{"1":{"name":"invalid.illegal.prefix.python"},"2":{"name":"string.interpolated.python string.quoted.multi.python storage.type.string.python"},"3":{"name":"punctuation.definition.string.begin.python string.quoted.multi.python"}},"end":"(\\\\3)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python string.interpolated.python string.quoted.multi.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.fstring.python","patterns":[{"include":"#fstring-guts"},{"include":"#fstring-illegal-multi-brace"},{"include":"#fstring-multi-brace"},{"include":"#fstring-multi-core"}]},"fstring-normf-quoted-single-line":{"begin":"\\\\b([BUbu])([Ff])(([\\"']))","beginCaptures":{"1":{"name":"invalid.illegal.prefix.python"},"2":{"name":"string.interpolated.python string.quoted.single.python storage.type.string.python"},"3":{"name":"punctuation.definition.string.begin.python string.quoted.single.python"}},"end":"(\\\\3)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python string.interpolated.python string.quoted.single.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.fstring.python","patterns":[{"include":"#fstring-guts"},{"include":"#fstring-illegal-single-brace"},{"include":"#fstring-single-brace"},{"include":"#fstring-single-core"}]},"fstring-raw-guts":{"patterns":[{"include":"#string-consume-escape"},{"include":"#fstring-formatting"}]},"fstring-raw-multi-core":{"match":"(.+?)($(\\\\n?)|(?=[\\\\\\\\{}]|'''|\\"\\"\\"))|\\\\n","name":"string.interpolated.python string.quoted.raw.multi.python"},"fstring-raw-quoted-multi-line":{"begin":"\\\\b([Rr][Ff]|[Ff][Rr])('''|\\"\\"\\")","beginCaptures":{"1":{"name":"string.interpolated.python string.quoted.raw.multi.python storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python string.quoted.raw.multi.python"}},"end":"(\\\\2)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python string.interpolated.python string.quoted.raw.multi.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.fstring.python","patterns":[{"include":"#fstring-raw-guts"},{"include":"#fstring-illegal-multi-brace"},{"include":"#fstring-multi-brace"},{"include":"#fstring-raw-multi-core"}]},"fstring-raw-quoted-single-line":{"begin":"\\\\b([Rr][Ff]|[Ff][Rr])(([\\"']))","beginCaptures":{"1":{"name":"string.interpolated.python string.quoted.raw.single.python storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python string.quoted.raw.single.python"}},"end":"(\\\\2)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python string.interpolated.python string.quoted.raw.single.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.fstring.python","patterns":[{"include":"#fstring-raw-guts"},{"include":"#fstring-illegal-single-brace"},{"include":"#fstring-single-brace"},{"include":"#fstring-raw-single-core"}]},"fstring-raw-single-core":{"match":"(.+?)($(\\\\n?)|(?=[\\\\\\\\{}]|([\\"'])|((?<!\\\\\\\\)\\\\n)))|\\\\n","name":"string.interpolated.python string.quoted.raw.single.python"},"fstring-single-brace":{"begin":"(\\\\{)","beginCaptures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"end":"(})|(?=\\\\n)","endCaptures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"patterns":[{"include":"#fstring-terminator-single"},{"include":"#f-expression"}]},"fstring-single-core":{"match":"(.+?)($(\\\\n?)|(?=[\\\\\\\\{}]|([\\"'])|((?<!\\\\\\\\)\\\\n)))|\\\\n","name":"string.interpolated.python string.quoted.single.python"},"fstring-terminator-multi":{"patterns":[{"match":"(=(![ars])?)(?=})","name":"storage.type.format.python"},{"match":"(=?![ars])(?=})","name":"storage.type.format.python"},{"captures":{"1":{"name":"storage.type.format.python"},"2":{"name":"storage.type.format.python"}},"match":"(=?(?:![ars])?)(:\\\\w?[<=>^]?[- +]?#?\\\\d*,?(\\\\.\\\\d+)?[%EFGXb-gnosx]?)(?=})"},{"include":"#fstring-terminator-multi-tail"}]},"fstring-terminator-multi-tail":{"begin":"(=?(?:![ars])?)(:)(?=.*?\\\\{)","beginCaptures":{"1":{"name":"storage.type.format.python"},"2":{"name":"storage.type.format.python"}},"end":"(?=})","patterns":[{"include":"#fstring-illegal-multi-brace"},{"include":"#fstring-multi-brace"},{"match":"([%EFGXb-gnosx])(?=})","name":"storage.type.format.python"},{"match":"(\\\\.\\\\d+)","name":"storage.type.format.python"},{"match":"(,)","name":"storage.type.format.python"},{"match":"(\\\\d+)","name":"storage.type.format.python"},{"match":"(#)","name":"storage.type.format.python"},{"match":"([- +])","name":"storage.type.format.python"},{"match":"([<=>^])","name":"storage.type.format.python"},{"match":"(\\\\w)","name":"storage.type.format.python"}]},"fstring-terminator-single":{"patterns":[{"match":"(=(![ars])?)(?=})","name":"storage.type.format.python"},{"match":"(=?![ars])(?=})","name":"storage.type.format.python"},{"captures":{"1":{"name":"storage.type.format.python"},"2":{"name":"storage.type.format.python"}},"match":"(=?(?:![ars])?)(:\\\\w?[<=>^]?[- +]?#?\\\\d*,?(\\\\.\\\\d+)?[%EFGXb-gnosx]?)(?=})"},{"include":"#fstring-terminator-single-tail"}]},"fstring-terminator-single-tail":{"begin":"(=?(?:![ars])?)(:)(?=.*?\\\\{)","beginCaptures":{"1":{"name":"storage.type.format.python"},"2":{"name":"storage.type.format.python"}},"end":"(?=})|(?=\\\\n)","patterns":[{"include":"#fstring-illegal-single-brace"},{"include":"#fstring-single-brace"},{"match":"([%EFGXb-gnosx])(?=})","name":"storage.type.format.python"},{"match":"(\\\\.\\\\d+)","name":"storage.type.format.python"},{"match":"(,)","name":"storage.type.format.python"},{"match":"(\\\\d+)","name":"storage.type.format.python"},{"match":"(#)","name":"storage.type.format.python"},{"match":"([- +])","name":"storage.type.format.python"},{"match":"([<=>^])","name":"storage.type.format.python"},{"match":"(\\\\w)","name":"storage.type.format.python"}]},"function-arguments":{"begin":"(\\\\()","beginCaptures":{"1":{"name":"punctuation.definition.arguments.begin.python"}},"contentName":"meta.function-call.arguments.python","end":"(?=\\\\))(?!\\\\)\\\\s*\\\\()","patterns":[{"match":"(,)","name":"punctuation.separator.arguments.python"},{"captures":{"1":{"name":"keyword.operator.unpacking.arguments.python"}},"match":"(?:(?<=[(,])|^)\\\\s*(\\\\*{1,2})"},{"include":"#lambda-incomplete"},{"include":"#illegal-names"},{"captures":{"1":{"name":"variable.parameter.function-call.python"},"2":{"name":"keyword.operator.assignment.python"}},"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\s*(=)(?!=)"},{"match":"=(?!=)","name":"keyword.operator.assignment.python"},{"include":"#expression"},{"captures":{"1":{"name":"punctuation.definition.arguments.end.python"},"2":{"name":"punctuation.definition.arguments.begin.python"}},"match":"\\\\s*(\\\\))\\\\s*(\\\\()"}]},"function-call":{"begin":"\\\\b(?=([_[:alpha:]]\\\\w*)\\\\s*(\\\\())","end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.arguments.end.python"}},"name":"meta.function-call.python","patterns":[{"include":"#special-variables"},{"include":"#function-name"},{"include":"#function-arguments"}]},"function-declaration":{"begin":"\\\\s*(?:\\\\b(async)\\\\s+)?\\\\b(def)\\\\s+(?=[_[:alpha:]]\\\\p{word}*\\\\s*\\\\()","beginCaptures":{"1":{"name":"storage.type.function.async.python"},"2":{"name":"storage.type.function.python"}},"end":"(:|(?=[\\\\n\\"#']))","endCaptures":{"1":{"name":"punctuation.section.function.begin.python"}},"name":"meta.function.python","patterns":[{"include":"#function-def-name"},{"include":"#parameters"},{"include":"#line-continuation"},{"include":"#return-annotation"}]},"function-def-name":{"patterns":[{"include":"#illegal-object-name"},{"include":"#builtin-possible-callables"},{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b","name":"entity.name.function.python"}]},"function-name":{"patterns":[{"include":"#builtin-possible-callables"},{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b","name":"meta.function-call.generic.python"}]},"generator":{"begin":"\\\\bfor\\\\b","beginCaptures":{"0":{"name":"keyword.control.flow.python"}},"end":"\\\\bin\\\\b","endCaptures":{"0":{"name":"keyword.control.flow.python"}},"patterns":[{"include":"#expression"}]},"illegal-anno":{"match":"->","name":"invalid.illegal.annotation.python"},"illegal-names":{"captures":{"1":{"name":"keyword.control.flow.python"},"2":{"name":"keyword.control.import.python"}},"match":"\\\\b(?:(and|assert|async|await|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|in|is|(?<=\\\\.)lambda|lambda(?=\\\\s*[.=])|nonlocal|not|or|pass|raise|return|try|while|with|yield)|(as|import))\\\\b"},"illegal-object-name":{"match":"\\\\b(True|False|None)\\\\b","name":"keyword.illegal.name.python"},"illegal-operator":{"patterns":[{"match":"&&|\\\\|\\\\||--|\\\\+\\\\+","name":"invalid.illegal.operator.python"},{"match":"[$?]","name":"invalid.illegal.operator.python"},{"match":"!\\\\b","name":"invalid.illegal.operator.python"}]},"import":{"patterns":[{"begin":"\\\\b(?<!\\\\.)(from)\\\\b(?=.+import)","beginCaptures":{"1":{"name":"keyword.control.import.python"}},"end":"$|(?=import)","patterns":[{"match":"\\\\.+","name":"punctuation.separator.period.python"},{"include":"#expression"}]},{"begin":"\\\\b(?<!\\\\.)(import)\\\\b","beginCaptures":{"1":{"name":"keyword.control.import.python"}},"end":"$","patterns":[{"match":"\\\\b(?<!\\\\.)as\\\\b","name":"keyword.control.import.python"},{"include":"#expression"}]}]},"impossible":{"match":"$.^"},"inheritance-identifier":{"captures":{"1":{"name":"entity.other.inherited-class.python"}},"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b"},"inheritance-name":{"patterns":[{"include":"#lambda-incomplete"},{"include":"#builtin-possible-callables"},{"include":"#inheritance-identifier"}]},"item-access":{"patterns":[{"begin":"\\\\b(?=[_[:alpha:]]\\\\w*\\\\s*\\\\[)","end":"(])","endCaptures":{"1":{"name":"punctuation.definition.arguments.end.python"}},"name":"meta.item-access.python","patterns":[{"include":"#item-name"},{"include":"#item-index"},{"include":"#expression"}]}]},"item-index":{"begin":"(\\\\[)","beginCaptures":{"1":{"name":"punctuation.definition.arguments.begin.python"}},"contentName":"meta.item-access.arguments.python","end":"(?=])","patterns":[{"match":":","name":"punctuation.separator.slice.python"},{"include":"#expression"}]},"item-name":{"patterns":[{"include":"#special-variables"},{"include":"#builtin-functions"},{"include":"#special-names"},{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b","name":"meta.indexed-name.python"}]},"lambda":{"patterns":[{"captures":{"1":{"name":"keyword.control.flow.python"}},"match":"((?<=\\\\.)lambda|lambda(?=\\\\s*[.=]))"},{"captures":{"1":{"name":"storage.type.function.lambda.python"}},"match":"\\\\b(lambda)\\\\s*?(?=[\\\\n,]|$)"},{"begin":"\\\\b(lambda)\\\\b","beginCaptures":{"1":{"name":"storage.type.function.lambda.python"}},"contentName":"meta.function.lambda.parameters.python","end":"(:)|(\\\\n)","endCaptures":{"1":{"name":"punctuation.section.function.lambda.begin.python"}},"name":"meta.lambda-function.python","patterns":[{"match":"/","name":"keyword.operator.positional.parameter.python"},{"match":"(\\\\*\\\\*?)","name":"keyword.operator.unpacking.parameter.python"},{"include":"#lambda-nested-incomplete"},{"include":"#illegal-names"},{"captures":{"1":{"name":"variable.parameter.function.language.python"},"2":{"name":"punctuation.separator.parameters.python"}},"match":"([_[:alpha:]]\\\\w*)\\\\s*(?:(,)|(?=:|$))"},{"include":"#comments"},{"include":"#backticks"},{"include":"#illegal-anno"},{"include":"#lambda-parameter-with-default"},{"include":"#line-continuation"},{"include":"#illegal-operator"}]}]},"lambda-incomplete":{"match":"\\\\blambda(?=\\\\s*[),])","name":"storage.type.function.lambda.python"},"lambda-nested-incomplete":{"match":"\\\\blambda(?=\\\\s*[),:])","name":"storage.type.function.lambda.python"},"lambda-parameter-with-default":{"begin":"\\\\b([_[:alpha:]]\\\\w*)\\\\s*(=)","beginCaptures":{"1":{"name":"variable.parameter.function.language.python"},"2":{"name":"keyword.operator.python"}},"end":"(,)|(?=:|$)","endCaptures":{"1":{"name":"punctuation.separator.parameters.python"}},"patterns":[{"include":"#expression"}]},"line-continuation":{"patterns":[{"captures":{"1":{"name":"punctuation.separator.continuation.line.python"},"2":{"name":"invalid.illegal.line.continuation.python"}},"match":"(\\\\\\\\)\\\\s*(\\\\S.*$\\\\n?)"},{"begin":"(\\\\\\\\)\\\\s*$\\\\n?","beginCaptures":{"1":{"name":"punctuation.separator.continuation.line.python"}},"end":"(?=^\\\\s*$)|(?!(\\\\s*[Rr]?('''|\\"\\"\\"|[\\"']))|\\\\G()$)","patterns":[{"include":"#regexp"},{"include":"#string"}]}]},"list":{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.list.begin.python"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.list.end.python"}},"patterns":[{"include":"#expression"}]},"literal":{"patterns":[{"match":"\\\\b(True|False|None|NotImplemented|Ellipsis)\\\\b","name":"constant.language.python"},{"include":"#number"}]},"loose-default":{"begin":"(=)","beginCaptures":{"1":{"name":"keyword.operator.python"}},"end":"(,)|(?=\\\\))","endCaptures":{"1":{"name":"punctuation.separator.parameters.python"}},"patterns":[{"include":"#expression"}]},"magic-function-names":{"captures":{"1":{"name":"support.function.magic.python"}},"match":"\\\\b(__(?:abs|add|aenter|aexit|aiter|and|anext|await|bool|call|ceil|class_getitem|cmp|coerce|complex|contains|copy|deepcopy|del|delattr|delete|delitem|delslice|dir|div|divmod|enter|eq|exit|float|floor|floordiv|format|get??|getattr|getattribute|getinitargs|getitem|getnewargs|getslice|getstate|gt|hash|hex|iadd|iand|idiv|ifloordiv||ilshift|imod|imul|index|init|instancecheck|int|invert|ior|ipow|irshift|isub|iter|itruediv|ixor|len??|long|lshift|lt|missing|mod|mul|neg??|new|next|nonzero|oct|or|pos|pow|radd|rand|rdiv|rdivmod|reduce|reduce_ex|repr|reversed|rfloordiv||rlshift|rmod|rmul|ror|round|rpow|rrshift|rshift|rsub|rtruediv|rxor|set|setattr|setitem|set_name|setslice|setstate|sizeof|str|sub|subclasscheck|truediv|trunc|unicode|xor|matmul|rmatmul|imatmul|init_subclass|set_name|fspath|bytes|prepare|length_hint)__)\\\\b"},"magic-names":{"patterns":[{"include":"#magic-function-names"},{"include":"#magic-variable-names"}]},"magic-variable-names":{"captures":{"1":{"name":"support.variable.magic.python"}},"match":"\\\\b(__(?:all|annotations|bases|builtins|class|closure|code|debug|defaults|dict|doc|file|func|globals|kwdefaults|match_args|members|metaclass|methods|module|mro|mro_entries|name|qualname|post_init|self|signature|slots|subclasses|version|weakref|wrapped|classcell|spec|path|package|future|traceback)__)\\\\b"},"member-access":{"begin":"(\\\\.)\\\\s*(?!\\\\.)","beginCaptures":{"1":{"name":"punctuation.separator.period.python"}},"end":"(?<=\\\\S)(?=\\\\W)|(^|(?<=\\\\s))(?=[^\\\\\\\\\\\\w\\\\s])|$","name":"meta.member.access.python","patterns":[{"include":"#function-call"},{"include":"#member-access-base"},{"include":"#member-access-attribute"}]},"member-access-attribute":{"match":"\\\\b([_[:alpha:]]\\\\w*)\\\\b","name":"meta.attribute.python"},"member-access-base":{"patterns":[{"include":"#magic-names"},{"include":"#illegal-names"},{"include":"#illegal-object-name"},{"include":"#special-names"},{"include":"#line-continuation"},{"include":"#item-access"}]},"member-access-class":{"begin":"(\\\\.)\\\\s*(?!\\\\.)","beginCaptures":{"1":{"name":"punctuation.separator.period.python"}},"end":"(?<=\\\\S)(?=\\\\W)|$","name":"meta.member.access.python","patterns":[{"include":"#call-wrapper-inheritance"},{"include":"#member-access-base"},{"include":"#inheritance-identifier"}]},"number":{"name":"constant.numeric.python","patterns":[{"include":"#number-float"},{"include":"#number-dec"},{"include":"#number-hex"},{"include":"#number-oct"},{"include":"#number-bin"},{"include":"#number-long"},{"match":"\\\\b[0-9]+\\\\w+","name":"invalid.illegal.name.python"}]},"number-bin":{"captures":{"1":{"name":"storage.type.number.python"}},"match":"(?<![.\\\\w])(0[Bb])(_?[01])+\\\\b","name":"constant.numeric.bin.python"},"number-dec":{"captures":{"1":{"name":"storage.type.imaginary.number.python"},"2":{"name":"invalid.illegal.dec.python"}},"match":"(?<![.\\\\w])(?:[1-9](?:_?[0-9])*|0+|[0-9](?:_?[0-9])*([Jj])|0([0-9]+)(?![.Ee]))\\\\b","name":"constant.numeric.dec.python"},"number-float":{"captures":{"1":{"name":"storage.type.imaginary.number.python"}},"match":"(?<!\\\\w)(?:(?:\\\\.[0-9](?:_?[0-9])*|[0-9](?:_?[0-9])*\\\\.[0-9](?:_?[0-9])*|[0-9](?:_?[0-9])*\\\\.)(?:[Ee][-+]?[0-9](?:_?[0-9])*)?|[0-9](?:_?[0-9])*[Ee][-+]?[0-9](?:_?[0-9])*)([Jj])?\\\\b","name":"constant.numeric.float.python"},"number-hex":{"captures":{"1":{"name":"storage.type.number.python"}},"match":"(?<![.\\\\w])(0[Xx])(_?\\\\h)+\\\\b","name":"constant.numeric.hex.python"},"number-long":{"captures":{"2":{"name":"storage.type.number.python"}},"match":"(?<![.\\\\w])([1-9][0-9]*|0)([Ll])\\\\b","name":"constant.numeric.bin.python"},"number-oct":{"captures":{"1":{"name":"storage.type.number.python"}},"match":"(?<![.\\\\w])(0[Oo])(_?[0-7])+\\\\b","name":"constant.numeric.oct.python"},"odd-function-call":{"begin":"(?<=[])])\\\\s*(?=\\\\()","end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.arguments.end.python"}},"patterns":[{"include":"#function-arguments"}]},"operator":{"captures":{"1":{"name":"keyword.operator.logical.python"},"2":{"name":"keyword.control.flow.python"},"3":{"name":"keyword.operator.bitwise.python"},"4":{"name":"keyword.operator.arithmetic.python"},"5":{"name":"keyword.operator.comparison.python"},"6":{"name":"keyword.operator.assignment.python"}},"match":"\\\\b(?<!\\\\.)(?:(and|or|not|in|is)|(for|if|else|await|yield(?:\\\\s+from)?))(?!\\\\s*:)\\\\b|(<<|>>|[\\\\&^|~])|(\\\\*\\\\*|[-%*+]|//|[/@])|(!=|==|>=|<=|[<>])|(:=)"},"parameter-special":{"captures":{"1":{"name":"variable.parameter.function.language.python"},"2":{"name":"variable.parameter.function.language.special.self.python"},"3":{"name":"variable.parameter.function.language.special.cls.python"},"4":{"name":"punctuation.separator.parameters.python"}},"match":"\\\\b((self)|(cls))\\\\b\\\\s*(?:(,)|(?=\\\\)))"},"parameters":{"begin":"(\\\\()","beginCaptures":{"1":{"name":"punctuation.definition.parameters.begin.python"}},"end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.parameters.end.python"}},"name":"meta.function.parameters.python","patterns":[{"match":"/","name":"keyword.operator.positional.parameter.python"},{"match":"(\\\\*\\\\*?)","name":"keyword.operator.unpacking.parameter.python"},{"include":"#lambda-incomplete"},{"include":"#illegal-names"},{"include":"#illegal-object-name"},{"include":"#parameter-special"},{"captures":{"1":{"name":"variable.parameter.function.language.python"},"2":{"name":"punctuation.separator.parameters.python"}},"match":"([_[:alpha:]]\\\\w*)\\\\s*(?:(,)|(?=[\\\\n#)=]))"},{"include":"#comments"},{"include":"#loose-default"},{"include":"#annotated-parameter"}]},"punctuation":{"patterns":[{"match":":","name":"punctuation.separator.colon.python"},{"match":",","name":"punctuation.separator.element.python"}]},"regexp":{"patterns":[{"include":"#regexp-single-three-line"},{"include":"#regexp-double-three-line"},{"include":"#regexp-single-one-line"},{"include":"#regexp-double-one-line"}]},"regexp-backreference":{"captures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.backreference.named.begin.regexp"},"2":{"name":"entity.name.tag.named.backreference.regexp"},"3":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.backreference.named.end.regexp"}},"match":"(\\\\()(\\\\?P=\\\\w+(?:\\\\s+\\\\p{alnum}+)?)(\\\\))","name":"meta.backreference.named.regexp"},"regexp-backreference-number":{"captures":{"1":{"name":"entity.name.tag.backreference.regexp"}},"match":"(\\\\\\\\[1-9]\\\\d?)","name":"meta.backreference.regexp"},"regexp-base-common":{"patterns":[{"match":"\\\\.","name":"support.other.match.any.regexp"},{"match":"\\\\^","name":"support.other.match.begin.regexp"},{"match":"\\\\$","name":"support.other.match.end.regexp"},{"match":"[*+?]\\\\??","name":"keyword.operator.quantifier.regexp"},{"match":"\\\\|","name":"keyword.operator.disjunction.regexp"},{"include":"#regexp-escape-sequence"}]},"regexp-base-expression":{"patterns":[{"include":"#regexp-quantifier"},{"include":"#regexp-base-common"}]},"regexp-charecter-set-escapes":{"patterns":[{"match":"\\\\\\\\[\\\\\\\\abfnrtv]","name":"constant.character.escape.regexp"},{"include":"#regexp-escape-special"},{"match":"\\\\\\\\([0-7]{1,3})","name":"constant.character.escape.regexp"},{"include":"#regexp-escape-character"},{"include":"#regexp-escape-unicode"},{"include":"#regexp-escape-catchall"}]},"regexp-double-one-line":{"begin":"\\\\b(([Uu]r)|([Bb]r)|(r[Bb]?))(\\")","beginCaptures":{"2":{"name":"invalid.deprecated.prefix.python"},"3":{"name":"storage.type.string.python"},"4":{"name":"storage.type.string.python"},"5":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\")|(?<!\\\\\\\\)(\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.regexp.quoted.single.python","patterns":[{"include":"#double-one-regexp-expression"}]},"regexp-double-three-line":{"begin":"\\\\b(([Uu]r)|([Bb]r)|(r[Bb]?))(\\"\\"\\")","beginCaptures":{"2":{"name":"invalid.deprecated.prefix.python"},"3":{"name":"storage.type.string.python"},"4":{"name":"storage.type.string.python"},"5":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\"\\"\\")","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.regexp.quoted.multi.python","patterns":[{"include":"#double-three-regexp-expression"}]},"regexp-escape-catchall":{"match":"\\\\\\\\(.|\\\\n)","name":"constant.character.escape.regexp"},"regexp-escape-character":{"match":"\\\\\\\\(x\\\\h{2}|0[0-7]{1,2}|[0-7]{3})","name":"constant.character.escape.regexp"},"regexp-escape-sequence":{"patterns":[{"include":"#regexp-escape-special"},{"include":"#regexp-escape-character"},{"include":"#regexp-escape-unicode"},{"include":"#regexp-backreference-number"},{"include":"#regexp-escape-catchall"}]},"regexp-escape-special":{"match":"\\\\\\\\([ABDSWZbdsw])","name":"support.other.escape.special.regexp"},"regexp-escape-unicode":{"match":"\\\\\\\\(u\\\\h{4}|U\\\\h{8})","name":"constant.character.unicode.regexp"},"regexp-flags":{"match":"\\\\(\\\\?[Laimsux]+\\\\)","name":"storage.modifier.flag.regexp"},"regexp-quantifier":{"match":"\\\\{(\\\\d+|\\\\d+,(\\\\d+)?|,\\\\d+)}","name":"keyword.operator.quantifier.regexp"},"regexp-single-one-line":{"begin":"\\\\b(([Uu]r)|([Bb]r)|(r[Bb]?))(')","beginCaptures":{"2":{"name":"invalid.deprecated.prefix.python"},"3":{"name":"storage.type.string.python"},"4":{"name":"storage.type.string.python"},"5":{"name":"punctuation.definition.string.begin.python"}},"end":"(')|(?<!\\\\\\\\)(\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.regexp.quoted.single.python","patterns":[{"include":"#single-one-regexp-expression"}]},"regexp-single-three-line":{"begin":"\\\\b(([Uu]r)|([Bb]r)|(r[Bb]?))(''')","beginCaptures":{"2":{"name":"invalid.deprecated.prefix.python"},"3":{"name":"storage.type.string.python"},"4":{"name":"storage.type.string.python"},"5":{"name":"punctuation.definition.string.begin.python"}},"end":"(''')","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.regexp.quoted.multi.python","patterns":[{"include":"#single-three-regexp-expression"}]},"return-annotation":{"begin":"(->)","beginCaptures":{"1":{"name":"punctuation.separator.annotation.result.python"}},"end":"(?=:)","patterns":[{"include":"#expression"}]},"round-braces":{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.parenthesis.begin.python"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.parenthesis.end.python"}},"patterns":[{"include":"#expression"}]},"semicolon":{"patterns":[{"match":";$","name":"invalid.deprecated.semicolon.python"}]},"single-one-regexp-character-set":{"patterns":[{"match":"\\\\[\\\\^?](?!.*?])"},{"begin":"(\\\\[)(\\\\^)?(])?","beginCaptures":{"1":{"name":"punctuation.character.set.begin.regexp constant.other.set.regexp"},"2":{"name":"keyword.operator.negation.regexp"},"3":{"name":"constant.character.set.regexp"}},"end":"(]|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"punctuation.character.set.end.regexp constant.other.set.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.character.set.regexp","patterns":[{"include":"#regexp-charecter-set-escapes"},{"match":"\\\\N","name":"constant.character.set.regexp"}]}]},"single-one-regexp-comments":{"begin":"\\\\(\\\\?#","beginCaptures":{"0":{"name":"punctuation.comment.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"punctuation.comment.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"comment.regexp","patterns":[{"include":"#codetags"}]},"single-one-regexp-conditional":{"begin":"(\\\\()\\\\?\\\\((\\\\w+(?:\\\\s+\\\\p{alnum}+)?|\\\\d+)\\\\)","beginCaptures":{"0":{"name":"keyword.operator.conditional.regexp"},"1":{"name":"punctuation.parenthesis.conditional.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.conditional.negative.regexp punctuation.parenthesis.conditional.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-expression":{"patterns":[{"include":"#regexp-base-expression"},{"include":"#single-one-regexp-character-set"},{"include":"#single-one-regexp-comments"},{"include":"#regexp-flags"},{"include":"#single-one-regexp-named-group"},{"include":"#regexp-backreference"},{"include":"#single-one-regexp-lookahead"},{"include":"#single-one-regexp-lookahead-negative"},{"include":"#single-one-regexp-lookbehind"},{"include":"#single-one-regexp-lookbehind-negative"},{"include":"#single-one-regexp-conditional"},{"include":"#single-one-regexp-parentheses-non-capturing"},{"include":"#single-one-regexp-parentheses"}]},"single-one-regexp-lookahead":{"begin":"(\\\\()\\\\?=","beginCaptures":{"0":{"name":"keyword.operator.lookahead.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-lookahead-negative":{"begin":"(\\\\()\\\\?!","beginCaptures":{"0":{"name":"keyword.operator.lookahead.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-lookbehind":{"begin":"(\\\\()\\\\?<=","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-lookbehind-negative":{"begin":"(\\\\()\\\\?<!","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.negative.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-named-group":{"begin":"(\\\\()(\\\\?P<\\\\w+(?:\\\\s+\\\\p{alnum}+)?>)","beginCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"},"2":{"name":"entity.name.tag.named.group.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.named.regexp","patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-parentheses":{"begin":"\\\\(","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-one-regexp-parentheses-non-capturing":{"begin":"\\\\(\\\\?:","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.begin.regexp"}},"end":"(\\\\)|(?='))|((?=(?<!\\\\\\\\)\\\\n))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-one-regexp-expression"}]},"single-three-regexp-character-set":{"patterns":[{"match":"\\\\[\\\\^?](?!.*?])"},{"begin":"(\\\\[)(\\\\^)?(])?","beginCaptures":{"1":{"name":"punctuation.character.set.begin.regexp constant.other.set.regexp"},"2":{"name":"keyword.operator.negation.regexp"},"3":{"name":"constant.character.set.regexp"}},"end":"(]|(?='''))","endCaptures":{"1":{"name":"punctuation.character.set.end.regexp constant.other.set.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.character.set.regexp","patterns":[{"include":"#regexp-charecter-set-escapes"},{"match":"\\\\N","name":"constant.character.set.regexp"}]}]},"single-three-regexp-comments":{"begin":"\\\\(\\\\?#","beginCaptures":{"0":{"name":"punctuation.comment.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"punctuation.comment.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"comment.regexp","patterns":[{"include":"#codetags"}]},"single-three-regexp-conditional":{"begin":"(\\\\()\\\\?\\\\((\\\\w+(?:\\\\s+\\\\p{alnum}+)?|\\\\d+)\\\\)","beginCaptures":{"0":{"name":"keyword.operator.conditional.regexp"},"1":{"name":"punctuation.parenthesis.conditional.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"keyword.operator.conditional.negative.regexp punctuation.parenthesis.conditional.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-expression":{"patterns":[{"include":"#regexp-base-expression"},{"include":"#single-three-regexp-character-set"},{"include":"#single-three-regexp-comments"},{"include":"#regexp-flags"},{"include":"#single-three-regexp-named-group"},{"include":"#regexp-backreference"},{"include":"#single-three-regexp-lookahead"},{"include":"#single-three-regexp-lookahead-negative"},{"include":"#single-three-regexp-lookbehind"},{"include":"#single-three-regexp-lookbehind-negative"},{"include":"#single-three-regexp-conditional"},{"include":"#single-three-regexp-parentheses-non-capturing"},{"include":"#single-three-regexp-parentheses"},{"include":"#comments-string-single-three"}]},"single-three-regexp-lookahead":{"begin":"(\\\\()\\\\?=","beginCaptures":{"0":{"name":"keyword.operator.lookahead.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"keyword.operator.lookahead.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-lookahead-negative":{"begin":"(\\\\()\\\\?!","beginCaptures":{"0":{"name":"keyword.operator.lookahead.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookahead.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"keyword.operator.lookahead.negative.regexp punctuation.parenthesis.lookahead.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-lookbehind":{"begin":"(\\\\()\\\\?<=","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-lookbehind-negative":{"begin":"(\\\\()\\\\?<!","beginCaptures":{"0":{"name":"keyword.operator.lookbehind.negative.regexp"},"1":{"name":"punctuation.parenthesis.lookbehind.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"keyword.operator.lookbehind.negative.regexp punctuation.parenthesis.lookbehind.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-named-group":{"begin":"(\\\\()(\\\\?P<\\\\w+(?:\\\\s+\\\\p{alnum}+)?>)","beginCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.begin.regexp"},"2":{"name":"entity.name.tag.named.group.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.named.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"name":"meta.named.regexp","patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-parentheses":{"begin":"\\\\(","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"single-three-regexp-parentheses-non-capturing":{"begin":"\\\\(\\\\?:","beginCaptures":{"0":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.begin.regexp"}},"end":"(\\\\)|(?='''))","endCaptures":{"1":{"name":"support.other.parenthesis.regexp punctuation.parenthesis.non-capturing.end.regexp"},"2":{"name":"invalid.illegal.newline.python"}},"patterns":[{"include":"#single-three-regexp-expression"},{"include":"#comments-string-single-three"}]},"special-names":{"match":"\\\\b(_*\\\\p{upper}[_\\\\d]*\\\\p{upper})[[:upper:]\\\\d]*(_\\\\w*)?\\\\b","name":"constant.other.caps.python"},"special-variables":{"captures":{"1":{"name":"variable.language.special.self.python"},"2":{"name":"variable.language.special.cls.python"}},"match":"\\\\b(?<!\\\\.)(?:(self)|(cls))\\\\b"},"statement":{"patterns":[{"include":"#import"},{"include":"#class-declaration"},{"include":"#function-declaration"},{"include":"#generator"},{"include":"#statement-keyword"},{"include":"#assignment-operator"},{"include":"#decorator"},{"include":"#docstring-statement"},{"include":"#semicolon"}]},"statement-keyword":{"patterns":[{"match":"\\\\b((async\\\\s+)?\\\\s*def)\\\\b","name":"storage.type.function.python"},{"match":"\\\\b(?<!\\\\.)as\\\\b(?=.*[:\\\\\\\\])","name":"keyword.control.flow.python"},{"match":"\\\\b(?<!\\\\.)as\\\\b","name":"keyword.control.import.python"},{"match":"\\\\b(?<!\\\\.)(async|continue|del|assert|break|finally|for|from|elif|else|if|except|pass|raise|return|try|while|with)\\\\b","name":"keyword.control.flow.python"},{"match":"\\\\b(?<!\\\\.)(global|nonlocal)\\\\b","name":"storage.modifier.declaration.python"},{"match":"\\\\b(?<!\\\\.)(class)\\\\b","name":"storage.type.class.python"},{"captures":{"1":{"name":"keyword.control.flow.python"}},"match":"^\\\\s*(case|match)(?=\\\\s*([-\\"#'(+:\\\\[{\\\\w\\\\d]|$))\\\\b"}]},"string":{"patterns":[{"include":"#string-quoted-multi-line"},{"include":"#string-quoted-single-line"},{"include":"#string-bin-quoted-multi-line"},{"include":"#string-bin-quoted-single-line"},{"include":"#string-raw-quoted-multi-line"},{"include":"#string-raw-quoted-single-line"},{"include":"#string-raw-bin-quoted-multi-line"},{"include":"#string-raw-bin-quoted-single-line"},{"include":"#fstring-fnorm-quoted-multi-line"},{"include":"#fstring-fnorm-quoted-single-line"},{"include":"#fstring-normf-quoted-multi-line"},{"include":"#fstring-normf-quoted-single-line"},{"include":"#fstring-raw-quoted-multi-line"},{"include":"#fstring-raw-quoted-single-line"}]},"string-bin-quoted-multi-line":{"begin":"\\\\b([Bb])('''|\\"\\"\\")","beginCaptures":{"1":{"name":"storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\2)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.binary.multi.python","patterns":[{"include":"#string-entity"}]},"string-bin-quoted-single-line":{"begin":"\\\\b([Bb])(([\\"']))","beginCaptures":{"1":{"name":"storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\2)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.binary.single.python","patterns":[{"include":"#string-entity"}]},"string-brace-formatting":{"patterns":[{"captures":{"1":{"name":"constant.character.format.placeholder.other.python"},"3":{"name":"storage.type.format.python"},"4":{"name":"storage.type.format.python"}},"match":"(\\\\{\\\\{|}}|\\\\{\\\\w*(\\\\.[_[:alpha:]]\\\\w*|\\\\[[^]\\"']+])*(![ars])?(:\\\\w?[<=>^]?[- +]?#?\\\\d*,?(\\\\.\\\\d+)?[%EFGXb-gnosx]?)?})","name":"meta.format.brace.python"},{"captures":{"1":{"name":"constant.character.format.placeholder.other.python"},"3":{"name":"storage.type.format.python"},"4":{"name":"storage.type.format.python"}},"match":"(\\\\{\\\\w*(\\\\.[_[:alpha:]]\\\\w*|\\\\[[^]\\"']+])*(![ars])?(:)[^\\\\n\\"'{}]*(?:\\\\{[^\\\\n\\"'}]*?}[^\\\\n\\"'{}]*)*})","name":"meta.format.brace.python"}]},"string-consume-escape":{"match":"\\\\\\\\[\\\\n\\"'\\\\\\\\]"},"string-entity":{"patterns":[{"include":"#escape-sequence"},{"include":"#string-line-continuation"},{"include":"#string-formatting"}]},"string-formatting":{"captures":{"1":{"name":"constant.character.format.placeholder.other.python"}},"match":"(%(\\\\([\\\\w\\\\s]*\\\\))?[- #+0]*(\\\\d+|\\\\*)?(\\\\.(\\\\d+|\\\\*))?([Lhl])?[%EFGXa-giorsux])","name":"meta.format.percent.python"},"string-line-continuation":{"match":"\\\\\\\\$","name":"constant.language.python"},"string-multi-bad-brace1-formatting-raw":{"begin":"(?=\\\\{%(.*?(?!'''|\\"\\"\\"))%})","end":"(?='''|\\"\\"\\")","patterns":[{"include":"#string-consume-escape"}]},"string-multi-bad-brace1-formatting-unicode":{"begin":"(?=\\\\{%(.*?(?!'''|\\"\\"\\"))%})","end":"(?='''|\\"\\"\\")","patterns":[{"include":"#escape-sequence-unicode"},{"include":"#escape-sequence"},{"include":"#string-line-continuation"}]},"string-multi-bad-brace2-formatting-raw":{"begin":"(?!\\\\{\\\\{)(?=\\\\{(\\\\w*?(?!'''|\\"\\"\\")[^!.:\\\\[}\\\\w]).*?(?!'''|\\"\\"\\")})","end":"(?='''|\\"\\"\\")","patterns":[{"include":"#string-consume-escape"},{"include":"#string-formatting"}]},"string-multi-bad-brace2-formatting-unicode":{"begin":"(?!\\\\{\\\\{)(?=\\\\{(\\\\w*?(?!'''|\\"\\"\\")[^!.:\\\\[}\\\\w]).*?(?!'''|\\"\\"\\")})","end":"(?='''|\\"\\"\\")","patterns":[{"include":"#escape-sequence-unicode"},{"include":"#string-entity"}]},"string-quoted-multi-line":{"begin":"(?:\\\\b([Rr])(?=[Uu]))?([Uu])?('''|\\"\\"\\")","beginCaptures":{"1":{"name":"invalid.illegal.prefix.python"},"2":{"name":"storage.type.string.python"},"3":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\3)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.multi.python","patterns":[{"include":"#string-multi-bad-brace1-formatting-unicode"},{"include":"#string-multi-bad-brace2-formatting-unicode"},{"include":"#string-unicode-guts"}]},"string-quoted-single-line":{"begin":"(?:\\\\b([Rr])(?=[Uu]))?([Uu])?(([\\"']))","beginCaptures":{"1":{"name":"invalid.illegal.prefix.python"},"2":{"name":"storage.type.string.python"},"3":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\3)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.single.python","patterns":[{"include":"#string-single-bad-brace1-formatting-unicode"},{"include":"#string-single-bad-brace2-formatting-unicode"},{"include":"#string-unicode-guts"}]},"string-raw-bin-guts":{"patterns":[{"include":"#string-consume-escape"},{"include":"#string-formatting"}]},"string-raw-bin-quoted-multi-line":{"begin":"\\\\b(R[Bb]|[Bb]R)('''|\\"\\"\\")","beginCaptures":{"1":{"name":"storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\2)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.raw.binary.multi.python","patterns":[{"include":"#string-raw-bin-guts"}]},"string-raw-bin-quoted-single-line":{"begin":"\\\\b(R[Bb]|[Bb]R)(([\\"']))","beginCaptures":{"1":{"name":"storage.type.string.python"},"2":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\2)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.raw.binary.single.python","patterns":[{"include":"#string-raw-bin-guts"}]},"string-raw-guts":{"patterns":[{"include":"#string-consume-escape"},{"include":"#string-formatting"},{"include":"#string-brace-formatting"}]},"string-raw-quoted-multi-line":{"begin":"\\\\b(([Uu]R)|(R))('''|\\"\\"\\")","beginCaptures":{"2":{"name":"invalid.deprecated.prefix.python"},"3":{"name":"storage.type.string.python"},"4":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\4)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.raw.multi.python","patterns":[{"include":"#string-multi-bad-brace1-formatting-raw"},{"include":"#string-multi-bad-brace2-formatting-raw"},{"include":"#string-raw-guts"}]},"string-raw-quoted-single-line":{"begin":"\\\\b(([Uu]R)|(R))(([\\"']))","beginCaptures":{"2":{"name":"invalid.deprecated.prefix.python"},"3":{"name":"storage.type.string.python"},"4":{"name":"punctuation.definition.string.begin.python"}},"end":"(\\\\4)|((?<!\\\\\\\\)\\\\n)","endCaptures":{"1":{"name":"punctuation.definition.string.end.python"},"2":{"name":"invalid.illegal.newline.python"}},"name":"string.quoted.raw.single.python","patterns":[{"include":"#string-single-bad-brace1-formatting-raw"},{"include":"#string-single-bad-brace2-formatting-raw"},{"include":"#string-raw-guts"}]},"string-single-bad-brace1-formatting-raw":{"begin":"(?=\\\\{%(.*?(?!([\\"'])|((?<!\\\\\\\\)\\\\n)))%})","end":"(?=([\\"'])|((?<!\\\\\\\\)\\\\n))","patterns":[{"include":"#string-consume-escape"}]},"string-single-bad-brace1-formatting-unicode":{"begin":"(?=\\\\{%(.*?(?!([\\"'])|((?<!\\\\\\\\)\\\\n)))%})","end":"(?=([\\"'])|((?<!\\\\\\\\)\\\\n))","patterns":[{"include":"#escape-sequence-unicode"},{"include":"#escape-sequence"},{"include":"#string-line-continuation"}]},"string-single-bad-brace2-formatting-raw":{"begin":"(?!\\\\{\\\\{)(?=\\\\{(\\\\w*?(?!([\\"'])|((?<!\\\\\\\\)\\\\n))[^!.:\\\\[}\\\\w]).*?(?!([\\"'])|((?<!\\\\\\\\)\\\\n))})","end":"(?=([\\"'])|((?<!\\\\\\\\)\\\\n))","patterns":[{"include":"#string-consume-escape"},{"include":"#string-formatting"}]},"string-single-bad-brace2-formatting-unicode":{"begin":"(?!\\\\{\\\\{)(?=\\\\{(\\\\w*?(?!([\\"'])|((?<!\\\\\\\\)\\\\n))[^!.:\\\\[}\\\\w]).*?(?!([\\"'])|((?<!\\\\\\\\)\\\\n))})","end":"(?=([\\"'])|((?<!\\\\\\\\)\\\\n))","patterns":[{"include":"#escape-sequence-unicode"},{"include":"#string-entity"}]},"string-unicode-guts":{"patterns":[{"include":"#escape-sequence-unicode"},{"include":"#string-entity"},{"include":"#string-brace-formatting"}]}},"scopeName":"source.python","aliases":["py"]}`))],Kc=[Object.freeze(JSON.parse('{"displayName":"SQL","name":"sql","patterns":[{"match":"((?<!@)@)\\\\b(\\\\w+)\\\\b","name":"text.variable"},{"match":"(\\\\[)[^]]*(])","name":"text.bracketed"},{"include":"#comments"},{"captures":{"1":{"name":"keyword.other.create.sql"},"2":{"name":"keyword.other.sql"},"5":{"name":"entity.name.function.sql"}},"match":"(?i:^\\\\s*(create(?:\\\\s+or\\\\s+replace)?)\\\\s+(aggregate|conversion|database|domain|function|group|(unique\\\\s+)?index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view)\\\\s+)([\\"\'`]?)(\\\\w+)\\\\4","name":"meta.create.sql"},{"captures":{"1":{"name":"keyword.other.create.sql"},"2":{"name":"keyword.other.sql"}},"match":"(?i:^\\\\s*(drop)\\\\s+(aggregate|conversion|database|domain|function|group|index|language|operator class|operator|rule|schema|sequence|table|tablespace|trigger|type|user|view))","name":"meta.drop.sql"},{"captures":{"1":{"name":"keyword.other.create.sql"},"2":{"name":"keyword.other.table.sql"},"3":{"name":"entity.name.function.sql"},"4":{"name":"keyword.other.cascade.sql"}},"match":"(?i:\\\\s*(drop)\\\\s+(table)\\\\s+(\\\\w+)(\\\\s+cascade)?\\\\b)","name":"meta.drop.sql"},{"captures":{"1":{"name":"keyword.other.create.sql"},"2":{"name":"keyword.other.table.sql"}},"match":"(?i:^\\\\s*(alter)\\\\s+(aggregate|conversion|database|domain|function|group|index|language|operator class|operator|proc(edure)?|rule|schema|sequence|table|tablespace|trigger|type|user|view)\\\\s+)","name":"meta.alter.sql"},{"captures":{"1":{"name":"storage.type.sql"},"2":{"name":"storage.type.sql"},"3":{"name":"constant.numeric.sql"},"4":{"name":"storage.type.sql"},"5":{"name":"constant.numeric.sql"},"6":{"name":"storage.type.sql"},"7":{"name":"constant.numeric.sql"},"8":{"name":"constant.numeric.sql"},"9":{"name":"storage.type.sql"},"10":{"name":"constant.numeric.sql"},"11":{"name":"storage.type.sql"},"12":{"name":"storage.type.sql"},"13":{"name":"storage.type.sql"},"14":{"name":"constant.numeric.sql"},"15":{"name":"storage.type.sql"}},"match":"(?i)\\\\b(bigint|bigserial|bit|boolean|box|bytea|cidr|circle|date|double\\\\sprecision|inet|int|integer|line|lseg|macaddr|money|oid|path|point|polygon|real|serial|smallint|sysdate|text)\\\\b|\\\\b(bit\\\\svarying|character\\\\s(?:varying)?|tinyint|var\\\\schar|float|interval)\\\\((\\\\d+)\\\\)|\\\\b(char|number|varchar\\\\d?)\\\\b(?:\\\\((\\\\d+)\\\\))?|\\\\b(numeric|decimal)\\\\b(?:\\\\((\\\\d+),(\\\\d+)\\\\))?|\\\\b(times?)\\\\b(?:\\\\((\\\\d+)\\\\))?(\\\\swith(?:out)?\\\\stime\\\\szone\\\\b)?|\\\\b(timestamp)(s|tz)?\\\\b(?:\\\\((\\\\d+)\\\\))?(\\\\s(with(?:|out))\\\\stime\\\\szone\\\\b)?"},{"match":"(?i:\\\\b((?:primary|foreign)\\\\s+key|references|on\\\\s+(delete|update)(\\\\s+cascade)?|nocheck|check|constraint|collate|default)\\\\b)","name":"storage.modifier.sql"},{"match":"\\\\b\\\\d+\\\\b","name":"constant.numeric.sql"},{"match":"(?i:\\\\b(select(\\\\s+(all|distinct))?|insert\\\\s+(ignore\\\\s+)?into|update|delete|from|set|where|group\\\\s+by|or|like|and|union(\\\\s+all)?|having|order\\\\s+by|limit|cross\\\\s+join|join|straight_join|(inner|(left|right|full)(\\\\s+outer)?)\\\\s+join|natural(\\\\s+(inner|(left|right|full)(\\\\s+outer)?))?\\\\s+join)\\\\b)","name":"keyword.other.DML.sql"},{"match":"(?i:\\\\b(on|off|((is\\\\s+)?not\\\\s+)?null)\\\\b)","name":"keyword.other.DDL.create.II.sql"},{"match":"(?i:\\\\bvalues\\\\b)","name":"keyword.other.DML.II.sql"},{"match":"(?i:\\\\b(begin(\\\\s+work)?|start\\\\s+transaction|commit(\\\\s+work)?|rollback(\\\\s+work)?)\\\\b)","name":"keyword.other.LUW.sql"},{"match":"(?i:\\\\b(grant(\\\\swith\\\\sgrant\\\\soption)?|revoke)\\\\b)","name":"keyword.other.authorization.sql"},{"match":"(?i:\\\\bin\\\\b)","name":"keyword.other.data-integrity.sql"},{"match":"(?i:^\\\\s*(comment\\\\s+on\\\\s+(table|column|aggregate|constraint|database|domain|function|index|operator|rule|schema|sequence|trigger|type|view))\\\\s+)","name":"keyword.other.object-comments.sql"},{"match":"(?i)\\\\bAS\\\\b","name":"keyword.other.alias.sql"},{"match":"(?i)\\\\b(DESC|ASC)\\\\b","name":"keyword.other.order.sql"},{"match":"\\\\*","name":"keyword.operator.star.sql"},{"match":"[!<>]?=|<>|[<>]","name":"keyword.operator.comparison.sql"},{"match":"[-+/]","name":"keyword.operator.math.sql"},{"match":"\\\\|\\\\|","name":"keyword.operator.concatenator.sql"},{"captures":{"1":{"name":"support.function.aggregate.sql"}},"match":"(?i)\\\\b(approx_count_distinct|approx_percentile_cont|approx_percentile_disc|avg|checksum_agg|count|count_big|group|grouping|grouping_id|max|min|sum|stdevp??|varp??)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.analytic.sql"}},"match":"(?i)\\\\b(cume_dist|first_value|lag|last_value|lead|percent_rank|percentile_cont|percentile_disc)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.bitmanipulation.sql"}},"match":"(?i)\\\\b((?:bit_coun|get_bi|left_shif|right_shif|set_bi)t)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.conversion.sql"}},"match":"(?i)\\\\b(cast|convert|parse|try_cast|try_convert|try_parse)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.collation.sql"}},"match":"(?i)\\\\b(collationproperty|tertiary_weights)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.cryptographic.sql"}},"match":"(?i)\\\\b(asymkey_id|asymkeyproperty|certproperty|cert_id|crypt_gen_random|decryptbyasymkey|decryptbycert|decryptbykey|decryptbykeyautoasymkey|decryptbykeyautocert|decryptbypassphrase|encryptbyasymkey|encryptbycert|encryptbykey|encryptbypassphrase|hashbytes|is_objectsigned|key_guid|key_id|key_name|signbyasymkey|signbycert|symkeyproperty|verifysignedbycert|verifysignedbyasymkey)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.cursor.sql"}},"match":"(?i)\\\\b(cursor_status)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.datetime.sql"}},"match":"(?i)\\\\b(sysdatetime|sysdatetimeoffset|sysutcdatetime|current_time(stamp)?|getdate|getutcdate|datename|datepart|day|month|year|datefromparts|datetime2fromparts|datetimefromparts|datetimeoffsetfromparts|smalldatetimefromparts|timefromparts|datediff|dateadd|datetrunc|eomonth|switchoffset|todatetimeoffset|isdate|date_bucket)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.datatype.sql"}},"match":"(?i)\\\\b(datalength|ident_current|ident_incr|ident_seed|identity|sql_variant_property)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.expression.sql"}},"match":"(?i)\\\\b(coalesce|nullif)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.globalvar.sql"}},"match":"(?<!@)@@(?i)\\\\b(cursor_rows|connections|cpu_busy|datefirst|dbts|error|fetch_status|identity|idle|io_busy|langid|language|lock_timeout|max_connections|max_precision|nestlevel|options|packet_errors|pack_received|pack_sent|procid|remserver|rowcount|servername|servicename|spid|textsize|timeticks|total_errors|total_read|total_write|trancount|version)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.json.sql"}},"match":"(?i)\\\\b(json|isjson|json_object|json_array|json_value|json_query|json_modify|json_path_exists)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.logical.sql"}},"match":"(?i)\\\\b(choose|iif|greatest|least)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.mathematical.sql"}},"match":"(?i)\\\\b(abs|acos|asin|atan|atn2|ceiling|cos|cot|degrees|exp|floor|log|log10|pi|power|radians|rand|round|sign|sin|sqrt|square|tan)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.metadata.sql"}},"match":"(?i)\\\\b(app_name|applock_mode|applock_test|assemblyproperty|col_length|col_name|columnproperty|database_principal_id|databasepropertyex|db_id|db_name|file_id|file_idex|file_name|filegroup_id|filegroup_name|filegroupproperty|fileproperty|fulltextcatalogproperty|fulltextserviceproperty|index_col|indexkey_property|indexproperty|object_definition|object_id|object_name|object_schema_name|objectproperty|objectpropertyex|original_db_name|parsename|schema_id|schema_name|scope_identity|serverproperty|stats_date|type_id|type_name|typeproperty)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.ranking.sql"}},"match":"(?i)\\\\b(rank|dense_rank|ntile|row_number)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.rowset.sql"}},"match":"(?i)\\\\b(generate_series|opendatasource|openjson|openrowset|openquery|openxml|predict|string_split)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.security.sql"}},"match":"(?i)\\\\b(certencoded|certprivatekey|current_user|database_principal_id|has_perms_by_name|is_member|is_rolemember|is_srvrolemember|original_login|permissions|pwdcompare|pwdencrypt|schema_id|schema_name|session_user|suser_id|suser_sid|suser_sname|system_user|suser_name|user_id|user_name)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.string.sql"}},"match":"(?i)\\\\b(ascii|char|charindex|concat|difference|format|left|len|lower|ltrim|nchar|nodes|patindex|quotename|replace|replicate|reverse|right|rtrim|soundex|space|str|string_agg|string_escape|string_split|stuff|substring|translate|trim|unicode|upper)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.system.sql"}},"match":"(?i)\\\\b(binary_checksum|checksum|compress|connectionproperty|context_info|current_request_id|current_transaction_id|decompress|error_line|error_message|error_number|error_procedure|error_severity|error_state|formatmessage|get_filestream_transaction_context|getansinull|host_id|host_name|isnull|isnumeric|min_active_rowversion|newid|newsequentialid|rowcount_big|session_context|session_id|xact_state)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.textimage.sql"}},"match":"(?i)\\\\b(patindex|textptr|textvalid)\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"support.function.vector.sql"}},"match":"(?i)\\\\b(vector_(?:distance|norm|normalize))\\\\b\\\\s*\\\\("},{"captures":{"1":{"name":"constant.other.database-name.sql"},"2":{"name":"constant.other.table-name.sql"}},"match":"(\\\\w+?)\\\\.(\\\\w+)"},{"include":"#strings"},{"include":"#regexps"},{"match":"\\\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|array|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_drop|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blockers|blocksize|bmk|both|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|checksum|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|connection|containment|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\\\\\\\s+or\\\\\\\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime2??|datetimeoffset|day(s)?|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_fulltext_language|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filegrowth|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|for|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hour(s)?|http|identity|identity_value|if|ifnull|ignore|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|incremental|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|leading|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|log|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|match|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minute(s)?|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|month|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|nested_triggers|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|nulls|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|quarter|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replace|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|respect|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|scalar|schema|schemabinding|scoped|scroll|scroll_locks|sddl|second|secexpr|seconds|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|sessions??|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|shortest_path|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablockx??|take|tape|target|target_index|target_partition|target_recovery_time|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|top??|torn_page_detection|track_columns_updated|trailing|tran|transaction|transfer|transform_noise_words|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|two_digit_year_cutoff|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|values??|varbinary|varchar|vector|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|wait_at_low_priority|waitfor|webmethod|week|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|windows??|with|within|within group|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|year|zone)\\\\b","name":"keyword.other.sql"},{"captures":{"1":{"name":"punctuation.section.scope.begin.sql"},"2":{"name":"punctuation.section.scope.end.sql"}},"match":"(\\\\()(\\\\))","name":"meta.block.sql"}],"repository":{"comment-block":{"begin":"/\\\\*","captures":{"0":{"name":"punctuation.definition.comment.sql"}},"end":"\\\\*/","name":"comment.block","patterns":[{"include":"#comment-block"}]},"comments":{"patterns":[{"begin":"(^[\\\\t ]+)?(?=--)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.sql"}},"end":"(?!\\\\G)","patterns":[{"begin":"--","beginCaptures":{"0":{"name":"punctuation.definition.comment.sql"}},"end":"\\\\n","name":"comment.line.double-dash.sql"}]},{"begin":"(^[\\\\t ]+)?(?=#)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.sql"}},"end":"(?!\\\\G)","patterns":[]},{"include":"#comment-block"}]},"regexps":{"patterns":[{"begin":"/(?=\\\\S.*/)","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.sql"}},"end":"/","endCaptures":{"0":{"name":"punctuation.definition.string.end.sql"}},"name":"string.regexp.sql","patterns":[{"include":"#string_interpolation"},{"match":"\\\\\\\\/","name":"constant.character.escape.slash.sql"}]},{"begin":"%r\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.sql"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.string.end.sql"}},"name":"string.regexp.modr.sql","patterns":[{"include":"#string_interpolation"}]}]},"string_escape":{"match":"\\\\\\\\.","name":"constant.character.escape.sql"},"string_interpolation":{"captures":{"1":{"name":"punctuation.definition.string.begin.sql"},"3":{"name":"punctuation.definition.string.end.sql"}},"match":"(#\\\\{)([^}]*)(})","name":"string.interpolated.sql"},"strings":{"patterns":[{"captures":{"2":{"name":"punctuation.definition.string.begin.sql"},"3":{"name":"punctuation.definition.string.end.sql"}},"match":"(N)?(\')[^\']*(\')","name":"string.quoted.single.sql"},{"begin":"\'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.sql"}},"end":"\'","endCaptures":{"0":{"name":"punctuation.definition.string.end.sql"}},"name":"string.quoted.single.sql","patterns":[{"include":"#string_escape"}]},{"captures":{"1":{"name":"punctuation.definition.string.begin.sql"},"2":{"name":"punctuation.definition.string.end.sql"}},"match":"(`)[^\\\\\\\\`]*(`)","name":"string.quoted.other.backtick.sql"},{"begin":"`","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.sql"}},"end":"`","endCaptures":{"0":{"name":"punctuation.definition.string.end.sql"}},"name":"string.quoted.other.backtick.sql","patterns":[{"include":"#string_escape"}]},{"captures":{"1":{"name":"punctuation.definition.string.begin.sql"},"2":{"name":"punctuation.definition.string.end.sql"}},"match":"(\\")[^\\"#]*(\\")","name":"string.quoted.double.sql"},{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.sql"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.sql"}},"name":"string.quoted.double.sql","patterns":[{"include":"#string_interpolation"}]},{"begin":"%\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.sql"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.string.end.sql"}},"name":"string.other.quoted.brackets.sql","patterns":[{"include":"#string_interpolation"}]}]}},"scopeName":"source.sql"}'))],el=[Object.freeze(JSON.parse(`{"displayName":"TSX","name":"tsx","patterns":[{"include":"#directives"},{"include":"#statements"},{"include":"#shebang"}],"repository":{"access-modifier":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(abstract|declare|override|public|protected|private|readonly|static)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.tsx"},"after-operator-block-as-object-literal":{"begin":"(?<!\\\\+\\\\+|--)(?<=[!(+,:=>?\\\\[]|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^yield|[^$._[:alnum:]]yield|^throw|[^$._[:alnum:]]throw|^in|[^$._[:alnum:]]in|^of|[^$._[:alnum:]]of|^typeof|[^$._[:alnum:]]typeof|&&|\\\\|\\\\||\\\\*)\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"name":"meta.objectliteral.tsx","patterns":[{"include":"#object-member"}]},"array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"patterns":[{"include":"#binding-element"},{"include":"#punctuation-comma"}]},"array-binding-pattern-const":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"patterns":[{"include":"#binding-element-const"},{"include":"#punctuation-comma"}]},"array-literal":{"begin":"\\\\s*(\\\\[)","beginCaptures":{"1":{"name":"meta.brace.square.tsx"}},"end":"]","endCaptures":{"0":{"name":"meta.brace.square.tsx"}},"name":"meta.array.literal.tsx","patterns":[{"include":"#expression"},{"include":"#punctuation-comma"}]},"arrow-function":{"patterns":[{"captures":{"1":{"name":"storage.modifier.async.tsx"},"2":{"name":"variable.parameter.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))\\\\b(async)\\\\s+)?([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?==>)","name":"meta.arrow.tsx"},{"begin":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))\\\\b(async))?((?<![]!)}])\\\\s*(?=((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.arrow.tsx","patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"include":"#function-parameters"},{"include":"#arrow-return-type"},{"include":"#possibly-arrow-return-type"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.tsx"}},"end":"((?<=[}\\\\S])(?<!=>)|((?!\\\\{)(?=\\\\S)))(?!/[*/])","name":"meta.arrow.tsx","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#decl-block"},{"include":"#expression"}]}]},"arrow-return-type":{"begin":"(?<=\\\\))\\\\s*(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.tsx"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.return.type.arrow.tsx","patterns":[{"include":"#arrow-return-type-body"}]},"arrow-return-type-body":{"patterns":[{"begin":"(?<=:)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"async-modifier":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(async)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.async.tsx"},"binding-element":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#object-binding-pattern"},{"include":"#array-binding-pattern"},{"include":"#destructuring-variable-rest"},{"include":"#variable-initializer"}]},"binding-element-const":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#object-binding-pattern-const"},{"include":"#array-binding-pattern-const"},{"include":"#destructuring-variable-rest-const"},{"include":"#variable-initializer"}]},"boolean-literal":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))true(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.boolean.true.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))false(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.boolean.false.tsx"}]},"brackets":{"patterns":[{"begin":"\\\\{","end":"}|(?=\\\\*/)","patterns":[{"include":"#brackets"}]},{"begin":"\\\\[","end":"]|(?=\\\\*/)","patterns":[{"include":"#brackets"}]}]},"cast":{"patterns":[{"include":"#jsx"}]},"class-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(?:(abstract)\\\\s+)?\\\\b(class)\\\\b(?=\\\\s+|/[*/])","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.tsx"},"4":{"name":"storage.type.class.tsx"}},"end":"(?<=})","name":"meta.class.tsx","patterns":[{"include":"#class-declaration-or-expression-patterns"}]},"class-declaration-or-expression-patterns":{"patterns":[{"include":"#comment"},{"include":"#class-or-interface-heritage"},{"captures":{"0":{"name":"entity.name.type.class.tsx"}},"match":"[$_[:alpha:]][$_[:alnum:]]*"},{"include":"#type-parameters"},{"include":"#class-or-interface-body"}]},"class-expression":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(abstract)\\\\s+)?(class)\\\\b(?=\\\\s+|[<{]|/[*/])","beginCaptures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"storage.type.class.tsx"}},"end":"(?<=})","name":"meta.class.tsx","patterns":[{"include":"#class-declaration-or-expression-patterns"}]},"class-or-interface-body":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"patterns":[{"include":"#comment"},{"include":"#decorator"},{"begin":"(?<=:)\\\\s*","end":"(?=[-\\\\])+,:;}\\\\s]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#expression"}]},{"include":"#method-declaration"},{"include":"#indexer-declaration"},{"include":"#field-declaration"},{"include":"#string"},{"include":"#type-annotation"},{"include":"#variable-initializer"},{"include":"#access-modifier"},{"include":"#property-accessor"},{"include":"#async-modifier"},{"include":"#after-operator-block-as-object-literal"},{"include":"#decl-block"},{"include":"#expression"},{"include":"#punctuation-comma"},{"include":"#punctuation-semicolon"}]},"class-or-interface-heritage":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))\\\\b(extends|implements)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"storage.modifier.tsx"}},"end":"(?=\\\\{)","patterns":[{"include":"#comment"},{"include":"#class-or-interface-heritage"},{"include":"#type-parameters"},{"include":"#expressionWithoutIdentifiers"},{"captures":{"1":{"name":"entity.name.type.module.tsx"},"2":{"name":"punctuation.accessor.tsx"},"3":{"name":"punctuation.accessor.optional.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))(?=\\\\s*[$_[:alpha:]][$_[:alnum:]]*(\\\\s*\\\\??\\\\.\\\\s*[$_[:alpha:]][$_[:alnum:]]*)*\\\\s*)"},{"captures":{"1":{"name":"entity.other.inherited-class.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)"},{"include":"#expressionPunctuations"}]},"comment":{"patterns":[{"begin":"/\\\\*\\\\*(?!/)","beginCaptures":{"0":{"name":"punctuation.definition.comment.tsx"}},"end":"\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.tsx"}},"name":"comment.block.documentation.tsx","patterns":[{"include":"#docblock"}]},{"begin":"(/\\\\*)(?:\\\\s*((@)internal)(?=\\\\s|(\\\\*/)))?","beginCaptures":{"1":{"name":"punctuation.definition.comment.tsx"},"2":{"name":"storage.type.internaldeclaration.tsx"},"3":{"name":"punctuation.decorator.internaldeclaration.tsx"}},"end":"\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.tsx"}},"name":"comment.block.tsx"},{"begin":"(^[\\\\t ]+)?((//)(?:\\\\s*((@)internal)(?=\\\\s|$))?)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.tsx"},"2":{"name":"comment.line.double-slash.tsx"},"3":{"name":"punctuation.definition.comment.tsx"},"4":{"name":"storage.type.internaldeclaration.tsx"},"5":{"name":"punctuation.decorator.internaldeclaration.tsx"}},"contentName":"comment.line.double-slash.tsx","end":"(?=$)"}]},"control-statement":{"patterns":[{"include":"#switch-statement"},{"include":"#for-loop"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(catch|finally|throw|try)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.trycatch.tsx"},{"captures":{"1":{"name":"keyword.control.loop.tsx"},"2":{"name":"entity.name.label.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(break|continue|goto)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(break|continue|do|goto|while)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.loop.tsx"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(return)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"0":{"name":"keyword.control.flow.tsx"}},"end":"(?=[;}]|$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#expression"}]},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(case|default|switch)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.switch.tsx"},{"include":"#if-statement"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(else|if)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.conditional.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(with)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.with.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(package)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(debugger)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.other.debugger.tsx"}]},"decl-block":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"name":"meta.block.tsx","patterns":[{"include":"#statements"}]},"declaration":{"patterns":[{"include":"#decorator"},{"include":"#var-expr"},{"include":"#function-declaration"},{"include":"#class-declaration"},{"include":"#interface-declaration"},{"include":"#enum-declaration"},{"include":"#namespace-declaration"},{"include":"#type-alias-declaration"},{"include":"#import-equals-declaration"},{"include":"#import-declaration"},{"include":"#export-declaration"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(declare|export)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.tsx"}]},"decorator":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))@","beginCaptures":{"0":{"name":"punctuation.decorator.tsx"}},"end":"(?=\\\\s)","name":"meta.decorator.tsx","patterns":[{"include":"#expression"}]},"destructuring-const":{"patterns":[{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\{)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.object-binding-pattern-variable.tsx","patterns":[{"include":"#object-binding-pattern-const"},{"include":"#type-annotation"},{"include":"#comment"}]},{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\[)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.array-binding-pattern-variable.tsx","patterns":[{"include":"#array-binding-pattern-const"},{"include":"#type-annotation"},{"include":"#comment"}]}]},"destructuring-parameter":{"patterns":[{"begin":"(?<![:=])\\\\s*(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"name":"meta.parameter.object-binding-pattern.tsx","patterns":[{"include":"#parameter-object-binding-element"}]},{"begin":"(?<![:=])\\\\s*(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"name":"meta.paramter.array-binding-pattern.tsx","patterns":[{"include":"#parameter-binding-element"},{"include":"#punctuation-comma"}]}]},"destructuring-parameter-rest":{"captures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"variable.parameter.tsx"}},"match":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)"},"destructuring-variable":{"patterns":[{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\{)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.object-binding-pattern-variable.tsx","patterns":[{"include":"#object-binding-pattern"},{"include":"#type-annotation"},{"include":"#comment"}]},{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\[)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.array-binding-pattern-variable.tsx","patterns":[{"include":"#array-binding-pattern"},{"include":"#type-annotation"},{"include":"#comment"}]}]},"destructuring-variable-rest":{"captures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"meta.definition.variable.tsx variable.other.readwrite.tsx"}},"match":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)"},"destructuring-variable-rest-const":{"captures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"meta.definition.variable.tsx variable.other.constant.tsx"}},"match":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)"},"directives":{"begin":"^(///)\\\\s*(?=<(reference|amd-dependency|amd-module)(\\\\s+(path|types|no-default-lib|lib|name|resolution-mode)\\\\s*=\\\\s*(('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)))+\\\\s*/>\\\\s*$)","beginCaptures":{"1":{"name":"punctuation.definition.comment.tsx"}},"end":"(?=$)","name":"comment.line.triple-slash.directive.tsx","patterns":[{"begin":"(<)(reference|amd-dependency|amd-module)","beginCaptures":{"1":{"name":"punctuation.definition.tag.directive.tsx"},"2":{"name":"entity.name.tag.directive.tsx"}},"end":"/>","endCaptures":{"0":{"name":"punctuation.definition.tag.directive.tsx"}},"name":"meta.tag.tsx","patterns":[{"match":"path|types|no-default-lib|lib|name|resolution-mode","name":"entity.other.attribute-name.directive.tsx"},{"match":"=","name":"keyword.operator.assignment.tsx"},{"include":"#string"}]}]},"docblock":{"patterns":[{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.access-type.jsdoc"}},"match":"((@)a(?:ccess|pi))\\\\s+(p(?:rivate|rotected|ublic))\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"5":{"name":"constant.other.email.link.underline.jsdoc"},"6":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"match":"((@)author)\\\\s+([^*/<>@\\\\s](?:[^*/<>@]|\\\\*[^/])*)(?:\\\\s*(<)([^>\\\\s]+)(>))?"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"keyword.operator.control.jsdoc"},"5":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)borrows)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)\\\\s+(as)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)"},{"begin":"((@)example)\\\\s+","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=@|\\\\*/)","name":"meta.example.jsdoc","patterns":[{"match":"^\\\\s\\\\*\\\\s+"},{"begin":"\\\\G(<)caption(>)","beginCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"contentName":"constant.other.description.jsdoc","end":"(</)caption(>)|(?=\\\\*/)","endCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}}},{"captures":{"0":{"name":"source.embedded.tsx"}},"match":"[^*@\\\\s](?:[^*]|\\\\*[^/])*"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.symbol-type.jsdoc"}},"match":"((@)kind)\\\\s+(class|constant|event|external|file|function|member|mixin|module|namespace|typedef)\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.link.underline.jsdoc"},"4":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)see)\\\\s+(?:((?=https?://)(?:[^*\\\\s]|\\\\*[^/])+)|((?!https?://|(?:\\\\[[^]\\\\[]*])?\\\\{@(?:link|linkcode|linkplain|tutorial)\\\\b)(?:[^*/@\\\\s]|\\\\*[^/])+))"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)template)\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*(?:\\\\s*,\\\\s*[$A-Z_a-z][]$.\\\\[\\\\w]*)*)"},{"begin":"((@)template)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:arg|argument|const|constant|member|namespace|param|var))\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*)"},{"begin":"((@)typedef)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"(?:[^*/@\\\\s]|\\\\*[^/])+","name":"entity.name.type.instance.jsdoc"}]},{"begin":"((@)(?:arg|argument|const|constant|member|namespace|param|prop|property|var))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"},{"captures":{"1":{"name":"punctuation.definition.optional-value.begin.bracket.square.jsdoc"},"2":{"name":"keyword.operator.assignment.jsdoc"},"3":{"name":"source.embedded.tsx"},"4":{"name":"punctuation.definition.optional-value.end.bracket.square.jsdoc"},"5":{"name":"invalid.illegal.syntax.jsdoc"}},"match":"(\\\\[)\\\\s*[$\\\\w]+(?:(?:\\\\[])?\\\\.[$\\\\w]+)*(?:\\\\s*(=)\\\\s*((?>\\"(?:\\\\*(?!/)|\\\\\\\\(?!\\")|[^*\\\\\\\\])*?\\"|'(?:\\\\*(?!/)|\\\\\\\\(?!')|[^*\\\\\\\\])*?'|\\\\[(?:\\\\*(?!/)|[^*])*?]|(?:\\\\*(?!/)|\\\\s(?!\\\\s*])|\\\\[.*?(?:]|(?=\\\\*/))|[^]*\\\\[\\\\s])*)*))?\\\\s*(?:(])((?:[^*\\\\s]|\\\\*[^/\\\\s])+)?|(?=\\\\*/))","name":"variable.other.jsdoc"}]},{"begin":"((@)(?:define|enum|exception|export|extends|lends|implements|modifies|namespace|private|protected|returns?|satisfies|suppress|this|throws|type|yields?))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)(?:alias|augments|callback|constructs|emits|event|fires|exports?|extends|external|function|func|host|lends|listens|interface|memberof!?|method|module|mixes|mixin|name|requires|see|this|typedef|uses))\\\\s+((?:[^*@{}\\\\s]|\\\\*[^/])+)"},{"begin":"((@)(?:default(?:value)?|license|version))\\\\s+(([\\"']))","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"},"4":{"name":"punctuation.definition.string.begin.jsdoc"}},"contentName":"variable.other.jsdoc","end":"(\\\\3)|(?=$|\\\\*/)","endCaptures":{"0":{"name":"variable.other.jsdoc"},"1":{"name":"punctuation.definition.string.end.jsdoc"}}},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:default(?:value)?|license|tutorial|variation|version))\\\\s+([^*\\\\s]+)"},{"captures":{"1":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"(@)(?:abstract|access|alias|api|arg|argument|async|attribute|augments|author|beta|borrows|bubbles|callback|chainable|class|classdesc|code|config|const|constant|constructor|constructs|copyright|default|defaultvalue|define|deprecated|desc|description|dict|emits|enum|event|example|exception|exports?|extends|extension(?:_?for)?|external|externs|file|fileoverview|final|fires|for|func|function|generator|global|hideconstructor|host|ignore|implements|implicitCast|inherit[Dd]oc|inner|instance|interface|internal|kind|lends|license|listens|main|member|memberof!?|method|mixes|mixins?|modifies|module|name|namespace|noalias|nocollapse|nocompile|nosideeffects|override|overview|package|param|polymer(?:Behavior)?|preserve|private|prop|property|protected|public|read[Oo]nly|record|require[ds]|returns?|see|since|static|struct|submodule|summary|suppress|template|this|throws|todo|tutorial|type|typedef|unrestricted|uses|var|variation|version|virtual|writeOnce|yields?)\\\\b","name":"storage.type.class.jsdoc"},{"include":"#inline-tags"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"((@)[$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s+)"}]},"enum-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?(?:\\\\b(const)\\\\s+)?\\\\b(enum)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.tsx"},"4":{"name":"storage.type.enum.tsx"},"5":{"name":"entity.name.type.enum.tsx"}},"end":"(?<=})","name":"meta.enum.declaration.tsx","patterns":[{"include":"#comment"},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"patterns":[{"include":"#comment"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)","beginCaptures":{"0":{"name":"variable.other.enummember.tsx"}},"end":"(?=[,}]|$)","patterns":[{"include":"#comment"},{"include":"#variable-initializer"}]},{"begin":"(?=(('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+])))","end":"(?=[,}]|$)","patterns":[{"include":"#string"},{"include":"#array-literal"},{"include":"#comment"},{"include":"#variable-initializer"}]},{"include":"#punctuation-comma"}]}]},"export-declaration":{"patterns":[{"captures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"keyword.control.as.tsx"},"3":{"name":"storage.type.namespace.tsx"},"4":{"name":"entity.name.type.module.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(export)\\\\s+(as)\\\\s+(namespace)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(export)(?:\\\\s+(type))?(?:\\\\s*(=)|\\\\s+(default)(?=\\\\s+))","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"keyword.control.type.tsx"},"3":{"name":"keyword.operator.assignment.tsx"},"4":{"name":"keyword.control.default.tsx"}},"end":"(?=$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.export.default.tsx","patterns":[{"include":"#interface-declaration"},{"include":"#expression"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(export)(?:\\\\s+(type))?\\\\b(?!(\\\\$)|(\\\\s*:))((?=\\\\s*[*{])|((?=\\\\s*[$_[:alpha:]][$_[:alnum:]]*([,\\\\s]))(?!\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)))","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"keyword.control.type.tsx"}},"end":"(?=$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.export.tsx","patterns":[{"include":"#import-export-declaration"}]}]},"expression":{"patterns":[{"include":"#expressionWithoutIdentifiers"},{"include":"#identifiers"},{"include":"#expressionPunctuations"}]},"expression-inside-possibly-arrow-parens":{"patterns":[{"include":"#expressionWithoutIdentifiers"},{"include":"#comment"},{"include":"#string"},{"include":"#decorator"},{"include":"#destructuring-parameter"},{"captures":{"1":{"name":"storage.modifier.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|protected|private|readonly)\\\\s+(?=(override|public|protected|private|readonly)\\\\s+)"},{"captures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.operator.rest.tsx"},"3":{"name":"entity.name.function.tsx variable.language.this.tsx"},"4":{"name":"entity.name.function.tsx"},"5":{"name":"keyword.operator.optional.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.operator.rest.tsx"},"3":{"name":"variable.parameter.tsx variable.language.this.tsx"},"4":{"name":"variable.parameter.tsx"},"5":{"name":"keyword.operator.optional.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)(?=\\\\s*[,:]|$)"},{"include":"#type-annotation"},{"include":"#variable-initializer"},{"match":",","name":"punctuation.separator.parameter.tsx"},{"include":"#identifiers"},{"include":"#expressionPunctuations"}]},"expression-operators":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(await)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.flow.tsx"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(yield)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?=\\\\s*/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*\\\\*)","beginCaptures":{"1":{"name":"keyword.control.flow.tsx"}},"end":"\\\\*","endCaptures":{"0":{"name":"keyword.generator.asterisk.tsx"}},"patterns":[{"include":"#comment"}]},{"captures":{"1":{"name":"keyword.control.flow.tsx"},"2":{"name":"keyword.generator.asterisk.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(yield)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?:\\\\s*(\\\\*))?"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))delete(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.delete.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))in(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?!\\\\()","name":"keyword.operator.expression.in.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))of(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?!\\\\()","name":"keyword.operator.expression.of.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))instanceof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.instanceof.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))new(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.new.tsx"},{"include":"#typeof-operator"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))void(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.void.tsx"},{"captures":{"1":{"name":"keyword.control.as.tsx"},"2":{"name":"storage.modifier.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as)\\\\s+(const)(?=\\\\s*($|[]),:;}]))"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(as)|(satisfies))\\\\s+","beginCaptures":{"1":{"name":"keyword.control.as.tsx"},"2":{"name":"keyword.control.satisfies.tsx"}},"end":"(?=^|[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as|satisfies)\\\\s+)|(\\\\s+<))","patterns":[{"include":"#type"}]},{"match":"\\\\.\\\\.\\\\.","name":"keyword.operator.spread.tsx"},{"match":"(?:\\\\*|(?<!\\\\()/|[-%+])=","name":"keyword.operator.assignment.compound.tsx"},{"match":"(?:[\\\\&^]|<<|>>>??|\\\\|)=","name":"keyword.operator.assignment.compound.bitwise.tsx"},{"match":"<<|>>>?","name":"keyword.operator.bitwise.shift.tsx"},{"match":"[!=]==?","name":"keyword.operator.comparison.tsx"},{"match":"<=|>=|<>|[<>]","name":"keyword.operator.relational.tsx"},{"captures":{"1":{"name":"keyword.operator.logical.tsx"},"2":{"name":"keyword.operator.assignment.compound.tsx"},"3":{"name":"keyword.operator.arithmetic.tsx"}},"match":"(?<=[$_[:alnum:]])(!)\\\\s*(?:(/=)|(/)(?![*/]))"},{"match":"!|&&|\\\\|\\\\||\\\\?\\\\?","name":"keyword.operator.logical.tsx"},{"match":"[\\\\&^|~]","name":"keyword.operator.bitwise.tsx"},{"match":"=","name":"keyword.operator.assignment.tsx"},{"match":"--","name":"keyword.operator.decrement.tsx"},{"match":"\\\\+\\\\+","name":"keyword.operator.increment.tsx"},{"match":"[-%*+/]","name":"keyword.operator.arithmetic.tsx"},{"begin":"(?<=[]$)_[:alnum:]])\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)+(?:(/=)|(/)(?![*/])))","end":"(/=)|(/)(?!\\\\*([^*]|(\\\\*[^/]))*\\\\*/)","endCaptures":{"1":{"name":"keyword.operator.assignment.compound.tsx"},"2":{"name":"keyword.operator.arithmetic.tsx"}},"patterns":[{"include":"#comment"}]},{"captures":{"1":{"name":"keyword.operator.assignment.compound.tsx"},"2":{"name":"keyword.operator.arithmetic.tsx"}},"match":"(?<=[]$)_[:alnum:]])\\\\s*(?:(/=)|(/)(?![*/]))"}]},"expressionPunctuations":{"patterns":[{"include":"#punctuation-comma"},{"include":"#punctuation-accessor"}]},"expressionWithoutIdentifiers":{"patterns":[{"include":"#jsx"},{"include":"#string"},{"include":"#regex"},{"include":"#comment"},{"include":"#function-expression"},{"include":"#class-expression"},{"include":"#arrow-function"},{"include":"#paren-expression-possibly-arrow"},{"include":"#cast"},{"include":"#ternary-expression"},{"include":"#new-expr"},{"include":"#instanceof-expr"},{"include":"#object-literal"},{"include":"#expression-operators"},{"include":"#function-call"},{"include":"#literal"},{"include":"#support-objects"},{"include":"#paren-expression"}]},"field-declaration":{"begin":"(?<!\\\\()(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(readonly)\\\\s+)?(?=\\\\s*(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|(#?[$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(?:(?:(\\\\?)|(!))\\\\s*)?([,:;=}]|$))","beginCaptures":{"1":{"name":"storage.modifier.tsx"}},"end":"(?=[,;}]|$|^((?!\\\\s*(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|(#?[$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(?:(?:(\\\\?)|(!))\\\\s*)?([,:;=]|$))))|(?<=})","name":"meta.field.declaration.tsx","patterns":[{"include":"#variable-initializer"},{"include":"#type-annotation"},{"include":"#string"},{"include":"#array-literal"},{"include":"#numeric-literal"},{"include":"#comment"},{"captures":{"1":{"name":"meta.definition.property.tsx entity.name.function.tsx"},"2":{"name":"keyword.operator.optional.tsx"},"3":{"name":"keyword.operator.definiteassignment.tsx"}},"match":"(#?[$_[:alpha:]][$_[:alnum:]]*)(?:(\\\\?)|(!))?(?=\\\\s*\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"match":"#?[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.property.tsx variable.object.property.tsx"},{"match":"\\\\?","name":"keyword.operator.optional.tsx"},{"match":"!","name":"keyword.operator.definiteassignment.tsx"}]},"for-loop":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))for(?=((\\\\s+|(\\\\s*/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*))await)?\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)?(\\\\())","beginCaptures":{"0":{"name":"keyword.control.loop.tsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#comment"},{"match":"await","name":"keyword.control.loop.tsx"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#var-expr"},{"include":"#expression"},{"include":"#punctuation-semicolon"}]}]},"function-body":{"patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"include":"#function-parameters"},{"include":"#return-type"},{"include":"#type-function-return-type"},{"include":"#decl-block"},{"match":"\\\\*","name":"keyword.generator.asterisk.tsx"}]},"function-call":{"patterns":[{"begin":"(?=(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\\\\())","end":"(?<=\\\\))(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\\\\())","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\\\\())","name":"meta.function-call.tsx","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"},{"include":"#paren-expression"}]},{"begin":"(?=(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","end":"(?<=>)(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*[(\\\\[{]\\\\s*)$)","name":"meta.function-call.tsx","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"}]}]},"function-call-optionals":{"patterns":[{"match":"\\\\?\\\\.","name":"meta.function-call.tsx punctuation.accessor.optional.tsx"},{"match":"!","name":"meta.function-call.tsx keyword.operator.definiteassignment.tsx"}]},"function-call-target":{"patterns":[{"include":"#support-function-call-identifiers"},{"match":"(#?[$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.tsx"}]},"function-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?(?:(async)\\\\s+)?(function)\\\\b(?:\\\\s*(\\\\*))?(?:(?:\\\\s+|(?<=\\\\*))([$_[:alpha:]][$_[:alnum:]]*))?\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.async.tsx"},"4":{"name":"storage.type.function.tsx"},"5":{"name":"keyword.generator.asterisk.tsx"},"6":{"name":"meta.definition.function.tsx entity.name.function.tsx"}},"end":"(?=;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|(?<=})","name":"meta.function.tsx","patterns":[{"include":"#function-name"},{"include":"#function-body"}]},"function-expression":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(async)\\\\s+)?(function)\\\\b(?:\\\\s*(\\\\*))?(?:(?:\\\\s+|(?<=\\\\*))([$_[:alpha:]][$_[:alnum:]]*))?\\\\s*","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"},"2":{"name":"storage.type.function.tsx"},"3":{"name":"keyword.generator.asterisk.tsx"},"4":{"name":"meta.definition.function.tsx entity.name.function.tsx"}},"end":"(?=;)|(?<=})","name":"meta.function.expression.tsx","patterns":[{"include":"#function-name"},{"include":"#single-line-comment-consuming-line-ending"},{"include":"#function-body"}]},"function-name":{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.function.tsx entity.name.function.tsx"},"function-parameters":{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.definition.parameters.begin.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.tsx"}},"name":"meta.parameters.tsx","patterns":[{"include":"#function-parameters-body"}]},"function-parameters-body":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#decorator"},{"include":"#destructuring-parameter"},{"include":"#parameter-name"},{"include":"#parameter-type-annotation"},{"include":"#variable-initializer"},{"match":",","name":"punctuation.separator.parameter.tsx"}]},"identifiers":{"patterns":[{"include":"#object-identifiers"},{"captures":{"1":{"name":"punctuation.accessor.tsx"},"2":{"name":"punctuation.accessor.optional.tsx"},"3":{"name":"entity.name.function.tsx"}},"match":"(?:(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s*=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))"},{"captures":{"1":{"name":"punctuation.accessor.tsx"},"2":{"name":"punctuation.accessor.optional.tsx"},"3":{"name":"variable.other.constant.property.tsx"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])"},{"captures":{"1":{"name":"punctuation.accessor.tsx"},"2":{"name":"punctuation.accessor.optional.tsx"},"3":{"name":"variable.other.property.tsx"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*)"},{"match":"(\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])","name":"variable.other.constant.tsx"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"variable.other.readwrite.tsx"}]},"if-statement":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?=\\\\bif\\\\s*(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))\\\\s*(?!\\\\{))","end":"(?=;|$|})","patterns":[{"include":"#comment"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(if)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"keyword.control.conditional.tsx"},"2":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#expression"}]},{"begin":"(?<=\\\\))\\\\s*/(?![*/])(?=(?:[^/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)*])+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.tsx"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.tsx"},"2":{"name":"keyword.other.tsx"}},"name":"string.regexp.tsx","patterns":[{"include":"#regexp"}]},{"include":"#statements"}]}]},"import-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(import)(?:\\\\s+(type)(?!\\\\s+from))?(?!\\\\s*[(:])(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"keyword.control.import.tsx"},"4":{"name":"keyword.control.type.tsx"}},"end":"(?<!(?:^|[^$._[:alnum:]])import)(?=;|$|^)","name":"meta.import.tsx","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#string"},{"begin":"(?<=(?:^|[^$._[:alnum:]])import)(?!\\\\s*[\\"'])","end":"\\\\bfrom\\\\b","endCaptures":{"0":{"name":"keyword.control.from.tsx"}},"patterns":[{"include":"#import-export-declaration"}]},{"include":"#import-export-declaration"}]},"import-equals-declaration":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(import)(?:\\\\s+(type))?\\\\s+([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(=)\\\\s*(require)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"keyword.control.import.tsx"},"4":{"name":"keyword.control.type.tsx"},"5":{"name":"variable.other.readwrite.alias.tsx"},"6":{"name":"keyword.operator.assignment.tsx"},"7":{"name":"keyword.control.require.tsx"},"8":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"name":"meta.import-equals.external.tsx","patterns":[{"include":"#comment"},{"include":"#string"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(import)(?:\\\\s+(type))?\\\\s+([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(=)\\\\s*(?!require\\\\b)","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"keyword.control.import.tsx"},"4":{"name":"keyword.control.type.tsx"},"5":{"name":"variable.other.readwrite.alias.tsx"},"6":{"name":"keyword.operator.assignment.tsx"}},"end":"(?=;|$|^)","name":"meta.import-equals.internal.tsx","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"captures":{"1":{"name":"entity.name.type.module.tsx"},"2":{"name":"punctuation.accessor.tsx"},"3":{"name":"punctuation.accessor.optional.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"variable.other.readwrite.tsx"}]}]},"import-export-assert-clause":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(with)|(assert))\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"keyword.control.with.tsx"},"2":{"name":"keyword.control.assert.tsx"},"3":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"patterns":[{"include":"#comment"},{"include":"#string"},{"match":"[$_[:alpha:]][$_[:alnum:]]*\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object-literal.key.tsx"},{"match":":","name":"punctuation.separator.key-value.tsx"}]},"import-export-block":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"name":"meta.block.tsx","patterns":[{"include":"#import-export-clause"}]},"import-export-clause":{"patterns":[{"include":"#comment"},{"captures":{"1":{"name":"keyword.control.type.tsx"},"2":{"name":"keyword.control.default.tsx"},"3":{"name":"constant.language.import-export-all.tsx"},"4":{"name":"variable.other.readwrite.tsx"},"5":{"name":"string.quoted.alias.tsx"},"12":{"name":"keyword.control.as.tsx"},"13":{"name":"keyword.control.default.tsx"},"14":{"name":"variable.other.readwrite.alias.tsx"},"15":{"name":"string.quoted.alias.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(type)\\\\s+)?(?:\\\\b(default)|(\\\\*)|\\\\b([$_[:alpha:]][$_[:alnum:]]*)|(('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)))\\\\s+(as)\\\\s+(?:(default(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|([$_[:alpha:]][$_[:alnum:]]*)|(('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)))"},{"include":"#punctuation-comma"},{"match":"\\\\*","name":"constant.language.import-export-all.tsx"},{"match":"\\\\b(default)\\\\b","name":"keyword.control.default.tsx"},{"captures":{"1":{"name":"keyword.control.type.tsx"},"2":{"name":"variable.other.readwrite.alias.tsx"},"3":{"name":"string.quoted.alias.tsx"}},"match":"(?:\\\\b(type)\\\\s+)?(?:([$_[:alpha:]][$_[:alnum:]]*)|(('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)))"}]},"import-export-declaration":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#import-export-block"},{"match":"\\\\bfrom\\\\b","name":"keyword.control.from.tsx"},{"include":"#import-export-assert-clause"},{"include":"#import-export-clause"}]},"indexer-declaration":{"begin":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(readonly)\\\\s*)?\\\\s*(\\\\[)\\\\s*([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=:)","beginCaptures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"meta.brace.square.tsx"},"3":{"name":"variable.parameter.tsx"}},"end":"(])\\\\s*(\\\\?\\\\s*)?|$","endCaptures":{"1":{"name":"meta.brace.square.tsx"},"2":{"name":"keyword.operator.optional.tsx"}},"name":"meta.indexer.declaration.tsx","patterns":[{"include":"#type-annotation"}]},"indexer-mapped-type-declaration":{"begin":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))([-+])?(readonly)\\\\s*)?\\\\s*(\\\\[)\\\\s*([$_[:alpha:]][$_[:alnum:]]*)\\\\s+(in)\\\\s+","beginCaptures":{"1":{"name":"keyword.operator.type.modifier.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"meta.brace.square.tsx"},"4":{"name":"entity.name.type.tsx"},"5":{"name":"keyword.operator.expression.in.tsx"}},"end":"(])([-+])?\\\\s*(\\\\?\\\\s*)?|$","endCaptures":{"1":{"name":"meta.brace.square.tsx"},"2":{"name":"keyword.operator.type.modifier.tsx"},"3":{"name":"keyword.operator.optional.tsx"}},"name":"meta.indexer.mappedtype.declaration.tsx","patterns":[{"captures":{"1":{"name":"keyword.control.as.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as)\\\\s+"},{"include":"#type"}]},"inline-tags":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.bracket.square.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.square.end.jsdoc"}},"match":"(\\\\[)[^]]+(])(?=\\\\{@(?:link|linkcode|linkplain|tutorial))","name":"constant.other.description.jsdoc"},{"begin":"(\\\\{)((@)(?:link(?:code|plain)?|tutorial))\\\\s*","beginCaptures":{"1":{"name":"punctuation.definition.bracket.curly.begin.jsdoc"},"2":{"name":"storage.type.class.jsdoc"},"3":{"name":"punctuation.definition.inline.tag.jsdoc"}},"end":"}|(?=\\\\*/)","endCaptures":{"0":{"name":"punctuation.definition.bracket.curly.end.jsdoc"}},"name":"entity.name.type.instance.jsdoc","patterns":[{"captures":{"1":{"name":"variable.other.link.underline.jsdoc"},"2":{"name":"punctuation.separator.pipe.jsdoc"}},"match":"\\\\G((?=https?://)(?:[^*|}\\\\s]|\\\\*/)+)(\\\\|)?"},{"captures":{"1":{"name":"variable.other.description.jsdoc"},"2":{"name":"punctuation.separator.pipe.jsdoc"}},"match":"\\\\G((?:[^*@{|}\\\\s]|\\\\*[^/])+)(\\\\|)?"}]}]},"instanceof-expr":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(instanceof)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.operator.expression.instanceof.tsx"}},"end":"(?<=\\\\))|(?=[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|([!=]==?)|(([\\\\&^|~]\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s+instanceof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))function((\\\\s+[$_[:alpha:]][$_[:alnum:]]*)|(\\\\s*\\\\())))","patterns":[{"include":"#type"}]},"interface-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(?:(abstract)\\\\s+)?\\\\b(interface)\\\\b(?=\\\\s+|/[*/])","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.tsx"},"4":{"name":"storage.type.interface.tsx"}},"end":"(?<=})","name":"meta.interface.tsx","patterns":[{"include":"#comment"},{"include":"#class-or-interface-heritage"},{"captures":{"0":{"name":"entity.name.type.interface.tsx"}},"match":"[$_[:alpha:]][$_[:alnum:]]*"},{"include":"#type-parameters"},{"include":"#class-or-interface-body"}]},"jsdoctype":{"patterns":[{"begin":"\\\\G(\\\\{)","beginCaptures":{"0":{"name":"entity.name.type.instance.jsdoc"},"1":{"name":"punctuation.definition.bracket.curly.begin.jsdoc"}},"contentName":"entity.name.type.instance.jsdoc","end":"((}))\\\\s*|(?=\\\\*/)","endCaptures":{"1":{"name":"entity.name.type.instance.jsdoc"},"2":{"name":"punctuation.definition.bracket.curly.end.jsdoc"}},"patterns":[{"include":"#brackets"}]}]},"jsx":{"patterns":[{"include":"#jsx-tag-without-attributes-in-expression"},{"include":"#jsx-tag-in-expression"}]},"jsx-children":{"patterns":[{"include":"#jsx-tag-without-attributes"},{"include":"#jsx-tag"},{"include":"#jsx-evaluated-code"},{"include":"#jsx-entities"}]},"jsx-entities":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.entity.tsx"},"3":{"name":"punctuation.definition.entity.tsx"}},"match":"(&)([0-9A-Za-z]+|#[0-9]+|#x\\\\h+)(;)","name":"constant.character.entity.tsx"}]},"jsx-evaluated-code":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.embedded.begin.tsx"}},"contentName":"meta.embedded.expression.tsx","end":"}","endCaptures":{"0":{"name":"punctuation.section.embedded.end.tsx"}},"patterns":[{"include":"#expression"}]},"jsx-string-double-quoted":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.tsx"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.tsx"}},"name":"string.quoted.double.tsx","patterns":[{"include":"#jsx-entities"}]},"jsx-string-single-quoted":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.tsx"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.tsx"}},"name":"string.quoted.single.tsx","patterns":[{"include":"#jsx-entities"}]},"jsx-tag":{"begin":"(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))(?=((<\\\\s*)|(\\\\s+))(?!\\\\?)|/?>))","end":"(/>)|(</)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))?\\\\s*(>)","endCaptures":{"1":{"name":"punctuation.definition.tag.end.tsx"},"2":{"name":"punctuation.definition.tag.begin.tsx"},"3":{"name":"entity.name.tag.namespace.tsx"},"4":{"name":"punctuation.separator.namespace.tsx"},"5":{"name":"entity.name.tag.tsx"},"6":{"name":"support.class.component.tsx"},"7":{"name":"punctuation.definition.tag.end.tsx"}},"name":"meta.tag.tsx","patterns":[{"begin":"(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))(?=((<\\\\s*)|(\\\\s+))(?!\\\\?)|/?>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.tsx"},"2":{"name":"entity.name.tag.namespace.tsx"},"3":{"name":"punctuation.separator.namespace.tsx"},"4":{"name":"entity.name.tag.tsx"},"5":{"name":"support.class.component.tsx"}},"end":"(?=/?>)","patterns":[{"include":"#comment"},{"include":"#type-arguments"},{"include":"#jsx-tag-attributes"}]},{"begin":"(>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.end.tsx"}},"contentName":"meta.jsx.children.tsx","end":"(?=</)","patterns":[{"include":"#jsx-children"}]}]},"jsx-tag-attribute-assignment":{"match":"=(?=\\\\s*(?:[\\"'{]|/\\\\*|//|\\\\n))","name":"keyword.operator.assignment.tsx"},"jsx-tag-attribute-name":{"captures":{"1":{"name":"entity.other.attribute-name.namespace.tsx"},"2":{"name":"punctuation.separator.namespace.tsx"},"3":{"name":"entity.other.attribute-name.tsx"}},"match":"\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(:))?([$_[:alpha:]][-$_[:alnum:]]*)(?=[=\\\\s]|/?>|/\\\\*|//)"},"jsx-tag-attributes":{"begin":"\\\\s+","end":"(?=/?>)","name":"meta.tag.attributes.tsx","patterns":[{"include":"#comment"},{"include":"#jsx-tag-attribute-name"},{"include":"#jsx-tag-attribute-assignment"},{"include":"#jsx-string-double-quoted"},{"include":"#jsx-string-single-quoted"},{"include":"#jsx-evaluated-code"},{"include":"#jsx-tag-attributes-illegal"}]},"jsx-tag-attributes-illegal":{"match":"\\\\S+","name":"invalid.illegal.attribute.tsx"},"jsx-tag-in-expression":{"begin":"(?<!\\\\+\\\\+|--)(?<=[(*,:=>?\\\\[{]|&&|\\\\|\\\\||\\\\?|\\\\*/|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^default|[^$._[:alnum:]]default|^yield|[^$._[:alnum:]]yield|^)\\\\s*(?!<\\\\s*[$_[:alpha:]][$_[:alnum:]]*((\\\\s+extends\\\\s+[^=>])|,))(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))(?=((<\\\\s*)|(\\\\s+))(?!\\\\?)|/?>))","end":"(?!(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))(?=((<\\\\s*)|(\\\\s+))(?!\\\\?)|/?>))","patterns":[{"include":"#jsx-tag"}]},"jsx-tag-without-attributes":{"begin":"(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))?\\\\s*(>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.begin.tsx"},"2":{"name":"entity.name.tag.namespace.tsx"},"3":{"name":"punctuation.separator.namespace.tsx"},"4":{"name":"entity.name.tag.tsx"},"5":{"name":"support.class.component.tsx"},"6":{"name":"punctuation.definition.tag.end.tsx"}},"contentName":"meta.jsx.children.tsx","end":"(</)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))?\\\\s*(>)","endCaptures":{"1":{"name":"punctuation.definition.tag.begin.tsx"},"2":{"name":"entity.name.tag.namespace.tsx"},"3":{"name":"punctuation.separator.namespace.tsx"},"4":{"name":"entity.name.tag.tsx"},"5":{"name":"support.class.component.tsx"},"6":{"name":"punctuation.definition.tag.end.tsx"}},"name":"meta.tag.without-attributes.tsx","patterns":[{"include":"#jsx-children"}]},"jsx-tag-without-attributes-in-expression":{"begin":"(?<!\\\\+\\\\+|--)(?<=[(*,:=>?\\\\[{]|&&|\\\\|\\\\||\\\\?|\\\\*/|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^default|[^$._[:alnum:]]default|^yield|[^$._[:alnum:]]yield|^)\\\\s*(?=(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))?\\\\s*(>))","end":"(?!(<)\\\\s*(?:([$_[:alpha:]][-$._[:alnum:]]*)(?<![-.])(:))?((?:[a-z][0-9a-z]*|([$_[:alpha:]][-$._[:alnum:]]*))(?<![-.]))?\\\\s*(>))","patterns":[{"include":"#jsx-tag-without-attributes"}]},"label":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)(?=\\\\s*\\\\{)","beginCaptures":{"1":{"name":"entity.name.label.tsx"},"2":{"name":"punctuation.separator.label.tsx"}},"end":"(?<=})","patterns":[{"include":"#decl-block"}]},{"captures":{"1":{"name":"entity.name.label.tsx"},"2":{"name":"punctuation.separator.label.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)"}]},"literal":{"patterns":[{"include":"#numeric-literal"},{"include":"#boolean-literal"},{"include":"#null-literal"},{"include":"#undefined-literal"},{"include":"#numericConstant-literal"},{"include":"#array-literal"},{"include":"#this-literal"},{"include":"#super-literal"}]},"method-declaration":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(override)\\\\s+)?(?:\\\\b(p(?:ublic|rivate|rotected))\\\\s+)?(?:\\\\b(abstract)\\\\s+)?(?:\\\\b(async)\\\\s+)?\\\\s*\\\\b(constructor)\\\\b(?!:)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.tsx"},"4":{"name":"storage.modifier.async.tsx"},"5":{"name":"storage.type.tsx"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.tsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(override)\\\\s+)?(?:\\\\b(p(?:ublic|rivate|rotected))\\\\s+)?(?:\\\\b(abstract)\\\\s+)?(?:\\\\b(async)\\\\s+)?(?:\\\\s*\\\\b(new)\\\\b(?!:)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))|(?:(\\\\*)\\\\s*)?)(?=\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.tsx"},"4":{"name":"storage.modifier.async.tsx"},"5":{"name":"keyword.operator.new.tsx"},"6":{"name":"keyword.generator.asterisk.tsx"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.tsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(override)\\\\s+)?(?:\\\\b(p(?:ublic|rivate|rotected))\\\\s+)?(?:\\\\b(abstract)\\\\s+)?(?:\\\\b(async)\\\\s+)?(?:\\\\b([gs]et)\\\\s+)?(?:(\\\\*)\\\\s*)?(?=\\\\s*((\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??))\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.modifier.tsx"},"4":{"name":"storage.modifier.async.tsx"},"5":{"name":"storage.type.property.tsx"},"6":{"name":"keyword.generator.asterisk.tsx"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.tsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]}]},"method-declaration-name":{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??)\\\\s*[(<])","end":"(?=[(<])","patterns":[{"include":"#string"},{"include":"#array-literal"},{"include":"#numeric-literal"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.method.tsx entity.name.function.tsx"},{"match":"\\\\?","name":"keyword.operator.optional.tsx"}]},"namespace-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(namespace|module)\\\\s+(?=[\\"$'_\`[:alpha:]])","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.namespace.tsx"}},"end":"(?<=})|(?=;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.namespace.declaration.tsx","patterns":[{"include":"#comment"},{"include":"#string"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.type.module.tsx"},{"include":"#punctuation-accessor"},{"include":"#decl-block"}]},"new-expr":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(new)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.operator.new.tsx"}},"end":"(?<=\\\\))|(?=[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))new(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))function((\\\\s+[$_[:alpha:]][$_[:alnum:]]*)|(\\\\s*\\\\())))","name":"new.expr.tsx","patterns":[{"include":"#expression"}]},"null-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))null(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.null.tsx"},"numeric-literal":{"patterns":[{"captures":{"1":{"name":"storage.type.numeric.bigint.tsx"}},"match":"\\\\b(?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$)","name":"constant.numeric.hex.tsx"},{"captures":{"1":{"name":"storage.type.numeric.bigint.tsx"}},"match":"\\\\b(?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$)","name":"constant.numeric.binary.tsx"},{"captures":{"1":{"name":"storage.type.numeric.bigint.tsx"}},"match":"\\\\b(?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$)","name":"constant.numeric.octal.tsx"},{"captures":{"0":{"name":"constant.numeric.decimal.tsx"},"1":{"name":"meta.delimiter.decimal.period.tsx"},"2":{"name":"storage.type.numeric.bigint.tsx"},"3":{"name":"meta.delimiter.decimal.period.tsx"},"4":{"name":"storage.type.numeric.bigint.tsx"},"5":{"name":"meta.delimiter.decimal.period.tsx"},"6":{"name":"storage.type.numeric.bigint.tsx"},"7":{"name":"storage.type.numeric.bigint.tsx"},"8":{"name":"meta.delimiter.decimal.period.tsx"},"9":{"name":"storage.type.numeric.bigint.tsx"},"10":{"name":"meta.delimiter.decimal.period.tsx"},"11":{"name":"storage.type.numeric.bigint.tsx"},"12":{"name":"meta.delimiter.decimal.period.tsx"},"13":{"name":"storage.type.numeric.bigint.tsx"},"14":{"name":"storage.type.numeric.bigint.tsx"}},"match":"(?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$)"}]},"numericConstant-literal":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))NaN(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.nan.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Infinity(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.infinity.tsx"}]},"object-binding-element":{"patterns":[{"include":"#comment"},{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(?=[,}])","patterns":[{"include":"#object-binding-element-propertyName"},{"include":"#binding-element"}]},{"include":"#object-binding-pattern"},{"include":"#destructuring-variable-rest"},{"include":"#variable-initializer"},{"include":"#punctuation-comma"}]},"object-binding-element-const":{"patterns":[{"include":"#comment"},{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(?=[,}])","patterns":[{"include":"#object-binding-element-propertyName"},{"include":"#binding-element-const"}]},{"include":"#object-binding-pattern-const"},{"include":"#destructuring-variable-rest-const"},{"include":"#variable-initializer"},{"include":"#punctuation-comma"}]},"object-binding-element-propertyName":{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(:)","endCaptures":{"0":{"name":"punctuation.destructuring.tsx"}},"patterns":[{"include":"#string"},{"include":"#array-literal"},{"include":"#numeric-literal"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"variable.object.property.tsx"}]},"object-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"patterns":[{"include":"#object-binding-element"}]},"object-binding-pattern-const":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"patterns":[{"include":"#object-binding-element-const"}]},"object-identifiers":{"patterns":[{"match":"([$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s*\\\\??\\\\.\\\\s*prototype\\\\b(?!\\\\$))","name":"support.class.tsx"},{"captures":{"1":{"name":"punctuation.accessor.tsx"},"2":{"name":"punctuation.accessor.optional.tsx"},"3":{"name":"variable.other.constant.object.property.tsx"},"4":{"name":"variable.other.object.property.tsx"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(?:(#?\\\\p{upper}[$_\\\\d[:upper:]]*)|(#?[$_[:alpha:]][$_[:alnum:]]*))(?=\\\\s*\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*)"},{"captures":{"1":{"name":"variable.other.constant.object.tsx"},"2":{"name":"variable.other.object.tsx"}},"match":"(?:(\\\\p{upper}[$_\\\\d[:upper:]]*)|([$_[:alpha:]][$_[:alnum:]]*))(?=\\\\s*\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*)"}]},"object-literal":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"name":"meta.objectliteral.tsx","patterns":[{"include":"#object-member"}]},"object-literal-method-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(async)\\\\s+)?(?:\\\\b([gs]et)\\\\s+)?(?:(\\\\*)\\\\s*)?(?=\\\\s*((\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??))\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"},"2":{"name":"storage.type.property.tsx"},"3":{"name":"keyword.generator.asterisk.tsx"}},"end":"(?=[,;}])|(?<=})","name":"meta.method.declaration.tsx","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(async)\\\\s+)?(?:\\\\b([gs]et)\\\\s+)?(?:(\\\\*)\\\\s*)?(?=\\\\s*((\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??))\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"},"2":{"name":"storage.type.property.tsx"},"3":{"name":"keyword.generator.asterisk.tsx"}},"end":"(?=[(<])","patterns":[{"include":"#method-declaration-name"}]}]},"object-member":{"patterns":[{"include":"#comment"},{"include":"#object-literal-method-declaration"},{"begin":"(?=\\\\[)","end":"(?=:)|((?<=])(?=\\\\s*[(<]))","name":"meta.object.member.tsx meta.object-literal.key.tsx","patterns":[{"include":"#comment"},{"include":"#array-literal"}]},{"begin":"(?=[\\"'\`])","end":"(?=:)|((?<=[\\"'\`])(?=((\\\\s*[(,<}])|(\\\\s+(as|satisifies)\\\\s+))))","name":"meta.object.member.tsx meta.object-literal.key.tsx","patterns":[{"include":"#comment"},{"include":"#string"}]},{"begin":"(?=\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$)))","end":"(?=:)|(?=\\\\s*([(,<}])|(\\\\s+as|satisifies\\\\s+))","name":"meta.object.member.tsx meta.object-literal.key.tsx","patterns":[{"include":"#comment"},{"include":"#numeric-literal"}]},{"begin":"(?<=[]\\"'\`])(?=\\\\s*[(<])","end":"(?=[,;}])|(?<=})","name":"meta.method.declaration.tsx","patterns":[{"include":"#function-body"}]},{"captures":{"0":{"name":"meta.object-literal.key.tsx"},"1":{"name":"constant.numeric.decimal.tsx"}},"match":"(?![$_[:alpha:]])(\\\\d+)\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object.member.tsx"},{"captures":{"0":{"name":"meta.object-literal.key.tsx"},"1":{"name":"entity.name.function.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:(\\\\s*/\\\\*([^*]|(\\\\*[^/]))*\\\\*/)*\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))","name":"meta.object.member.tsx"},{"captures":{"0":{"name":"meta.object-literal.key.tsx"}},"match":"[$_[:alpha:]][$_[:alnum:]]*\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object.member.tsx"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.tsx"}},"end":"(?=[,}])","name":"meta.object.member.tsx","patterns":[{"include":"#expression"}]},{"captures":{"1":{"name":"variable.other.readwrite.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=[,}]|$|//|/\\\\*)","name":"meta.object.member.tsx"},{"captures":{"1":{"name":"keyword.control.as.tsx"},"2":{"name":"storage.modifier.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as)\\\\s+(const)(?=\\\\s*([,}]|$))","name":"meta.object.member.tsx"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(as)|(satisfies))\\\\s+","beginCaptures":{"1":{"name":"keyword.control.as.tsx"},"2":{"name":"keyword.control.satisfies.tsx"}},"end":"(?=[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|^|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as|satisifies)\\\\s+))","name":"meta.object.member.tsx","patterns":[{"include":"#type"}]},{"begin":"(?=[$_[:alpha:]][$_[:alnum:]]*\\\\s*=)","end":"(?=[,}]|$|//|/\\\\*)","name":"meta.object.member.tsx","patterns":[{"include":"#expression"}]},{"begin":":","beginCaptures":{"0":{"name":"meta.object-literal.key.tsx punctuation.separator.key-value.tsx"}},"end":"(?=[,}])","name":"meta.object.member.tsx","patterns":[{"begin":"(?<=:)\\\\s*(async)?(?=\\\\s*(<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"},"2":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(?=<\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"}},"end":"(?<=>)","patterns":[{"include":"#type-parameters"}]},{"begin":"(?<=>)\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"include":"#possibly-arrow-return-type"},{"include":"#expression"}]},{"include":"#punctuation-comma"},{"include":"#decl-block"}]},"parameter-array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.tsx"}},"patterns":[{"include":"#parameter-binding-element"},{"include":"#punctuation-comma"}]},"parameter-binding-element":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#parameter-object-binding-pattern"},{"include":"#parameter-array-binding-pattern"},{"include":"#destructuring-parameter-rest"},{"include":"#variable-initializer"}]},"parameter-name":{"patterns":[{"captures":{"1":{"name":"storage.modifier.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|protected|private|readonly)\\\\s+(?=(override|public|protected|private|readonly)\\\\s+)"},{"captures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.operator.rest.tsx"},"3":{"name":"entity.name.function.tsx variable.language.this.tsx"},"4":{"name":"entity.name.function.tsx"},"5":{"name":"keyword.operator.optional.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.operator.rest.tsx"},"3":{"name":"variable.parameter.tsx variable.language.this.tsx"},"4":{"name":"variable.parameter.tsx"},"5":{"name":"keyword.operator.optional.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)"}]},"parameter-object-binding-element":{"patterns":[{"include":"#comment"},{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(?=[,}])","patterns":[{"include":"#object-binding-element-propertyName"},{"include":"#parameter-binding-element"},{"include":"#paren-expression"}]},{"include":"#parameter-object-binding-pattern"},{"include":"#destructuring-parameter-rest"},{"include":"#variable-initializer"},{"include":"#punctuation-comma"}]},"parameter-object-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.tsx"},"2":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.tsx"}},"patterns":[{"include":"#parameter-object-binding-element"}]},"parameter-type-annotation":{"patterns":[{"begin":"(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.tsx"}},"end":"(?=[),])|(?==[^>])","name":"meta.type.annotation.tsx","patterns":[{"include":"#type"}]}]},"paren-expression":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#expression"}]},"paren-expression-possibly-arrow":{"patterns":[{"begin":"(?<=[(,=])\\\\s*(async)?(?=\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"begin":"(?<=[(,=]|=>|^return|[^$._[:alnum:]]return)\\\\s*(async)?(?=\\\\s*((((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()|(<)|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)))\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.tsx"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"include":"#possibly-arrow-return-type"}]},"paren-expression-possibly-arrow-with-typeparameters":{"patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},"possibly-arrow-return-type":{"begin":"(?<=\\\\)|^)\\\\s*(:)(?=\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*=>)","beginCaptures":{"1":{"name":"meta.arrow.tsx meta.return.type.arrow.tsx keyword.operator.type.annotation.tsx"}},"contentName":"meta.arrow.tsx meta.return.type.arrow.tsx","end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","patterns":[{"include":"#arrow-return-type-body"}]},"property-accessor":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(accessor|get|set)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.type.property.tsx"},"punctuation-accessor":{"captures":{"1":{"name":"punctuation.accessor.tsx"},"2":{"name":"punctuation.accessor.optional.tsx"}},"match":"(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d))"},"punctuation-comma":{"match":",","name":"punctuation.separator.comma.tsx"},"punctuation-semicolon":{"match":";","name":"punctuation.terminator.statement.tsx"},"qstring-double":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.tsx"}},"end":"(\\")|([^\\\\n\\\\\\\\])$","endCaptures":{"1":{"name":"punctuation.definition.string.end.tsx"},"2":{"name":"invalid.illegal.newline.tsx"}},"name":"string.quoted.double.tsx","patterns":[{"include":"#string-character-escape"}]},"qstring-single":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.tsx"}},"end":"(')|([^\\\\n\\\\\\\\])$","endCaptures":{"1":{"name":"punctuation.definition.string.end.tsx"},"2":{"name":"invalid.illegal.newline.tsx"}},"name":"string.quoted.single.tsx","patterns":[{"include":"#string-character-escape"}]},"regex":{"patterns":[{"begin":"(?<!\\\\+\\\\+|--|})(?<=[!(+,:=?\\\\[]|^return|[^$._[:alnum:]]return|^case|[^$._[:alnum:]]case|=>|&&|\\\\|\\\\||\\\\*/)\\\\s*(/)(?![*/])(?=(?:[^()/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)+]|\\\\(([^)\\\\\\\\]|\\\\\\\\.)+\\\\))+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.tsx"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.tsx"},"2":{"name":"keyword.other.tsx"}},"name":"string.regexp.tsx","patterns":[{"include":"#regexp"}]},{"begin":"((?<![]$)_[:alnum:]]|\\\\+\\\\+|--|}|\\\\*/)|((?<=^return|[^$._[:alnum:]]return|^case|[^$._[:alnum:]]case))\\\\s*)/(?![*/])(?=(?:[^/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)*])+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.tsx"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.tsx"},"2":{"name":"keyword.other.tsx"}},"name":"string.regexp.tsx","patterns":[{"include":"#regexp"}]}]},"regex-character-class":{"patterns":[{"match":"\\\\\\\\[DSWdfnrstvw]|\\\\.","name":"constant.other.character-class.regexp"},{"match":"\\\\\\\\([0-7]{3}|x\\\\h{2}|u\\\\h{4})","name":"constant.character.numeric.regexp"},{"match":"\\\\\\\\c[A-Z]","name":"constant.character.control.regexp"},{"match":"\\\\\\\\.","name":"constant.character.escape.backslash.regexp"}]},"regexp":{"patterns":[{"match":"\\\\\\\\[Bb]|[$^]","name":"keyword.control.anchor.regexp"},{"captures":{"0":{"name":"keyword.other.back-reference.regexp"},"1":{"name":"variable.other.regexp"}},"match":"\\\\\\\\(?:[1-9]\\\\d*|k<([$A-Z_a-z][$\\\\w]*)>)"},{"match":"[*+?]|\\\\{(\\\\d+,\\\\d+|\\\\d+,|,\\\\d+|\\\\d+)}\\\\??","name":"keyword.operator.quantifier.regexp"},{"match":"\\\\|","name":"keyword.operator.or.regexp"},{"begin":"(\\\\()((\\\\?=)|(\\\\?!)|(\\\\?<=)|(\\\\?<!))","beginCaptures":{"1":{"name":"punctuation.definition.group.regexp"},"2":{"name":"punctuation.definition.group.assertion.regexp"},"3":{"name":"meta.assertion.look-ahead.regexp"},"4":{"name":"meta.assertion.negative-look-ahead.regexp"},"5":{"name":"meta.assertion.look-behind.regexp"},"6":{"name":"meta.assertion.negative-look-behind.regexp"}},"end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.group.regexp"}},"name":"meta.group.assertion.regexp","patterns":[{"include":"#regexp"}]},{"begin":"\\\\((?:(\\\\?:)|\\\\?<([$A-Z_a-z][$\\\\w]*)>)?","beginCaptures":{"0":{"name":"punctuation.definition.group.regexp"},"1":{"name":"punctuation.definition.group.no-capture.regexp"},"2":{"name":"variable.other.regexp"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.group.regexp"}},"name":"meta.group.regexp","patterns":[{"include":"#regexp"}]},{"begin":"(\\\\[)(\\\\^)?","beginCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"},"2":{"name":"keyword.operator.negation.regexp"}},"end":"(])","endCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"}},"name":"constant.other.character-class.set.regexp","patterns":[{"captures":{"1":{"name":"constant.character.numeric.regexp"},"2":{"name":"constant.character.control.regexp"},"3":{"name":"constant.character.escape.backslash.regexp"},"4":{"name":"constant.character.numeric.regexp"},"5":{"name":"constant.character.control.regexp"},"6":{"name":"constant.character.escape.backslash.regexp"}},"match":"(?:.|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))-(?:[^]\\\\\\\\]|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))","name":"constant.other.character-class.range.regexp"},{"include":"#regex-character-class"}]},{"include":"#regex-character-class"}]},"return-type":{"patterns":[{"begin":"(?<=\\\\))\\\\s*(:)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.tsx"}},"end":"(?<![\\\\&:|])(?=$|^|[,;{}]|//)","name":"meta.return.type.tsx","patterns":[{"include":"#return-type-core"}]},{"begin":"(?<=\\\\))\\\\s*(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.tsx"}},"end":"(?<![\\\\&:|])((?=[,;{}]|//|^\\\\s*$)|((?<=\\\\S)(?=\\\\s*$)))","name":"meta.return.type.tsx","patterns":[{"include":"#return-type-core"}]}]},"return-type-core":{"patterns":[{"include":"#comment"},{"begin":"(?<=[\\\\&:|])(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"shebang":{"captures":{"1":{"name":"punctuation.definition.comment.tsx"}},"match":"\\\\A(#!).*(?=$)","name":"comment.line.shebang.tsx"},"single-line-comment-consuming-line-ending":{"begin":"(^[\\\\t ]+)?((//)(?:\\\\s*((@)internal)(?=\\\\s|$))?)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.tsx"},"2":{"name":"comment.line.double-slash.tsx"},"3":{"name":"punctuation.definition.comment.tsx"},"4":{"name":"storage.type.internaldeclaration.tsx"},"5":{"name":"punctuation.decorator.internaldeclaration.tsx"}},"contentName":"comment.line.double-slash.tsx","end":"(?=^)"},"statements":{"patterns":[{"include":"#declaration"},{"include":"#control-statement"},{"include":"#after-operator-block-as-object-literal"},{"include":"#decl-block"},{"include":"#label"},{"include":"#expression"},{"include":"#punctuation-semicolon"},{"include":"#string"},{"include":"#comment"}]},"string":{"patterns":[{"include":"#qstring-single"},{"include":"#qstring-double"},{"include":"#template"}]},"string-character-escape":{"match":"\\\\\\\\(x\\\\h{2}|u\\\\h{4}|u\\\\{\\\\h+}|[012][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.|$)","name":"constant.character.escape.tsx"},"super-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))super\\\\b(?!\\\\$)","name":"variable.language.super.tsx"},"support-function-call-identifiers":{"patterns":[{"include":"#literal"},{"include":"#support-objects"},{"include":"#object-identifiers"},{"include":"#punctuation-accessor"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))import(?=\\\\s*\\\\(\\\\s*[\\"'\`])","name":"keyword.operator.expression.import.tsx"}]},"support-objects":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(arguments)\\\\b(?!\\\\$)","name":"variable.language.arguments.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(Promise)\\\\b(?!\\\\$)","name":"support.class.promise.tsx"},{"captures":{"1":{"name":"keyword.control.import.tsx"},"2":{"name":"punctuation.accessor.tsx"},"3":{"name":"punctuation.accessor.optional.tsx"},"4":{"name":"support.variable.property.importmeta.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(import)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(meta)\\\\b(?!\\\\$)"},{"captures":{"1":{"name":"keyword.operator.new.tsx"},"2":{"name":"punctuation.accessor.tsx"},"3":{"name":"punctuation.accessor.optional.tsx"},"4":{"name":"support.variable.property.target.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(new)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(target)\\\\b(?!\\\\$)"},{"captures":{"1":{"name":"punctuation.accessor.tsx"},"2":{"name":"punctuation.accessor.optional.tsx"},"3":{"name":"support.variable.property.tsx"},"4":{"name":"support.constant.tsx"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(?:(constructor|length|prototype|__proto__)\\\\b(?!\\\\$|\\\\s*(<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\()|(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\\\b(?!\\\\$))"},{"captures":{"1":{"name":"support.type.object.module.tsx"},"2":{"name":"support.type.object.module.tsx"},"3":{"name":"punctuation.accessor.tsx"},"4":{"name":"punctuation.accessor.optional.tsx"},"5":{"name":"support.type.object.module.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(exports)|(module)(?:(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))(exports|id|filename|loaded|parent|children))?)\\\\b(?!\\\\$)"}]},"switch-statement":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?=\\\\bswitch\\\\s*\\\\()","end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"name":"switch-statement.expr.tsx","patterns":[{"include":"#comment"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(switch)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"keyword.control.switch.tsx"},"2":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"name":"switch-expression.expr.tsx","patterns":[{"include":"#expression"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"(?=})","name":"switch-block.expr.tsx","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(case|default(?=:))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.control.switch.tsx"}},"end":"(?=:)","name":"case-clause.expr.tsx","patterns":[{"include":"#expression"}]},{"begin":"(:)\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"case-clause.expr.tsx punctuation.definition.section.case-statement.tsx"},"2":{"name":"meta.block.tsx punctuation.definition.block.tsx"}},"contentName":"meta.block.tsx","end":"}","endCaptures":{"0":{"name":"meta.block.tsx punctuation.definition.block.tsx"}},"patterns":[{"include":"#statements"}]},{"captures":{"0":{"name":"case-clause.expr.tsx punctuation.definition.section.case-statement.tsx"}},"match":"(:)"},{"include":"#statements"}]}]},"template":{"patterns":[{"include":"#template-call"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?(\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.tsx"},"2":{"name":"string.template.tsx punctuation.definition.string.template.begin.tsx"}},"contentName":"string.template.tsx","end":"\`","endCaptures":{"0":{"name":"string.template.tsx punctuation.definition.string.template.end.tsx"}},"patterns":[{"include":"#template-substitution-element"},{"include":"#string-character-escape"}]}]},"template-call":{"patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*\\\\s*\\\\??\\\\.\\\\s*)*|(\\\\??\\\\.\\\\s*)?)([$_[:alpha:]][$_[:alnum:]]*)(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\`)","end":"(?=\`)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*\\\\s*\\\\??\\\\.\\\\s*)*|(\\\\??\\\\.\\\\s*)?)([$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\`)","patterns":[{"include":"#support-function-call-identifiers"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.tagged-template.tsx"}]},{"include":"#type-arguments"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?\\\\s*(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.tsx"}},"end":"(?=\`)","patterns":[{"include":"#type-arguments"}]}]},"template-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.tsx"}},"contentName":"meta.embedded.line.tsx","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.tsx"}},"name":"meta.template.expression.tsx","patterns":[{"include":"#expression"}]},"template-type":{"patterns":[{"include":"#template-call"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?(\`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.tsx"},"2":{"name":"string.template.tsx punctuation.definition.string.template.begin.tsx"}},"contentName":"string.template.tsx","end":"\`","endCaptures":{"0":{"name":"string.template.tsx punctuation.definition.string.template.end.tsx"}},"patterns":[{"include":"#template-type-substitution-element"},{"include":"#string-character-escape"}]}]},"template-type-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.tsx"}},"contentName":"meta.embedded.line.tsx","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.tsx"}},"name":"meta.template.expression.tsx","patterns":[{"include":"#type"}]},"ternary-expression":{"begin":"(?!\\\\?\\\\.\\\\s*\\\\D)(\\\\?)(?!\\\\?)","beginCaptures":{"1":{"name":"keyword.operator.ternary.tsx"}},"end":"\\\\s*(:)","endCaptures":{"1":{"name":"keyword.operator.ternary.tsx"}},"patterns":[{"include":"#expression"}]},"this-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))this\\\\b(?!\\\\$)","name":"variable.language.this.tsx"},"type":{"patterns":[{"include":"#comment"},{"include":"#type-string"},{"include":"#numeric-literal"},{"include":"#type-primitive"},{"include":"#type-builtin-literals"},{"include":"#type-parameters"},{"include":"#type-tuple"},{"include":"#type-object"},{"include":"#type-operators"},{"include":"#type-conditional"},{"include":"#type-fn-type-parameters"},{"include":"#type-paren-or-function-parameters"},{"include":"#type-function-return-type"},{"captures":{"1":{"name":"storage.modifier.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(readonly)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*"},{"include":"#type-name"}]},"type-alias-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(type)\\\\b\\\\s+([$_[:alpha:]][$_[:alnum:]]*)\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.type.tsx"},"4":{"name":"entity.name.type.alias.tsx"}},"end":"(?=[;}]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.type.declaration.tsx","patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"begin":"(=)\\\\s*(intrinsic)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.operator.assignment.tsx"},"2":{"name":"keyword.control.intrinsic.tsx"}},"end":"(?=[;}]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type"}]},{"begin":"(=)\\\\s*","beginCaptures":{"1":{"name":"keyword.operator.assignment.tsx"}},"end":"(?=[;}]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type"}]}]},"type-annotation":{"patterns":[{"begin":"(:)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.tsx"}},"end":"(?<![\\\\&:|])(?!\\\\s*[\\\\&|]\\\\s+)((?=^|[]),;}]|//)|(?==[^>])|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.tsx","patterns":[{"include":"#type"}]},{"begin":"(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.tsx"}},"end":"(?<![\\\\&:|])((?=[]),;}]|//)|(?==[^>])|(?=^\\\\s*$)|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.tsx","patterns":[{"include":"#type"}]}]},"type-arguments":{"begin":"<","beginCaptures":{"0":{"name":"punctuation.definition.typeparameters.begin.tsx"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.typeparameters.end.tsx"}},"name":"meta.type.parameters.tsx","patterns":[{"include":"#type-arguments-body"}]},"type-arguments-body":{"patterns":[{"captures":{"0":{"name":"keyword.operator.type.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(_)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"include":"#type"},{"include":"#punctuation-comma"}]},"type-builtin-literals":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(this|true|false|undefined|null|object)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"support.type.builtin.tsx"},"type-conditional":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(extends)\\\\s+","beginCaptures":{"1":{"name":"storage.modifier.tsx"}},"end":"(?<=:)","patterns":[{"begin":"\\\\?","beginCaptures":{"0":{"name":"keyword.operator.ternary.tsx"}},"end":":","endCaptures":{"0":{"name":"keyword.operator.ternary.tsx"}},"patterns":[{"include":"#type"}]},{"include":"#type"}]}]},"type-fn-type-parameters":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(abstract)\\\\s+)?(new)\\\\b(?=\\\\s*<)","beginCaptures":{"1":{"name":"meta.type.constructor.tsx storage.modifier.tsx"},"2":{"name":"meta.type.constructor.tsx keyword.control.new.tsx"}},"end":"(?<=>)","patterns":[{"include":"#comment"},{"include":"#type-parameters"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(abstract)\\\\s+)?(new)\\\\b\\\\s*(?=\\\\()","beginCaptures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.control.new.tsx"}},"end":"(?<=\\\\))","name":"meta.type.constructor.tsx","patterns":[{"include":"#function-parameters"}]},{"begin":"((?=\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>))))))","end":"(?<=\\\\))","name":"meta.type.function.tsx","patterns":[{"include":"#function-parameters"}]}]},"type-function-return-type":{"patterns":[{"begin":"(=>)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"storage.type.function.arrow.tsx"}},"end":"(?<!=>)(?<![\\\\&|])(?=[]),:;=>?{}]|//|$)","name":"meta.type.function.return.tsx","patterns":[{"include":"#type-function-return-type-core"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.tsx"}},"end":"(?<!=>)(?<![\\\\&|])((?=[]),:;=>?{}]|//|^\\\\s*$)|((?<=\\\\S)(?=\\\\s*$)))","name":"meta.type.function.return.tsx","patterns":[{"include":"#type-function-return-type-core"}]}]},"type-function-return-type-core":{"patterns":[{"include":"#comment"},{"begin":"(?<==>)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"type-infer":{"patterns":[{"captures":{"1":{"name":"keyword.operator.expression.infer.tsx"},"2":{"name":"entity.name.type.tsx"},"3":{"name":"keyword.operator.expression.extends.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(infer)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?:\\\\s+(extends)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))?","name":"meta.type.infer.tsx"}]},"type-name":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(<)","captures":{"1":{"name":"entity.name.type.module.tsx"},"2":{"name":"punctuation.accessor.tsx"},"3":{"name":"punctuation.accessor.optional.tsx"},"4":{"name":"meta.type.parameters.tsx punctuation.definition.typeparameters.begin.tsx"}},"contentName":"meta.type.parameters.tsx","end":"(>)","endCaptures":{"1":{"name":"meta.type.parameters.tsx punctuation.definition.typeparameters.end.tsx"}},"patterns":[{"include":"#type-arguments-body"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(<)","beginCaptures":{"1":{"name":"entity.name.type.tsx"},"2":{"name":"meta.type.parameters.tsx punctuation.definition.typeparameters.begin.tsx"}},"contentName":"meta.type.parameters.tsx","end":"(>)","endCaptures":{"1":{"name":"meta.type.parameters.tsx punctuation.definition.typeparameters.end.tsx"}},"patterns":[{"include":"#type-arguments-body"}]},{"captures":{"1":{"name":"entity.name.type.module.tsx"},"2":{"name":"punctuation.accessor.tsx"},"3":{"name":"punctuation.accessor.optional.tsx"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"entity.name.type.tsx"}]},"type-object":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.tsx"}},"name":"meta.object.type.tsx","patterns":[{"include":"#comment"},{"include":"#method-declaration"},{"include":"#indexer-declaration"},{"include":"#indexer-mapped-type-declaration"},{"include":"#field-declaration"},{"include":"#type-annotation"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.tsx"}},"end":"(?=[,;}]|$)|(?<=})","patterns":[{"include":"#type"}]},{"include":"#punctuation-comma"},{"include":"#punctuation-semicolon"},{"include":"#type"}]},"type-operators":{"patterns":[{"include":"#typeof-operator"},{"include":"#type-infer"},{"begin":"([\\\\&|])(?=\\\\s*\\\\{)","beginCaptures":{"0":{"name":"keyword.operator.type.tsx"}},"end":"(?<=})","patterns":[{"include":"#type-object"}]},{"begin":"[\\\\&|]","beginCaptures":{"0":{"name":"keyword.operator.type.tsx"}},"end":"(?=\\\\S)"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))keyof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.keyof.tsx"},{"match":"([:?])","name":"keyword.operator.ternary.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))import(?=\\\\s*\\\\()","name":"keyword.operator.expression.import.tsx"}]},"type-parameters":{"begin":"(<)","beginCaptures":{"1":{"name":"punctuation.definition.typeparameters.begin.tsx"}},"end":"(>)","endCaptures":{"1":{"name":"punctuation.definition.typeparameters.end.tsx"}},"name":"meta.type.parameters.tsx","patterns":[{"include":"#comment"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(extends|in|out|const)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.tsx"},{"include":"#type"},{"include":"#punctuation-comma"},{"match":"(=)(?!>)","name":"keyword.operator.assignment.tsx"}]},"type-paren-or-function-parameters":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.tsx"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.tsx"}},"name":"meta.type.paren.cover.tsx","patterns":[{"captures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.operator.rest.tsx"},"3":{"name":"entity.name.function.tsx variable.language.this.tsx"},"4":{"name":"entity.name.function.tsx"},"5":{"name":"keyword.operator.optional.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))\\\\s*(\\\\??)(?=\\\\s*(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))))"},{"captures":{"1":{"name":"storage.modifier.tsx"},"2":{"name":"keyword.operator.rest.tsx"},"3":{"name":"variable.parameter.tsx variable.language.this.tsx"},"4":{"name":"variable.parameter.tsx"},"5":{"name":"keyword.operator.optional.tsx"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))\\\\s*(\\\\??)(?=:)"},{"include":"#type-annotation"},{"match":",","name":"punctuation.separator.parameter.tsx"},{"include":"#type"}]},"type-predicate-operator":{"patterns":[{"captures":{"1":{"name":"keyword.operator.type.asserts.tsx"},"2":{"name":"variable.parameter.tsx variable.language.this.tsx"},"3":{"name":"variable.parameter.tsx"},"4":{"name":"keyword.operator.expression.is.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(asserts)\\\\s+)?(?!asserts)(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))\\\\s(is)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"captures":{"1":{"name":"keyword.operator.type.asserts.tsx"},"2":{"name":"variable.parameter.tsx variable.language.this.tsx"},"3":{"name":"variable.parameter.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(asserts)\\\\s+(?!is)(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))asserts(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.type.asserts.tsx"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))is(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.is.tsx"}]},"type-primitive":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(string|number|bigint|boolean|symbol|any|void|never|unknown)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"support.type.primitive.tsx"},"type-string":{"patterns":[{"include":"#qstring-single"},{"include":"#qstring-double"},{"include":"#template-type"}]},"type-tuple":{"begin":"\\\\[","beginCaptures":{"0":{"name":"meta.brace.square.tsx"}},"end":"]","endCaptures":{"0":{"name":"meta.brace.square.tsx"}},"name":"meta.type.tuple.tsx","patterns":[{"match":"\\\\.\\\\.\\\\.","name":"keyword.operator.rest.tsx"},{"captures":{"1":{"name":"entity.name.label.tsx"},"2":{"name":"keyword.operator.optional.tsx"},"3":{"name":"punctuation.separator.label.tsx"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(\\\\?)?\\\\s*(:)"},{"include":"#type"},{"include":"#punctuation-comma"}]},"typeof-operator":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))typeof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"0":{"name":"keyword.operator.expression.typeof.tsx"}},"end":"(?=[]\\\\&),:;=>?{|}]|(extends\\\\s+)|$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type-arguments"},{"include":"#expression"}]},"undefined-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))undefined(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.undefined.tsx"},"var-expr":{"patterns":[{"begin":"(?=(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(var|let)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))","end":"(?!(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(var|let)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))((?=^|[;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|((?<!^let|[^$._[:alnum:]]let|^var|[^$._[:alnum:]]var)(?=\\\\s*$)))","name":"meta.var.expr.tsx","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(var|let)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.tsx"}},"end":"(?=\\\\S)"},{"include":"#destructuring-variable"},{"include":"#var-single-variable"},{"include":"#variable-initializer"},{"include":"#comment"},{"begin":"(,)\\\\s*(?=$|//)","beginCaptures":{"1":{"name":"punctuation.separator.comma.tsx"}},"end":"(?<!,)(((?=[;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|^\\\\s*$))|((?<=\\\\S)(?=\\\\s*$)))","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#destructuring-variable"},{"include":"#var-single-variable"},{"include":"#punctuation-comma"}]},{"include":"#punctuation-comma"}]},{"begin":"(?=(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(const(?!\\\\s+enum\\\\b))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.tsx"}},"end":"(?!(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(const(?!\\\\s+enum\\\\b))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))((?=^|[;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|((?<!(?:^|[^$._[:alnum:]])const)(?=\\\\s*$)))","name":"meta.var.expr.tsx","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(const(?!\\\\s+enum\\\\b))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.tsx"}},"end":"(?=\\\\S)"},{"include":"#destructuring-const"},{"include":"#var-single-const"},{"include":"#variable-initializer"},{"include":"#comment"},{"begin":"(,)\\\\s*(?=$|//)","beginCaptures":{"1":{"name":"punctuation.separator.comma.tsx"}},"end":"(?<!,)(((?=[;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|^\\\\s*$))|((?<=\\\\S)(?=\\\\s*$)))","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#destructuring-const"},{"include":"#var-single-const"},{"include":"#punctuation-comma"}]},{"include":"#punctuation-comma"}]},{"begin":"(?=(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b\\\\b(using(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])|await\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.tsx"}},"end":"(?!(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b\\\\b(using(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])|await\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))((?=[;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|((?<!(?:^|[^$._[:alnum:]]|^await\\\\s+|[^$._[:alnum:]]await\\\\s+)using)(?=\\\\s*$)))","name":"meta.var.expr.tsx","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b\\\\b(using(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])|await\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.tsx"},"2":{"name":"storage.modifier.tsx"},"3":{"name":"storage.type.tsx"}},"end":"(?=\\\\S)"},{"include":"#var-single-const"},{"include":"#variable-initializer"},{"include":"#comment"},{"begin":"(,)\\\\s*((?!\\\\S)|(?=//))","beginCaptures":{"1":{"name":"punctuation.separator.comma.tsx"}},"end":"(?<!,)(((?=[;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|^\\\\s*$))|((?<=\\\\S)(?=\\\\s*$)))","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#var-single-const"},{"include":"#punctuation-comma"}]},{"include":"#punctuation-comma"}]}]},"var-single-const":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.tsx variable.other.constant.tsx entity.name.function.tsx"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.tsx","patterns":[{"include":"#var-single-variable-type-annotation"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)","beginCaptures":{"1":{"name":"meta.definition.variable.tsx variable.other.constant.tsx"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.tsx","patterns":[{"include":"#var-single-variable-type-annotation"}]}]},"var-single-variable":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)(!)?(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|(\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|(<\\\\s*[$_[:alpha:]][$_[:alnum:]]*\\\\s+extends\\\\s*[^=>])|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"'()\`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|('([^'\\\\\\\\]|\\\\\\\\.)*')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(\`([^\\\\\\\\\`]|\\\\\\\\.)*\`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.tsx entity.name.function.tsx"},"2":{"name":"keyword.operator.definiteassignment.tsx"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.tsx","patterns":[{"include":"#var-single-variable-type-annotation"}]},{"begin":"(\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])(!)?","beginCaptures":{"1":{"name":"meta.definition.variable.tsx variable.other.constant.tsx"},"2":{"name":"keyword.operator.definiteassignment.tsx"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.tsx","patterns":[{"include":"#var-single-variable-type-annotation"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)(!)?","beginCaptures":{"1":{"name":"meta.definition.variable.tsx variable.other.readwrite.tsx"},"2":{"name":"keyword.operator.definiteassignment.tsx"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.tsx","patterns":[{"include":"#var-single-variable-type-annotation"}]}]},"var-single-variable-type-annotation":{"patterns":[{"include":"#type-annotation"},{"include":"#string"},{"include":"#comment"}]},"variable-initializer":{"patterns":[{"begin":"(?<![!=])(=)(?!=)(?=\\\\s*\\\\S)(?!\\\\s*.*=>\\\\s*$)","beginCaptures":{"1":{"name":"keyword.operator.assignment.tsx"}},"end":"(?=$|^|[]),;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","patterns":[{"include":"#expression"}]},{"begin":"(?<![!=])(=)(?!=)","beginCaptures":{"1":{"name":"keyword.operator.assignment.tsx"}},"end":"(?=[]),;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))|(?=^\\\\s*$)|(?<![-\\\\&*+/|])(?<=\\\\S)(?<!=)(?=\\\\s*$)","patterns":[{"include":"#expression"}]}]}},"scopeName":"source.tsx"}`))],nl=[Object.freeze(JSON.parse('{"displayName":"TypeScript","name":"typescript","patterns":[{"include":"#directives"},{"include":"#statements"},{"include":"#shebang"}],"repository":{"access-modifier":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(abstract|declare|override|public|protected|private|readonly|static)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.ts"},"after-operator-block-as-object-literal":{"begin":"(?<!\\\\+\\\\+|--)(?<=[!(+,:=>?\\\\[]|^await|[^$._[:alnum:]]await|^return|[^$._[:alnum:]]return|^yield|[^$._[:alnum:]]yield|^throw|[^$._[:alnum:]]throw|^in|[^$._[:alnum:]]in|^of|[^$._[:alnum:]]of|^typeof|[^$._[:alnum:]]typeof|&&|\\\\|\\\\||\\\\*)\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"name":"meta.objectliteral.ts","patterns":[{"include":"#object-member"}]},"array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.array.ts"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.ts"}},"patterns":[{"include":"#binding-element"},{"include":"#punctuation-comma"}]},"array-binding-pattern-const":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.array.ts"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.ts"}},"patterns":[{"include":"#binding-element-const"},{"include":"#punctuation-comma"}]},"array-literal":{"begin":"\\\\s*(\\\\[)","beginCaptures":{"1":{"name":"meta.brace.square.ts"}},"end":"]","endCaptures":{"0":{"name":"meta.brace.square.ts"}},"name":"meta.array.literal.ts","patterns":[{"include":"#expression"},{"include":"#punctuation-comma"}]},"arrow-function":{"patterns":[{"captures":{"1":{"name":"storage.modifier.async.ts"},"2":{"name":"variable.parameter.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))\\\\b(async)\\\\s+)?([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?==>)","name":"meta.arrow.ts"},{"begin":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))\\\\b(async))?((?<![]!)}])\\\\s*(?=((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))","beginCaptures":{"1":{"name":"storage.modifier.async.ts"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.arrow.ts","patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"include":"#function-parameters"},{"include":"#arrow-return-type"},{"include":"#possibly-arrow-return-type"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.ts"}},"end":"((?<=[}\\\\S])(?<!=>)|((?!\\\\{)(?=\\\\S)))(?!/[*/])","name":"meta.arrow.ts","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#decl-block"},{"include":"#expression"}]}]},"arrow-return-type":{"begin":"(?<=\\\\))\\\\s*(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.ts"}},"end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","name":"meta.return.type.arrow.ts","patterns":[{"include":"#arrow-return-type-body"}]},"arrow-return-type-body":{"patterns":[{"begin":"(?<=:)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"async-modifier":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(async)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.async.ts"},"binding-element":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#object-binding-pattern"},{"include":"#array-binding-pattern"},{"include":"#destructuring-variable-rest"},{"include":"#variable-initializer"}]},"binding-element-const":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#object-binding-pattern-const"},{"include":"#array-binding-pattern-const"},{"include":"#destructuring-variable-rest-const"},{"include":"#variable-initializer"}]},"boolean-literal":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))true(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.boolean.true.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))false(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.boolean.false.ts"}]},"brackets":{"patterns":[{"begin":"\\\\{","end":"}|(?=\\\\*/)","patterns":[{"include":"#brackets"}]},{"begin":"\\\\[","end":"]|(?=\\\\*/)","patterns":[{"include":"#brackets"}]}]},"cast":{"patterns":[{"captures":{"1":{"name":"meta.brace.angle.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"meta.brace.angle.ts"}},"match":"\\\\s*(<)\\\\s*(const)\\\\s*(>)","name":"cast.expr.ts"},{"begin":"(?<!\\\\+\\\\+|--)(?<=^return|[^$._[:alnum:]]return|^throw|[^$._[:alnum:]]throw|^yield|[^$._[:alnum:]]yield|^await|[^$._[:alnum:]]await|^default|[^$._[:alnum:]]default|[\\\\&(*,:=>?^|]|[^$_[:alnum:]](?:\\\\+\\\\+|--)|[^+]\\\\+|[^-]-)\\\\s*(<)(?!<?=)(?!\\\\s*$)","beginCaptures":{"1":{"name":"meta.brace.angle.ts"}},"end":"(>)","endCaptures":{"1":{"name":"meta.brace.angle.ts"}},"name":"cast.expr.ts","patterns":[{"include":"#type"}]},{"begin":"(?<=^)\\\\s*(<)(?=[$_[:alpha:]][$_[:alnum:]]*\\\\s*>)","beginCaptures":{"1":{"name":"meta.brace.angle.ts"}},"end":"(>)","endCaptures":{"1":{"name":"meta.brace.angle.ts"}},"name":"cast.expr.ts","patterns":[{"include":"#type"}]}]},"class-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(?:(abstract)\\\\s+)?\\\\b(class)\\\\b(?=\\\\s+|/[*/])","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.ts"},"4":{"name":"storage.type.class.ts"}},"end":"(?<=})","name":"meta.class.ts","patterns":[{"include":"#class-declaration-or-expression-patterns"}]},"class-declaration-or-expression-patterns":{"patterns":[{"include":"#comment"},{"include":"#class-or-interface-heritage"},{"captures":{"0":{"name":"entity.name.type.class.ts"}},"match":"[$_[:alpha:]][$_[:alnum:]]*"},{"include":"#type-parameters"},{"include":"#class-or-interface-body"}]},"class-expression":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(abstract)\\\\s+)?(class)\\\\b(?=\\\\s+|[<{]|/[*/])","beginCaptures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"storage.type.class.ts"}},"end":"(?<=})","name":"meta.class.ts","patterns":[{"include":"#class-declaration-or-expression-patterns"}]},"class-or-interface-body":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"patterns":[{"include":"#comment"},{"include":"#decorator"},{"begin":"(?<=:)\\\\s*","end":"(?=[-\\\\])+,:;}\\\\s]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#expression"}]},{"include":"#method-declaration"},{"include":"#indexer-declaration"},{"include":"#field-declaration"},{"include":"#string"},{"include":"#type-annotation"},{"include":"#variable-initializer"},{"include":"#access-modifier"},{"include":"#property-accessor"},{"include":"#async-modifier"},{"include":"#after-operator-block-as-object-literal"},{"include":"#decl-block"},{"include":"#expression"},{"include":"#punctuation-comma"},{"include":"#punctuation-semicolon"}]},"class-or-interface-heritage":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))\\\\b(extends|implements)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"storage.modifier.ts"}},"end":"(?=\\\\{)","patterns":[{"include":"#comment"},{"include":"#class-or-interface-heritage"},{"include":"#type-parameters"},{"include":"#expressionWithoutIdentifiers"},{"captures":{"1":{"name":"entity.name.type.module.ts"},"2":{"name":"punctuation.accessor.ts"},"3":{"name":"punctuation.accessor.optional.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))(?=\\\\s*[$_[:alpha:]][$_[:alnum:]]*(\\\\s*\\\\??\\\\.\\\\s*[$_[:alpha:]][$_[:alnum:]]*)*\\\\s*)"},{"captures":{"1":{"name":"entity.other.inherited-class.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)"},{"include":"#expressionPunctuations"}]},"comment":{"patterns":[{"begin":"/\\\\*\\\\*(?!/)","beginCaptures":{"0":{"name":"punctuation.definition.comment.ts"}},"end":"\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.ts"}},"name":"comment.block.documentation.ts","patterns":[{"include":"#docblock"}]},{"begin":"(/\\\\*)(?:\\\\s*((@)internal)(?=\\\\s|(\\\\*/)))?","beginCaptures":{"1":{"name":"punctuation.definition.comment.ts"},"2":{"name":"storage.type.internaldeclaration.ts"},"3":{"name":"punctuation.decorator.internaldeclaration.ts"}},"end":"\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.ts"}},"name":"comment.block.ts"},{"begin":"(^[\\\\t ]+)?((//)(?:\\\\s*((@)internal)(?=\\\\s|$))?)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.ts"},"2":{"name":"comment.line.double-slash.ts"},"3":{"name":"punctuation.definition.comment.ts"},"4":{"name":"storage.type.internaldeclaration.ts"},"5":{"name":"punctuation.decorator.internaldeclaration.ts"}},"contentName":"comment.line.double-slash.ts","end":"(?=$)"}]},"control-statement":{"patterns":[{"include":"#switch-statement"},{"include":"#for-loop"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(catch|finally|throw|try)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.trycatch.ts"},{"captures":{"1":{"name":"keyword.control.loop.ts"},"2":{"name":"entity.name.label.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(break|continue|goto)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(break|continue|do|goto|while)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.loop.ts"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(return)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"0":{"name":"keyword.control.flow.ts"}},"end":"(?=[;}]|$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#expression"}]},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(case|default|switch)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.switch.ts"},{"include":"#if-statement"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(else|if)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.conditional.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(with)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.with.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(package)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(debugger)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.other.debugger.ts"}]},"decl-block":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"name":"meta.block.ts","patterns":[{"include":"#statements"}]},"declaration":{"patterns":[{"include":"#decorator"},{"include":"#var-expr"},{"include":"#function-declaration"},{"include":"#class-declaration"},{"include":"#interface-declaration"},{"include":"#enum-declaration"},{"include":"#namespace-declaration"},{"include":"#type-alias-declaration"},{"include":"#import-equals-declaration"},{"include":"#import-declaration"},{"include":"#export-declaration"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(declare|export)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.ts"}]},"decorator":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))@","beginCaptures":{"0":{"name":"punctuation.decorator.ts"}},"end":"(?=\\\\s)","name":"meta.decorator.ts","patterns":[{"include":"#expression"}]},"destructuring-const":{"patterns":[{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\{)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.object-binding-pattern-variable.ts","patterns":[{"include":"#object-binding-pattern-const"},{"include":"#type-annotation"},{"include":"#comment"}]},{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\[)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.array-binding-pattern-variable.ts","patterns":[{"include":"#array-binding-pattern-const"},{"include":"#type-annotation"},{"include":"#comment"}]}]},"destructuring-parameter":{"patterns":[{"begin":"(?<![:=])\\\\s*(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.object.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.ts"}},"name":"meta.parameter.object-binding-pattern.ts","patterns":[{"include":"#parameter-object-binding-element"}]},{"begin":"(?<![:=])\\\\s*(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.array.ts"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.ts"}},"name":"meta.paramter.array-binding-pattern.ts","patterns":[{"include":"#parameter-binding-element"},{"include":"#punctuation-comma"}]}]},"destructuring-parameter-rest":{"captures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"variable.parameter.ts"}},"match":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)"},"destructuring-variable":{"patterns":[{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\{)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.object-binding-pattern-variable.ts","patterns":[{"include":"#object-binding-pattern"},{"include":"#type-annotation"},{"include":"#comment"}]},{"begin":"(?<![:=]|^of|[^$._[:alnum:]]of|^in|[^$._[:alnum:]]in)\\\\s*(?=\\\\[)","end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","name":"meta.array-binding-pattern-variable.ts","patterns":[{"include":"#array-binding-pattern"},{"include":"#type-annotation"},{"include":"#comment"}]}]},"destructuring-variable-rest":{"captures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"meta.definition.variable.ts variable.other.readwrite.ts"}},"match":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)"},"destructuring-variable-rest-const":{"captures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"meta.definition.variable.ts variable.other.constant.ts"}},"match":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)"},"directives":{"begin":"^(///)\\\\s*(?=<(reference|amd-dependency|amd-module)(\\\\s+(path|types|no-default-lib|lib|name|resolution-mode)\\\\s*=\\\\s*((\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)))+\\\\s*/>\\\\s*$)","beginCaptures":{"1":{"name":"punctuation.definition.comment.ts"}},"end":"(?=$)","name":"comment.line.triple-slash.directive.ts","patterns":[{"begin":"(<)(reference|amd-dependency|amd-module)","beginCaptures":{"1":{"name":"punctuation.definition.tag.directive.ts"},"2":{"name":"entity.name.tag.directive.ts"}},"end":"/>","endCaptures":{"0":{"name":"punctuation.definition.tag.directive.ts"}},"name":"meta.tag.ts","patterns":[{"match":"path|types|no-default-lib|lib|name|resolution-mode","name":"entity.other.attribute-name.directive.ts"},{"match":"=","name":"keyword.operator.assignment.ts"},{"include":"#string"}]}]},"docblock":{"patterns":[{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.access-type.jsdoc"}},"match":"((@)a(?:ccess|pi))\\\\s+(p(?:rivate|rotected|ublic))\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"5":{"name":"constant.other.email.link.underline.jsdoc"},"6":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"match":"((@)author)\\\\s+([^*/<>@\\\\s](?:[^*/<>@]|\\\\*[^/])*)(?:\\\\s*(<)([^>\\\\s]+)(>))?"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"},"4":{"name":"keyword.operator.control.jsdoc"},"5":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)borrows)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)\\\\s+(as)\\\\s+((?:[^*/@\\\\s]|\\\\*[^/])+)"},{"begin":"((@)example)\\\\s+","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=@|\\\\*/)","name":"meta.example.jsdoc","patterns":[{"match":"^\\\\s\\\\*\\\\s+"},{"begin":"\\\\G(<)caption(>)","beginCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}},"contentName":"constant.other.description.jsdoc","end":"(</)caption(>)|(?=\\\\*/)","endCaptures":{"0":{"name":"entity.name.tag.inline.jsdoc"},"1":{"name":"punctuation.definition.bracket.angle.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.angle.end.jsdoc"}}},{"captures":{"0":{"name":"source.embedded.ts"}},"match":"[^*@\\\\s](?:[^*]|\\\\*[^/])*"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"constant.language.symbol-type.jsdoc"}},"match":"((@)kind)\\\\s+(class|constant|event|external|file|function|member|mixin|module|namespace|typedef)\\\\b"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.link.underline.jsdoc"},"4":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)see)\\\\s+(?:((?=https?://)(?:[^*\\\\s]|\\\\*[^/])+)|((?!https?://|(?:\\\\[[^]\\\\[]*])?\\\\{@(?:link|linkcode|linkplain|tutorial)\\\\b)(?:[^*/@\\\\s]|\\\\*[^/])+))"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)template)\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*(?:\\\\s*,\\\\s*[$A-Z_a-z][]$.\\\\[\\\\w]*)*)"},{"begin":"((@)template)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:arg|argument|const|constant|member|namespace|param|var))\\\\s+([$A-Z_a-z][]$.\\\\[\\\\w]*)"},{"begin":"((@)typedef)\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"(?:[^*/@\\\\s]|\\\\*[^/])+","name":"entity.name.type.instance.jsdoc"}]},{"begin":"((@)(?:arg|argument|const|constant|member|namespace|param|prop|property|var))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"},{"match":"([$A-Z_a-z][]$.\\\\[\\\\w]*)","name":"variable.other.jsdoc"},{"captures":{"1":{"name":"punctuation.definition.optional-value.begin.bracket.square.jsdoc"},"2":{"name":"keyword.operator.assignment.jsdoc"},"3":{"name":"source.embedded.ts"},"4":{"name":"punctuation.definition.optional-value.end.bracket.square.jsdoc"},"5":{"name":"invalid.illegal.syntax.jsdoc"}},"match":"(\\\\[)\\\\s*[$\\\\w]+(?:(?:\\\\[])?\\\\.[$\\\\w]+)*(?:\\\\s*(=)\\\\s*((?>\\"(?:\\\\*(?!/)|\\\\\\\\(?!\\")|[^*\\\\\\\\])*?\\"|\'(?:\\\\*(?!/)|\\\\\\\\(?!\')|[^*\\\\\\\\])*?\'|\\\\[(?:\\\\*(?!/)|[^*])*?]|(?:\\\\*(?!/)|\\\\s(?!\\\\s*])|\\\\[.*?(?:]|(?=\\\\*/))|[^]*\\\\[\\\\s])*)*))?\\\\s*(?:(])((?:[^*\\\\s]|\\\\*[^/\\\\s])+)?|(?=\\\\*/))","name":"variable.other.jsdoc"}]},{"begin":"((@)(?:define|enum|exception|export|extends|lends|implements|modifies|namespace|private|protected|returns?|satisfies|suppress|this|throws|type|yields?))\\\\s+(?=\\\\{)","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"end":"(?=\\\\s|\\\\*/|[^]$A-\\\\[_a-{}])","patterns":[{"include":"#jsdoctype"}]},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"entity.name.type.instance.jsdoc"}},"match":"((@)(?:alias|augments|callback|constructs|emits|event|fires|exports?|extends|external|function|func|host|lends|listens|interface|memberof!?|method|module|mixes|mixin|name|requires|see|this|typedef|uses))\\\\s+((?:[^*@{}\\\\s]|\\\\*[^/])+)"},{"begin":"((@)(?:default(?:value)?|license|version))\\\\s+(([\\"\']))","beginCaptures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"},"4":{"name":"punctuation.definition.string.begin.jsdoc"}},"contentName":"variable.other.jsdoc","end":"(\\\\3)|(?=$|\\\\*/)","endCaptures":{"0":{"name":"variable.other.jsdoc"},"1":{"name":"punctuation.definition.string.end.jsdoc"}}},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"},"3":{"name":"variable.other.jsdoc"}},"match":"((@)(?:default(?:value)?|license|tutorial|variation|version))\\\\s+([^*\\\\s]+)"},{"captures":{"1":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"(@)(?:abstract|access|alias|api|arg|argument|async|attribute|augments|author|beta|borrows|bubbles|callback|chainable|class|classdesc|code|config|const|constant|constructor|constructs|copyright|default|defaultvalue|define|deprecated|desc|description|dict|emits|enum|event|example|exception|exports?|extends|extension(?:_?for)?|external|externs|file|fileoverview|final|fires|for|func|function|generator|global|hideconstructor|host|ignore|implements|implicitCast|inherit[Dd]oc|inner|instance|interface|internal|kind|lends|license|listens|main|member|memberof!?|method|mixes|mixins?|modifies|module|name|namespace|noalias|nocollapse|nocompile|nosideeffects|override|overview|package|param|polymer(?:Behavior)?|preserve|private|prop|property|protected|public|read[Oo]nly|record|require[ds]|returns?|see|since|static|struct|submodule|summary|suppress|template|this|throws|todo|tutorial|type|typedef|unrestricted|uses|var|variation|version|virtual|writeOnce|yields?)\\\\b","name":"storage.type.class.jsdoc"},{"include":"#inline-tags"},{"captures":{"1":{"name":"storage.type.class.jsdoc"},"2":{"name":"punctuation.definition.block.tag.jsdoc"}},"match":"((@)[$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s+)"}]},"enum-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?(?:\\\\b(const)\\\\s+)?\\\\b(enum)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.ts"},"4":{"name":"storage.type.enum.ts"},"5":{"name":"entity.name.type.enum.ts"}},"end":"(?<=})","name":"meta.enum.declaration.ts","patterns":[{"include":"#comment"},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"patterns":[{"include":"#comment"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)","beginCaptures":{"0":{"name":"variable.other.enummember.ts"}},"end":"(?=[,}]|$)","patterns":[{"include":"#comment"},{"include":"#variable-initializer"}]},{"begin":"(?=((\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+])))","end":"(?=[,}]|$)","patterns":[{"include":"#string"},{"include":"#array-literal"},{"include":"#comment"},{"include":"#variable-initializer"}]},{"include":"#punctuation-comma"}]}]},"export-declaration":{"patterns":[{"captures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"keyword.control.as.ts"},"3":{"name":"storage.type.namespace.ts"},"4":{"name":"entity.name.type.module.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(export)\\\\s+(as)\\\\s+(namespace)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(export)(?:\\\\s+(type))?(?:\\\\s*(=)|\\\\s+(default)(?=\\\\s+))","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"keyword.control.type.ts"},"3":{"name":"keyword.operator.assignment.ts"},"4":{"name":"keyword.control.default.ts"}},"end":"(?=$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.export.default.ts","patterns":[{"include":"#interface-declaration"},{"include":"#expression"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(export)(?:\\\\s+(type))?\\\\b(?!(\\\\$)|(\\\\s*:))((?=\\\\s*[*{])|((?=\\\\s*[$_[:alpha:]][$_[:alnum:]]*([,\\\\s]))(?!\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)))","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"keyword.control.type.ts"}},"end":"(?=$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.export.ts","patterns":[{"include":"#import-export-declaration"}]}]},"expression":{"patterns":[{"include":"#expressionWithoutIdentifiers"},{"include":"#identifiers"},{"include":"#expressionPunctuations"}]},"expression-inside-possibly-arrow-parens":{"patterns":[{"include":"#expressionWithoutIdentifiers"},{"include":"#comment"},{"include":"#string"},{"include":"#decorator"},{"include":"#destructuring-parameter"},{"captures":{"1":{"name":"storage.modifier.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|protected|private|readonly)\\\\s+(?=(override|public|protected|private|readonly)\\\\s+)"},{"captures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.operator.rest.ts"},"3":{"name":"entity.name.function.ts variable.language.this.ts"},"4":{"name":"entity.name.function.ts"},"5":{"name":"keyword.operator.optional.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.operator.rest.ts"},"3":{"name":"variable.parameter.ts variable.language.this.ts"},"4":{"name":"variable.parameter.ts"},"5":{"name":"keyword.operator.optional.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)(?=\\\\s*[,:]|$)"},{"include":"#type-annotation"},{"include":"#variable-initializer"},{"match":",","name":"punctuation.separator.parameter.ts"},{"include":"#identifiers"},{"include":"#expressionPunctuations"}]},"expression-operators":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(await)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.control.flow.ts"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(yield)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?=\\\\s*/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*\\\\*)","beginCaptures":{"1":{"name":"keyword.control.flow.ts"}},"end":"\\\\*","endCaptures":{"0":{"name":"keyword.generator.asterisk.ts"}},"patterns":[{"include":"#comment"}]},{"captures":{"1":{"name":"keyword.control.flow.ts"},"2":{"name":"keyword.generator.asterisk.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(yield)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?:\\\\s*(\\\\*))?"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))delete(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.delete.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))in(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?!\\\\()","name":"keyword.operator.expression.in.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))of(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?!\\\\()","name":"keyword.operator.expression.of.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))instanceof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.instanceof.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))new(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.new.ts"},{"include":"#typeof-operator"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))void(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.void.ts"},{"captures":{"1":{"name":"keyword.control.as.ts"},"2":{"name":"storage.modifier.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as)\\\\s+(const)(?=\\\\s*($|[]),:;}]))"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(as)|(satisfies))\\\\s+","beginCaptures":{"1":{"name":"keyword.control.as.ts"},"2":{"name":"keyword.control.satisfies.ts"}},"end":"(?=^|[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as|satisfies)\\\\s+)|(\\\\s+<))","patterns":[{"include":"#type"}]},{"match":"\\\\.\\\\.\\\\.","name":"keyword.operator.spread.ts"},{"match":"(?:\\\\*|(?<!\\\\()/|[-%+])=","name":"keyword.operator.assignment.compound.ts"},{"match":"(?:[\\\\&^]|<<|>>>??|\\\\|)=","name":"keyword.operator.assignment.compound.bitwise.ts"},{"match":"<<|>>>?","name":"keyword.operator.bitwise.shift.ts"},{"match":"[!=]==?","name":"keyword.operator.comparison.ts"},{"match":"<=|>=|<>|[<>]","name":"keyword.operator.relational.ts"},{"captures":{"1":{"name":"keyword.operator.logical.ts"},"2":{"name":"keyword.operator.assignment.compound.ts"},"3":{"name":"keyword.operator.arithmetic.ts"}},"match":"(?<=[$_[:alnum:]])(!)\\\\s*(?:(/=)|(/)(?![*/]))"},{"match":"!|&&|\\\\|\\\\||\\\\?\\\\?","name":"keyword.operator.logical.ts"},{"match":"[\\\\&^|~]","name":"keyword.operator.bitwise.ts"},{"match":"=","name":"keyword.operator.assignment.ts"},{"match":"--","name":"keyword.operator.decrement.ts"},{"match":"\\\\+\\\\+","name":"keyword.operator.increment.ts"},{"match":"[-%*+/]","name":"keyword.operator.arithmetic.ts"},{"begin":"(?<=[]$)_[:alnum:]])\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)+(?:(/=)|(/)(?![*/])))","end":"(/=)|(/)(?!\\\\*([^*]|(\\\\*[^/]))*\\\\*/)","endCaptures":{"1":{"name":"keyword.operator.assignment.compound.ts"},"2":{"name":"keyword.operator.arithmetic.ts"}},"patterns":[{"include":"#comment"}]},{"captures":{"1":{"name":"keyword.operator.assignment.compound.ts"},"2":{"name":"keyword.operator.arithmetic.ts"}},"match":"(?<=[]$)_[:alnum:]])\\\\s*(?:(/=)|(/)(?![*/]))"}]},"expressionPunctuations":{"patterns":[{"include":"#punctuation-comma"},{"include":"#punctuation-accessor"}]},"expressionWithoutIdentifiers":{"patterns":[{"include":"#string"},{"include":"#regex"},{"include":"#comment"},{"include":"#function-expression"},{"include":"#class-expression"},{"include":"#arrow-function"},{"include":"#paren-expression-possibly-arrow"},{"include":"#cast"},{"include":"#ternary-expression"},{"include":"#new-expr"},{"include":"#instanceof-expr"},{"include":"#object-literal"},{"include":"#expression-operators"},{"include":"#function-call"},{"include":"#literal"},{"include":"#support-objects"},{"include":"#paren-expression"}]},"field-declaration":{"begin":"(?<!\\\\()(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(readonly)\\\\s+)?(?=\\\\s*(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|(#?[$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(?:(?:(\\\\?)|(!))\\\\s*)?([,:;=}]|$))","beginCaptures":{"1":{"name":"storage.modifier.ts"}},"end":"(?=[,;}]|$|^((?!\\\\s*(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|(#?[$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(?:(?:(\\\\?)|(!))\\\\s*)?([,:;=]|$))))|(?<=})","name":"meta.field.declaration.ts","patterns":[{"include":"#variable-initializer"},{"include":"#type-annotation"},{"include":"#string"},{"include":"#array-literal"},{"include":"#numeric-literal"},{"include":"#comment"},{"captures":{"1":{"name":"meta.definition.property.ts entity.name.function.ts"},"2":{"name":"keyword.operator.optional.ts"},"3":{"name":"keyword.operator.definiteassignment.ts"}},"match":"(#?[$_[:alpha:]][$_[:alnum:]]*)(?:(\\\\?)|(!))?(?=\\\\s*\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"match":"#?[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.property.ts variable.object.property.ts"},{"match":"\\\\?","name":"keyword.operator.optional.ts"},{"match":"!","name":"keyword.operator.definiteassignment.ts"}]},"for-loop":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))for(?=((\\\\s+|(\\\\s*/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*))await)?\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)?(\\\\())","beginCaptures":{"0":{"name":"keyword.control.loop.ts"}},"end":"(?<=\\\\))","patterns":[{"include":"#comment"},{"match":"await","name":"keyword.control.loop.ts"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#var-expr"},{"include":"#expression"},{"include":"#punctuation-semicolon"}]}]},"function-body":{"patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"include":"#function-parameters"},{"include":"#return-type"},{"include":"#type-function-return-type"},{"include":"#decl-block"},{"match":"\\\\*","name":"keyword.generator.asterisk.ts"}]},"function-call":{"patterns":[{"begin":"(?=(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\\\\())","end":"(?<=\\\\))(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\\\\())","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=\\\\s*(?:(\\\\?\\\\.\\\\s*)|(!))?((<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?\\\\())","name":"meta.function-call.ts","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"},{"include":"#paren-expression"}]},{"begin":"(?=(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","end":"(?<=>)(?!(((([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))|(?<=\\\\)))(<\\\\s*[(\\\\[{]\\\\s*)$)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*)(\\\\s*\\\\??\\\\.\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*))*)|(\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*[(\\\\[{]\\\\s*)$)","name":"meta.function-call.ts","patterns":[{"include":"#function-call-target"}]},{"include":"#comment"},{"include":"#function-call-optionals"},{"include":"#type-arguments"}]}]},"function-call-optionals":{"patterns":[{"match":"\\\\?\\\\.","name":"meta.function-call.ts punctuation.accessor.optional.ts"},{"match":"!","name":"meta.function-call.ts keyword.operator.definiteassignment.ts"}]},"function-call-target":{"patterns":[{"include":"#support-function-call-identifiers"},{"match":"(#?[$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.ts"}]},"function-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?(?:(async)\\\\s+)?(function)\\\\b(?:\\\\s*(\\\\*))?(?:(?:\\\\s+|(?<=\\\\*))([$_[:alpha:]][$_[:alnum:]]*))?\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.async.ts"},"4":{"name":"storage.type.function.ts"},"5":{"name":"keyword.generator.asterisk.ts"},"6":{"name":"meta.definition.function.ts entity.name.function.ts"}},"end":"(?=;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|(?<=})","name":"meta.function.ts","patterns":[{"include":"#function-name"},{"include":"#function-body"}]},"function-expression":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(async)\\\\s+)?(function)\\\\b(?:\\\\s*(\\\\*))?(?:(?:\\\\s+|(?<=\\\\*))([$_[:alpha:]][$_[:alnum:]]*))?\\\\s*","beginCaptures":{"1":{"name":"storage.modifier.async.ts"},"2":{"name":"storage.type.function.ts"},"3":{"name":"keyword.generator.asterisk.ts"},"4":{"name":"meta.definition.function.ts entity.name.function.ts"}},"end":"(?=;)|(?<=})","name":"meta.function.expression.ts","patterns":[{"include":"#function-name"},{"include":"#single-line-comment-consuming-line-ending"},{"include":"#function-body"}]},"function-name":{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.function.ts entity.name.function.ts"},"function-parameters":{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.definition.parameters.begin.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.ts"}},"name":"meta.parameters.ts","patterns":[{"include":"#function-parameters-body"}]},"function-parameters-body":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#decorator"},{"include":"#destructuring-parameter"},{"include":"#parameter-name"},{"include":"#parameter-type-annotation"},{"include":"#variable-initializer"},{"match":",","name":"punctuation.separator.parameter.ts"}]},"identifiers":{"patterns":[{"include":"#object-identifiers"},{"captures":{"1":{"name":"punctuation.accessor.ts"},"2":{"name":"punctuation.accessor.optional.ts"},"3":{"name":"entity.name.function.ts"}},"match":"(?:(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*)?([$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s*=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))"},{"captures":{"1":{"name":"punctuation.accessor.ts"},"2":{"name":"punctuation.accessor.optional.ts"},"3":{"name":"variable.other.constant.property.ts"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])"},{"captures":{"1":{"name":"punctuation.accessor.ts"},"2":{"name":"punctuation.accessor.optional.ts"},"3":{"name":"variable.other.property.ts"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(#?[$_[:alpha:]][$_[:alnum:]]*)"},{"match":"(\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])","name":"variable.other.constant.ts"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"variable.other.readwrite.ts"}]},"if-statement":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?=\\\\bif\\\\s*(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))\\\\s*(?!\\\\{))","end":"(?=;|$|})","patterns":[{"include":"#comment"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(if)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"keyword.control.conditional.ts"},"2":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#expression"}]},{"begin":"(?<=\\\\))\\\\s*/(?![*/])(?=(?:[^/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)*])+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.ts"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.ts"},"2":{"name":"keyword.other.ts"}},"name":"string.regexp.ts","patterns":[{"include":"#regexp"}]},{"include":"#statements"}]}]},"import-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(import)(?:\\\\s+(type)(?!\\\\s+from))?(?!\\\\s*[(:])(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"keyword.control.import.ts"},"4":{"name":"keyword.control.type.ts"}},"end":"(?<!(?:^|[^$._[:alnum:]])import)(?=;|$|^)","name":"meta.import.ts","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#string"},{"begin":"(?<=(?:^|[^$._[:alnum:]])import)(?!\\\\s*[\\"\'])","end":"\\\\bfrom\\\\b","endCaptures":{"0":{"name":"keyword.control.from.ts"}},"patterns":[{"include":"#import-export-declaration"}]},{"include":"#import-export-declaration"}]},"import-equals-declaration":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(import)(?:\\\\s+(type))?\\\\s+([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(=)\\\\s*(require)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"keyword.control.import.ts"},"4":{"name":"keyword.control.type.ts"},"5":{"name":"variable.other.readwrite.alias.ts"},"6":{"name":"keyword.operator.assignment.ts"},"7":{"name":"keyword.control.require.ts"},"8":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"name":"meta.import-equals.external.ts","patterns":[{"include":"#comment"},{"include":"#string"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(import)(?:\\\\s+(type))?\\\\s+([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(=)\\\\s*(?!require\\\\b)","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"keyword.control.import.ts"},"4":{"name":"keyword.control.type.ts"},"5":{"name":"variable.other.readwrite.alias.ts"},"6":{"name":"keyword.operator.assignment.ts"}},"end":"(?=;|$|^)","name":"meta.import-equals.internal.ts","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"captures":{"1":{"name":"entity.name.type.module.ts"},"2":{"name":"punctuation.accessor.ts"},"3":{"name":"punctuation.accessor.optional.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"variable.other.readwrite.ts"}]}]},"import-export-assert-clause":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(with)|(assert))\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"keyword.control.with.ts"},"2":{"name":"keyword.control.assert.ts"},"3":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"patterns":[{"include":"#comment"},{"include":"#string"},{"match":"[$_[:alpha:]][$_[:alnum:]]*\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object-literal.key.ts"},{"match":":","name":"punctuation.separator.key-value.ts"}]},"import-export-block":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"name":"meta.block.ts","patterns":[{"include":"#import-export-clause"}]},"import-export-clause":{"patterns":[{"include":"#comment"},{"captures":{"1":{"name":"keyword.control.type.ts"},"2":{"name":"keyword.control.default.ts"},"3":{"name":"constant.language.import-export-all.ts"},"4":{"name":"variable.other.readwrite.ts"},"5":{"name":"string.quoted.alias.ts"},"12":{"name":"keyword.control.as.ts"},"13":{"name":"keyword.control.default.ts"},"14":{"name":"variable.other.readwrite.alias.ts"},"15":{"name":"string.quoted.alias.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(type)\\\\s+)?(?:\\\\b(default)|(\\\\*)|\\\\b([$_[:alpha:]][$_[:alnum:]]*)|((\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)))\\\\s+(as)\\\\s+(?:(default(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|([$_[:alpha:]][$_[:alnum:]]*)|((\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)))"},{"include":"#punctuation-comma"},{"match":"\\\\*","name":"constant.language.import-export-all.ts"},{"match":"\\\\b(default)\\\\b","name":"keyword.control.default.ts"},{"captures":{"1":{"name":"keyword.control.type.ts"},"2":{"name":"variable.other.readwrite.alias.ts"},"3":{"name":"string.quoted.alias.ts"}},"match":"(?:\\\\b(type)\\\\s+)?(?:([$_[:alpha:]][$_[:alnum:]]*)|((\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)))"}]},"import-export-declaration":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#import-export-block"},{"match":"\\\\bfrom\\\\b","name":"keyword.control.from.ts"},{"include":"#import-export-assert-clause"},{"include":"#import-export-clause"}]},"indexer-declaration":{"begin":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(readonly)\\\\s*)?\\\\s*(\\\\[)\\\\s*([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=:)","beginCaptures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"meta.brace.square.ts"},"3":{"name":"variable.parameter.ts"}},"end":"(])\\\\s*(\\\\?\\\\s*)?|$","endCaptures":{"1":{"name":"meta.brace.square.ts"},"2":{"name":"keyword.operator.optional.ts"}},"name":"meta.indexer.declaration.ts","patterns":[{"include":"#type-annotation"}]},"indexer-mapped-type-declaration":{"begin":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))([-+])?(readonly)\\\\s*)?\\\\s*(\\\\[)\\\\s*([$_[:alpha:]][$_[:alnum:]]*)\\\\s+(in)\\\\s+","beginCaptures":{"1":{"name":"keyword.operator.type.modifier.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"meta.brace.square.ts"},"4":{"name":"entity.name.type.ts"},"5":{"name":"keyword.operator.expression.in.ts"}},"end":"(])([-+])?\\\\s*(\\\\?\\\\s*)?|$","endCaptures":{"1":{"name":"meta.brace.square.ts"},"2":{"name":"keyword.operator.type.modifier.ts"},"3":{"name":"keyword.operator.optional.ts"}},"name":"meta.indexer.mappedtype.declaration.ts","patterns":[{"captures":{"1":{"name":"keyword.control.as.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as)\\\\s+"},{"include":"#type"}]},"inline-tags":{"patterns":[{"captures":{"1":{"name":"punctuation.definition.bracket.square.begin.jsdoc"},"2":{"name":"punctuation.definition.bracket.square.end.jsdoc"}},"match":"(\\\\[)[^]]+(])(?=\\\\{@(?:link|linkcode|linkplain|tutorial))","name":"constant.other.description.jsdoc"},{"begin":"(\\\\{)((@)(?:link(?:code|plain)?|tutorial))\\\\s*","beginCaptures":{"1":{"name":"punctuation.definition.bracket.curly.begin.jsdoc"},"2":{"name":"storage.type.class.jsdoc"},"3":{"name":"punctuation.definition.inline.tag.jsdoc"}},"end":"}|(?=\\\\*/)","endCaptures":{"0":{"name":"punctuation.definition.bracket.curly.end.jsdoc"}},"name":"entity.name.type.instance.jsdoc","patterns":[{"captures":{"1":{"name":"variable.other.link.underline.jsdoc"},"2":{"name":"punctuation.separator.pipe.jsdoc"}},"match":"\\\\G((?=https?://)(?:[^*|}\\\\s]|\\\\*/)+)(\\\\|)?"},{"captures":{"1":{"name":"variable.other.description.jsdoc"},"2":{"name":"punctuation.separator.pipe.jsdoc"}},"match":"\\\\G((?:[^*@{|}\\\\s]|\\\\*[^/])+)(\\\\|)?"}]}]},"instanceof-expr":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(instanceof)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.operator.expression.instanceof.ts"}},"end":"(?<=\\\\))|(?=[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|([!=]==?)|(([\\\\&^|~]\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s+instanceof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))function((\\\\s+[$_[:alpha:]][$_[:alnum:]]*)|(\\\\s*\\\\())))","patterns":[{"include":"#type"}]},"interface-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(?:(abstract)\\\\s+)?\\\\b(interface)\\\\b(?=\\\\s+|/[*/])","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.ts"},"4":{"name":"storage.type.interface.ts"}},"end":"(?<=})","name":"meta.interface.ts","patterns":[{"include":"#comment"},{"include":"#class-or-interface-heritage"},{"captures":{"0":{"name":"entity.name.type.interface.ts"}},"match":"[$_[:alpha:]][$_[:alnum:]]*"},{"include":"#type-parameters"},{"include":"#class-or-interface-body"}]},"jsdoctype":{"patterns":[{"begin":"\\\\G(\\\\{)","beginCaptures":{"0":{"name":"entity.name.type.instance.jsdoc"},"1":{"name":"punctuation.definition.bracket.curly.begin.jsdoc"}},"contentName":"entity.name.type.instance.jsdoc","end":"((}))\\\\s*|(?=\\\\*/)","endCaptures":{"1":{"name":"entity.name.type.instance.jsdoc"},"2":{"name":"punctuation.definition.bracket.curly.end.jsdoc"}},"patterns":[{"include":"#brackets"}]}]},"label":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)(?=\\\\s*\\\\{)","beginCaptures":{"1":{"name":"entity.name.label.ts"},"2":{"name":"punctuation.separator.label.ts"}},"end":"(?<=})","patterns":[{"include":"#decl-block"}]},{"captures":{"1":{"name":"entity.name.label.ts"},"2":{"name":"punctuation.separator.label.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(:)"}]},"literal":{"patterns":[{"include":"#numeric-literal"},{"include":"#boolean-literal"},{"include":"#null-literal"},{"include":"#undefined-literal"},{"include":"#numericConstant-literal"},{"include":"#array-literal"},{"include":"#this-literal"},{"include":"#super-literal"}]},"method-declaration":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(override)\\\\s+)?(?:\\\\b(p(?:ublic|rivate|rotected))\\\\s+)?(?:\\\\b(abstract)\\\\s+)?(?:\\\\b(async)\\\\s+)?\\\\s*\\\\b(constructor)\\\\b(?!:)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.ts"},"4":{"name":"storage.modifier.async.ts"},"5":{"name":"storage.type.ts"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.ts","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(override)\\\\s+)?(?:\\\\b(p(?:ublic|rivate|rotected))\\\\s+)?(?:\\\\b(abstract)\\\\s+)?(?:\\\\b(async)\\\\s+)?(?:\\\\s*\\\\b(new)\\\\b(?!:)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))|(?:(\\\\*)\\\\s*)?)(?=\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.ts"},"4":{"name":"storage.modifier.async.ts"},"5":{"name":"keyword.operator.new.ts"},"6":{"name":"keyword.generator.asterisk.ts"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.ts","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(override)\\\\s+)?(?:\\\\b(p(?:ublic|rivate|rotected))\\\\s+)?(?:\\\\b(abstract)\\\\s+)?(?:\\\\b(async)\\\\s+)?(?:\\\\b([gs]et)\\\\s+)?(?:(\\\\*)\\\\s*)?(?=\\\\s*((\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??))\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.modifier.ts"},"4":{"name":"storage.modifier.async.ts"},"5":{"name":"storage.type.property.ts"},"6":{"name":"keyword.generator.asterisk.ts"}},"end":"(?=[,;}]|$)|(?<=})","name":"meta.method.declaration.ts","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"}]}]},"method-declaration-name":{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??)\\\\s*[(<])","end":"(?=[(<])","patterns":[{"include":"#string"},{"include":"#array-literal"},{"include":"#numeric-literal"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"meta.definition.method.ts entity.name.function.ts"},{"match":"\\\\?","name":"keyword.operator.optional.ts"}]},"namespace-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(namespace|module)\\\\s+(?=[\\"$\'_`[:alpha:]])","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.namespace.ts"}},"end":"(?<=})|(?=;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.namespace.declaration.ts","patterns":[{"include":"#comment"},{"include":"#string"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.type.module.ts"},{"include":"#punctuation-accessor"},{"include":"#decl-block"}]},"new-expr":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(new)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.operator.new.ts"}},"end":"(?<=\\\\))|(?=[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))new(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))function((\\\\s+[$_[:alpha:]][$_[:alnum:]]*)|(\\\\s*\\\\())))","name":"new.expr.ts","patterns":[{"include":"#expression"}]},"null-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))null(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.null.ts"},"numeric-literal":{"patterns":[{"captures":{"1":{"name":"storage.type.numeric.bigint.ts"}},"match":"\\\\b(?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$)","name":"constant.numeric.hex.ts"},{"captures":{"1":{"name":"storage.type.numeric.bigint.ts"}},"match":"\\\\b(?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$)","name":"constant.numeric.binary.ts"},{"captures":{"1":{"name":"storage.type.numeric.bigint.ts"}},"match":"\\\\b(?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$)","name":"constant.numeric.octal.ts"},{"captures":{"0":{"name":"constant.numeric.decimal.ts"},"1":{"name":"meta.delimiter.decimal.period.ts"},"2":{"name":"storage.type.numeric.bigint.ts"},"3":{"name":"meta.delimiter.decimal.period.ts"},"4":{"name":"storage.type.numeric.bigint.ts"},"5":{"name":"meta.delimiter.decimal.period.ts"},"6":{"name":"storage.type.numeric.bigint.ts"},"7":{"name":"storage.type.numeric.bigint.ts"},"8":{"name":"meta.delimiter.decimal.period.ts"},"9":{"name":"storage.type.numeric.bigint.ts"},"10":{"name":"meta.delimiter.decimal.period.ts"},"11":{"name":"storage.type.numeric.bigint.ts"},"12":{"name":"meta.delimiter.decimal.period.ts"},"13":{"name":"storage.type.numeric.bigint.ts"},"14":{"name":"storage.type.numeric.bigint.ts"}},"match":"(?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$)"}]},"numericConstant-literal":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))NaN(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.nan.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Infinity(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.infinity.ts"}]},"object-binding-element":{"patterns":[{"include":"#comment"},{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(?=[,}])","patterns":[{"include":"#object-binding-element-propertyName"},{"include":"#binding-element"}]},{"include":"#object-binding-pattern"},{"include":"#destructuring-variable-rest"},{"include":"#variable-initializer"},{"include":"#punctuation-comma"}]},"object-binding-element-const":{"patterns":[{"include":"#comment"},{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(?=[,}])","patterns":[{"include":"#object-binding-element-propertyName"},{"include":"#binding-element-const"}]},{"include":"#object-binding-pattern-const"},{"include":"#destructuring-variable-rest-const"},{"include":"#variable-initializer"},{"include":"#punctuation-comma"}]},"object-binding-element-propertyName":{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(:)","endCaptures":{"0":{"name":"punctuation.destructuring.ts"}},"patterns":[{"include":"#string"},{"include":"#array-literal"},{"include":"#numeric-literal"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"variable.object.property.ts"}]},"object-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.object.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.ts"}},"patterns":[{"include":"#object-binding-element"}]},"object-binding-pattern-const":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.object.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.ts"}},"patterns":[{"include":"#object-binding-element-const"}]},"object-identifiers":{"patterns":[{"match":"([$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s*\\\\??\\\\.\\\\s*prototype\\\\b(?!\\\\$))","name":"support.class.ts"},{"captures":{"1":{"name":"punctuation.accessor.ts"},"2":{"name":"punctuation.accessor.optional.ts"},"3":{"name":"variable.other.constant.object.property.ts"},"4":{"name":"variable.other.object.property.ts"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(?:(#?\\\\p{upper}[$_\\\\d[:upper:]]*)|(#?[$_[:alpha:]][$_[:alnum:]]*))(?=\\\\s*\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*)"},{"captures":{"1":{"name":"variable.other.constant.object.ts"},"2":{"name":"variable.other.object.ts"}},"match":"(?:(\\\\p{upper}[$_\\\\d[:upper:]]*)|([$_[:alpha:]][$_[:alnum:]]*))(?=\\\\s*\\\\??\\\\.\\\\s*#?[$_[:alpha:]][$_[:alnum:]]*)"}]},"object-literal":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"name":"meta.objectliteral.ts","patterns":[{"include":"#object-member"}]},"object-literal-method-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(async)\\\\s+)?(?:\\\\b([gs]et)\\\\s+)?(?:(\\\\*)\\\\s*)?(?=\\\\s*((\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??))\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.ts"},"2":{"name":"storage.type.property.ts"},"3":{"name":"keyword.generator.asterisk.ts"}},"end":"(?=[,;}])|(?<=})","name":"meta.method.declaration.ts","patterns":[{"include":"#method-declaration-name"},{"include":"#function-body"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(async)\\\\s+)?(?:\\\\b([gs]et)\\\\s+)?(?:(\\\\*)\\\\s*)?(?=\\\\s*((\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(\\\\??))\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()","beginCaptures":{"1":{"name":"storage.modifier.async.ts"},"2":{"name":"storage.type.property.ts"},"3":{"name":"keyword.generator.asterisk.ts"}},"end":"(?=[(<])","patterns":[{"include":"#method-declaration-name"}]}]},"object-member":{"patterns":[{"include":"#comment"},{"include":"#object-literal-method-declaration"},{"begin":"(?=\\\\[)","end":"(?=:)|((?<=])(?=\\\\s*[(<]))","name":"meta.object.member.ts meta.object-literal.key.ts","patterns":[{"include":"#comment"},{"include":"#array-literal"}]},{"begin":"(?=[\\"\'`])","end":"(?=:)|((?<=[\\"\'`])(?=((\\\\s*[(,<}])|(\\\\s+(as|satisifies)\\\\s+))))","name":"meta.object.member.ts meta.object-literal.key.ts","patterns":[{"include":"#comment"},{"include":"#string"}]},{"begin":"(?=\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$)))","end":"(?=:)|(?=\\\\s*([(,<}])|(\\\\s+as|satisifies\\\\s+))","name":"meta.object.member.ts meta.object-literal.key.ts","patterns":[{"include":"#comment"},{"include":"#numeric-literal"}]},{"begin":"(?<=[]\\"\'`])(?=\\\\s*[(<])","end":"(?=[,;}])|(?<=})","name":"meta.method.declaration.ts","patterns":[{"include":"#function-body"}]},{"captures":{"0":{"name":"meta.object-literal.key.ts"},"1":{"name":"constant.numeric.decimal.ts"}},"match":"(?![$_[:alpha:]])(\\\\d+)\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object.member.ts"},{"captures":{"0":{"name":"meta.object-literal.key.ts"},"1":{"name":"entity.name.function.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:(\\\\s*/\\\\*([^*]|(\\\\*[^/]))*\\\\*/)*\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))","name":"meta.object.member.ts"},{"captures":{"0":{"name":"meta.object-literal.key.ts"}},"match":"[$_[:alpha:]][$_[:alnum:]]*\\\\s*(?=(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*:)","name":"meta.object.member.ts"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.ts"}},"end":"(?=[,}])","name":"meta.object.member.ts","patterns":[{"include":"#expression"}]},{"captures":{"1":{"name":"variable.other.readwrite.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?=[,}]|$|//|/\\\\*)","name":"meta.object.member.ts"},{"captures":{"1":{"name":"keyword.control.as.ts"},"2":{"name":"storage.modifier.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as)\\\\s+(const)(?=\\\\s*([,}]|$))","name":"meta.object.member.ts"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(as)|(satisfies))\\\\s+","beginCaptures":{"1":{"name":"keyword.control.as.ts"},"2":{"name":"keyword.control.satisfies.ts"}},"end":"(?=[-\\\\])+,:;>?}]|\\\\|\\\\||&&|!==|$|^|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(as|satisifies)\\\\s+))","name":"meta.object.member.ts","patterns":[{"include":"#type"}]},{"begin":"(?=[$_[:alpha:]][$_[:alnum:]]*\\\\s*=)","end":"(?=[,}]|$|//|/\\\\*)","name":"meta.object.member.ts","patterns":[{"include":"#expression"}]},{"begin":":","beginCaptures":{"0":{"name":"meta.object-literal.key.ts punctuation.separator.key-value.ts"}},"end":"(?=[,}])","name":"meta.object.member.ts","patterns":[{"begin":"(?<=:)\\\\s*(async)?(?=\\\\s*(<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.ts"}},"end":"(?<=\\\\))","patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.ts"},"2":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"begin":"(?<=:)\\\\s*(async)?\\\\s*(?=<\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.ts"}},"end":"(?<=>)","patterns":[{"include":"#type-parameters"}]},{"begin":"(?<=>)\\\\s*(\\\\()(?=\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]},{"include":"#possibly-arrow-return-type"},{"include":"#expression"}]},{"include":"#punctuation-comma"},{"include":"#decl-block"}]},"parameter-array-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\[)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.array.ts"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.array.ts"}},"patterns":[{"include":"#parameter-binding-element"},{"include":"#punctuation-comma"}]},"parameter-binding-element":{"patterns":[{"include":"#comment"},{"include":"#string"},{"include":"#numeric-literal"},{"include":"#regex"},{"include":"#parameter-object-binding-pattern"},{"include":"#parameter-array-binding-pattern"},{"include":"#destructuring-parameter-rest"},{"include":"#variable-initializer"}]},"parameter-name":{"patterns":[{"captures":{"1":{"name":"storage.modifier.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|protected|private|readonly)\\\\s+(?=(override|public|protected|private|readonly)\\\\s+)"},{"captures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.operator.rest.ts"},"3":{"name":"entity.name.function.ts variable.language.this.ts"},"4":{"name":"entity.name.function.ts"},"5":{"name":"keyword.operator.optional.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))"},{"captures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.operator.rest.ts"},"3":{"name":"variable.parameter.ts variable.language.this.ts"},"4":{"name":"variable.parameter.ts"},"5":{"name":"keyword.operator.optional.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(override|public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*(\\\\??)"}]},"parameter-object-binding-element":{"patterns":[{"include":"#comment"},{"begin":"(?=(\\\\b((?<!\\\\$)0[Xx]\\\\h[_\\\\h]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Bb][01][01_]*(n)?\\\\b(?!\\\\$))|\\\\b((?<!\\\\$)0[Oo]?[0-7][0-7_]*(n)?\\\\b(?!\\\\$))|((?<!\\\\$)(?:\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\B(\\\\.)[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*[Ee][-+]?[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(\\\\.)(n)?\\\\B|\\\\B(\\\\.)[0-9][0-9_]*(n)?\\\\b|\\\\b[0-9][0-9_]*(n)?\\\\b(?!\\\\.))(?!\\\\$))|([$_[:alpha:]][$_[:alnum:]]*)|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`)|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])+]))\\\\s*(:))","end":"(?=[,}])","patterns":[{"include":"#object-binding-element-propertyName"},{"include":"#parameter-binding-element"},{"include":"#paren-expression"}]},{"include":"#parameter-object-binding-pattern"},{"include":"#destructuring-parameter-rest"},{"include":"#variable-initializer"},{"include":"#punctuation-comma"}]},"parameter-object-binding-pattern":{"begin":"(?:(\\\\.\\\\.\\\\.)\\\\s*)?(\\\\{)","beginCaptures":{"1":{"name":"keyword.operator.rest.ts"},"2":{"name":"punctuation.definition.binding-pattern.object.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.binding-pattern.object.ts"}},"patterns":[{"include":"#parameter-object-binding-element"}]},"parameter-type-annotation":{"patterns":[{"begin":"(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.ts"}},"end":"(?=[),])|(?==[^>])","name":"meta.type.annotation.ts","patterns":[{"include":"#type"}]}]},"paren-expression":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#expression"}]},"paren-expression-possibly-arrow":{"patterns":[{"begin":"(?<=[(,=])\\\\s*(async)?(?=\\\\s*((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))","beginCaptures":{"1":{"name":"storage.modifier.async.ts"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"begin":"(?<=[(,=]|=>|^return|[^$._[:alnum:]]return)\\\\s*(async)?(?=\\\\s*((((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*))?\\\\()|(<)|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)))\\\\s*$)","beginCaptures":{"1":{"name":"storage.modifier.async.ts"}},"end":"(?<=\\\\))","patterns":[{"include":"#paren-expression-possibly-arrow-with-typeparameters"}]},{"include":"#possibly-arrow-return-type"}]},"paren-expression-possibly-arrow-with-typeparameters":{"patterns":[{"include":"#type-parameters"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"patterns":[{"include":"#expression-inside-possibly-arrow-parens"}]}]},"possibly-arrow-return-type":{"begin":"(?<=\\\\)|^)\\\\s*(:)(?=\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*=>)","beginCaptures":{"1":{"name":"meta.arrow.ts meta.return.type.arrow.ts keyword.operator.type.annotation.ts"}},"contentName":"meta.arrow.ts meta.return.type.arrow.ts","end":"(?==>|\\\\{|^(\\\\s*(export|function|class|interface|let|var|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|const|import|enum|namespace|module|type|abstract|declare)\\\\s+))","patterns":[{"include":"#arrow-return-type-body"}]},"property-accessor":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(accessor|get|set)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.type.property.ts"},"punctuation-accessor":{"captures":{"1":{"name":"punctuation.accessor.ts"},"2":{"name":"punctuation.accessor.optional.ts"}},"match":"(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d))"},"punctuation-comma":{"match":",","name":"punctuation.separator.comma.ts"},"punctuation-semicolon":{"match":";","name":"punctuation.terminator.statement.ts"},"qstring-double":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.ts"}},"end":"(\\")|([^\\\\n\\\\\\\\])$","endCaptures":{"1":{"name":"punctuation.definition.string.end.ts"},"2":{"name":"invalid.illegal.newline.ts"}},"name":"string.quoted.double.ts","patterns":[{"include":"#string-character-escape"}]},"qstring-single":{"begin":"\'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.ts"}},"end":"(\')|([^\\\\n\\\\\\\\])$","endCaptures":{"1":{"name":"punctuation.definition.string.end.ts"},"2":{"name":"invalid.illegal.newline.ts"}},"name":"string.quoted.single.ts","patterns":[{"include":"#string-character-escape"}]},"regex":{"patterns":[{"begin":"(?<!\\\\+\\\\+|--|})(?<=[!(+,:=?\\\\[]|^return|[^$._[:alnum:]]return|^case|[^$._[:alnum:]]case|=>|&&|\\\\|\\\\||\\\\*/)\\\\s*(/)(?![*/])(?=(?:[^()/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)+]|\\\\(([^)\\\\\\\\]|\\\\\\\\.)+\\\\))+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"1":{"name":"punctuation.definition.string.begin.ts"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.ts"},"2":{"name":"keyword.other.ts"}},"name":"string.regexp.ts","patterns":[{"include":"#regexp"}]},{"begin":"((?<![]$)_[:alnum:]]|\\\\+\\\\+|--|}|\\\\*/)|((?<=^return|[^$._[:alnum:]]return|^case|[^$._[:alnum:]]case))\\\\s*)/(?![*/])(?=(?:[^/\\\\[\\\\\\\\]|\\\\\\\\.|\\\\[([^]\\\\\\\\]|\\\\\\\\.)*])+/([dgimsuvy]+|(?![*/])|(?=/\\\\*))(?!\\\\s*[$0-9A-Z_a-z]))","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.ts"}},"end":"(/)([dgimsuvy]*)","endCaptures":{"1":{"name":"punctuation.definition.string.end.ts"},"2":{"name":"keyword.other.ts"}},"name":"string.regexp.ts","patterns":[{"include":"#regexp"}]}]},"regex-character-class":{"patterns":[{"match":"\\\\\\\\[DSWdfnrstvw]|\\\\.","name":"constant.other.character-class.regexp"},{"match":"\\\\\\\\([0-7]{3}|x\\\\h{2}|u\\\\h{4})","name":"constant.character.numeric.regexp"},{"match":"\\\\\\\\c[A-Z]","name":"constant.character.control.regexp"},{"match":"\\\\\\\\.","name":"constant.character.escape.backslash.regexp"}]},"regexp":{"patterns":[{"match":"\\\\\\\\[Bb]|[$^]","name":"keyword.control.anchor.regexp"},{"captures":{"0":{"name":"keyword.other.back-reference.regexp"},"1":{"name":"variable.other.regexp"}},"match":"\\\\\\\\(?:[1-9]\\\\d*|k<([$A-Z_a-z][$\\\\w]*)>)"},{"match":"[*+?]|\\\\{(\\\\d+,\\\\d+|\\\\d+,|,\\\\d+|\\\\d+)}\\\\??","name":"keyword.operator.quantifier.regexp"},{"match":"\\\\|","name":"keyword.operator.or.regexp"},{"begin":"(\\\\()((\\\\?=)|(\\\\?!)|(\\\\?<=)|(\\\\?<!))","beginCaptures":{"1":{"name":"punctuation.definition.group.regexp"},"2":{"name":"punctuation.definition.group.assertion.regexp"},"3":{"name":"meta.assertion.look-ahead.regexp"},"4":{"name":"meta.assertion.negative-look-ahead.regexp"},"5":{"name":"meta.assertion.look-behind.regexp"},"6":{"name":"meta.assertion.negative-look-behind.regexp"}},"end":"(\\\\))","endCaptures":{"1":{"name":"punctuation.definition.group.regexp"}},"name":"meta.group.assertion.regexp","patterns":[{"include":"#regexp"}]},{"begin":"\\\\((?:(\\\\?:)|\\\\?<([$A-Z_a-z][$\\\\w]*)>)?","beginCaptures":{"0":{"name":"punctuation.definition.group.regexp"},"1":{"name":"punctuation.definition.group.no-capture.regexp"},"2":{"name":"variable.other.regexp"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.group.regexp"}},"name":"meta.group.regexp","patterns":[{"include":"#regexp"}]},{"begin":"(\\\\[)(\\\\^)?","beginCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"},"2":{"name":"keyword.operator.negation.regexp"}},"end":"(])","endCaptures":{"1":{"name":"punctuation.definition.character-class.regexp"}},"name":"constant.other.character-class.set.regexp","patterns":[{"captures":{"1":{"name":"constant.character.numeric.regexp"},"2":{"name":"constant.character.control.regexp"},"3":{"name":"constant.character.escape.backslash.regexp"},"4":{"name":"constant.character.numeric.regexp"},"5":{"name":"constant.character.control.regexp"},"6":{"name":"constant.character.escape.backslash.regexp"}},"match":"(?:.|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))-(?:[^]\\\\\\\\]|(\\\\\\\\(?:[0-7]{3}|x\\\\h{2}|u\\\\h{4}))|(\\\\\\\\c[A-Z])|(\\\\\\\\.))","name":"constant.other.character-class.range.regexp"},{"include":"#regex-character-class"}]},{"include":"#regex-character-class"}]},"return-type":{"patterns":[{"begin":"(?<=\\\\))\\\\s*(:)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.ts"}},"end":"(?<![\\\\&:|])(?=$|^|[,;{}]|//)","name":"meta.return.type.ts","patterns":[{"include":"#return-type-core"}]},{"begin":"(?<=\\\\))\\\\s*(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.ts"}},"end":"(?<![\\\\&:|])((?=[,;{}]|//|^\\\\s*$)|((?<=\\\\S)(?=\\\\s*$)))","name":"meta.return.type.ts","patterns":[{"include":"#return-type-core"}]}]},"return-type-core":{"patterns":[{"include":"#comment"},{"begin":"(?<=[\\\\&:|])(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"shebang":{"captures":{"1":{"name":"punctuation.definition.comment.ts"}},"match":"\\\\A(#!).*(?=$)","name":"comment.line.shebang.ts"},"single-line-comment-consuming-line-ending":{"begin":"(^[\\\\t ]+)?((//)(?:\\\\s*((@)internal)(?=\\\\s|$))?)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.ts"},"2":{"name":"comment.line.double-slash.ts"},"3":{"name":"punctuation.definition.comment.ts"},"4":{"name":"storage.type.internaldeclaration.ts"},"5":{"name":"punctuation.decorator.internaldeclaration.ts"}},"contentName":"comment.line.double-slash.ts","end":"(?=^)"},"statements":{"patterns":[{"include":"#declaration"},{"include":"#control-statement"},{"include":"#after-operator-block-as-object-literal"},{"include":"#decl-block"},{"include":"#label"},{"include":"#expression"},{"include":"#punctuation-semicolon"},{"include":"#string"},{"include":"#comment"}]},"string":{"patterns":[{"include":"#qstring-single"},{"include":"#qstring-double"},{"include":"#template"}]},"string-character-escape":{"match":"\\\\\\\\(x\\\\h{2}|u\\\\h{4}|u\\\\{\\\\h+}|[012][0-7]{0,2}|3[0-6][0-7]?|37[0-7]?|[4-7][0-7]?|.|$)","name":"constant.character.escape.ts"},"super-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))super\\\\b(?!\\\\$)","name":"variable.language.super.ts"},"support-function-call-identifiers":{"patterns":[{"include":"#literal"},{"include":"#support-objects"},{"include":"#object-identifiers"},{"include":"#punctuation-accessor"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))import(?=\\\\s*\\\\(\\\\s*[\\"\'`])","name":"keyword.operator.expression.import.ts"}]},"support-objects":{"patterns":[{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(arguments)\\\\b(?!\\\\$)","name":"variable.language.arguments.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(Promise)\\\\b(?!\\\\$)","name":"support.class.promise.ts"},{"captures":{"1":{"name":"keyword.control.import.ts"},"2":{"name":"punctuation.accessor.ts"},"3":{"name":"punctuation.accessor.optional.ts"},"4":{"name":"support.variable.property.importmeta.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(import)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(meta)\\\\b(?!\\\\$)"},{"captures":{"1":{"name":"keyword.operator.new.ts"},"2":{"name":"punctuation.accessor.ts"},"3":{"name":"punctuation.accessor.optional.ts"},"4":{"name":"support.variable.property.target.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(new)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(target)\\\\b(?!\\\\$)"},{"captures":{"1":{"name":"punctuation.accessor.ts"},"2":{"name":"punctuation.accessor.optional.ts"},"3":{"name":"support.variable.property.ts"},"4":{"name":"support.constant.ts"}},"match":"(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(?:(constructor|length|prototype|__proto__)\\\\b(?!\\\\$|\\\\s*(<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\()|(EPSILON|MAX_SAFE_INTEGER|MAX_VALUE|MIN_SAFE_INTEGER|MIN_VALUE|NEGATIVE_INFINITY|POSITIVE_INFINITY)\\\\b(?!\\\\$))"},{"captures":{"1":{"name":"support.type.object.module.ts"},"2":{"name":"support.type.object.module.ts"},"3":{"name":"punctuation.accessor.ts"},"4":{"name":"punctuation.accessor.optional.ts"},"5":{"name":"support.type.object.module.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(exports)|(module)(?:(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))(exports|id|filename|loaded|parent|children))?)\\\\b(?!\\\\$)"}]},"switch-statement":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?=\\\\bswitch\\\\s*\\\\()","end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"name":"switch-statement.expr.ts","patterns":[{"include":"#comment"},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(switch)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"keyword.control.switch.ts"},"2":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"name":"switch-expression.expr.ts","patterns":[{"include":"#expression"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"(?=})","name":"switch-block.expr.ts","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(case|default(?=:))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.control.switch.ts"}},"end":"(?=:)","name":"case-clause.expr.ts","patterns":[{"include":"#expression"}]},{"begin":"(:)\\\\s*(\\\\{)","beginCaptures":{"1":{"name":"case-clause.expr.ts punctuation.definition.section.case-statement.ts"},"2":{"name":"meta.block.ts punctuation.definition.block.ts"}},"contentName":"meta.block.ts","end":"}","endCaptures":{"0":{"name":"meta.block.ts punctuation.definition.block.ts"}},"patterns":[{"include":"#statements"}]},{"captures":{"0":{"name":"case-clause.expr.ts punctuation.definition.section.case-statement.ts"}},"match":"(:)"},{"include":"#statements"}]}]},"template":{"patterns":[{"include":"#template-call"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?(`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.ts"},"2":{"name":"string.template.ts punctuation.definition.string.template.begin.ts"}},"contentName":"string.template.ts","end":"`","endCaptures":{"0":{"name":"string.template.ts punctuation.definition.string.template.end.ts"}},"patterns":[{"include":"#template-substitution-element"},{"include":"#string-character-escape"}]}]},"template-call":{"patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*\\\\s*\\\\??\\\\.\\\\s*)*|(\\\\??\\\\.\\\\s*)?)([$_[:alpha:]][$_[:alnum:]]*)(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?`)","end":"(?=`)","patterns":[{"begin":"(?=(([$_[:alpha:]][$_[:alnum:]]*\\\\s*\\\\??\\\\.\\\\s*)*|(\\\\??\\\\.\\\\s*)?)([$_[:alpha:]][$_[:alnum:]]*))","end":"(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)?`)","patterns":[{"include":"#support-function-call-identifiers"},{"match":"([$_[:alpha:]][$_[:alnum:]]*)","name":"entity.name.function.tagged-template.ts"}]},{"include":"#type-arguments"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?\\\\s*(?=(<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))(([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>|<\\\\s*(((keyof|infer|typeof|readonly)\\\\s+)|(([$_[:alpha:]][$_[:alnum:]]*|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))(?=\\\\s*([,.<>\\\\[]|=>|&(?!&)|\\\\|(?!\\\\|)))))([^(<>]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(?<==)>)*(?<!=)>))*(?<!=)>)*(?<!=)>\\\\s*)`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.ts"}},"end":"(?=`)","patterns":[{"include":"#type-arguments"}]}]},"template-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.ts"}},"contentName":"meta.embedded.line.ts","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.ts"}},"name":"meta.template.expression.ts","patterns":[{"include":"#expression"}]},"template-type":{"patterns":[{"include":"#template-call"},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)?(`)","beginCaptures":{"1":{"name":"entity.name.function.tagged-template.ts"},"2":{"name":"string.template.ts punctuation.definition.string.template.begin.ts"}},"contentName":"string.template.ts","end":"`","endCaptures":{"0":{"name":"string.template.ts punctuation.definition.string.template.end.ts"}},"patterns":[{"include":"#template-type-substitution-element"},{"include":"#string-character-escape"}]}]},"template-type-substitution-element":{"begin":"\\\\$\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.template-expression.begin.ts"}},"contentName":"meta.embedded.line.ts","end":"}","endCaptures":{"0":{"name":"punctuation.definition.template-expression.end.ts"}},"name":"meta.template.expression.ts","patterns":[{"include":"#type"}]},"ternary-expression":{"begin":"(?!\\\\?\\\\.\\\\s*\\\\D)(\\\\?)(?!\\\\?)","beginCaptures":{"1":{"name":"keyword.operator.ternary.ts"}},"end":"\\\\s*(:)","endCaptures":{"1":{"name":"keyword.operator.ternary.ts"}},"patterns":[{"include":"#expression"}]},"this-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))this\\\\b(?!\\\\$)","name":"variable.language.this.ts"},"type":{"patterns":[{"include":"#comment"},{"include":"#type-string"},{"include":"#numeric-literal"},{"include":"#type-primitive"},{"include":"#type-builtin-literals"},{"include":"#type-parameters"},{"include":"#type-tuple"},{"include":"#type-object"},{"include":"#type-operators"},{"include":"#type-conditional"},{"include":"#type-fn-type-parameters"},{"include":"#type-paren-or-function-parameters"},{"include":"#type-function-return-type"},{"captures":{"1":{"name":"storage.modifier.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(readonly)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*"},{"include":"#type-name"}]},"type-alias-declaration":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(type)\\\\b\\\\s+([$_[:alpha:]][$_[:alnum:]]*)\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.type.ts"},"4":{"name":"entity.name.type.alias.ts"}},"end":"(?=[;}]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","name":"meta.type.declaration.ts","patterns":[{"include":"#comment"},{"include":"#type-parameters"},{"begin":"(=)\\\\s*(intrinsic)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"1":{"name":"keyword.operator.assignment.ts"},"2":{"name":"keyword.control.intrinsic.ts"}},"end":"(?=[;}]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type"}]},{"begin":"(=)\\\\s*","beginCaptures":{"1":{"name":"keyword.operator.assignment.ts"}},"end":"(?=[;}]|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type"}]}]},"type-annotation":{"patterns":[{"begin":"(:)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.ts"}},"end":"(?<![\\\\&:|])(?!\\\\s*[\\\\&|]\\\\s+)((?=^|[]),;}]|//)|(?==[^>])|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.ts","patterns":[{"include":"#type"}]},{"begin":"(:)","beginCaptures":{"1":{"name":"keyword.operator.type.annotation.ts"}},"end":"(?<![\\\\&:|])((?=[]),;}]|//)|(?==[^>])|(?=^\\\\s*$)|((?<=[]$)>_}[:alpha:]])\\\\s*(?=\\\\{)))","name":"meta.type.annotation.ts","patterns":[{"include":"#type"}]}]},"type-arguments":{"begin":"<","beginCaptures":{"0":{"name":"punctuation.definition.typeparameters.begin.ts"}},"end":">","endCaptures":{"0":{"name":"punctuation.definition.typeparameters.end.ts"}},"name":"meta.type.parameters.ts","patterns":[{"include":"#type-arguments-body"}]},"type-arguments-body":{"patterns":[{"captures":{"0":{"name":"keyword.operator.type.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(_)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"include":"#type"},{"include":"#punctuation-comma"}]},"type-builtin-literals":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(this|true|false|undefined|null|object)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"support.type.builtin.ts"},"type-conditional":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(extends)\\\\s+","beginCaptures":{"1":{"name":"storage.modifier.ts"}},"end":"(?<=:)","patterns":[{"begin":"\\\\?","beginCaptures":{"0":{"name":"keyword.operator.ternary.ts"}},"end":":","endCaptures":{"0":{"name":"keyword.operator.ternary.ts"}},"patterns":[{"include":"#type"}]},{"include":"#type"}]}]},"type-fn-type-parameters":{"patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(abstract)\\\\s+)?(new)\\\\b(?=\\\\s*<)","beginCaptures":{"1":{"name":"meta.type.constructor.ts storage.modifier.ts"},"2":{"name":"meta.type.constructor.ts keyword.control.new.ts"}},"end":"(?<=>)","patterns":[{"include":"#comment"},{"include":"#type-parameters"}]},{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(abstract)\\\\s+)?(new)\\\\b\\\\s*(?=\\\\()","beginCaptures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.control.new.ts"}},"end":"(?<=\\\\))","name":"meta.type.constructor.ts","patterns":[{"include":"#function-parameters"}]},{"begin":"((?=\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>))))))","end":"(?<=\\\\))","name":"meta.type.function.ts","patterns":[{"include":"#function-parameters"}]}]},"type-function-return-type":{"patterns":[{"begin":"(=>)(?=\\\\s*\\\\S)","beginCaptures":{"1":{"name":"storage.type.function.arrow.ts"}},"end":"(?<!=>)(?<![\\\\&|])(?=[]),:;=>?{}]|//|$)","name":"meta.type.function.return.ts","patterns":[{"include":"#type-function-return-type-core"}]},{"begin":"=>","beginCaptures":{"0":{"name":"storage.type.function.arrow.ts"}},"end":"(?<!=>)(?<![\\\\&|])((?=[]),:;=>?{}]|//|^\\\\s*$)|((?<=\\\\S)(?=\\\\s*$)))","name":"meta.type.function.return.ts","patterns":[{"include":"#type-function-return-type-core"}]}]},"type-function-return-type-core":{"patterns":[{"include":"#comment"},{"begin":"(?<==>)(?=\\\\s*\\\\{)","end":"(?<=})","patterns":[{"include":"#type-object"}]},{"include":"#type-predicate-operator"},{"include":"#type"}]},"type-infer":{"patterns":[{"captures":{"1":{"name":"keyword.operator.expression.infer.ts"},"2":{"name":"entity.name.type.ts"},"3":{"name":"keyword.operator.expression.extends.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(infer)\\\\s+([$_[:alpha:]][$_[:alnum:]]*)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))(?:\\\\s+(extends)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))?","name":"meta.type.infer.ts"}]},"type-name":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))\\\\s*(<)","captures":{"1":{"name":"entity.name.type.module.ts"},"2":{"name":"punctuation.accessor.ts"},"3":{"name":"punctuation.accessor.optional.ts"},"4":{"name":"meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts"}},"contentName":"meta.type.parameters.ts","end":"(>)","endCaptures":{"1":{"name":"meta.type.parameters.ts punctuation.definition.typeparameters.end.ts"}},"patterns":[{"include":"#type-arguments-body"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(<)","beginCaptures":{"1":{"name":"entity.name.type.ts"},"2":{"name":"meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts"}},"contentName":"meta.type.parameters.ts","end":"(>)","endCaptures":{"1":{"name":"meta.type.parameters.ts punctuation.definition.typeparameters.end.ts"}},"patterns":[{"include":"#type-arguments-body"}]},{"captures":{"1":{"name":"entity.name.type.module.ts"},"2":{"name":"punctuation.accessor.ts"},"3":{"name":"punctuation.accessor.optional.ts"}},"match":"([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(?:(\\\\.)|(\\\\?\\\\.(?!\\\\s*\\\\d)))"},{"match":"[$_[:alpha:]][$_[:alnum:]]*","name":"entity.name.type.ts"}]},"type-object":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.block.ts"}},"name":"meta.object.type.ts","patterns":[{"include":"#comment"},{"include":"#method-declaration"},{"include":"#indexer-declaration"},{"include":"#indexer-mapped-type-declaration"},{"include":"#field-declaration"},{"include":"#type-annotation"},{"begin":"\\\\.\\\\.\\\\.","beginCaptures":{"0":{"name":"keyword.operator.spread.ts"}},"end":"(?=[,;}]|$)|(?<=})","patterns":[{"include":"#type"}]},{"include":"#punctuation-comma"},{"include":"#punctuation-semicolon"},{"include":"#type"}]},"type-operators":{"patterns":[{"include":"#typeof-operator"},{"include":"#type-infer"},{"begin":"([\\\\&|])(?=\\\\s*\\\\{)","beginCaptures":{"0":{"name":"keyword.operator.type.ts"}},"end":"(?<=})","patterns":[{"include":"#type-object"}]},{"begin":"[\\\\&|]","beginCaptures":{"0":{"name":"keyword.operator.type.ts"}},"end":"(?=\\\\S)"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))keyof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.keyof.ts"},{"match":"([:?])","name":"keyword.operator.ternary.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))import(?=\\\\s*\\\\()","name":"keyword.operator.expression.import.ts"}]},"type-parameters":{"begin":"(<)","beginCaptures":{"1":{"name":"punctuation.definition.typeparameters.begin.ts"}},"end":"(>)","endCaptures":{"1":{"name":"punctuation.definition.typeparameters.end.ts"}},"name":"meta.type.parameters.ts","patterns":[{"include":"#comment"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(extends|in|out|const)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"storage.modifier.ts"},{"include":"#type"},{"include":"#punctuation-comma"},{"match":"(=)(?!>)","name":"keyword.operator.assignment.ts"}]},"type-paren-or-function-parameters":{"begin":"\\\\(","beginCaptures":{"0":{"name":"meta.brace.round.ts"}},"end":"\\\\)","endCaptures":{"0":{"name":"meta.brace.round.ts"}},"name":"meta.type.paren.cover.ts","patterns":[{"captures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.operator.rest.ts"},"3":{"name":"entity.name.function.ts variable.language.this.ts"},"4":{"name":"entity.name.function.ts"},"5":{"name":"keyword.operator.optional.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))\\\\s*(\\\\??)(?=\\\\s*(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))))"},{"captures":{"1":{"name":"storage.modifier.ts"},"2":{"name":"keyword.operator.rest.ts"},"3":{"name":"variable.parameter.ts variable.language.this.ts"},"4":{"name":"variable.parameter.ts"},"5":{"name":"keyword.operator.optional.ts"}},"match":"(?:(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(public|private|protected|readonly)\\\\s+)?(?:(\\\\.\\\\.\\\\.)\\\\s*)?(?<![:=])(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))\\\\s*(\\\\??)(?=:)"},{"include":"#type-annotation"},{"match":",","name":"punctuation.separator.parameter.ts"},{"include":"#type"}]},"type-predicate-operator":{"patterns":[{"captures":{"1":{"name":"keyword.operator.type.asserts.ts"},"2":{"name":"variable.parameter.ts variable.language.this.ts"},"3":{"name":"variable.parameter.ts"},"4":{"name":"keyword.operator.expression.is.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:(asserts)\\\\s+)?(?!asserts)(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))\\\\s(is)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"captures":{"1":{"name":"keyword.operator.type.asserts.ts"},"2":{"name":"variable.parameter.ts variable.language.this.ts"},"3":{"name":"variable.parameter.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(asserts)\\\\s+(?!is)(?:(this)|([$_[:alpha:]][$_[:alnum:]]*))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))asserts(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.type.asserts.ts"},{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))is(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"keyword.operator.expression.is.ts"}]},"type-primitive":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(string|number|bigint|boolean|symbol|any|void|never|unknown)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"support.type.primitive.ts"},"type-string":{"patterns":[{"include":"#qstring-single"},{"include":"#qstring-double"},{"include":"#template-type"}]},"type-tuple":{"begin":"\\\\[","beginCaptures":{"0":{"name":"meta.brace.square.ts"}},"end":"]","endCaptures":{"0":{"name":"meta.brace.square.ts"}},"name":"meta.type.tuple.ts","patterns":[{"match":"\\\\.\\\\.\\\\.","name":"keyword.operator.rest.ts"},{"captures":{"1":{"name":"entity.name.label.ts"},"2":{"name":"keyword.operator.optional.ts"},"3":{"name":"punctuation.separator.label.ts"}},"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))([$_[:alpha:]][$_[:alnum:]]*)\\\\s*(\\\\?)?\\\\s*(:)"},{"include":"#type"},{"include":"#punctuation-comma"}]},"typeof-operator":{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))typeof(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","beginCaptures":{"0":{"name":"keyword.operator.expression.typeof.ts"}},"end":"(?=[]\\\\&),:;=>?{|}]|(extends\\\\s+)|$|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)","patterns":[{"include":"#type-arguments"},{"include":"#expression"}]},"undefined-literal":{"match":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))undefined(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))","name":"constant.language.undefined.ts"},"var-expr":{"patterns":[{"begin":"(?=(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(var|let)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))","end":"(?!(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(var|let)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))((?=^|[;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|((?<!^let|[^$._[:alnum:]]let|^var|[^$._[:alnum:]]var)(?=\\\\s*$)))","name":"meta.var.expr.ts","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(var|let)(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.ts"}},"end":"(?=\\\\S)"},{"include":"#destructuring-variable"},{"include":"#var-single-variable"},{"include":"#variable-initializer"},{"include":"#comment"},{"begin":"(,)\\\\s*(?=$|//)","beginCaptures":{"1":{"name":"punctuation.separator.comma.ts"}},"end":"(?<!,)(((?=[;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|^\\\\s*$))|((?<=\\\\S)(?=\\\\s*$)))","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#destructuring-variable"},{"include":"#var-single-variable"},{"include":"#punctuation-comma"}]},{"include":"#punctuation-comma"}]},{"begin":"(?=(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(const(?!\\\\s+enum\\\\b))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.ts"}},"end":"(?!(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(const(?!\\\\s+enum\\\\b))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))((?=^|[;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|((?<!(?:^|[^$._[:alnum:]])const)(?=\\\\s*$)))","name":"meta.var.expr.ts","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b(const(?!\\\\s+enum\\\\b))(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.ts"}},"end":"(?=\\\\S)"},{"include":"#destructuring-const"},{"include":"#var-single-const"},{"include":"#variable-initializer"},{"include":"#comment"},{"begin":"(,)\\\\s*(?=$|//)","beginCaptures":{"1":{"name":"punctuation.separator.comma.ts"}},"end":"(?<!,)(((?=[;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|^\\\\s*$))|((?<=\\\\S)(?=\\\\s*$)))","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#destructuring-const"},{"include":"#var-single-const"},{"include":"#punctuation-comma"}]},{"include":"#punctuation-comma"}]},{"begin":"(?=(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b\\\\b(using(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])|await\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.ts"}},"end":"(?!(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b\\\\b(using(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])|await\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))((?=[;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b)|((?<!(?:^|[^$._[:alnum:]]|^await\\\\s+|[^$._[:alnum:]]await\\\\s+)using)(?=\\\\s*$)))","name":"meta.var.expr.ts","patterns":[{"begin":"(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(?:\\\\b(export)\\\\s+)?(?:\\\\b(declare)\\\\s+)?\\\\b\\\\b(using(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])|await\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b)\\\\b(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.))\\\\s*","beginCaptures":{"1":{"name":"keyword.control.export.ts"},"2":{"name":"storage.modifier.ts"},"3":{"name":"storage.type.ts"}},"end":"(?=\\\\S)"},{"include":"#var-single-const"},{"include":"#variable-initializer"},{"include":"#comment"},{"begin":"(,)\\\\s*((?!\\\\S)|(?=//))","beginCaptures":{"1":{"name":"punctuation.separator.comma.ts"}},"end":"(?<!,)(((?=[;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|^\\\\s*$))|((?<=\\\\S)(?=\\\\s*$)))","patterns":[{"include":"#single-line-comment-consuming-line-ending"},{"include":"#comment"},{"include":"#var-single-const"},{"include":"#punctuation-comma"}]},{"include":"#punctuation-comma"}]}]},"var-single-const":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.ts variable.other.constant.ts entity.name.function.ts"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.ts","patterns":[{"include":"#var-single-variable-type-annotation"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)","beginCaptures":{"1":{"name":"meta.definition.variable.ts variable.other.constant.ts"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.ts","patterns":[{"include":"#var-single-variable-type-annotation"}]}]},"var-single-variable":{"patterns":[{"begin":"([$_[:alpha:]][$_[:alnum:]]*)(!)?(?=\\\\s*(=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>)))))|(:\\\\s*((<)|(\\\\(\\\\s*((\\\\))|(\\\\.\\\\.\\\\.)|([$_[:alnum:]]+\\\\s*(([,:=?])|(\\\\)\\\\s*=>)))))))|(:\\\\s*(?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))Function(?![$_[:alnum:]])(?:(?=\\\\.\\\\.\\\\.)|(?!\\\\.)))|(:\\\\s*((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))))))|(:\\\\s*(=>|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(<[^<>]*>)|[^(),<=>])+=\\\\s*(((async\\\\s+)?((function\\\\s*[(*<])|(function\\\\s+)|([$_[:alpha:]][$_[:alnum:]]*\\\\s*=>)))|((async\\\\s*)?(((<\\\\s*)$|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*((([\\\\[{]\\\\s*)?)$|((\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})\\\\s*((:\\\\s*\\\\{?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*)))|((\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])\\\\s*((:\\\\s*\\\\[?)$|((\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+\\\\s*)?=\\\\s*))))))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*((\\\\)\\\\s*:)|((\\\\.\\\\.\\\\.\\\\s*)?[$_[:alpha:]][$_[:alnum:]]*\\\\s*:)))|((<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<]|<\\\\s*(((const\\\\s+)?[$_[:alpha:]])|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*]))([^<=>]|=[^<])*>)*>)*>\\\\s*)?\\\\(\\\\s*(/\\\\*([^*]|(\\\\*[^/]))*\\\\*/\\\\s*)*(([$_[:alpha:]]|(\\\\{([^{}]|(\\\\{([^{}]|\\\\{[^{}]*})*}))*})|(\\\\[([^]\\\\[]|(\\\\[([^]\\\\[]|\\\\[[^]\\\\[]*])*]))*])|(\\\\.\\\\.\\\\.\\\\s*[$_[:alpha:]]))([^\\"\'()`]|(\\\\(([^()]|(\\\\(([^()]|\\\\([^()]*\\\\))*\\\\)))*\\\\))|(\'([^\'\\\\\\\\]|\\\\\\\\.)*\')|(\\"([^\\"\\\\\\\\]|\\\\\\\\.)*\\")|(`([^\\\\\\\\`]|\\\\\\\\.)*`))*)?\\\\)(\\\\s*:\\\\s*([^()<>{}]|<([^<>]|<([^<>]|<[^<>]+>)+>)+>|\\\\([^()]+\\\\)|\\\\{[^{}]+})+)?\\\\s*=>))))))","beginCaptures":{"1":{"name":"meta.definition.variable.ts entity.name.function.ts"},"2":{"name":"keyword.operator.definiteassignment.ts"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.ts","patterns":[{"include":"#var-single-variable-type-annotation"}]},{"begin":"(\\\\p{upper}[$_\\\\d[:upper:]]*)(?![$_[:alnum:]])(!)?","beginCaptures":{"1":{"name":"meta.definition.variable.ts variable.other.constant.ts"},"2":{"name":"keyword.operator.definiteassignment.ts"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.ts","patterns":[{"include":"#var-single-variable-type-annotation"}]},{"begin":"([$_[:alpha:]][$_[:alnum:]]*)(!)?","beginCaptures":{"1":{"name":"meta.definition.variable.ts variable.other.readwrite.ts"},"2":{"name":"keyword.operator.definiteassignment.ts"}},"end":"(?=$|^|[,;=}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+)|(;|^\\\\s*$|^\\\\s*(?:abstract|async|\\\\bawait\\\\s+\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b\\\\b|break|case|catch|class|const|continue|declare|do|else|enum|export|finally|function|for|goto|if|import|interface|let|module|namespace|switch|return|throw|try|type|\\\\busing(?=\\\\s+(?!in\\\\b|of\\\\b(?!\\\\s*(?:of\\\\b|=)))[$_[:alpha:]])\\\\b|var|while)\\\\b))","name":"meta.var-single-variable.expr.ts","patterns":[{"include":"#var-single-variable-type-annotation"}]}]},"var-single-variable-type-annotation":{"patterns":[{"include":"#type-annotation"},{"include":"#string"},{"include":"#comment"}]},"variable-initializer":{"patterns":[{"begin":"(?<![!=])(=)(?!=)(?=\\\\s*\\\\S)(?!\\\\s*.*=>\\\\s*$)","beginCaptures":{"1":{"name":"keyword.operator.assignment.ts"}},"end":"(?=$|^|[]),;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))","patterns":[{"include":"#expression"}]},{"begin":"(?<![!=])(=)(?!=)","beginCaptures":{"1":{"name":"keyword.operator.assignment.ts"}},"end":"(?=[]),;}]|((?<![$_[:alnum:]])(?:(?<=\\\\.\\\\.\\\\.)|(?<!\\\\.))(of|in)\\\\s+))|(?=^\\\\s*$)|(?<![-\\\\&*+/|])(?<=\\\\S)(?<!=)(?=\\\\s*$)","patterns":[{"include":"#expression"}]}]}},"scopeName":"source.ts","aliases":["ts","cts","mts"]}'))],tl=[Object.freeze(JSON.parse(`{"displayName":"Java","name":"java","patterns":[{"begin":"\\\\b(package)\\\\b\\\\s*","beginCaptures":{"1":{"name":"keyword.other.package.java"}},"contentName":"storage.modifier.package.java","end":"\\\\s*(;)","endCaptures":{"1":{"name":"punctuation.terminator.java"}},"name":"meta.package.java","patterns":[{"include":"#comments"},{"match":"(?<=\\\\.)\\\\s*\\\\.|\\\\.(?=\\\\s*;)","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"(?<!_)_(?=\\\\s*([.;]))|\\\\b\\\\d+|-+","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"[A-Z]+","name":"invalid.deprecated.package_name_not_lowercase.java"},{"match":"\\\\b(?<!\\\\$)(abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|non-sealed|package|permits|private|protected|public|return|sealed|short|static|strictfp|super|switch|syncronized|this|throws??|transient|try|void|volatile|while|yield|true|false|null)\\\\b","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"\\\\.","name":"punctuation.separator.java"}]},{"begin":"\\\\b(import)\\\\b\\\\s*\\\\b(static)?\\\\b\\\\s","beginCaptures":{"1":{"name":"keyword.other.import.java"},"2":{"name":"storage.modifier.java"}},"contentName":"storage.modifier.import.java","end":"\\\\s*(;)","endCaptures":{"1":{"name":"punctuation.terminator.java"}},"name":"meta.import.java","patterns":[{"include":"#comments"},{"match":"(?<=\\\\.)\\\\s*\\\\.|\\\\.(?=\\\\s*;)","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"(?<!\\\\.)\\\\s*\\\\*","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"(?<!_)_(?=\\\\s*([.;]))|\\\\b\\\\d+|-+","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"\\\\b(?<!\\\\$)(abstract|assert|boolean|break|byte|case|catch|char|class|const|continue|default|do|double|else|enum|extends|final|finally|float|for|goto|if|implements|import|instanceof|int|interface|long|native|new|non-sealed|package|permits|private|protected|public|return|sealed|short|static|strictfp|super|switch|syncronized|this|throws??|transient|try|void|volatile|while|yield|true|false|null)\\\\b","name":"invalid.illegal.character_not_allowed_here.java"},{"match":"\\\\.","name":"punctuation.separator.java"},{"match":"\\\\*","name":"variable.language.wildcard.java"}]},{"include":"#comments-javadoc"},{"include":"#code"},{"include":"#module"}],"repository":{"all-types":{"patterns":[{"include":"#primitive-arrays"},{"include":"#primitive-types"},{"include":"#object-types"}]},"annotations":{"patterns":[{"begin":"((@)\\\\s*([^(\\\\s]+))(\\\\()","beginCaptures":{"2":{"name":"punctuation.definition.annotation.java"},"3":{"name":"storage.type.annotation.java"},"4":{"name":"punctuation.definition.annotation-arguments.begin.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.annotation-arguments.end.bracket.round.java"}},"name":"meta.declaration.annotation.java","patterns":[{"captures":{"1":{"name":"constant.other.key.java"},"2":{"name":"keyword.operator.assignment.java"}},"match":"(\\\\w*)\\\\s*(=)"},{"include":"#code"}]},{"captures":{"1":{"name":"punctuation.definition.annotation.java"},"2":{"name":"storage.modifier.java"},"3":{"name":"storage.type.annotation.java"},"5":{"name":"punctuation.definition.annotation.java"},"6":{"name":"storage.type.annotation.java"}},"match":"(@)(interface)\\\\s+(\\\\w*)|((@)\\\\s*(\\\\w+))","name":"meta.declaration.annotation.java"}]},"anonymous-block-and-instance-initializer":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.block.begin.bracket.curly.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.block.end.bracket.curly.java"}},"patterns":[{"include":"#code"}]},"anonymous-classes-and-new":{"begin":"\\\\bnew\\\\b","beginCaptures":{"0":{"name":"keyword.control.new.java"}},"end":"(?=[])-.:;?}]|/(?![*/])|[!%\\\\&=^|])","patterns":[{"include":"#comments"},{"include":"#function-call"},{"include":"#all-types"},{"begin":"(?<=\\\\))","end":"(?=[])-.:;?}]|/(?![*/])|[!%\\\\&=^|])","patterns":[{"include":"#comments"},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.inner-class.begin.bracket.curly.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.inner-class.end.bracket.curly.java"}},"name":"meta.inner-class.java","patterns":[{"include":"#class-body"}]}]},{"begin":"(?<=])","end":"(?=[])-.:;?}]|/(?![*/])|[!%\\\\&=^|])","patterns":[{"include":"#comments"},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.array-initializer.begin.bracket.curly.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.array-initializer.end.bracket.curly.java"}},"name":"meta.array-initializer.java","patterns":[{"include":"#code"}]}]},{"include":"#parens"}]},"assertions":{"patterns":[{"begin":"\\\\b(assert)\\\\s","beginCaptures":{"1":{"name":"keyword.control.assert.java"}},"end":"$","name":"meta.declaration.assertion.java","patterns":[{"match":":","name":"keyword.operator.assert.expression-separator.java"},{"include":"#code"}]}]},"class":{"begin":"(?=\\\\w?[-\\\\w\\\\s]*\\\\b(?:class|(?<!@)interface|enum)\\\\s+[$\\\\w]+)","end":"}","endCaptures":{"0":{"name":"punctuation.section.class.end.bracket.curly.java"}},"name":"meta.class.java","patterns":[{"include":"#storage-modifiers"},{"include":"#generics"},{"include":"#comments"},{"captures":{"1":{"name":"storage.modifier.java"},"2":{"name":"entity.name.type.class.java"}},"match":"(class|(?<!@)interface|enum)\\\\s+([$\\\\w]+)","name":"meta.class.identifier.java"},{"begin":"extends","beginCaptures":{"0":{"name":"storage.modifier.extends.java"}},"end":"(?=\\\\{|implements|permits)","name":"meta.definition.class.inherited.classes.java","patterns":[{"include":"#object-types-inherited"},{"include":"#comments"}]},{"begin":"(implements)\\\\s","beginCaptures":{"1":{"name":"storage.modifier.implements.java"}},"end":"(?=\\\\s*extends|permits|\\\\{)","name":"meta.definition.class.implemented.interfaces.java","patterns":[{"include":"#object-types-inherited"},{"include":"#comments"}]},{"begin":"(permits)\\\\s","beginCaptures":{"1":{"name":"storage.modifier.permits.java"}},"end":"(?=\\\\s*extends|implements|\\\\{)","name":"meta.definition.class.permits.classes.java","patterns":[{"include":"#object-types-inherited"},{"include":"#comments"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.class.begin.bracket.curly.java"}},"contentName":"meta.class.body.java","end":"(?=})","patterns":[{"include":"#class-body"}]}]},"class-body":{"patterns":[{"include":"#comments-javadoc"},{"include":"#comments"},{"include":"#enums"},{"include":"#class"},{"include":"#generics"},{"include":"#static-initializer"},{"include":"#class-fields-and-methods"},{"include":"#annotations"},{"include":"#storage-modifiers"},{"include":"#member-variables"},{"include":"#code"}]},"class-fields-and-methods":{"patterns":[{"begin":"(?==)","end":"(?=;)","patterns":[{"include":"#code"}]},{"include":"#methods"}]},"code":{"patterns":[{"include":"#annotations"},{"include":"#comments"},{"include":"#enums"},{"include":"#class"},{"include":"#record"},{"include":"#anonymous-block-and-instance-initializer"},{"include":"#try-catch-finally"},{"include":"#assertions"},{"include":"#parens"},{"include":"#constants-and-special-vars"},{"include":"#numbers"},{"include":"#anonymous-classes-and-new"},{"include":"#lambda-expression"},{"include":"#keywords"},{"include":"#storage-modifiers"},{"include":"#method-call"},{"include":"#function-call"},{"include":"#variables"},{"include":"#variables-local"},{"include":"#objects"},{"include":"#properties"},{"include":"#strings"},{"include":"#all-types"},{"match":",","name":"punctuation.separator.delimiter.java"},{"match":"\\\\.","name":"punctuation.separator.period.java"},{"match":";","name":"punctuation.terminator.java"}]},"comments":{"patterns":[{"captures":{"0":{"name":"punctuation.definition.comment.java"}},"match":"/\\\\*\\\\*/","name":"comment.block.empty.java"},{"include":"#comments-inline"}]},"comments-inline":{"patterns":[{"begin":"/\\\\*","captures":{"0":{"name":"punctuation.definition.comment.java"}},"end":"\\\\*/","name":"comment.block.java"},{"begin":"(^[\\\\t ]+)?(?=//)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.java"}},"end":"(?!\\\\G)","patterns":[{"begin":"//","beginCaptures":{"0":{"name":"punctuation.definition.comment.java"}},"end":"\\\\n","name":"comment.line.double-slash.java"}]}]},"comments-javadoc":{"patterns":[{"begin":"^\\\\s*(/\\\\*\\\\*)(?!/)","beginCaptures":{"1":{"name":"punctuation.definition.comment.java"}},"end":"\\\\*/","endCaptures":{"0":{"name":"punctuation.definition.comment.java"}},"name":"comment.block.javadoc.java","patterns":[{"match":"@(author|deprecated|return|see|serial|since|version)\\\\b","name":"keyword.other.documentation.javadoc.java"},{"captures":{"1":{"name":"keyword.other.documentation.javadoc.java"},"2":{"name":"variable.parameter.java"}},"match":"(@param)\\\\s+(\\\\S+)"},{"captures":{"1":{"name":"keyword.other.documentation.javadoc.java"},"2":{"name":"entity.name.type.class.java"}},"match":"(@(?:exception|throws))\\\\s+(\\\\S+)"},{"captures":{"1":{"name":"keyword.other.documentation.javadoc.java"},"2":{"name":"entity.name.type.class.java"},"3":{"name":"variable.parameter.java"}},"match":"\\\\{(@link)\\\\s+(\\\\S+)?#([$\\\\w]+\\\\s*\\\\([^()]*\\\\)).*?}"}]}]},"constants-and-special-vars":{"patterns":[{"match":"\\\\b(true|false|null)\\\\b","name":"constant.language.java"},{"match":"\\\\bthis\\\\b","name":"variable.language.this.java"},{"match":"\\\\bsuper\\\\b","name":"variable.language.java"}]},"enums":{"begin":"^\\\\s*([\\\\w\\\\s]*)(enum)\\\\s+(\\\\w+)","beginCaptures":{"1":{"patterns":[{"include":"#storage-modifiers"}]},"2":{"name":"storage.modifier.java"},"3":{"name":"entity.name.type.enum.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.enum.end.bracket.curly.java"}},"name":"meta.enum.java","patterns":[{"begin":"\\\\b(extends)\\\\b","beginCaptures":{"1":{"name":"storage.modifier.extends.java"}},"end":"(?=\\\\{|\\\\bimplements\\\\b)","name":"meta.definition.class.inherited.classes.java","patterns":[{"include":"#object-types-inherited"},{"include":"#comments"}]},{"begin":"\\\\b(implements)\\\\b","beginCaptures":{"1":{"name":"storage.modifier.implements.java"}},"end":"(?=\\\\{|\\\\bextends\\\\b)","name":"meta.definition.class.implemented.interfaces.java","patterns":[{"include":"#object-types-inherited"},{"include":"#comments"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.enum.begin.bracket.curly.java"}},"end":"(?=})","patterns":[{"begin":"(?<=\\\\{)","end":"(?=[;}])","patterns":[{"include":"#comments-javadoc"},{"include":"#comments"},{"begin":"\\\\b(\\\\w+)\\\\b","beginCaptures":{"1":{"name":"constant.other.enum.java"}},"end":"(,)|(?=[;}])","endCaptures":{"1":{"name":"punctuation.separator.delimiter.java"}},"patterns":[{"include":"#comments-javadoc"},{"include":"#comments"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.bracket.round.java"}},"patterns":[{"include":"#code"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.bracket.curly.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.bracket.curly.java"}},"patterns":[{"include":"#class-body"}]}]}]},{"include":"#class-body"}]}]},"function-call":{"begin":"([$A-Z_a-z][$\\\\w]*)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"entity.name.function.java"},"2":{"name":"punctuation.definition.parameters.begin.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.bracket.round.java"}},"name":"meta.function-call.java","patterns":[{"include":"#code"}]},"generics":{"begin":"<","beginCaptures":{"0":{"name":"punctuation.bracket.angle.java"}},"end":">","endCaptures":{"0":{"name":"punctuation.bracket.angle.java"}},"patterns":[{"match":"\\\\b(extends|super)\\\\b","name":"storage.modifier.$1.java"},{"captures":{"1":{"name":"storage.type.java"}},"match":"(?<!\\\\.)([$A-Z_a-z][$0-9A-Z_a-z]*)(?=\\\\s*<)"},{"include":"#primitive-arrays"},{"match":"[$A-Z_a-z][$0-9A-Z_a-z]*","name":"storage.type.generic.java"},{"match":"\\\\?","name":"storage.type.generic.wildcard.java"},{"match":"&","name":"punctuation.separator.types.java"},{"match":",","name":"punctuation.separator.delimiter.java"},{"match":"\\\\.","name":"punctuation.separator.period.java"},{"include":"#parens"},{"include":"#generics"},{"include":"#comments"}]},"keywords":{"patterns":[{"match":"\\\\bthrow\\\\b","name":"keyword.control.throw.java"},{"match":"[:?]","name":"keyword.control.ternary.java"},{"match":"\\\\b(return|yield|break|case|continue|default|do|while|for|switch|if|else)\\\\b","name":"keyword.control.java"},{"match":"\\\\b(instanceof)\\\\b","name":"keyword.operator.instanceof.java"},{"match":"(<<|>>>?|[\\\\^~])","name":"keyword.operator.bitwise.java"},{"match":"(([\\\\&^|]|<<|>>>?)=)","name":"keyword.operator.assignment.bitwise.java"},{"match":"(===?|!=|<=|>=|<>|[<>])","name":"keyword.operator.comparison.java"},{"match":"([-%*+/]=)","name":"keyword.operator.assignment.arithmetic.java"},{"match":"(=)","name":"keyword.operator.assignment.java"},{"match":"(--|\\\\+\\\\+)","name":"keyword.operator.increment-decrement.java"},{"match":"([-%*+/])","name":"keyword.operator.arithmetic.java"},{"match":"(!|&&|\\\\|\\\\|)","name":"keyword.operator.logical.java"},{"match":"([\\\\&|])","name":"keyword.operator.bitwise.java"},{"match":"\\\\b(const|goto)\\\\b","name":"keyword.reserved.java"}]},"lambda-expression":{"patterns":[{"match":"->","name":"storage.type.function.arrow.java"}]},"member-variables":{"begin":"(?=private|protected|public|native|synchronized|abstract|threadsafe|transient|static|final)","end":"(?=[;=])","patterns":[{"include":"#storage-modifiers"},{"include":"#variables"},{"include":"#primitive-arrays"},{"include":"#object-types"}]},"method-call":{"begin":"(\\\\.)\\\\s*([$A-Z_a-z][$\\\\w]*)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"punctuation.separator.period.java"},"2":{"name":"entity.name.function.java"},"3":{"name":"punctuation.definition.parameters.begin.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.bracket.round.java"}},"name":"meta.method-call.java","patterns":[{"include":"#code"}]},"methods":{"begin":"(?!new)(?=[<\\\\w].*\\\\s+)(?=([^/=]|/(?!/))+\\\\()","end":"(})|(?=;)","endCaptures":{"1":{"name":"punctuation.section.method.end.bracket.curly.java"}},"name":"meta.method.java","patterns":[{"include":"#storage-modifiers"},{"begin":"(\\\\w+)\\\\s*(\\\\()","beginCaptures":{"1":{"name":"entity.name.function.java"},"2":{"name":"punctuation.definition.parameters.begin.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.bracket.round.java"}},"name":"meta.method.identifier.java","patterns":[{"include":"#parameters"},{"include":"#parens"},{"include":"#comments"}]},{"include":"#generics"},{"begin":"(?=\\\\w.*\\\\s+\\\\w+\\\\s*\\\\()","end":"(?=\\\\s+\\\\w+\\\\s*\\\\()","name":"meta.method.return-type.java","patterns":[{"include":"#all-types"},{"include":"#parens"},{"include":"#comments"}]},{"include":"#throws"},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.method.begin.bracket.curly.java"}},"contentName":"meta.method.body.java","end":"(?=})","patterns":[{"include":"#code"}]},{"include":"#comments"}]},"module":{"begin":"((open)\\\\s)?(module)\\\\s+(\\\\w+)","beginCaptures":{"1":{"name":"storage.modifier.java"},"3":{"name":"storage.modifier.java"},"4":{"name":"entity.name.type.module.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.module.end.bracket.curly.java"}},"name":"meta.module.java","patterns":[{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.module.begin.bracket.curly.java"}},"contentName":"meta.module.body.java","end":"(?=})","patterns":[{"include":"#comments"},{"include":"#comments-javadoc"},{"match":"\\\\b(requires|transitive|exports|opens|to|uses|provides|with)\\\\b","name":"keyword.module.java"}]}]},"numbers":{"patterns":[{"match":"\\\\b(?<!\\\\$)0([Xx])((?<!\\\\.)\\\\h([_\\\\h]*\\\\h)?[Ll]?(?!\\\\.)|(\\\\h([_\\\\h]*\\\\h)?\\\\.?|(\\\\h([_\\\\h]*\\\\h)?)?\\\\.\\\\h([_\\\\h]*\\\\h)?)[Pp][-+]?[0-9]([0-9_]*[0-9])?[DFdf]?)\\\\b(?!\\\\$)","name":"constant.numeric.hex.java"},{"match":"\\\\b(?<!\\\\$)0([Bb])[01]([01_]*[01])?[Ll]?\\\\b(?!\\\\$)","name":"constant.numeric.binary.java"},{"match":"\\\\b(?<!\\\\$)0[0-7]([0-7_]*[0-7])?[Ll]?\\\\b(?!\\\\$)","name":"constant.numeric.octal.java"},{"match":"(?<!\\\\$)(\\\\b[0-9]([0-9_]*[0-9])?\\\\.\\\\B(?!\\\\.)|\\\\b[0-9]([0-9_]*[0-9])?\\\\.([Ee][-+]?[0-9]([0-9_]*[0-9])?)[DFdf]?\\\\b|\\\\b[0-9]([0-9_]*[0-9])?\\\\.([Ee][-+]?[0-9]([0-9_]*[0-9])?)?[DFdf]\\\\b|\\\\b[0-9]([0-9_]*[0-9])?\\\\.([0-9]([0-9_]*[0-9])?)([Ee][-+]?[0-9]([0-9_]*[0-9])?)?[DFdf]?\\\\b|(?<!\\\\.)\\\\B\\\\.[0-9]([0-9_]*[0-9])?([Ee][-+]?[0-9]([0-9_]*[0-9])?)?[DFdf]?\\\\b|\\\\b[0-9]([0-9_]*[0-9])?([Ee][-+]?[0-9]([0-9_]*[0-9])?)[DFdf]?\\\\b|\\\\b[0-9]([0-9_]*[0-9])?([Ee][-+]?[0-9]([0-9_]*[0-9])?)?[DFdf]\\\\b|\\\\b(0|[1-9]([0-9_]*[0-9])?)(?!\\\\.)[Ll]?\\\\b)(?!\\\\$)","name":"constant.numeric.decimal.java"}]},"object-types":{"patterns":[{"include":"#generics"},{"begin":"\\\\b((?:[A-Z_a-z]\\\\w*\\\\s*\\\\.\\\\s*)*)([A-Z_]\\\\w*)\\\\s*(?=\\\\[)","beginCaptures":{"1":{"patterns":[{"match":"[A-Z_a-z]\\\\w*","name":"storage.type.java"},{"match":"\\\\.","name":"punctuation.separator.period.java"}]},"2":{"name":"storage.type.object.array.java"}},"end":"(?!\\\\s*\\\\[)","patterns":[{"include":"#comments"},{"include":"#parens"}]},{"captures":{"1":{"patterns":[{"match":"[A-Z_a-z]\\\\w*","name":"storage.type.java"},{"match":"\\\\.","name":"punctuation.separator.period.java"}]}},"match":"\\\\b((?:[A-Z_a-z]\\\\w*\\\\s*\\\\.\\\\s*)*[A-Z_]\\\\w*)\\\\s*(?=<)"},{"captures":{"1":{"patterns":[{"match":"[A-Z_a-z]\\\\w*","name":"storage.type.java"},{"match":"\\\\.","name":"punctuation.separator.period.java"}]}},"match":"\\\\b((?:[A-Z_a-z]\\\\w*\\\\s*\\\\.\\\\s*)*[A-Z_]\\\\w*)\\\\b((?=\\\\s*[\\\\n$A-Z_a-z])|(?=\\\\s*\\\\.\\\\.\\\\.))"}]},"object-types-inherited":{"patterns":[{"include":"#generics"},{"captures":{"1":{"name":"punctuation.separator.period.java"}},"match":"\\\\b(?:[A-Z]\\\\w*\\\\s*(\\\\.)\\\\s*)*[A-Z]\\\\w*\\\\b","name":"entity.other.inherited-class.java"},{"match":",","name":"punctuation.separator.delimiter.java"}]},"objects":{"match":"(?<![$\\\\w])[$A-Z_a-z][$\\\\w]*(?=\\\\s*\\\\.\\\\s*[$\\\\w]+)","name":"variable.other.object.java"},"parameters":{"patterns":[{"match":"\\\\bfinal\\\\b","name":"storage.modifier.java"},{"include":"#annotations"},{"include":"#all-types"},{"include":"#strings"},{"match":"\\\\w+","name":"variable.parameter.java"},{"match":",","name":"punctuation.separator.delimiter.java"},{"match":"\\\\.\\\\.\\\\.","name":"punctuation.definition.parameters.varargs.java"}]},"parens":{"patterns":[{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.bracket.round.java"}},"patterns":[{"include":"#code"}]},{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.bracket.square.java"}},"end":"]","endCaptures":{"0":{"name":"punctuation.bracket.square.java"}},"patterns":[{"include":"#code"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.bracket.curly.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.bracket.curly.java"}},"patterns":[{"include":"#code"}]}]},"primitive-arrays":{"patterns":[{"begin":"\\\\b(void|boolean|byte|char|short|int|float|long|double)\\\\b\\\\s*(?=\\\\[)","beginCaptures":{"1":{"name":"storage.type.primitive.array.java"}},"end":"(?!\\\\s*\\\\[)","patterns":[{"include":"#comments"},{"include":"#parens"}]}]},"primitive-types":{"match":"\\\\b(void|boolean|byte|char|short|int|float|long|double)\\\\b","name":"storage.type.primitive.java"},"properties":{"patterns":[{"captures":{"1":{"name":"punctuation.separator.period.java"},"2":{"name":"keyword.control.new.java"}},"match":"(\\\\.)\\\\s*(new)"},{"captures":{"1":{"name":"punctuation.separator.period.java"},"2":{"name":"variable.other.object.property.java"}},"match":"(\\\\.)\\\\s*([$A-Z_a-z][$\\\\w]*)(?=\\\\s*\\\\.\\\\s*[$A-Z_a-z][$\\\\w]*)"},{"captures":{"1":{"name":"punctuation.separator.period.java"},"2":{"name":"variable.other.object.property.java"}},"match":"(\\\\.)\\\\s*([$A-Z_a-z][$\\\\w]*)"},{"captures":{"1":{"name":"punctuation.separator.period.java"},"2":{"name":"invalid.illegal.identifier.java"}},"match":"(\\\\.)\\\\s*([0-9][$\\\\w]*)"}]},"record":{"begin":"(?=\\\\w?[\\\\w\\\\s]*\\\\brecord\\\\s+[$\\\\w]+)","end":"}","endCaptures":{"0":{"name":"punctuation.section.class.end.bracket.curly.java"}},"name":"meta.record.java","patterns":[{"include":"#storage-modifiers"},{"include":"#generics"},{"include":"#comments"},{"begin":"(record)\\\\s+([$\\\\w]+)(<[$\\\\w]+>)?(\\\\()","beginCaptures":{"1":{"name":"storage.modifier.java"},"2":{"name":"entity.name.type.record.java"},"3":{"patterns":[{"include":"#generics"}]},"4":{"name":"punctuation.definition.parameters.begin.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.bracket.round.java"}},"name":"meta.record.identifier.java","patterns":[{"include":"#code"}]},{"begin":"(implements)\\\\s","beginCaptures":{"1":{"name":"storage.modifier.implements.java"}},"end":"(?=\\\\s*\\\\{)","name":"meta.definition.class.implemented.interfaces.java","patterns":[{"include":"#object-types-inherited"},{"include":"#comments"}]},{"include":"#record-body"}]},"record-body":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.class.begin.bracket.curly.java"}},"end":"(?=})","name":"meta.record.body.java","patterns":[{"include":"#record-constructor"},{"include":"#class-body"}]},"record-constructor":{"begin":"(?!new)(?=[<\\\\w].*\\\\s+)(?=([^(/=]|/(?!/))+(?=\\\\{))","end":"(})|(?=;)","endCaptures":{"1":{"name":"punctuation.section.method.end.bracket.curly.java"}},"name":"meta.method.java","patterns":[{"include":"#storage-modifiers"},{"begin":"(\\\\w+)","beginCaptures":{"1":{"name":"entity.name.function.java"}},"end":"(?=\\\\s*\\\\{)","name":"meta.method.identifier.java","patterns":[{"include":"#comments"}]},{"include":"#comments"},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.method.begin.bracket.curly.java"}},"contentName":"meta.method.body.java","end":"(?=})","patterns":[{"include":"#code"}]}]},"static-initializer":{"patterns":[{"include":"#anonymous-block-and-instance-initializer"},{"match":"static","name":"storage.modifier.java"}]},"storage-modifiers":{"match":"\\\\b(public|private|protected|static|final|native|synchronized|abstract|threadsafe|transient|volatile|default|strictfp|sealed|non-sealed)\\\\b","name":"storage.modifier.java"},"strings":{"patterns":[{"begin":"\\"\\"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.java"}},"end":"\\"\\"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.java"}},"name":"string.quoted.triple.java","patterns":[{"match":"(\\\\\\\\\\"\\"\\")(?!\\")|(\\\\\\\\.)","name":"constant.character.escape.java"}]},{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.java"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.java"}},"name":"string.quoted.double.java","patterns":[{"match":"\\\\\\\\.","name":"constant.character.escape.java"}]},{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.java"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.java"}},"name":"string.quoted.single.java","patterns":[{"match":"\\\\\\\\.","name":"constant.character.escape.java"}]}]},"throws":{"begin":"throws","beginCaptures":{"0":{"name":"storage.modifier.java"}},"end":"(?=[;{])","name":"meta.throwables.java","patterns":[{"match":",","name":"punctuation.separator.delimiter.java"},{"match":"[$A-Z_a-z][$.0-9A-Z_a-z]*","name":"storage.type.java"},{"include":"#comments"}]},"try-catch-finally":{"patterns":[{"begin":"\\\\btry\\\\b","beginCaptures":{"0":{"name":"keyword.control.try.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.try.end.bracket.curly.java"}},"name":"meta.try.java","patterns":[{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.section.try.resources.begin.bracket.round.java"}},"end":"\\\\)","endCaptures":{"0":{"name":"punctuation.section.try.resources.end.bracket.round.java"}},"name":"meta.try.resources.java","patterns":[{"include":"#code"}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.try.begin.bracket.curly.java"}},"contentName":"meta.try.body.java","end":"(?=})","patterns":[{"include":"#code"}]}]},{"begin":"\\\\b(catch)\\\\b","beginCaptures":{"1":{"name":"keyword.control.catch.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.catch.end.bracket.curly.java"}},"name":"meta.catch.java","patterns":[{"include":"#comments"},{"begin":"\\\\(","beginCaptures":{"0":{"name":"punctuation.definition.parameters.begin.bracket.round.java"}},"contentName":"meta.catch.parameters.java","end":"\\\\)","endCaptures":{"0":{"name":"punctuation.definition.parameters.end.bracket.round.java"}},"patterns":[{"include":"#comments"},{"include":"#storage-modifiers"},{"begin":"[$A-Z_a-z][$.0-9A-Z_a-z]*","beginCaptures":{"0":{"name":"storage.type.java"}},"end":"(\\\\|)|(?=\\\\))","endCaptures":{"1":{"name":"punctuation.catch.separator.java"}},"patterns":[{"include":"#comments"},{"captures":{"0":{"name":"variable.parameter.java"}},"match":"\\\\w+"}]}]},{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.catch.begin.bracket.curly.java"}},"contentName":"meta.catch.body.java","end":"(?=})","patterns":[{"include":"#code"}]}]},{"begin":"\\\\bfinally\\\\b","beginCaptures":{"0":{"name":"keyword.control.finally.java"}},"end":"}","endCaptures":{"0":{"name":"punctuation.section.finally.end.bracket.curly.java"}},"name":"meta.finally.java","patterns":[{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.section.finally.begin.bracket.curly.java"}},"contentName":"meta.finally.body.java","end":"(?=})","patterns":[{"include":"#code"}]}]}]},"variables":{"begin":"(?=\\\\b((void|boolean|byte|char|short|int|float|long|double)|(?>(\\\\w+\\\\.)*[A-Z_]+\\\\w*))\\\\b\\\\s*(<[],.<>?\\\\[\\\\w\\\\s]*>)?\\\\s*((\\\\[])*)?\\\\s+[$A-Z_a-z][$\\\\w]*([]$,\\\\[\\\\w][],\\\\[\\\\w\\\\s]*)?\\\\s*([:;=]))","end":"(?=[:;=])","name":"meta.definition.variable.java","patterns":[{"captures":{"1":{"name":"variable.other.definition.java"}},"match":"([$A-Z_a-z][$\\\\w]*)(?=\\\\s*(\\\\[])*\\\\s*([,:;=]))"},{"include":"#all-types"},{"include":"#code"}]},"variables-local":{"begin":"(?=\\\\b(var)\\\\b\\\\s+[$A-Z_a-z][$\\\\w]*\\\\s*([:;=]))","end":"(?=[:;=])","name":"meta.definition.variable.local.java","patterns":[{"match":"\\\\bvar\\\\b","name":"storage.type.local.java"},{"captures":{"1":{"name":"variable.other.definition.java"}},"match":"([$A-Z_a-z][$\\\\w]*)(?=\\\\s*(\\\\[])*\\\\s*([:;=]))"},{"include":"#code"}]}},"scopeName":"source.java"}`))],al=Object.freeze(JSON.parse(`{"displayName":"XML","name":"xml","patterns":[{"begin":"(<\\\\?)\\\\s*([-0-9A-Z_a-z]+)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.xml"}},"end":"(\\\\?>)","name":"meta.tag.preprocessor.xml","patterns":[{"match":" ([-A-Za-z]+)","name":"entity.other.attribute-name.xml"},{"include":"#doublequotedString"},{"include":"#singlequotedString"}]},{"begin":"(<!)(DOCTYPE)\\\\s+([:A-Z_a-z][-.0-:A-Z_a-z]*)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"keyword.other.doctype.xml"},"3":{"name":"variable.language.documentroot.xml"}},"end":"\\\\s*(>)","name":"meta.tag.sgml.doctype.xml","patterns":[{"include":"#internalSubset"}]},{"include":"#comments"},{"begin":"(<)((?:([-0-9A-Z_a-z]+)(:))?([-0-:A-Z_a-z]+))(?=(\\\\s[^>]*)?></\\\\2>)","beginCaptures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.xml"},"3":{"name":"entity.name.tag.namespace.xml"},"4":{"name":"punctuation.separator.namespace.xml"},"5":{"name":"entity.name.tag.localname.xml"}},"end":"(>)(</)((?:([-0-9A-Z_a-z]+)(:))?([-0-:A-Z_a-z]+))(>)","endCaptures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"punctuation.definition.tag.xml"},"3":{"name":"entity.name.tag.xml"},"4":{"name":"entity.name.tag.namespace.xml"},"5":{"name":"punctuation.separator.namespace.xml"},"6":{"name":"entity.name.tag.localname.xml"},"7":{"name":"punctuation.definition.tag.xml"}},"name":"meta.tag.no-content.xml","patterns":[{"include":"#tagStuff"}]},{"begin":"(</?)(?:([-.\\\\w]+)((:)))?([-.:\\\\w]+)","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"entity.name.tag.namespace.xml"},"3":{"name":"entity.name.tag.xml"},"4":{"name":"punctuation.separator.namespace.xml"},"5":{"name":"entity.name.tag.localname.xml"}},"end":"(/?>)","name":"meta.tag.xml","patterns":[{"include":"#tagStuff"}]},{"include":"#entity"},{"include":"#bare-ampersand"},{"begin":"<%@","beginCaptures":{"0":{"name":"punctuation.section.embedded.begin.xml"}},"end":"%>","endCaptures":{"0":{"name":"punctuation.section.embedded.end.xml"}},"name":"source.java-props.embedded.xml","patterns":[{"match":"page|include|taglib","name":"keyword.other.page-props.xml"}]},{"begin":"<%[!=]?(?!--)","beginCaptures":{"0":{"name":"punctuation.section.embedded.begin.xml"}},"end":"(?!--)%>","endCaptures":{"0":{"name":"punctuation.section.embedded.end.xml"}},"name":"source.java.embedded.xml","patterns":[{"include":"source.java"}]},{"begin":"<!\\\\[CDATA\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"]]>","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.unquoted.cdata.xml"}],"repository":{"EntityDecl":{"begin":"(<!)(ENTITY)\\\\s+(%\\\\s+)?([:A-Z_a-z][-.0-:A-Z_a-z]*)(\\\\s+(?:SYSTEM|PUBLIC)\\\\s+)?","captures":{"1":{"name":"punctuation.definition.tag.xml"},"2":{"name":"keyword.other.entity.xml"},"3":{"name":"punctuation.definition.entity.xml"},"4":{"name":"variable.language.entity.xml"},"5":{"name":"keyword.other.entitytype.xml"}},"end":"(>)","patterns":[{"include":"#doublequotedString"},{"include":"#singlequotedString"}]},"bare-ampersand":{"match":"&","name":"invalid.illegal.bad-ampersand.xml"},"comments":{"patterns":[{"begin":"<%--","captures":{"0":{"name":"punctuation.definition.comment.xml"},"end":"--%>","name":"comment.block.xml"}},{"begin":"<!--","captures":{"0":{"name":"punctuation.definition.comment.xml"}},"end":"-->","name":"comment.block.xml","patterns":[{"begin":"--(?!>)","captures":{"0":{"name":"invalid.illegal.bad-comments-or-CDATA.xml"}}}]}]},"doublequotedString":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.double.xml","patterns":[{"include":"#entity"},{"include":"#bare-ampersand"}]},"entity":{"captures":{"1":{"name":"punctuation.definition.constant.xml"},"3":{"name":"punctuation.definition.constant.xml"}},"match":"(&)([:A-Z_a-z][-.0-:A-Z_a-z]*|#[0-9]+|#x\\\\h+)(;)","name":"constant.character.entity.xml"},"internalSubset":{"begin":"(\\\\[)","captures":{"1":{"name":"punctuation.definition.constant.xml"}},"end":"(])","name":"meta.internalsubset.xml","patterns":[{"include":"#EntityDecl"},{"include":"#parameterEntity"},{"include":"#comments"}]},"parameterEntity":{"captures":{"1":{"name":"punctuation.definition.constant.xml"},"3":{"name":"punctuation.definition.constant.xml"}},"match":"(%)([:A-Z_a-z][-.0-:A-Z_a-z]*)(;)","name":"constant.character.parameter-entity.xml"},"singlequotedString":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.xml"}},"end":"'","endCaptures":{"0":{"name":"punctuation.definition.string.end.xml"}},"name":"string.quoted.single.xml","patterns":[{"include":"#entity"},{"include":"#bare-ampersand"}]},"tagStuff":{"patterns":[{"captures":{"1":{"name":"entity.other.attribute-name.namespace.xml"},"2":{"name":"entity.other.attribute-name.xml"},"3":{"name":"punctuation.separator.namespace.xml"},"4":{"name":"entity.other.attribute-name.localname.xml"}},"match":"(?:^|\\\\s+)(?:([-.\\\\w]+)((:)))?([-.:\\\\w]+)\\\\s*="},{"include":"#doublequotedString"},{"include":"#singlequotedString"}]}},"scopeName":"text.xml","embeddedLangs":["java"]}`)),sl=[...tl,al],il=[Object.freeze(JSON.parse(`{"displayName":"YAML","fileTypes":["yaml","yml","rviz","reek","clang-format","yaml-tmlanguage","syntax","sublime-syntax"],"firstLineMatch":"^%YAML( ?1.\\\\d+)?","name":"yaml","patterns":[{"include":"#comment"},{"include":"#property"},{"include":"#directive"},{"match":"^---","name":"entity.other.document.begin.yaml"},{"match":"^\\\\.{3}","name":"entity.other.document.end.yaml"},{"include":"#node"}],"repository":{"block-collection":{"patterns":[{"include":"#block-sequence"},{"include":"#block-mapping"}]},"block-mapping":{"patterns":[{"include":"#block-pair"}]},"block-node":{"patterns":[{"include":"#prototype"},{"include":"#block-scalar"},{"include":"#block-collection"},{"include":"#flow-scalar-plain-out"},{"include":"#flow-node"}]},"block-pair":{"patterns":[{"begin":"\\\\?","beginCaptures":{"1":{"name":"punctuation.definition.key-value.begin.yaml"}},"end":"(?=\\\\?)|^ *(:)|(:)","endCaptures":{"1":{"name":"punctuation.separator.key-value.mapping.yaml"},"2":{"name":"invalid.illegal.expected-newline.yaml"}},"name":"meta.block-mapping.yaml","patterns":[{"include":"#block-node"}]},{"begin":"(?=(?:[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S)([^:\\\\s]|:\\\\S|\\\\s+(?![#\\\\s]))*\\\\s*:(\\\\s|$))","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","patterns":[{"include":"#flow-scalar-plain-out-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S","beginCaptures":{"0":{"name":"entity.name.tag.yaml"}},"contentName":"entity.name.tag.yaml","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","name":"string.unquoted.plain.out.yaml"}]},{"match":":(?=\\\\s|$)","name":"punctuation.separator.key-value.mapping.yaml"}]},"block-scalar":{"begin":"(?:(\\\\|)|(>))([1-9])?([-+])?(.*\\\\n?)","beginCaptures":{"1":{"name":"keyword.control.flow.block-scalar.literal.yaml"},"2":{"name":"keyword.control.flow.block-scalar.folded.yaml"},"3":{"name":"constant.numeric.indentation-indicator.yaml"},"4":{"name":"storage.modifier.chomping-indicator.yaml"},"5":{"patterns":[{"include":"#comment"},{"match":".+","name":"invalid.illegal.expected-comment-or-newline.yaml"}]}},"end":"^(?=\\\\S)|(?!\\\\G)","patterns":[{"begin":"^( +)(?! )","end":"^(?!\\\\1|\\\\s*$)","name":"string.unquoted.block.yaml"}]},"block-sequence":{"match":"(-)(?!\\\\S)","name":"punctuation.definition.block.sequence.item.yaml"},"comment":{"begin":"(?:^([\\\\t ]*)|[\\\\t ]+)(?=#\\\\p{print}*$)","beginCaptures":{"1":{"name":"punctuation.whitespace.comment.leading.yaml"}},"end":"(?!\\\\G)","patterns":[{"begin":"#","beginCaptures":{"0":{"name":"punctuation.definition.comment.yaml"}},"end":"\\\\n","name":"comment.line.number-sign.yaml"}]},"directive":{"begin":"^%","beginCaptures":{"0":{"name":"punctuation.definition.directive.begin.yaml"}},"end":"(?=$|[\\\\t ]+($|#))","name":"meta.directive.yaml","patterns":[{"captures":{"1":{"name":"keyword.other.directive.yaml.yaml"},"2":{"name":"constant.numeric.yaml-version.yaml"}},"match":"\\\\G(YAML)[\\\\t ]+(\\\\d+\\\\.\\\\d+)"},{"captures":{"1":{"name":"keyword.other.directive.tag.yaml"},"2":{"name":"storage.type.tag-handle.yaml"},"3":{"name":"support.type.tag-prefix.yaml"}},"match":"\\\\G(TAG)(?:[\\\\t ]+(!(?:[-0-9A-Za-z]*!)?)(?:[\\\\t ]+(!(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])*|(?![]!,\\\\[{}])(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])+))?)?"},{"captures":{"1":{"name":"support.other.directive.reserved.yaml"},"2":{"name":"string.unquoted.directive-name.yaml"},"3":{"name":"string.unquoted.directive-parameter.yaml"}},"match":"\\\\G(\\\\w+)(?:[\\\\t ]+(\\\\w+)(?:[\\\\t ]+(\\\\w+))?)?"},{"match":"\\\\S+","name":"invalid.illegal.unrecognized.yaml"}]},"flow-alias":{"captures":{"1":{"name":"keyword.control.flow.alias.yaml"},"2":{"name":"punctuation.definition.alias.yaml"},"3":{"name":"variable.other.alias.yaml"},"4":{"name":"invalid.illegal.character.anchor.yaml"}},"match":"((\\\\*))([^],/\\\\[{}\\\\s]+)([^],}\\\\s]\\\\S*)?"},"flow-collection":{"patterns":[{"include":"#flow-sequence"},{"include":"#flow-mapping"}]},"flow-mapping":{"begin":"\\\\{","beginCaptures":{"0":{"name":"punctuation.definition.mapping.begin.yaml"}},"end":"}","endCaptures":{"0":{"name":"punctuation.definition.mapping.end.yaml"}},"name":"meta.flow-mapping.yaml","patterns":[{"include":"#prototype"},{"match":",","name":"punctuation.separator.mapping.yaml"},{"include":"#flow-pair"}]},"flow-node":{"patterns":[{"include":"#prototype"},{"include":"#flow-alias"},{"include":"#flow-collection"},{"include":"#flow-scalar"}]},"flow-pair":{"patterns":[{"begin":"\\\\?","beginCaptures":{"0":{"name":"punctuation.definition.key-value.begin.yaml"}},"end":"(?=[],}])","name":"meta.flow-pair.explicit.yaml","patterns":[{"include":"#prototype"},{"include":"#flow-pair"},{"include":"#flow-node"},{"begin":":(?=\\\\s|$|[],\\\\[{}])","beginCaptures":{"0":{"name":"punctuation.separator.key-value.mapping.yaml"}},"end":"(?=[],}])","patterns":[{"include":"#flow-value"}]}]},{"begin":"(?=(?:[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s])([^],:\\\\[{}\\\\s]|:[^],\\\\[{}\\\\s]|\\\\s+(?![#\\\\s]))*\\\\s*:(\\\\s|$))","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"meta.flow-pair.key.yaml","patterns":[{"include":"#flow-scalar-plain-in-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s]","beginCaptures":{"0":{"name":"entity.name.tag.yaml"}},"contentName":"entity.name.tag.yaml","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"string.unquoted.plain.in.yaml"}]},{"include":"#flow-node"},{"begin":":(?=\\\\s|$|[],\\\\[{}])","captures":{"0":{"name":"punctuation.separator.key-value.mapping.yaml"}},"end":"(?=[],}])","name":"meta.flow-pair.yaml","patterns":[{"include":"#flow-value"}]}]},"flow-scalar":{"patterns":[{"include":"#flow-scalar-double-quoted"},{"include":"#flow-scalar-single-quoted"},{"include":"#flow-scalar-plain-in"}]},"flow-scalar-double-quoted":{"begin":"\\"","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.yaml"}},"end":"\\"","endCaptures":{"0":{"name":"punctuation.definition.string.end.yaml"}},"name":"string.quoted.double.yaml","patterns":[{"match":"\\\\\\\\([ \\"/0LN\\\\\\\\_abefnprtv]|x\\\\d\\\\d|u\\\\d{4}|U\\\\d{8})","name":"constant.character.escape.yaml"},{"match":"\\\\\\\\\\\\n","name":"constant.character.escape.double-quoted.newline.yaml"}]},"flow-scalar-plain-in":{"patterns":[{"include":"#flow-scalar-plain-in-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?][^],\\\\[{}\\\\s]","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])","name":"string.unquoted.plain.in.yaml"}]},"flow-scalar-plain-in-implicit-type":{"patterns":[{"captures":{"1":{"name":"constant.language.null.yaml"},"2":{"name":"constant.language.boolean.yaml"},"3":{"name":"constant.numeric.integer.yaml"},"4":{"name":"constant.numeric.float.yaml"},"5":{"name":"constant.other.timestamp.yaml"},"6":{"name":"constant.language.value.yaml"},"7":{"name":"constant.language.merge.yaml"}},"match":"(?:(null|Null|NULL|~)|([Yy]|yes|Yes|YES|[Nn]|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|([-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[_\\\\h]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)|([-+]?(?:[0-9][0-9_]*)?\\\\.[.0-9]*(?:[Ee][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*|[-+]?\\\\.(?:inf|Inf|INF)|\\\\.(?:nan|NaN|NAN))|(\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(?:[Tt]|[\\\\t ]+)\\\\d{1,2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[\\\\t ]*Z|[-+]\\\\d{1,2}(?::\\\\d{1,2})?)?)|(=)|(<<))(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$)|\\\\s*:[],\\\\[{}]|\\\\s*[],\\\\[{}])"}]},"flow-scalar-plain-out":{"patterns":[{"include":"#flow-scalar-plain-out-implicit-type"},{"begin":"[^-\\\\]!\\"#%\\\\&'*,:>?@\\\\[\`{|}\\\\s]|[-:?]\\\\S","end":"(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))","name":"string.unquoted.plain.out.yaml"}]},"flow-scalar-plain-out-implicit-type":{"patterns":[{"captures":{"1":{"name":"constant.language.null.yaml"},"2":{"name":"constant.language.boolean.yaml"},"3":{"name":"constant.numeric.integer.yaml"},"4":{"name":"constant.numeric.float.yaml"},"5":{"name":"constant.other.timestamp.yaml"},"6":{"name":"constant.language.value.yaml"},"7":{"name":"constant.language.merge.yaml"}},"match":"(?:(null|Null|NULL|~)|([Yy]|yes|Yes|YES|[Nn]|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)|([-+]?0b[01_]+|[-+]?0[0-7_]+|[-+]?(?:0|[1-9][0-9_]*)|[-+]?0x[_\\\\h]+|[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)|([-+]?(?:[0-9][0-9_]*)?\\\\.[.0-9]*(?:[Ee][-+][0-9]+)?|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\\\.[0-9_]*|[-+]?\\\\.(?:inf|Inf|INF)|\\\\.(?:nan|NaN|NAN))|(\\\\d{4}-\\\\d{2}-\\\\d{2}|\\\\d{4}-\\\\d{1,2}-\\\\d{1,2}(?:[Tt]|[\\\\t ]+)\\\\d{1,2}:\\\\d{2}:\\\\d{2}(?:\\\\.\\\\d*)?(?:[\\\\t ]*Z|[-+]\\\\d{1,2}(?::\\\\d{1,2})?)?)|(=)|(<<))(?=\\\\s*$|\\\\s+#|\\\\s*:(\\\\s|$))"}]},"flow-scalar-single-quoted":{"begin":"'","beginCaptures":{"0":{"name":"punctuation.definition.string.begin.yaml"}},"end":"'(?!')","endCaptures":{"0":{"name":"punctuation.definition.string.end.yaml"}},"name":"string.quoted.single.yaml","patterns":[{"match":"''","name":"constant.character.escape.single-quoted.yaml"}]},"flow-sequence":{"begin":"\\\\[","beginCaptures":{"0":{"name":"punctuation.definition.sequence.begin.yaml"}},"end":"]","endCaptures":{"0":{"name":"punctuation.definition.sequence.end.yaml"}},"name":"meta.flow-sequence.yaml","patterns":[{"include":"#prototype"},{"match":",","name":"punctuation.separator.sequence.yaml"},{"include":"#flow-pair"},{"include":"#flow-node"}]},"flow-value":{"patterns":[{"begin":"\\\\G(?![],}])","end":"(?=[],}])","name":"meta.flow-pair.value.yaml","patterns":[{"include":"#flow-node"}]}]},"node":{"patterns":[{"include":"#block-node"}]},"property":{"begin":"(?=[!\\\\&])","end":"(?!\\\\G)","name":"meta.property.yaml","patterns":[{"captures":{"1":{"name":"keyword.control.property.anchor.yaml"},"2":{"name":"punctuation.definition.anchor.yaml"},"3":{"name":"entity.name.type.anchor.yaml"},"4":{"name":"invalid.illegal.character.anchor.yaml"}},"match":"\\\\G((&))([^],/\\\\[{}\\\\s]+)(\\\\S+)?"},{"match":"\\\\G!(?:<(?:%\\\\h{2}|[]!#$\\\\&-;=?-\\\\[_a-z~])+>|(?:[-0-9A-Za-z]*!)?(?:%\\\\h{2}|[#$\\\\&-+\\\\--;=?-Z_a-z~])+|)(?=[\\\\t ]|$)","name":"storage.type.tag-handle.yaml"},{"match":"\\\\S+","name":"invalid.illegal.tag-handle.yaml"}]},"prototype":{"patterns":[{"include":"#comment"},{"include":"#property"}]}},"scopeName":"source.yaml","aliases":["yml"]}`))],rl=Object.freeze(JSON.parse('{"colors":{"activityBar.activeBorder":"#f9826c","activityBar.background":"#fff","activityBar.border":"#e1e4e8","activityBar.foreground":"#2f363d","activityBar.inactiveForeground":"#959da5","activityBarBadge.background":"#2188ff","activityBarBadge.foreground":"#fff","badge.background":"#dbedff","badge.foreground":"#005cc5","breadcrumb.activeSelectionForeground":"#586069","breadcrumb.focusForeground":"#2f363d","breadcrumb.foreground":"#6a737d","breadcrumbPicker.background":"#fafbfc","button.background":"#159739","button.foreground":"#fff","button.hoverBackground":"#138934","button.secondaryBackground":"#e1e4e8","button.secondaryForeground":"#1b1f23","button.secondaryHoverBackground":"#d1d5da","checkbox.background":"#fafbfc","checkbox.border":"#d1d5da","debugToolBar.background":"#fff","descriptionForeground":"#6a737d","diffEditor.insertedTextBackground":"#34d05822","diffEditor.removedTextBackground":"#d73a4922","dropdown.background":"#fafbfc","dropdown.border":"#e1e4e8","dropdown.foreground":"#2f363d","dropdown.listBackground":"#fff","editor.background":"#fff","editor.findMatchBackground":"#ffdf5d","editor.findMatchHighlightBackground":"#ffdf5d66","editor.focusedStackFrameHighlightBackground":"#28a74525","editor.foldBackground":"#d1d5da11","editor.foreground":"#24292e","editor.inactiveSelectionBackground":"#0366d611","editor.lineHighlightBackground":"#f6f8fa","editor.linkedEditingBackground":"#0366d611","editor.selectionBackground":"#0366d625","editor.selectionHighlightBackground":"#34d05840","editor.selectionHighlightBorder":"#34d05800","editor.stackFrameHighlightBackground":"#ffd33d33","editor.wordHighlightBackground":"#34d05800","editor.wordHighlightBorder":"#24943e99","editor.wordHighlightStrongBackground":"#34d05800","editor.wordHighlightStrongBorder":"#24943e50","editorBracketHighlight.foreground1":"#005cc5","editorBracketHighlight.foreground2":"#e36209","editorBracketHighlight.foreground3":"#5a32a3","editorBracketHighlight.foreground4":"#005cc5","editorBracketHighlight.foreground5":"#e36209","editorBracketHighlight.foreground6":"#5a32a3","editorBracketMatch.background":"#34d05840","editorBracketMatch.border":"#34d05800","editorCursor.foreground":"#044289","editorError.foreground":"#cb2431","editorGroup.border":"#e1e4e8","editorGroupHeader.tabsBackground":"#f6f8fa","editorGroupHeader.tabsBorder":"#e1e4e8","editorGutter.addedBackground":"#28a745","editorGutter.deletedBackground":"#d73a49","editorGutter.modifiedBackground":"#2188ff","editorIndentGuide.activeBackground":"#d7dbe0","editorIndentGuide.background":"#eff2f6","editorLineNumber.activeForeground":"#24292e","editorLineNumber.foreground":"#1b1f234d","editorOverviewRuler.border":"#fff","editorWarning.foreground":"#f9c513","editorWhitespace.foreground":"#d1d5da","editorWidget.background":"#f6f8fa","errorForeground":"#cb2431","focusBorder":"#2188ff","foreground":"#444d56","gitDecoration.addedResourceForeground":"#28a745","gitDecoration.conflictingResourceForeground":"#e36209","gitDecoration.deletedResourceForeground":"#d73a49","gitDecoration.ignoredResourceForeground":"#959da5","gitDecoration.modifiedResourceForeground":"#005cc5","gitDecoration.submoduleResourceForeground":"#959da5","gitDecoration.untrackedResourceForeground":"#28a745","input.background":"#fafbfc","input.border":"#e1e4e8","input.foreground":"#2f363d","input.placeholderForeground":"#959da5","list.activeSelectionBackground":"#e2e5e9","list.activeSelectionForeground":"#2f363d","list.focusBackground":"#cce5ff","list.hoverBackground":"#ebf0f4","list.hoverForeground":"#2f363d","list.inactiveFocusBackground":"#dbedff","list.inactiveSelectionBackground":"#e8eaed","list.inactiveSelectionForeground":"#2f363d","notificationCenterHeader.background":"#e1e4e8","notificationCenterHeader.foreground":"#6a737d","notifications.background":"#fafbfc","notifications.border":"#e1e4e8","notifications.foreground":"#2f363d","notificationsErrorIcon.foreground":"#d73a49","notificationsInfoIcon.foreground":"#005cc5","notificationsWarningIcon.foreground":"#e36209","panel.background":"#f6f8fa","panel.border":"#e1e4e8","panelInput.border":"#e1e4e8","panelTitle.activeBorder":"#f9826c","panelTitle.activeForeground":"#2f363d","panelTitle.inactiveForeground":"#6a737d","pickerGroup.border":"#e1e4e8","pickerGroup.foreground":"#2f363d","progressBar.background":"#2188ff","quickInput.background":"#fafbfc","quickInput.foreground":"#2f363d","scrollbar.shadow":"#6a737d33","scrollbarSlider.activeBackground":"#959da588","scrollbarSlider.background":"#959da533","scrollbarSlider.hoverBackground":"#959da544","settings.headerForeground":"#2f363d","settings.modifiedItemIndicator":"#2188ff","sideBar.background":"#f6f8fa","sideBar.border":"#e1e4e8","sideBar.foreground":"#586069","sideBarSectionHeader.background":"#f6f8fa","sideBarSectionHeader.border":"#e1e4e8","sideBarSectionHeader.foreground":"#2f363d","sideBarTitle.foreground":"#2f363d","statusBar.background":"#fff","statusBar.border":"#e1e4e8","statusBar.debuggingBackground":"#f9826c","statusBar.debuggingForeground":"#fff","statusBar.foreground":"#586069","statusBar.noFolderBackground":"#fff","statusBarItem.prominentBackground":"#e8eaed","statusBarItem.remoteBackground":"#fff","statusBarItem.remoteForeground":"#586069","tab.activeBackground":"#fff","tab.activeBorder":"#fff","tab.activeBorderTop":"#f9826c","tab.activeForeground":"#2f363d","tab.border":"#e1e4e8","tab.hoverBackground":"#fff","tab.inactiveBackground":"#f6f8fa","tab.inactiveForeground":"#6a737d","tab.unfocusedActiveBorder":"#fff","tab.unfocusedActiveBorderTop":"#e1e4e8","tab.unfocusedHoverBackground":"#fff","terminal.ansiBlack":"#24292e","terminal.ansiBlue":"#0366d6","terminal.ansiBrightBlack":"#959da5","terminal.ansiBrightBlue":"#005cc5","terminal.ansiBrightCyan":"#3192aa","terminal.ansiBrightGreen":"#22863a","terminal.ansiBrightMagenta":"#5a32a3","terminal.ansiBrightRed":"#cb2431","terminal.ansiBrightWhite":"#d1d5da","terminal.ansiBrightYellow":"#b08800","terminal.ansiCyan":"#1b7c83","terminal.ansiGreen":"#28a745","terminal.ansiMagenta":"#5a32a3","terminal.ansiRed":"#d73a49","terminal.ansiWhite":"#6a737d","terminal.ansiYellow":"#dbab09","terminal.foreground":"#586069","terminal.tab.activeBorder":"#f9826c","terminalCursor.background":"#d1d5da","terminalCursor.foreground":"#005cc5","textBlockQuote.background":"#fafbfc","textBlockQuote.border":"#e1e4e8","textCodeBlock.background":"#f6f8fa","textLink.activeForeground":"#005cc5","textLink.foreground":"#0366d6","textPreformat.foreground":"#586069","textSeparator.foreground":"#d1d5da","titleBar.activeBackground":"#fff","titleBar.activeForeground":"#2f363d","titleBar.border":"#e1e4e8","titleBar.inactiveBackground":"#f6f8fa","titleBar.inactiveForeground":"#6a737d","tree.indentGuidesStroke":"#e1e4e8","welcomePage.buttonBackground":"#f6f8fa","welcomePage.buttonHoverBackground":"#e1e4e8"},"displayName":"GitHub Light","name":"github-light","semanticHighlighting":true,"tokenColors":[{"scope":["comment","punctuation.definition.comment","string.comment"],"settings":{"foreground":"#6a737d"}},{"scope":["constant","entity.name.constant","variable.other.constant","variable.other.enummember","variable.language"],"settings":{"foreground":"#005cc5"}},{"scope":["entity","entity.name"],"settings":{"foreground":"#6f42c1"}},{"scope":"variable.parameter.function","settings":{"foreground":"#24292e"}},{"scope":"entity.name.tag","settings":{"foreground":"#22863a"}},{"scope":"keyword","settings":{"foreground":"#d73a49"}},{"scope":["storage","storage.type"],"settings":{"foreground":"#d73a49"}},{"scope":["storage.modifier.package","storage.modifier.import","storage.type.java"],"settings":{"foreground":"#24292e"}},{"scope":["string","punctuation.definition.string","string punctuation.section.embedded source"],"settings":{"foreground":"#032f62"}},{"scope":"support","settings":{"foreground":"#005cc5"}},{"scope":"meta.property-name","settings":{"foreground":"#005cc5"}},{"scope":"variable","settings":{"foreground":"#e36209"}},{"scope":"variable.other","settings":{"foreground":"#24292e"}},{"scope":"invalid.broken","settings":{"fontStyle":"italic","foreground":"#b31d28"}},{"scope":"invalid.deprecated","settings":{"fontStyle":"italic","foreground":"#b31d28"}},{"scope":"invalid.illegal","settings":{"fontStyle":"italic","foreground":"#b31d28"}},{"scope":"invalid.unimplemented","settings":{"fontStyle":"italic","foreground":"#b31d28"}},{"scope":"carriage-return","settings":{"background":"#d73a49","content":"^M","fontStyle":"italic underline","foreground":"#fafbfc"}},{"scope":"message.error","settings":{"foreground":"#b31d28"}},{"scope":"string variable","settings":{"foreground":"#005cc5"}},{"scope":["source.regexp","string.regexp"],"settings":{"foreground":"#032f62"}},{"scope":["string.regexp.character-class","string.regexp constant.character.escape","string.regexp source.ruby.embedded","string.regexp string.regexp.arbitrary-repitition"],"settings":{"foreground":"#032f62"}},{"scope":"string.regexp constant.character.escape","settings":{"fontStyle":"bold","foreground":"#22863a"}},{"scope":"support.constant","settings":{"foreground":"#005cc5"}},{"scope":"support.variable","settings":{"foreground":"#005cc5"}},{"scope":"meta.module-reference","settings":{"foreground":"#005cc5"}},{"scope":"punctuation.definition.list.begin.markdown","settings":{"foreground":"#e36209"}},{"scope":["markup.heading","markup.heading entity.name"],"settings":{"fontStyle":"bold","foreground":"#005cc5"}},{"scope":"markup.quote","settings":{"foreground":"#22863a"}},{"scope":"markup.italic","settings":{"fontStyle":"italic","foreground":"#24292e"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#24292e"}},{"scope":["markup.underline"],"settings":{"fontStyle":"underline"}},{"scope":["markup.strikethrough"],"settings":{"fontStyle":"strikethrough"}},{"scope":"markup.inline.raw","settings":{"foreground":"#005cc5"}},{"scope":["markup.deleted","meta.diff.header.from-file","punctuation.definition.deleted"],"settings":{"background":"#ffeef0","foreground":"#b31d28"}},{"scope":["markup.inserted","meta.diff.header.to-file","punctuation.definition.inserted"],"settings":{"background":"#f0fff4","foreground":"#22863a"}},{"scope":["markup.changed","punctuation.definition.changed"],"settings":{"background":"#ffebda","foreground":"#e36209"}},{"scope":["markup.ignored","markup.untracked"],"settings":{"background":"#005cc5","foreground":"#f6f8fa"}},{"scope":"meta.diff.range","settings":{"fontStyle":"bold","foreground":"#6f42c1"}},{"scope":"meta.diff.header","settings":{"foreground":"#005cc5"}},{"scope":"meta.separator","settings":{"fontStyle":"bold","foreground":"#005cc5"}},{"scope":"meta.output","settings":{"foreground":"#005cc5"}},{"scope":["brackethighlighter.tag","brackethighlighter.curly","brackethighlighter.round","brackethighlighter.square","brackethighlighter.angle","brackethighlighter.quote"],"settings":{"foreground":"#586069"}},{"scope":"brackethighlighter.unmatched","settings":{"foreground":"#b31d28"}},{"scope":["constant.other.reference.link","string.other.link"],"settings":{"fontStyle":"underline","foreground":"#032f62"}}],"type":"light"}')),ol=Object.freeze(JSON.parse('{"colors":{"activityBar.activeBorder":"#f9826c","activityBar.background":"#24292e","activityBar.border":"#1b1f23","activityBar.foreground":"#e1e4e8","activityBar.inactiveForeground":"#6a737d","activityBarBadge.background":"#0366d6","activityBarBadge.foreground":"#fff","badge.background":"#044289","badge.foreground":"#c8e1ff","breadcrumb.activeSelectionForeground":"#d1d5da","breadcrumb.focusForeground":"#e1e4e8","breadcrumb.foreground":"#959da5","breadcrumbPicker.background":"#2b3036","button.background":"#176f2c","button.foreground":"#dcffe4","button.hoverBackground":"#22863a","button.secondaryBackground":"#444d56","button.secondaryForeground":"#fff","button.secondaryHoverBackground":"#586069","checkbox.background":"#444d56","checkbox.border":"#1b1f23","debugToolBar.background":"#2b3036","descriptionForeground":"#959da5","diffEditor.insertedTextBackground":"#28a74530","diffEditor.removedTextBackground":"#d73a4930","dropdown.background":"#2f363d","dropdown.border":"#1b1f23","dropdown.foreground":"#e1e4e8","dropdown.listBackground":"#24292e","editor.background":"#24292e","editor.findMatchBackground":"#ffd33d44","editor.findMatchHighlightBackground":"#ffd33d22","editor.focusedStackFrameHighlightBackground":"#2b6a3033","editor.foldBackground":"#58606915","editor.foreground":"#e1e4e8","editor.inactiveSelectionBackground":"#3392FF22","editor.lineHighlightBackground":"#2b3036","editor.linkedEditingBackground":"#3392FF22","editor.selectionBackground":"#3392FF44","editor.selectionHighlightBackground":"#17E5E633","editor.selectionHighlightBorder":"#17E5E600","editor.stackFrameHighlightBackground":"#C6902625","editor.wordHighlightBackground":"#17E5E600","editor.wordHighlightBorder":"#17E5E699","editor.wordHighlightStrongBackground":"#17E5E600","editor.wordHighlightStrongBorder":"#17E5E666","editorBracketHighlight.foreground1":"#79b8ff","editorBracketHighlight.foreground2":"#ffab70","editorBracketHighlight.foreground3":"#b392f0","editorBracketHighlight.foreground4":"#79b8ff","editorBracketHighlight.foreground5":"#ffab70","editorBracketHighlight.foreground6":"#b392f0","editorBracketMatch.background":"#17E5E650","editorBracketMatch.border":"#17E5E600","editorCursor.foreground":"#c8e1ff","editorError.foreground":"#f97583","editorGroup.border":"#1b1f23","editorGroupHeader.tabsBackground":"#1f2428","editorGroupHeader.tabsBorder":"#1b1f23","editorGutter.addedBackground":"#28a745","editorGutter.deletedBackground":"#ea4a5a","editorGutter.modifiedBackground":"#2188ff","editorIndentGuide.activeBackground":"#444d56","editorIndentGuide.background":"#2f363d","editorLineNumber.activeForeground":"#e1e4e8","editorLineNumber.foreground":"#444d56","editorOverviewRuler.border":"#1b1f23","editorWarning.foreground":"#ffea7f","editorWhitespace.foreground":"#444d56","editorWidget.background":"#1f2428","errorForeground":"#f97583","focusBorder":"#005cc5","foreground":"#d1d5da","gitDecoration.addedResourceForeground":"#34d058","gitDecoration.conflictingResourceForeground":"#ffab70","gitDecoration.deletedResourceForeground":"#ea4a5a","gitDecoration.ignoredResourceForeground":"#6a737d","gitDecoration.modifiedResourceForeground":"#79b8ff","gitDecoration.submoduleResourceForeground":"#6a737d","gitDecoration.untrackedResourceForeground":"#34d058","input.background":"#2f363d","input.border":"#1b1f23","input.foreground":"#e1e4e8","input.placeholderForeground":"#959da5","list.activeSelectionBackground":"#39414a","list.activeSelectionForeground":"#e1e4e8","list.focusBackground":"#044289","list.hoverBackground":"#282e34","list.hoverForeground":"#e1e4e8","list.inactiveFocusBackground":"#1d2d3e","list.inactiveSelectionBackground":"#282e34","list.inactiveSelectionForeground":"#e1e4e8","notificationCenterHeader.background":"#24292e","notificationCenterHeader.foreground":"#959da5","notifications.background":"#2f363d","notifications.border":"#1b1f23","notifications.foreground":"#e1e4e8","notificationsErrorIcon.foreground":"#ea4a5a","notificationsInfoIcon.foreground":"#79b8ff","notificationsWarningIcon.foreground":"#ffab70","panel.background":"#1f2428","panel.border":"#1b1f23","panelInput.border":"#2f363d","panelTitle.activeBorder":"#f9826c","panelTitle.activeForeground":"#e1e4e8","panelTitle.inactiveForeground":"#959da5","peekViewEditor.background":"#1f242888","peekViewEditor.matchHighlightBackground":"#ffd33d33","peekViewResult.background":"#1f2428","peekViewResult.matchHighlightBackground":"#ffd33d33","pickerGroup.border":"#444d56","pickerGroup.foreground":"#e1e4e8","progressBar.background":"#0366d6","quickInput.background":"#24292e","quickInput.foreground":"#e1e4e8","scrollbar.shadow":"#0008","scrollbarSlider.activeBackground":"#6a737d88","scrollbarSlider.background":"#6a737d33","scrollbarSlider.hoverBackground":"#6a737d44","settings.headerForeground":"#e1e4e8","settings.modifiedItemIndicator":"#0366d6","sideBar.background":"#1f2428","sideBar.border":"#1b1f23","sideBar.foreground":"#d1d5da","sideBarSectionHeader.background":"#1f2428","sideBarSectionHeader.border":"#1b1f23","sideBarSectionHeader.foreground":"#e1e4e8","sideBarTitle.foreground":"#e1e4e8","statusBar.background":"#24292e","statusBar.border":"#1b1f23","statusBar.debuggingBackground":"#931c06","statusBar.debuggingForeground":"#fff","statusBar.foreground":"#d1d5da","statusBar.noFolderBackground":"#24292e","statusBarItem.prominentBackground":"#282e34","statusBarItem.remoteBackground":"#24292e","statusBarItem.remoteForeground":"#d1d5da","tab.activeBackground":"#24292e","tab.activeBorder":"#24292e","tab.activeBorderTop":"#f9826c","tab.activeForeground":"#e1e4e8","tab.border":"#1b1f23","tab.hoverBackground":"#24292e","tab.inactiveBackground":"#1f2428","tab.inactiveForeground":"#959da5","tab.unfocusedActiveBorder":"#24292e","tab.unfocusedActiveBorderTop":"#1b1f23","tab.unfocusedHoverBackground":"#24292e","terminal.ansiBlack":"#586069","terminal.ansiBlue":"#2188ff","terminal.ansiBrightBlack":"#959da5","terminal.ansiBrightBlue":"#79b8ff","terminal.ansiBrightCyan":"#56d4dd","terminal.ansiBrightGreen":"#85e89d","terminal.ansiBrightMagenta":"#b392f0","terminal.ansiBrightRed":"#f97583","terminal.ansiBrightWhite":"#fafbfc","terminal.ansiBrightYellow":"#ffea7f","terminal.ansiCyan":"#39c5cf","terminal.ansiGreen":"#34d058","terminal.ansiMagenta":"#b392f0","terminal.ansiRed":"#ea4a5a","terminal.ansiWhite":"#d1d5da","terminal.ansiYellow":"#ffea7f","terminal.foreground":"#d1d5da","terminal.tab.activeBorder":"#f9826c","terminalCursor.background":"#586069","terminalCursor.foreground":"#79b8ff","textBlockQuote.background":"#24292e","textBlockQuote.border":"#444d56","textCodeBlock.background":"#2f363d","textLink.activeForeground":"#c8e1ff","textLink.foreground":"#79b8ff","textPreformat.foreground":"#d1d5da","textSeparator.foreground":"#586069","titleBar.activeBackground":"#24292e","titleBar.activeForeground":"#e1e4e8","titleBar.border":"#1b1f23","titleBar.inactiveBackground":"#1f2428","titleBar.inactiveForeground":"#959da5","tree.indentGuidesStroke":"#2f363d","welcomePage.buttonBackground":"#2f363d","welcomePage.buttonHoverBackground":"#444d56"},"displayName":"GitHub Dark","name":"github-dark","semanticHighlighting":true,"tokenColors":[{"scope":["comment","punctuation.definition.comment","string.comment"],"settings":{"foreground":"#6a737d"}},{"scope":["constant","entity.name.constant","variable.other.constant","variable.other.enummember","variable.language"],"settings":{"foreground":"#79b8ff"}},{"scope":["entity","entity.name"],"settings":{"foreground":"#b392f0"}},{"scope":"variable.parameter.function","settings":{"foreground":"#e1e4e8"}},{"scope":"entity.name.tag","settings":{"foreground":"#85e89d"}},{"scope":"keyword","settings":{"foreground":"#f97583"}},{"scope":["storage","storage.type"],"settings":{"foreground":"#f97583"}},{"scope":["storage.modifier.package","storage.modifier.import","storage.type.java"],"settings":{"foreground":"#e1e4e8"}},{"scope":["string","punctuation.definition.string","string punctuation.section.embedded source"],"settings":{"foreground":"#9ecbff"}},{"scope":"support","settings":{"foreground":"#79b8ff"}},{"scope":"meta.property-name","settings":{"foreground":"#79b8ff"}},{"scope":"variable","settings":{"foreground":"#ffab70"}},{"scope":"variable.other","settings":{"foreground":"#e1e4e8"}},{"scope":"invalid.broken","settings":{"fontStyle":"italic","foreground":"#fdaeb7"}},{"scope":"invalid.deprecated","settings":{"fontStyle":"italic","foreground":"#fdaeb7"}},{"scope":"invalid.illegal","settings":{"fontStyle":"italic","foreground":"#fdaeb7"}},{"scope":"invalid.unimplemented","settings":{"fontStyle":"italic","foreground":"#fdaeb7"}},{"scope":"carriage-return","settings":{"background":"#f97583","content":"^M","fontStyle":"italic underline","foreground":"#24292e"}},{"scope":"message.error","settings":{"foreground":"#fdaeb7"}},{"scope":"string variable","settings":{"foreground":"#79b8ff"}},{"scope":["source.regexp","string.regexp"],"settings":{"foreground":"#dbedff"}},{"scope":["string.regexp.character-class","string.regexp constant.character.escape","string.regexp source.ruby.embedded","string.regexp string.regexp.arbitrary-repitition"],"settings":{"foreground":"#dbedff"}},{"scope":"string.regexp constant.character.escape","settings":{"fontStyle":"bold","foreground":"#85e89d"}},{"scope":"support.constant","settings":{"foreground":"#79b8ff"}},{"scope":"support.variable","settings":{"foreground":"#79b8ff"}},{"scope":"meta.module-reference","settings":{"foreground":"#79b8ff"}},{"scope":"punctuation.definition.list.begin.markdown","settings":{"foreground":"#ffab70"}},{"scope":["markup.heading","markup.heading entity.name"],"settings":{"fontStyle":"bold","foreground":"#79b8ff"}},{"scope":"markup.quote","settings":{"foreground":"#85e89d"}},{"scope":"markup.italic","settings":{"fontStyle":"italic","foreground":"#e1e4e8"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#e1e4e8"}},{"scope":["markup.underline"],"settings":{"fontStyle":"underline"}},{"scope":["markup.strikethrough"],"settings":{"fontStyle":"strikethrough"}},{"scope":"markup.inline.raw","settings":{"foreground":"#79b8ff"}},{"scope":["markup.deleted","meta.diff.header.from-file","punctuation.definition.deleted"],"settings":{"background":"#86181d","foreground":"#fdaeb7"}},{"scope":["markup.inserted","meta.diff.header.to-file","punctuation.definition.inserted"],"settings":{"background":"#144620","foreground":"#85e89d"}},{"scope":["markup.changed","punctuation.definition.changed"],"settings":{"background":"#c24e00","foreground":"#ffab70"}},{"scope":["markup.ignored","markup.untracked"],"settings":{"background":"#79b8ff","foreground":"#2f363d"}},{"scope":"meta.diff.range","settings":{"fontStyle":"bold","foreground":"#b392f0"}},{"scope":"meta.diff.header","settings":{"foreground":"#79b8ff"}},{"scope":"meta.separator","settings":{"fontStyle":"bold","foreground":"#79b8ff"}},{"scope":"meta.output","settings":{"foreground":"#79b8ff"}},{"scope":["brackethighlighter.tag","brackethighlighter.curly","brackethighlighter.round","brackethighlighter.square","brackethighlighter.angle","brackethighlighter.quote"],"settings":{"foreground":"#d1d5da"}},{"scope":"brackethighlighter.unmatched","settings":{"foreground":"#fdaeb7"}},{"scope":["constant.other.reference.link","string.other.link"],"settings":{"fontStyle":"underline","foreground":"#dbedff"}}],"type":"dark"}')),cs=ao({engine:Uc(),langs:[...Zc,...rs,...Wc,...os,...Vc,...Xc,...Yc,...Jc,...Qc,...Kc,...el,...nl,...sl,...il],themes:[rl,ol]}),cl=new Set(["text","bash","css","html","javascript","json","jsx","markdown","powershell","python","sql","tsx","typescript","xml","yaml"]),ls={bat:"powershell",cjs:"javascript",console:"bash",cts:"typescript",dockerfile:"bash",env:"bash",htm:"html",js:"javascript",md:"markdown",mjs:"javascript",mts:"typescript",plain:"text",plaintext:"text",ps1:"powershell",pwsh:"powershell",py:"python",shell:"bash",shellscript:"bash",sh:"bash",svg:"xml",text:"text",ts:"typescript",txt:"text",vue:"html",xhtml:"html",xml:"xml",yml:"yaml",zsh:"bash"},ll=new Set(["github-light","github-dark"]);function us(n){const e=String(n||"").trim().toLowerCase();return e?e in ls?ls[e]:cl.has(e)?e:"text":"text"}function ul(n){const e=String(n||"").trim();return ll.has(e)?e:"github-light"}function ml(n){const t=(n.className||"").match(/language-([\w+#.-]+)/i);return t?t[1]:""}function ms(n,e,t){const a=ul(t);try{return cs.codeToHtml(String(n||""),{lang:us(e),theme:a})}catch(s){return console.warn("Failed to render T2I code block with Shiki.",s),cs.codeToHtml(String(n||""),{lang:"text",theme:a})}}function pl(n,e){n&&n.querySelectorAll("pre > code").forEach(t=>{const a=t.parentElement;!a||a.classList.contains("shiki")||(a.outerHTML=ms(t.textContent||"",ml(t),e))})}window.AstrBotT2IShiki=Object.freeze({highlightAllCodeBlocks:pl,normalizeLanguage:us,renderCodeToHtml:ms})})(); diff --git a/legacy-dashboard/scripts/build-t2i-shiki-runtime.mjs b/legacy-dashboard/scripts/build-t2i-shiki-runtime.mjs deleted file mode 100644 index 96f29cf4..00000000 --- a/legacy-dashboard/scripts/build-t2i-shiki-runtime.mjs +++ /dev/null @@ -1,228 +0,0 @@ -import { createRequire } from "node:module"; -import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; -import { tmpdir } from "node:os"; -import path from "node:path"; -import { fileURLToPath, pathToFileURL } from "node:url"; - -import { build } from "vite"; - -const require = createRequire(import.meta.url); -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const dashboardRoot = path.resolve(__dirname, ".."); -const runtimeOutputFile = path.resolve( - dashboardRoot, - "public", - "t2i", - "shiki_runtime.iife.js", -); -const shikiRequire = createRequire(require.resolve("shiki/package.json")); - -const languageSpecs = [ - ["bash", "bash"], - ["css", "css"], - ["html", "html"], - ["javascript", "javascript"], - ["json", "json"], - ["jsx", "jsx"], - ["markdown", "markdown"], - ["powershell", "powershell"], - ["python", "python"], - ["sql", "sql"], - ["tsx", "tsx"], - ["typescript", "typescript"], - ["xml", "xml"], - ["yaml", "yaml"], -]; - -const themeSpecs = [ - ["github-light", "github-light"], - ["github-dark", "github-dark"], -]; - -// Shiki exposes plain text as a built-in special language, so we keep it -// in the supported language list without importing a package for it. -const builtInLanguageSpecs = ["text"]; - -const languageAliases = { - bat: "powershell", - cjs: "javascript", - console: "bash", - cts: "typescript", - dockerfile: "bash", - env: "bash", - htm: "html", - js: "javascript", - md: "markdown", - mjs: "javascript", - mts: "typescript", - plain: "text", - plaintext: "text", - ps1: "powershell", - pwsh: "powershell", - py: "python", - shell: "bash", - shellscript: "bash", - sh: "bash", - svg: "xml", - text: "text", - ts: "typescript", - txt: "text", - vue: "html", - xhtml: "html", - xml: "xml", - yml: "yaml", - zsh: "bash", -}; - -function resolveShikiModule(specifier) { - return pathToFileURL(shikiRequire.resolve(specifier)).href; -} - -function buildVirtualSource() { - const shikiImport = JSON.stringify( - pathToFileURL(require.resolve("shiki")).href, - ); - const languageImports = languageSpecs - .map( - ([, packageName], index) => - `import lang${index} from ${JSON.stringify(resolveShikiModule(`@shikijs/langs/${packageName}`))};`, - ) - .join("\n"); - - const themeImports = themeSpecs - .map( - ([, packageName], index) => - `import theme${index} from ${JSON.stringify(resolveShikiModule(`@shikijs/themes/${packageName}`))};`, - ) - .join("\n"); - - const supportedLanguages = [ - ...builtInLanguageSpecs, - ...languageSpecs.map(([runtimeName]) => runtimeName), - ]; - - return `import { createHighlighterCoreSync, createJavaScriptRegexEngine } from ${shikiImport}; -${languageImports} -${themeImports} - -const highlighter = createHighlighterCoreSync({ - engine: createJavaScriptRegexEngine(), - langs: [${languageSpecs.map((_, index) => `...lang${index}`).join(", ")}], - themes: [${themeSpecs.map((_, index) => `theme${index}`).join(", ")}], -}); - -const supportedLanguages = new Set(${JSON.stringify(supportedLanguages)}); -const languageAliases = ${JSON.stringify(languageAliases)}; -const supportedThemes = new Set(${JSON.stringify(themeSpecs.map(([theme]) => theme))}); - -function normalizeLanguage(language) { - const normalized = String(language || "").trim().toLowerCase(); - if (!normalized) { - return "text"; - } - - if (normalized in languageAliases) { - return languageAliases[normalized]; - } - - return supportedLanguages.has(normalized) ? normalized : "text"; -} - -function normalizeTheme(theme) { - const normalized = String(theme || "").trim(); - return supportedThemes.has(normalized) ? normalized : "github-light"; -} - -function extractLanguage(codeElement) { - const className = codeElement.className || ""; - const match = className.match(/language-([\\w+#.-]+)/i); - return match ? match[1] : ""; -} - -function renderCodeToHtml(code, language, theme) { - const normalizedTheme = normalizeTheme(theme); - - try { - return highlighter.codeToHtml(String(code || ""), { - lang: normalizeLanguage(language), - theme: normalizedTheme, - }); - } catch (error) { - console.warn("Failed to render T2I code block with Shiki.", error); - return highlighter.codeToHtml(String(code || ""), { - lang: "text", - theme: normalizedTheme, - }); - } -} - -function highlightAllCodeBlocks(root, theme) { - if (!root) { - return; - } - - root.querySelectorAll("pre > code").forEach((codeElement) => { - const preElement = codeElement.parentElement; - if (!preElement || preElement.classList.contains("shiki")) { - return; - } - - preElement.outerHTML = renderCodeToHtml( - codeElement.textContent || "", - extractLanguage(codeElement), - theme, - ); - }); -} - -window.AstrBotT2IShiki = Object.freeze({ - highlightAllCodeBlocks, - normalizeLanguage, - renderCodeToHtml, -}); -`; -} - -async function main() { - const tempDir = mkdtempSync(path.join(tmpdir(), "astrbot-t2i-shiki-runtime-")); - const entryPath = path.join(tempDir, "entry.mjs"); - - writeFileSync(entryPath, buildVirtualSource(), "utf-8"); - - try { - await build({ - configFile: false, - logLevel: "info", - publicDir: false, - build: { - chunkSizeWarningLimit: 1500, - cssCodeSplit: false, - emptyOutDir: false, - lib: { - entry: entryPath, - fileName: () => path.basename(runtimeOutputFile), - formats: ["iife"], - name: "AstrBotT2IShikiRuntime", - }, - minify: "esbuild", - outDir: path.dirname(runtimeOutputFile), - rollupOptions: { - output: { - inlineDynamicImports: true, - }, - }, - sourcemap: false, - target: "es2018", - }, - }); - } finally { - rmSync(tempDir, { force: true, recursive: true }); - } - - console.log(`Built ${runtimeOutputFile}`); -} - -main().catch((error) => { - console.error(error); - process.exit(1); -}); diff --git a/legacy-dashboard/scripts/subset-mdi-font.mjs b/legacy-dashboard/scripts/subset-mdi-font.mjs deleted file mode 100644 index 6dd4f578..00000000 --- a/legacy-dashboard/scripts/subset-mdi-font.mjs +++ /dev/null @@ -1,330 +0,0 @@ -/** - * subset-mdi-font.mjs - * - * Build script that: - * 1. Scans src/ for all mdi-* icon names used in .vue/.ts files - * 2. Resolves their Unicode codepoints from @mdi/font CSS - * 3. Subsets the MDI font to include only those glyphs (via subset-font, pure JS) - * 4. Generates a minimal CSS file with only the needed icon classes - * 5. Outputs to src/assets/mdi-subset/ - * - * Fallback: if any step fails, copies the original full @mdi/font CSS and fonts - * so the build never breaks. - */ -import { readFileSync, writeFileSync, copyFileSync, readdirSync, statSync, existsSync, mkdirSync } from "fs"; -import { join, resolve, extname } from "path"; -import { fileURLToPath } from "url"; - -// Derive __dirname portably from import.meta.url (works across all Node ESM versions) -const __dirname = fileURLToPath(new URL(".", import.meta.url)); -const ROOT = resolve(__dirname, ".."); -const SRC = join(ROOT, "src"); -const MDI_CSS_PATH = join(ROOT, "node_modules/@mdi/font/css/materialdesignicons.css"); -const MDI_TTF_PATH = join(ROOT, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.ttf"); -const MDI_WOFF2_PATH = join(ROOT, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff2"); -const MDI_WOFF_PATH = join(ROOT, "node_modules/@mdi/font/fonts/materialdesignicons-webfont.woff"); -const OUT_DIR = join(ROOT, "src/assets/mdi-subset"); - -// Utility classes that should not be treated as icon names -const UTILITY_CLASSES = new Set([ - "mdi-set", "mdi-spin", "mdi-rotate-45", "mdi-rotate-90", "mdi-rotate-135", - "mdi-rotate-180", "mdi-rotate-225", "mdi-rotate-270", "mdi-rotate-315", - "mdi-flip-h", "mdi-flip-v", "mdi-light", "mdi-dark", "mdi-inactive", - "mdi-18px", "mdi-24px", "mdi-36px", "mdi-48px", -]); - -// Icons used indirectly by Vuetify internals, so they won't appear in src/ static scans. -export const REQUIRED_ICONS = new Set([ - "mdi-radiobox-blank", - "mdi-radiobox-marked", - "mdi-menu-down", - "mdi-menu-right", - "mdi-check-circle", - "mdi-information", - "mdi-alert-circle", - "mdi-close-circle", - "mdi-chevron-down", - "mdi-chevron-up", - "mdi-chevron-left", - "mdi-chevron-right", - "mdi-check", - "mdi-close", - "mdi-checkbox-marked", - "mdi-checkbox-blank-outline", - "mdi-minus-box", - "mdi-circle", - "mdi-arrow-up", - "mdi-arrow-down", - "mdi-menu", - "mdi-pencil", - "mdi-star-outline", - "mdi-star", - "mdi-star-half-full", - "mdi-cached", - "mdi-page-first", - "mdi-page-last", - "mdi-unfold-more-horizontal", - "mdi-paperclip", - "mdi-plus", - "mdi-minus", - "mdi-calendar", - "mdi-eyedropper", - "mdi-cloud-upload", -]); - -// Regex to match individual icon class definitions in MDI CSS -export const ICON_CLASS_PATTERN = /\.(mdi-[a-z][a-z0-9-]*)::before\s*\{\s*content:\s*"\\([0-9A-Fa-f]+)"\s*;?\s*}/g; - -// ── Helper functions ──────────────────────────────────────────────────────── - -/** Recursively collect files with given extensions, skipping node_modules. */ -export function* collectFiles(dir, exts) { - for (const entry of readdirSync(dir, { withFileTypes: true })) { - const full = join(dir, entry.name); - if (entry.isDirectory() && entry.name !== "node_modules") { - yield* collectFiles(full, exts); - } else if (exts.includes(extname(entry.name))) { - yield full; - } - } -} - -/** Scan source files and return a Set of used mdi-* icon names. */ -export function scanUsedIcons(sourceFiles) { - const iconPattern = /mdi-[a-z][a-z0-9-]*/g; - const usedIcons = new Set(REQUIRED_ICONS); - for (const file of sourceFiles) { - const content = readFileSync(file, "utf-8"); - for (const match of content.matchAll(iconPattern)) { - if (!UTILITY_CLASSES.has(match[0])) { - usedIcons.add(match[0]); - } - } - } - return usedIcons; -} - -/** Parse @mdi/font CSS and return a Map of icon-name → hex codepoint. */ -export function parseIconCodepoints(mdiCSS) { - const iconMap = new Map(); - for (const match of mdiCSS.matchAll(ICON_CLASS_PATTERN)) { - iconMap.set(match[1], match[2]); - } - return iconMap; -} - -/** Resolve used icons against the codepoint map, returning resolved/missing/subsetChars. */ -export function resolveUsedIcons(usedIcons, iconMap) { - const resolvedIcons = []; - const missingIcons = []; - const subsetChars = []; - for (const icon of usedIcons) { - const cp = iconMap.get(icon); - if (cp) { - resolvedIcons.push(icon); - subsetChars.push(String.fromCodePoint(parseInt(cp, 16))); - } else { - missingIcons.push(icon); - } - } - return { resolvedIcons, missingIcons, subsetChars }; -} - -/** - * Extract utility CSS rules (size, rotation, flip, spin, etc.) from the original MDI CSS. - * Uses a subtraction approach: removes the parts we regenerate (icon definitions, - * @font-face, base .mdi rules) and keeps everything else. This is more robust than - * relying on a fixed start marker, as it tolerates CSS reordering in future versions. - */ -export function extractUtilityCss(mdiCSS, iconClassPattern) { - let utilityCss = mdiCSS - .replace(iconClassPattern, "") // Remove icon definitions - .replace(/@font-face\s*\{[\s\S]*?}/g, "") // Remove @font-face - .replace(/\.mdi:before,\s*\.mdi-set\s*\{[\s\S]*?}/g, "") // Remove base rules - .replace(/\/\*# sourceMappingURL=.*\*\//, "") // Remove source map - .trim(); - - // Clean up excess blank lines left after removals - utilityCss = utilityCss.replace(/(\r\n|\n){3,}/g, "\n\n"); - - return utilityCss; -} - -/** Build a fallback CSS that rewrites font URLs to use subset filenames. */ -function buildFallbackCss() { - const mdiCSS = readFileSync(MDI_CSS_PATH, "utf-8"); - return mdiCSS - // Rewrite woff/woff2 URLs to point at subset filenames - .replace(/url\("\.\.\/fonts\/materialdesignicons-webfont\.(woff2?)\?[^"]*"\)/g, - (_, ext) => `url("./materialdesignicons-webfont-subset.${ext}")`) - // Remove legacy eot/ttf sources - .replace(/url\("\.\.\/fonts\/materialdesignicons-webfont\.(eot|ttf)\?[^"]*"\)[^,]*/g, "") - // Clean up dangling commas/separators - .replace(/src:\s*,/g, "src:") - .replace(/,\s*;/g, ";"); -} - -// ── Fallback: copy original full MDI font if subsetting fails ─────────────── -function fallbackToFullFont(reason) { - console.warn(`\n⚠️ Subsetting failed: ${reason}`); - console.warn(`⚠️ Falling back to full @mdi/font (build will not break)\n`); - - // Copy original font files - if (existsSync(MDI_WOFF2_PATH)) { - copyFileSync(MDI_WOFF2_PATH, join(OUT_DIR, "materialdesignicons-webfont-subset.woff2")); - } - if (existsSync(MDI_WOFF_PATH)) { - copyFileSync(MDI_WOFF_PATH, join(OUT_DIR, "materialdesignicons-webfont-subset.woff")); - } - - writeFileSync(join(OUT_DIR, "materialdesignicons-subset.css"), buildFallbackCss()); - - const size = existsSync(MDI_WOFF2_PATH) ? statSync(MDI_WOFF2_PATH).size : 0; - console.warn(`⚠️ Fallback complete: using full font (${(size / 1024).toFixed(1)} KB woff2)`); -} - -// ── Exported entry point ──────────────────────────────────────────────────── - -export async function runMdiSubset() { - mkdirSync(OUT_DIR, { recursive: true }); - - try { - // Pre-checks - if (!existsSync(MDI_CSS_PATH)) { - throw new Error(`@mdi/font CSS not found at ${MDI_CSS_PATH}. Run 'pnpm install' first.`); - } - if (!existsSync(MDI_TTF_PATH)) { - throw new Error(`@mdi/font TTF not found at ${MDI_TTF_PATH}. Run 'pnpm install' first.`); - } - - // Dynamic import subset-font (may not be installed in all environments) - let subsetFont; - try { - subsetFont = (await import("subset-font")).default; - } catch (e) { - throw new Error(`subset-font package not available: ${e.message}. Run 'pnpm install' first.`); - } - - // Step 1: Scan source files for mdi-* icon names - const sourceFiles = collectFiles(SRC, [".vue", ".ts", ".js"]); - const usedIcons = scanUsedIcons(sourceFiles); - if (usedIcons.size === 0) { - throw new Error("No mdi-* icons found in source files. Something is wrong with scanning."); - } - console.log(`✅ Found ${usedIcons.size} unique mdi-* icons in source files`); - - // Step 2: Parse @mdi/font CSS to get codepoints for each icon - const mdiCSS = readFileSync(MDI_CSS_PATH, "utf-8"); - const iconMap = parseIconCodepoints(mdiCSS); - if (iconMap.size === 0) { - throw new Error("Could not parse any icon definitions from @mdi/font CSS. Format may have changed."); - } - console.log(`📦 MDI font CSS contains ${iconMap.size} icon definitions`); - - // Step 3: Resolve codepoints for used icons - const { resolvedIcons, missingIcons, subsetChars } = resolveUsedIcons(usedIcons, iconMap); - if (missingIcons.length > 0) { - console.warn(`⚠️ ${missingIcons.length} icons not found in MDI CSS:`, missingIcons.join(", ")); - } - if (resolvedIcons.length === 0) { - throw new Error("No icon codepoints could be resolved. Icon name format may have changed."); - } - console.log(`🔍 Resolved ${resolvedIcons.length} codepoints for subsetting`); - - // Add space character - subsetChars.push(" "); - const subsetText = subsetChars.join(""); - - // Step 4: Subset font with subset-font (pure JS/WASM) - const fontBuffer = readFileSync(MDI_TTF_PATH); - - console.log(`🔧 Subsetting font to woff2...`); - const woff2Buffer = await subsetFont(fontBuffer, subsetText, { - targetFormat: "woff2", - }); - - console.log(`🔧 Subsetting font to woff...`); - const woffBuffer = await subsetFont(fontBuffer, subsetText, { - targetFormat: "woff", - }); - - if (woff2Buffer.length === 0 || woffBuffer.length === 0) { - throw new Error("subset-font produced empty output. Font file may be corrupted."); - } - - const outWoff2 = join(OUT_DIR, "materialdesignicons-webfont-subset.woff2"); - const outWoff = join(OUT_DIR, "materialdesignicons-webfont-subset.woff"); - writeFileSync(outWoff2, woff2Buffer); - writeFileSync(outWoff, woffBuffer); - - // Step 5: Generate subset CSS - let css = `/* Auto-generated MDI subset – ${resolvedIcons.length} icons */ -/* Do not edit manually. Run: pnpm run subset-icons */ - -@font-face { - font-family: "Material Design Icons"; - src: url("./materialdesignicons-webfont-subset.woff2") format("woff2"), - url("./materialdesignicons-webfont-subset.woff") format("woff"); - font-weight: normal; - font-style: normal; -} - -.mdi:before, -.mdi-set { - display: inline-block; - font: normal normal normal 24px/1 "Material Design Icons"; - font-size: inherit; - text-rendering: auto; - line-height: inherit; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -`; - - for (const icon of resolvedIcons.sort()) { - const cp = iconMap.get(icon); - css += `.${icon}::before {\n content: "\\${cp}";\n}\n\n`; - } - - const utilityCss = extractUtilityCss(mdiCSS, ICON_CLASS_PATTERN); - if (utilityCss) { - css += `/* Utility classes (extracted from @mdi/font) */\n${utilityCss}\n`; - } else { - console.warn("⚠️ Could not find MDI utility classes in original CSS, skipping"); - } - - const outCSS = join(OUT_DIR, "materialdesignicons-subset.css"); - writeFileSync(outCSS, css); - - // Report - const origSize = statSync(MDI_TTF_PATH).size; - const subsetWoff2Size = woff2Buffer.length; - console.log(`\n📊 Results:`); - console.log(` Original TTF font: ${(origSize / 1024).toFixed(1)} KB`); - console.log(` Subset WOFF2: ${(subsetWoff2Size / 1024).toFixed(1)} KB`); - console.log(` Reduction: ${((1 - subsetWoff2Size / origSize) * 100).toFixed(1)}%`); - console.log(` Icons included: ${resolvedIcons.length}`); - console.log(` CSS file: ${outCSS}`); - console.log(`\n✅ MDI font subset generated successfully!`); - - } catch (err) { - // Fallback: any failure → use original full font so build never breaks - try { - fallbackToFullFont(err.message); - } catch (fallbackErr) { - console.error(`❌ Fallback also failed: ${fallbackErr.message}`); - console.error(`❌ Please ensure @mdi/font is installed: pnpm install`); - throw fallbackErr; - } - } -} - -// ── CLI entry point: allows running directly via `node scripts/subset-mdi-font.mjs` ── - -if (import.meta.url.startsWith('file:') && process.argv[1] === fileURLToPath(import.meta.url)) { - runMdiSubset().catch(err => { - console.error(err); - process.exit(1); - }); -} diff --git a/legacy-dashboard/src/App.vue b/legacy-dashboard/src/App.vue deleted file mode 100644 index 579fec28..00000000 --- a/legacy-dashboard/src/App.vue +++ /dev/null @@ -1,67 +0,0 @@ -<template> - <RouterView></RouterView> - <WaitingForRestart ref="globalWaitingRef" /> - <UpgradeRecoveryDialog /> - - <!-- 全局唯一 snackbar --> - <v-snackbar v-if="toastStore.current" v-model="snackbarShow" :color="toastStore.current.color" - :timeout="toastStore.current.timeout" :multi-line="toastStore.current.multiLine" - :location="toastStore.current.location" close-on-back> - <div class="app-snackbar-message"> - <v-icon v-if="toastIcon" :icon="toastIcon" size="24" /> - <span>{{ toastStore.current.message }}</span> - </div> - <template #actions v-if="toastStore.current.closable"> - <v-btn icon="mdi-close" variant="text" size="small" aria-label="Close notification" @click="snackbarShow = false" /> - </template> - </v-snackbar> -</template> - -<script setup> -import { RouterView } from 'vue-router'; -import { computed, onBeforeUnmount, onMounted, ref } from 'vue' -import { useToastStore } from '@/stores/toast' -import WaitingForRestart from '@/components/shared/WaitingForRestart.vue' -import UpgradeRecoveryDialog from '@/components/shared/UpgradeRecoveryDialog.vue' - -const toastStore = useToastStore() -const globalWaitingRef = ref(null) -let disposeTrayRestartListener = null - -const snackbarShow = computed({ - get: () => !!toastStore.current, - set: (val) => { - if (!val) toastStore.shift() - } -}) - -const toastIcon = computed(() => ({ - success: 'mdi-check-circle-outline', - error: 'mdi-alert-circle-outline', - warning: 'mdi-alert-outline', - info: 'mdi-information-outline', - primary: 'mdi-information-outline' -})[toastStore.current?.color] || '') - -onMounted(() => { - const desktopBridge = window.astrbotDesktop - if (!desktopBridge?.onTrayRestartBackend) { - return - } - disposeTrayRestartListener = desktopBridge.onTrayRestartBackend(async () => { - try { - await globalWaitingRef.value?.check?.() - } catch (error) { - globalWaitingRef.value?.stop?.() - console.error('Tray restart backend failed:', error) - } - }) -}) - -onBeforeUnmount(() => { - if (disposeTrayRestartListener) { - disposeTrayRestartListener() - disposeTrayRestartListener = null - } -}) -</script> diff --git a/legacy-dashboard/src/api/generated/openapi-v1/index.ts b/legacy-dashboard/src/api/generated/openapi-v1/index.ts deleted file mode 100644 index 81abc822..00000000 --- a/legacy-dashboard/src/api/generated/openapi-v1/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts -export * from './sdk.gen'; -export * from './types.gen'; \ No newline at end of file diff --git a/legacy-dashboard/src/api/generated/openapi-v1/sdk.gen.ts b/legacy-dashboard/src/api/generated/openapi-v1/sdk.gen.ts deleted file mode 100644 index a75ccf57..00000000 --- a/legacy-dashboard/src/api/generated/openapi-v1/sdk.gen.ts +++ /dev/null @@ -1,3096 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -import { createClient, createConfig, type OptionsLegacyParser, formDataBodySerializer } from '@hey-api/client-axios'; -import type { LoginData, LoginError, LoginResponse, LogoutError, LogoutResponse, GetAuthSetupStatusError, GetAuthSetupStatusResponse, SetupAuthData, SetupAuthError, SetupAuthResponse, SetupTotpData, SetupTotpError, SetupTotpResponse, RecoverTotpError, RecoverTotpResponse, UpdateAuthAccountData, UpdateAuthAccountError, UpdateAuthAccountResponse, ListApiKeysError, ListApiKeysResponse, CreateApiKeyData, CreateApiKeyError, CreateApiKeyResponse, RevokeApiKeyData, RevokeApiKeyError, RevokeApiKeyResponse, DeleteApiKeyData, DeleteApiKeyError, DeleteApiKeyResponse, GetSystemConfigSchemaError, GetSystemConfigSchemaResponse, GetSystemConfigError, GetSystemConfigResponse, UpdateSystemConfigData, UpdateSystemConfigError, UpdateSystemConfigResponse, GetSystemConfigRuntimeError, GetSystemConfigRuntimeResponse, GetConfigProfileSchemaError, GetConfigProfileSchemaResponse, ListConfigProfilesError, ListConfigProfilesResponse, CreateConfigProfileData, CreateConfigProfileError, CreateConfigProfileResponse, GetConfigProfileData, GetConfigProfileError, GetConfigProfileResponse, UpdateConfigProfileContentData, UpdateConfigProfileContentError, UpdateConfigProfileContentResponse, RenameConfigProfileData, RenameConfigProfileError, RenameConfigProfileResponse, DeleteConfigProfileData, DeleteConfigProfileError, DeleteConfigProfileResponse, ListConfigRoutesError, ListConfigRoutesResponse, ReplaceConfigRoutesData, ReplaceConfigRoutesError, ReplaceConfigRoutesResponse, UpsertConfigRouteData, UpsertConfigRouteError, UpsertConfigRouteResponse, DeleteConfigRouteData, DeleteConfigRouteError, DeleteConfigRouteResponse, ListBotTypesError, ListBotTypesResponse, RegisterBotTypeData, RegisterBotTypeError, RegisterBotTypeResponse, ListBotsData, ListBotsError, ListBotsResponse, CreateBotData, CreateBotError, CreateBotResponse, ListBotStatsError, ListBotStatsResponse, GetBotByIdData, GetBotByIdError, GetBotByIdResponse, UpdateBotByIdData, UpdateBotByIdError, UpdateBotByIdResponse, DeleteBotByIdData, DeleteBotByIdError, DeleteBotByIdResponse, SetBotEnabledByIdData, SetBotEnabledByIdError, SetBotEnabledByIdResponse, TestBotByIdData, TestBotByIdError, TestBotByIdResponse, GetBotData, GetBotError, GetBotResponse, UpdateBotData, UpdateBotError, UpdateBotResponse, DeleteBotData, DeleteBotError, DeleteBotResponse, SetBotEnabledData, SetBotEnabledError, SetBotEnabledResponse, TestBotData, TestBotError, TestBotResponse, GetProviderSchemaError, GetProviderSchemaResponse, ListProviderSourcesError, ListProviderSourcesResponse, CreateProviderSourceData, CreateProviderSourceError, CreateProviderSourceResponse, GetProviderSourceByIdData, GetProviderSourceByIdError, GetProviderSourceByIdResponse, UpsertProviderSourceByIdData, UpsertProviderSourceByIdError, UpsertProviderSourceByIdResponse, DeleteProviderSourceByIdData, DeleteProviderSourceByIdError, DeleteProviderSourceByIdResponse, ListProviderSourceModelsByIdData, ListProviderSourceModelsByIdError, ListProviderSourceModelsByIdResponse, ListProvidersBySourceIdData, ListProvidersBySourceIdError, ListProvidersBySourceIdResponse, CreateProviderInSourceByIdData, CreateProviderInSourceByIdError, CreateProviderInSourceByIdResponse, GetProviderSourceData, GetProviderSourceError, GetProviderSourceResponse, UpsertProviderSourceData, UpsertProviderSourceError, UpsertProviderSourceResponse, DeleteProviderSourceData, DeleteProviderSourceError, DeleteProviderSourceResponse, ListProviderSourceModelsData, ListProviderSourceModelsError, ListProviderSourceModelsResponse, ListProvidersBySourceData, ListProvidersBySourceError, ListProvidersBySourceResponse, CreateProviderInSourceData, CreateProviderInSourceError, CreateProviderInSourceResponse, ListProvidersData, ListProvidersError, ListProvidersResponse, CreateProviderData, CreateProviderError, CreateProviderResponse, GetProviderByIdData, GetProviderByIdError, GetProviderByIdResponse, UpdateProviderByIdData, UpdateProviderByIdError, UpdateProviderByIdResponse, DeleteProviderByIdData, DeleteProviderByIdError, DeleteProviderByIdResponse, SetProviderEnabledByIdData, SetProviderEnabledByIdError, SetProviderEnabledByIdResponse, TestProviderByIdData, TestProviderByIdError, TestProviderByIdResponse, GetProviderEmbeddingDimensionByIdData, GetProviderEmbeddingDimensionByIdError, GetProviderEmbeddingDimensionByIdResponse, GetProviderData, GetProviderError, GetProviderResponse, UpdateProviderData, UpdateProviderError, UpdateProviderResponse, DeleteProviderData, DeleteProviderError, DeleteProviderResponse, SetProviderEnabledData, SetProviderEnabledError, SetProviderEnabledResponse, TestProviderData, TestProviderError, TestProviderResponse, GetProviderEmbeddingDimensionData, GetProviderEmbeddingDimensionError, GetProviderEmbeddingDimensionResponse, SendChatMessageData, SendChatMessageError, SendChatMessageResponse, OpenChatWebSocketData, OpenLiveChatWebSocketData, OpenUnifiedChatWebSocketData, ListChatSessionsData, ListChatSessionsError, ListChatSessionsResponse, CreateChatSessionData, CreateChatSessionError, CreateChatSessionResponse, BatchDeleteChatSessionsData, BatchDeleteChatSessionsError, BatchDeleteChatSessionsResponse, GetChatSessionData, GetChatSessionError, GetChatSessionResponse, UpdateChatSessionData, UpdateChatSessionError, UpdateChatSessionResponse, DeleteChatSessionData, DeleteChatSessionError, DeleteChatSessionResponse, StopChatSessionData, StopChatSessionError, StopChatSessionResponse, UpdateChatMessageData, UpdateChatMessageError, UpdateChatMessageResponse, RegenerateChatMessageData, RegenerateChatMessageError, RegenerateChatMessageResponse, ListChatConfigsError, ListChatConfigsResponse, CreateChatThreadData, CreateChatThreadError, CreateChatThreadResponse, GetChatThreadData, GetChatThreadError, GetChatThreadResponse, DeleteChatThreadData, DeleteChatThreadError, DeleteChatThreadResponse, SendChatThreadMessageData, SendChatThreadMessageError, SendChatThreadMessageResponse, ListChatProjectsError, ListChatProjectsResponse, CreateChatProjectData, CreateChatProjectError, CreateChatProjectResponse, GetChatProjectData, GetChatProjectError, GetChatProjectResponse, UpdateChatProjectData, UpdateChatProjectError, UpdateChatProjectResponse, DeleteChatProjectData, DeleteChatProjectError, DeleteChatProjectResponse, ListChatProjectSessionsData, ListChatProjectSessionsError, ListChatProjectSessionsResponse, AddChatProjectSessionData, AddChatProjectSessionError, AddChatProjectSessionResponse, RemoveChatProjectSessionData, RemoveChatProjectSessionError, RemoveChatProjectSessionResponse, SendImMessageData, SendImMessageError, SendImMessageResponse, ListImBotsError, ListImBotsResponse, UploadFileData, UploadFileError, UploadFileResponse, UploadOpenApiFileData, UploadOpenApiFileError, UploadOpenApiFileResponse, DownloadOpenApiFileData, DownloadOpenApiFileError, DownloadOpenApiFileResponse, GetFileByNameData, GetFileByNameError, GetFileByNameResponse, GetTokenFileData, GetTokenFileError, GetTokenFileResponse, GetAttachmentData, GetAttachmentError, GetAttachmentResponse, DeleteAttachmentData, DeleteAttachmentError, DeleteAttachmentResponse, DownloadAttachmentData, DownloadAttachmentError, DownloadAttachmentResponse, ListPluginsData, ListPluginsError, ListPluginsResponse, GetPluginByIdData, GetPluginByIdError, GetPluginByIdResponse, UninstallPluginByIdData, UninstallPluginByIdError, UninstallPluginByIdResponse, GetPluginConfigByIdData, GetPluginConfigByIdError, GetPluginConfigByIdResponse, UpdatePluginConfigByIdData, UpdatePluginConfigByIdError, UpdatePluginConfigByIdResponse, GetPluginConfigSchemaByIdData, GetPluginConfigSchemaByIdError, GetPluginConfigSchemaByIdResponse, ListPluginConfigFilesByIdData, ListPluginConfigFilesByIdError, ListPluginConfigFilesByIdResponse, UploadPluginConfigFilesByIdData, UploadPluginConfigFilesByIdError, UploadPluginConfigFilesByIdResponse, DeletePluginConfigFileByIdData, DeletePluginConfigFileByIdError, DeletePluginConfigFileByIdResponse, GetPluginReadmeByIdData, GetPluginReadmeByIdError, GetPluginReadmeByIdResponse, GetPluginChangelogByIdData, GetPluginChangelogByIdError, GetPluginChangelogByIdResponse, ReloadPluginByIdData, ReloadPluginByIdError, ReloadPluginByIdResponse, SetPluginEnabledByIdData, SetPluginEnabledByIdError, SetPluginEnabledByIdResponse, ListPluginPagesByIdData, ListPluginPagesByIdError, ListPluginPagesByIdResponse, GetPluginPageByIdData, GetPluginPageByIdError, GetPluginPageByIdResponse, GetPluginPageAssetByIdData, GetPluginPageAssetByIdError, GetPluginPageAssetByIdResponse, GetPluginData, GetPluginError, GetPluginResponse, UninstallPluginData, UninstallPluginError, UninstallPluginResponse, GetPluginConfigData, GetPluginConfigError, GetPluginConfigResponse, UpdatePluginConfigData, UpdatePluginConfigError, UpdatePluginConfigResponse, GetPluginConfigSchemaData, GetPluginConfigSchemaError, GetPluginConfigSchemaResponse, ListPluginConfigFilesData, ListPluginConfigFilesError, ListPluginConfigFilesResponse, UploadPluginConfigFilesData, UploadPluginConfigFilesError, UploadPluginConfigFilesResponse, DeletePluginConfigFileData, DeletePluginConfigFileError, DeletePluginConfigFileResponse, GetPluginReadmeData, GetPluginReadmeError, GetPluginReadmeResponse, GetPluginChangelogData, GetPluginChangelogError, GetPluginChangelogResponse, ReloadPluginData, ReloadPluginError, ReloadPluginResponse, BindPluginSourceData, BindPluginSourceError, BindPluginSourceResponse, SetPluginEnabledData, SetPluginEnabledError, SetPluginEnabledResponse, UpdatePluginData, UpdatePluginError, UpdatePluginResponse, UpdatePluginsData, UpdatePluginsError, UpdatePluginsResponse, CheckPluginVersionSupportData, CheckPluginVersionSupportError, CheckPluginVersionSupportResponse, ValidatePluginRepoData, ValidatePluginRepoError, ValidatePluginRepoResponse, ListFailedPluginsError, ListFailedPluginsResponse, UninstallFailedPluginData, UninstallFailedPluginError, UninstallFailedPluginResponse, ReloadFailedPluginData, ReloadFailedPluginError, ReloadFailedPluginResponse, InstallPluginFromGithubData, InstallPluginFromGithubError, InstallPluginFromGithubResponse, InstallPluginFromUrlData, InstallPluginFromUrlError, InstallPluginFromUrlResponse, InstallPluginFromUploadData, InstallPluginFromUploadError, InstallPluginFromUploadResponse, ListPluginMarketData, ListPluginMarketError, ListPluginMarketResponse, ListPluginMarketCategoriesError, ListPluginMarketCategoriesResponse, ListPluginSourcesError, ListPluginSourcesResponse, CreatePluginSourceData, CreatePluginSourceError, CreatePluginSourceResponse, ReplacePluginSourcesData, ReplacePluginSourcesError, ReplacePluginSourcesResponse, DeletePluginSourceData, DeletePluginSourceError, DeletePluginSourceResponse, DeletePluginSourceByIdData, DeletePluginSourceByIdError, DeletePluginSourceByIdResponse, ListPluginPagesData, ListPluginPagesError, ListPluginPagesResponse, GetPluginPageData, GetPluginPageError, GetPluginPageResponse, GetPluginPageAssetData, GetPluginPageAssetError, GetPluginPageAssetResponse, GetPluginPageBridgeSdkError, GetPluginPageBridgeSdkResponse, GetPluginExtensionRouteData, GetPluginExtensionRouteError, GetPluginExtensionRouteResponse, PostPluginExtensionRouteData, PostPluginExtensionRouteError, PostPluginExtensionRouteResponse, PutPluginExtensionRouteData, PutPluginExtensionRouteError, PutPluginExtensionRouteResponse, PatchPluginExtensionRouteData, PatchPluginExtensionRouteError, PatchPluginExtensionRouteResponse, DeletePluginExtensionRouteData, DeletePluginExtensionRouteError, DeletePluginExtensionRouteResponse, ListCommandsData, ListCommandsError, ListCommandsResponse, UpdateCommandData, UpdateCommandError, UpdateCommandResponse, ListCommandConflictsError, ListCommandConflictsResponse, ListToolsData, ListToolsError, ListToolsResponse, SetToolEnabledData, SetToolEnabledError, SetToolEnabledResponse, SetToolPermissionData, SetToolPermissionError, SetToolPermissionResponse, ListMcpServersError, ListMcpServersResponse, CreateMcpServerData, CreateMcpServerError, CreateMcpServerResponse, UpdateMcpServerByNameData, UpdateMcpServerByNameError, UpdateMcpServerByNameResponse, DeleteMcpServerByNameData, DeleteMcpServerByNameError, DeleteMcpServerByNameResponse, SetMcpServerEnabledByNameData, SetMcpServerEnabledByNameError, SetMcpServerEnabledByNameResponse, TestMcpServerByNameData, TestMcpServerByNameError, TestMcpServerByNameResponse, UpdateMcpServerData, UpdateMcpServerError, UpdateMcpServerResponse, DeleteMcpServerData, DeleteMcpServerError, DeleteMcpServerResponse, SetMcpServerEnabledData, SetMcpServerEnabledError, SetMcpServerEnabledResponse, TestMcpServerData, TestMcpServerError, TestMcpServerResponse, SyncModelScopeMcpServersData, SyncModelScopeMcpServersError, SyncModelScopeMcpServersResponse, ListSkillsData, ListSkillsError, ListSkillsResponse, UploadSkillData, UploadSkillError, UploadSkillResponse, UploadSkillsBatchData, UploadSkillsBatchError, UploadSkillsBatchResponse, UpdateSkillByNameData, UpdateSkillByNameError, UpdateSkillByNameResponse, DeleteSkillByNameData, DeleteSkillByNameError, DeleteSkillByNameResponse, DownloadSkillByNameData, DownloadSkillByNameError, DownloadSkillByNameResponse, ListSkillFilesByNameData, ListSkillFilesByNameError, ListSkillFilesByNameResponse, GetSkillFileByNameData, GetSkillFileByNameError, GetSkillFileByNameResponse, UpdateSkillFileByNameData, UpdateSkillFileByNameError, UpdateSkillFileByNameResponse, UpdateSkillData, UpdateSkillError, UpdateSkillResponse, DeleteSkillData, DeleteSkillError, DeleteSkillResponse, DownloadSkillData, DownloadSkillError, DownloadSkillResponse, ListSkillFilesData, ListSkillFilesError, ListSkillFilesResponse, GetSkillFileData, GetSkillFileError, GetSkillFileResponse, UpdateSkillFileData, UpdateSkillFileError, UpdateSkillFileResponse, ListNeoSkillCandidatesData, ListNeoSkillCandidatesError, ListNeoSkillCandidatesResponse, ListNeoSkillReleasesData, ListNeoSkillReleasesError, ListNeoSkillReleasesResponse, GetNeoSkillPayloadData, GetNeoSkillPayloadError, GetNeoSkillPayloadResponse, EvaluateNeoSkillCandidateData, EvaluateNeoSkillCandidateError, EvaluateNeoSkillCandidateResponse, PromoteNeoSkillCandidateData, PromoteNeoSkillCandidateError, PromoteNeoSkillCandidateResponse, RollbackNeoSkillReleaseData, RollbackNeoSkillReleaseError, RollbackNeoSkillReleaseResponse, SyncNeoSkillReleaseData, SyncNeoSkillReleaseError, SyncNeoSkillReleaseResponse, DeleteNeoSkillCandidateData, DeleteNeoSkillCandidateError, DeleteNeoSkillCandidateResponse, DeleteNeoSkillReleaseData, DeleteNeoSkillReleaseError, DeleteNeoSkillReleaseResponse, ListKnowledgeBasesData, ListKnowledgeBasesError, ListKnowledgeBasesResponse, CreateKnowledgeBaseData, CreateKnowledgeBaseError, CreateKnowledgeBaseResponse, GetKnowledgeBaseData, GetKnowledgeBaseError, GetKnowledgeBaseResponse, UpdateKnowledgeBaseData, UpdateKnowledgeBaseError, UpdateKnowledgeBaseResponse, DeleteKnowledgeBaseData, DeleteKnowledgeBaseError, DeleteKnowledgeBaseResponse, GetKnowledgeBaseStatsData, GetKnowledgeBaseStatsError, GetKnowledgeBaseStatsResponse, ListKnowledgeDocumentsData, ListKnowledgeDocumentsError, ListKnowledgeDocumentsResponse, UploadKnowledgeDocumentData, UploadKnowledgeDocumentError, UploadKnowledgeDocumentResponse, ImportKnowledgeDocumentsData, ImportKnowledgeDocumentsError, ImportKnowledgeDocumentsResponse, ImportKnowledgeDocumentFromUrlData, ImportKnowledgeDocumentFromUrlError, ImportKnowledgeDocumentFromUrlResponse, GetKnowledgeDocumentData, GetKnowledgeDocumentError, GetKnowledgeDocumentResponse, DeleteKnowledgeDocumentData, DeleteKnowledgeDocumentError, DeleteKnowledgeDocumentResponse, ListKnowledgeChunksData, ListKnowledgeChunksError, ListKnowledgeChunksResponse, DeleteKnowledgeChunkData, DeleteKnowledgeChunkError, DeleteKnowledgeChunkResponse, RetrieveKnowledgeBaseData, RetrieveKnowledgeBaseError, RetrieveKnowledgeBaseResponse, GetKnowledgeTaskData, GetKnowledgeTaskError, GetKnowledgeTaskResponse, GetPersonaTreeError, GetPersonaTreeResponse, ListPersonasData, ListPersonasError, ListPersonasResponse, CreatePersonaData, CreatePersonaError, CreatePersonaResponse, GetPersonaByIdData, GetPersonaByIdError, GetPersonaByIdResponse, UpdatePersonaByIdData, UpdatePersonaByIdError, UpdatePersonaByIdResponse, DeletePersonaByIdData, DeletePersonaByIdError, DeletePersonaByIdResponse, GetPersonaData, GetPersonaError, GetPersonaResponse, UpdatePersonaData, UpdatePersonaError, UpdatePersonaResponse, DeletePersonaData, DeletePersonaError, DeletePersonaResponse, ListPersonaFoldersData, ListPersonaFoldersError, ListPersonaFoldersResponse, CreatePersonaFolderData, CreatePersonaFolderError, CreatePersonaFolderResponse, UpdatePersonaFolderData, UpdatePersonaFolderError, UpdatePersonaFolderResponse, DeletePersonaFolderData, DeletePersonaFolderError, DeletePersonaFolderResponse, MovePersonaItemData, MovePersonaItemError, MovePersonaItemResponse, ReorderPersonaItemsData, ReorderPersonaItemsError, ReorderPersonaItemsResponse, ListSessionsData, ListSessionsError, ListSessionsResponse, ListActiveUmosError, ListActiveUmosResponse, ListSessionRulesData, ListSessionRulesError, ListSessionRulesResponse, UpsertSessionRuleData, UpsertSessionRuleError, UpsertSessionRuleResponse, DeleteSessionRulesData, DeleteSessionRulesError, DeleteSessionRulesResponse, BatchUpdateSessionProviderData, BatchUpdateSessionProviderError, BatchUpdateSessionProviderResponse, BatchUpdateSessionServiceData, BatchUpdateSessionServiceError, BatchUpdateSessionServiceResponse, ListSessionGroupsError, ListSessionGroupsResponse, CreateSessionGroupData, CreateSessionGroupError, CreateSessionGroupResponse, UpdateSessionGroupData, UpdateSessionGroupError, UpdateSessionGroupResponse, DeleteSessionGroupData, DeleteSessionGroupError, DeleteSessionGroupResponse, ListConversationsData, ListConversationsError, ListConversationsResponse, BatchDeleteConversationsData, BatchDeleteConversationsError, BatchDeleteConversationsResponse, GetConversationData, GetConversationError, GetConversationResponse, UpdateConversationData, UpdateConversationError, UpdateConversationResponse, DeleteConversationData, DeleteConversationError, DeleteConversationResponse, ReplaceConversationMessagesData, ReplaceConversationMessagesError, ReplaceConversationMessagesResponse, ExportConversationsData, ExportConversationsError, ExportConversationsResponse, GetStatsData, GetStatsError, GetStatsResponse, GetProviderTokenStatsData, GetProviderTokenStatsError, GetProviderTokenStatsResponse, GetVersionError, GetVersionResponse, GetPublicVersionsError, GetPublicVersionsResponse, GetFirstNoticeData, GetFirstNoticeError, GetFirstNoticeResponse, TestGhproxyConnectionData, TestGhproxyConnectionError, TestGhproxyConnectionResponse, ListChangelogVersionsError, ListChangelogVersionsResponse, GetChangelogData, GetChangelogError, GetChangelogResponse, GetStartTimeError, GetStartTimeResponse, GetStorageStatusError, GetStorageStatusResponse, CleanupStorageData, CleanupStorageError, CleanupStorageResponse, RestartCoreError, RestartCoreResponse, ListBackupsData, ListBackupsError, ListBackupsResponse, CreateBackupData, CreateBackupError, CreateBackupResponse, UploadBackupData, UploadBackupError, UploadBackupResponse, InitBackupUploadData, InitBackupUploadError, InitBackupUploadResponse, UploadBackupChunkData, UploadBackupChunkError, UploadBackupChunkResponse, CompleteBackupUploadData, CompleteBackupUploadError, CompleteBackupUploadResponse, AbortBackupUploadData, AbortBackupUploadError, AbortBackupUploadResponse, GetBackupProgressData, GetBackupProgressError, GetBackupProgressResponse, DownloadBackupData, DownloadBackupError, DownloadBackupResponse, RenameBackupData, RenameBackupError, RenameBackupResponse, DeleteBackupData, DeleteBackupError, DeleteBackupResponse, CheckBackupData, CheckBackupError, CheckBackupResponse, ImportBackupData, ImportBackupError, ImportBackupResponse, CheckUpdateError, CheckUpdateResponse, ListReleasesData, ListReleasesError, ListReleasesResponse, UpdateCoreData, UpdateCoreError, UpdateCoreResponse, UpdateDashboardData, UpdateDashboardError, UpdateDashboardResponse, GetUpdateProgressData, GetUpdateProgressError, GetUpdateProgressResponse, InstallPipPackageData, InstallPipPackageError, InstallPipPackageResponse, ListCronJobsData, ListCronJobsError, ListCronJobsResponse, CreateCronJobData, CreateCronJobError, CreateCronJobResponse, UpdateCronJobData, UpdateCronJobError, UpdateCronJobResponse, DeleteCronJobData, DeleteCronJobError, DeleteCronJobResponse, RunCronJobData, RunCronJobError, RunCronJobResponse, StreamLiveLogsError, StreamLiveLogsResponse, GetLogHistoryError, GetLogHistoryResponse, GetTraceSettingsError, GetTraceSettingsResponse, UpdateTraceSettingsData, UpdateTraceSettingsError, UpdateTraceSettingsResponse, ListT2iTemplatesError, ListT2iTemplatesResponse, CreateT2iTemplateData, CreateT2iTemplateError, CreateT2iTemplateResponse, GetActiveT2iTemplateError, GetActiveT2iTemplateResponse, SetActiveT2iTemplateData, SetActiveT2iTemplateError, SetActiveT2iTemplateResponse, ResetDefaultT2iTemplateError, ResetDefaultT2iTemplateResponse, GetT2iTemplateData, GetT2iTemplateError, GetT2iTemplateResponse, UpdateT2iTemplateData, UpdateT2iTemplateError, UpdateT2iTemplateResponse, DeleteT2iTemplateData, DeleteT2iTemplateError, DeleteT2iTemplateResponse, GetSubagentConfigError, GetSubagentConfigResponse, UpdateSubagentConfigData, UpdateSubagentConfigError, UpdateSubagentConfigResponse, ListSubagentAvailableToolsError, ListSubagentAvailableToolsResponse, VerifyPlatformWebhookData, VerifyPlatformWebhookError, VerifyPlatformWebhookResponse, ReceivePlatformWebhookData, ReceivePlatformWebhookError, ReceivePlatformWebhookResponse } from './types.gen'; - -export const client = createClient(createConfig()); - -/** - * Login to the dashboard API - */ -export const login = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<LoginData, ThrowOnError>) => { - return (options?.client ?? client).post<LoginResponse, LoginError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/login' - }); -}; - -/** - * Logout from the dashboard API - */ -export const logout = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).post<LogoutResponse, LogoutError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/logout' - }); -}; - -/** - * Get first-run setup status - */ -export const getAuthSetupStatus = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetAuthSetupStatusResponse, GetAuthSetupStatusError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/setup-status' - }); -}; - -/** - * Complete first-run dashboard account setup - */ -export const setupAuth = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetupAuthData, ThrowOnError>) => { - return (options?.client ?? client).post<SetupAuthResponse, SetupAuthError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/setup' - }); -}; - -/** - * Start or refresh TOTP setup - */ -export const setupTotp = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<SetupTotpData, ThrowOnError>) => { - return (options?.client ?? client).post<SetupTotpResponse, SetupTotpError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/totp/setup' - }); -}; - -/** - * Generate or rotate TOTP recovery codes - */ -export const recoverTotp = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).post<RecoverTotpResponse, RecoverTotpError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/totp/recovery' - }); -}; - -/** - * Update dashboard account credentials - */ -export const updateAuthAccount = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateAuthAccountData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateAuthAccountResponse, UpdateAuthAccountError, ThrowOnError>({ - ...options, - url: '/api/v1/auth/account' - }); -}; - -/** - * List OpenAPI keys - */ -export const listApiKeys = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListApiKeysResponse, ListApiKeysError, ThrowOnError>({ - ...options, - url: '/api/v1/api-keys' - }); -}; - -/** - * Create an OpenAPI key - */ -export const createApiKey = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateApiKeyData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateApiKeyResponse, CreateApiKeyError, ThrowOnError>({ - ...options, - url: '/api/v1/api-keys' - }); -}; - -/** - * Revoke an OpenAPI key - */ -export const revokeApiKey = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RevokeApiKeyData, ThrowOnError>) => { - return (options?.client ?? client).post<RevokeApiKeyResponse, RevokeApiKeyError, ThrowOnError>({ - ...options, - url: '/api/v1/api-keys/{key_id}/revoke' - }); -}; - -/** - * Delete an OpenAPI key - */ -export const deleteApiKey = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteApiKeyData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteApiKeyResponse, DeleteApiKeyError, ThrowOnError>({ - ...options, - url: '/api/v1/api-keys/{key_id}' - }); -}; - -/** - * Get the system configuration schema - */ -export const getSystemConfigSchema = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetSystemConfigSchemaResponse, GetSystemConfigSchemaError, ThrowOnError>({ - ...options, - url: '/api/v1/system-config/schema' - }); -}; - -/** - * Get the system configuration - */ -export const getSystemConfig = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetSystemConfigResponse, GetSystemConfigError, ThrowOnError>({ - ...options, - url: '/api/v1/system-config' - }); -}; - -/** - * Replace the system configuration - */ -export const updateSystemConfig = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSystemConfigData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateSystemConfigResponse, UpdateSystemConfigError, ThrowOnError>({ - ...options, - url: '/api/v1/system-config' - }); -}; - -/** - * Get runtime-enriched system configuration metadata - */ -export const getSystemConfigRuntime = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetSystemConfigRuntimeResponse, GetSystemConfigRuntimeError, ThrowOnError>({ - ...options, - url: '/api/v1/system-config/runtime' - }); -}; - -/** - * Get the configuration profile schema - */ -export const getConfigProfileSchema = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetConfigProfileSchemaResponse, GetConfigProfileSchemaError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles/schema' - }); -}; - -/** - * List configuration profiles - */ -export const listConfigProfiles = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListConfigProfilesResponse, ListConfigProfilesError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles' - }); -}; - -/** - * Create a configuration profile - */ -export const createConfigProfile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateConfigProfileData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateConfigProfileResponse, CreateConfigProfileError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles' - }); -}; - -/** - * Get a configuration profile - */ -export const getConfigProfile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetConfigProfileData, ThrowOnError>) => { - return (options?.client ?? client).get<GetConfigProfileResponse, GetConfigProfileError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles/{config_id}' - }); -}; - -/** - * Replace a configuration profile - */ -export const updateConfigProfileContent = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateConfigProfileContentData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateConfigProfileContentResponse, UpdateConfigProfileContentError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles/{config_id}' - }); -}; - -/** - * Rename a configuration profile - */ -export const renameConfigProfile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RenameConfigProfileData, ThrowOnError>) => { - return (options?.client ?? client).patch<RenameConfigProfileResponse, RenameConfigProfileError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles/{config_id}' - }); -}; - -/** - * Delete a configuration profile - */ -export const deleteConfigProfile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteConfigProfileData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteConfigProfileResponse, DeleteConfigProfileError, ThrowOnError>({ - ...options, - url: '/api/v1/config-profiles/{config_id}' - }); -}; - -/** - * List UMO to configuration profile bindings - */ -export const listConfigRoutes = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListConfigRoutesResponse, ListConfigRoutesError, ThrowOnError>({ - ...options, - url: '/api/v1/config-routes' - }); -}; - -/** - * Replace all UMO to configuration profile bindings - */ -export const replaceConfigRoutes = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReplaceConfigRoutesData, ThrowOnError>) => { - return (options?.client ?? client).put<ReplaceConfigRoutesResponse, ReplaceConfigRoutesError, ThrowOnError>({ - ...options, - url: '/api/v1/config-routes' - }); -}; - -/** - * Bind a UMO to a configuration profile - */ -export const upsertConfigRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpsertConfigRouteData, ThrowOnError>) => { - return (options?.client ?? client).put<UpsertConfigRouteResponse, UpsertConfigRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/config-routes/{umo}' - }); -}; - -/** - * Remove a UMO configuration profile binding - */ -export const deleteConfigRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteConfigRouteData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteConfigRouteResponse, DeleteConfigRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/config-routes/{umo}' - }); -}; - -/** - * List configurable bot types and their schemas - */ -export const listBotTypes = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListBotTypesResponse, ListBotTypesError, ThrowOnError>({ - ...options, - url: '/api/v1/bot-types' - }); -}; - -/** - * Run a bot type registration flow action - */ -export const registerBotType = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RegisterBotTypeData, ThrowOnError>) => { - return (options?.client ?? client).post<RegisterBotTypeResponse, RegisterBotTypeError, ThrowOnError>({ - ...options, - url: '/api/v1/bot-types/{bot_type}/registration' - }); -}; - -/** - * List created bots and their configurations - */ -export const listBots = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListBotsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListBotsResponse, ListBotsError, ThrowOnError>({ - ...options, - url: '/api/v1/bots' - }); -}; - -/** - * Create a bot from configuration - */ -export const createBot = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateBotData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateBotResponse, CreateBotError, ThrowOnError>({ - ...options, - url: '/api/v1/bots' - }); -}; - -/** - * List runtime status for all bots - */ -export const listBotStats = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListBotStatsResponse, ListBotStatsError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/stats' - }); -}; - -/** - * Get a bot configuration by query ID - */ -export const getBotById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetBotByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetBotByIdResponse, GetBotByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/by-id' - }); -}; - -/** - * Replace a bot configuration by body ID - */ -export const updateBotById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateBotByIdData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateBotByIdResponse, UpdateBotByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/by-id' - }); -}; - -/** - * Delete a bot by query ID - */ -export const deleteBotById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteBotByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteBotByIdResponse, DeleteBotByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/by-id' - }); -}; - -/** - * Enable or disable a bot by body ID - */ -export const setBotEnabledById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetBotEnabledByIdData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetBotEnabledByIdResponse, SetBotEnabledByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/enabled' - }); -}; - -/** - * Test a bot registration or connection by body ID - */ -export const testBotById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestBotByIdData, ThrowOnError>) => { - return (options?.client ?? client).post<TestBotByIdResponse, TestBotByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/test' - }); -}; - -/** - * Get a bot configuration - */ -export const getBot = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetBotData, ThrowOnError>) => { - return (options?.client ?? client).get<GetBotResponse, GetBotError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/{bot_id}' - }); -}; - -/** - * Replace a bot configuration - */ -export const updateBot = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateBotData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateBotResponse, UpdateBotError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/{bot_id}' - }); -}; - -/** - * Delete a bot - */ -export const deleteBot = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteBotData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteBotResponse, DeleteBotError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/{bot_id}' - }); -}; - -/** - * Enable or disable a bot - */ -export const setBotEnabled = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetBotEnabledData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetBotEnabledResponse, SetBotEnabledError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/{bot_id}/enabled' - }); -}; - -/** - * Test a bot registration or connection - */ -export const testBot = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestBotData, ThrowOnError>) => { - return (options?.client ?? client).post<TestBotResponse, TestBotError, ThrowOnError>({ - ...options, - url: '/api/v1/bots/{bot_id}/test' - }); -}; - -/** - * Get the overall provider configuration schema - */ -export const getProviderSchema = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetProviderSchemaResponse, GetProviderSchemaError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/schema' - }); -}; - -/** - * List provider sources - */ -export const listProviderSources = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListProviderSourcesResponse, ListProviderSourcesError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources' - }); -}; - -/** - * Create a provider source - */ -export const createProviderSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateProviderSourceData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateProviderSourceResponse, CreateProviderSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources' - }); -}; - -/** - * Get a provider source by query ID - */ -export const getProviderSourceById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetProviderSourceByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetProviderSourceByIdResponse, GetProviderSourceByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/by-id' - }); -}; - -/** - * Update a provider source by body ID - */ -export const upsertProviderSourceById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpsertProviderSourceByIdData, ThrowOnError>) => { - return (options?.client ?? client).put<UpsertProviderSourceByIdResponse, UpsertProviderSourceByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/by-id' - }); -}; - -/** - * Delete a provider source by query ID - */ -export const deleteProviderSourceById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteProviderSourceByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteProviderSourceByIdResponse, DeleteProviderSourceByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/by-id' - }); -}; - -/** - * List models available from a provider source by query ID - */ -export const listProviderSourceModelsById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListProviderSourceModelsByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<ListProviderSourceModelsByIdResponse, ListProviderSourceModelsByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/models' - }); -}; - -/** - * List providers under a provider source by query ID - */ -export const listProvidersBySourceId = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListProvidersBySourceIdData, ThrowOnError>) => { - return (options?.client ?? client).get<ListProvidersBySourceIdResponse, ListProvidersBySourceIdError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/providers' - }); -}; - -/** - * Create a provider under a provider source by body ID - */ -export const createProviderInSourceById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateProviderInSourceByIdData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateProviderInSourceByIdResponse, CreateProviderInSourceByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/providers' - }); -}; - -/** - * Get a provider source - */ -export const getProviderSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetProviderSourceData, ThrowOnError>) => { - return (options?.client ?? client).get<GetProviderSourceResponse, GetProviderSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/{source_id}' - }); -}; - -/** - * Update a provider source or create it if missing - */ -export const upsertProviderSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpsertProviderSourceData, ThrowOnError>) => { - return (options?.client ?? client).put<UpsertProviderSourceResponse, UpsertProviderSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/{source_id}' - }); -}; - -/** - * Delete a provider source and providers that reference it - */ -export const deleteProviderSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteProviderSourceData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteProviderSourceResponse, DeleteProviderSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/{source_id}' - }); -}; - -/** - * List models available from a provider source - */ -export const listProviderSourceModels = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListProviderSourceModelsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListProviderSourceModelsResponse, ListProviderSourceModelsError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/{source_id}/models' - }); -}; - -/** - * List providers under a provider source - */ -export const listProvidersBySource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListProvidersBySourceData, ThrowOnError>) => { - return (options?.client ?? client).get<ListProvidersBySourceResponse, ListProvidersBySourceError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/{source_id}/providers' - }); -}; - -/** - * Create a provider under a provider source - */ -export const createProviderInSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateProviderInSourceData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateProviderInSourceResponse, CreateProviderInSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/provider-sources/{source_id}/providers' - }); -}; - -/** - * List providers by capability or source - */ -export const listProviders = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListProvidersData, ThrowOnError>) => { - return (options?.client ?? client).get<ListProvidersResponse, ListProvidersError, ThrowOnError>({ - ...options, - url: '/api/v1/providers' - }); -}; - -/** - * Create a standalone provider - */ -export const createProvider = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateProviderData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateProviderResponse, CreateProviderError, ThrowOnError>({ - ...options, - url: '/api/v1/providers' - }); -}; - -/** - * Get a provider configuration by query ID - */ -export const getProviderById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetProviderByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetProviderByIdResponse, GetProviderByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/by-id' - }); -}; - -/** - * Replace a provider configuration by body ID - */ -export const updateProviderById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateProviderByIdData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateProviderByIdResponse, UpdateProviderByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/by-id' - }); -}; - -/** - * Delete a provider by query ID - */ -export const deleteProviderById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteProviderByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteProviderByIdResponse, DeleteProviderByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/by-id' - }); -}; - -/** - * Enable or disable a provider by body ID - */ -export const setProviderEnabledById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetProviderEnabledByIdData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetProviderEnabledByIdResponse, SetProviderEnabledByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/enabled' - }); -}; - -/** - * Test a provider by body ID - */ -export const testProviderById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestProviderByIdData, ThrowOnError>) => { - return (options?.client ?? client).post<TestProviderByIdResponse, TestProviderByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/test' - }); -}; - -/** - * Probe embedding dimension for a provider by body ID - */ -export const getProviderEmbeddingDimensionById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetProviderEmbeddingDimensionByIdData, ThrowOnError>) => { - return (options?.client ?? client).post<GetProviderEmbeddingDimensionByIdResponse, GetProviderEmbeddingDimensionByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/embedding-dimension' - }); -}; - -/** - * Get a provider configuration - */ -export const getProvider = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetProviderData, ThrowOnError>) => { - return (options?.client ?? client).get<GetProviderResponse, GetProviderError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/{provider_id}' - }); -}; - -/** - * Replace a provider configuration - */ -export const updateProvider = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateProviderData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateProviderResponse, UpdateProviderError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/{provider_id}' - }); -}; - -/** - * Delete a provider - */ -export const deleteProvider = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteProviderData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteProviderResponse, DeleteProviderError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/{provider_id}' - }); -}; - -/** - * Enable or disable a provider - */ -export const setProviderEnabled = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetProviderEnabledData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetProviderEnabledResponse, SetProviderEnabledError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/{provider_id}/enabled' - }); -}; - -/** - * Test a provider - */ -export const testProvider = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestProviderData, ThrowOnError>) => { - return (options?.client ?? client).post<TestProviderResponse, TestProviderError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/{provider_id}/test' - }); -}; - -/** - * Probe embedding dimension for a provider - */ -export const getProviderEmbeddingDimension = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetProviderEmbeddingDimensionData, ThrowOnError>) => { - return (options?.client ?? client).post<GetProviderEmbeddingDimensionResponse, GetProviderEmbeddingDimensionError, ThrowOnError>({ - ...options, - url: '/api/v1/providers/{provider_id}/embedding-dimension' - }); -}; - -/** - * Send a webchat message - */ -export const sendChatMessage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SendChatMessageData, ThrowOnError>) => { - return (options?.client ?? client).post<SendChatMessageResponse, SendChatMessageError, ThrowOnError>({ - ...options, - url: '/api/v1/chat' - }); -}; - -/** - * Open a streaming chat WebSocket - */ -export const openChatWebSocket = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<OpenChatWebSocketData, ThrowOnError>) => { - return (options?.client ?? client).get<void, unknown, ThrowOnError>({ - ...options, - url: '/api/v1/chat/ws' - }); -}; - -/** - * Open the live voice chat WebSocket - */ -export const openLiveChatWebSocket = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<OpenLiveChatWebSocketData, ThrowOnError>) => { - return (options?.client ?? client).get<void, unknown, ThrowOnError>({ - ...options, - url: '/api/v1/live-chat/ws' - }); -}; - -/** - * Open the unified live/chat WebSocket - */ -export const openUnifiedChatWebSocket = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<OpenUnifiedChatWebSocketData, ThrowOnError>) => { - return (options?.client ?? client).get<void, unknown, ThrowOnError>({ - ...options, - url: '/api/v1/unified-chat/ws' - }); -}; - -/** - * List webchat sessions - */ -export const listChatSessions = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListChatSessionsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListChatSessionsResponse, ListChatSessionsError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions' - }); -}; - -/** - * Create a webchat session - */ -export const createChatSession = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<CreateChatSessionData, ThrowOnError>) => { - return (options?.client ?? client).get<CreateChatSessionResponse, CreateChatSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/new' - }); -}; - -/** - * Delete multiple webchat sessions - */ -export const batchDeleteChatSessions = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<BatchDeleteChatSessionsData, ThrowOnError>) => { - return (options?.client ?? client).post<BatchDeleteChatSessionsResponse, BatchDeleteChatSessionsError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/batch-delete' - }); -}; - -/** - * Get a webchat session and its messages - */ -export const getChatSession = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetChatSessionData, ThrowOnError>) => { - return (options?.client ?? client).get<GetChatSessionResponse, GetChatSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/{session_id}' - }); -}; - -/** - * Update a webchat session - */ -export const updateChatSession = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateChatSessionData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateChatSessionResponse, UpdateChatSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/{session_id}' - }); -}; - -/** - * Delete a webchat session - */ -export const deleteChatSession = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteChatSessionData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteChatSessionResponse, DeleteChatSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/{session_id}' - }); -}; - -/** - * Stop active webchat work for a session - */ -export const stopChatSession = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<StopChatSessionData, ThrowOnError>) => { - return (options?.client ?? client).post<StopChatSessionResponse, StopChatSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/{session_id}/stop' - }); -}; - -/** - * Update the latest user message in a webchat session - */ -export const updateChatMessage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateChatMessageData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateChatMessageResponse, UpdateChatMessageError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/{session_id}/messages/{message_id}' - }); -}; - -/** - * Regenerate a bot message in a webchat session - */ -export const regenerateChatMessage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RegenerateChatMessageData, ThrowOnError>) => { - return (options?.client ?? client).post<RegenerateChatMessageResponse, RegenerateChatMessageError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/sessions/{session_id}/messages/{message_id}/regenerate' - }); -}; - -/** - * List chat-selectable configuration profiles - */ -export const listChatConfigs = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListChatConfigsResponse, ListChatConfigsError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/configs' - }); -}; - -/** - * Create or reuse a side thread from a webchat message - */ -export const createChatThread = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateChatThreadData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateChatThreadResponse, CreateChatThreadError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/threads' - }); -}; - -/** - * Get a webchat side thread - */ -export const getChatThread = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetChatThreadData, ThrowOnError>) => { - return (options?.client ?? client).get<GetChatThreadResponse, GetChatThreadError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/threads/{thread_id}' - }); -}; - -/** - * Delete a webchat side thread - */ -export const deleteChatThread = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteChatThreadData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteChatThreadResponse, DeleteChatThreadError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/threads/{thread_id}' - }); -}; - -/** - * Send a message inside a webchat side thread - */ -export const sendChatThreadMessage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SendChatThreadMessageData, ThrowOnError>) => { - return (options?.client ?? client).post<SendChatThreadMessageResponse, SendChatThreadMessageError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/threads/{thread_id}/messages' - }); -}; - -/** - * List ChatUI projects - */ -export const listChatProjects = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListChatProjectsResponse, ListChatProjectsError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects' - }); -}; - -/** - * Create a ChatUI project - */ -export const createChatProject = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateChatProjectData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateChatProjectResponse, CreateChatProjectError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects' - }); -}; - -/** - * Get a ChatUI project - */ -export const getChatProject = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetChatProjectData, ThrowOnError>) => { - return (options?.client ?? client).get<GetChatProjectResponse, GetChatProjectError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects/{project_id}' - }); -}; - -/** - * Update a ChatUI project - */ -export const updateChatProject = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateChatProjectData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateChatProjectResponse, UpdateChatProjectError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects/{project_id}' - }); -}; - -/** - * Delete a ChatUI project - */ -export const deleteChatProject = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteChatProjectData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteChatProjectResponse, DeleteChatProjectError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects/{project_id}' - }); -}; - -/** - * List sessions in a ChatUI project - */ -export const listChatProjectSessions = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListChatProjectSessionsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListChatProjectSessionsResponse, ListChatProjectSessionsError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects/{project_id}/sessions' - }); -}; - -/** - * Add a session to a ChatUI project - */ -export const addChatProjectSession = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<AddChatProjectSessionData, ThrowOnError>) => { - return (options?.client ?? client).post<AddChatProjectSessionResponse, AddChatProjectSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects/{project_id}/sessions/{session_id}' - }); -}; - -/** - * Remove a session from its ChatUI project - */ -export const removeChatProjectSession = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RemoveChatProjectSessionData, ThrowOnError>) => { - return (options?.client ?? client).delete<RemoveChatProjectSessionResponse, RemoveChatProjectSessionError, ThrowOnError>({ - ...options, - url: '/api/v1/chat/projects/sessions/{session_id}' - }); -}; - -/** - * Send a message to a UMO - */ -export const sendImMessage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SendImMessageData, ThrowOnError>) => { - return (options?.client ?? client).post<SendImMessageResponse, SendImMessageError, ThrowOnError>({ - ...options, - url: '/api/v1/im/messages' - }); -}; - -/** - * List active IM bot IDs - */ -export const listImBots = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListImBotsResponse, ListImBotsError, ThrowOnError>({ - ...options, - url: '/api/v1/im/bots' - }); -}; - -/** - * Upload a file - */ -export const uploadFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadFileData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadFileResponse, UploadFileError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/files' - }); -}; - -/** - * Upload a file - */ -export const uploadOpenApiFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadOpenApiFileData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadOpenApiFileResponse, UploadOpenApiFileError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/file' - }); -}; - -/** - * Download an uploaded file by attachment ID - */ -export const downloadOpenApiFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DownloadOpenApiFileData, ThrowOnError>) => { - return (options?.client ?? client).get<DownloadOpenApiFileResponse, DownloadOpenApiFileError, ThrowOnError>({ - ...options, - url: '/api/v1/file' - }); -}; - -/** - * Get an uploaded file by stored filename - */ -export const getFileByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetFileByNameData, ThrowOnError>) => { - return (options?.client ?? client).get<GetFileByNameResponse, GetFileByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/files/content' - }); -}; - -/** - * Get a tokenized public file - */ -export const getTokenFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetTokenFileData, ThrowOnError>) => { - return (options?.client ?? client).get<GetTokenFileResponse, GetTokenFileError, ThrowOnError>({ - ...options, - url: '/api/v1/files/tokens/{file_token}' - }); -}; - -/** - * Get attachment metadata - */ -export const getAttachment = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetAttachmentData, ThrowOnError>) => { - return (options?.client ?? client).get<GetAttachmentResponse, GetAttachmentError, ThrowOnError>({ - ...options, - url: '/api/v1/files/{attachment_id}' - }); -}; - -/** - * Delete an attachment - */ -export const deleteAttachment = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteAttachmentData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteAttachmentResponse, DeleteAttachmentError, ThrowOnError>({ - ...options, - url: '/api/v1/files/{attachment_id}' - }); -}; - -/** - * Download attachment content - */ -export const downloadAttachment = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DownloadAttachmentData, ThrowOnError>) => { - return (options?.client ?? client).get<DownloadAttachmentResponse, DownloadAttachmentError, ThrowOnError>({ - ...options, - url: '/api/v1/files/{attachment_id}/content' - }); -}; - -/** - * List installed plugins - */ -export const listPlugins = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListPluginsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginsResponse, ListPluginsError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins' - }); -}; - -/** - * Get plugin details by query ID - */ -export const getPluginById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginByIdResponse, GetPluginByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/by-id' - }); -}; - -/** - * Uninstall a plugin by query ID - */ -export const uninstallPluginById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UninstallPluginByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<UninstallPluginByIdResponse, UninstallPluginByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/by-id' - }); -}; - -/** - * Get plugin configuration by query ID - */ -export const getPluginConfigById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginConfigByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginConfigByIdResponse, GetPluginConfigByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/config' - }); -}; - -/** - * Save plugin configuration by body ID - */ -export const updatePluginConfigById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePluginConfigByIdData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdatePluginConfigByIdResponse, UpdatePluginConfigByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/config' - }); -}; - -/** - * Get plugin configuration schema by query ID - */ -export const getPluginConfigSchemaById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginConfigSchemaByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginConfigSchemaByIdResponse, GetPluginConfigSchemaByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/config/schema' - }); -}; - -/** - * List uploaded files for a plugin file configuration item by query ID - */ -export const listPluginConfigFilesById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListPluginConfigFilesByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginConfigFilesByIdResponse, ListPluginConfigFilesByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/config-files' - }); -}; - -/** - * Upload files for a plugin file configuration item by query ID - */ -export const uploadPluginConfigFilesById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadPluginConfigFilesByIdData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadPluginConfigFilesByIdResponse, UploadPluginConfigFilesByIdError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/plugins/config-files' - }); -}; - -/** - * Delete a plugin file configuration upload by query ID - */ -export const deletePluginConfigFileById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePluginConfigFileByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePluginConfigFileByIdResponse, DeletePluginConfigFileByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/config-files' - }); -}; - -/** - * Get plugin README content by query ID - */ -export const getPluginReadmeById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginReadmeByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginReadmeByIdResponse, GetPluginReadmeByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/readme' - }); -}; - -/** - * Get plugin CHANGELOG content by query ID - */ -export const getPluginChangelogById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginChangelogByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginChangelogByIdResponse, GetPluginChangelogByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/changelog' - }); -}; - -/** - * Reload a plugin by body ID - */ -export const reloadPluginById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReloadPluginByIdData, ThrowOnError>) => { - return (options?.client ?? client).post<ReloadPluginByIdResponse, ReloadPluginByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/reload' - }); -}; - -/** - * Enable or disable a plugin by body ID - */ -export const setPluginEnabledById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetPluginEnabledByIdData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetPluginEnabledByIdResponse, SetPluginEnabledByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/enabled' - }); -}; - -/** - * List plugin pages by query ID - */ -export const listPluginPagesById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListPluginPagesByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginPagesByIdResponse, ListPluginPagesByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/pages' - }); -}; - -/** - * Get plugin page entry HTML by query ID - */ -export const getPluginPageById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginPageByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginPageByIdResponse, GetPluginPageByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/page' - }); -}; - -/** - * Get a plugin page asset by query ID - */ -export const getPluginPageAssetById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginPageAssetByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginPageAssetByIdResponse, GetPluginPageAssetByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/page/assets' - }); -}; - -/** - * Get plugin details - */ -export const getPlugin = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginResponse, GetPluginError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}' - }); -}; - -/** - * Uninstall a plugin - */ -export const uninstallPlugin = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UninstallPluginData, ThrowOnError>) => { - return (options?.client ?? client).delete<UninstallPluginResponse, UninstallPluginError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}' - }); -}; - -/** - * Get plugin configuration - */ -export const getPluginConfig = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginConfigData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginConfigResponse, GetPluginConfigError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/config' - }); -}; - -/** - * Save plugin configuration - */ -export const updatePluginConfig = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePluginConfigData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdatePluginConfigResponse, UpdatePluginConfigError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/config' - }); -}; - -/** - * Get plugin configuration schema - */ -export const getPluginConfigSchema = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginConfigSchemaData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginConfigSchemaResponse, GetPluginConfigSchemaError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/config/schema' - }); -}; - -/** - * List uploaded files for a plugin file configuration item - */ -export const listPluginConfigFiles = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListPluginConfigFilesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginConfigFilesResponse, ListPluginConfigFilesError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/config-files/{config_key}' - }); -}; - -/** - * Upload files for a plugin file configuration item - */ -export const uploadPluginConfigFiles = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadPluginConfigFilesData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadPluginConfigFilesResponse, UploadPluginConfigFilesError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/plugins/{plugin_id}/config-files/{config_key}' - }); -}; - -/** - * Delete a plugin file configuration upload - */ -export const deletePluginConfigFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePluginConfigFileData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePluginConfigFileResponse, DeletePluginConfigFileError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/config-files' - }); -}; - -/** - * Get plugin README content - */ -export const getPluginReadme = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginReadmeData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginReadmeResponse, GetPluginReadmeError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/readme' - }); -}; - -/** - * Get plugin CHANGELOG content - */ -export const getPluginChangelog = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginChangelogData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginChangelogResponse, GetPluginChangelogError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/changelog' - }); -}; - -/** - * Reload a plugin - */ -export const reloadPlugin = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReloadPluginData, ThrowOnError>) => { - return (options?.client ?? client).post<ReloadPluginResponse, ReloadPluginError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/reload' - }); -}; - -/** - * Bind an installed plugin to a marketplace source - */ -export const bindPluginSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<BindPluginSourceData, ThrowOnError>) => { - return (options?.client ?? client).post<BindPluginSourceResponse, BindPluginSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/source' - }); -}; - -/** - * Enable or disable a plugin - */ -export const setPluginEnabled = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetPluginEnabledData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetPluginEnabledResponse, SetPluginEnabledError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/enabled' - }); -}; - -/** - * Update or reinstall a plugin - */ -export const updatePlugin = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePluginData, ThrowOnError>) => { - return (options?.client ?? client).post<UpdatePluginResponse, UpdatePluginError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/update' - }); -}; - -/** - * Update multiple plugins - */ -export const updatePlugins = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePluginsData, ThrowOnError>) => { - return (options?.client ?? client).post<UpdatePluginsResponse, UpdatePluginsError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/update' - }); -}; - -/** - * Check whether a plugin version constraint is supported - */ -export const checkPluginVersionSupport = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CheckPluginVersionSupportData, ThrowOnError>) => { - return (options?.client ?? client).post<CheckPluginVersionSupportResponse, CheckPluginVersionSupportError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/version-support/check' - }); -}; - -/** - * Validate a GitHub plugin repository before installation - */ -export const validatePluginRepo = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ValidatePluginRepoData, ThrowOnError>) => { - return (options?.client ?? client).post<ValidatePluginRepoResponse, ValidatePluginRepoError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/validate/repo' - }); -}; - -/** - * List failed plugins and errors - */ -export const listFailedPlugins = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListFailedPluginsResponse, ListFailedPluginsError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/failed' - }); -}; - -/** - * Uninstall a failed plugin - */ -export const uninstallFailedPlugin = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UninstallFailedPluginData, ThrowOnError>) => { - return (options?.client ?? client).delete<UninstallFailedPluginResponse, UninstallFailedPluginError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/failed/{plugin_id}' - }); -}; - -/** - * Reload a failed plugin - */ -export const reloadFailedPlugin = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReloadFailedPluginData, ThrowOnError>) => { - return (options?.client ?? client).post<ReloadFailedPluginResponse, ReloadFailedPluginError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/failed/{plugin_id}/reload' - }); -}; - -/** - * Install a plugin from GitHub - */ -export const installPluginFromGithub = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<InstallPluginFromGithubData, ThrowOnError>) => { - return (options?.client ?? client).post<InstallPluginFromGithubResponse, InstallPluginFromGithubError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/install/github' - }); -}; - -/** - * Install a plugin from a downloadable ZIP URL - */ -export const installPluginFromUrl = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<InstallPluginFromUrlData, ThrowOnError>) => { - return (options?.client ?? client).post<InstallPluginFromUrlResponse, InstallPluginFromUrlError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/install/url' - }); -}; - -/** - * Install a plugin from an uploaded ZIP file - */ -export const installPluginFromUpload = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<InstallPluginFromUploadData, ThrowOnError>) => { - return (options?.client ?? client).post<InstallPluginFromUploadResponse, InstallPluginFromUploadError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/plugins/install/upload' - }); -}; - -/** - * List plugin marketplace entries - */ -export const listPluginMarket = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListPluginMarketData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginMarketResponse, ListPluginMarketError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/market' - }); -}; - -/** - * List plugin marketplace categories - */ -export const listPluginMarketCategories = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginMarketCategoriesResponse, ListPluginMarketCategoriesError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/market/categories' - }); -}; - -/** - * List plugin sources - */ -export const listPluginSources = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginSourcesResponse, ListPluginSourcesError, ThrowOnError>({ - ...options, - url: '/api/v1/plugin-sources' - }); -}; - -/** - * Add a plugin source - */ -export const createPluginSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreatePluginSourceData, ThrowOnError>) => { - return (options?.client ?? client).post<CreatePluginSourceResponse, CreatePluginSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/plugin-sources' - }); -}; - -/** - * Replace plugin sources - */ -export const replacePluginSources = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReplacePluginSourcesData, ThrowOnError>) => { - return (options?.client ?? client).put<ReplacePluginSourcesResponse, ReplacePluginSourcesError, ThrowOnError>({ - ...options, - url: '/api/v1/plugin-sources' - }); -}; - -/** - * Delete a plugin source - */ -export const deletePluginSource = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePluginSourceData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePluginSourceResponse, DeletePluginSourceError, ThrowOnError>({ - ...options, - url: '/api/v1/plugin-sources/{source_id}' - }); -}; - -/** - * Delete a plugin source by query ID - */ -export const deletePluginSourceById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePluginSourceByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePluginSourceByIdResponse, DeletePluginSourceByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/plugin-sources/by-id' - }); -}; - -/** - * List plugin pages - */ -export const listPluginPages = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListPluginPagesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPluginPagesResponse, ListPluginPagesError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/pages' - }); -}; - -/** - * Get plugin page entry HTML - */ -export const getPluginPage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginPageData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginPageResponse, GetPluginPageError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/pages/{page_name}' - }); -}; - -/** - * Get a plugin page asset - */ -export const getPluginPageAsset = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginPageAssetData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginPageAssetResponse, GetPluginPageAssetError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/{plugin_id}/pages/{page_name}/assets/{asset_path}' - }); -}; - -/** - * Get the plugin page bridge SDK - */ -export const getPluginPageBridgeSdk = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginPageBridgeSdkResponse, GetPluginPageBridgeSdkError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/page-bridge-sdk.js' - }); -}; - -/** - * Proxy a plugin extension GET route - */ -export const getPluginExtensionRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPluginExtensionRouteData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPluginExtensionRouteResponse, GetPluginExtensionRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/extensions/{plugin_path}' - }); -}; - -/** - * Proxy a plugin extension POST route - */ -export const postPluginExtensionRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<PostPluginExtensionRouteData, ThrowOnError>) => { - return (options?.client ?? client).post<PostPluginExtensionRouteResponse, PostPluginExtensionRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/extensions/{plugin_path}' - }); -}; - -/** - * Proxy a plugin extension PUT route - */ -export const putPluginExtensionRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<PutPluginExtensionRouteData, ThrowOnError>) => { - return (options?.client ?? client).put<PutPluginExtensionRouteResponse, PutPluginExtensionRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/extensions/{plugin_path}' - }); -}; - -/** - * Proxy a plugin extension PATCH route - */ -export const patchPluginExtensionRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<PatchPluginExtensionRouteData, ThrowOnError>) => { - return (options?.client ?? client).patch<PatchPluginExtensionRouteResponse, PatchPluginExtensionRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/extensions/{plugin_path}' - }); -}; - -/** - * Proxy a plugin extension DELETE route - */ -export const deletePluginExtensionRoute = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePluginExtensionRouteData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePluginExtensionRouteResponse, DeletePluginExtensionRouteError, ThrowOnError>({ - ...options, - url: '/api/v1/plugins/extensions/{plugin_path}' - }); -}; - -/** - * List plugin commands - */ -export const listCommands = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListCommandsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListCommandsResponse, ListCommandsError, ThrowOnError>({ - ...options, - url: '/api/v1/commands' - }); -}; - -/** - * Update command enabled state, alias, or permission group - */ -export const updateCommand = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateCommandData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateCommandResponse, UpdateCommandError, ThrowOnError>({ - ...options, - url: '/api/v1/commands/{command_id}' - }); -}; - -/** - * List command conflicts - */ -export const listCommandConflicts = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListCommandConflictsResponse, ListCommandConflictsError, ThrowOnError>({ - ...options, - url: '/api/v1/commands/conflicts' - }); -}; - -/** - * List LLM tools - */ -export const listTools = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListToolsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListToolsResponse, ListToolsError, ThrowOnError>({ - ...options, - url: '/api/v1/tools' - }); -}; - -/** - * Enable or disable an LLM tool - */ -export const setToolEnabled = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetToolEnabledData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetToolEnabledResponse, SetToolEnabledError, ThrowOnError>({ - ...options, - url: '/api/v1/tools/{tool_id}/enabled' - }); -}; - -/** - * Update an LLM tool permission - */ -export const setToolPermission = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetToolPermissionData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetToolPermissionResponse, SetToolPermissionError, ThrowOnError>({ - ...options, - url: '/api/v1/tools/{tool_id}/permission' - }); -}; - -/** - * List MCP servers - */ -export const listMcpServers = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListMcpServersResponse, ListMcpServersError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers' - }); -}; - -/** - * Add an MCP server - */ -export const createMcpServer = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateMcpServerData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateMcpServerResponse, CreateMcpServerError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers' - }); -}; - -/** - * Update an MCP server by body name - */ -export const updateMcpServerByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateMcpServerByNameData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateMcpServerByNameResponse, UpdateMcpServerByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/by-name' - }); -}; - -/** - * Delete an MCP server by query name - */ -export const deleteMcpServerByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteMcpServerByNameData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteMcpServerByNameResponse, DeleteMcpServerByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/by-name' - }); -}; - -/** - * Enable or disable an MCP server by body name - */ -export const setMcpServerEnabledByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetMcpServerEnabledByNameData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetMcpServerEnabledByNameResponse, SetMcpServerEnabledByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/enabled' - }); -}; - -/** - * Test an MCP server connection by body name - */ -export const testMcpServerByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestMcpServerByNameData, ThrowOnError>) => { - return (options?.client ?? client).post<TestMcpServerByNameResponse, TestMcpServerByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/test' - }); -}; - -/** - * Update an MCP server - */ -export const updateMcpServer = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateMcpServerData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateMcpServerResponse, UpdateMcpServerError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/{server_name}' - }); -}; - -/** - * Delete an MCP server - */ -export const deleteMcpServer = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteMcpServerData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteMcpServerResponse, DeleteMcpServerError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/{server_name}' - }); -}; - -/** - * Enable or disable an MCP server - */ -export const setMcpServerEnabled = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetMcpServerEnabledData, ThrowOnError>) => { - return (options?.client ?? client).patch<SetMcpServerEnabledResponse, SetMcpServerEnabledError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/{server_name}/enabled' - }); -}; - -/** - * Test an MCP server connection - */ -export const testMcpServer = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestMcpServerData, ThrowOnError>) => { - return (options?.client ?? client).post<TestMcpServerResponse, TestMcpServerError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/servers/{server_name}/test' - }); -}; - -/** - * Sync MCP servers from ModelScope - */ -export const syncModelScopeMcpServers = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<SyncModelScopeMcpServersData, ThrowOnError>) => { - return (options?.client ?? client).post<SyncModelScopeMcpServersResponse, SyncModelScopeMcpServersError, ThrowOnError>({ - ...options, - url: '/api/v1/mcp/providers/modelscope/sync' - }); -}; - -/** - * List skills - */ -export const listSkills = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListSkillsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListSkillsResponse, ListSkillsError, ThrowOnError>({ - ...options, - url: '/api/v1/skills' - }); -}; - -/** - * Upload or import a skill archive - */ -export const uploadSkill = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadSkillData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadSkillResponse, UploadSkillError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/skills' - }); -}; - -/** - * Upload multiple skill archives - */ -export const uploadSkillsBatch = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadSkillsBatchData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadSkillsBatchResponse, UploadSkillsBatchError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/skills/batch' - }); -}; - -/** - * Update skill metadata or enabled state by body name - */ -export const updateSkillByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSkillByNameData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateSkillByNameResponse, UpdateSkillByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/by-name' - }); -}; - -/** - * Delete a skill by query name - */ -export const deleteSkillByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteSkillByNameData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteSkillByNameResponse, DeleteSkillByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/by-name' - }); -}; - -/** - * Download a skill archive by query name - */ -export const downloadSkillByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DownloadSkillByNameData, ThrowOnError>) => { - return (options?.client ?? client).get<DownloadSkillByNameResponse, DownloadSkillByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/archive' - }); -}; - -/** - * List files in a skill by query name - */ -export const listSkillFilesByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListSkillFilesByNameData, ThrowOnError>) => { - return (options?.client ?? client).get<ListSkillFilesByNameResponse, ListSkillFilesByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/files' - }); -}; - -/** - * Get skill file content by query name and path - */ -export const getSkillFileByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetSkillFileByNameData, ThrowOnError>) => { - return (options?.client ?? client).get<GetSkillFileByNameResponse, GetSkillFileByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/file' - }); -}; - -/** - * Update skill file content by body name and path - */ -export const updateSkillFileByName = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSkillFileByNameData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateSkillFileByNameResponse, UpdateSkillFileByNameError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/file' - }); -}; - -/** - * Update skill metadata or enabled state - */ -export const updateSkill = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSkillData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateSkillResponse, UpdateSkillError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/{skill_name}' - }); -}; - -/** - * Delete a skill - */ -export const deleteSkill = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteSkillData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteSkillResponse, DeleteSkillError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/{skill_name}' - }); -}; - -/** - * Download a skill archive - */ -export const downloadSkill = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DownloadSkillData, ThrowOnError>) => { - return (options?.client ?? client).get<DownloadSkillResponse, DownloadSkillError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/{skill_name}/archive' - }); -}; - -/** - * List files in a skill - */ -export const listSkillFiles = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListSkillFilesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListSkillFilesResponse, ListSkillFilesError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/{skill_name}/files' - }); -}; - -/** - * Get skill file content - */ -export const getSkillFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetSkillFileData, ThrowOnError>) => { - return (options?.client ?? client).get<GetSkillFileResponse, GetSkillFileError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/{skill_name}/files/{file_path}' - }); -}; - -/** - * Update skill file content - */ -export const updateSkillFile = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSkillFileData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateSkillFileResponse, UpdateSkillFileError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/{skill_name}/files/{file_path}' - }); -}; - -/** - * List Shipyard Neo skill candidates - */ -export const listNeoSkillCandidates = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListNeoSkillCandidatesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListNeoSkillCandidatesResponse, ListNeoSkillCandidatesError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/candidates' - }); -}; - -/** - * List Shipyard Neo skill releases - */ -export const listNeoSkillReleases = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListNeoSkillReleasesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListNeoSkillReleasesResponse, ListNeoSkillReleasesError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/releases' - }); -}; - -/** - * Get a Shipyard Neo skill payload - */ -export const getNeoSkillPayload = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetNeoSkillPayloadData, ThrowOnError>) => { - return (options?.client ?? client).get<GetNeoSkillPayloadResponse, GetNeoSkillPayloadError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/payload' - }); -}; - -/** - * Evaluate a Shipyard Neo skill candidate - */ -export const evaluateNeoSkillCandidate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<EvaluateNeoSkillCandidateData, ThrowOnError>) => { - return (options?.client ?? client).post<EvaluateNeoSkillCandidateResponse, EvaluateNeoSkillCandidateError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/evaluate' - }); -}; - -/** - * Promote a Shipyard Neo skill candidate - */ -export const promoteNeoSkillCandidate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<PromoteNeoSkillCandidateData, ThrowOnError>) => { - return (options?.client ?? client).post<PromoteNeoSkillCandidateResponse, PromoteNeoSkillCandidateError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/promote' - }); -}; - -/** - * Roll back a Shipyard Neo skill release - */ -export const rollbackNeoSkillRelease = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RollbackNeoSkillReleaseData, ThrowOnError>) => { - return (options?.client ?? client).post<RollbackNeoSkillReleaseResponse, RollbackNeoSkillReleaseError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/rollback' - }); -}; - -/** - * Sync a Shipyard Neo skill release - */ -export const syncNeoSkillRelease = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SyncNeoSkillReleaseData, ThrowOnError>) => { - return (options?.client ?? client).post<SyncNeoSkillReleaseResponse, SyncNeoSkillReleaseError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/sync' - }); -}; - -/** - * Delete a Shipyard Neo skill candidate - */ -export const deleteNeoSkillCandidate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteNeoSkillCandidateData, ThrowOnError>) => { - return (options?.client ?? client).post<DeleteNeoSkillCandidateResponse, DeleteNeoSkillCandidateError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/candidates/delete' - }); -}; - -/** - * Delete a Shipyard Neo skill release - */ -export const deleteNeoSkillRelease = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteNeoSkillReleaseData, ThrowOnError>) => { - return (options?.client ?? client).post<DeleteNeoSkillReleaseResponse, DeleteNeoSkillReleaseError, ThrowOnError>({ - ...options, - url: '/api/v1/skills/neo/releases/delete' - }); -}; - -/** - * List knowledge bases - */ -export const listKnowledgeBases = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListKnowledgeBasesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListKnowledgeBasesResponse, ListKnowledgeBasesError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases' - }); -}; - -/** - * Create a knowledge base - */ -export const createKnowledgeBase = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateKnowledgeBaseData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateKnowledgeBaseResponse, CreateKnowledgeBaseError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases' - }); -}; - -/** - * Get a knowledge base - */ -export const getKnowledgeBase = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetKnowledgeBaseData, ThrowOnError>) => { - return (options?.client ?? client).get<GetKnowledgeBaseResponse, GetKnowledgeBaseError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}' - }); -}; - -/** - * Update a knowledge base - */ -export const updateKnowledgeBase = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateKnowledgeBaseData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateKnowledgeBaseResponse, UpdateKnowledgeBaseError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}' - }); -}; - -/** - * Delete a knowledge base - */ -export const deleteKnowledgeBase = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteKnowledgeBaseData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteKnowledgeBaseResponse, DeleteKnowledgeBaseError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}' - }); -}; - -/** - * Get knowledge base stats - */ -export const getKnowledgeBaseStats = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetKnowledgeBaseStatsData, ThrowOnError>) => { - return (options?.client ?? client).get<GetKnowledgeBaseStatsResponse, GetKnowledgeBaseStatsError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/stats' - }); -}; - -/** - * List knowledge base documents - */ -export const listKnowledgeDocuments = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListKnowledgeDocumentsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListKnowledgeDocumentsResponse, ListKnowledgeDocumentsError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/documents' - }); -}; - -/** - * Upload a document to a knowledge base - */ -export const uploadKnowledgeDocument = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadKnowledgeDocumentData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadKnowledgeDocumentResponse, UploadKnowledgeDocumentError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/knowledge-bases/{kb_id}/documents' - }); -}; - -/** - * Import documents already available on the server - */ -export const importKnowledgeDocuments = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ImportKnowledgeDocumentsData, ThrowOnError>) => { - return (options?.client ?? client).post<ImportKnowledgeDocumentsResponse, ImportKnowledgeDocumentsError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/documents/import' - }); -}; - -/** - * Import a document from URL - */ -export const importKnowledgeDocumentFromUrl = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ImportKnowledgeDocumentFromUrlData, ThrowOnError>) => { - return (options?.client ?? client).post<ImportKnowledgeDocumentFromUrlResponse, ImportKnowledgeDocumentFromUrlError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/documents/import-url' - }); -}; - -/** - * Get a knowledge base document - */ -export const getKnowledgeDocument = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetKnowledgeDocumentData, ThrowOnError>) => { - return (options?.client ?? client).get<GetKnowledgeDocumentResponse, GetKnowledgeDocumentError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/documents/{document_id}' - }); -}; - -/** - * Delete a knowledge base document - */ -export const deleteKnowledgeDocument = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteKnowledgeDocumentData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteKnowledgeDocumentResponse, DeleteKnowledgeDocumentError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/documents/{document_id}' - }); -}; - -/** - * List document chunks - */ -export const listKnowledgeChunks = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ListKnowledgeChunksData, ThrowOnError>) => { - return (options?.client ?? client).get<ListKnowledgeChunksResponse, ListKnowledgeChunksError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/chunks' - }); -}; - -/** - * Delete a document chunk - */ -export const deleteKnowledgeChunk = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteKnowledgeChunkData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteKnowledgeChunkResponse, DeleteKnowledgeChunkError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/chunks/{chunk_id}' - }); -}; - -/** - * Retrieve knowledge base chunks - */ -export const retrieveKnowledgeBase = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RetrieveKnowledgeBaseData, ThrowOnError>) => { - return (options?.client ?? client).post<RetrieveKnowledgeBaseResponse, RetrieveKnowledgeBaseError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/{kb_id}/retrieve' - }); -}; - -/** - * Get knowledge base import task progress - */ -export const getKnowledgeTask = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetKnowledgeTaskData, ThrowOnError>) => { - return (options?.client ?? client).get<GetKnowledgeTaskResponse, GetKnowledgeTaskError, ThrowOnError>({ - ...options, - url: '/api/v1/knowledge-bases/tasks/{task_id}' - }); -}; - -/** - * Get persona folder tree - */ -export const getPersonaTree = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetPersonaTreeResponse, GetPersonaTreeError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/tree' - }); -}; - -/** - * List personas - */ -export const listPersonas = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListPersonasData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPersonasResponse, ListPersonasError, ThrowOnError>({ - ...options, - url: '/api/v1/personas' - }); -}; - -/** - * Create a persona - */ -export const createPersona = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreatePersonaData, ThrowOnError>) => { - return (options?.client ?? client).post<CreatePersonaResponse, CreatePersonaError, ThrowOnError>({ - ...options, - url: '/api/v1/personas' - }); -}; - -/** - * Get a persona by query ID - */ -export const getPersonaById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPersonaByIdData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPersonaByIdResponse, GetPersonaByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/by-id' - }); -}; - -/** - * Update a persona by body ID - */ -export const updatePersonaById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePersonaByIdData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdatePersonaByIdResponse, UpdatePersonaByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/by-id' - }); -}; - -/** - * Delete a persona by query ID - */ -export const deletePersonaById = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePersonaByIdData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePersonaByIdResponse, DeletePersonaByIdError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/by-id' - }); -}; - -/** - * Get a persona - */ -export const getPersona = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetPersonaData, ThrowOnError>) => { - return (options?.client ?? client).get<GetPersonaResponse, GetPersonaError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/{persona_id}' - }); -}; - -/** - * Update a persona - */ -export const updatePersona = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePersonaData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdatePersonaResponse, UpdatePersonaError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/{persona_id}' - }); -}; - -/** - * Delete a persona - */ -export const deletePersona = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePersonaData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePersonaResponse, DeletePersonaError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/{persona_id}' - }); -}; - -/** - * List persona folders - */ -export const listPersonaFolders = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListPersonaFoldersData, ThrowOnError>) => { - return (options?.client ?? client).get<ListPersonaFoldersResponse, ListPersonaFoldersError, ThrowOnError>({ - ...options, - url: '/api/v1/persona-folders' - }); -}; - -/** - * Create a persona folder - */ -export const createPersonaFolder = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreatePersonaFolderData, ThrowOnError>) => { - return (options?.client ?? client).post<CreatePersonaFolderResponse, CreatePersonaFolderError, ThrowOnError>({ - ...options, - url: '/api/v1/persona-folders' - }); -}; - -/** - * Update a persona folder - */ -export const updatePersonaFolder = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdatePersonaFolderData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdatePersonaFolderResponse, UpdatePersonaFolderError, ThrowOnError>({ - ...options, - url: '/api/v1/persona-folders/{folder_id}' - }); -}; - -/** - * Delete a persona folder - */ -export const deletePersonaFolder = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeletePersonaFolderData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeletePersonaFolderResponse, DeletePersonaFolderError, ThrowOnError>({ - ...options, - url: '/api/v1/persona-folders/{folder_id}' - }); -}; - -/** - * Move a persona or folder - */ -export const movePersonaItem = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<MovePersonaItemData, ThrowOnError>) => { - return (options?.client ?? client).post<MovePersonaItemResponse, MovePersonaItemError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/move' - }); -}; - -/** - * Reorder personas or folders - */ -export const reorderPersonaItems = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReorderPersonaItemsData, ThrowOnError>) => { - return (options?.client ?? client).post<ReorderPersonaItemsResponse, ReorderPersonaItemsError, ThrowOnError>({ - ...options, - url: '/api/v1/personas/reorder' - }); -}; - -/** - * List active UMOs and session status - */ -export const listSessions = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListSessionsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListSessionsResponse, ListSessionsError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions' - }); -}; - -/** - * List active and known UMOs - */ -export const listActiveUmos = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListActiveUmosResponse, ListActiveUmosError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions/active-umos' - }); -}; - -/** - * List session rules - */ -export const listSessionRules = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListSessionRulesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListSessionRulesResponse, ListSessionRulesError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions/rules' - }); -}; - -/** - * Update or create a session rule - */ -export const upsertSessionRule = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpsertSessionRuleData, ThrowOnError>) => { - return (options?.client ?? client).post<UpsertSessionRuleResponse, UpsertSessionRuleError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions/rules' - }); -}; - -/** - * Delete one or more session rules - */ -export const deleteSessionRules = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteSessionRulesData, ThrowOnError>) => { - return (options?.client ?? client).post<DeleteSessionRulesResponse, DeleteSessionRulesError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions/rules/delete' - }); -}; - -/** - * Batch update session provider selection - */ -export const batchUpdateSessionProvider = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<BatchUpdateSessionProviderData, ThrowOnError>) => { - return (options?.client ?? client).patch<BatchUpdateSessionProviderResponse, BatchUpdateSessionProviderError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions/provider' - }); -}; - -/** - * Batch update session service settings - */ -export const batchUpdateSessionService = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<BatchUpdateSessionServiceData, ThrowOnError>) => { - return (options?.client ?? client).patch<BatchUpdateSessionServiceResponse, BatchUpdateSessionServiceError, ThrowOnError>({ - ...options, - url: '/api/v1/sessions/service' - }); -}; - -/** - * List session groups - */ -export const listSessionGroups = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListSessionGroupsResponse, ListSessionGroupsError, ThrowOnError>({ - ...options, - url: '/api/v1/session-groups' - }); -}; - -/** - * Create a session group - */ -export const createSessionGroup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateSessionGroupData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateSessionGroupResponse, CreateSessionGroupError, ThrowOnError>({ - ...options, - url: '/api/v1/session-groups' - }); -}; - -/** - * Update a session group - */ -export const updateSessionGroup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSessionGroupData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateSessionGroupResponse, UpdateSessionGroupError, ThrowOnError>({ - ...options, - url: '/api/v1/session-groups/{group_id}' - }); -}; - -/** - * Delete a session group - */ -export const deleteSessionGroup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteSessionGroupData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteSessionGroupResponse, DeleteSessionGroupError, ThrowOnError>({ - ...options, - url: '/api/v1/session-groups/{group_id}' - }); -}; - -/** - * List conversation history data - */ -export const listConversations = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListConversationsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListConversationsResponse, ListConversationsError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations' - }); -}; - -/** - * Delete multiple conversations - */ -export const batchDeleteConversations = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<BatchDeleteConversationsData, ThrowOnError>) => { - return (options?.client ?? client).post<BatchDeleteConversationsResponse, BatchDeleteConversationsError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations/batch-delete' - }); -}; - -/** - * Get conversation details and messages - */ -export const getConversation = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetConversationData, ThrowOnError>) => { - return (options?.client ?? client).get<GetConversationResponse, GetConversationError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations/{conversation_id}' - }); -}; - -/** - * Update conversation metadata - */ -export const updateConversation = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateConversationData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateConversationResponse, UpdateConversationError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations/{conversation_id}' - }); -}; - -/** - * Delete a conversation - */ -export const deleteConversation = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteConversationData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteConversationResponse, DeleteConversationError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations/{conversation_id}' - }); -}; - -/** - * Replace conversation message history - */ -export const replaceConversationMessages = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReplaceConversationMessagesData, ThrowOnError>) => { - return (options?.client ?? client).put<ReplaceConversationMessagesResponse, ReplaceConversationMessagesError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations/{conversation_id}/messages' - }); -}; - -/** - * Export conversations - */ -export const exportConversations = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ExportConversationsData, ThrowOnError>) => { - return (options?.client ?? client).post<ExportConversationsResponse, ExportConversationsError, ThrowOnError>({ - ...options, - url: '/api/v1/conversations/export' - }); -}; - -/** - * Get runtime statistics - */ -export const getStats = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<GetStatsData, ThrowOnError>) => { - return (options?.client ?? client).get<GetStatsResponse, GetStatsError, ThrowOnError>({ - ...options, - url: '/api/v1/stats' - }); -}; - -/** - * Get provider token usage statistics - */ -export const getProviderTokenStats = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<GetProviderTokenStatsData, ThrowOnError>) => { - return (options?.client ?? client).get<GetProviderTokenStatsResponse, GetProviderTokenStatsError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/provider-tokens' - }); -}; - -/** - * Get AstrBot version - */ -export const getVersion = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetVersionResponse, GetVersionError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/version' - }); -}; - -/** - * Get public WebUI and AstrBot versions - */ -export const getPublicVersions = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetPublicVersionsResponse, GetPublicVersionsError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/versions' - }); -}; - -/** - * Get first-run dashboard notice content - */ -export const getFirstNotice = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<GetFirstNoticeData, ThrowOnError>) => { - return (options?.client ?? client).get<GetFirstNoticeResponse, GetFirstNoticeError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/first-notice' - }); -}; - -/** - * Test a GitHub proxy endpoint - */ -export const testGhproxyConnection = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<TestGhproxyConnectionData, ThrowOnError>) => { - return (options?.client ?? client).post<TestGhproxyConnectionResponse, TestGhproxyConnectionError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/ghproxy/test' - }); -}; - -/** - * List available changelog versions - */ -export const listChangelogVersions = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListChangelogVersionsResponse, ListChangelogVersionsError, ThrowOnError>({ - ...options, - url: '/api/v1/changelogs' - }); -}; - -/** - * Get changelog content for a version - */ -export const getChangelog = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetChangelogData, ThrowOnError>) => { - return (options?.client ?? client).get<GetChangelogResponse, GetChangelogError, ThrowOnError>({ - ...options, - url: '/api/v1/changelogs/{version}' - }); -}; - -/** - * Get runtime start time - */ -export const getStartTime = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetStartTimeResponse, GetStartTimeError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/start-time' - }); -}; - -/** - * Get storage status - */ -export const getStorageStatus = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetStorageStatusResponse, GetStorageStatusError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/storage' - }); -}; - -/** - * Clean storage - */ -export const cleanupStorage = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<CleanupStorageData, ThrowOnError>) => { - return (options?.client ?? client).post<CleanupStorageResponse, CleanupStorageError, ThrowOnError>({ - ...options, - url: '/api/v1/stats/storage/cleanup' - }); -}; - -/** - * Restart AstrBot core - */ -export const restartCore = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).post<RestartCoreResponse, RestartCoreError, ThrowOnError>({ - ...options, - url: '/api/v1/system/restart' - }); -}; - -/** - * List backups - */ -export const listBackups = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListBackupsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListBackupsResponse, ListBackupsError, ThrowOnError>({ - ...options, - url: '/api/v1/backups' - }); -}; - -/** - * Export a backup - */ -export const createBackup = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<CreateBackupData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateBackupResponse, CreateBackupError, ThrowOnError>({ - ...options, - url: '/api/v1/backups' - }); -}; - -/** - * Upload a backup file - */ -export const uploadBackup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadBackupData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadBackupResponse, UploadBackupError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/backups/upload' - }); -}; - -/** - * Initialize chunked backup upload - */ -export const initBackupUpload = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<InitBackupUploadData, ThrowOnError>) => { - return (options?.client ?? client).post<InitBackupUploadResponse, InitBackupUploadError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/upload/init' - }); -}; - -/** - * Upload a backup chunk - */ -export const uploadBackupChunk = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UploadBackupChunkData, ThrowOnError>) => { - return (options?.client ?? client).post<UploadBackupChunkResponse, UploadBackupChunkError, ThrowOnError>({ - ...options, - ...formDataBodySerializer, - headers: { - 'Content-Type': null, - ...options?.headers - }, - url: '/api/v1/backups/upload/chunk' - }); -}; - -/** - * Complete chunked backup upload - */ -export const completeBackupUpload = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CompleteBackupUploadData, ThrowOnError>) => { - return (options?.client ?? client).post<CompleteBackupUploadResponse, CompleteBackupUploadError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/upload/complete' - }); -}; - -/** - * Abort chunked backup upload - */ -export const abortBackupUpload = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<AbortBackupUploadData, ThrowOnError>) => { - return (options?.client ?? client).post<AbortBackupUploadResponse, AbortBackupUploadError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/upload/abort' - }); -}; - -/** - * Get backup task progress - */ -export const getBackupProgress = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetBackupProgressData, ThrowOnError>) => { - return (options?.client ?? client).get<GetBackupProgressResponse, GetBackupProgressError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/tasks/{task_id}' - }); -}; - -/** - * Download a backup - */ -export const downloadBackup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DownloadBackupData, ThrowOnError>) => { - return (options?.client ?? client).get<DownloadBackupResponse, DownloadBackupError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/{filename}' - }); -}; - -/** - * Rename a backup - */ -export const renameBackup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RenameBackupData, ThrowOnError>) => { - return (options?.client ?? client).patch<RenameBackupResponse, RenameBackupError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/{filename}' - }); -}; - -/** - * Delete a backup - */ -export const deleteBackup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteBackupData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteBackupResponse, DeleteBackupError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/{filename}' - }); -}; - -/** - * Check a backup before import - */ -export const checkBackup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CheckBackupData, ThrowOnError>) => { - return (options?.client ?? client).post<CheckBackupResponse, CheckBackupError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/{filename}/check' - }); -}; - -/** - * Import a backup - */ -export const importBackup = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ImportBackupData, ThrowOnError>) => { - return (options?.client ?? client).post<ImportBackupResponse, ImportBackupError, ThrowOnError>({ - ...options, - url: '/api/v1/backups/{filename}/import' - }); -}; - -/** - * Check for updates - */ -export const checkUpdate = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<CheckUpdateResponse, CheckUpdateError, ThrowOnError>({ - ...options, - url: '/api/v1/updates/check' - }); -}; - -/** - * List releases - */ -export const listReleases = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListReleasesData, ThrowOnError>) => { - return (options?.client ?? client).get<ListReleasesResponse, ListReleasesError, ThrowOnError>({ - ...options, - url: '/api/v1/updates/releases' - }); -}; - -/** - * Update AstrBot core - */ -export const updateCore = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<UpdateCoreData, ThrowOnError>) => { - return (options?.client ?? client).post<UpdateCoreResponse, UpdateCoreError, ThrowOnError>({ - ...options, - url: '/api/v1/updates/core' - }); -}; - -/** - * Update dashboard assets - */ -export const updateDashboard = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<UpdateDashboardData, ThrowOnError>) => { - return (options?.client ?? client).post<UpdateDashboardResponse, UpdateDashboardError, ThrowOnError>({ - ...options, - url: '/api/v1/updates/dashboard' - }); -}; - -/** - * Get update progress - */ -export const getUpdateProgress = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetUpdateProgressData, ThrowOnError>) => { - return (options?.client ?? client).get<GetUpdateProgressResponse, GetUpdateProgressError, ThrowOnError>({ - ...options, - url: '/api/v1/updates/progress/{task_id}' - }); -}; - -/** - * Install a Python package - */ -export const installPipPackage = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<InstallPipPackageData, ThrowOnError>) => { - return (options?.client ?? client).post<InstallPipPackageResponse, InstallPipPackageError, ThrowOnError>({ - ...options, - url: '/api/v1/pip/install' - }); -}; - -/** - * List cron jobs - */ -export const listCronJobs = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<ListCronJobsData, ThrowOnError>) => { - return (options?.client ?? client).get<ListCronJobsResponse, ListCronJobsError, ThrowOnError>({ - ...options, - url: '/api/v1/cron/jobs' - }); -}; - -/** - * Create a cron job - */ -export const createCronJob = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateCronJobData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateCronJobResponse, CreateCronJobError, ThrowOnError>({ - ...options, - url: '/api/v1/cron/jobs' - }); -}; - -/** - * Update a cron job - */ -export const updateCronJob = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateCronJobData, ThrowOnError>) => { - return (options?.client ?? client).patch<UpdateCronJobResponse, UpdateCronJobError, ThrowOnError>({ - ...options, - url: '/api/v1/cron/jobs/{job_id}' - }); -}; - -/** - * Delete a cron job - */ -export const deleteCronJob = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteCronJobData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteCronJobResponse, DeleteCronJobError, ThrowOnError>({ - ...options, - url: '/api/v1/cron/jobs/{job_id}' - }); -}; - -/** - * Run a cron job immediately - */ -export const runCronJob = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<RunCronJobData, ThrowOnError>) => { - return (options?.client ?? client).post<RunCronJobResponse, RunCronJobError, ThrowOnError>({ - ...options, - url: '/api/v1/cron/jobs/{job_id}/run' - }); -}; - -/** - * Stream live logs - */ -export const streamLiveLogs = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<StreamLiveLogsResponse, StreamLiveLogsError, ThrowOnError>({ - ...options, - url: '/api/v1/logs/live' - }); -}; - -/** - * Get log history - */ -export const getLogHistory = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetLogHistoryResponse, GetLogHistoryError, ThrowOnError>({ - ...options, - url: '/api/v1/logs/history' - }); -}; - -/** - * Get trace settings - */ -export const getTraceSettings = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetTraceSettingsResponse, GetTraceSettingsError, ThrowOnError>({ - ...options, - url: '/api/v1/trace/settings' - }); -}; - -/** - * Update trace settings - */ -export const updateTraceSettings = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateTraceSettingsData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateTraceSettingsResponse, UpdateTraceSettingsError, ThrowOnError>({ - ...options, - url: '/api/v1/trace/settings' - }); -}; - -/** - * List text-to-image templates - */ -export const listT2iTemplates = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListT2iTemplatesResponse, ListT2iTemplatesError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates' - }); -}; - -/** - * Create a text-to-image template - */ -export const createT2iTemplate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<CreateT2iTemplateData, ThrowOnError>) => { - return (options?.client ?? client).post<CreateT2iTemplateResponse, CreateT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates' - }); -}; - -/** - * Get active text-to-image template - */ -export const getActiveT2iTemplate = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetActiveT2iTemplateResponse, GetActiveT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates/active' - }); -}; - -/** - * Set active text-to-image template - */ -export const setActiveT2iTemplate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<SetActiveT2iTemplateData, ThrowOnError>) => { - return (options?.client ?? client).put<SetActiveT2iTemplateResponse, SetActiveT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates/active' - }); -}; - -/** - * Reset the default text-to-image template - */ -export const resetDefaultT2iTemplate = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).post<ResetDefaultT2iTemplateResponse, ResetDefaultT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates/default/reset' - }); -}; - -/** - * Get a text-to-image template - */ -export const getT2iTemplate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<GetT2iTemplateData, ThrowOnError>) => { - return (options?.client ?? client).get<GetT2iTemplateResponse, GetT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates/{name}' - }); -}; - -/** - * Update a text-to-image template - */ -export const updateT2iTemplate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateT2iTemplateData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateT2iTemplateResponse, UpdateT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates/{name}' - }); -}; - -/** - * Delete a text-to-image template - */ -export const deleteT2iTemplate = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<DeleteT2iTemplateData, ThrowOnError>) => { - return (options?.client ?? client).delete<DeleteT2iTemplateResponse, DeleteT2iTemplateError, ThrowOnError>({ - ...options, - url: '/api/v1/t2i/templates/{name}' - }); -}; - -/** - * Get subagent orchestrator configuration - */ -export const getSubagentConfig = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<GetSubagentConfigResponse, GetSubagentConfigError, ThrowOnError>({ - ...options, - url: '/api/v1/subagents/config' - }); -}; - -/** - * Update subagent orchestrator configuration - */ -export const updateSubagentConfig = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<UpdateSubagentConfigData, ThrowOnError>) => { - return (options?.client ?? client).put<UpdateSubagentConfigResponse, UpdateSubagentConfigError, ThrowOnError>({ - ...options, - url: '/api/v1/subagents/config' - }); -}; - -/** - * List tools available to subagents - */ -export const listSubagentAvailableTools = <ThrowOnError extends boolean = false>(options?: OptionsLegacyParser<unknown, ThrowOnError>) => { - return (options?.client ?? client).get<ListSubagentAvailableToolsResponse, ListSubagentAvailableToolsError, ThrowOnError>({ - ...options, - url: '/api/v1/subagents/available-tools' - }); -}; - -/** - * Verify a platform webhook - */ -export const verifyPlatformWebhook = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<VerifyPlatformWebhookData, ThrowOnError>) => { - return (options?.client ?? client).get<VerifyPlatformWebhookResponse, VerifyPlatformWebhookError, ThrowOnError>({ - ...options, - url: '/api/v1/webhooks/platforms/{webhook_uuid}' - }); -}; - -/** - * Receive a platform webhook - */ -export const receivePlatformWebhook = <ThrowOnError extends boolean = false>(options: OptionsLegacyParser<ReceivePlatformWebhookData, ThrowOnError>) => { - return (options?.client ?? client).post<ReceivePlatformWebhookResponse, ReceivePlatformWebhookError, ThrowOnError>({ - ...options, - url: '/api/v1/webhooks/platforms/{webhook_uuid}' - }); -}; \ No newline at end of file diff --git a/legacy-dashboard/src/api/generated/openapi-v1/types.gen.ts b/legacy-dashboard/src/api/generated/openapi-v1/types.gen.ts deleted file mode 100644 index 3b2314e4..00000000 --- a/legacy-dashboard/src/api/generated/openapi-v1/types.gen.ts +++ /dev/null @@ -1,3539 +0,0 @@ -// This file is auto-generated by @hey-api/openapi-ts - -export type BackupChunkUploadRequest = { - upload_id: string; - chunk_index: number; - chunk: (Blob | File); -}; - -export type BackupExportRequest = { - include?: Array<(string)>; - exclude?: Array<(string)>; -}; - -export type BackupImportRequest = { - confirmed?: boolean; -}; - -export type BackupRenameRequest = { - new_name: string; -}; - -export type BackupUploadInitRequest = { - filename: string; - total_size: number; -}; - -export type BackupUploadRequest = { - file: (Blob | File); -}; - -export type BackupUploadSessionRequest = { - upload_id: string; -}; - -export type BatchSessionProviderRequest = UmoListRequest & { - provider_id: string; - provider_type: 'chat_completion' | 'speech_to_text' | 'text_to_speech'; -}; - -export type provider_type = 'chat_completion' | 'speech_to_text' | 'text_to_speech'; - -export type BatchSessionServiceRequest = UmoListRequest & { - session_enabled?: boolean; - llm_enabled?: boolean; - tts_enabled?: boolean; -}; - -export type BotConfigRequest = { - id?: string; - name?: string; - /** - * Platform adapter type, such as aiocqhttp, telegram, lark. - */ - type: string; - enabled?: boolean; - config: DynamicConfig; -}; - -export type BotRegistrationRequest = { - action: 'start' | 'poll'; - platform_config?: DynamicConfig; - registration_code?: string; - device_code?: string; - qrcode?: string; - [key: string]: unknown | string | DynamicConfig; -}; - -export type action = 'start' | 'poll'; - -export type ChatMessagePatchRequest = { - content: { - [key: string]: unknown; - }; -}; - -export type ChatMessageRegenerateRequest = { - selected_provider?: string; - selected_model?: string; - enable_streaming?: boolean; -}; - -export type ChatProjectRequest = { - title?: string; - emoji?: string; - description?: string; - workspace_type?: 'session' | 'project' | 'custom'; - workspace_path?: string; -}; - -export type workspace_type = 'session' | 'project' | 'custom'; - -export type ChatRequest = { - /** - * Caller-declared WebChat sender/session owner. This value is used as the message sender identity and may participate in sender-ID-based command permission checks. Treat chat-scoped API keys as trusted backend credentials and map or validate usernames before accepting end-user input. - */ - username?: string; - session_id?: string; - /** - * Deprecated alias for session_id. - */ - conversation_id?: string; - message: (string | Array<MessagePart>); - config_id?: string; - config_name?: string; - selected_provider?: string; - selected_model?: string; - enable_streaming?: boolean; - /** - * Internal WebUI flag for edit/regenerate flows. - */ - _skip_user_history?: boolean; - /** - * Internal WebUI checkpoint override. - */ - _llm_checkpoint_id?: string; - /** - * Internal WebUI platform history override. - */ - _platform_history_id?: string; - /** - * Internal WebUI side-thread context. - */ - _thread_selected_text?: string; -}; - -export type ChatSessionBatchDeleteRequest = { - session_ids: Array<(string)>; -}; - -export type ChatSessionPatchRequest = { - display_name?: string; -}; - -export type ChatThreadCreateRequest = { - session_id: string; - parent_message_id: (string | number); - selected_text: string; -}; - -export type ChatThreadMessageRequest = { - message: (string | Array<MessagePart>); - selected_provider?: string; - selected_model?: string; - enable_streaming?: boolean; -}; - -export type CommandPatchRequest = { - enabled?: boolean; - alias?: string; - aliases?: Array<(string)>; - permission_group?: string; -}; - -export type ConfigRoutesReplaceRequest = { - routing: { - [key: string]: (string); - }; -}; - -export type ConfigRouteUpsertRequest = { - /** - * Use "default" to remove a custom route and fall back. - */ - config_id: string; -}; - -export type ConversationBatchDeleteRequest = { - conversations: Array<ConversationRef>; -}; - -export type ConversationExportRequest = { - conversations?: Array<ConversationRef>; - conversation_ids?: Array<(string)>; - format?: 'json' | 'markdown'; -}; - -export type format = 'json' | 'markdown'; - -export type ConversationMessagesReplaceRequest = { - user_id?: string; - messages?: Array<{ - [key: string]: unknown; - }>; - history?: Array<{ - [key: string]: unknown; - }>; -}; - -export type ConversationPatchRequest = { - title?: string; - persona_id?: string; -}; - -export type ConversationRef = { - user_id: string; - cid: string; -}; - -export type CreateApiKeyRequest = { - name: string; - scopes?: Array<('bot' | 'provider' | 'persona' | 'im' | 'config' | 'chat' | 'data' | 'file' | 'plugin' | 'mcp' | 'skill')>; - expires_at?: string; - expires_in_days?: number; -}; - -export type CreateConfigProfileRequest = { - name?: string; - config?: DynamicConfig; -}; - -export type CronJobPatchRequest = CronJobRequest; - -export type CronJobRequest = { - name?: string; - cron_expression?: string; - timezone?: string; - session?: string; - note?: string; - description?: string; - persona_id?: string; - provider_id?: string; - enabled?: boolean; - run_once?: boolean; - run_at?: string; - payload?: { - [key: string]: unknown; - }; - [key: string]: unknown | string | boolean; -}; - -export type DynamicConfig = { - [key: string]: unknown; -}; - -export type EnabledPatch = { - enabled: boolean; -}; - -export type ErrorEnvelope = { - status: "error"; - message: string; - data?: unknown; -}; - -export type FileUploadRequest = { - file: (Blob | File); -}; - -export type GhproxyTestRequest = { - proxy_url: string; -}; - -export type ImMessageRequest = { - umo: string; - message: (string | Array<MessagePart>); -}; - -export type JsonSchema = { - [key: string]: unknown; -}; - -export type KnowledgeBaseCreateRequest = KnowledgeBaseRequest & { - kb_name: string; - embedding_provider_id: string; -}; - -export type KnowledgeBaseRequest = { - kb_name?: string; - description?: string; - emoji?: string; - embedding_provider_id?: (string) | null; - rerank_provider_id?: (string) | null; - chunk_size?: number; - chunk_overlap?: number; - top_k_dense?: number; - top_k_sparse?: number; - top_m_final?: number; -}; - -export type KnowledgeDocumentImportRequest = { - paths: Array<(string)>; - parser?: string; -}; - -export type KnowledgeDocumentUploadRequest = { - file: (Blob | File); -}; - -export type KnowledgeDocumentUrlImportRequest = { - url: string; - parser?: string; -}; - -export type KnowledgeRetrieveRequest = { - query: string; - top_k?: number; - score_threshold?: number; -}; - -export type LoginRequest = { - username: string; - password: string; - /** - * TOTP code or recovery code when two-factor authentication is required. - */ - code?: string; - trust_device_flag?: boolean; -}; - -export type McpServerConfig = { - name: string; - enabled?: boolean; - transport?: 'stdio' | 'sse' | 'streamable_http'; - command?: string; - args?: Array<(string)>; - url?: string; - headers?: { - [key: string]: (string); - }; - timeout?: number; - [key: string]: unknown | string | boolean | number; -}; - -export type transport = 'stdio' | 'sse' | 'streamable_http'; - -export type MessagePart = { - type: 'text' | 'plain' | 'image' | 'file' | 'audio' | 'record' | 'video' | 'reply'; - text?: string; - attachment_id?: string; - url?: string; - filename?: string; - stored_filename?: string; - mime_type?: string; - [key: string]: unknown | string; -}; - -export type type = 'text' | 'plain' | 'image' | 'file' | 'audio' | 'record' | 'video' | 'reply'; - -export type ModelScopeSyncRequest = { - access_token?: string; -}; - -export type NameRequest = { - name: string; -}; - -export type NeoCandidateActionRequest = { - candidate_id: string; - [key: string]: unknown | string; -}; - -export type NeoReleaseActionRequest = { - release_id: string; - [key: string]: unknown | string; -}; - -export type ParameterAttachmentId = string; - -export type ParameterAttachmentIdQuery = string; - -export type ParameterBotId = string; - -export type ParameterChunkId = string; - -/** - * URL-encoded command handler full name. - */ -export type ParameterCommandId = string; - -export type ParameterConfigId = string; - -export type ParameterConversationId = string; - -export type ParameterDocumentId = string; - -export type ParameterFilename = string; - -/** - * URL-encoded relative file path. - */ -export type ParameterFilePath = string; - -export type ParameterFolderId = string; - -export type ParameterGroupId = string; - -export type ParameterKbId = string; - -export type ParameterKeyId = string; - -export type ParameterName = string; - -export type ParameterPage = number; - -export type ParameterPageSize = number; - -export type ParameterPersonaId = string; - -export type ParameterPluginId = string; - -export type ParameterProviderId = string; - -export type ParameterServerName = string; - -export type ParameterSessionId = string; - -export type ParameterSkillName = string; - -export type ParameterSourceId = string; - -export type ParameterTaskId = string; - -export type ParameterToolId = string; - -/** - * URL-encoded unified message origin. - */ -export type ParameterUmo = string; - -export type ParameterWebhookUuid = string; - -export type PersonaFolderRequest = { - name?: string; - parent_id?: string; - description?: string; - [key: string]: unknown | string; -}; - -export type PersonaMoveRequest = { - persona_id: string; - folder_id?: string; - [key: string]: unknown | string; -}; - -export type PersonaRequest = { - persona_id: string; - system_prompt: string; - begin_dialogs?: Array<(string)>; - folder_id?: string; - tools?: Array<(string)>; - skills?: Array<(string)>; - custom_error_message?: string; - [key: string]: unknown | string; -}; - -export type PipInstallRequest = { - package: string; - mirror?: string; -}; - -export type PluginBatchUpdateRequest = { - /** - * When set, update this single plugin instead of a batch. - */ - plugin_id?: string; - plugin_ids?: Array<(string)>; - reinstall?: boolean; - update_all?: boolean; - [key: string]: unknown | string | boolean; -}; - -export type PluginConfigFileDeleteRequest = { - path: string; -}; - -export type PluginGithubInstallRequest = { - /** - * GitHub URL or owner/repository slug. - */ - repository: string; - ref?: string; - /** - * Optional downloadable ZIP URL to use instead of GitHub archive resolution. - */ - download_url?: string; - proxy?: string; - ignore_version_check?: boolean; - install_method?: string; - registry_url?: (string) | null; - market_plugin_id?: string; -}; - -export type PluginSourceBindRequest = { - install_method?: string; - registry_url?: (string) | null; - market_plugin_id?: string; -}; - -export type PluginSourceRequest = { - id?: string; - name?: string; - url: string; -}; - -export type PluginUpdateRequest = { - reinstall?: boolean; -}; - -export type PluginUploadInstallRequest = { - file: (Blob | File); -}; - -export type PluginUrlInstallRequest = { - url: string; - /** - * Optional downloadable ZIP URL when url is the plugin source page or repository. - */ - download_url?: string; - proxy?: string; - ignore_version_check?: boolean; - install_method?: string; - registry_url?: (string) | null; - market_plugin_id?: string; -}; - -export type PluginValidateRepoRequest = { - repository?: string; - url?: string; - proxy?: string; -}; - -export type PluginVersionSupportRequest = { - astrbot_version?: string; -}; - -export type ProviderCapability = 'chat' | 'agent' | 'stt' | 'tts' | 'embedding' | 'rerank'; - -export type ProviderConfigRequest = { - id?: string; - provider_source_id?: string; - capability?: ProviderCapability; - enabled?: boolean; - config: DynamicConfig; -}; - -export type ProviderSourceConfigRequest = { - id?: string; - config: DynamicConfig; -}; - -export type RenameRequest = { - name: string; -}; - -export type ReorderRequest = { - items: Array<{ - id: string; - type: 'persona' | 'folder'; - sort_order: number; - }>; -}; - -export type SessionGroupRequest = { - name?: string; - umos?: Array<(string)>; - add_umos?: Array<(string)>; - remove_umos?: Array<(string)>; -}; - -export type SessionRuleRequest = { - umo: string; - rule_key: string; - rule_value?: DynamicConfig; - [key: string]: unknown | string | DynamicConfig; -}; - -export type SetupAuthRequest = { - username: string; - password: string; - confirm_password: string; -}; - -export type SkillPatchRequest = { - enabled?: boolean; - display_name?: string; - description?: string; -}; - -export type SkillUploadRequest = { - file: (Blob | File); - overwrite?: boolean; -}; - -export type SuccessEnvelope = { - status: "ok"; - message?: string; - data: unknown; -}; - -export type T2iTemplateContentRequest = { - content: string; - [key: string]: unknown | string; -}; - -export type T2iTemplateRequest = { - name: string; - content: string; - [key: string]: unknown | string; -}; - -export type ToolPermissionPatch = { - permission: 'admin' | 'member'; -}; - -export type permission = 'admin' | 'member'; - -export type TotpSetupRequest = { - code?: string; - secret?: string; -}; - -export type TraceSettingsRequest = { - enabled?: boolean; - level?: string; - [key: string]: unknown | boolean | string; -}; - -export type UmoListRequest = { - umo?: string; - umos?: Array<(string)>; - scope?: 'all' | 'group' | 'private' | 'custom_group'; - group_id?: string; - rule_key?: string; -}; - -export type scope = 'all' | 'group' | 'private' | 'custom_group'; - -export type UpdateAccountRequest = { - password: string; - new_password?: string; - confirm_password?: string; - new_username?: string; -}; - -export type UpdateRequest = { - version?: string; - proxy?: string; - reboot?: boolean; - progress_id?: string; -}; - -export type LoginData = { - body: LoginRequest; -}; - -export type LoginResponse = (SuccessEnvelope); - -export type LoginError = (ErrorEnvelope); - -export type LogoutResponse = (SuccessEnvelope); - -export type LogoutError = unknown; - -export type GetAuthSetupStatusResponse = (SuccessEnvelope); - -export type GetAuthSetupStatusError = unknown; - -export type SetupAuthData = { - body: SetupAuthRequest; -}; - -export type SetupAuthResponse = (SuccessEnvelope); - -export type SetupAuthError = unknown; - -export type SetupTotpData = { - body?: TotpSetupRequest; -}; - -export type SetupTotpResponse = (SuccessEnvelope); - -export type SetupTotpError = unknown; - -export type RecoverTotpResponse = (SuccessEnvelope); - -export type RecoverTotpError = unknown; - -export type UpdateAuthAccountData = { - body: UpdateAccountRequest; -}; - -export type UpdateAuthAccountResponse = (SuccessEnvelope); - -export type UpdateAuthAccountError = unknown; - -export type ListApiKeysResponse = (SuccessEnvelope); - -export type ListApiKeysError = unknown; - -export type CreateApiKeyData = { - body: CreateApiKeyRequest; -}; - -export type CreateApiKeyResponse = (SuccessEnvelope); - -export type CreateApiKeyError = unknown; - -export type RevokeApiKeyData = { - path: { - key_id: string; - }; -}; - -export type RevokeApiKeyResponse = (SuccessEnvelope); - -export type RevokeApiKeyError = unknown; - -export type DeleteApiKeyData = { - path: { - key_id: string; - }; -}; - -export type DeleteApiKeyResponse = (SuccessEnvelope); - -export type DeleteApiKeyError = unknown; - -export type GetSystemConfigSchemaResponse = (SuccessEnvelope); - -export type GetSystemConfigSchemaError = unknown; - -export type GetSystemConfigResponse = (SuccessEnvelope); - -export type GetSystemConfigError = unknown; - -export type UpdateSystemConfigData = { - body: DynamicConfig; -}; - -export type UpdateSystemConfigResponse = (SuccessEnvelope); - -export type UpdateSystemConfigError = unknown; - -export type GetSystemConfigRuntimeResponse = (SuccessEnvelope); - -export type GetSystemConfigRuntimeError = unknown; - -export type GetConfigProfileSchemaResponse = (SuccessEnvelope); - -export type GetConfigProfileSchemaError = unknown; - -export type ListConfigProfilesResponse = (SuccessEnvelope); - -export type ListConfigProfilesError = unknown; - -export type CreateConfigProfileData = { - body: CreateConfigProfileRequest; -}; - -export type CreateConfigProfileResponse = (SuccessEnvelope); - -export type CreateConfigProfileError = unknown; - -export type GetConfigProfileData = { - path: { - config_id: string; - }; -}; - -export type GetConfigProfileResponse = (SuccessEnvelope); - -export type GetConfigProfileError = unknown; - -export type UpdateConfigProfileContentData = { - body: DynamicConfig; - path: { - config_id: string; - }; -}; - -export type UpdateConfigProfileContentResponse = (SuccessEnvelope); - -export type UpdateConfigProfileContentError = unknown; - -export type RenameConfigProfileData = { - body: RenameRequest; - path: { - config_id: string; - }; -}; - -export type RenameConfigProfileResponse = (SuccessEnvelope); - -export type RenameConfigProfileError = unknown; - -export type DeleteConfigProfileData = { - path: { - config_id: string; - }; -}; - -export type DeleteConfigProfileResponse = (SuccessEnvelope); - -export type DeleteConfigProfileError = unknown; - -export type ListConfigRoutesResponse = (SuccessEnvelope); - -export type ListConfigRoutesError = unknown; - -export type ReplaceConfigRoutesData = { - body: ConfigRoutesReplaceRequest; -}; - -export type ReplaceConfigRoutesResponse = (SuccessEnvelope); - -export type ReplaceConfigRoutesError = unknown; - -export type UpsertConfigRouteData = { - body: ConfigRouteUpsertRequest; - path: { - /** - * URL-encoded unified message origin. - */ - umo: string; - }; -}; - -export type UpsertConfigRouteResponse = (SuccessEnvelope); - -export type UpsertConfigRouteError = unknown; - -export type DeleteConfigRouteData = { - path: { - /** - * URL-encoded unified message origin. - */ - umo: string; - }; -}; - -export type DeleteConfigRouteResponse = (SuccessEnvelope); - -export type DeleteConfigRouteError = unknown; - -export type ListBotTypesResponse = (SuccessEnvelope); - -export type ListBotTypesError = unknown; - -export type RegisterBotTypeData = { - body: BotRegistrationRequest; - path: { - bot_type: string; - }; -}; - -export type RegisterBotTypeResponse = (SuccessEnvelope); - -export type RegisterBotTypeError = unknown; - -export type ListBotsData = { - query?: { - enabled?: boolean; - type?: string; - }; -}; - -export type ListBotsResponse = (SuccessEnvelope); - -export type ListBotsError = unknown; - -export type CreateBotData = { - body: BotConfigRequest; -}; - -export type CreateBotResponse = (SuccessEnvelope); - -export type CreateBotError = unknown; - -export type ListBotStatsResponse = (SuccessEnvelope); - -export type ListBotStatsError = unknown; - -export type GetBotByIdData = { - query: { - bot_id: string; - }; -}; - -export type GetBotByIdResponse = (SuccessEnvelope); - -export type GetBotByIdError = unknown; - -export type UpdateBotByIdData = { - body: { - bot_id: string; - config: DynamicConfig; - }; -}; - -export type UpdateBotByIdResponse = (SuccessEnvelope); - -export type UpdateBotByIdError = unknown; - -export type DeleteBotByIdData = { - query: { - bot_id: string; - }; -}; - -export type DeleteBotByIdResponse = (SuccessEnvelope); - -export type DeleteBotByIdError = unknown; - -export type SetBotEnabledByIdData = { - body: { - bot_id: string; - enabled: boolean; - }; -}; - -export type SetBotEnabledByIdResponse = (SuccessEnvelope); - -export type SetBotEnabledByIdError = unknown; - -export type TestBotByIdData = { - body: { - bot_id: string; - }; -}; - -export type TestBotByIdResponse = (SuccessEnvelope); - -export type TestBotByIdError = unknown; - -export type GetBotData = { - path: { - bot_id: string; - }; -}; - -export type GetBotResponse = (SuccessEnvelope); - -export type GetBotError = unknown; - -export type UpdateBotData = { - body: BotConfigRequest; - path: { - bot_id: string; - }; -}; - -export type UpdateBotResponse = (SuccessEnvelope); - -export type UpdateBotError = unknown; - -export type DeleteBotData = { - path: { - bot_id: string; - }; -}; - -export type DeleteBotResponse = (SuccessEnvelope); - -export type DeleteBotError = unknown; - -export type SetBotEnabledData = { - body: EnabledPatch; - path: { - bot_id: string; - }; -}; - -export type SetBotEnabledResponse = (SuccessEnvelope); - -export type SetBotEnabledError = unknown; - -export type TestBotData = { - path: { - bot_id: string; - }; -}; - -export type TestBotResponse = (SuccessEnvelope); - -export type TestBotError = unknown; - -export type GetProviderSchemaResponse = (SuccessEnvelope); - -export type GetProviderSchemaError = unknown; - -export type ListProviderSourcesResponse = (SuccessEnvelope); - -export type ListProviderSourcesError = unknown; - -export type CreateProviderSourceData = { - body: ProviderSourceConfigRequest; -}; - -export type CreateProviderSourceResponse = (SuccessEnvelope); - -export type CreateProviderSourceError = unknown; - -export type GetProviderSourceByIdData = { - query: { - source_id: string; - }; -}; - -export type GetProviderSourceByIdResponse = (SuccessEnvelope); - -export type GetProviderSourceByIdError = unknown; - -export type UpsertProviderSourceByIdData = { - body: { - source_id: string; - config: DynamicConfig; - }; -}; - -export type UpsertProviderSourceByIdResponse = (SuccessEnvelope); - -export type UpsertProviderSourceByIdError = unknown; - -export type DeleteProviderSourceByIdData = { - query: { - source_id: string; - }; -}; - -export type DeleteProviderSourceByIdResponse = (SuccessEnvelope); - -export type DeleteProviderSourceByIdError = unknown; - -export type ListProviderSourceModelsByIdData = { - query: { - capability?: ProviderCapability; - source_id: string; - }; -}; - -export type ListProviderSourceModelsByIdResponse = (SuccessEnvelope); - -export type ListProviderSourceModelsByIdError = unknown; - -export type ListProvidersBySourceIdData = { - query: { - capability?: ProviderCapability; - source_id: string; - }; -}; - -export type ListProvidersBySourceIdResponse = (SuccessEnvelope); - -export type ListProvidersBySourceIdError = unknown; - -export type CreateProviderInSourceByIdData = { - body: { - source_id: string; - config: DynamicConfig; - }; -}; - -export type CreateProviderInSourceByIdResponse = (SuccessEnvelope); - -export type CreateProviderInSourceByIdError = unknown; - -export type GetProviderSourceData = { - path: { - source_id: string; - }; -}; - -export type GetProviderSourceResponse = (SuccessEnvelope); - -export type GetProviderSourceError = unknown; - -export type UpsertProviderSourceData = { - body: ProviderSourceConfigRequest; - path: { - source_id: string; - }; -}; - -export type UpsertProviderSourceResponse = (SuccessEnvelope); - -export type UpsertProviderSourceError = unknown; - -export type DeleteProviderSourceData = { - path: { - source_id: string; - }; -}; - -export type DeleteProviderSourceResponse = (SuccessEnvelope); - -export type DeleteProviderSourceError = unknown; - -export type ListProviderSourceModelsData = { - path: { - source_id: string; - }; - query?: { - capability?: ProviderCapability; - }; -}; - -export type ListProviderSourceModelsResponse = (SuccessEnvelope); - -export type ListProviderSourceModelsError = unknown; - -export type ListProvidersBySourceData = { - path: { - source_id: string; - }; - query?: { - capability?: ProviderCapability; - }; -}; - -export type ListProvidersBySourceResponse = (SuccessEnvelope); - -export type ListProvidersBySourceError = unknown; - -export type CreateProviderInSourceData = { - body: ProviderConfigRequest; - path: { - source_id: string; - }; -}; - -export type CreateProviderInSourceResponse = (SuccessEnvelope); - -export type CreateProviderInSourceError = unknown; - -export type ListProvidersData = { - query?: { - capability?: ProviderCapability; - enabled?: boolean; - source_id?: string; - }; -}; - -export type ListProvidersResponse = (SuccessEnvelope); - -export type ListProvidersError = unknown; - -export type CreateProviderData = { - body: ProviderConfigRequest; -}; - -export type CreateProviderResponse = (SuccessEnvelope); - -export type CreateProviderError = unknown; - -export type GetProviderByIdData = { - query: { - merged?: boolean; - provider_id: string; - }; -}; - -export type GetProviderByIdResponse = (SuccessEnvelope); - -export type GetProviderByIdError = unknown; - -export type UpdateProviderByIdData = { - body: { - provider_id: string; - config: DynamicConfig; - }; -}; - -export type UpdateProviderByIdResponse = (SuccessEnvelope); - -export type UpdateProviderByIdError = unknown; - -export type DeleteProviderByIdData = { - query: { - provider_id: string; - }; -}; - -export type DeleteProviderByIdResponse = (SuccessEnvelope); - -export type DeleteProviderByIdError = unknown; - -export type SetProviderEnabledByIdData = { - body: { - provider_id: string; - enabled: boolean; - }; -}; - -export type SetProviderEnabledByIdResponse = (SuccessEnvelope); - -export type SetProviderEnabledByIdError = unknown; - -export type TestProviderByIdData = { - body: { - provider_id: string; - }; -}; - -export type TestProviderByIdResponse = (SuccessEnvelope); - -export type TestProviderByIdError = unknown; - -export type GetProviderEmbeddingDimensionByIdData = { - body: { - provider_id: string; - provider_config?: DynamicConfig; - }; -}; - -export type GetProviderEmbeddingDimensionByIdResponse = (SuccessEnvelope); - -export type GetProviderEmbeddingDimensionByIdError = unknown; - -export type GetProviderData = { - path: { - provider_id: string; - }; - query?: { - merged?: boolean; - }; -}; - -export type GetProviderResponse = (SuccessEnvelope); - -export type GetProviderError = unknown; - -export type UpdateProviderData = { - body: ProviderConfigRequest; - path: { - provider_id: string; - }; -}; - -export type UpdateProviderResponse = (SuccessEnvelope); - -export type UpdateProviderError = unknown; - -export type DeleteProviderData = { - body?: DynamicConfig; - path: { - provider_id: string; - }; -}; - -export type DeleteProviderResponse = (SuccessEnvelope); - -export type DeleteProviderError = unknown; - -export type SetProviderEnabledData = { - body: EnabledPatch; - path: { - provider_id: string; - }; -}; - -export type SetProviderEnabledResponse = (SuccessEnvelope); - -export type SetProviderEnabledError = unknown; - -export type TestProviderData = { - path: { - provider_id: string; - }; -}; - -export type TestProviderResponse = (SuccessEnvelope); - -export type TestProviderError = unknown; - -export type GetProviderEmbeddingDimensionData = { - body?: DynamicConfig; - path: { - provider_id: string; - }; -}; - -export type GetProviderEmbeddingDimensionResponse = (SuccessEnvelope); - -export type GetProviderEmbeddingDimensionError = unknown; - -export type SendChatMessageData = { - body: ChatRequest; -}; - -export type SendChatMessageResponse = (SuccessEnvelope); - -export type SendChatMessageError = unknown; - -export type OpenChatWebSocketData = { - query?: { - api_key?: string; - key?: string; - }; -}; - -export type OpenLiveChatWebSocketData = { - query: { - token: string; - }; -}; - -export type OpenUnifiedChatWebSocketData = { - query: { - token: string; - }; -}; - -export type ListChatSessionsData = { - query?: { - page?: number; - page_size?: number; - username?: string; - }; -}; - -export type ListChatSessionsResponse = (SuccessEnvelope); - -export type ListChatSessionsError = unknown; - -export type CreateChatSessionData = { - query?: { - platform_id?: string; - }; -}; - -export type CreateChatSessionResponse = (SuccessEnvelope); - -export type CreateChatSessionError = unknown; - -export type BatchDeleteChatSessionsData = { - body: ChatSessionBatchDeleteRequest; -}; - -export type BatchDeleteChatSessionsResponse = (SuccessEnvelope); - -export type BatchDeleteChatSessionsError = unknown; - -export type GetChatSessionData = { - path: { - session_id: string; - }; -}; - -export type GetChatSessionResponse = (SuccessEnvelope); - -export type GetChatSessionError = unknown; - -export type UpdateChatSessionData = { - body: ChatSessionPatchRequest; - path: { - session_id: string; - }; -}; - -export type UpdateChatSessionResponse = (SuccessEnvelope); - -export type UpdateChatSessionError = unknown; - -export type DeleteChatSessionData = { - path: { - session_id: string; - }; -}; - -export type DeleteChatSessionResponse = (SuccessEnvelope); - -export type DeleteChatSessionError = unknown; - -export type StopChatSessionData = { - path: { - session_id: string; - }; -}; - -export type StopChatSessionResponse = (SuccessEnvelope); - -export type StopChatSessionError = unknown; - -export type UpdateChatMessageData = { - body: ChatMessagePatchRequest; - path: { - message_id: string; - session_id: string; - }; -}; - -export type UpdateChatMessageResponse = (SuccessEnvelope); - -export type UpdateChatMessageError = unknown; - -export type RegenerateChatMessageData = { - body?: ChatMessageRegenerateRequest; - path: { - message_id: string; - session_id: string; - }; -}; - -export type RegenerateChatMessageResponse = (unknown); - -export type RegenerateChatMessageError = unknown; - -export type ListChatConfigsResponse = (SuccessEnvelope); - -export type ListChatConfigsError = unknown; - -export type CreateChatThreadData = { - body: ChatThreadCreateRequest; -}; - -export type CreateChatThreadResponse = (SuccessEnvelope); - -export type CreateChatThreadError = unknown; - -export type GetChatThreadData = { - path: { - thread_id: string; - }; -}; - -export type GetChatThreadResponse = (SuccessEnvelope); - -export type GetChatThreadError = unknown; - -export type DeleteChatThreadData = { - path: { - thread_id: string; - }; -}; - -export type DeleteChatThreadResponse = (SuccessEnvelope); - -export type DeleteChatThreadError = unknown; - -export type SendChatThreadMessageData = { - body: ChatThreadMessageRequest; - path: { - thread_id: string; - }; -}; - -export type SendChatThreadMessageResponse = (unknown); - -export type SendChatThreadMessageError = unknown; - -export type ListChatProjectsResponse = (SuccessEnvelope); - -export type ListChatProjectsError = unknown; - -export type CreateChatProjectData = { - body: ChatProjectRequest; -}; - -export type CreateChatProjectResponse = (SuccessEnvelope); - -export type CreateChatProjectError = unknown; - -export type GetChatProjectData = { - path: { - project_id: string; - }; -}; - -export type GetChatProjectResponse = (SuccessEnvelope); - -export type GetChatProjectError = unknown; - -export type UpdateChatProjectData = { - body: ChatProjectRequest; - path: { - project_id: string; - }; -}; - -export type UpdateChatProjectResponse = (SuccessEnvelope); - -export type UpdateChatProjectError = unknown; - -export type DeleteChatProjectData = { - path: { - project_id: string; - }; -}; - -export type DeleteChatProjectResponse = (SuccessEnvelope); - -export type DeleteChatProjectError = unknown; - -export type ListChatProjectSessionsData = { - path: { - project_id: string; - }; -}; - -export type ListChatProjectSessionsResponse = (SuccessEnvelope); - -export type ListChatProjectSessionsError = unknown; - -export type AddChatProjectSessionData = { - path: { - project_id: string; - session_id: string; - }; -}; - -export type AddChatProjectSessionResponse = (SuccessEnvelope); - -export type AddChatProjectSessionError = unknown; - -export type RemoveChatProjectSessionData = { - path: { - session_id: string; - }; -}; - -export type RemoveChatProjectSessionResponse = (SuccessEnvelope); - -export type RemoveChatProjectSessionError = unknown; - -export type SendImMessageData = { - body: ImMessageRequest; -}; - -export type SendImMessageResponse = (SuccessEnvelope); - -export type SendImMessageError = unknown; - -export type ListImBotsResponse = (SuccessEnvelope); - -export type ListImBotsError = unknown; - -export type UploadFileData = { - body: FileUploadRequest; -}; - -export type UploadFileResponse = (SuccessEnvelope); - -export type UploadFileError = unknown; - -export type UploadOpenApiFileData = { - body: FileUploadRequest; -}; - -export type UploadOpenApiFileResponse = (SuccessEnvelope); - -export type UploadOpenApiFileError = unknown; - -export type DownloadOpenApiFileData = { - query: { - attachment_id: string; - }; -}; - -export type DownloadOpenApiFileResponse = ((Blob | File)); - -export type DownloadOpenApiFileError = unknown; - -export type GetFileByNameData = { - query: { - filename: string; - }; -}; - -export type GetFileByNameResponse = ((Blob | File)); - -export type GetFileByNameError = unknown; - -export type GetTokenFileData = { - path: { - file_token: string; - }; -}; - -export type GetTokenFileResponse = ((Blob | File)); - -export type GetTokenFileError = unknown; - -export type GetAttachmentData = { - path: { - attachment_id: string; - }; -}; - -export type GetAttachmentResponse = (SuccessEnvelope); - -export type GetAttachmentError = unknown; - -export type DeleteAttachmentData = { - path: { - attachment_id: string; - }; -}; - -export type DeleteAttachmentResponse = (SuccessEnvelope); - -export type DeleteAttachmentError = unknown; - -export type DownloadAttachmentData = { - path: { - attachment_id: string; - }; -}; - -export type DownloadAttachmentResponse = ((Blob | File)); - -export type DownloadAttachmentError = unknown; - -export type ListPluginsData = { - query?: { - enabled?: boolean; - include_reserved?: boolean; - }; -}; - -export type ListPluginsResponse = (SuccessEnvelope); - -export type ListPluginsError = unknown; - -export type GetPluginByIdData = { - query: { - plugin_id: string; - }; -}; - -export type GetPluginByIdResponse = (SuccessEnvelope); - -export type GetPluginByIdError = unknown; - -export type UninstallPluginByIdData = { - body?: { - delete_config?: boolean; - delete_data?: boolean; - [key: string]: unknown | boolean; - }; - query: { - plugin_id: string; - }; -}; - -export type UninstallPluginByIdResponse = (SuccessEnvelope); - -export type UninstallPluginByIdError = unknown; - -export type GetPluginConfigByIdData = { - query: { - plugin_id: string; - }; -}; - -export type GetPluginConfigByIdResponse = (SuccessEnvelope); - -export type GetPluginConfigByIdError = unknown; - -export type UpdatePluginConfigByIdData = { - body: { - plugin_id: string; - config: DynamicConfig; - }; -}; - -export type UpdatePluginConfigByIdResponse = (SuccessEnvelope); - -export type UpdatePluginConfigByIdError = unknown; - -export type GetPluginConfigSchemaByIdData = { - query: { - plugin_id: string; - }; -}; - -export type GetPluginConfigSchemaByIdResponse = (SuccessEnvelope); - -export type GetPluginConfigSchemaByIdError = unknown; - -export type ListPluginConfigFilesByIdData = { - query: { - config_key: string; - plugin_id: string; - }; -}; - -export type ListPluginConfigFilesByIdResponse = (SuccessEnvelope); - -export type ListPluginConfigFilesByIdError = unknown; - -export type UploadPluginConfigFilesByIdData = { - body: { - [key: string]: unknown; - }; - query: { - config_key: string; - plugin_id: string; - }; -}; - -export type UploadPluginConfigFilesByIdResponse = (SuccessEnvelope); - -export type UploadPluginConfigFilesByIdError = unknown; - -export type DeletePluginConfigFileByIdData = { - body: PluginConfigFileDeleteRequest; - query: { - plugin_id: string; - }; -}; - -export type DeletePluginConfigFileByIdResponse = (SuccessEnvelope); - -export type DeletePluginConfigFileByIdError = unknown; - -export type GetPluginReadmeByIdData = { - query: { - plugin_id: string; - }; -}; - -export type GetPluginReadmeByIdResponse = (string); - -export type GetPluginReadmeByIdError = unknown; - -export type GetPluginChangelogByIdData = { - query: { - plugin_id: string; - }; -}; - -export type GetPluginChangelogByIdResponse = (string); - -export type GetPluginChangelogByIdError = unknown; - -export type ReloadPluginByIdData = { - body: { - plugin_id: string; - }; -}; - -export type ReloadPluginByIdResponse = (SuccessEnvelope); - -export type ReloadPluginByIdError = unknown; - -export type SetPluginEnabledByIdData = { - body: { - plugin_id: string; - enabled: boolean; - }; -}; - -export type SetPluginEnabledByIdResponse = (SuccessEnvelope); - -export type SetPluginEnabledByIdError = unknown; - -export type ListPluginPagesByIdData = { - query: { - plugin_id: string; - }; -}; - -export type ListPluginPagesByIdResponse = (SuccessEnvelope); - -export type ListPluginPagesByIdError = unknown; - -export type GetPluginPageByIdData = { - query: { - page_name: string; - plugin_id: string; - }; -}; - -export type GetPluginPageByIdResponse = (string); - -export type GetPluginPageByIdError = unknown; - -export type GetPluginPageAssetByIdData = { - query: { - asset_path: string; - page_name: string; - plugin_id: string; - }; -}; - -export type GetPluginPageAssetByIdResponse = (unknown); - -export type GetPluginPageAssetByIdError = unknown; - -export type GetPluginData = { - body?: { - delete_config?: boolean; - delete_data?: boolean; - [key: string]: unknown | boolean; - }; - path: { - plugin_id: string; - }; -}; - -export type GetPluginResponse = (SuccessEnvelope); - -export type GetPluginError = unknown; - -export type UninstallPluginData = { - body?: { - delete_config?: boolean; - delete_data?: boolean; - [key: string]: unknown | boolean; - }; - path: { - plugin_id: string; - }; -}; - -export type UninstallPluginResponse = (SuccessEnvelope); - -export type UninstallPluginError = unknown; - -export type GetPluginConfigData = { - path: { - plugin_id: string; - }; -}; - -export type GetPluginConfigResponse = (SuccessEnvelope); - -export type GetPluginConfigError = unknown; - -export type UpdatePluginConfigData = { - body: DynamicConfig; - path: { - plugin_id: string; - }; -}; - -export type UpdatePluginConfigResponse = (SuccessEnvelope); - -export type UpdatePluginConfigError = unknown; - -export type GetPluginConfigSchemaData = { - path: { - plugin_id: string; - }; -}; - -export type GetPluginConfigSchemaResponse = (SuccessEnvelope); - -export type GetPluginConfigSchemaError = unknown; - -export type ListPluginConfigFilesData = { - path: { - /** - * URL-encoded configuration key path. - */ - config_key: string; - plugin_id: string; - }; -}; - -export type ListPluginConfigFilesResponse = (SuccessEnvelope); - -export type ListPluginConfigFilesError = unknown; - -export type UploadPluginConfigFilesData = { - body: { - [key: string]: unknown; - }; - path: { - /** - * URL-encoded configuration key path. - */ - config_key: string; - plugin_id: string; - }; -}; - -export type UploadPluginConfigFilesResponse = (SuccessEnvelope); - -export type UploadPluginConfigFilesError = unknown; - -export type DeletePluginConfigFileData = { - body: PluginConfigFileDeleteRequest; - path: { - plugin_id: string; - }; -}; - -export type DeletePluginConfigFileResponse = (SuccessEnvelope); - -export type DeletePluginConfigFileError = unknown; - -export type GetPluginReadmeData = { - path: { - plugin_id: string; - }; -}; - -export type GetPluginReadmeResponse = (string); - -export type GetPluginReadmeError = unknown; - -export type GetPluginChangelogData = { - path: { - plugin_id: string; - }; -}; - -export type GetPluginChangelogResponse = (string); - -export type GetPluginChangelogError = unknown; - -export type ReloadPluginData = { - path: { - plugin_id: string; - }; -}; - -export type ReloadPluginResponse = (SuccessEnvelope); - -export type ReloadPluginError = unknown; - -export type BindPluginSourceData = { - body: PluginSourceBindRequest; - path: { - plugin_id: string; - }; -}; - -export type BindPluginSourceResponse = (SuccessEnvelope); - -export type BindPluginSourceError = unknown; - -export type SetPluginEnabledData = { - body: EnabledPatch; - path: { - plugin_id: string; - }; -}; - -export type SetPluginEnabledResponse = (SuccessEnvelope); - -export type SetPluginEnabledError = unknown; - -export type UpdatePluginData = { - body?: PluginUpdateRequest; - path: { - plugin_id: string; - }; -}; - -export type UpdatePluginResponse = (SuccessEnvelope); - -export type UpdatePluginError = unknown; - -export type UpdatePluginsData = { - body: PluginBatchUpdateRequest; -}; - -export type UpdatePluginsResponse = (SuccessEnvelope); - -export type UpdatePluginsError = unknown; - -export type CheckPluginVersionSupportData = { - body: PluginVersionSupportRequest; -}; - -export type CheckPluginVersionSupportResponse = (SuccessEnvelope); - -export type CheckPluginVersionSupportError = unknown; - -export type ValidatePluginRepoData = { - body: PluginValidateRepoRequest; -}; - -export type ValidatePluginRepoResponse = (SuccessEnvelope); - -export type ValidatePluginRepoError = unknown; - -export type ListFailedPluginsResponse = (SuccessEnvelope); - -export type ListFailedPluginsError = unknown; - -export type UninstallFailedPluginData = { - body?: { - delete_config?: boolean; - delete_data?: boolean; - [key: string]: unknown | boolean; - }; - path: { - plugin_id: string; - }; -}; - -export type UninstallFailedPluginResponse = (SuccessEnvelope); - -export type UninstallFailedPluginError = unknown; - -export type ReloadFailedPluginData = { - path: { - plugin_id: string; - }; -}; - -export type ReloadFailedPluginResponse = (SuccessEnvelope); - -export type ReloadFailedPluginError = unknown; - -export type InstallPluginFromGithubData = { - body: PluginGithubInstallRequest; -}; - -export type InstallPluginFromGithubResponse = (SuccessEnvelope); - -export type InstallPluginFromGithubError = unknown; - -export type InstallPluginFromUrlData = { - body: PluginUrlInstallRequest; -}; - -export type InstallPluginFromUrlResponse = (SuccessEnvelope); - -export type InstallPluginFromUrlError = unknown; - -export type InstallPluginFromUploadData = { - body: PluginUploadInstallRequest; -}; - -export type InstallPluginFromUploadResponse = (SuccessEnvelope); - -export type InstallPluginFromUploadError = unknown; - -export type ListPluginMarketData = { - query?: { - category?: string; - custom_registry?: string; - force_refresh?: boolean; - keyword?: string; - page?: number; - page_size?: number; - sort?: 'recommended' | 'downloads' | 'updated' | 'name'; - }; -}; - -export type ListPluginMarketResponse = (SuccessEnvelope); - -export type ListPluginMarketError = unknown; - -export type ListPluginMarketCategoriesResponse = (SuccessEnvelope); - -export type ListPluginMarketCategoriesError = unknown; - -export type ListPluginSourcesResponse = (SuccessEnvelope); - -export type ListPluginSourcesError = unknown; - -export type CreatePluginSourceData = { - body: PluginSourceRequest; -}; - -export type CreatePluginSourceResponse = (SuccessEnvelope); - -export type CreatePluginSourceError = unknown; - -export type ReplacePluginSourcesData = { - body: { - sources: Array<PluginSourceRequest>; - }; -}; - -export type ReplacePluginSourcesResponse = (SuccessEnvelope); - -export type ReplacePluginSourcesError = unknown; - -export type DeletePluginSourceData = { - path: { - source_id: string; - }; -}; - -export type DeletePluginSourceResponse = (SuccessEnvelope); - -export type DeletePluginSourceError = unknown; - -export type DeletePluginSourceByIdData = { - query: { - source_id: string; - }; -}; - -export type DeletePluginSourceByIdResponse = (SuccessEnvelope); - -export type DeletePluginSourceByIdError = unknown; - -export type ListPluginPagesData = { - path: { - plugin_id: string; - }; -}; - -export type ListPluginPagesResponse = (SuccessEnvelope); - -export type ListPluginPagesError = unknown; - -export type GetPluginPageData = { - path: { - page_name: string; - plugin_id: string; - }; -}; - -export type GetPluginPageResponse = (string); - -export type GetPluginPageError = unknown; - -export type GetPluginPageAssetData = { - path: { - /** - * URL-encoded relative asset path. - */ - asset_path: string; - page_name: string; - plugin_id: string; - }; -}; - -export type GetPluginPageAssetResponse = (unknown); - -export type GetPluginPageAssetError = unknown; - -export type GetPluginPageBridgeSdkResponse = (string); - -export type GetPluginPageBridgeSdkError = unknown; - -export type GetPluginExtensionRouteData = { - path: { - /** - * Plugin extension path after /api/plug/. It may contain slash-separated segments. - */ - plugin_path: string; - }; -}; - -export type GetPluginExtensionRouteResponse = (SuccessEnvelope); - -export type GetPluginExtensionRouteError = unknown; - -export type PostPluginExtensionRouteData = { - body?: DynamicConfig; - path: { - /** - * Plugin extension path after /api/plug/. It may contain slash-separated segments. - */ - plugin_path: string; - }; -}; - -export type PostPluginExtensionRouteResponse = (SuccessEnvelope); - -export type PostPluginExtensionRouteError = unknown; - -export type PutPluginExtensionRouteData = { - body?: DynamicConfig; - path: { - plugin_path: string; - }; -}; - -export type PutPluginExtensionRouteResponse = (SuccessEnvelope); - -export type PutPluginExtensionRouteError = unknown; - -export type PatchPluginExtensionRouteData = { - body?: DynamicConfig; - path: { - plugin_path: string; - }; -}; - -export type PatchPluginExtensionRouteResponse = (SuccessEnvelope); - -export type PatchPluginExtensionRouteError = unknown; - -export type DeletePluginExtensionRouteData = { - path: { - plugin_path: string; - }; -}; - -export type DeletePluginExtensionRouteResponse = (SuccessEnvelope); - -export type DeletePluginExtensionRouteError = unknown; - -export type ListCommandsData = { - query?: { - config_id?: string; - }; -}; - -export type ListCommandsResponse = (SuccessEnvelope); - -export type ListCommandsError = unknown; - -export type UpdateCommandData = { - body: CommandPatchRequest; - path: { - /** - * URL-encoded command handler full name. - */ - command_id: string; - }; -}; - -export type UpdateCommandResponse = (SuccessEnvelope); - -export type UpdateCommandError = unknown; - -export type ListCommandConflictsResponse = (SuccessEnvelope); - -export type ListCommandConflictsError = unknown; - -export type ListToolsData = { - query?: { - enabled?: boolean; - origin?: 'builtin' | 'plugin' | 'mcp'; - }; -}; - -export type ListToolsResponse = (SuccessEnvelope); - -export type ListToolsError = unknown; - -export type SetToolEnabledData = { - body: EnabledPatch; - path: { - tool_id: string; - }; -}; - -export type SetToolEnabledResponse = (SuccessEnvelope); - -export type SetToolEnabledError = unknown; - -export type SetToolPermissionData = { - body: ToolPermissionPatch; - path: { - tool_id: string; - }; -}; - -export type SetToolPermissionResponse = (SuccessEnvelope); - -export type SetToolPermissionError = unknown; - -export type ListMcpServersResponse = (SuccessEnvelope); - -export type ListMcpServersError = unknown; - -export type CreateMcpServerData = { - body: McpServerConfig; -}; - -export type CreateMcpServerResponse = (SuccessEnvelope); - -export type CreateMcpServerError = unknown; - -export type UpdateMcpServerByNameData = { - body: { - server_name: string; - config?: DynamicConfig; - enabled?: boolean; - [key: string]: unknown | string | DynamicConfig | boolean; - }; -}; - -export type UpdateMcpServerByNameResponse = (SuccessEnvelope); - -export type UpdateMcpServerByNameError = unknown; - -export type DeleteMcpServerByNameData = { - query: { - server_name: string; - }; -}; - -export type DeleteMcpServerByNameResponse = (SuccessEnvelope); - -export type DeleteMcpServerByNameError = unknown; - -export type SetMcpServerEnabledByNameData = { - body: { - server_name: string; - enabled: boolean; - }; -}; - -export type SetMcpServerEnabledByNameResponse = (SuccessEnvelope); - -export type SetMcpServerEnabledByNameError = unknown; - -export type TestMcpServerByNameData = { - body: { - server_name: string; - mcp_server_config?: DynamicConfig; - config?: DynamicConfig; - [key: string]: unknown | string | DynamicConfig; - }; -}; - -export type TestMcpServerByNameResponse = (SuccessEnvelope); - -export type TestMcpServerByNameError = unknown; - -export type UpdateMcpServerData = { - body: McpServerConfig; - path: { - server_name: string; - }; -}; - -export type UpdateMcpServerResponse = (SuccessEnvelope); - -export type UpdateMcpServerError = unknown; - -export type DeleteMcpServerData = { - path: { - server_name: string; - }; -}; - -export type DeleteMcpServerResponse = (SuccessEnvelope); - -export type DeleteMcpServerError = unknown; - -export type SetMcpServerEnabledData = { - body: EnabledPatch; - path: { - server_name: string; - }; -}; - -export type SetMcpServerEnabledResponse = (SuccessEnvelope); - -export type SetMcpServerEnabledError = unknown; - -export type TestMcpServerData = { - body?: { - mcp_server_config?: { - [key: string]: unknown; - }; - [key: string]: unknown; - }; - path: { - server_name: string; - }; -}; - -export type TestMcpServerResponse = (SuccessEnvelope); - -export type TestMcpServerError = unknown; - -export type SyncModelScopeMcpServersData = { - body?: ModelScopeSyncRequest; -}; - -export type SyncModelScopeMcpServersResponse = (SuccessEnvelope); - -export type SyncModelScopeMcpServersError = unknown; - -export type ListSkillsData = { - query?: { - enabled?: boolean; - source?: string; - }; -}; - -export type ListSkillsResponse = (SuccessEnvelope); - -export type ListSkillsError = unknown; - -export type UploadSkillData = { - body: SkillUploadRequest; -}; - -export type UploadSkillResponse = (SuccessEnvelope); - -export type UploadSkillError = unknown; - -export type UploadSkillsBatchData = { - body: { - files: Array<((Blob | File))>; - }; -}; - -export type UploadSkillsBatchResponse = (SuccessEnvelope); - -export type UploadSkillsBatchError = unknown; - -export type UpdateSkillByNameData = { - body: { - skill_name: string; - enabled?: boolean; - active?: boolean; - [key: string]: unknown | string | boolean; - }; -}; - -export type UpdateSkillByNameResponse = (SuccessEnvelope); - -export type UpdateSkillByNameError = unknown; - -export type DeleteSkillByNameData = { - query: { - skill_name: string; - }; -}; - -export type DeleteSkillByNameResponse = (SuccessEnvelope); - -export type DeleteSkillByNameError = unknown; - -export type DownloadSkillByNameData = { - query: { - skill_name: string; - }; -}; - -export type DownloadSkillByNameResponse = ((Blob | File)); - -export type DownloadSkillByNameError = unknown; - -export type ListSkillFilesByNameData = { - query: { - path?: string; - skill_name: string; - }; -}; - -export type ListSkillFilesByNameResponse = (SuccessEnvelope); - -export type ListSkillFilesByNameError = unknown; - -export type GetSkillFileByNameData = { - query: { - path: string; - skill_name: string; - }; -}; - -export type GetSkillFileByNameResponse = (string); - -export type GetSkillFileByNameError = unknown; - -export type UpdateSkillFileByNameData = { - body: { - skill_name: string; - path: string; - content: string; - }; -}; - -export type UpdateSkillFileByNameResponse = (SuccessEnvelope); - -export type UpdateSkillFileByNameError = unknown; - -export type UpdateSkillData = { - body: SkillPatchRequest; - path: { - skill_name: string; - }; -}; - -export type UpdateSkillResponse = (SuccessEnvelope); - -export type UpdateSkillError = unknown; - -export type DeleteSkillData = { - path: { - skill_name: string; - }; -}; - -export type DeleteSkillResponse = (SuccessEnvelope); - -export type DeleteSkillError = unknown; - -export type DownloadSkillData = { - path: { - skill_name: string; - }; -}; - -export type DownloadSkillResponse = ((Blob | File)); - -export type DownloadSkillError = unknown; - -export type ListSkillFilesData = { - path: { - skill_name: string; - }; - query?: { - path?: string; - }; -}; - -export type ListSkillFilesResponse = (SuccessEnvelope); - -export type ListSkillFilesError = unknown; - -export type GetSkillFileData = { - path: { - /** - * URL-encoded relative file path. - */ - file_path: string; - skill_name: string; - }; -}; - -export type GetSkillFileResponse = (string); - -export type GetSkillFileError = unknown; - -export type UpdateSkillFileData = { - body: string; - path: { - /** - * URL-encoded relative file path. - */ - file_path: string; - skill_name: string; - }; -}; - -export type UpdateSkillFileResponse = (SuccessEnvelope); - -export type UpdateSkillFileError = unknown; - -export type ListNeoSkillCandidatesData = { - query?: { - skill_key?: string; - status?: string; - }; -}; - -export type ListNeoSkillCandidatesResponse = (SuccessEnvelope); - -export type ListNeoSkillCandidatesError = unknown; - -export type ListNeoSkillReleasesData = { - query?: { - skill_key?: string; - stage?: string; - }; -}; - -export type ListNeoSkillReleasesResponse = (SuccessEnvelope); - -export type ListNeoSkillReleasesError = unknown; - -export type GetNeoSkillPayloadData = { - query: { - payload_ref: string; - }; -}; - -export type GetNeoSkillPayloadResponse = (SuccessEnvelope); - -export type GetNeoSkillPayloadError = unknown; - -export type EvaluateNeoSkillCandidateData = { - body: NeoCandidateActionRequest; -}; - -export type EvaluateNeoSkillCandidateResponse = (SuccessEnvelope); - -export type EvaluateNeoSkillCandidateError = unknown; - -export type PromoteNeoSkillCandidateData = { - body: NeoCandidateActionRequest; -}; - -export type PromoteNeoSkillCandidateResponse = (SuccessEnvelope); - -export type PromoteNeoSkillCandidateError = unknown; - -export type RollbackNeoSkillReleaseData = { - body: NeoReleaseActionRequest; -}; - -export type RollbackNeoSkillReleaseResponse = (SuccessEnvelope); - -export type RollbackNeoSkillReleaseError = unknown; - -export type SyncNeoSkillReleaseData = { - body: NeoReleaseActionRequest; -}; - -export type SyncNeoSkillReleaseResponse = (SuccessEnvelope); - -export type SyncNeoSkillReleaseError = unknown; - -export type DeleteNeoSkillCandidateData = { - body: NeoCandidateActionRequest; -}; - -export type DeleteNeoSkillCandidateResponse = (SuccessEnvelope); - -export type DeleteNeoSkillCandidateError = unknown; - -export type DeleteNeoSkillReleaseData = { - body: NeoReleaseActionRequest; -}; - -export type DeleteNeoSkillReleaseResponse = (SuccessEnvelope); - -export type DeleteNeoSkillReleaseError = unknown; - -export type ListKnowledgeBasesData = { - query?: { - page?: number; - page_size?: number; - refresh_stats?: boolean; - }; -}; - -export type ListKnowledgeBasesResponse = (SuccessEnvelope); - -export type ListKnowledgeBasesError = unknown; - -export type CreateKnowledgeBaseData = { - body: KnowledgeBaseCreateRequest; -}; - -export type CreateKnowledgeBaseResponse = (SuccessEnvelope); - -export type CreateKnowledgeBaseError = unknown; - -export type GetKnowledgeBaseData = { - path: { - kb_id: string; - }; -}; - -export type GetKnowledgeBaseResponse = (SuccessEnvelope); - -export type GetKnowledgeBaseError = unknown; - -export type UpdateKnowledgeBaseData = { - body: KnowledgeBaseRequest; - path: { - kb_id: string; - }; -}; - -export type UpdateKnowledgeBaseResponse = (SuccessEnvelope); - -export type UpdateKnowledgeBaseError = unknown; - -export type DeleteKnowledgeBaseData = { - path: { - kb_id: string; - }; -}; - -export type DeleteKnowledgeBaseResponse = (SuccessEnvelope); - -export type DeleteKnowledgeBaseError = unknown; - -export type GetKnowledgeBaseStatsData = { - path: { - kb_id: string; - }; -}; - -export type GetKnowledgeBaseStatsResponse = (SuccessEnvelope); - -export type GetKnowledgeBaseStatsError = unknown; - -export type ListKnowledgeDocumentsData = { - path: { - kb_id: string; - }; - query?: { - page?: number; - page_size?: number; - /** - * Filter documents by name (case-insensitive partial match). - */ - search?: string; - }; -}; - -export type ListKnowledgeDocumentsResponse = (SuccessEnvelope); - -export type ListKnowledgeDocumentsError = unknown; - -export type UploadKnowledgeDocumentData = { - body: KnowledgeDocumentUploadRequest; - path: { - kb_id: string; - }; -}; - -export type UploadKnowledgeDocumentResponse = (SuccessEnvelope); - -export type UploadKnowledgeDocumentError = unknown; - -export type ImportKnowledgeDocumentsData = { - body: KnowledgeDocumentImportRequest; - path: { - kb_id: string; - }; -}; - -export type ImportKnowledgeDocumentsResponse = (SuccessEnvelope); - -export type ImportKnowledgeDocumentsError = unknown; - -export type ImportKnowledgeDocumentFromUrlData = { - body: KnowledgeDocumentUrlImportRequest; - path: { - kb_id: string; - }; -}; - -export type ImportKnowledgeDocumentFromUrlResponse = (SuccessEnvelope); - -export type ImportKnowledgeDocumentFromUrlError = unknown; - -export type GetKnowledgeDocumentData = { - path: { - document_id: string; - kb_id: string; - }; -}; - -export type GetKnowledgeDocumentResponse = (SuccessEnvelope); - -export type GetKnowledgeDocumentError = unknown; - -export type DeleteKnowledgeDocumentData = { - path: { - document_id: string; - kb_id: string; - }; -}; - -export type DeleteKnowledgeDocumentResponse = (SuccessEnvelope); - -export type DeleteKnowledgeDocumentError = unknown; - -export type ListKnowledgeChunksData = { - path: { - kb_id: string; - }; - query?: { - document_id?: string; - page?: number; - page_size?: number; - }; -}; - -export type ListKnowledgeChunksResponse = (SuccessEnvelope); - -export type ListKnowledgeChunksError = unknown; - -export type DeleteKnowledgeChunkData = { - path: { - chunk_id: string; - kb_id: string; - }; - query: { - document_id: string; - }; -}; - -export type DeleteKnowledgeChunkResponse = (SuccessEnvelope); - -export type DeleteKnowledgeChunkError = unknown; - -export type RetrieveKnowledgeBaseData = { - body: KnowledgeRetrieveRequest; - path: { - kb_id: string; - }; -}; - -export type RetrieveKnowledgeBaseResponse = (SuccessEnvelope); - -export type RetrieveKnowledgeBaseError = unknown; - -export type GetKnowledgeTaskData = { - path: { - task_id: string; - }; -}; - -export type GetKnowledgeTaskResponse = (SuccessEnvelope); - -export type GetKnowledgeTaskError = unknown; - -export type GetPersonaTreeResponse = (SuccessEnvelope); - -export type GetPersonaTreeError = unknown; - -export type ListPersonasData = { - query?: { - folder_id?: string; - }; -}; - -export type ListPersonasResponse = (SuccessEnvelope); - -export type ListPersonasError = unknown; - -export type CreatePersonaData = { - body: PersonaRequest; -}; - -export type CreatePersonaResponse = (SuccessEnvelope); - -export type CreatePersonaError = unknown; - -export type GetPersonaByIdData = { - query: { - persona_id: string; - }; -}; - -export type GetPersonaByIdResponse = (SuccessEnvelope); - -export type GetPersonaByIdError = unknown; - -export type UpdatePersonaByIdData = { - body: { - persona_id: string; - [key: string]: unknown | string; - }; -}; - -export type UpdatePersonaByIdResponse = (SuccessEnvelope); - -export type UpdatePersonaByIdError = unknown; - -export type DeletePersonaByIdData = { - query: { - persona_id: string; - }; -}; - -export type DeletePersonaByIdResponse = (SuccessEnvelope); - -export type DeletePersonaByIdError = unknown; - -export type GetPersonaData = { - path: { - persona_id: string; - }; -}; - -export type GetPersonaResponse = (SuccessEnvelope); - -export type GetPersonaError = unknown; - -export type UpdatePersonaData = { - body: PersonaRequest; - path: { - persona_id: string; - }; -}; - -export type UpdatePersonaResponse = (SuccessEnvelope); - -export type UpdatePersonaError = unknown; - -export type DeletePersonaData = { - path: { - persona_id: string; - }; -}; - -export type DeletePersonaResponse = (SuccessEnvelope); - -export type DeletePersonaError = unknown; - -export type ListPersonaFoldersData = { - query?: { - parent_id?: string; - }; -}; - -export type ListPersonaFoldersResponse = (SuccessEnvelope); - -export type ListPersonaFoldersError = unknown; - -export type CreatePersonaFolderData = { - body: PersonaFolderRequest; -}; - -export type CreatePersonaFolderResponse = (SuccessEnvelope); - -export type CreatePersonaFolderError = unknown; - -export type UpdatePersonaFolderData = { - body: PersonaFolderRequest; - path: { - folder_id: string; - }; -}; - -export type UpdatePersonaFolderResponse = (SuccessEnvelope); - -export type UpdatePersonaFolderError = unknown; - -export type DeletePersonaFolderData = { - path: { - folder_id: string; - }; -}; - -export type DeletePersonaFolderResponse = (SuccessEnvelope); - -export type DeletePersonaFolderError = unknown; - -export type MovePersonaItemData = { - body: PersonaMoveRequest; -}; - -export type MovePersonaItemResponse = (SuccessEnvelope); - -export type MovePersonaItemError = unknown; - -export type ReorderPersonaItemsData = { - body: ReorderRequest; -}; - -export type ReorderPersonaItemsResponse = (SuccessEnvelope); - -export type ReorderPersonaItemsError = unknown; - -export type ListSessionsData = { - query?: { - message_type?: 'all' | 'group' | 'private'; - page?: number; - page_size?: number; - platform?: string; - search?: string; - }; -}; - -export type ListSessionsResponse = (SuccessEnvelope); - -export type ListSessionsError = unknown; - -export type ListActiveUmosResponse = (SuccessEnvelope); - -export type ListActiveUmosError = unknown; - -export type ListSessionRulesData = { - query?: { - page?: number; - page_size?: number; - search?: string; - }; -}; - -export type ListSessionRulesResponse = (SuccessEnvelope); - -export type ListSessionRulesError = unknown; - -export type UpsertSessionRuleData = { - body: SessionRuleRequest; -}; - -export type UpsertSessionRuleResponse = (SuccessEnvelope); - -export type UpsertSessionRuleError = unknown; - -export type DeleteSessionRulesData = { - body: UmoListRequest; -}; - -export type DeleteSessionRulesResponse = (SuccessEnvelope); - -export type DeleteSessionRulesError = unknown; - -export type BatchUpdateSessionProviderData = { - body: BatchSessionProviderRequest; -}; - -export type BatchUpdateSessionProviderResponse = (SuccessEnvelope); - -export type BatchUpdateSessionProviderError = unknown; - -export type BatchUpdateSessionServiceData = { - body: BatchSessionServiceRequest; -}; - -export type BatchUpdateSessionServiceResponse = (SuccessEnvelope); - -export type BatchUpdateSessionServiceError = unknown; - -export type ListSessionGroupsResponse = (SuccessEnvelope); - -export type ListSessionGroupsError = unknown; - -export type CreateSessionGroupData = { - body: SessionGroupRequest; -}; - -export type CreateSessionGroupResponse = (SuccessEnvelope); - -export type CreateSessionGroupError = unknown; - -export type UpdateSessionGroupData = { - body: SessionGroupRequest; - path: { - group_id: string; - }; -}; - -export type UpdateSessionGroupResponse = (SuccessEnvelope); - -export type UpdateSessionGroupError = unknown; - -export type DeleteSessionGroupData = { - path: { - group_id: string; - }; -}; - -export type DeleteSessionGroupResponse = (SuccessEnvelope); - -export type DeleteSessionGroupError = unknown; - -export type ListConversationsData = { - query?: { - /** - * Comma-separated user IDs to exclude. - */ - exclude_ids?: string; - /** - * Comma-separated platforms to exclude. - */ - exclude_platforms?: string; - /** - * Comma-separated message types. - */ - message_types?: string; - page?: number; - page_size?: number; - platform_id?: string; - /** - * Comma-separated platform IDs. - */ - platforms?: string; - search?: string; - user_id?: string; - }; -}; - -export type ListConversationsResponse = (SuccessEnvelope); - -export type ListConversationsError = unknown; - -export type BatchDeleteConversationsData = { - body: ConversationBatchDeleteRequest; -}; - -export type BatchDeleteConversationsResponse = (SuccessEnvelope); - -export type BatchDeleteConversationsError = unknown; - -export type GetConversationData = { - path: { - conversation_id: string; - }; - query: { - user_id: string; - }; -}; - -export type GetConversationResponse = (SuccessEnvelope); - -export type GetConversationError = unknown; - -export type UpdateConversationData = { - body: ConversationPatchRequest; - path: { - conversation_id: string; - }; - query: { - user_id: string; - }; -}; - -export type UpdateConversationResponse = (SuccessEnvelope); - -export type UpdateConversationError = unknown; - -export type DeleteConversationData = { - path: { - conversation_id: string; - }; - query: { - user_id: string; - }; -}; - -export type DeleteConversationResponse = (SuccessEnvelope); - -export type DeleteConversationError = unknown; - -export type ReplaceConversationMessagesData = { - body: ConversationMessagesReplaceRequest; - path: { - conversation_id: string; - }; - query: { - user_id: string; - }; -}; - -export type ReplaceConversationMessagesResponse = (SuccessEnvelope); - -export type ReplaceConversationMessagesError = unknown; - -export type ExportConversationsData = { - body: ConversationExportRequest; -}; - -export type ExportConversationsResponse = (unknown); - -export type ExportConversationsError = unknown; - -export type GetStatsData = { - query?: { - offset_sec?: number; - }; -}; - -export type GetStatsResponse = (SuccessEnvelope); - -export type GetStatsError = unknown; - -export type GetProviderTokenStatsData = { - query?: { - days?: number; - }; -}; - -export type GetProviderTokenStatsResponse = (SuccessEnvelope); - -export type GetProviderTokenStatsError = unknown; - -export type GetVersionResponse = (SuccessEnvelope); - -export type GetVersionError = unknown; - -export type GetPublicVersionsResponse = (SuccessEnvelope); - -export type GetPublicVersionsError = unknown; - -export type GetFirstNoticeData = { - query?: { - locale?: string; - }; -}; - -export type GetFirstNoticeResponse = (SuccessEnvelope); - -export type GetFirstNoticeError = unknown; - -export type TestGhproxyConnectionData = { - body: GhproxyTestRequest; -}; - -export type TestGhproxyConnectionResponse = (SuccessEnvelope); - -export type TestGhproxyConnectionError = unknown; - -export type ListChangelogVersionsResponse = (SuccessEnvelope); - -export type ListChangelogVersionsError = unknown; - -export type GetChangelogData = { - path: { - version: string; - }; -}; - -export type GetChangelogResponse = (SuccessEnvelope); - -export type GetChangelogError = (ErrorEnvelope); - -export type GetStartTimeResponse = (SuccessEnvelope); - -export type GetStartTimeError = unknown; - -export type GetStorageStatusResponse = (SuccessEnvelope); - -export type GetStorageStatusError = unknown; - -export type CleanupStorageData = { - body?: DynamicConfig; -}; - -export type CleanupStorageResponse = (SuccessEnvelope); - -export type CleanupStorageError = unknown; - -export type RestartCoreResponse = (SuccessEnvelope); - -export type RestartCoreError = unknown; - -export type ListBackupsData = { - query?: { - page?: number; - page_size?: number; - }; -}; - -export type ListBackupsResponse = (SuccessEnvelope); - -export type ListBackupsError = unknown; - -export type CreateBackupData = { - body?: BackupExportRequest; -}; - -export type CreateBackupResponse = (SuccessEnvelope); - -export type CreateBackupError = unknown; - -export type UploadBackupData = { - body: BackupUploadRequest; -}; - -export type UploadBackupResponse = (SuccessEnvelope); - -export type UploadBackupError = unknown; - -export type InitBackupUploadData = { - body: BackupUploadInitRequest; -}; - -export type InitBackupUploadResponse = (SuccessEnvelope); - -export type InitBackupUploadError = unknown; - -export type UploadBackupChunkData = { - body: BackupChunkUploadRequest; -}; - -export type UploadBackupChunkResponse = (SuccessEnvelope); - -export type UploadBackupChunkError = unknown; - -export type CompleteBackupUploadData = { - body: BackupUploadSessionRequest; -}; - -export type CompleteBackupUploadResponse = (SuccessEnvelope); - -export type CompleteBackupUploadError = unknown; - -export type AbortBackupUploadData = { - body: BackupUploadSessionRequest; -}; - -export type AbortBackupUploadResponse = (SuccessEnvelope); - -export type AbortBackupUploadError = unknown; - -export type GetBackupProgressData = { - path: { - task_id: string; - }; -}; - -export type GetBackupProgressResponse = (SuccessEnvelope); - -export type GetBackupProgressError = unknown; - -export type DownloadBackupData = { - path: { - filename: string; - }; -}; - -export type DownloadBackupResponse = (unknown); - -export type DownloadBackupError = unknown; - -export type RenameBackupData = { - body: BackupRenameRequest; - path: { - filename: string; - }; -}; - -export type RenameBackupResponse = (SuccessEnvelope); - -export type RenameBackupError = unknown; - -export type DeleteBackupData = { - path: { - filename: string; - }; -}; - -export type DeleteBackupResponse = (SuccessEnvelope); - -export type DeleteBackupError = unknown; - -export type CheckBackupData = { - path: { - filename: string; - }; -}; - -export type CheckBackupResponse = (SuccessEnvelope); - -export type CheckBackupError = unknown; - -export type ImportBackupData = { - body?: BackupImportRequest; - path: { - filename: string; - }; -}; - -export type ImportBackupResponse = (SuccessEnvelope); - -export type ImportBackupError = unknown; - -export type CheckUpdateResponse = (SuccessEnvelope); - -export type CheckUpdateError = unknown; - -export type ListReleasesData = { - query?: { - type?: 'core' | 'dashboard'; - }; -}; - -export type ListReleasesResponse = (SuccessEnvelope); - -export type ListReleasesError = unknown; - -export type UpdateCoreData = { - body?: UpdateRequest; -}; - -export type UpdateCoreResponse = (SuccessEnvelope); - -export type UpdateCoreError = unknown; - -export type UpdateDashboardData = { - body?: UpdateRequest; -}; - -export type UpdateDashboardResponse = (SuccessEnvelope); - -export type UpdateDashboardError = unknown; - -export type GetUpdateProgressData = { - path: { - task_id: string; - }; -}; - -export type GetUpdateProgressResponse = (SuccessEnvelope); - -export type GetUpdateProgressError = unknown; - -export type InstallPipPackageData = { - body: PipInstallRequest; -}; - -export type InstallPipPackageResponse = (SuccessEnvelope); - -export type InstallPipPackageError = unknown; - -export type ListCronJobsData = { - query?: { - type?: string; - }; -}; - -export type ListCronJobsResponse = (SuccessEnvelope); - -export type ListCronJobsError = unknown; - -export type CreateCronJobData = { - body: CronJobRequest; -}; - -export type CreateCronJobResponse = (SuccessEnvelope); - -export type CreateCronJobError = unknown; - -export type UpdateCronJobData = { - body: CronJobPatchRequest; - path: { - job_id: string; - }; -}; - -export type UpdateCronJobResponse = (SuccessEnvelope); - -export type UpdateCronJobError = unknown; - -export type DeleteCronJobData = { - path: { - job_id: string; - }; -}; - -export type DeleteCronJobResponse = (SuccessEnvelope); - -export type DeleteCronJobError = unknown; - -export type RunCronJobData = { - path: { - job_id: string; - }; -}; - -export type RunCronJobResponse = (SuccessEnvelope); - -export type RunCronJobError = unknown; - -export type StreamLiveLogsResponse = (string); - -export type StreamLiveLogsError = unknown; - -export type GetLogHistoryResponse = (string); - -export type GetLogHistoryError = unknown; - -export type GetTraceSettingsResponse = (SuccessEnvelope); - -export type GetTraceSettingsError = unknown; - -export type UpdateTraceSettingsData = { - body: TraceSettingsRequest; -}; - -export type UpdateTraceSettingsResponse = (SuccessEnvelope); - -export type UpdateTraceSettingsError = unknown; - -export type ListT2iTemplatesResponse = (SuccessEnvelope); - -export type ListT2iTemplatesError = unknown; - -export type CreateT2iTemplateData = { - body: T2iTemplateRequest; -}; - -export type CreateT2iTemplateResponse = (SuccessEnvelope); - -export type CreateT2iTemplateError = unknown; - -export type GetActiveT2iTemplateResponse = (SuccessEnvelope); - -export type GetActiveT2iTemplateError = unknown; - -export type SetActiveT2iTemplateData = { - body: NameRequest; -}; - -export type SetActiveT2iTemplateResponse = (SuccessEnvelope); - -export type SetActiveT2iTemplateError = unknown; - -export type ResetDefaultT2iTemplateResponse = (SuccessEnvelope); - -export type ResetDefaultT2iTemplateError = unknown; - -export type GetT2iTemplateData = { - path: { - name: string; - }; -}; - -export type GetT2iTemplateResponse = (SuccessEnvelope); - -export type GetT2iTemplateError = unknown; - -export type UpdateT2iTemplateData = { - body: T2iTemplateContentRequest; - path: { - name: string; - }; -}; - -export type UpdateT2iTemplateResponse = (SuccessEnvelope); - -export type UpdateT2iTemplateError = unknown; - -export type DeleteT2iTemplateData = { - path: { - name: string; - }; -}; - -export type DeleteT2iTemplateResponse = (SuccessEnvelope); - -export type DeleteT2iTemplateError = unknown; - -export type GetSubagentConfigResponse = (SuccessEnvelope); - -export type GetSubagentConfigError = unknown; - -export type UpdateSubagentConfigData = { - body: DynamicConfig; -}; - -export type UpdateSubagentConfigResponse = (SuccessEnvelope); - -export type UpdateSubagentConfigError = unknown; - -export type ListSubagentAvailableToolsResponse = (SuccessEnvelope); - -export type ListSubagentAvailableToolsError = unknown; - -export type VerifyPlatformWebhookData = { - path: { - webhook_uuid: string; - }; -}; - -export type VerifyPlatformWebhookResponse = (unknown); - -export type VerifyPlatformWebhookError = unknown; - -export type ReceivePlatformWebhookData = { - body?: DynamicConfig; - path: { - webhook_uuid: string; - }; -}; - -export type ReceivePlatformWebhookResponse = (unknown); - -export type ReceivePlatformWebhookError = unknown; \ No newline at end of file diff --git a/legacy-dashboard/src/api/http.ts b/legacy-dashboard/src/api/http.ts deleted file mode 100644 index 25111942..00000000 --- a/legacy-dashboard/src/api/http.ts +++ /dev/null @@ -1,151 +0,0 @@ -import axios, { - type AxiosError, - type AxiosInstance, - type InternalAxiosRequestConfig, -} from 'axios'; - -const AUTH_HEADER = 'Authorization'; -const LOCALE_HEADER = 'Accept-Language'; - -let configured = false; -let originalFetch: typeof window.fetch | null = null; - -export const httpClient = axios; -export const apiV1Client = axios.create({ baseURL: '/api/v1' }); - -function getToken(): string | null { - return localStorage.getItem('token'); -} - -function getLocale(): string | null { - return localStorage.getItem('astrbot-locale'); -} - -function setAxiosHeader( - headers: InternalAxiosRequestConfig['headers'], - key: string, - value: string, -) { - if (typeof headers.set === 'function') { - headers.set(key, value); - return; - } - headers[key] = value; -} - -function attachAxiosHeaders(config: InternalAxiosRequestConfig) { - const token = getToken(); - if (token) { - setAxiosHeader(config.headers, AUTH_HEADER, `Bearer ${token}`); - } - - const locale = getLocale(); - if (locale) { - setAxiosHeader(config.headers, LOCALE_HEADER, locale); - } - - return config; -} - -function normalizeAxiosError(error: AxiosError) { - if (error.response?.status === 401) { - let requestPath = ''; - try { - const url = error.config?.url || ''; - const baseURL = error.config?.baseURL; - const resolvedUrl = - url && baseURL && !/^([a-z][a-z\d+\-.]*:)?\/\//i.test(url) - ? `${baseURL.replace(/\/+$/, '')}/${url.replace(/^\/+/, '')}` - : url; - const requestUrl = new URL(resolvedUrl || '/', window.location.origin); - if (requestUrl.origin === window.location.origin) { - requestPath = requestUrl.pathname; - } - } catch { - requestPath = ''; - } - - const isAuthChallenge = - [ - '/api/auth/login', - '/api/auth/setup', - '/api/auth/setup-status', - '/api/v1/auth/login', - '/api/v1/auth/setup', - '/api/v1/auth/setup-status', - ].includes(requestPath) || - Boolean( - ( - error.response.data as - | { data?: { totp_required?: boolean } } - | undefined - )?.data?.totp_required, - ); - - if (requestPath.startsWith('/api/') && !isAuthChallenge) { - [ - 'user', - 'token', - 'change_pwd_hint', - 'md5_pwd_hint', - 'password_upgrade_required', - ].forEach((key) => localStorage.removeItem(key)); - - if (!window.location.hash.startsWith('#/auth/login')) { - window.location.hash = '/auth/login'; - } - } - } - - if (error.response?.status === 429) { - const data = error.response.data as { message?: string } | undefined; - if (data?.message) { - return Promise.reject(data.message); - } - } - return Promise.reject(error); -} - -function installAxiosInterceptors(instance: AxiosInstance) { - instance.interceptors.request.use(attachAxiosHeaders); - instance.interceptors.response.use((response) => response, normalizeAxiosError); -} - -export function fetchWithAuth(input: RequestInfo | URL, init?: RequestInit) { - const fetchImpl = originalFetch ?? window.fetch.bind(window); - const token = getToken(); - const locale = getLocale(); - - if (!token && !locale) { - return fetchImpl(input, init); - } - - const requestHeaders = - typeof input !== 'string' && 'headers' in input - ? (input as Request).headers - : undefined; - const headers = new Headers(init?.headers || requestHeaders); - - if (token && !headers.has(AUTH_HEADER)) { - headers.set(AUTH_HEADER, `Bearer ${token}`); - } - if (locale && !headers.has(LOCALE_HEADER)) { - headers.set(LOCALE_HEADER, locale); - } - - return fetchImpl(input, { ...init, headers }); -} - -export function setupHttpClient() { - if (configured) { - return; - } - - installAxiosInterceptors(axios); - installAxiosInterceptors(apiV1Client); - - originalFetch = window.fetch.bind(window); - window.fetch = fetchWithAuth; - - configured = true; -} diff --git a/legacy-dashboard/src/api/v1.ts b/legacy-dashboard/src/api/v1.ts deleted file mode 100644 index 37022d33..00000000 --- a/legacy-dashboard/src/api/v1.ts +++ /dev/null @@ -1,1795 +0,0 @@ -import type { AxiosRequestConfig, AxiosResponse } from 'axios'; - -import * as openApiV1 from './generated/openapi-v1'; -import { - type BackupChunkUploadRequest, - client as openApiV1Client, - type BackupExportRequest, - type BackupRenameRequest, - type BackupUploadInitRequest, - type BackupUploadRequest, - type BackupUploadSessionRequest, - type BotConfigRequest, - type BotRegistrationRequest, - type ChatMessagePatchRequest, - type ChatMessageRegenerateRequest, - type ChatProjectRequest, - type ChatRequest, - type ChatSessionBatchDeleteRequest, - type ChatSessionPatchRequest, - type ChatThreadCreateRequest, - type ChatThreadMessageRequest, - type CommandPatchRequest, - type ConfigRouteUpsertRequest, - type ConfigRoutesReplaceRequest, - type ConversationBatchDeleteRequest, - type ConversationExportRequest, - type ConversationMessagesReplaceRequest, - type ConversationPatchRequest, - type CreateApiKeyRequest, - type CronJobPatchRequest, - type CronJobRequest, - type DynamicConfig, - type EnabledPatch, - type GhproxyTestRequest, - type KnowledgeBaseCreateRequest, - type KnowledgeBaseRequest, - type LoginRequest, - type ListConversationsData, - type McpServerConfig, - type ModelScopeSyncRequest, - type PipInstallRequest, - type PluginVersionSupportRequest, - type PluginValidateRepoRequest, - type PluginConfigFileDeleteRequest, - type ProviderConfigRequest, - type BatchSessionProviderRequest, - type BatchSessionServiceRequest, - type SetupAuthRequest, - type SessionGroupRequest, - type SessionRuleRequest, - type UmoListRequest, - type SuccessEnvelope, - type T2iTemplateRequest, - type TotpSetupRequest, - type TraceSettingsRequest, - type UpdateAccountRequest, - type UpdateRequest, -} from './generated/openapi-v1'; -import { apiV1Client, httpClient } from './http'; - -openApiV1Client.setConfig({ - axios: httpClient, - throwOnError: true, -}); - -export interface ApiEnvelope<T> { - status: 'ok' | 'error'; - message?: string | null; - data: T; -} - -export const UPGRADE_RECOVERY_EVENT = 'astrbot-upgrade-recovery'; -export const UPGRADE_RECOVERY_TOKEN_KEY = 'astrbot-upgrade-recovery-token'; - -export type OpenConfig = DynamicConfig; - -export interface ProviderSchemaData { - config_schema?: OpenConfig; - providers?: OpenConfig[]; - provider_sources?: OpenConfig[]; - model_metadata?: Record<string, unknown>; -} - -export interface ProviderListData { - providers?: OpenConfig[]; - model_metadata?: Record<string, unknown>; -} - -export interface ProviderByTypeEnvelope extends ApiEnvelope<OpenConfig[]> { - model_metadata?: Record<string, unknown>; -} - -export interface ProviderByIdData { - provider?: OpenConfig; - model_metadata?: Record<string, unknown>; -} - -export interface ProviderSourceModelsData { - models?: string[]; - model_metadata?: Record<string, unknown>; -} - -export interface ProviderTestData { - id?: string; - model?: string | null; - type?: string | null; - name?: string; - status?: string; - error?: string | null; -} - -export interface ProviderEmbeddingDimensionData { - embedding_dimensions?: number; - [key: string]: unknown; -} - -export interface VersionData { - version?: string; - dashboard_version?: string; - change_pwd_hint?: boolean; - md5_pwd_hint?: boolean; - password_upgrade_required?: boolean; - [key: string]: unknown; -} - -export interface PublicVersionData { - webui_version?: string | null; - astrbot_version?: string | null; - astrbot_code_version?: string | null; - [key: string]: unknown; -} - -type StartTimeData = { - start_time?: number | string | null; -}; - -export interface CommandListData { - items?: any[]; - wake_prefix?: string[]; - summary?: { - disabled?: number; - conflicts?: number; - [key: string]: unknown; - }; - [key: string]: unknown; -} - -export interface BotListParams { - enabled?: boolean; - type?: string; -} - -export interface ProviderListParams { - capability?: 'chat' | 'agent' | 'stt' | 'tts' | 'embedding' | 'rerank'; - source_id?: string; - enabled?: boolean; -} - -export interface ToolListParams { - origin?: 'builtin' | 'plugin' | 'mcp'; - enabled?: boolean; -} - -export interface BackupListParams { - page?: number; - page_size?: number; -} - -export interface SessionListParams { - page?: number; - page_size?: number; - search?: string; - platform?: string; - message_type?: 'all' | 'group' | 'private'; -} - -export interface SessionRuleListParams { - page?: number; - page_size?: number; - search?: string; -} - -export interface ChatSessionListParams { - page?: number; - page_size?: number; - username?: string; -} - -export interface CronJobListParams { - type?: string; -} - -type ProviderCapability = NonNullable<ProviderListParams['capability']>; - -const PROVIDER_TYPE_TO_CAPABILITY: Record<string, ProviderCapability> = { - chat_completion: 'chat', - agent_runner: 'agent', - speech_to_text: 'stt', - text_to_speech: 'tts', - embedding: 'embedding', - rerank: 'rerank', -}; - -type V1Response<T> = Promise< - AxiosResponse<ApiEnvelope<T>> & { legacyFallback?: boolean } ->; -type ListConversationsQuery = NonNullable<ListConversationsData['query']>; - -function typed<T>(response: Promise<unknown>): V1Response<T> { - return response as unknown as V1Response<T>; -} - -export function isLegacyFallbackError(error: unknown): boolean { - const axiosError = error as { - response?: { status?: number; data?: { message?: string } | string }; - message?: string; - }; - if (axiosError.response?.status === 404) { - return true; - } - - const data = axiosError.response?.data; - const message = - typeof data === 'string' ? data : data?.message || axiosError.message || ''; - return message.toLowerCase().includes('missing api key'); -} - -function withLegacyFallback<T>( - primary: Promise<unknown>, - legacy: () => Promise<AxiosResponse<ApiEnvelope<T>>>, -): V1Response<T> { - const legacyRequest = () => - legacy().then((response) => { - const legacyResponse = response as AxiosResponse<ApiEnvelope<T>> & { - legacyFallback?: boolean; - }; - legacyResponse.legacyFallback = true; - return legacyResponse; - }); - - return typed<T>(primary).then((response) => { - const message = response.data?.message || ''; - if ( - response.data?.status === 'error' && - message.toLowerCase().includes('missing api key') - ) { - return legacyRequest(); - } - return response; - }).catch((error) => { - if (isLegacyFallbackError(error)) { - return legacyRequest(); - } - throw error; - }); -} - -function firstSuccessfulResponse<T>( - requests: Array<Promise<AxiosResponse<ApiEnvelope<T>>>>, -): V1Response<T> { - return new Promise<AxiosResponse<ApiEnvelope<T>>>((resolve, reject) => { - let pending = requests.length; - let firstError: unknown; - requests.forEach((request) => { - request.then(resolve).catch((error) => { - if (firstError === undefined) { - firstError = error; - } - pending -= 1; - if (pending === 0) { - reject(firstError); - } - }); - }); - }); -} - -function generatedOptions( - options: Record<string, unknown>, - requestConfig?: AxiosRequestConfig, -) { - return { ...options, ...(requestConfig || {}) } as any; -} - -function generatedQuery<T extends object>( - params?: T, -): (T & Record<string, unknown>) | undefined { - return params as (T & Record<string, unknown>) | undefined; -} - -function generatedFormData(formData: FormData | Record<string, unknown>) { - if (typeof FormData !== 'undefined' && formData instanceof FormData) { - const body: Record<string, unknown> = {}; - formData.forEach((value, key) => { - const existing = body[key]; - if (existing === undefined) { - body[key] = value; - } else if (Array.isArray(existing)) { - existing.push(value); - } else { - body[key] = [existing, value]; - } - }); - return body as any; - } - return formData as any; -} - -function botConfig(config: OpenConfig): BotConfigRequest { - return { - id: typeof config.id === 'string' ? config.id : undefined, - type: typeof config.type === 'string' ? config.type : '', - enabled: - typeof config.enable === 'boolean' - ? config.enable - : typeof config.enabled === 'boolean' - ? config.enabled - : undefined, - config, - }; -} - -function providerConfig(config: OpenConfig): ProviderConfigRequest { - return { config } as ProviderConfigRequest; -} - -function providerTypeToCapabilities(providerType: string): ProviderCapability[] { - return providerType - .split(',') - .map((item) => item.trim()) - .filter(Boolean) - .map((item) => PROVIDER_TYPE_TO_CAPABILITY[item] || (item as ProviderCapability)); -} - -function pluginExtensionPath(pluginPath: string): string { - return pluginPath - .replace(/^\/+/, '') - .split('/') - .filter(Boolean) - .map((segment) => encodeURIComponent(segment)) - .join('/'); -} - -export const configProfileApi = { - schema() { - return typed<OpenConfig>(openApiV1.getConfigProfileSchema()); - }, - list() { - return typed<{ info_list: OpenConfig[] }>(openApiV1.listConfigProfiles()); - }, - create(payload: { name?: string | null; config?: OpenConfig | null }) { - return typed<{ conf_id: string }>( - openApiV1.createConfigProfile({ - body: { - name: payload.name ?? undefined, - config: payload.config ?? undefined, - }, - }), - ); - }, - get(configId: string) { - return typed<OpenConfig>( - openApiV1.getConfigProfile({ path: { config_id: configId } }), - ); - }, - update( - configId: string, - config: OpenConfig, - requestConfig?: AxiosRequestConfig, - ) { - return typed<OpenConfig>( - openApiV1.updateConfigProfileContent( - generatedOptions( - { - path: { config_id: configId }, - body: config, - }, - requestConfig, - ), - ), - ); - }, - rename(configId: string, name: string | null) { - return typed<OpenConfig>( - openApiV1.renameConfigProfile({ - path: { config_id: configId }, - body: { name: name ?? '' }, - }), - ); - }, - delete(configId: string) { - return typed<OpenConfig>( - openApiV1.deleteConfigProfile({ path: { config_id: configId } }), - ); - }, -}; - -export const systemConfigApi = { - schema() { - return typed<OpenConfig>(openApiV1.getSystemConfigSchema()); - }, - get() { - return typed<OpenConfig>(openApiV1.getSystemConfig()); - }, - runtime() { - return typed<OpenConfig>(openApiV1.getSystemConfigRuntime()); - }, - update(config: OpenConfig, requestConfig?: AxiosRequestConfig) { - return typed<OpenConfig>( - openApiV1.updateSystemConfig( - generatedOptions({ body: config }, requestConfig), - ), - ); - }, -}; - -export const configRouteApi = { - list() { - return typed<{ routing?: Record<string, string> }>(openApiV1.listConfigRoutes()); - }, - replace(payload: ConfigRoutesReplaceRequest) { - return typed<OpenConfig>(openApiV1.replaceConfigRoutes({ body: payload })); - }, - upsert(umo: string, payload: ConfigRouteUpsertRequest) { - return typed<OpenConfig>( - openApiV1.upsertConfigRoute({ path: { umo }, body: payload }), - ); - }, - delete(umo: string) { - return typed<OpenConfig>( - openApiV1.deleteConfigRoute({ path: { umo } }), - ); - }, -}; - -export const botApi = { - types() { - return typed<{ bot_types: OpenConfig[] }>(openApiV1.listBotTypes()); - }, - list(params?: BotListParams) { - return typed<{ bots: OpenConfig[] }>( - openApiV1.listBots({ query: generatedQuery(params) }), - ); - }, - stats() { - return typed<{ platforms: OpenConfig[] }>(openApiV1.listBotStats()); - }, - registration(botType: string, payload: BotRegistrationRequest) { - return typed<any>( - openApiV1.registerBotType({ - path: { bot_type: botType }, - body: payload, - }), - ); - }, - create(config: OpenConfig) { - return typed<OpenConfig>(openApiV1.createBot({ body: botConfig(config) })); - }, - get(botId: string) { - return typed<{ bot: OpenConfig }>( - openApiV1.getBotById({ query: { bot_id: botId } }), - ); - }, - update(botId: string, config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updateBotById({ - body: { bot_id: botId, config }, - }), - ); - }, - setEnabled(botId: string, payload: EnabledPatch) { - return typed<OpenConfig>( - openApiV1.setBotEnabledById({ - body: { bot_id: botId, enabled: payload.enabled }, - }), - ); - }, - delete(botId: string) { - return typed<OpenConfig>( - openApiV1.deleteBotById({ query: { bot_id: botId } }), - ); - }, -}; - -export const providerApi = { - schema() { - return typed<ProviderSchemaData>(openApiV1.getProviderSchema()); - }, - sources() { - return typed<{ provider_sources: OpenConfig[] }>( - openApiV1.listProviderSources(), - ); - }, - upsertSource(sourceId: string, config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.upsertProviderSourceById({ - body: { source_id: sourceId, config }, - }), - ); - }, - deleteSource(sourceId: string) { - return typed<OpenConfig>( - openApiV1.deleteProviderSourceById({ query: { source_id: sourceId } }), - ); - }, - sourceModels(sourceId: string) { - return typed<ProviderSourceModelsData>( - openApiV1.listProviderSourceModelsById({ - query: { source_id: sourceId }, - }), - ); - }, - list(params?: ProviderListParams) { - return typed<ProviderListData>( - openApiV1.listProviders({ query: generatedQuery(params) }), - ); - }, - async listByProviderType( - providerType: string, - ): Promise<AxiosResponse<ProviderByTypeEnvelope>> { - const capabilities = providerTypeToCapabilities(providerType); - if (capabilities.length === 0) { - const response = await providerApi.list(); - return { - ...response, - data: { - ...response.data, - data: response.data.data.providers || [], - model_metadata: response.data.data.model_metadata || {}, - }, - }; - } - - const responses = await Promise.all( - capabilities.map((capability) => providerApi.list({ capability })), - ); - const first = responses[0]; - const modelMetadata = responses.reduce<Record<string, unknown>>( - (acc, response) => ({ - ...acc, - ...(response.data.data.model_metadata || {}), - }), - {}, - ); - return { - ...first, - data: { - ...first.data, - data: responses.flatMap( - (response) => response.data.data.providers || [], - ), - model_metadata: modelMetadata, - }, - }; - }, - create(config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.createProvider({ body: providerConfig(config) }), - ); - }, - listBySource(sourceId: string, params?: Pick<ProviderListParams, 'capability'>) { - return typed<{ providers: OpenConfig[] }>( - openApiV1.listProvidersBySourceId({ - query: { source_id: sourceId, ...params }, - }), - ); - }, - createInSource(sourceId: string, config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.createProviderInSourceById({ - body: { source_id: sourceId, config }, - }), - ); - }, - get(providerId: string, merged = false) { - return typed<ProviderByIdData>( - openApiV1.getProviderById({ - query: { provider_id: providerId, merged }, - }), - ); - }, - update(providerId: string, config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updateProviderById({ - body: { provider_id: providerId, config }, - }), - ); - }, - setEnabled(providerId: string, payload: EnabledPatch) { - return typed<OpenConfig>( - openApiV1.setProviderEnabledById({ - body: { provider_id: providerId, enabled: payload.enabled }, - }), - ); - }, - delete(providerId: string) { - return typed<OpenConfig>( - openApiV1.deleteProviderById({ query: { provider_id: providerId } }), - ); - }, - test(providerId: string) { - return typed<ProviderTestData>( - openApiV1.testProviderById({ body: { provider_id: providerId } }), - ); - }, - embeddingDimension(providerId: string, providerConfig?: OpenConfig) { - return typed<ProviderEmbeddingDimensionData>( - openApiV1.getProviderEmbeddingDimensionById({ - body: { - provider_id: providerId, - ...(providerConfig ? { provider_config: providerConfig } : {}), - }, - }), - ); - }, -}; - -export const authApi = { - login(payload: LoginRequest) { - return withLegacyFallback<any>(openApiV1.login({ body: payload }), () => - httpClient.post<ApiEnvelope<any>>('/api/auth/login', payload), - ); - }, - logout() { - return withLegacyFallback<OpenConfig>(openApiV1.logout(), () => - httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/logout'), - ); - }, - setupStatus() { - return withLegacyFallback<any>(openApiV1.getAuthSetupStatus(), () => - httpClient.get<ApiEnvelope<any>>('/api/auth/setup-status'), - ); - }, - setup(payload: SetupAuthRequest) { - return withLegacyFallback<OpenConfig>(openApiV1.setupAuth({ body: payload }), () => - httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/setup', payload), - ); - }, - setupTotp(payload?: TotpSetupRequest) { - return withLegacyFallback<any>(openApiV1.setupTotp({ body: payload }), () => - httpClient.post<ApiEnvelope<any>>('/api/auth/totp/setup', payload), - ); - }, - recoverTotp() { - return withLegacyFallback<any>(openApiV1.recoverTotp(), () => - httpClient.post<ApiEnvelope<any>>('/api/auth/totp/recovery'), - ); - }, - updateAccount(payload: UpdateAccountRequest) { - return withLegacyFallback<OpenConfig>( - openApiV1.updateAuthAccount({ body: payload }), - () => httpClient.post<ApiEnvelope<OpenConfig>>('/api/auth/account/edit', payload), - ); - }, -}; - -export const apiKeyApi = { - list() { - return typed<OpenConfig[]>(openApiV1.listApiKeys()); - }, - create(payload: CreateApiKeyRequest) { - return typed<{ api_key?: string }>(openApiV1.createApiKey({ body: payload })); - }, - revoke(keyId: string) { - return typed<OpenConfig>( - openApiV1.revokeApiKey({ path: { key_id: keyId } }), - ); - }, - delete(keyId: string) { - return typed<OpenConfig>( - openApiV1.deleteApiKey({ path: { key_id: keyId } }), - ); - }, -}; - -export const traceApi = { - getSettings() { - return typed<OpenConfig>(openApiV1.getTraceSettings()); - }, - updateSettings(settings: TraceSettingsRequest) { - return typed<OpenConfig>( - openApiV1.updateTraceSettings({ body: settings }), - ); - }, -}; - -export const updatesApi = { - check() { - return withLegacyFallback<any>(openApiV1.checkUpdate(), () => - httpClient.get<ApiEnvelope<any>>('/api/update/check'), - ); - }, - releases(type?: 'core' | 'dashboard') { - return withLegacyFallback<any[]>( - openApiV1.listReleases({ - query: type ? { type } : undefined, - }), - () => - httpClient.get<ApiEnvelope<any[]>>('/api/update/releases', { - params: type ? { type } : undefined, - }), - ); - }, - core(payload?: UpdateRequest) { - return withLegacyFallback<OpenConfig>(openApiV1.updateCore({ body: payload }), () => - httpClient.post<ApiEnvelope<OpenConfig>>('/api/update/do', payload), - ); - }, - dashboard(payload?: UpdateRequest) { - return withLegacyFallback<OpenConfig>( - openApiV1.updateDashboard({ body: payload }), - () => httpClient.post<ApiEnvelope<OpenConfig>>('/api/update/dashboard', payload), - ); - }, - progress(taskId: string) { - return withLegacyFallback<any>( - openApiV1.getUpdateProgress({ path: { task_id: taskId } }), - () => - httpClient.get<ApiEnvelope<any>>('/api/update/progress', { - params: { id: taskId }, - }), - ); - }, - installPip(payload: PipInstallRequest) { - return withLegacyFallback<OpenConfig>( - openApiV1.installPipPackage({ body: payload }), - () => httpClient.post<ApiEnvelope<OpenConfig>>('/api/update/pip-install', payload), - ); - }, -}; - -export const backupApi = { - list(params?: BackupListParams) { - return typed<any>(openApiV1.listBackups({ query: generatedQuery(params) })); - }, - create(payload?: BackupExportRequest) { - return typed<any>(openApiV1.createBackup({ body: payload })); - }, - progress(taskId: string) { - return typed<any>( - openApiV1.getBackupProgress({ path: { task_id: taskId } }), - ); - }, - upload(formData: FormData | BackupUploadRequest) { - return typed<any>( - openApiV1.uploadBackup({ body: generatedFormData(formData) }), - ); - }, - initUpload(payload: BackupUploadInitRequest) { - return typed<any>(openApiV1.initBackupUpload({ body: payload })); - }, - uploadChunk(formData: FormData | BackupChunkUploadRequest) { - return typed<any>( - openApiV1.uploadBackupChunk({ body: generatedFormData(formData) }), - ); - }, - completeUpload(payload: BackupUploadSessionRequest) { - return typed<any>(openApiV1.completeBackupUpload({ body: payload })); - }, - abortUpload(payload: BackupUploadSessionRequest) { - return typed<OpenConfig>(openApiV1.abortBackupUpload({ body: payload })); - }, - check(filename: string) { - return typed<any>( - openApiV1.checkBackup({ path: { filename } }), - ); - }, - import(filename: string, confirmed = true) { - return typed<any>( - openApiV1.importBackup({ - path: { filename }, - body: { confirmed } as any, - }), - ); - }, - delete(filename: string) { - return typed<OpenConfig>( - openApiV1.deleteBackup({ path: { filename } }), - ); - }, - rename(filename: string, payload: BackupRenameRequest) { - return typed<any>( - openApiV1.renameBackup({ path: { filename }, body: payload }), - ); - }, - downloadUrl(filename: string, token: string) { - return `/api/v1/backups/${encodeURIComponent(filename)}?token=${encodeURIComponent(token)}`; - }, -}; - -export const chatApi = { - send(payload: ChatRequest) { - return typed<any>(openApiV1.sendChatMessage({ body: payload })); - }, - sendStreamUrl() { - return '/api/v1/chat'; - }, - liveWebSocketUrl(token: string, host = window.location.host) { - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - return `${protocol}//${host}/api/v1/live-chat/ws?token=${encodeURIComponent(token)}`; - }, - unifiedWebSocketUrl(token: string, host = window.location.host) { - const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - return `${protocol}//${host}/api/v1/unified-chat/ws?token=${encodeURIComponent(token)}`; - }, - listSessions(params?: ChatSessionListParams) { - return typed<any>( - openApiV1.listChatSessions({ query: generatedQuery(params) }), - ); - }, - createSession(platformId?: string) { - return typed<any>( - openApiV1.createChatSession({ - query: platformId ? { platform_id: platformId } : undefined, - }), - ); - }, - getSession(sessionId: string) { - return typed<any>( - openApiV1.getChatSession({ path: { session_id: sessionId } }), - ); - }, - updateSession(sessionId: string, payload: ChatSessionPatchRequest) { - return typed<any>( - openApiV1.updateChatSession({ - path: { session_id: sessionId }, - body: payload, - }), - ); - }, - deleteSession(sessionId: string) { - return typed<any>( - openApiV1.deleteChatSession({ path: { session_id: sessionId } }), - ); - }, - batchDeleteSessions(payload: ChatSessionBatchDeleteRequest) { - return typed<any>(openApiV1.batchDeleteChatSessions({ body: payload })); - }, - stopSession(sessionId: string) { - return typed<any>( - openApiV1.stopChatSession({ path: { session_id: sessionId } }), - ); - }, - updateMessage( - sessionId: string, - messageId: string | number, - payload: ChatMessagePatchRequest, - ) { - return typed<any>( - openApiV1.updateChatMessage({ - path: { session_id: sessionId, message_id: String(messageId) }, - body: payload, - }), - ); - }, - regenerateMessage( - sessionId: string, - messageId: string | number, - payload?: ChatMessageRegenerateRequest, - ) { - return typed<any>( - openApiV1.regenerateChatMessage({ - path: { session_id: sessionId, message_id: String(messageId) }, - body: payload, - }) as any, - ); - }, - regenerateMessageUrl(sessionId: string, messageId: string | number) { - return `/api/v1/chat/sessions/${encodeURIComponent(sessionId)}/messages/${encodeURIComponent(String(messageId))}/regenerate`; - }, - createThread(payload: ChatThreadCreateRequest) { - return typed<any>(openApiV1.createChatThread({ body: payload })); - }, - getThread(threadId: string) { - return typed<any>( - openApiV1.getChatThread({ path: { thread_id: threadId } }), - ); - }, - deleteThread(threadId: string) { - return typed<any>( - openApiV1.deleteChatThread({ path: { thread_id: threadId } }), - ); - }, - sendThreadMessage(threadId: string, payload: ChatThreadMessageRequest) { - return typed<any>( - openApiV1.sendChatThreadMessage({ - path: { thread_id: threadId }, - body: payload, - }) as any, - ); - }, - sendThreadMessageUrl(threadId: string) { - return `/api/v1/chat/threads/${encodeURIComponent(threadId)}/messages`; - }, - listProjects() { - return typed<any>(openApiV1.listChatProjects()); - }, - createProject(payload: ChatProjectRequest) { - return typed<any>(openApiV1.createChatProject({ body: payload })); - }, - getProject(projectId: string) { - return typed<any>( - openApiV1.getChatProject({ path: { project_id: projectId } }), - ); - }, - updateProject(projectId: string, payload: ChatProjectRequest) { - return typed<any>( - openApiV1.updateChatProject({ - path: { project_id: projectId }, - body: payload, - }), - ); - }, - deleteProject(projectId: string) { - return typed<any>( - openApiV1.deleteChatProject({ path: { project_id: projectId } }), - ); - }, - listProjectSessions(projectId: string) { - return typed<any>( - openApiV1.listChatProjectSessions({ path: { project_id: projectId } }), - ); - }, - addProjectSession(projectId: string, sessionId: string) { - return typed<any>( - openApiV1.addChatProjectSession({ - path: { project_id: projectId, session_id: sessionId }, - }), - ); - }, - removeProjectSession(sessionId: string) { - return typed<any>( - openApiV1.removeChatProjectSession({ path: { session_id: sessionId } }), - ); - }, -}; - -export const fileApi = { - upload(formData: FormData) { - return typed<any>( - openApiV1.uploadFile({ body: generatedFormData(formData) }), - ); - }, - getByName(filename: string) { - return openApiV1.getFileByName({ - query: { filename }, - responseType: 'blob', - }) as Promise<AxiosResponse<Blob>>; - }, - byNameUrl(filename: string) { - return `/api/v1/files/content?filename=${encodeURIComponent(filename)}`; - }, - contentUrl(attachmentId: string) { - return `/api/v1/files/${encodeURIComponent(attachmentId)}/content`; - }, - tokenUrl(fileToken: string) { - return `/api/v1/files/tokens/${encodeURIComponent(fileToken)}`; - }, -}; - -export const sessionApi = { - list(params?: SessionListParams) { - return typed<any>(openApiV1.listSessions({ query: generatedQuery(params) })); - }, - activeUmos() { - return typed<any>(openApiV1.listActiveUmos()); - }, - listRules(params?: SessionRuleListParams) { - return typed<any>( - openApiV1.listSessionRules({ query: generatedQuery(params) }), - ); - }, - upsertRule(payload: SessionRuleRequest) { - return typed<any>(openApiV1.upsertSessionRule({ body: payload })); - }, - deleteRules(payload: UmoListRequest) { - return typed<any>(openApiV1.deleteSessionRules({ body: payload })); - }, - batchUpdateProvider(payload: BatchSessionProviderRequest) { - return typed<any>( - openApiV1.batchUpdateSessionProvider({ body: payload }), - ); - }, - batchUpdateService(payload: BatchSessionServiceRequest) { - return typed<any>( - openApiV1.batchUpdateSessionService({ body: payload }), - ); - }, - listGroups() { - return typed<any>(openApiV1.listSessionGroups()); - }, - createGroup(payload: SessionGroupRequest) { - return typed<any>(openApiV1.createSessionGroup({ body: payload })); - }, - updateGroup(groupId: string, payload: SessionGroupRequest) { - return typed<any>( - openApiV1.updateSessionGroup({ - path: { group_id: groupId }, - body: payload, - }), - ); - }, - deleteGroup(groupId: string) { - return typed<any>( - openApiV1.deleteSessionGroup({ path: { group_id: groupId } }), - ); - }, -}; - -export const cronApi = { - list(params?: CronJobListParams) { - return typed<any>(openApiV1.listCronJobs({ query: generatedQuery(params) })); - }, - create(payload: CronJobRequest) { - return typed<any>(openApiV1.createCronJob({ body: payload })); - }, - update(jobId: string, payload: CronJobPatchRequest) { - return typed<any>( - openApiV1.updateCronJob({ path: { job_id: jobId }, body: payload }), - ); - }, - delete(jobId: string) { - return typed<any>(openApiV1.deleteCronJob({ path: { job_id: jobId } })); - }, - run(jobId: string) { - return typed<any>(openApiV1.runCronJob({ path: { job_id: jobId } })); - }, -}; - -export const subagentApi = { - getConfig() { - return typed<OpenConfig>(openApiV1.getSubagentConfig()); - }, - updateConfig(config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updateSubagentConfig({ body: config }), - ); - }, - availableTools() { - return typed<any>(openApiV1.listSubagentAvailableTools()); - }, -}; - -export const commandApi = { - list(configId?: string) { - return typed<CommandListData>( - openApiV1.listCommands({ - query: configId ? { config_id: configId } : undefined, - }), - ); - }, - conflicts() { - return typed<OpenConfig>(openApiV1.listCommandConflicts()); - }, - update(commandId: string, patch: CommandPatchRequest) { - return typed<OpenConfig>( - openApiV1.updateCommand({ - path: { command_id: commandId }, - body: patch, - }), - ); - }, -}; - -export const toolApi = { - list(params?: ToolListParams) { - return typed<any[]>(openApiV1.listTools({ query: generatedQuery(params) })); - }, - setEnabled(toolId: string, enabled: boolean) { - return typed<OpenConfig>( - openApiV1.setToolEnabled({ - path: { tool_id: toolId }, - body: { enabled }, - }), - ); - }, - setPermission(toolId: string, permission: 'admin' | 'member') { - return typed<OpenConfig>( - openApiV1.setToolPermission({ - path: { tool_id: toolId }, - body: { permission }, - }), - ); - }, -}; - -export const mcpApi = { - list() { - return typed<OpenConfig[]>(openApiV1.listMcpServers()); - }, - create(config: McpServerConfig) { - return typed<OpenConfig>(openApiV1.createMcpServer({ body: config })); - }, - update(serverName: string, config: McpServerConfig) { - return typed<OpenConfig>( - openApiV1.updateMcpServerByName({ - body: { server_name: serverName, config }, - }), - ); - }, - delete(serverName: string) { - return typed<OpenConfig>( - openApiV1.deleteMcpServerByName({ query: { server_name: serverName } }), - ); - }, - setEnabled(serverName: string, enabled: boolean) { - return typed<OpenConfig>( - openApiV1.setMcpServerEnabledByName({ - body: { server_name: serverName, enabled }, - }), - ); - }, - test(serverName: string, config?: DynamicConfig) { - return typed<OpenConfig>( - openApiV1.testMcpServerByName({ - body: { - server_name: serverName, - ...(config ? { mcp_server_config: config } : {}), - }, - }), - ); - }, - syncModelScope(payload?: ModelScopeSyncRequest) { - return typed<OpenConfig>( - openApiV1.syncModelScopeMcpServers({ body: payload }), - ); - }, -}; - -export const t2iApi = { - listTemplates() { - return typed<OpenConfig[]>(openApiV1.listT2iTemplates()); - }, - getTemplate(name: string) { - return typed<{ name: string; content: string }>( - openApiV1.getT2iTemplate({ path: { name } }), - ); - }, - createTemplate(payload: T2iTemplateRequest) { - return typed<OpenConfig>( - openApiV1.createT2iTemplate({ body: payload }), - ); - }, - updateTemplate(name: string, content: string) { - return typed<OpenConfig>( - openApiV1.updateT2iTemplate({ - path: { name }, - body: { content }, - }), - ); - }, - deleteTemplate(name: string) { - return typed<OpenConfig>( - openApiV1.deleteT2iTemplate({ path: { name } }), - ); - }, - getActiveTemplate() { - return typed<{ active_template?: string }>(openApiV1.getActiveT2iTemplate()); - }, - setActiveTemplate(name: string) { - return typed<OpenConfig>( - openApiV1.setActiveT2iTemplate({ body: { name } }), - ); - }, - resetDefaultTemplate() { - return typed<OpenConfig>(openApiV1.resetDefaultT2iTemplate()); - }, -}; - -export const logApi = { - history() { - return typed<{ logs?: OpenConfig[] }>(openApiV1.getLogHistory()); - }, - liveUrl() { - return '/api/v1/logs/live'; - }, -}; - -export const pluginApi = { - list(params?: { include_reserved?: boolean; enabled?: boolean }) { - return typed<any[]>(openApiV1.listPlugins({ query: params })); - }, - get(pluginId: string) { - return typed<OpenConfig>( - openApiV1.getPluginById({ query: { plugin_id: pluginId } }), - ); - }, - failed() { - return typed<Record<string, OpenConfig>>(openApiV1.listFailedPlugins()); - }, - reloadFailed(pluginId: string) { - return typed<OpenConfig>( - openApiV1.reloadFailedPlugin({ path: { plugin_id: pluginId } }), - ); - }, - uninstallFailed( - pluginId: string, - options?: { delete_config?: boolean; delete_data?: boolean }, - ) { - return typed<OpenConfig>( - openApiV1.uninstallFailedPlugin({ - path: { plugin_id: pluginId }, - body: options, - }), - ); - }, - uninstall( - pluginId: string, - options?: { delete_config?: boolean; delete_data?: boolean }, - ) { - return typed<OpenConfig>( - openApiV1.uninstallPluginById({ - query: { plugin_id: pluginId }, - body: options, - }), - ); - }, - reload(pluginId: string) { - return typed<OpenConfig>( - openApiV1.reloadPluginById({ body: { plugin_id: pluginId } }), - ); - }, - setEnabled(pluginId: string, enabled: boolean) { - return typed<OpenConfig>( - openApiV1.setPluginEnabledById({ - body: { plugin_id: pluginId, enabled }, - }), - ); - }, - update(pluginId: string, body?: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updatePlugins({ - body: { plugin_id: pluginId, ...(body || {}) } as any, - }), - ); - }, - updateMany(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updatePlugins({ body: body as any }), - ); - }, - checkVersionSupport(payload: PluginVersionSupportRequest) { - return typed<any>( - openApiV1.checkPluginVersionSupport({ body: payload }), - ); - }, - config(pluginId: string) { - return typed<OpenConfig>( - openApiV1.getPluginConfigById({ query: { plugin_id: pluginId } }), - ); - }, - updateConfig(pluginId: string, config: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updatePluginConfigById({ - body: { plugin_id: pluginId, config }, - }), - ); - }, - listConfigFiles(pluginId: string, configKey: string) { - return typed<any>( - openApiV1.listPluginConfigFilesById({ - query: { plugin_id: pluginId, config_key: configKey }, - }), - ); - }, - uploadConfigFiles(pluginId: string, configKey: string, formData: FormData) { - return typed<any>( - openApiV1.uploadPluginConfigFilesById({ - query: { plugin_id: pluginId, config_key: configKey }, - body: generatedFormData(formData), - }), - ); - }, - deleteConfigFile(pluginId: string, payload: PluginConfigFileDeleteRequest) { - return typed<OpenConfig>( - openApiV1.deletePluginConfigFileById({ - query: { plugin_id: pluginId }, - body: payload, - }), - ); - }, - readme(pluginId: string) { - return typed<OpenConfig>( - openApiV1.getPluginReadmeById({ query: { plugin_id: pluginId } }), - ); - }, - changelog(pluginId: string) { - return typed<OpenConfig>( - openApiV1.getPluginChangelogById({ query: { plugin_id: pluginId } }), - ); - }, - market(params?: { - page?: number; - page_size?: number; - category?: string; - sort?: 'recommended' | 'downloads' | 'updated' | 'name'; - keyword?: string; - force_refresh?: boolean; - custom_registry?: string; - }) { - return typed<any>(openApiV1.listPluginMarket({ query: params })); - }, - sources() { - return typed<any>(openApiV1.listPluginSources()); - }, - replaceSources(sources: OpenConfig[]) { - return typed<OpenConfig>( - openApiV1.replacePluginSources({ body: { sources: sources as any } }), - ); - }, - installUpload(formData: FormData) { - return typed<OpenConfig>( - openApiV1.installPluginFromUpload({ - body: generatedFormData(formData), - }), - ); - }, - installGithub(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.installPluginFromGithub({ body: body as any }), - ); - }, - installUrl(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.installPluginFromUrl({ body: body as any }), - ); - }, - validateRepo(body: PluginValidateRepoRequest) { - return typed<OpenConfig>( - openApiV1.validatePluginRepo({ body }), - ); - }, - bindSource(pluginId: string, body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.bindPluginSource({ - path: { plugin_id: pluginId }, - body: body as any, - }), - ); - }, - page(pluginId: string, pageName: string) { - return typed<any>( - openApiV1.getPluginPageById({ - query: { plugin_id: pluginId, page_name: pageName }, - }) as any, - ); - }, -}; - -export const pluginExtensionApi = { - get<T = any>(pluginPath: string, config?: AxiosRequestConfig) { - return apiV1Client.get<ApiEnvelope<T>>( - `/plugins/extensions/${pluginExtensionPath(pluginPath)}`, - config, - ); - }, - post<T = any>( - pluginPath: string, - data?: unknown, - config?: AxiosRequestConfig, - ) { - return apiV1Client.post<ApiEnvelope<T>>( - `/plugins/extensions/${pluginExtensionPath(pluginPath)}`, - data, - config, - ); - }, - put<T = any>(pluginPath: string, data?: unknown, config?: AxiosRequestConfig) { - return apiV1Client.put<ApiEnvelope<T>>( - `/plugins/extensions/${pluginExtensionPath(pluginPath)}`, - data, - config, - ); - }, - patch<T = any>(pluginPath: string, data?: unknown, config?: AxiosRequestConfig) { - return apiV1Client.patch<ApiEnvelope<T>>( - `/plugins/extensions/${pluginExtensionPath(pluginPath)}`, - data, - config, - ); - }, - delete<T = any>(pluginPath: string, config?: AxiosRequestConfig) { - return apiV1Client.delete<ApiEnvelope<T>>( - `/plugins/extensions/${pluginExtensionPath(pluginPath)}`, - config, - ); - }, -}; - -export const knowledgeApi = { - list(params?: { page?: number; page_size?: number; refresh_stats?: boolean }) { - return typed<any>(openApiV1.listKnowledgeBases({ query: params })); - }, - get(kbId: string) { - return typed<OpenConfig>( - openApiV1.getKnowledgeBase({ path: { kb_id: kbId } }), - ); - }, - create(config: KnowledgeBaseCreateRequest) { - return typed<OpenConfig>( - openApiV1.createKnowledgeBase({ body: config }), - ); - }, - update(kbId: string, config: KnowledgeBaseRequest) { - return typed<OpenConfig>( - openApiV1.updateKnowledgeBase({ - path: { kb_id: kbId }, - body: config, - }), - ); - }, - delete(kbId: string) { - return typed<OpenConfig>( - openApiV1.deleteKnowledgeBase({ path: { kb_id: kbId } }), - ); - }, - documents(kbId: string, params?: { page?: number; page_size?: number; search?: string }) { - return typed<any>( - openApiV1.listKnowledgeDocuments({ - path: { kb_id: kbId }, - query: params, - }), - ); - }, - uploadDocument(kbId: string, formData: FormData) { - return typed<any>( - openApiV1.uploadKnowledgeDocument({ - path: { kb_id: kbId }, - body: generatedFormData(formData), - }), - ); - }, - importDocumentFromUrl(kbId: string, payload: OpenConfig) { - return typed<any>( - openApiV1.importKnowledgeDocumentFromUrl({ - path: { kb_id: kbId }, - body: payload as any, - }), - ); - }, - task(taskId: string) { - return typed<any>( - openApiV1.getKnowledgeTask({ path: { task_id: taskId } }), - ); - }, - document(kbId: string, documentId: string) { - return typed<OpenConfig>( - openApiV1.getKnowledgeDocument({ - path: { kb_id: kbId, document_id: documentId }, - }), - ); - }, - deleteDocument(kbId: string, documentId: string) { - return typed<OpenConfig>( - openApiV1.deleteKnowledgeDocument({ - path: { kb_id: kbId, document_id: documentId }, - }), - ); - }, - chunks( - kbId: string, - params?: { document_id?: string; page?: number; page_size?: number }, - ) { - return typed<any>( - openApiV1.listKnowledgeChunks({ - path: { kb_id: kbId }, - query: params, - }), - ); - }, - deleteChunk(kbId: string, chunkId: string, documentId: string) { - return typed<OpenConfig>( - openApiV1.deleteKnowledgeChunk({ - path: { kb_id: kbId, chunk_id: chunkId }, - query: { document_id: documentId }, - }), - ); - }, - retrieve(kbId: string, payload: OpenConfig) { - return typed<any>( - openApiV1.retrieveKnowledgeBase({ - path: { kb_id: kbId }, - body: payload as any, - }), - ); - }, -}; - -export const skillApi = { - list(params?: { enabled?: boolean; source?: string }) { - return typed<any>(openApiV1.listSkills({ query: params })); - }, - uploadBatch(files: File[]) { - return typed<any>( - openApiV1.uploadSkillsBatch({ body: { files } }), - ); - }, - setEnabled(skillName: string, enabled: boolean) { - return typed<OpenConfig>( - openApiV1.updateSkillByName({ - body: { skill_name: skillName, enabled }, - }), - ); - }, - delete(skillName: string) { - return typed<OpenConfig>( - openApiV1.deleteSkillByName({ query: { skill_name: skillName } }), - ); - }, - download(skillName: string) { - return openApiV1.downloadSkillByName({ - query: { skill_name: skillName }, - responseType: 'blob', - }); - }, - listFiles(skillName: string, path = '') { - return typed<any>( - openApiV1.listSkillFilesByName({ - query: { skill_name: skillName, ...(path ? { path } : {}) }, - }), - ); - }, - getFile(skillName: string, path: string) { - return typed<any>( - openApiV1.getSkillFileByName({ - query: { skill_name: skillName, path }, - }), - ); - }, - updateFile(skillName: string, path: string, content: string) { - return typed<OpenConfig>( - openApiV1.updateSkillFileByName({ - body: { skill_name: skillName, path, content }, - }), - ); - }, - neoCandidates(params?: { skill_key?: string; status?: string }) { - return typed<any>(openApiV1.listNeoSkillCandidates({ query: params })); - }, - neoReleases(params?: { skill_key?: string; stage?: string }) { - return typed<any>(openApiV1.listNeoSkillReleases({ query: params })); - }, - neoPayload(payloadRef: string) { - return typed<any>( - openApiV1.getNeoSkillPayload({ query: { payload_ref: payloadRef } }), - ); - }, - evaluateNeoCandidate(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.evaluateNeoSkillCandidate({ body: body as any }), - ); - }, - promoteNeoCandidate(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.promoteNeoSkillCandidate({ body: body as any }), - ); - }, - rollbackNeoRelease(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.rollbackNeoSkillRelease({ body: body as any }), - ); - }, - syncNeoRelease(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.syncNeoSkillRelease({ body: body as any }), - ); - }, - deleteNeoCandidate(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.deleteNeoSkillCandidate({ body: body as any }), - ); - }, - deleteNeoRelease(body: OpenConfig) { - return typed<OpenConfig>( - openApiV1.deleteNeoSkillRelease({ body: body as any }), - ); - }, -}; - -export const personaApi = { - tree() { - return typed<any>(openApiV1.getPersonaTree()); - }, - folders(parentId?: string | null) { - return typed<any[]>( - openApiV1.listPersonaFolders({ - query: - parentId === undefined ? undefined : { parent_id: parentId ?? '' }, - }), - ); - }, - createFolder(folder: OpenConfig) { - return typed<OpenConfig>( - openApiV1.createPersonaFolder({ body: folder as any }), - ); - }, - updateFolder(folderId: string, folder: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updatePersonaFolder({ - path: { folder_id: folderId }, - body: folder as any, - }), - ); - }, - deleteFolder(folderId: string) { - return typed<OpenConfig>( - openApiV1.deletePersonaFolder({ path: { folder_id: folderId } }), - ); - }, - list(folderId?: string | null) { - return typed<any[]>( - openApiV1.listPersonas({ - query: - folderId === undefined ? undefined : { folder_id: folderId ?? '' }, - }), - ); - }, - get(personaId: string) { - return typed<OpenConfig>( - openApiV1.getPersonaById({ query: { persona_id: personaId } }), - ); - }, - create(persona: OpenConfig) { - return typed<OpenConfig>( - openApiV1.createPersona({ body: persona as any }), - ); - }, - update(personaId: string, persona: OpenConfig) { - return typed<OpenConfig>( - openApiV1.updatePersonaById({ - body: { persona_id: personaId, ...persona } as any, - }), - ); - }, - delete(personaId: string) { - return typed<OpenConfig>( - openApiV1.deletePersonaById({ query: { persona_id: personaId } }), - ); - }, - move(personaId: string, folderId: string | null) { - return typed<OpenConfig>( - openApiV1.movePersonaItem({ - body: { persona_id: personaId, folder_id: folderId } as any, - }), - ); - }, - reorder(items: any[]) { - return typed<OpenConfig>( - openApiV1.reorderPersonaItems({ body: { items } as any }), - ); - }, -}; - -export const conversationApi = { - list(params?: ListConversationsQuery, requestConfig?: AxiosRequestConfig) { - return typed<any>( - openApiV1.listConversations( - generatedOptions( - { query: generatedQuery(params) }, - requestConfig, - ), - ), - ); - }, - get(userId: string, cid: string) { - return typed<any>( - openApiV1.getConversation({ - path: { conversation_id: cid }, - query: { user_id: userId }, - }), - ); - }, - update(userId: string, cid: string, payload: ConversationPatchRequest) { - return typed<any>( - openApiV1.updateConversation({ - path: { conversation_id: cid }, - query: { user_id: userId }, - body: payload, - }), - ); - }, - replaceMessages( - userId: string, - cid: string, - payload: ConversationMessagesReplaceRequest, - ) { - return typed<any>( - openApiV1.replaceConversationMessages({ - path: { conversation_id: cid }, - query: { user_id: userId }, - body: payload, - }), - ); - }, - delete(userId: string, cid: string) { - return typed<any>( - openApiV1.deleteConversation({ - path: { conversation_id: cid }, - query: { user_id: userId }, - }), - ); - }, - batchDelete(payload: ConversationBatchDeleteRequest) { - return typed<any>(openApiV1.batchDeleteConversations({ body: payload })); - }, - export(payload: ConversationExportRequest) { - return openApiV1.exportConversations({ - body: payload, - responseType: 'blob', - }) as Promise<AxiosResponse<Blob>>; - }, -}; - -export const statsApi = { - get(offsetSec?: number) { - return typed<any>( - openApiV1.getStats({ - query: offsetSec === undefined ? undefined : { offset_sec: offsetSec }, - }), - ); - }, - providerTokens(days?: number) { - return typed<any>( - openApiV1.getProviderTokenStats({ - query: days === undefined ? undefined : { days }, - }), - ); - }, - version() { - return withLegacyFallback<VersionData>(openApiV1.getVersion(), () => - httpClient.get<ApiEnvelope<VersionData>>('/api/stat/version'), - ); - }, - firstNotice(locale?: string) { - return typed<{ content?: string | null }>( - openApiV1.getFirstNotice({ - query: locale ? { locale } : undefined, - }), - ); - }, - testGhproxy(payload: GhproxyTestRequest) { - return withLegacyFallback<{ latency?: number }>( - openApiV1.testGhproxyConnection({ body: payload }), - () => - httpClient.post<ApiEnvelope<{ latency?: number }>>( - '/api/stat/test-ghproxy-connection', - payload, - ), - ); - }, - startTime() { - const v1Request = typed<StartTimeData>(openApiV1.getStartTime()); - const legacyRequest = httpClient.get<ApiEnvelope<StartTimeData>>( - '/api/stat/start-time', - ); - - // Restart polling must also work after downgrading to backends without v1 stats routes. - return firstSuccessfulResponse<StartTimeData>([v1Request, legacyRequest]); - }, - restart() { - return withLegacyFallback<OpenConfig>(openApiV1.restartCore(), () => - httpClient.post<ApiEnvelope<OpenConfig>>('/api/stat/restart-core'), - ); - }, - storage() { - return typed<OpenConfig>(openApiV1.getStorageStatus()); - }, - cleanupStorage(target?: string) { - return typed<OpenConfig>( - openApiV1.cleanupStorage({ - body: target ? { target } : undefined, - }), - ); - }, -}; - -export const publicApi = { - versions() { - return withLegacyFallback<PublicVersionData>( - openApiV1.getPublicVersions(), - () => httpClient.get<ApiEnvelope<PublicVersionData>>('/api/stat/versions'), - ); - }, -}; - -export const changelogApi = { - listVersions() { - return typed<{ versions?: string[] }>(openApiV1.listChangelogVersions()); - }, - get(version: string) { - return typed<{ content?: string }>( - openApiV1.getChangelog({ path: { version } }), - ); - }, -}; diff --git a/legacy-dashboard/src/assets/images/astrbot_banner.png b/legacy-dashboard/src/assets/images/astrbot_banner.png deleted file mode 100644 index f837d9fb..00000000 Binary files a/legacy-dashboard/src/assets/images/astrbot_banner.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/astrbot_logo_mini.webp b/legacy-dashboard/src/assets/images/astrbot_logo_mini.webp deleted file mode 100644 index f6a868c5..00000000 Binary files a/legacy-dashboard/src/assets/images/astrbot_logo_mini.webp and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/icon-no-shadow.svg b/legacy-dashboard/src/assets/images/icon-no-shadow.svg deleted file mode 100644 index 4268e03e..00000000 --- a/legacy-dashboard/src/assets/images/icon-no-shadow.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600"><foreignObject width="100%" height="100%" x="0" y="0" externalResourcesRequired="true"><div xmlns="http://www.w3.org/1999/xhtml" class="bg-transparent w-screen max-w-full aspect-square md:w-[350px] md:h-[350px] lg:w-[400px] lg:h-[400px] xl:w-[600px] xl:h-[600px]" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box transparent; background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: 600px; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; d: none; direction: ltr; display: block; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: 600px; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: 600px; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: 100%; max-width: 100%; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 45px; padding: 45px; padding-inline: 45px; paint-order: normal; perspective: none; perspective-origin: 300px 300px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: none; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 300px 300px; transform-style: flat; transition: all; translate: none; unicode-bidi: isolate; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: 600px; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"><div style="background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-radius: 80px; accent-color: auto; place-content: normal center; place-items: center normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: 510px; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 80px; border-end-start-radius: 80px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 80px; border-start-start-radius: 80px; inset: auto; box-decoration-break: slice; box-shadow: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; d: none; direction: ltr; display: flex; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: 510px; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: 510px; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: hidden; overflow-clip-margin: 0px; overflow-inline: hidden; overflow-wrap: normal; overflow: hidden; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 255px 255px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: none; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 255px 255px; transform-style: flat; transition: all; translate: none; unicode-bidi: isolate; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: 510px; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;" class="w-full aspect-square overflow-hidden flex justify-center items-center shadow-none"><span style="transform: matrix(1, 0, 0, 1, 0, 0); accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: 400px; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; d: none; direction: ltr; display: block; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(0, 0, 0); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: 400px; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: 400px; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: auto; min-height: auto; min-inline-size: auto; min-width: auto; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 200px 200px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: none; stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 1px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform-origin: 200px 200px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: 400px; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"><svg xmlns="http://www.w3.org/2000/svg" width="400" height="400" viewBox="0 0 24 24" fill="rgba(132, 183, 213, 1)" stroke="rgba(132, 183, 213, 1)" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-sparkles" fill-opacity="1" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: 400px; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; d: none; direction: ltr; display: block; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(132, 183, 213); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: 400px; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: 400px; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: hidden; overflow-clip-margin: content-box; overflow-inline: hidden; overflow-wrap: normal; overflow: hidden; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 200px 200px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: rgb(132, 183, 213); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 2.3px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 200px 200px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: middle; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: 400px; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"><path d="m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: auto; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: inline; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(132, 183, 213); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: auto; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: auto; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 0px 0px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: rgb(132, 183, 213); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 2.3px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 0px 0px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: auto; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"/><path d="M5 3v4" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: auto; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: inline; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(132, 183, 213); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: auto; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: auto; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 0px 0px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: rgb(132, 183, 213); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 2.3px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 0px 0px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: auto; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"/><path d="M19 17v4" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: auto; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: inline; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(132, 183, 213); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: auto; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: auto; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 0px 0px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: rgb(132, 183, 213); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 2.3px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 0px 0px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: auto; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"/><path d="M3 5h4" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: auto; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: inline; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(132, 183, 213); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: auto; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: auto; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 0px 0px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: rgb(132, 183, 213); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 2.3px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 0px 0px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: auto; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"/><path d="M17 19h4" style="accent-color: auto; place-content: normal; place-items: normal; place-self: auto; alignment-baseline: auto; anchor-name: none; anchor-scope: none; animation-composition: replace; animation: 0s ease 0s 1 normal none running none; app-region: none; appearance: none; backdrop-filter: none; backface-visibility: visible; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0); background-blend-mode: normal; baseline-shift: 0px; baseline-source: auto; block-size: auto; border-block-end: 0px solid rgb(229, 231, 235); border-block-start: 0px solid rgb(229, 231, 235); border-color: rgb(229, 231, 235); border-radius: 0px; border-style: solid; border-width: 0px; border-collapse: separate; border-end-end-radius: 0px; border-end-start-radius: 0px; border-image: none 100% / 1 / 0 stretch; border-inline-end: 0px solid rgb(229, 231, 235); border-inline-start: 0px solid rgb(229, 231, 235); border-start-end-radius: 0px; border-start-start-radius: 0px; inset: auto; box-decoration-break: slice; box-shadow: none; box-sizing: border-box; break-after: auto; break-before: auto; break-inside: auto; buffered-rendering: auto; caption-side: top; caret-color: rgba(48, 48, 48, 0.8); clear: none; clip: auto; clip-path: none; clip-rule: nonzero; color: rgba(48, 48, 48, 0.8); color-interpolation: srgb; color-interpolation-filters: linearrgb; color-rendering: auto; columns: auto; gap: normal; column-rule: 0px rgba(48, 48, 48, 0.8); column-span: none; contain-intrinsic-block-size: none; contain-intrinsic-size: none; contain-intrinsic-inline-size: none; container: none; content: normal; cursor: auto; cx: 0px; cy: 0px; direction: ltr; display: inline; dominant-baseline: auto; dynamic-range-limit: no-limit; empty-cells: show; field-sizing: fixed; fill: rgb(132, 183, 213); fill-opacity: 1; fill-rule: nonzero; filter: none; flex: 0 1 auto; flex-flow: row; float: none; flood-color: rgb(0, 0, 0); flood-opacity: 1; font-family: __Space_Grotesk_bc0dcf, __Space_Grotesk_Fallback_bc0dcf; font-kerning: auto; font-optical-sizing: auto; font-palette: normal; font-size: 15.9px; font-size-adjust: none; font-stretch: 100%; font-style: normal; font-synthesis: weight style small-caps; font-variant: normal; font-weight: 400; grid: none; grid-area: auto; height: auto; hyphenate-character: auto; hyphenate-limit-chars: auto; hyphens: manual; image-orientation: from-image; image-rendering: auto; initial-letter: normal; inline-size: auto; inset-block: auto; inset-inline: auto; interactivity: auto; interpolate-size: numeric-only; isolation: auto; letter-spacing: normal; lighting-color: rgb(255, 255, 255); line-break: auto; line-height: 24px; list-style: outside none disc; margin-block: 0px; margin: 0px; margin-inline: 0px; marker: none; mask: none; mask-type: luminance; math-depth: 0; math-shift: normal; math-style: normal; max-block-size: none; max-height: none; max-inline-size: none; max-width: none; min-block-size: 0px; min-height: 0px; min-inline-size: 0px; min-width: 0px; mix-blend-mode: normal; object-fit: fill; object-position: 50% 50%; object-view-box: none; offset: normal; opacity: 1; order: 0; orphans: 2; outline: rgba(48, 48, 48, 0.8) none 0px; outline-offset: 0px; overflow-anchor: auto; overflow-block: visible; overflow-clip-margin: 0px; overflow-inline: visible; overflow-wrap: normal; overflow: visible; overlay: none; overscroll-behavior-block: auto; overscroll-behavior-inline: auto; padding-block: 0px; padding: 0px; padding-inline: 0px; paint-order: normal; perspective: none; perspective-origin: 0px 0px; pointer-events: auto; position: static; position-anchor: auto; position-area: none; position-try: none; position-visibility: always; print-color-adjust: economy; r: 0px; reading-flow: normal; reading-order: 0; resize: none; rotate: none; ruby-align: space-around; ruby-position: over; rx: auto; ry: auto; scale: none; scroll-behavior: auto; scroll-initial-target: none; scroll-margin-block: 0px; scroll-margin-inline: 0px; scroll-marker-group: none; scroll-padding-block: auto; scroll-padding-inline: auto; scroll-timeline: none; scrollbar-color: auto; scrollbar-gutter: auto; scrollbar-width: auto; shape-image-threshold: 0; shape-margin: 0px; shape-outside: none; shape-rendering: auto; speak: normal; stop-color: rgb(0, 0, 0); stop-opacity: 1; stroke: rgb(132, 183, 213); stroke-dasharray: none; stroke-dashoffset: 0px; stroke-linecap: round; stroke-linejoin: round; stroke-miterlimit: 4; stroke-opacity: 1; stroke-width: 2.3px; tab-size: 4; table-layout: auto; text-align: start; text-align-last: auto; text-anchor: start; text-box: normal; text-decoration: none solid rgba(48, 48, 48, 0.8); text-decoration-skip-ink: auto; text-emphasis: none rgba(48, 48, 48, 0.8); text-emphasis-position: over; text-indent: 0px; text-overflow: clip; text-rendering: auto; text-shadow: none; text-size-adjust: 100%; text-spacing-trim: normal; text-transform: none; text-underline-position: auto; text-wrap: wrap; timeline-scope: none; touch-action: auto; transform: none; transform-origin: 0px 0px; transform-style: flat; transition: all; translate: none; unicode-bidi: normal; user-select: auto; vector-effect: none; vertical-align: baseline; view-timeline: none; view-transition-class: none; view-transition-name: none; visibility: visible; white-space-collapse: collapse; widows: 2; width: auto; will-change: auto; word-break: normal; word-spacing: 0px; writing-mode: horizontal-tb; x: 0px; y: 0px; z-index: auto; zoom: 1; border-spacing: 0px; -webkit-border-image: none; -webkit-box-align: stretch; -webkit-box-decoration-break: slice; -webkit-box-direction: normal; -webkit-box-flex: 0; -webkit-box-ordinal-group: 1; -webkit-box-orient: horizontal; -webkit-box-pack: start; -webkit-font-smoothing: auto; -webkit-line-break: auto; -webkit-line-clamp: none; -webkit-locale: "en"; -webkit-mask-box-image-source: none; -webkit-mask-box-image-slice: 0 fill; -webkit-mask-box-image-width: auto; -webkit-mask-box-image-outset: 0; -webkit-mask-box-image-repeat: stretch; -webkit-rtl-ordering: logical; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -webkit-text-combine: none; -webkit-text-decorations-in-effect: none; -webkit-text-fill-color: rgba(48, 48, 48, 0.8); -webkit-text-orientation: vertical-right; -webkit-text-security: none; -webkit-text-stroke: 0px rgba(48, 48, 48, 0.8); -webkit-user-drag: auto; -webkit-user-modify: read-only; -webkit-writing-mode: horizontal-tb;"/></svg></span></div></div></foreignObject></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/loading-seio.webp b/legacy-dashboard/src/assets/images/loading-seio.webp deleted file mode 100644 index 62e159f9..00000000 Binary files a/legacy-dashboard/src/assets/images/loading-seio.webp and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/dingtalk.svg b/legacy-dashboard/src/assets/images/platform_logos/dingtalk.svg deleted file mode 100644 index 323aaf14..00000000 --- a/legacy-dashboard/src/assets/images/platform_logos/dingtalk.svg +++ /dev/null @@ -1 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" width="2500" height="2500"><path fill="#4285F4" d="M1024 155.733C1024 70.4 953.6 0 868.267 0H155.733C70.4 0 0 70.4 0 155.733v712.534C0 953.6 70.4 1024 155.733 1024h712.534C953.6 1024 1024 953.6 1024 868.267V155.733z"/><path fill="#FFF" d="M810.667 422.4c-2.134 6.4-4.267 14.933-8.534 23.467C774.4 505.6 701.867 620.8 701.867 620.8l-21.334 36.267h102.4L588.8 915.2l44.8-174.933h-78.933l27.733-115.2c-23.467 6.4-49.067 12.8-81.067 23.466 0 0-42.666 25.6-121.6-46.933 0 0-53.333-46.933-23.466-59.733 12.8-4.267 64-10.667 104.533-17.067 55.467-6.4 87.467-10.667 87.467-10.667s-168.534 2.134-206.934-4.266c-40.533-6.4-89.6-72.534-100.266-132.267 0 0-17.067-32 36.266-17.067s268.8 59.734 268.8 59.734-281.6-87.467-300.8-108.8c-19.2-21.334-55.466-115.2-51.2-172.8 0 0 2.134-14.934 17.067-10.667 0 0 209.067 96 352 147.2 140.8 53.333 264.533 81.067 247.467 147.2z"/></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/platform_logos/discord.svg b/legacy-dashboard/src/assets/images/platform_logos/discord.svg deleted file mode 100644 index fb73caa5..00000000 --- a/legacy-dashboard/src/assets/images/platform_logos/discord.svg +++ /dev/null @@ -1 +0,0 @@ -<svg height="1828" viewBox="-10.63 -.07077792 823.87 610.06955549" width="2500" xmlns="http://www.w3.org/2000/svg"><path d="m678.27 51.62c90.35 132.84 134.97 282.68 118.29 455.18-.07.73-.45 1.4-1.05 1.84-68.42 50.24-134.71 80.73-200.07 100.95a2.55 2.55 0 0 1 -2.81-.95c-15.1-21.01-28.82-43.16-40.84-66.42-.69-1.37-.06-3.02 1.36-3.56 21.79-8.21 42.51-18.05 62.44-29.7 1.57-.92 1.67-3.17.22-4.25-4.23-3.14-8.42-6.44-12.43-9.74-.75-.61-1.76-.73-2.61-.32-129.39 59.75-271.13 59.75-402.05 0-.85-.38-1.86-.25-2.59.35-4 3.3-8.2 6.57-12.39 9.71-1.45 1.08-1.33 3.33.25 4.25 19.93 11.43 40.65 21.49 62.41 29.74 1.41.54 2.08 2.15 1.38 3.52-11.76 23.29-25.48 45.44-40.86 66.45-.67.85-1.77 1.24-2.81.92-65.05-20.22-131.34-50.71-199.76-100.95-.57-.44-.98-1.14-1.04-1.87-13.94-149.21 14.47-300.29 118.18-455.18.25-.41.63-.73 1.07-.92 51.03-23.42 105.7-40.65 162.84-50.49 1.04-.16 2.08.32 2.62 1.24 7.06 12.5 15.13 28.53 20.59 41.63 60.23-9.2 121.4-9.2 182.89 0 5.46-12.82 13.25-29.13 20.28-41.63a2.47 2.47 0 0 1 2.62-1.24c57.17 9.87 111.84 27.1 162.83 50.49.45.19.82.51 1.04.95zm-339.04 283.7c.63-44.11-31.53-80.61-71.9-80.61-40.04 0-71.89 36.18-71.89 80.61 0 44.42 32.48 80.6 71.89 80.6 40.05 0 71.9-36.18 71.9-80.6zm265.82 0c.63-44.11-31.53-80.61-71.89-80.61-40.05 0-71.9 36.18-71.9 80.61 0 44.42 32.48 80.6 71.9 80.6 40.36 0 71.89-36.18 71.89-80.6z" fill="#5865f2"/></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/platform_logos/kook.png b/legacy-dashboard/src/assets/images/platform_logos/kook.png deleted file mode 100644 index c99421c5..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/kook.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/lark.png b/legacy-dashboard/src/assets/images/platform_logos/lark.png deleted file mode 100644 index d1372f13..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/lark.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/line.png b/legacy-dashboard/src/assets/images/platform_logos/line.png deleted file mode 100644 index df80d27f..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/line.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/matrix.svg b/legacy-dashboard/src/assets/images/platform_logos/matrix.svg deleted file mode 100644 index b8e64ecc..00000000 --- a/legacy-dashboard/src/assets/images/platform_logos/matrix.svg +++ /dev/null @@ -1 +0,0 @@ -<svg width="75" height="32" xmlns="http://www.w3.org/2000/svg"><g fill="#000" fill-rule="nonzero"><path d="M.936.732V31.25H3.13v.732H.095V0h3.034v.732zm8.45 9.675v1.544h.044a4.461 4.461.0 011.487-1.368c.58-.323 1.245-.485 1.993-.485.72.0 1.377.14 1.972.42.595.279 1.047.771 1.355 1.477.338-.5.796-.941 1.377-1.323.58-.383 1.266-.574 2.06-.574.602.0 1.16.074 1.674.22.514.148.954.383 1.322.707.366.323.653.746.859 1.268.205.522.308 1.15.308 1.887v7.633H20.71v-6.464c0-.383-.015-.743-.044-1.082a2.305 2.305.0 00-.242-.882 1.473 1.473.0 00-.584-.596c-.257-.146-.606-.22-1.047-.22-.44.0-.796.085-1.068.253-.272.17-.485.39-.639.662a2.654 2.654.0 00-.308.927 7.074 7.074.0 00-.078 1.048v6.354h-3.128v-6.398c0-.338-.007-.673-.021-1.004a2.825 2.825.0 00-.188-.916 1.411 1.411.0 00-.55-.673c-.258-.168-.636-.253-1.135-.253a2.33 2.33.0 00-.584.1 1.94 1.94.0 00-.705.374c-.228.184-.422.449-.584.794-.161.346-.242.798-.242 1.357v6.619H6.434V10.407h2.952zm16.456 1.677a3.751 3.751.0 011.233-1.17 5.37 5.37.0 011.685-.629 9.579 9.579.0 011.884-.187c.573.0 1.153.04 1.74.121.588.081 1.124.24 1.609.475.484.235.88.562 1.19.981.308.42.462.975.462 1.666v5.934c0 .516.03 1.008.088 1.478.058.471.161.824.308 1.06H32.87a4.435 4.435.0 01-.22-1.104c-.5.515-1.087.876-1.762 1.081a7.084 7.084.0 01-2.071.31c-.544.0-1.05-.067-1.52-.2a3.472 3.472.0 01-1.234-.617 2.87 2.87.0 01-.826-1.059c-.199-.426-.298-.934-.298-1.522.0-.647.114-1.18.342-1.6.227-.419.52-.753.881-1.004.36-.25.771-.437 1.234-.562.462-.125.929-.224 1.399-.298.47-.073.932-.132 1.387-.176.456-.044.86-.11 1.212-.199.353-.088.631-.217.837-.386.206-.169.301-.415.287-.74.0-.337-.055-.606-.166-.804a1.217 1.217.0 00-.44-.464 1.737 1.737.0 00-.639-.22 5.292 5.292.0 00-.782-.055c-.617.0-1.101.132-1.454.397-.352.264-.558.706-.617 1.323h-3.128c.044-.735.227-1.345.55-1.83zm6.179 4.423a5.095 5.095.0 01-.639.165 9.68 9.68.0 01-.716.11c-.25.03-.5.067-.749.11a5.616 5.616.0 00-.694.177 2.057 2.057.0 00-.594.298c-.17.125-.305.284-.408.474-.103.192-.154.434-.154.728.0.28.051.515.154.706.103.192.242.342.419.453.176.11.381.187.617.231.234.044.477.066.726.066.617.0 1.094-.102 1.432-.309.338-.205.587-.452.75-.739.16-.286.26-.576.297-.87.036-.295.055-.53.055-.707v-1.17a1.4 1.4.0 01-.496.277zm11.863-6.1v2.096h-2.291v5.647c0 .53.088.883.264 1.059.176.177.529.265 1.057.265.177.0.345-.007.507-.022.161-.015.316-.037.463-.066v2.426a7.49 7.49.0 01-.882.089 21.67 21.67.0 01-.947.022c-.484.0-.944-.034-1.377-.1a3.233 3.233.0 01-1.145-.386 2.04 2.04.0 01-.782-.816c-.191-.353-.287-.816-.287-1.39v-6.728H36.57v-2.096h1.894v-3.42h3.129v3.42h2.29zm4.471.0v2.118h.044a3.907 3.907.0 011.454-1.754 4.213 4.213.0 011.036-.497 3.734 3.734.0 011.145-.176c.206.0.433.037.683.11v2.912a5.862 5.862.0 00-.528-.077 5.566 5.566.0 00-.595-.033c-.573.0-1.058.096-1.454.287a2.52 2.52.0 00-.958.783 3.143 3.143.0 00-.518 1.158 6.32 6.32.0 00-.154 1.434v5.14h-3.128V10.407h2.973zM54.039 8.642V6.06h3.128v2.582H54.04zm3.128 1.765v11.405H54.04V10.407h3.128zm1.63.0h3.569l2.005 2.978 1.982-2.978h3.459l-3.745 5.339 4.208 6.067h-3.57l-2.378-3.596-2.38 3.596h-3.502l4.097-6.001zM74.094 31.25V.732H71.9V0h3.035v31.982H71.9v-.732z"></path></g></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/platform_logos/mattermost.svg b/legacy-dashboard/src/assets/images/platform_logos/mattermost.svg deleted file mode 100644 index 9b055325..00000000 --- a/legacy-dashboard/src/assets/images/platform_logos/mattermost.svg +++ /dev/null @@ -1,2 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools --> -<svg fill="#000000" width="800px" height="800px" viewBox="0 0 501 501" xmlns="http://www.w3.org/2000/svg" version="1"><path d="M236 .7C137.7 7.5 54 68.2 18.2 158.5c-32 81-19.6 172.8 33 242.5 39.8 53 97.2 87 164.3 97 16.5 2.7 48 3.2 63.5 1.2 48.7-6.3 92.2-24.6 129-54.2 13-10.5 33-31.2 42.2-43.7 26.4-35.5 42.8-75.8 49-120.3 1.6-12.3 1.6-48.7 0-61-4-28.3-12-54.8-24.2-79.5-12.8-26-26.5-45.3-46.8-65.8C417.8 64 400.2 49 398.4 49c-.6 0-.4 10.5.3 26l1.3 26 7 8.7c19 23.7 32.8 53.5 38.2 83 2.5 14 3 43 1 55.8-4.5 27.8-15.2 54-31 76.5-8.6 12.2-28 31.6-40.2 40.2-24 17-50 27.6-80 33-10 1.8-49 1.8-59 0-43-7.7-78.8-26-107.2-54.8-29.3-29.7-46.5-64-52.4-104.4-2-14-1.5-42 1-55C90 121.4 132 72 192 49.7c8-3 18.4-5.8 29.5-8.2 1.7-.4 34.4-38 35.3-40.6.3-1-10.2-1-20.8-.4z"/><path d="M322.2 24.6c-1.3.8-8.4 9.3-16 18.7-7.4 9.5-22.4 28-33.2 41.2-51 62.2-66 81.6-70.6 91-6 12-8.4 21-9 33-1.2 19.8 5 36 19 50C222 268 230 273 243 277.2c9 3 10.4 3.2 24 3.2 13.8 0 15 0 22.6-3 23.2-9 39-28.4 45-55.7 2-8.2 2-28.7.4-79.7l-2-72c-1-36.8-1.4-41.8-3-44-2-3-4.8-3.6-7.8-1.4z"/></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/platform_logos/misskey.png b/legacy-dashboard/src/assets/images/platform_logos/misskey.png deleted file mode 100644 index 396209ae..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/misskey.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/onebot.png b/legacy-dashboard/src/assets/images/platform_logos/onebot.png deleted file mode 100644 index 70cc8829..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/onebot.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/qq.png b/legacy-dashboard/src/assets/images/platform_logos/qq.png deleted file mode 100644 index 51227994..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/qq.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/satori.png b/legacy-dashboard/src/assets/images/platform_logos/satori.png deleted file mode 100644 index 8e94f609..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/satori.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/slack.svg b/legacy-dashboard/src/assets/images/platform_logos/slack.svg deleted file mode 100644 index 69a4eb6a..00000000 --- a/legacy-dashboard/src/assets/images/platform_logos/slack.svg +++ /dev/null @@ -1 +0,0 @@ -<svg enable-background="new 0 0 2447.6 2452.5" viewBox="0 0 2447.6 2452.5" xmlns="http://www.w3.org/2000/svg"><g clip-rule="evenodd" fill-rule="evenodd"><path d="m897.4 0c-135.3.1-244.8 109.9-244.7 245.2-.1 135.3 109.5 245.1 244.8 245.2h244.8v-245.1c.1-135.3-109.5-245.1-244.9-245.3.1 0 .1 0 0 0m0 654h-652.6c-135.3.1-244.9 109.9-244.8 245.2-.2 135.3 109.4 245.1 244.7 245.3h652.7c135.3-.1 244.9-109.9 244.8-245.2.1-135.4-109.5-245.2-244.8-245.3z" fill="#36c5f0"/><path d="m2447.6 899.2c.1-135.3-109.5-245.1-244.8-245.2-135.3.1-244.9 109.9-244.8 245.2v245.3h244.8c135.3-.1 244.9-109.9 244.8-245.3zm-652.7 0v-654c.1-135.2-109.4-245-244.7-245.2-135.3.1-244.9 109.9-244.8 245.2v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.3z" fill="#2eb67d"/><path d="m1550.1 2452.5c135.3-.1 244.9-109.9 244.8-245.2.1-135.3-109.5-245.1-244.8-245.2h-244.8v245.2c-.1 135.2 109.5 245 244.8 245.2zm0-654.1h652.7c135.3-.1 244.9-109.9 244.8-245.2.2-135.3-109.4-245.1-244.7-245.3h-652.7c-135.3.1-244.9 109.9-244.8 245.2-.1 135.4 109.4 245.2 244.7 245.3z" fill="#ecb22e"/><path d="m0 1553.2c-.1 135.3 109.5 245.1 244.8 245.2 135.3-.1 244.9-109.9 244.8-245.2v-245.2h-244.8c-135.3.1-244.9 109.9-244.8 245.2zm652.7 0v654c-.2 135.3 109.4 245.1 244.7 245.3 135.3-.1 244.9-109.9 244.8-245.2v-653.9c.2-135.3-109.4-245.1-244.7-245.3-135.4 0-244.9 109.8-244.8 245.1 0 0 0 .1 0 0" fill="#e01e5a"/></g></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/platform_logos/telegram.svg b/legacy-dashboard/src/assets/images/platform_logos/telegram.svg deleted file mode 100644 index 8e4e9849..00000000 --- a/legacy-dashboard/src/assets/images/platform_logos/telegram.svg +++ /dev/null @@ -1 +0,0 @@ -<svg id="svg2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 240 240" width="2500" height="2500"><style>.st0{fill:url(#path2995-1-0_1_)}.st1{fill:#c8daea}.st2{fill:#a9c9dd}.st3{fill:url(#path2991_1_)}</style><linearGradient id="path2995-1-0_1_" gradientUnits="userSpaceOnUse" x1="-683.305" y1="534.845" x2="-693.305" y2="511.512" gradientTransform="matrix(6 0 0 -6 4255 3247)"><stop offset="0" stop-color="#37aee2"/><stop offset="1" stop-color="#1e96c8"/></linearGradient><path id="path2995-1-0" class="st0" d="M240 120c0 66.3-53.7 120-120 120S0 186.3 0 120 53.7 0 120 0s120 53.7 120 120z"/><path id="path2993" class="st1" d="M98 175c-3.9 0-3.2-1.5-4.6-5.2L82 132.2 152.8 88l8.3 2.2-6.9 18.8L98 175z"/><path id="path2989" class="st2" d="M98 175c3 0 4.3-1.4 6-3 2.6-2.5 36-35 36-35l-20.5-5-19 12-2.5 30v1z"/><linearGradient id="path2991_1_" gradientUnits="userSpaceOnUse" x1="128.991" y1="118.245" x2="153.991" y2="78.245" gradientTransform="matrix(1 0 0 -1 0 242)"><stop offset="0" stop-color="#eff7fc"/><stop offset="1" stop-color="#fff"/></linearGradient><path id="path2991" class="st3" d="M100 144.4l48.4 35.7c5.5 3 9.5 1.5 10.9-5.1L179 82.2c2-8.1-3.1-11.7-8.4-9.3L55 117.5c-7.9 3.2-7.8 7.6-1.4 9.5l29.7 9.3L152 93c3.2-2 6.2-.9 3.8 1.3L100 144.4z"/></svg> \ No newline at end of file diff --git a/legacy-dashboard/src/assets/images/platform_logos/wechat.png b/legacy-dashboard/src/assets/images/platform_logos/wechat.png deleted file mode 100644 index 89f8aed2..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/wechat.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/platform_logos/wecom.png b/legacy-dashboard/src/assets/images/platform_logos/wecom.png deleted file mode 100644 index 95bab3e2..00000000 Binary files a/legacy-dashboard/src/assets/images/platform_logos/wecom.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/plugin_icon.png b/legacy-dashboard/src/assets/images/plugin_icon.png deleted file mode 100644 index 7e4c4f3a..00000000 Binary files a/legacy-dashboard/src/assets/images/plugin_icon.png and /dev/null differ diff --git a/legacy-dashboard/src/assets/images/xmas-hat.png b/legacy-dashboard/src/assets/images/xmas-hat.png deleted file mode 100644 index 60184336..00000000 Binary files a/legacy-dashboard/src/assets/images/xmas-hat.png and /dev/null differ diff --git a/legacy-dashboard/src/components/ConfirmDialog.vue b/legacy-dashboard/src/components/ConfirmDialog.vue deleted file mode 100644 index ab477b9e..00000000 --- a/legacy-dashboard/src/components/ConfirmDialog.vue +++ /dev/null @@ -1,47 +0,0 @@ -<template> - <v-dialog v-model="isOpen" max-width="400"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ title }}</v-card-title> - <v-card-text>{{ message }}</v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn color="gray" variant="text" @click="handleCancel">{{ t('core.common.dialog.cancelButton') }}</v-btn> - <v-btn color="red" variant="tonal" @click="handleConfirm">{{ t('core.common.dialog.confirmButton') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref } from "vue"; -import { useI18n } from '@/i18n/composables'; - -const { t } = useI18n(); - -const isOpen = ref(false); -const title = ref(""); -const message = ref(""); -let resolvePromise = null; // ✅ 确保 Promise 句柄可用 - -const open = (options) => { - title.value = options.title || t('core.common.dialog.confirmTitle'); - message.value = options.message || t('core.common.dialog.confirmMessage'); - isOpen.value = true; - - return new Promise((resolve) => { - resolvePromise = resolve; // ✅ 赋值 Promise 解析方法 - }); -}; - -const handleConfirm = () => { - isOpen.value = false; - if (resolvePromise) resolvePromise(true); // ✅ 解析 Promise -}; - -const handleCancel = () => { - isOpen.value = false; - if (resolvePromise) resolvePromise(false); // ✅ 解析 Promise -}; - -defineExpose({ open }); // ✅ 确保 `confirmPlugin.ts` 可以访问 `open` -</script> diff --git a/legacy-dashboard/src/components/chat/Chat.vue b/legacy-dashboard/src/components/chat/Chat.vue deleted file mode 100644 index 38e0e7c4..00000000 --- a/legacy-dashboard/src/components/chat/Chat.vue +++ /dev/null @@ -1,2310 +0,0 @@ -<template> - <div - v-if="props.active" - class="chat-ui" - :class="{ 'is-dark': isDark, 'sidebar-collapsed': isSidebarCollapsed }" - > - <v-navigation-drawer - v-model="chatSidebarDrawer" - class="chat-sidebar" - :class="{ collapsed: isSidebarCollapsed }" - :permanent="lgAndUp" - :temporary="!lgAndUp" - :rail="lgAndUp && customizer.chatSidebarCollapsed" - :width="280" - :rail-width="56" - location="left" - floating - > - <div class="sidebar-top"> - <div - class="chat-sidebar-brand" - :class="{ collapsed: isSidebarCollapsed }" - > - <div v-if="!isSidebarCollapsed" class="chat-sidebar-brand-title Outfit"> - <ChatUILogo class="chat-sidebar-brand-logo" /> - <span class="chat-sidebar-brand-copy"> - <span class="chat-sidebar-brand-name">AstrBot</span> - <span class="chat-sidebar-brand-mode">ChatUI</span> - </span> - </div> - <button - v-if="isSidebarCollapsed" - class="chat-sidebar-brand-toggle chat-sidebar-rail-btn" - type="button" - aria-label="Toggle sidebar" - @click.stop="toggleChatSidebar" - > - <span class="chat-sidebar-rail-icon-stack"> - <ChatUILogo class="chat-sidebar-brand-logo chat-sidebar-brand-logo--collapsed" /> - <PanelLeft - :size="20" - class="sidebar-panel-toggle-icon" - /> - </span> - </button> - <v-btn - v-else - class="chat-sidebar-brand-toggle" - icon - rounded="sm" - variant="text" - @click.stop="toggleChatSidebar" - > - <PanelLeft - :size="20" - class="sidebar-panel-toggle-icon" - /> - </v-btn> - </div> - - <button - v-if="isSidebarCollapsed" - class="new-chat-btn sidebar-provider-btn icon-only chat-sidebar-rail-btn" - :class="{ 'sidebar-workspace-btn--active': isProviderWorkspace }" - type="button" - :title="tm('actions.providerConfig')" - @click="openProviderWorkspace" - > - <Box :size="18" class="sidebar-action-icon" /> - </button> - <v-btn - v-else - class="new-chat-btn sidebar-provider-btn" - :class="{ 'sidebar-workspace-btn--active': isProviderWorkspace }" - variant="text" - @click="openProviderWorkspace" - > - <Box :size="18" class="sidebar-action-icon mr-2" /> - <span>{{ tm("actions.providerConfig") }}</span> - </v-btn> - - <button - v-if="isSidebarCollapsed" - class="new-chat-btn icon-only chat-sidebar-rail-btn" - type="button" - :title="tm('actions.newChat')" - @click="startNewChat" - > - <SquarePen :size="18" class="sidebar-action-icon" /> - </button> - <v-btn - v-else - class="new-chat-btn" - variant="text" - @click="startNewChat" - > - <SquarePen :size="18" class="sidebar-action-icon mr-2" /> - <span>{{ tm("actions.newChat") }}</span> - </v-btn> - - </div> - - <div v-if="!isSidebarCollapsed" class="sidebar-content"> - <ProjectList - :projects="projects" - :project-sessions="projectSessionsById" - :loading-project-ids="loadingProjectSessionIds" - :selected-project-id="selectedProjectId" - :active-session-id="currSessionId" - :is-session-running="isSessionRunning" - @create-project="openCreateProjectDialog" - @edit-project="openEditProjectDialog" - @delete-project="handleDeleteProject" - @toggle-project="handleProjectToggle" - @select-project="selectProject" - @select-session="selectProjectSession" - @edit-session-title="editProjectSessionTitle" - @delete-session="deleteProjectSession" - /> - - <section class="sidebar-section session-list"> - <div class="sidebar-section-header"> - <span>{{ tm("conversation.title") }}</span> - </div> - <div - v-for="session in sessions" - :key="session.session_id" - class="session-item" - :class="{ - active: !isProviderWorkspace && currSessionId === session.session_id, - }" - role="button" - tabindex="0" - @click="selectSession(session.session_id)" - @keydown.enter="selectSession(session.session_id)" - @keydown.space.prevent="selectSession(session.session_id)" - > - <span class="session-title">{{ sessionTitle(session) }}</span> - <div class="session-actions" @click.stop> - <v-btn - icon - size="x-small" - variant="text" - class="session-action-btn" - :title="tm('conversation.editDisplayName')" - @click="editSidebarSessionTitle(session)" - > - <Pencil :size="15" /> - </v-btn> - <v-btn - icon - size="x-small" - variant="text" - class="session-action-btn" - :title="tm('actions.deleteChat')" - @click="deleteSidebarSession(session)" - > - <Trash2 :size="15" /> - </v-btn> - </div> - <v-progress-circular - v-if="isSessionRunning(session.session_id)" - class="session-progress" - indeterminate - size="16" - width="2" - /> - </div> - </section> - </div> - - <div class="sidebar-footer"> - <StyledMenu - location="top start" - offset="10" - :close-on-content-click="false" - > - <template #activator="{ props: menuProps }"> - <v-btn - v-bind="menuProps" - class="settings-btn" - :class="{ 'icon-only': isSidebarCollapsed }" - variant="text" - :icon="isSidebarCollapsed" - > - <Settings - :size="20" - :class="['sidebar-action-icon', { 'mr-2': !isSidebarCollapsed }]" - /> - <span v-if="!isSidebarCollapsed">{{ - t("core.common.settings") - }}</span> - </v-btn> - </template> - - <div class="settings-menu-content"> - <v-menu - location="end" - offset="8" - open-on-hover - :close-on-content-click="true" - > - <template #activator="{ props: transportMenuProps }"> - <v-list-item - v-bind="transportMenuProps" - class="styled-menu-item settings-menu-item" - rounded="md" - > - <template #prepend> - <Cable :size="18" class="styled-menu-lucide-icon" /> - </template> - <v-list-item-title>{{ - tm("transport.title") - }}</v-list-item-title> - <template #append> - <span class="settings-menu-value">{{ - currentTransportLabel - }}</span> - <ChevronRight :size="18" class="styled-menu-lucide-icon" /> - </template> - </v-list-item> - </template> - - <v-card class="styled-menu-card" elevation="8" rounded="lg"> - <v-list density="compact" class="styled-menu-list pa-1"> - <v-list-item - v-for="item in transportOptions" - :key="item.value" - class="styled-menu-item" - :class="{ - 'styled-menu-item-active': transportMode === item.value, - }" - rounded="md" - @click="transportMode = item.value" - > - <v-list-item-title>{{ - tm(item.labelKey) - }}</v-list-item-title> - <template #append> - <Check - v-if="transportMode === item.value" - :size="18" - class="styled-menu-lucide-icon" - /> - </template> - </v-list-item> - </v-list> - </v-card> - </v-menu> - - <v-menu - location="end" - offset="8" - open-on-hover - :close-on-content-click="true" - > - <template #activator="{ props: languageMenuProps }"> - <v-list-item - v-bind="languageMenuProps" - class="styled-menu-item settings-menu-item" - rounded="md" - > - <template #prepend> - <Languages :size="18" class="styled-menu-lucide-icon" /> - </template> - <v-list-item-title>{{ - t("core.common.language") - }}</v-list-item-title> - <template #append> - <span class="settings-menu-value">{{ - currentLanguage?.label || locale - }}</span> - <ChevronRight :size="18" class="styled-menu-lucide-icon" /> - </template> - </v-list-item> - </template> - - <v-card class="styled-menu-card" elevation="8" rounded="lg"> - <v-list density="compact" class="styled-menu-list pa-1"> - <v-list-item - v-for="lang in languageOptions" - :key="lang.value" - class="styled-menu-item" - :class="{ - 'styled-menu-item-active': locale === lang.value, - }" - rounded="md" - @click="switchLanguage(lang.value as Locale)" - > - <template #prepend> - <span class="language-flag">{{ lang.flag }}</span> - </template> - <v-list-item-title>{{ lang.label }}</v-list-item-title> - <template #append> - <Check - v-if="locale === lang.value" - :size="18" - class="styled-menu-lucide-icon" - /> - </template> - </v-list-item> - </v-list> - </v-card> - </v-menu> - - <v-list-item - class="styled-menu-item settings-menu-item" - rounded="md" - @click="toggleTheme" - > - <template #prepend> - <Sun - v-if="isDark" - :size="18" - class="styled-menu-lucide-icon" - /> - <Moon v-else :size="18" class="styled-menu-lucide-icon" /> - </template> - <v-list-item-title>{{ - isDark ? tm("modes.lightMode") : tm("modes.darkMode") - }}</v-list-item-title> - </v-list-item> - </div> - </StyledMenu> - </div> - </v-navigation-drawer> - - <main - class="chat-main" - :class="{ 'empty-chat': isEmptyChat }" - > - <section v-if="isProviderWorkspace" class="provider-workspace-shell"> - <ProviderChatCompletionPanel - class="provider-workspace-page" - :show-border="false" - /> - </section> - - <ProjectView - v-else-if="selectedProject" - :project="selectedProject" - :sessions="projectSessions" - @select-session="selectProjectSession" - @edit-session-title="editProjectSessionTitle" - @delete-session="deleteProjectSession" - > - <section class="project-composer-shell"> - <ChatInput - ref="inputRef" - v-model:prompt="draft" - :staged-images-url="stagedImagesUrl" - :staged-audio-url="stagedAudioUrl" - :staged-files="stagedNonImageFiles" - :disabled="sending" - :enable-streaming="enableStreaming" - :is-recording="isRecording" - :is-running=" - Boolean(currSessionId && isSessionRunning(currSessionId)) - " - :token-usage="tokenUsageIndicator" - :session-id="currSessionId || null" - :current-session="currentSession" - :reply-to="chatInputReplyTarget" - :send-shortcut="sendShortcut" - :show-provider-selector="false" - @send="sendCurrentMessage" - @stop="stopCurrentSession" - @toggle-streaming="toggleStreaming" - @remove-image="removeImage" - @remove-audio="removeAudio" - @remove-file="removeFile" - @start-recording="startRecording" - @stop-recording="stopRecording" - @paste-image="handlePaste" - @file-select="handleFilesSelected" - @clear-reply="replyTarget = null" - /> - </section> - </ProjectView> - - <div - v-else - class="conversation-stack" - :class="{ 'is-empty': isEmptyChat }" - > - <section - ref="messagesContainer" - class="messages-panel" - @scroll="handleMessagesScroll" - > - <div v-if="loadingMessages" class="center-state"> - <v-progress-circular indeterminate size="32" width="3" /> - </div> - - <div v-else-if="!activeMessages.length" class="welcome-state"> - <div class="welcome-title">{{ tm("welcome.title") }}</div> - </div> - - <div - v-if="!loadingMessages && activeMessages.length" - class="messages-list-shell" - > - <ChatMessageList - v-model:edit-draft="messageEditDraft" - :messages="activeMessages" - :is-dark="isDark" - :is-streaming=" - Boolean(currSessionId && isSessionRunning(currSessionId)) - " - :enable-edit=" - !Boolean(currSessionId && isSessionRunning(currSessionId)) - " - enable-regenerate - enable-thread-selection - :manage-refs-sidebar="false" - :editing-message-id="editingMessage?.id || null" - :saving-edit="savingMessageEdit" - @open-edit="openMessageEdit" - @cancel-edit="cancelMessageEdit" - @save-edit="saveMessageEdit" - @regenerate="handleRegenerateMessage" - @regenerate-with-model="handleRegenerateMessage" - @select-bot-text="handleBotTextSelection" - @open-thread="openThreadPanel" - @open-reasoning="openReasoningPanel" - @open-refs="openRefsSidebar" - /> - </div> - </section> - - <section class="composer-shell"> - <ChatInput - ref="inputRef" - v-model:prompt="draft" - :staged-images-url="stagedImagesUrl" - :staged-audio-url="stagedAudioUrl" - :staged-files="stagedNonImageFiles" - :disabled="sending" - :enable-streaming="enableStreaming" - :is-recording="isRecording" - :is-running=" - Boolean(currSessionId && isSessionRunning(currSessionId)) - " - :token-usage="tokenUsageIndicator" - :session-id="currSessionId || null" - :current-session="currentSession" - :reply-to="chatInputReplyTarget" - :send-shortcut="sendShortcut" - :show-provider-selector="false" - @send="sendCurrentMessage" - @stop="stopCurrentSession" - @toggle-streaming="toggleStreaming" - @remove-image="removeImage" - @remove-audio="removeAudio" - @remove-file="removeFile" - @start-recording="startRecording" - @stop-recording="stopRecording" - @paste-image="handlePaste" - @file-select="handleFilesSelected" - @clear-reply="replyTarget = null" - /> - </section> - </div> - </main> - - <div - v-if="threadSelection.visible" - class="thread-selection-action" - :style="{ - left: `${threadSelection.left}px`, - top: `${threadSelection.top}px`, - }" - > - <button - class="thread-selection-button" - type="button" - @click="createThreadFromSelection" - > - {{ tm("thread.askInThread") }} - </button> - </div> - - <ProjectDialog - v-model="projectDialogOpen" - :project="editingProject" - :error-message="projectDialogError" - :saving="savingProject" - @save="saveProject" - /> - <v-dialog v-model="sessionTitleDialogOpen" max-width="420"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ tm("conversation.editDisplayName") }} - </v-card-title> - <v-card-text> - <v-text-field - v-model="sessionTitleDraft" - :label="tm('conversation.displayName')" - variant="outlined" - density="comfortable" - hide-details - autofocus - @keydown.enter="saveSessionTitleDialog" - /> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn variant="text" @click="sessionTitleDialogOpen = false"> - {{ t("core.common.cancel") }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="savingSessionTitle" - @click="saveSessionTitleDialog" - > - {{ t("core.common.save") }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - <ThreadPanel - v-model="threadPanelOpen" - :thread="activeThread" - :is-dark="isDark" - :deleting="deletingThread" - @delete="deleteThread" - /> - <ReasoningSidebar - v-model="reasoningPanelOpen" - :parts="activeReasoningParts" - :is-dark="isDark" - /> - <RefsSidebar v-model="refsSidebarOpen" :refs="selectedRefs" /> - </div> -</template> - -<script setup lang="ts"> -import { - computed, - nextTick, - onBeforeUnmount, - onMounted, - provide, - reactive, - ref, - watch, -} from "vue"; -import { useRoute, useRouter } from "vue-router"; -import { useDisplay } from "vuetify"; -import { isAxiosError } from "axios"; -import { - Box, - Cable, - Check, - ChevronRight, - Languages, - Moon, - PanelLeft, - Pencil, - Settings, - SquarePen, - Sun, - Trash2, -} from "@lucide/vue"; -import { chatApi, providerApi } from "@/api/v1"; -import StyledMenu from "@/components/shared/StyledMenu.vue"; -import ProjectDialog, { - type ProjectFormData, -} from "@/components/chat/ProjectDialog.vue"; -import ProjectList, { type Project } from "@/components/chat/ProjectList.vue"; -import ProjectView from "@/components/chat/ProjectView.vue"; -import ChatInput from "@/components/chat/ChatInput.vue"; -import ChatMessageList from "@/components/chat/ChatMessageList.vue"; -import ChatUILogo from "@/components/chat/ChatUILogo.vue"; -import type { RegenerateModelSelection } from "@/components/chat/RegenerateMenu.vue"; -import ReasoningSidebar from "@/components/chat/ReasoningSidebar.vue"; -import ThreadPanel from "@/components/chat/ThreadPanel.vue"; -import RefsSidebar from "@/components/chat/message_list_comps/RefsSidebar.vue"; -import { useSessions, type Session } from "@/composables/useSessions"; -import { - messageBlocks as buildMessageBlocks, - useMessages, - type ChatRecord, - type ChatThread, - type MessagePart, - type TransportMode, -} from "@/composables/useMessages"; -import { useMediaHandling } from "@/composables/useMediaHandling"; -import { useRecording } from "@/composables/useRecording"; -import { useProjects } from "@/composables/useProjects"; -import { useChatHeaderStore } from "@/stores/chatHeader"; -import { useCustomizerStore } from "@/stores/customizer"; -import ProviderChatCompletionPanel from "@/components/provider/ProviderChatCompletionPanel.vue"; -import { - useI18n, - useLanguageSwitcher, - useModuleI18n, -} from "@/i18n/composables"; -import type { Locale } from "@/i18n/types"; -import { askForConfirmation, useConfirmDialog } from "@/utils/confirmDialog"; -import { - contextLimit, - formatTokenCount, - type ProviderModelMetadata, - type ProviderMetadataSource, -} from "@/utils/providerMetadata"; -import { useToast } from "@/utils/toast"; - -const props = withDefaults(defineProps<{ chatboxMode?: boolean; active?: boolean }>(), { - chatboxMode: false, - active: true, -}); - -const route = useRoute(); -const router = useRouter(); -const { lgAndUp } = useDisplay(); -const chatHeader = useChatHeaderStore(); -const customizer = useCustomizerStore(); -const { t } = useI18n(); -const { tm } = useModuleI18n("features/chat"); -const confirmDialog = useConfirmDialog(); -const toast = useToast(); -const { languageOptions, currentLanguage, switchLanguage, locale } = - useLanguageSwitcher(); -const { - sessions, - currSessionId, - getSessions, - newSession, - newChat, - deleteSession, - updateSessionTitle, -} = useSessions(props.chatboxMode); -const { - projects, - selectedProjectId, - getProjects, - createProject, - updateProject, - deleteProject: deleteProjectById, - addSessionToProject, - getProjectSessions, -} = useProjects(); - -const { - stagedFiles, - stagedImagesUrl, - stagedAudioUrl, - stagedNonImageFiles, - processAndUploadImage, - processAndUploadFile, - handlePaste, - removeImage, - removeAudio, - removeFile, - clearStaged, - cleanupMediaCache, -} = useMediaHandling(); - -type WorkspaceView = "chat" | "providers"; - -interface TokenProviderConfig extends ProviderMetadataSource { - id: string; - enable?: boolean; -} - -const activeWorkspace = ref<WorkspaceView>("chat"); -const projectDialogOpen = ref(false); -const editingProject = ref<Project | null>(null); -const projectDialogError = ref(""); -const savingProject = ref(false); -const sessionTitleDialogOpen = ref(false); -const sessionTitleDraft = ref(""); -const editingSessionTitleId = ref(""); -const refreshProjectSessionsAfterTitleSave = ref(false); -const savingSessionTitle = ref(false); -const messageEditDraft = ref(""); -const editingMessage = ref<ChatRecord | null>(null); -const savingMessageEdit = ref(false); -const projectSessions = ref<Session[]>([]); -const projectSessionsById = ref<Record<string, Session[]>>({}); -const loadingProjectSessionIds = ref<string[]>([]); -const loadingSessions = ref(false); -const draft = ref(""); -const tokenProviderConfigs = ref<TokenProviderConfig[]>([]); -const tokenModelMetadata = ref<Record<string, ProviderModelMetadata>>({}); -const selectedTokenProviderId = ref(""); -const messagesContainer = ref<HTMLElement | null>(null); -const inputRef = ref<InstanceType<typeof ChatInput> | null>(null); -const shouldStickToBottom = ref(true); -const replyTarget = ref<ChatRecord | null>(null); -const threadPanelOpen = ref(false); -const activeThread = ref<ChatThread | null>(null); -const reasoningPanelOpen = ref(false); -const activeReasoningTarget = ref<{ - message: ChatRecord; - blockIndex: number; -} | null>(null); -const deletingThread = ref(false); -const refsSidebarOpen = ref(false); -const selectedRefs = ref<Record<string, unknown> | null>(null); -const threadSelection = reactive<{ - visible: boolean; - left: number; - top: number; - message: ChatRecord | null; - selectedText: string; -}>({ - visible: false, - left: 0, - top: 0, - message: null, - selectedText: "", -}); -const enableStreaming = ref(true); -const sendShortcut = ref<"enter" | "shift_enter">("enter"); -const { - isRecording, - startRecording: startRecorder, - stopRecording: stopRecorder, -} = useRecording(); -const chatSidebarDrawer = computed({ - get: () => lgAndUp.value || customizer.chatSidebarOpen, - set: (value: boolean) => { - if (!lgAndUp.value) { - customizer.SET_CHAT_SIDEBAR(value); - } - }, -}); -const isSidebarCollapsed = computed(() => - lgAndUp.value ? customizer.chatSidebarCollapsed : !customizer.chatSidebarOpen, -); -const isProviderWorkspace = computed( - () => activeWorkspace.value === "providers", -); - -function toggleChatSidebar() { - if (lgAndUp.value) { - customizer.SET_CHAT_SIDEBAR_COLLAPSED(!customizer.chatSidebarCollapsed); - return; - } - customizer.TOGGLE_CHAT_SIDEBAR(); -} - -const activeReasoningParts = computed<MessagePart[]>(() => { - if (!activeReasoningTarget.value) return []; - const blocks = buildMessageBlocks( - activeReasoningTarget.value.message.content || { type: "bot", message: [] }, - ); - const block = blocks[activeReasoningTarget.value.blockIndex]; - return block?.kind === "thinking" ? block.parts : []; -}); - -watch(reasoningPanelOpen, (open) => { - if (!open) { - activeReasoningTarget.value = null; - } -}); - -const { - loadingMessages, - sending, - loadedSessions, - sessionProjects, - activeMessages, - isSessionRunning, - isUserMessage, - messageParts, - loadSessionMessages, - createLocalExchange, - sendMessageStream, - editMessage, - continueEditedMessage, - regenerateMessage, - stopSession, -} = useMessages({ - currentSessionId: currSessionId, - onSessionsChanged: getSessions, - onStreamUpdate: (sessionId) => { - if (sessionId === currSessionId.value && shouldStickToBottom.value) { - scrollToBottom(); - } - }, -}); - -const transportMode = ref<TransportMode>( - (localStorage.getItem("chat.transportMode") as TransportMode) === "websocket" - ? "websocket" - : "sse", -); -const transportOptions: Array<{ value: TransportMode; labelKey: string }> = [ - { value: "sse", labelKey: "transport.sse" }, - { value: "websocket", labelKey: "transport.websocket" }, -]; -const currentTransportLabel = computed(() => - tm( - transportOptions.find((item) => item.value === transportMode.value) - ?.labelKey || "transport.sse", - ), -); - -watch(transportMode, (mode) => { - localStorage.setItem("chat.transportMode", mode); -}); - -const isDark = computed(() => customizer.uiTheme === "PurpleThemeDark"); -const canSend = computed( - () => - Boolean(draft.value.trim() || stagedFiles.value.length) && !sending.value, -); -const currentSession = computed( - () => - sessions.value.find( - (session) => session.session_id === currSessionId.value, - ) || - projectSessions.value.find( - (session) => session.session_id === currSessionId.value, - ) || - Object.values(projectSessionsById.value) - .flat() - .find((session) => session.session_id === currSessionId.value) || - null, -); -const sessionProject = computed(() => - currSessionId.value ? sessionProjects[currSessionId.value] : null, -); -const currentSessionTitle = computed(() => - currentSession.value ? sessionTitle(currentSession.value) : "", -); -const selectedProject = computed( - () => - projects.value.find( - (project) => project.project_id === selectedProjectId.value, - ) || null, -); -const isEmptyChat = computed( - () => - !isProviderWorkspace.value && - !selectedProject.value && - !loadingMessages.value && - !activeMessages.value.length, -); -const chatHeaderTitle = computed( - () => currentSessionTitle.value || selectedProject.value?.title || "", -); -const chatHeaderSubtitle = computed(() => - currentSessionTitle.value - ? sessionProject.value?.title || selectedProject.value?.title || "" - : "", -); -const chatInputReplyTarget = computed(() => - replyTarget.value?.id == null - ? null - : { - messageId: replyTarget.value.id, - selectedText: replyPreview(replyTarget.value.id, ""), - }, -); -const currentTokenProvider = computed(() => { - const selectedProvider = tokenProviderConfigs.value.find( - (provider) => provider.id === selectedTokenProviderId.value, - ); - return selectedProvider || tokenProviderConfigs.value[0] || null; -}); -const currentTokenMetadata = computed(() => { - const model = currentTokenProvider.value?.model; - return model ? tokenModelMetadata.value[model] || null : null; -}); -const latestTokenUsageTotal = computed(() => { - for (let index = activeMessages.value.length - 1; index >= 0; index -= 1) { - const message = activeMessages.value[index]; - if (isUserMessage(message)) continue; - const usage = message.content?.agentStats?.token_usage; - if (!usage) continue; - return ( - readTokenCount(usage.input_other) + - readTokenCount(usage.input_cached) + - readTokenCount(usage.output) - ); - } - return 0; -}); -const tokenUsageIndicator = computed(() => { - const used = latestTokenUsageTotal.value; - const limit = contextLimit(currentTokenProvider.value, currentTokenMetadata.value); - if (used <= 0 || limit <= 0) return null; - - const percent = (used / limit) * 100; - return { - used, - limit, - percent: Math.min(100, Math.max(0, percent)), - tooltip: tm("tokenUsage.tooltip", { - used: formatTokenCount(used), - limit: formatTokenCount(limit), - percent: formatUsagePercent(percent), - }), - }; -}); - -function getSelectedProviderSelection() { - const inputSelection = inputRef.value?.getCurrentSelection(); - if (inputSelection?.providerId) { - selectedTokenProviderId.value = inputSelection.providerId; - return inputSelection; - } - if (typeof window === "undefined") { - return { providerId: "", modelName: "" }; - } - syncSelectedTokenProvider(); - return { - providerId: localStorage.getItem("selectedProvider") || "", - modelName: localStorage.getItem("selectedProviderModel") || "", - }; -} - -provide("isDark", isDark); - -watch( - [chatHeaderTitle, chatHeaderSubtitle], - ([title, subtitle]) => { - chatHeader.SET_CONTEXT({ title, subtitle }); - }, - { immediate: true }, -); - -onMounted(async () => { - loadingSessions.value = true; - try { - await Promise.all([getSessions(), getProjects(), loadTokenProviders()]); - const routeSessionId = getRouteSessionId(); - if (routeSessionId === "models") { - activeWorkspace.value = "providers"; - } else if (routeSessionId) { - await selectSession(routeSessionId, false); - } - } finally { - loadingSessions.value = false; - } -}); - -onBeforeUnmount(() => { - chatHeader.CLEAR_CONTEXT(); - cleanupMediaCache(); -}); - -watch( - () => route.params.conversationId, - async () => { - const routeSessionId = getRouteSessionId(); - if (routeSessionId === "models") { - activeWorkspace.value = "providers"; - return; - } - if (routeSessionId && routeSessionId !== currSessionId.value) { - showChatWorkspace(); - selectedProjectId.value = null; - await selectSession(routeSessionId, false); - } else if (!routeSessionId && currSessionId.value) { - showChatWorkspace(); - currSessionId.value = ""; - } - }, -); - -watch(activeMessages, () => { - if (shouldStickToBottom.value) { - scrollToBottom(); - } -}); - -function getRouteSessionId() { - const raw = route.params.conversationId; - return Array.isArray(raw) ? raw[0] : raw || ""; -} - -function basePath() { - return props.chatboxMode ? "/chatbox" : "/chat"; -} - -function closeMobileSidebar() { - if (!lgAndUp.value) { - customizer.SET_CHAT_SIDEBAR(false); - } -} - -function closeSecondaryPanels() { - threadSelection.visible = false; - threadPanelOpen.value = false; - activeThread.value = null; - reasoningPanelOpen.value = false; - activeReasoningTarget.value = null; - refsSidebarOpen.value = false; - selectedRefs.value = null; -} - -function showChatWorkspace() { - activeWorkspace.value = "chat"; -} - -async function openProviderWorkspace() { - closeSecondaryPanels(); - activeWorkspace.value = "providers"; - const targetPath = `${basePath()}/models`; - if (route.path !== targetPath) { - await router.push(targetPath); - } - closeMobileSidebar(); -} - -function sessionTitle(session: Session) { - return session.display_name?.trim() || tm("conversation.newConversation"); -} - -function syncSelectedTokenProvider() { - if (typeof window === "undefined") return; - selectedTokenProviderId.value = localStorage.getItem("selectedProvider") || ""; -} - -async function loadTokenProviders() { - syncSelectedTokenProvider(); - try { - const response = await providerApi.listByProviderType("chat_completion"); - if (response.data.status === "ok") { - tokenModelMetadata.value = ( - (response.data as any).model_metadata || {} - ) as Record<string, ProviderModelMetadata>; - tokenProviderConfigs.value = ( - (response.data.data || []) as unknown as TokenProviderConfig[] - ).filter((provider) => provider.enable !== false); - } - } catch (error) { - console.error("Failed to load provider context metadata:", error); - } -} - -function readTokenCount(value: unknown) { - const count = Number(value || 0); - return Number.isFinite(count) && count > 0 ? count : 0; -} - -function formatUsagePercent(value: number) { - if (!Number.isFinite(value) || value <= 0) return "0"; - if (value >= 10) return String(Math.round(value)); - if (value >= 1) return String(Math.round(value * 10) / 10); - return String(Math.round(value * 100) / 100); -} - -async function startNewChat() { - showChatWorkspace(); - selectedProjectId.value = null; - replyTarget.value = null; - newChat(); - closeMobileSidebar(); - await focusChatInput(); -} - -function openCreateProjectDialog() { - editingProject.value = null; - projectDialogError.value = ""; - projectDialogOpen.value = true; -} - -function openEditProjectDialog(project: Project) { - editingProject.value = project; - projectDialogError.value = ""; - projectDialogOpen.value = true; -} - -async function selectProject(projectId: string) { - showChatWorkspace(); - selectedProjectId.value = projectId; - currSessionId.value = ""; - replyTarget.value = null; - await router.push(basePath()); - await loadProjectSessions(projectId); - closeMobileSidebar(); -} - -async function loadProjectSessions(projectId = selectedProjectId.value) { - if (!projectId) { - projectSessions.value = []; - return []; - } - const sessions = await getProjectSessions(projectId); - projectSessionsById.value = { - ...projectSessionsById.value, - [projectId]: sessions, - }; - if (projectId === selectedProjectId.value) { - projectSessions.value = sessions; - } - return sessions; -} - -async function handleProjectToggle(projectId: string, expanded: boolean) { - if (!expanded || projectSessionsById.value[projectId]) return; - if (loadingProjectSessionIds.value.includes(projectId)) return; - loadingProjectSessionIds.value = [...loadingProjectSessionIds.value, projectId]; - try { - await loadProjectSessions(projectId); - } finally { - loadingProjectSessionIds.value = loadingProjectSessionIds.value.filter( - (item) => item !== projectId, - ); - } -} - -async function handleDeleteProject(projectId: string) { - await deleteProjectById(projectId); - const nextSessionsById = { ...projectSessionsById.value }; - delete nextSessionsById[projectId]; - projectSessionsById.value = nextSessionsById; - loadingProjectSessionIds.value = loadingProjectSessionIds.value.filter( - (item) => item !== projectId, - ); - if (selectedProjectId.value === projectId) { - selectedProjectId.value = null; - projectSessions.value = []; - } -} - -function openSessionTitleDialog( - sessionId: string, - title: string, - refreshProjectSessions = false, -) { - editingSessionTitleId.value = sessionId; - sessionTitleDraft.value = title; - refreshProjectSessionsAfterTitleSave.value = refreshProjectSessions; - sessionTitleDialogOpen.value = true; -} - -async function saveSessionTitleDialog() { - if (!editingSessionTitleId.value) return; - - savingSessionTitle.value = true; - try { - const sessionId = editingSessionTitleId.value; - const displayName = sessionTitleDraft.value.trim(); - await chatApi.updateSession(sessionId, { - display_name: displayName, - }); - updateSessionTitle(sessionId, displayName); - const projectSession = projectSessions.value.find( - (session) => session.session_id === sessionId, - ); - if (projectSession) { - projectSession.display_name = displayName; - } - Object.values(projectSessionsById.value).forEach((projectSessionList) => { - const cachedProjectSession = projectSessionList.find( - (session) => session.session_id === sessionId, - ); - if (cachedProjectSession) { - cachedProjectSession.display_name = displayName; - } - }); - if (refreshProjectSessionsAfterTitleSave.value) { - await loadProjectSessions(); - } - sessionTitleDialogOpen.value = false; - } finally { - savingSessionTitle.value = false; - } -} - -function editSidebarSessionTitle(session: Session) { - openSessionTitleDialog(session.session_id, session.display_name || ""); -} - -async function deleteSidebarSession(session: Session) { - const title = sessionTitle(session); - const message = tm("conversation.confirmDelete", { name: title }); - if (!(await askForConfirmation(message, confirmDialog))) return; - - const wasCurrent = currSessionId.value === session.session_id; - await deleteSession(session.session_id); - if (wasCurrent) { - selectedProjectId.value = null; - await router.push(basePath()); - } -} - -async function selectProjectSession(sessionId: string) { - selectedProjectId.value = null; - await selectSession(sessionId); -} - -async function editProjectSessionTitle(sessionId: string, title: string) { - openSessionTitleDialog(sessionId, title, true); -} - -async function deleteProjectSession( - sessionId: string, - projectId = selectedProjectId.value, -) { - await deleteSession(sessionId); - if (projectId) { - await loadProjectSessions(projectId); - } else { - await loadProjectSessions(); - } -} - -async function saveProject(formData: ProjectFormData, projectId?: string) { - savingProject.value = true; - projectDialogError.value = ""; - try { - if (projectId) { - await updateProject( - projectId, - formData.title, - formData.emoji, - formData.description, - formData.workspace_type, - formData.workspace_path, - ); - } else { - await createProject( - formData.title, - formData.emoji, - formData.description, - formData.workspace_type, - formData.workspace_path, - ); - } - projectDialogOpen.value = false; - editingProject.value = null; - } catch (error) { - projectDialogError.value = - error instanceof Error ? error.message : "Failed to save project"; - } finally { - savingProject.value = false; - } -} - -watch(projectDialogOpen, (open) => { - if (!open) { - projectDialogError.value = ""; - savingProject.value = false; - } -}); - -async function selectSession(sessionId: string, pushRoute = true) { - showChatWorkspace(); - selectedProjectId.value = null; - currSessionId.value = sessionId; - replyTarget.value = null; - if (pushRoute && route.path !== `${basePath()}/${sessionId}`) { - await router.push(`${basePath()}/${sessionId}`); - } - if (!loadedSessions[sessionId]) { - await loadSessionMessages(sessionId); - } - scrollToBottom(); - closeMobileSidebar(); - await focusChatInput(); -} - -async function sendCurrentMessage() { - if (!canSend.value) return; - - sending.value = true; - try { - let sessionId = currSessionId.value; - const targetProjectId = selectedProjectId.value; - const targetProject = selectedProject.value; - if (!sessionId) { - sessionId = await newSession(); - if (targetProjectId) { - await addSessionToProject(sessionId, targetProjectId); - sessionProjects[sessionId] = targetProject - ? { - project_id: targetProject.project_id, - title: targetProject.title, - emoji: targetProject.emoji, - } - : null; - await loadProjectSessions(targetProjectId); - selectedProjectId.value = null; - } - } - - const text = draft.value.trim(); - const messageId = crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`; - const outgoingParts = buildOutgoingParts(text); - const selection = getSelectedProviderSelection(); - const { userRecord, botRecord } = createLocalExchange({ - sessionId, - messageId, - parts: outgoingParts, - }); - updateTitleFromText(sessionId, text); - - draft.value = ""; - replyTarget.value = null; - clearStaged({ revokeUrls: false }); - scrollToBottom(); - - sendMessageStream({ - sessionId, - messageId, - parts: outgoingParts, - transport: transportMode.value, - enableStreaming: enableStreaming.value, - selectedProvider: selection?.providerId || "", - selectedModel: selection?.modelName || "", - userRecord, - botRecord, - }); - } catch (error) { - console.error("Failed to send message:", error); - } finally { - sending.value = false; - await focusChatInput(); - } -} - -function buildOutgoingParts(text: string): MessagePart[] { - const parts: MessagePart[] = []; - if (replyTarget.value?.id != null) { - parts.push({ - type: "reply", - message_id: replyTarget.value.id, - selected_text: "", - }); - } - if (text) { - parts.push({ type: "plain", text }); - } - stagedFiles.value.forEach((file) => { - parts.push({ - type: file.type, - attachment_id: file.attachment_id, - filename: file.filename, - embedded_url: file.url, - }); - }); - return parts; -} - -function updateTitleFromText(sessionId: string, text: string) { - const session = sessions.value.find((item) => item.session_id === sessionId); - const projectSession = projectSessions.value.find( - (item) => item.session_id === sessionId, - ); - const cachedProjectSessions = Object.values(projectSessionsById.value) - .flat() - .filter((item) => item.session_id === sessionId); - if ( - (!session && !projectSession && !cachedProjectSessions.length) || - session?.display_name || - projectSession?.display_name || - cachedProjectSessions.some((item) => item.display_name) || - !text - ) { - return; - } - updateSessionTitle(sessionId, text.slice(0, 40)); - if (projectSession) { - projectSession.display_name = text.slice(0, 40); - } - cachedProjectSessions.forEach((item) => { - item.display_name = text.slice(0, 40); - }); -} - -function replyPreview(messageId?: string | number, fallback?: string) { - if (fallback) return truncate(fallback, 80); - const found = activeMessages.value.find( - (message) => String(message.id) === String(messageId), - ); - const text = found ? plainTextFromMessage(found) : ""; - return text ? truncate(text, 80) : tm("reply.replyTo"); -} - -function plainTextFromMessage(message: ChatRecord) { - return messageParts(message) - .filter((part) => part.type === "plain" && part.text) - .map((part) => part.text) - .join("\n"); -} - -function truncate(value: string, max: number) { - return value.length > max ? `${value.slice(0, max)}...` : value; -} - -function scrollToMessage(messageId?: string | number) { - if (!messageId) return; - const index = activeMessages.value.findIndex( - (message) => String(message.id) === String(messageId), - ); - if (index < 0) return; - const rows = messagesContainer.value?.querySelectorAll(".message-row"); - rows?.[index]?.scrollIntoView({ behavior: "smooth", block: "center" }); -} - -function openMessageEdit(message: ChatRecord) { - messageEditDraft.value = plainTextFromMessage(message); - editingMessage.value = message; - nextTick(() => scrollToMessage(message.id)); -} - -function cancelMessageEdit() { - editingMessage.value = null; - messageEditDraft.value = ""; -} - -async function saveMessageEdit() { - if (!currSessionId.value || !editingMessage.value) return; - savingMessageEdit.value = true; - try { - const target = editingMessage.value; - const result = await editMessage( - currSessionId.value, - target, - messageEditDraft.value, - ); - cancelMessageEdit(); - - if (result.needsRegenerate && result.truncatedAfterMessage) { - const selection = getSelectedProviderSelection(); - continueEditedMessage({ - sessionId: currSessionId.value, - sourceRecord: target, - enableStreaming: enableStreaming.value, - selectedProvider: selection?.providerId || "", - selectedModel: selection?.modelName || "", - }); - scrollToBottom(); - } else if (result.needsRegenerate) { - const index = activeMessages.value.findIndex( - (message) => String(message.id) === String(target.id), - ); - const nextBot = activeMessages.value - .slice(index + 1) - .find((message) => !isUserMessage(message)); - if (nextBot) { - await handleRegenerateMessage(nextBot); - } - } - } catch (error) { - console.error("Failed to edit message:", error); - } finally { - savingMessageEdit.value = false; - } -} - -async function handleRegenerateMessage( - message: ChatRecord, - selection?: RegenerateModelSelection, -) { - if (!currSessionId.value || isUserMessage(message)) return; - message.threads = []; - await regenerateMessage( - currSessionId.value, - message, - selection?.providerId || "", - selection?.modelName || "", - ); -} - -function handleBotTextSelection(event: MouseEvent, message: ChatRecord) { - if (message.id == null || String(message.id).startsWith("local-")) return; - const container = event.currentTarget as HTMLElement | null; - window.setTimeout(() => { - const selection = window.getSelection(); - const selectedText = selection?.toString().trim() || ""; - if (!selection || !selectedText) { - threadSelection.visible = false; - return; - } - if ( - !container || - !container.contains(selection.anchorNode) || - !container.contains(selection.focusNode) - ) { - threadSelection.visible = false; - return; - } - const range = selection.getRangeAt(0); - const rect = range.getBoundingClientRect(); - threadSelection.message = message; - threadSelection.selectedText = selectedText; - threadSelection.left = Math.min( - window.innerWidth - 180, - Math.max(12, rect.left + rect.width / 2 - 70), - ); - threadSelection.top = Math.max(12, rect.top - 42); - threadSelection.visible = true; - }, 0); -} - -async function createThreadFromSelection() { - const message = threadSelection.message; - if (!currSessionId.value || !message?.id || !threadSelection.selectedText) return; - try { - const response = await chatApi.createThread({ - session_id: currSessionId.value, - parent_message_id: message.id, - selected_text: threadSelection.selectedText, - }); - if (response.data?.status !== "ok") { - toast.error(response.data?.message || tm("thread.createFailed")); - return; - } - const thread = response.data?.data as ChatThread | undefined; - if (!thread) { - toast.error(tm("thread.createFailed")); - return; - } - message.threads = message.threads || []; - if (!message.threads.some((item) => item.thread_id === thread.thread_id)) { - message.threads.push(thread); - } - openThreadPanel(thread); - window.getSelection()?.removeAllRanges(); - } catch (error) { - toast.error( - isAxiosError(error) - ? error.response?.data?.message || error.message - : tm("thread.createFailed"), - ); - console.error("Failed to create thread:", error); - } finally { - threadSelection.visible = false; - } -} - -function openThreadPanel(thread: ChatThread) { - reasoningPanelOpen.value = false; - activeReasoningTarget.value = null; - refsSidebarOpen.value = false; - activeThread.value = thread; - threadPanelOpen.value = true; -} - -function openRefsSidebar(refs: unknown) { - threadPanelOpen.value = false; - activeThread.value = null; - reasoningPanelOpen.value = false; - activeReasoningTarget.value = null; - selectedRefs.value = - refs && typeof refs === "object" ? (refs as Record<string, unknown>) : null; - refsSidebarOpen.value = true; -} - -function openReasoningPanel(payload: { - message: ChatRecord; - blockIndex: number; -}) { - threadPanelOpen.value = false; - activeThread.value = null; - refsSidebarOpen.value = false; - selectedRefs.value = null; - activeReasoningTarget.value = payload; - reasoningPanelOpen.value = true; -} - -async function deleteThread(thread: ChatThread) { - if (deletingThread.value) return; - if (!(await askForConfirmation(tm("thread.confirmDelete"), confirmDialog))) return; - deletingThread.value = true; - try { - await chatApi.deleteThread(thread.thread_id); - removeThreadFromMessages(thread.thread_id); - if (activeThread.value?.thread_id === thread.thread_id) { - threadPanelOpen.value = false; - activeThread.value = null; - } - } catch (error) { - console.error("Failed to delete thread:", error); - } finally { - deletingThread.value = false; - } -} - -function removeThreadFromMessages(threadId: string) { - for (const message of activeMessages.value) { - if (!message.threads?.length) continue; - message.threads = message.threads.filter( - (thread) => thread.thread_id !== threadId, - ); - } -} - -async function handleFilesSelected(files: FileList) { - const selectedFiles = Array.from(files || []); - for (const file of selectedFiles) { - if (file.type.startsWith("image/")) { - await processAndUploadImage(file); - } else { - await processAndUploadFile(file); - } - } -} - -function toggleStreaming() { - enableStreaming.value = !enableStreaming.value; -} - -async function startRecording() { - try { - await startRecorder(); - } catch (error) { - console.error("Failed to start recording:", error); - toast.error(tm("voice.error")); - } -} - -async function stopRecording() { - try { - const audioFile = await stopRecorder(); - const uploaded = await processAndUploadFile(audioFile); - if (!uploaded) { - toast.error(tm("voice.error")); - } - } catch (error) { - console.error("Failed to stop recording:", error); - toast.error(tm("voice.error")); - } -} - -function handleMessagesScroll() { - threadSelection.visible = false; - const container = messagesContainer.value; - if (!container) return; - const distance = - container.scrollHeight - container.scrollTop - container.clientHeight; - shouldStickToBottom.value = distance < 80; -} - -function scrollToBottom() { - nextTick(() => { - const container = messagesContainer.value; - if (!container) return; - container.scrollTop = container.scrollHeight; - shouldStickToBottom.value = true; - }); -} - -async function focusChatInput() { - await nextTick(); - window.requestAnimationFrame(() => { - inputRef.value?.focusInput(); - }); -} - -async function stopCurrentSession() { - if (!currSessionId.value) return; - try { - await stopSession(currSessionId.value); - } catch (error) { - console.error("Failed to stop session:", error); - } -} - -function toggleTheme() { - customizer.SET_UI_THEME(isDark.value ? "PurpleTheme" : "PurpleThemeDark"); -} -</script> - -<style scoped> -.chat-ui { - --chat-panel-top-offset: 50px; - --chat-sidebar-bg: rgb(var(--v-theme-surface)); - --chat-session-active-bg: #efefef; - --chat-page-bg: #fdfcfc; - --chat-border: #f2f2f2; - --chat-muted: rgba(var(--v-theme-on-surface), 0.62); - --chat-section-label: rgba(var(--v-theme-on-surface), 0.48); - --chat-content-width: 76%; - --chat-content-max-width: 760px; - display: flex; - height: 100%; - min-height: 0; - overflow: hidden; - background: var(--chat-page-bg); - color: rgb(var(--v-theme-on-surface)); - font-family: - system-ui, - -apple-system, - BlinkMacSystemFont, - "Segoe UI", - Roboto, - Oxygen, - Ubuntu, - Cantarell, - "Open Sans", - "Helvetica Neue", - sans-serif; -} - -.chat-ui.is-dark { - --chat-sidebar-bg: #2d2d2d; - --chat-session-active-bg: rgba(255, 255, 255, 0.08); - --chat-page-bg: rgb(var(--v-theme-background)); - --chat-border: rgba(255, 255, 255, 0.1); - --chat-section-label: rgba(255, 255, 255, 0.5); -} - -.chat-sidebar { - top: 0 !important; - height: 100vh !important; - background: var(--chat-sidebar-bg); - border-right: 1px solid var(--chat-border); -} - -.chat-sidebar.collapsed { - background: var(--chat-sidebar-bg); - border-right: 1px solid var(--chat-border); -} - -.chat-sidebar :deep(.v-navigation-drawer__content) { - display: flex; - flex-direction: column; - height: 100%; -} - -.sidebar-top { - padding: 0 16px 2px; -} - -.chat-sidebar.collapsed .sidebar-top { - width: 56px; - box-sizing: border-box; - display: flex; - flex-direction: column; - align-items: center; - padding: 0 0 2px; -} - -.chat-sidebar-brand { - min-height: 50px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; - padding: 0 10px 2px; -} - -.chat-sidebar-brand.collapsed { - width: 36px; - justify-content: center; - padding: 0 0 2px; -} - -.chat-sidebar-brand-title { - min-width: 0; - display: flex; - align-items: center; - gap: 8px; - color: rgb(var(--v-theme-on-surface)); - line-height: 1.05; -} - -.chat-sidebar-brand-logo { - width: 22px; - height: 22px; - flex: 0 0 22px; - display: block; -} - -.chat-sidebar-brand-title .chat-sidebar-brand-logo { - transform: translateX(-2px); -} - -.chat-sidebar-brand-copy { - min-width: 0; - display: inline-flex; - align-items: baseline; - gap: 5px; -} - -.chat-sidebar-brand-name { - font-size: 18px; - font-weight: 800; -} - -.chat-sidebar-brand-mode { - color: var(--chat-muted); - font-size: 18px; - font-weight: 500; -} - -.chat-sidebar-brand-toggle { - width: 36px; - height: 36px; - min-width: 36px; - color: var(--chat-muted); -} - -.chat-sidebar-rail-btn { - border: 0; - border-radius: 8px; - background: transparent; - color: rgb(var(--v-theme-on-surface)); - cursor: pointer; - display: grid; - place-items: center; - padding: 0; -} - -.chat-sidebar-brand-toggle:hover { - background: var(--chat-session-active-bg); - color: rgb(var(--v-theme-on-surface)); -} - -.chat-sidebar-rail-icon-stack { - width: 24px; - height: 24px; - display: grid; - place-items: center; -} - -.chat-sidebar-rail-icon-stack > * { - grid-area: 1 / 1; -} - -.chat-sidebar-brand-logo--collapsed { - width: 20px; - height: 20px; - transition: - opacity 0.14s ease, - visibility 0.14s ease; - opacity: 1; - visibility: visible; -} - -.chat-sidebar-rail-icon-stack .sidebar-panel-toggle-icon { - transition: - opacity 0.14s ease, - visibility 0.14s ease; - opacity: 0; - visibility: hidden; -} - -.chat-sidebar-brand-toggle:hover .chat-sidebar-brand-logo--collapsed, -.chat-sidebar-brand-toggle:focus-visible .chat-sidebar-brand-logo--collapsed { - opacity: 0; - visibility: hidden; -} - -.chat-sidebar-brand-toggle:hover .sidebar-panel-toggle-icon, -.chat-sidebar-brand-toggle:focus-visible .sidebar-panel-toggle-icon { - opacity: 1; - visibility: visible; -} - -.sidebar-panel-toggle-icon { - flex: 0 0 auto; -} - -.new-chat-btn, -.settings-btn { - color: rgb(var(--v-theme-on-surface)); - border-radius: 8px; -} - -.sidebar-action-icon { - color: currentcolor; - flex: 0 0 auto; - stroke-width: 2; -} - -.new-chat-btn:not(.icon-only) .sidebar-action-icon { - margin-right: 12px !important; -} - -.new-chat-btn, -.settings-btn { - width: 100%; - min-height: 36px; - height: 36px; - justify-content: flex-start; - border-radius: 8px; - text-transform: none; - letter-spacing: 0; - font-size: 14px; - font-weight: 500; -} - -.sidebar-provider-btn { - margin-bottom: 2px; -} - -.new-chat-btn:not(.icon-only), -.settings-btn:not(.icon-only) { - padding-inline: 10px; -} - -.new-chat-btn.icon-only, -.settings-btn.icon-only { - width: 36px !important; - height: 36px !important; - min-width: 36px !important; - margin-inline: auto; - padding: 0 !important; - justify-content: center; -} - -.chat-sidebar.collapsed .new-chat-btn.icon-only :deep(.v-btn__content), -.chat-sidebar.collapsed .settings-btn.icon-only :deep(.v-btn__content) { - display: flex; - align-items: center; - justify-content: center; -} - -.new-chat-btn :deep(.v-btn__content), -.settings-btn :deep(.v-btn__content) { - min-width: 0; - font-size: 14px; - line-height: 20px; -} - -.chat-sidebar.collapsed .sidebar-footer { - display: flex; - justify-content: center; -} - -.new-chat-btn:hover, -.settings-btn:hover { - background: var(--chat-session-active-bg); -} - -.sidebar-workspace-btn--active { - background: var(--chat-session-active-bg); - color: rgb(var(--v-theme-on-surface)); -} - -.sidebar-content { - flex: 1; - overflow-y: auto; - padding: 2px 16px 12px; - display: flex; - flex-direction: column; - gap: 8px; -} - -.sidebar-section { - flex: 0 0 auto; -} - -.sidebar-section-header { - min-height: 24px; - display: flex; - align-items: center; - padding: 0 10px 4px; - color: var(--chat-section-label); - font-size: 12px; - font-weight: 500; -} - -.session-list { - padding: 0; - display: flex; - flex-direction: column; - gap: 2px; -} - -.session-item { - width: 100%; - min-height: 30px; - border: 0; - border-radius: 8px; - background: transparent; - color: inherit; - display: flex; - align-items: center; - gap: 7px; - padding: 4px 56px 4px 10px; - position: relative; - box-sizing: border-box; - cursor: pointer; - text-align: left; -} - -.session-item:hover, -.session-item.active { - background: var(--chat-session-active-bg); -} - -.session-title { - min-width: 0; - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 14px; - font-weight: 500; -} - -.session-progress { - position: absolute; - right: 4px; - top: 50%; - transform: translateY(-50%); - flex-shrink: 0; - transition: - opacity 0.14s ease, - visibility 0.14s ease; -} - -.session-actions { - display: flex; - align-items: center; - gap: 2px; - flex-shrink: 0; - opacity: 0; - pointer-events: none; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - visibility: hidden; -} - -.session-item:hover .session-actions, -.session-item:focus-within .session-actions { - opacity: 1; - pointer-events: auto; - visibility: visible; -} - -.session-item:hover .session-progress, -.session-item:focus-within .session-progress { - opacity: 0; - visibility: hidden; -} - -.session-action-btn { - color: var(--chat-muted); -} - -.session-action-btn:hover { - color: rgb(var(--v-theme-on-surface)); -} - -.sidebar-footer { - margin-top: auto; - padding: 10px 16px 14px; -} - -.chat-sidebar.collapsed .sidebar-footer { - width: 56px; - box-sizing: border-box; - padding-inline: 10px; -} - -.settings-menu-content { - min-width: 270px; - padding: 6px; -} - -.settings-menu-item { - min-height: 42px; -} - -.settings-menu-content :deep(.settings-menu-item .v-list-item__prepend) { - width: 28px; - margin-inline-end: 12px; - align-self: center; -} - -.settings-menu-content :deep(.settings-menu-item .v-list-item__content) { - min-width: 0; -} - -.settings-menu-content :deep(.settings-menu-item .v-list-item-title) { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.settings-menu-content :deep(.settings-menu-item .v-list-item__append) { - margin-inline-start: auto; - padding-inline-start: 18px; - gap: 8px; - align-self: center; -} - -.styled-menu-lucide-icon { - flex: 0 0 auto; - color: currentcolor; - stroke-width: 2; -} - -.settings-menu-value { - color: var(--chat-muted); - font-size: 12px; - margin-right: 4px; - max-width: 92px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.language-flag { - display: inline-block; - width: 20px; - margin-right: 8px; -} - -.chat-main { - flex: 1; - min-width: 0; - height: 100%; - display: flex; - flex-direction: column; - position: relative; - box-sizing: border-box; - padding-top: 50px; -} - -.provider-workspace-shell { - flex: 1; - min-height: 0; - overflow: hidden; -} - -.provider-workspace-page { - height: 100%; - min-height: 0; -} - -.conversation-stack { - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; - position: relative; -} - -.conversation-stack.is-empty { - display: flex; - flex-direction: column; - justify-content: center; - gap: 28px; -} - -.messages-panel { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 24px 0 116px; - scroll-padding-bottom: 116px; -} - -.conversation-stack.is-empty .messages-panel { - flex: none; - min-height: auto; - overflow: visible; - padding: 0; - scroll-padding-bottom: 0; -} - -.messages-list-shell { - width: var(--chat-content-width); - max-width: var(--chat-content-max-width); - margin: 0 auto; -} - -.center-state, -.welcome-state { - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; -} - -.conversation-stack.is-empty .welcome-state { - height: auto; -} - -.welcome-title { - font-family: "Outfit", "Noto Sans", sans-serif; - font-size: 28px; - font-weight: 800; -} - -.welcome-subtitle { - margin-top: 8px; - color: var(--chat-muted); - font-size: 16px; -} - -.thread-selection-action { - position: fixed; - z-index: 1200; - pointer-events: auto; -} - -.thread-selection-button { - min-height: 34px; - padding: 0 14px; - border: 1px solid rgba(var(--v-theme-on-surface), 0.14); - border-radius: 999px; - background: rgb(var(--v-theme-surface)); - color: rgb(var(--v-theme-on-surface)); - box-shadow: 0 10px 28px rgba(0, 0, 0, 0.14); - font-size: 13px; - font-weight: 500; - cursor: pointer; -} - -.composer-shell { - position: relative; - z-index: 3; - isolation: isolate; - background: transparent; - padding: 0 0 18px; -} - -.conversation-stack:not(.is-empty) .composer-shell { - position: absolute; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; -} - -.conversation-stack:not(.is-empty) .composer-shell::before { - content: ""; - position: absolute; - z-index: 0; - top: 32px; - right: 0; - bottom: 0; - left: 0; - pointer-events: none; - background: var(--chat-page-bg); -} - -.composer-shell :deep(.input-area), -.project-composer-shell :deep(.input-area) { - padding-top: 0; - border-top: 0; -} - -.conversation-stack:not(.is-empty) .composer-shell :deep(.input-area) { - position: relative; - z-index: 1; - pointer-events: none; - background: transparent; -} - -.conversation-stack:not(.is-empty) .composer-shell :deep(.input-container) { - pointer-events: auto; -} - -.conversation-stack.is-empty .composer-shell { - padding-bottom: 0; -} - -kbd { - padding: 1px 5px; - border-radius: 4px; - background: rgba(var(--v-theme-on-surface), 0.08); - font: inherit; -} - -:deep(.hr-node) { - margin-top: 1.25rem; - margin-bottom: 1.25rem; - opacity: 0.5; - border-top-width: .3px; -} - -:deep(.paragraph-node) { - margin: .5rem 0; - line-height: 1.7; -} - -:deep(.list-node) { - margin-top: .5rem; - margin-bottom: .5rem; -} - -@media (max-width: 760px) { - .chat-sidebar { - top: 50px !important; - height: calc(100vh - 50px) !important; - } - - .messages-panel { - padding: 18px 0 92px; - scroll-padding-bottom: 92px; - } - - .conversation-stack.is-empty .messages-panel { - padding: 0; - scroll-padding-bottom: 0; - } - - .messages-list-shell { - width: calc(100% - 20px); - max-width: 100%; - } - - .composer-shell, - .project-composer-shell { - padding: 0; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/ChatInput.vue b/legacy-dashboard/src/components/chat/ChatInput.vue deleted file mode 100644 index 767ad164..00000000 --- a/legacy-dashboard/src/components/chat/ChatInput.vue +++ /dev/null @@ -1,1758 +0,0 @@ -<template> - <div - class="input-area fade-in" - :class="{ 'is-dark': isDark }" - @dragover.prevent="handleDragOver" - @dragleave.prevent="handleDragLeave" - @drop.prevent="handleDrop" - > - <div - class="input-container" - :class="{ - 'is-multiline': inputIsMultiline, - 'has-attachments': hasStagedAttachments, - }" - :style="{ - width: 'var(--chat-content-width, 76%)', - maxWidth: 'var(--chat-content-max-width, 760px)', - margin: '0 auto', - border: isDark ? 'none' : '1px solid #e0e0e0', - borderRadius: '24px', - boxShadow: isDark ? 'none' : '0px 2px 2px rgba(0, 0, 0, 0.1)', - backgroundColor: isDark ? '#2d2d2d' : '#fff', - position: 'relative', - transition: 'min-height 0.2s ease, padding 0.2s ease', - }" - > - <!-- 拖拽上传遮罩 --> - <transition name="fade"> - <div v-if="isDragging" class="drop-overlay"> - <div class="drop-overlay-content"> - <v-icon size="48" color="primary">mdi-cloud-upload</v-icon> - <span class="drop-text">{{ tm("input.dropToUpload") }}</span> - </div> - </div> - </transition> - <!-- 引用预览区 --> - <transition name="slideReply" @after-leave="handleReplyAfterLeave"> - <div class="reply-preview" v-if="props.replyTo && !isReplyClosing"> - <div class="reply-content"> - <v-icon size="small" class="reply-icon">mdi-reply</v-icon> - "<span class="reply-text">{{ props.replyTo.selectedText }}</span - >" - </div> - <v-btn - @click="handleClearReply" - class="remove-reply-btn" - icon="mdi-close" - size="x-small" - color="grey" - variant="text" - /> - </div> - </transition> - - <transition name="attachments"> - <div class="attachments-preview" v-if="hasStagedAttachments"> - <div - v-for="(img, index) in stagedImagesUrl" - :key="'img-' + index" - class="attachment-card image-preview" - > - <img :src="img" class="preview-image" alt="attachment preview" /> - <v-btn - @click="$emit('removeImage', index)" - class="remove-attachment-btn" - icon="mdi-close" - size="x-small" - color="error" - variant="tonal" - /> - </div> - - <div v-if="stagedAudioUrl" class="attachment-card audio-preview"> - <div class="attachment-icon attachment-icon--audio"> - <v-icon icon="mdi-microphone" size="24"></v-icon> - </div> - <span class="attachment-name">{{ tm("voice.recording") }}</span> - <v-btn - @click="$emit('removeAudio')" - class="remove-attachment-btn" - icon="mdi-close" - size="x-small" - color="error" - variant="tonal" - /> - </div> - - <div - v-for="(file, index) in stagedFiles" - :key="'file-' + index" - class="attachment-card file-preview" - > - <div - class="attachment-icon" - :style="{ '--attachment-color': filePresentation(file).color }" - > - <v-icon :icon="filePresentation(file).icon" size="24"></v-icon> - <span class="attachment-ext">{{ - filePresentation(file).label - }}</span> - </div> - <span class="attachment-name">{{ file.original_name }}</span> - <v-btn - @click="$emit('removeFile', index)" - class="remove-attachment-btn" - icon="mdi-close" - size="x-small" - color="error" - variant="tonal" - /> - </div> - </div> - </transition> - - <CommandSuggestion - :visible="showCommandSuggestion" - :commands="filteredCommands" - :selected-index="selectedCommandIndex" - :is-dark="isDark" - @select="handleCommandSelect" - @update-selected-index="selectedCommandIndex = $event" - /> - - <div class="composer-row"> - <div class="input-left-actions"> - <!-- Settings Menu --> - <StyledMenu - offset="8" - location="top start" - :close-on-content-click="false" - > - <template v-slot:activator="{ props: activatorProps }"> - <v-btn - v-bind="activatorProps" - icon="mdi-plus" - variant="outlined" - class="input-neutral-btn input-outline-control" - /> - </template> - - <!-- Upload Files --> - <v-list-item - class="styled-menu-item" - rounded="md" - @click="triggerImageInput" - > - <template v-slot:prepend> - <v-icon icon="mdi-file-upload" size="small"></v-icon> - </template> - <v-list-item-title> - {{ tm("input.upload") }} - </v-list-item-title> - </v-list-item> - - <!-- Config Selector in Menu --> - <ConfigSelector - :session-id="sessionId || null" - :platform-id="sessionPlatformId" - :is-group="sessionIsGroup" - :initial-config-id="props.configId" - @config-changed="handleConfigChange" - /> - - <!-- Streaming Toggle in Menu --> - <v-list-item - class="styled-menu-item" - rounded="md" - @click="$emit('toggleStreaming')" - > - <template v-slot:prepend> - <v-icon icon="mdi-lightning-bolt" size="small"></v-icon> - </template> - <v-list-item-title> - {{ - enableStreaming - ? tm("streaming.enabled") - : tm("streaming.disabled") - }} - </v-list-item-title> - </v-list-item> - </StyledMenu> - - </div> - <div class="input-field-shell"> - <input - v-if="!inputIsMultiline" - ref="inputField" - v-model="localPrompt" - @keydown="handleKeyDown" - @input="handleInput" - @compositionstart="handleCompositionStart" - @compositionend="handleCompositionEnd" - @compositioncancel="handleCompositionEnd" - @blur="handleBlur" - @paste="handlePaste" - :disabled="disabled" - :placeholder="tm('input.placeholder')" - class="chat-text-input" - autocomplete="off" - autocorrect="off" - autocapitalize="sentences" - spellcheck="false" - type="text" - /> - <textarea - v-else - ref="inputField" - v-model="localPrompt" - @keydown="handleKeyDown" - @input="handleInput" - @compositionstart="handleCompositionStart" - @compositionend="handleCompositionEnd" - @compositioncancel="handleCompositionEnd" - @blur="handleBlur" - @paste="handlePaste" - :disabled="disabled" - :placeholder="tm('input.placeholder')" - class="chat-textarea" - autocomplete="off" - autocorrect="off" - autocapitalize="sentences" - spellcheck="false" - ></textarea> - </div> - <div class="input-right-actions"> - <input - type="file" - ref="imageInputRef" - @change="handleFileSelect" - style="display: none" - multiple - /> - <!-- Provider/Model Selector Menu --> - <ProviderModelMenu - v-if="props.showProviderSelector && providerSelectorAvailable" - ref="providerModelMenuRef" - /> - <v-progress-circular - v-if="disabled && !mobile" - indeterminate - size="16" - class="mr-1" - width="1.5" - /> - <v-tooltip - v-if="tokenUsageVisible" - location="top" - max-width="320" - > - <template #activator="{ props: tokenTooltipProps }"> - <span - v-bind="tokenTooltipProps" - class="token-usage-indicator" - :style="{ '--token-usage-color': tokenUsageColor }" - > - <v-progress-circular - :model-value="tokenUsagePercent" - size="24" - width="2.5" - class="token-usage-progress" - /> - </span> - </template> - <span>{{ props.tokenUsage?.tooltip }}</span> - </v-tooltip> - <!-- <v-btn @click="$emit('openLiveMode')" - icon - variant="text" - color="purple" - size="small" - > - <v-icon icon="mdi-phone-in-talk" variant="text" plain></v-icon> - <v-tooltip activator="parent" location="top"> - {{ tm('voice.liveMode') }} - </v-tooltip> - </v-btn> --> - <v-btn - @click="handleRecordClick" - icon - variant="text" - class="record-btn input-icon-btn" - > - <v-icon - :icon="isRecording ? 'mdi-stop-circle' : 'mdi-microphone'" - variant="text" - plain - ></v-icon> - <v-tooltip activator="parent" location="top"> - {{ - isRecording ? tm("voice.speaking") : tm("voice.startRecording") - }} - </v-tooltip> - </v-btn> - <v-btn - icon - v-if="isRunning && !canSend" - @click="$emit('stop')" - variant="tonal" - class="send-btn input-action-btn" - > - <v-icon icon="mdi-stop" variant="text" plain></v-icon> - <v-tooltip activator="parent" location="top"> - {{ tm("input.stopGenerating") }} - </v-tooltip> - </v-btn> - <v-btn - v-else - @click="$emit('send')" - icon="mdi-arrow-up" - variant="tonal" - :disabled="!canSend" - class="send-btn input-action-btn" - /> - </div> - </div> - </div> - </div> -</template> - -<script setup lang="ts"> -import { - ref, - computed, - watch, - nextTick, - onMounted, - onBeforeUnmount, -} from "vue"; -import { useDisplay } from "vuetify"; -import { useModuleI18n } from "@/i18n/composables"; -import { useCustomizerStore } from "@/stores/customizer"; -import { isComposingEnter } from "@/utils/imeInput.mjs"; -import { commandApi } from "@/api/v1"; -import type { CommandItem } from "@/components/extension/componentPanel/types"; -import ConfigSelector from "./ConfigSelector.vue"; -import ProviderModelMenu from "./ProviderModelMenu.vue"; -import StyledMenu from "@/components/shared/StyledMenu.vue"; -import CommandSuggestion from "./CommandSuggestion.vue"; -import { attachmentPresentation } from "./attachmentPresentation"; -import type { Session } from "@/composables/useSessions"; -import type { SuggestionCommand } from "./CommandSuggestion.vue"; - -interface StagedFileInfo { - attachment_id: string; - filename: string; - original_name: string; - url: string; - type: string; -} - -interface ReplyInfo { - messageId: string | number; - selectedText?: string; -} - -interface TokenUsageInfo { - used: number; - limit: number; - percent: number; - tooltip: string; -} - -interface Props { - prompt: string; - stagedImagesUrl: string[]; - stagedAudioUrl: string; - stagedFiles?: StagedFileInfo[]; - disabled: boolean; - enableStreaming: boolean; - isRecording: boolean; - isRunning: boolean; - sessionId?: string | null; - currentSession?: Session | null; - configId?: string | null; - replyTo?: ReplyInfo | null; - sendShortcut?: "enter" | "shift_enter"; - showProviderSelector?: boolean; - tokenUsage?: TokenUsageInfo | null; -} - -const props = withDefaults(defineProps<Props>(), { - sessionId: null, - currentSession: null, - configId: null, - stagedFiles: () => [], - replyTo: null, - sendShortcut: "shift_enter", - showProviderSelector: true, - tokenUsage: null, -}); - -const emit = defineEmits<{ - "update:prompt": [value: string]; - send: []; - stop: []; - toggleStreaming: []; - removeImage: [index: number]; - removeAudio: []; - removeFile: [index: number]; - startRecording: []; - stopRecording: []; - pasteImage: [event: ClipboardEvent]; - fileSelect: [files: FileList]; - clearReply: []; - openLiveMode: []; -}>(); - -const { tm } = useModuleI18n("features/chat"); -const isDark = computed( - () => useCustomizerStore().uiTheme === "PurpleThemeDark", -); - -const inputField = ref<HTMLInputElement | HTMLTextAreaElement | null>(null); -const imageInputRef = ref<HTMLInputElement | null>(null); -const providerModelMenuRef = ref<InstanceType<typeof ProviderModelMenu> | null>( - null, -); -const providerSelectorAvailable = ref(true); -const isReplyClosing = ref(false); -const isDragging = ref(false); -const isComposing = ref(false); -const inputIsMultiline = ref(false); -const lastCompositionEndAt = ref<number | null>(null); -let dragLeaveTimeout: number | null = null; - -// 命令提示相关状态 -const allCommands = ref<CommandItem[]>([]); -const showCommandSuggestion = ref(false); -const selectedCommandIndex = ref(0); -const commandSuggestionLoading = ref(false); -const wakePrefixes = ref<string[]>(["/"]); -const currentConfigId = ref((props.configId as string) || "default"); - -/** 检查文本是否以任意一个唤醒词前缀开头 */ -function hasWakePrefix(text: string): boolean { - return wakePrefixes.value.some((p) => text.startsWith(p)); -} - -/** 去掉文本开头匹配的任意唤醒词前缀,返回剥离后的文本 */ -function stripWakePrefix(text: string): string { - let result = text; - for (const p of wakePrefixes.value) { - if (result.startsWith(p)) { - result = result.slice(p.length); - break; // 只剥离第一个匹配的前缀 - } - } - return result; -} - -function normalizeCommandSearchText(value: string) { - return stripWakePrefix(value.trim()).toLowerCase(); -} - -/** 从所有指令中展平获取启用的普通指令和子指令 */ -const enabledCommands = computed(() => { - const result: SuggestionCommand[] = []; - const seen = new Set<string>(); - // 使用第一个唤醒词前缀作为指令的展示前缀 - const displayPrefix = wakePrefixes.value[0] || "/"; - - function addCommand(cmd: CommandItem) { - if (!cmd.enabled) return; - if (cmd.type === "group") { - // 指令组本身不加入,但其子指令加入 - cmd.sub_commands?.forEach(addCommand); - return; - } - // 统一添加唤醒词前缀(子命令的 effective_command 如 "music play" 需要变成 "/music play") - const displayCmd = hasWakePrefix(cmd.effective_command) - ? cmd.effective_command - : `${displayPrefix}${cmd.effective_command}`; - if (!seen.has(displayCmd)) { - seen.add(displayCmd); - result.push({ - handler_full_name: cmd.handler_full_name, - effective_command: displayCmd, - description: cmd.description, - plugin_display_name: cmd.plugin_display_name, - enabled: cmd.enabled, - reserved: cmd.reserved, - }); - } - // 同时加入别名(别名也需要加上唤醒词前缀) - cmd.aliases?.forEach((alias) => { - const aliasBase = cmd.parent_signature - ? `${cmd.parent_signature} ${alias}` - : alias; - const aliasKey = hasWakePrefix(aliasBase) - ? aliasBase - : `${displayPrefix}${aliasBase}`; - if (!seen.has(aliasKey)) { - seen.add(aliasKey); - result.push({ - handler_full_name: cmd.handler_full_name, - effective_command: aliasKey, - description: cmd.description, - plugin_display_name: cmd.plugin_display_name, - enabled: cmd.enabled, - reserved: cmd.reserved, - }); - } - }); - } - - allCommands.value.forEach(addCommand); - return result; -}); - -function sortSystemPluginCommandsFirst(commands: SuggestionCommand[]) { - return [...commands].sort((a, b) => Number(b.reserved) - Number(a.reserved)); -} - -/** 根据当前输入过滤候选指令 */ -const filteredCommands = computed(() => { - const text = props.prompt; - if (!text || !hasWakePrefix(text)) return []; - - const query = normalizeCommandSearchText(text); - if (!query) return sortSystemPluginCommandsFirst(enabledCommands.value); - - const startsWithMatches: SuggestionCommand[] = []; - const containsMatches: SuggestionCommand[] = []; - - for (const cmd of enabledCommands.value) { - const commandText = normalizeCommandSearchText(cmd.effective_command); - const pluginText = normalizeCommandSearchText(cmd.plugin_display_name || ""); - const descriptionText = normalizeCommandSearchText(cmd.description || ""); - const matchesCommand = commandText.includes(query); - const matchesMetadata = - pluginText.includes(query) || descriptionText.includes(query); - - if (commandText.startsWith(query)) { - startsWithMatches.push(cmd); - } else if (matchesCommand || matchesMetadata) { - containsMatches.push(cmd); - } - } - - return [ - ...sortSystemPluginCommandsFirst(startsWithMatches), - ...sortSystemPluginCommandsFirst(containsMatches), - ]; -}); - -const localPrompt = computed({ - get: () => props.prompt, - set: (value) => { - // Suppress v-model sync during IME composition to avoid a reactive - // feedback loop. Vue's :value binding overwrites the native textarea - // DOM state mid-composition, which interferes with IME insertion at - // non-terminal cursor positions (alternating character loss). - // The final value is synced manually in handleCompositionEnd. - if (!isComposing.value) emit("update:prompt", value); - }, -}); - -const sessionPlatformId = computed( - () => props.currentSession?.platform_id || "webchat", -); -const sessionIsGroup = computed(() => Boolean(props.currentSession?.is_group)); - -const canSend = computed(() => { - return ( - (props.prompt && props.prompt.trim()) || - props.stagedImagesUrl.length > 0 || - props.stagedAudioUrl || - (props.stagedFiles && props.stagedFiles.length > 0) - ); -}); - -const hasStagedAttachments = computed(() => { - return ( - props.stagedImagesUrl.length > 0 || - props.stagedAudioUrl || - (props.stagedFiles && props.stagedFiles.length > 0) - ); -}); - -function filePresentation(file: StagedFileInfo) { - return attachmentPresentation(file); -} - -// Ctrl+B 长按录音相关 -const ctrlKeyDown = ref(false); -const ctrlKeyTimer = ref<number | null>(null); -const ctrlKeyLongPressThreshold = 300; - -// 处理清除引用 - 触发关闭动画 -function handleClearReply() { - isReplyClosing.value = true; -} - -// 动画完成后发送clearReply事件 -function handleReplyAfterLeave() { - emit("clearReply"); - isReplyClosing.value = false; -} - -const { mobile } = useDisplay(); - -const tokenUsageVisible = computed(() => { - const usage = props.tokenUsage; - return Boolean( - usage && - Number.isFinite(usage.used) && - Number.isFinite(usage.limit) && - usage.used > 0 && - usage.limit > 0, - ); -}); - -const tokenUsagePercent = computed(() => { - const percent = props.tokenUsage?.percent || 0; - if (!Number.isFinite(percent)) return 0; - return Math.min(100, Math.max(0, percent)); -}); - -const tokenUsageColor = computed(() => - isDark.value - ? "rgba(var(--v-theme-on-surface), 0.82)" - : "rgba(var(--v-theme-on-surface), 0.72)", -); - -// Auto-resize textarea -function autoResize() { - const el = inputField.value; - if (!el) return; - if (!(el instanceof HTMLTextAreaElement)) { - const shouldExpand = - localPrompt.value.includes("\n") || - (el.clientWidth > 0 && el.scrollWidth > el.clientWidth + 4); - if (shouldExpand) { - const cursor = el.selectionStart ?? localPrompt.value.length; - inputIsMultiline.value = true; - nextTick(() => { - inputField.value?.focus(); - inputField.value?.setSelectionRange(cursor, cursor); - autoResize(); - }); - } - return; - } - const isMobileViewport = - typeof window !== "undefined" && - window.matchMedia("(max-width: 768px)").matches; - const viewportHeight = - typeof window !== "undefined" ? window.innerHeight : 900; - const minHeight = isMobileViewport ? 56 : 52; - const maxHeight = isMobileViewport - ? Math.min(220, Math.round(viewportHeight * 0.42)) - : Math.min(420, Math.round(viewportHeight * 0.48)); - if (!localPrompt.value) { - inputIsMultiline.value = false; - el.style.height = minHeight + "px"; - return; - } - el.style.height = "auto"; - el.style.setProperty("min-height", "0", "important"); - const measuredHeight = el.scrollHeight; - el.style.removeProperty("min-height"); - const computed = getComputedStyle(el); - let lineHeight = parseFloat(computed.lineHeight); - if (!Number.isFinite(lineHeight)) { - lineHeight = parseFloat(computed.fontSize) * 1.2; - } - const paddingVertical = - parseFloat(computed.paddingTop) + parseFloat(computed.paddingBottom); - const shouldUseMultiline = - localPrompt.value.includes("\n") || - measuredHeight > lineHeight + paddingVertical + 0.5; - if (inputIsMultiline.value !== shouldUseMultiline) { - const cursor = el.selectionStart ?? localPrompt.value.length; - inputIsMultiline.value = shouldUseMultiline; - nextTick(() => { - inputField.value?.focus(); - inputField.value?.setSelectionRange(cursor, cursor); - autoResize(); - }); - return; - } - el.style.height = shouldUseMultiline - ? Math.min(Math.max(measuredHeight, minHeight), maxHeight) + "px" - : minHeight + "px"; -} - -watch( - () => props.prompt, - (value) => { - if (!value) { - inputIsMultiline.value = false; - } - nextTick(autoResize); - }, -); - -watch(inputIsMultiline, () => { - nextTick(autoResize); -}); - -function handleKeyDown(e: KeyboardEvent) { - // 命令提示激活时,拦截方向键和 Enter/Esc - if (showCommandSuggestion.value && filteredCommands.value.length > 0) { - if (e.key === "ArrowDown") { - e.preventDefault(); - selectedCommandIndex.value = - (selectedCommandIndex.value + 1) % filteredCommands.value.length; - return; - } - if (e.key === "ArrowUp") { - e.preventDefault(); - selectedCommandIndex.value = - (selectedCommandIndex.value - 1 + filteredCommands.value.length) % - filteredCommands.value.length; - return; - } - if (e.key === "Enter") { - e.preventDefault(); - const cmd = filteredCommands.value[selectedCommandIndex.value]; - if (cmd) { - handleCommandSelect(cmd); - } - return; - } - if (e.key === "Escape") { - e.preventDefault(); - showCommandSuggestion.value = false; - return; - } - } - - const isEnter = e.key === "Enter"; - if (!isEnter) { - // Ctrl+B 录音 - if (e.ctrlKey && e.keyCode === 66) { - e.preventDefault(); - if (ctrlKeyDown.value) return; - - ctrlKeyDown.value = true; - ctrlKeyTimer.value = window.setTimeout(() => { - if (ctrlKeyDown.value && !props.isRecording) { - emit("startRecording"); - } - }, ctrlKeyLongPressThreshold); - } - return; - } - - if (isComposingEnter(e, isComposing.value, lastCompositionEndAt.value)) { - return; - } - - const isSendHotkey = - e.ctrlKey || - e.metaKey || - (props.sendShortcut === "enter" ? !e.shiftKey : e.shiftKey); - - if (isSendHotkey) { - e.preventDefault(); - if (localPrompt.value.trim() === "/astr_live_dev") { - emit("openLiveMode"); - localPrompt.value = ""; - return; - } - if (canSend.value) { - emit("send"); - } - return; - } - - if (!inputIsMultiline.value) { - e.preventDefault(); - const target = e.target as HTMLInputElement; - const start = target.selectionStart ?? localPrompt.value.length; - const end = target.selectionEnd ?? start; - localPrompt.value = - localPrompt.value.slice(0, start) + "\n" + localPrompt.value.slice(end); - inputIsMultiline.value = true; - nextTick(() => { - inputField.value?.focus(); - inputField.value?.setSelectionRange(start + 1, start + 1); - autoResize(); - }); - } -} - -/** 处理输入变化,控制命令提示显示 */ -function handleInput() { - const text = props.prompt; - if (text && hasWakePrefix(text) && !isComposing.value) { - showCommandSuggestion.value = filteredCommands.value.length > 0; - selectedCommandIndex.value = 0; - } else { - showCommandSuggestion.value = false; - } -} - -/** 处理 blur 事件,延迟关闭命令提示以允许点击 */ -function handleBlur() { - clearCompositionState(); - // 延迟关闭,避免点击候选项时面板已消失 - setTimeout(() => { - showCommandSuggestion.value = false; - }, 200); -} - -/** 选择命令,填入输入框 */ -function handleCommandSelect(cmd: SuggestionCommand) { - localPrompt.value = cmd.effective_command + " "; - showCommandSuggestion.value = false; - nextTick(() => { - inputField.value?.focus(); - autoResize(); - }); -} - -/** 获取指令列表 */ -async function fetchCommands() { - if (commandSuggestionLoading.value) return; - commandSuggestionLoading.value = true; - try { - const cid = currentConfigId.value; - const res = await commandApi.list(cid && cid !== "default" ? cid : undefined); - if (res.data.status === "ok") { - allCommands.value = res.data.data.items || []; - // 读取当前配置的唤醒词列表,用于指令候选的触发前缀 - const prefixes: string[] = res.data.data.wake_prefix || []; - if (prefixes && prefixes.length > 0) { - wakePrefixes.value = prefixes; - } - } - } catch (err) { - // 静默失败,不影响聊天功能 - console.warn("Failed to fetch commands for suggestion:", err); - } finally { - commandSuggestionLoading.value = false; - } -} - -function handleCompositionStart() { - isComposing.value = true; - lastCompositionEndAt.value = null; -} - -function handleCompositionEnd(e: CompositionEvent) { - lastCompositionEndAt.value = e.timeStamp; - clearCompositionState({ keepLastEndAt: true }); - - // Manually sync the final composited text to the parent component - // after the IME commits. The v-model setter is suppressed during - // composition (see localPrompt computed), so we must explicitly - // propagate the DOM value once composition ends. - // - // Capture the DOM value at compositionend to guard against a race - // where props.prompt is externally updated between now and nextTick. - const endValue = inputField.value?.value; - - nextTick(() => { - const el = inputField.value; - // Only sync if the DOM hasn't been changed externally in the meantime. - if (el && el.value === endValue && el.value !== props.prompt) { - emit("update:prompt", el.value); - // Re-evaluate command suggestions that were suppressed during IME - // composition (handleInput checks isComposing). Only needed when - // the value actually changed. Runs in a nested nextTick so - // props.prompt reflects the emit above. - nextTick(() => { - handleInput(); - }); - } - }); -} - -function clearCompositionState({ keepLastEndAt = false } = {}) { - isComposing.value = false; - if (!keepLastEndAt) { - lastCompositionEndAt.value = null; - } -} - -function handleKeyUp(e: KeyboardEvent) { - if (e.keyCode === 66) { - ctrlKeyDown.value = false; - - if (ctrlKeyTimer.value) { - clearTimeout(ctrlKeyTimer.value); - ctrlKeyTimer.value = null; - } - - if (props.isRecording) { - emit("stopRecording"); - } - } -} - -function handlePaste(e: ClipboardEvent) { - const pastedText = e.clipboardData?.getData("text/plain") || ""; - if (!inputIsMultiline.value && pastedText.includes("\n")) { - e.preventDefault(); - const target = e.target as HTMLInputElement; - const start = target.selectionStart ?? localPrompt.value.length; - const end = target.selectionEnd ?? start; - localPrompt.value = - localPrompt.value.slice(0, start) + - pastedText + - localPrompt.value.slice(end); - inputIsMultiline.value = true; - nextTick(() => { - inputField.value?.focus(); - const cursor = start + pastedText.length; - inputField.value?.setSelectionRange(cursor, cursor); - autoResize(); - }); - } - emit("pasteImage", e); -} - -function handleDragOver(e: DragEvent) { - // 清除之前的 leave timeout - if (dragLeaveTimeout) { - clearTimeout(dragLeaveTimeout); - dragLeaveTimeout = null; - } - - // 检查是否有文件 - if (e.dataTransfer?.types.includes("Files")) { - isDragging.value = true; - } -} - -function handleDragLeave(e: DragEvent) { - // 使用 timeout 避免在子元素间移动时闪烁 - dragLeaveTimeout = window.setTimeout(() => { - isDragging.value = false; - }, 50); -} - -function handleDrop(e: DragEvent) { - isDragging.value = false; - - const files = e.dataTransfer?.files; - if (files && files.length > 0) { - emit("fileSelect", files); - } -} - -function triggerImageInput() { - imageInputRef.value?.click(); -} - -function handleFileSelect(event: Event) { - const target = event.target as HTMLInputElement; - const files = target.files; - if (files) { - emit("fileSelect", files); - } - target.value = ""; -} - -function handleRecordClick() { - if (props.isRecording) { - emit("stopRecording"); - } else { - emit("startRecording"); - } -} - -function handleConfigChange(payload: { - configId: string; - agentRunnerType: string; -}) { - const runnerType = (payload.agentRunnerType || "").toLowerCase(); - const isInternal = runnerType === "internal" || runnerType === "local"; - providerSelectorAvailable.value = isInternal; - // 配置切换后重新获取指令列表和唤醒词 - if (payload.configId && payload.configId !== currentConfigId.value) { - currentConfigId.value = payload.configId; - fetchCommands(); - } -} - -function getCurrentSelection() { - if (!props.showProviderSelector || !providerSelectorAvailable.value) { - return null; - } - return providerModelMenuRef.value?.getCurrentSelection(); -} - -function focusInput() { - if (!inputField.value) return; - inputField.value.focus(); -} - -onMounted(() => { - document.addEventListener("keyup", handleKeyUp); - // 预加载指令列表 - fetchCommands(); - nextTick(autoResize); -}); - -onBeforeUnmount(() => { - clearCompositionState(); - document.removeEventListener("keyup", handleKeyUp); -}); - -defineExpose({ - getCurrentSelection, - focusInput, -}); -</script> - -<style scoped> -.input-area { - padding: 12px 16px 0; - background-color: transparent; - position: relative; - border-top: 1px solid var(--v-theme-border); - flex-shrink: 0; -} - -.input-neutral-btn { - color: #6f6f6f !important; -} - -.input-neutral-btn:hover { - background: #efefef; -} - -.input-neutral-btn--tonal { - background: #efefef; - color: #4f4f4f !important; -} - -.input-neutral-btn--tonal:hover { - background: #e7e7e7; -} - -.input-action-btn { - background: #5594c6 !important; - color: #fff !important; -} - -.input-action-btn:hover { - background: #4c86b3 !important; -} - -.input-action-btn:disabled { - background: rgba(85, 148, 198, 0.24) !important; - color: rgba(255, 255, 255, 0.72) !important; -} - -.input-icon-btn { - background: transparent !important; - color: rgb(var(--v-theme-on-surface)) !important; - margin-right: 8px; -} - -.input-icon-btn:hover { - background: rgba(var(--v-theme-on-surface), 0.04) !important; -} - -.token-usage-indicator { - width: 24px; - height: 24px; - display: inline-flex; - align-items: center; - justify-content: center; - flex: 0 0 24px; - border-radius: 50%; - color: var(--token-usage-color); -} - -.token-usage-progress { - color: currentColor; -} - -.token-usage-progress :deep(.v-progress-circular__underlay) { - color: rgba(var(--v-theme-on-surface), 0.18); - stroke: currentColor; - opacity: 0.24; -} - -.token-usage-progress :deep(.v-progress-circular__overlay) { - stroke: currentColor; -} - -.input-outline-control { - width: 36px !important; - height: 36px !important; - min-width: 36px !important; - border: 0 !important; - border-color: transparent !important; - background: transparent !important; - box-shadow: none !important; -} - -.input-outline-control:hover, -.input-outline-control:focus-visible { - border-color: transparent !important; - background: rgba(var(--v-theme-on-surface), 0.04) !important; -} - -.input-area.is-dark .input-neutral-btn { - color: rgba(255, 255, 255, 0.78) !important; -} - -.input-area.is-dark .input-neutral-btn:hover, -.input-area.is-dark .input-neutral-btn--tonal { - background: rgba(255, 255, 255, 0.1); -} - -.input-area.is-dark .input-outline-control { - border-color: transparent !important; - background: transparent !important; -} - -.input-area.is-dark .input-outline-control:hover, -.input-area.is-dark .input-outline-control:focus-visible { - border-color: transparent !important; - background: rgba(255, 255, 255, 0.06) !important; -} - -.input-area.is-dark .input-action-btn { - background: rgb(var(--v-theme-on-surface)) !important; - color: rgb(var(--v-theme-surface)) !important; -} - -.input-area.is-dark .input-action-btn:hover { - background: rgba(var(--v-theme-on-surface), 0.86) !important; -} - -.input-area.is-dark .input-action-btn:disabled { - background: rgba(var(--v-theme-on-surface), 0.14) !important; - color: rgba(var(--v-theme-on-surface), 0.4) !important; -} - -.input-container { - display: flex; - flex-direction: column; - justify-content: center; - min-height: 64px; - padding: 6px 12px 6px 14px !important; - border-color: #f0f0f0 !important; - border-radius: 999px !important; - background: #fff !important; - box-shadow: 0 8px 22px rgba(0, 0, 0, 0.08) !important; -} - -.input-container.is-multiline { - justify-content: flex-start; - padding: 16px 20px 14px !important; - border-radius: 34px !important; -} - -.input-container.has-attachments { - justify-content: flex-start; - min-height: 130px; - padding: 14px 18px 10px !important; - border-radius: 30px !important; -} - -.input-area.is-dark .input-container { - border: 1px solid rgba(255, 255, 255, 0.12) !important; - background: #2d2d2d !important; - box-shadow: none !important; -} - -.reply-preview, -.attachments-preview { - width: 100%; - flex: 0 0 auto; -} - -.composer-row { - width: 100%; - min-height: 52px; - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - grid-template-areas: "left field right"; - align-items: center; - column-gap: 10px; -} - -.input-container.is-multiline .composer-row { - grid-template-areas: - "field field field" - "left . right"; - row-gap: 10px; - align-items: end; -} - -.input-field-shell { - grid-area: field; - min-width: 0; - min-height: 52px; - display: flex; - align-items: center; -} - -.input-container.is-multiline .input-field-shell { - min-height: auto; - align-items: flex-start; -} - -.chat-text-input, -.chat-textarea { - display: block; - width: 100%; - box-sizing: border-box; - min-width: 0; - min-height: 52px !important; - max-height: 72px !important; - margin: 0; - padding: 0 !important; - border: 0 !important; - border-radius: 0 !important; - background: transparent !important; - box-shadow: none !important; - resize: none; - outline: none; - font-family: inherit; - font-size: 18px !important; -} - -.chat-text-input { - height: 52px !important; - padding: 0 !important; - line-height: normal !important; - overflow: hidden; -} - -.chat-textarea { - max-height: min(48vh, 420px) !important; - padding: 12px 0 !important; - overflow-y: auto; - overflow-wrap: break-word; - line-height: 28px !important; - transition: height 0.16s ease; -} - -.chat-text-input::placeholder, -.chat-textarea::placeholder { - color: rgba(var(--v-theme-on-surface), 0.56); - opacity: 1; -} - -.input-left-actions { - grid-area: left; - display: flex; - align-items: center; - flex: 0 0 auto !important; - justify-content: center !important; - gap: 0 !important; - min-width: auto !important; - margin-top: 0 !important; - overflow: visible !important; -} - -.input-right-actions { - grid-area: right; - display: flex; - align-items: center; - justify-content: flex-end; - flex-shrink: 0; - gap: 10px; - margin-top: 0 !important; -} - -.input-outline-control { - width: 34px !important; - height: 34px !important; - min-width: 34px !important; - border: 0 !important; - border-color: transparent !important; - border-radius: 50% !important; - box-shadow: none !important; -} - -.input-icon-btn { - width: 42px !important; - height: 42px !important; - min-width: 42px !important; - margin-right: 0; -} - -.input-right-actions :deep(.provider-chip) { - height: 40px !important; - min-height: 40px !important; - border-radius: 999px !important; -} - -.input-area:not(.is-dark) .input-action-btn { - width: 46px !important; - height: 46px !important; - min-width: 46px !important; - background: #8fcfb4 !important; - color: #fff !important; -} - -.input-area:not(.is-dark) .input-action-btn:hover { - background: #7fc4a8 !important; -} - -.input-area:not(.is-dark) .input-action-btn:disabled { - background: #f2f5f3 !important; - color: rgba(0, 0, 0, 0.18) !important; -} - -/* 拖拽上传遮罩 */ -.drop-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(var(--v-theme-primary), 0.12); - border: 2px dashed rgba(var(--v-theme-primary), 0.45); - border-radius: 24px; - display: flex; - align-items: center; - justify-content: center; - z-index: 100; - pointer-events: none; -} - -.drop-overlay-content { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} - -.drop-text { - font-size: 16px; - font-weight: 500; - color: rgb(var(--v-theme-primary)); -} - -/* Fade transition for drop overlay */ -.fade-enter-active, -.fade-leave-active { - transition: opacity 0.2s ease; -} - -.fade-enter-from, -.fade-leave-to { - opacity: 0; -} - -.reply-preview { - display: flex; - align-items: center; - justify-content: space-between; - padding: 8px 16px; - margin: 8px 8px 0 8px; - background-color: rgba(var(--v-theme-primary), 0.06); - border-radius: 12px; - gap: 8px; - max-height: 500px; - overflow: hidden; -} - -/* Transition animations for reply preview */ -.slideReply-enter-active { - animation: slideDown 0.2s ease-out; -} - -.slideReply-leave-active { - animation: slideUp 0.2s ease-out; -} - -@keyframes slideDown { - from { - max-height: 0; - opacity: 0; - margin-top: 0; - padding-top: 0; - padding-bottom: 0; - } - - to { - max-height: 500px; - opacity: 1; - margin-top: 8px; - padding-top: 8px; - padding-bottom: 8px; - } -} - -@keyframes slideUp { - from { - max-height: 500px; - opacity: 1; - margin-top: 8px; - padding-top: 8px; - padding-bottom: 8px; - } - - to { - max-height: 0; - opacity: 0; - margin-top: 0; - padding-top: 0; - padding-bottom: 0; - } -} - -.reply-content { - display: flex; - align-items: center; - gap: 6px; - flex: 1; - min-width: 0; - overflow: hidden; -} - -.reply-icon { - color: var(--v-theme-secondary); - flex-shrink: 0; -} - -.reply-text { - font-size: 13px; - color: var(--v-theme-secondaryText); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1; - min-width: 0; -} - -.remove-reply-btn { - flex-shrink: 0; - opacity: 0.6; -} - -.attachments-preview { - display: flex; - gap: 10px; - margin: 10px 12px 0; - padding: 2px 2px 4px; - flex-wrap: nowrap; - align-items: center; - overflow-x: auto; - overflow-y: hidden; - scrollbar-width: thin; - max-height: 72px; -} - -.input-container.has-attachments .attachments-preview { - margin: 0 0 8px; - padding: 0; -} - -.attachment-card { - --attachment-color: #607d8b; - position: relative; - display: inline-flex; - align-items: center; - justify-content: flex-start; - gap: 8px; - width: 210px; - height: 54px; - flex: 0 0 auto; - min-width: 0; - padding: 7px 32px 7px 10px; - overflow: hidden; - color: rgb(var(--v-theme-on-surface)); - background: rgba(var(--v-theme-on-surface), 0.055); - border: 0; - border-radius: 8px; -} - -.file-preview { - background: rgba(var(--v-theme-on-surface), 0.055); - background: linear-gradient( - 90deg, - color-mix(in srgb, var(--attachment-color) 14%, transparent), - rgba(var(--v-theme-on-surface), 0.055) 62% - ); -} - -.image-preview { - width: 54px; - flex-basis: 54px; - padding: 0; - background: rgba(var(--v-theme-on-surface), 0.06); -} - -.preview-image { - width: 100%; - height: 100%; - object-fit: cover; - border-radius: 8px; -} - -.attachment-icon { - display: inline-flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1px; - flex-shrink: 0; - min-width: 34px; - color: var(--attachment-color); -} - -.attachment-icon--audio { - color: #00897b; -} - -.attachment-ext { - max-width: 58px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 10px; - font-weight: 700; - line-height: 12px; - color: var(--attachment-color); -} - -.attachment-name { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 13px; - line-height: 17px; -} - -.remove-attachment-btn { - position: absolute; - top: 4px; - right: 4px; - width: 22px !important; - height: 22px !important; - min-width: 22px !important; - opacity: 0.8; - transition: opacity 0.2s; -} - -.remove-attachment-btn:hover { - opacity: 1; -} - -.fade-in { - animation: fadeIn 0.3s ease-in-out; -} - -.attachments-enter-active, -.attachments-leave-active { - overflow: hidden; - transition: - max-height 0.2s ease, - margin 0.2s ease, - padding 0.2s ease, - opacity 0.16s ease, - transform 0.2s ease; -} - -.attachments-enter-from, -.attachments-leave-to { - max-height: 0; - margin-top: 0; - padding-top: 0; - padding-bottom: 0; - opacity: 0; - transform: translateY(6px); -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(10px); - } - - to { - opacity: 1; - transform: translateY(0); - } -} - -@media (max-width: 768px) { - .input-area { - padding: 8px 0 0 !important; - border-top: 0; - } - - .input-container { - display: flex !important; - flex-direction: column; - justify-content: center; - width: calc(100% - 20px) !important; - max-width: 100% !important; - min-height: 64px; - margin: 0 10px calc(8px + env(safe-area-inset-bottom)) !important; - padding: 6px 8px 6px 10px !important; - overflow: hidden; - border: 1px solid rgba(var(--v-theme-on-surface), 0.14) !important; - border-radius: 999px !important; - background: #fff !important; - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08) !important; - } - - .input-container.is-multiline { - justify-content: flex-start; - min-height: 128px; - padding: 10px !important; - border-radius: 26px !important; - } - - .input-container.has-attachments { - justify-content: flex-start; - min-height: 124px; - padding: 10px !important; - border-radius: 26px !important; - } - - .input-area.is-dark .input-container { - border-color: rgba(255, 255, 255, 0.16) !important; - background: #2d2d2d !important; - box-shadow: none !important; - } - - .composer-row { - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - grid-template-areas: "left field right"; - min-height: 52px; - row-gap: 0; - column-gap: 8px; - align-items: center; - } - - .input-container.is-multiline .composer-row { - grid-template-columns: auto minmax(0, 1fr) auto; - grid-template-areas: - "field field field" - "left . right"; - min-height: auto; - row-gap: 4px; - } - - .input-field-shell { - min-height: 52px; - align-items: center; - } - - .input-container.is-multiline .input-field-shell { - min-height: 56px; - align-items: flex-start; - } - - .input-left-actions, - .input-right-actions { - margin-top: 0 !important; - align-items: center !important; - } - - .input-right-actions { - gap: 6px; - } - - .input-outline-control { - width: 38px !important; - height: 38px !important; - min-width: 38px !important; - border: 0 !important; - border-color: transparent !important; - border-radius: 50% !important; - } - - .chat-text-input, - .chat-textarea { - min-height: 52px !important; - max-height: 132px !important; - border: 0 !important; - border-radius: 0 !important; - background: transparent !important; - box-shadow: none !important; - font-size: 18px !important; - } - - .chat-text-input { - height: 52px !important; - padding: 0 2px !important; - line-height: normal !important; - overflow: hidden; - } - - .chat-textarea { - max-height: min(42vh, 220px) !important; - padding: 4px 10px 2px !important; - line-height: 24px !important; - overflow-y: auto; - } - - .chat-text-input::placeholder, - .chat-textarea::placeholder { - color: rgba(var(--v-theme-on-surface), 0.56); - opacity: 1; - } - - .input-icon-btn { - width: 38px !important; - height: 38px !important; - min-width: 38px !important; - margin-right: 0; - } - - .input-action-btn { - width: 42px !important; - height: 42px !important; - min-width: 42px !important; - border-radius: 50% !important; - } - - .input-action-btn:not(:disabled) { - background: rgb(var(--v-theme-on-surface)) !important; - color: rgb(var(--v-theme-surface)) !important; - } - - .input-action-btn:disabled { - background: rgba(var(--v-theme-on-surface), 0.04) !important; - color: rgba(var(--v-theme-on-surface), 0.18) !important; - } - - :deep(.provider-chip) { - height: 38px !important; - min-height: 38px !important; - border-radius: 999px !important; - padding: 0 12px !important; - font-size: 14px !important; - border-color: rgba(var(--v-theme-on-surface), 0.18) !important; - background: transparent !important; - } - - .attachments-preview { - margin: 8px 16px 0; - gap: 8px; - } - - .input-container.has-attachments .attachments-preview { - margin: 0 0 8px; - } - - .attachment-card { - width: min(220px, calc(100vw - 28px)); - height: 54px; - } - - .image-preview { - width: 54px; - flex-basis: 54px; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/ChatMessageList.vue b/legacy-dashboard/src/components/chat/ChatMessageList.vue deleted file mode 100644 index 9825902c..00000000 --- a/legacy-dashboard/src/components/chat/ChatMessageList.vue +++ /dev/null @@ -1,1456 +0,0 @@ -<template> - <div - ref="listRoot" - class="chat-message-list" - :class="[`variant-${variant}`, { 'is-dark': isDark }]" - > - <div class="messages-list"> - <div - v-for="(msg, msgIndex) in messages" - :key="msg.id || `${msgIndex}-${msg.created_at || ''}`" - class="message-row" - :class="isUserMessage(msg) ? 'from-user' : 'from-bot'" - > - <v-avatar v-if="!isUserMessage(msg)" class="bot-avatar" :size="avatarSize"> - <v-progress-circular - v-if="isMessageStreaming(msg, msgIndex)" - class="bot-streaming-spinner" - indeterminate - size="22" - width="2" - /> - <span v-else class="bot-avatar-symbol" aria-hidden="true">✦</span> - </v-avatar> - - <div class="message-stack"> - <div - v-if="isUserMessage(msg) && userAttachmentParts(msg).length" - class="sent-attachments" - :class="{ 'images-only': hasImageOnlyAttachments(msg) }" - > - <template - v-for="(part, attachmentIndex) in userAttachmentParts(msg)" - :key="`${msgIndex}-attachment-${attachmentIndex}-${part.type}`" - > - <button - v-if="part.type === 'image'" - class="sent-attachment-card sent-image-card" - type="button" - @click="openImage(partUrl(part))" - > - <img :src="partUrl(part)" :alt="part.filename || 'image'" /> - </button> - - <div v-else class="sent-attachment-card sent-file-card"> - <div - class="sent-attachment-icon" - :style="{ - '--attachment-color': attachmentPresentation(part).color, - }" - > - <v-icon - class="sent-attachment-icon-symbol" - :icon="attachmentPresentation(part).icon" - size="24" - /> - <span class="sent-attachment-ext"> - {{ attachmentPresentation(part).label }} - </span> - </div> - <span class="sent-attachment-name"> - {{ attachmentName(part) }} - </span> - <v-btn - v-if="part.type === 'file'" - icon="mdi-download" - size="x-small" - variant="text" - :loading=" - downloadingFiles.has( - part.attachment_id || - part.stored_filename || - part.filename || - '', - ) - " - @click="downloadPart(part)" - /> - </div> - </template> - </div> - - <div - v-if="shouldShowMessageBubble(msg)" - class="message-bubble" - :class="{ user: isUserMessage(msg), bot: !isUserMessage(msg) }" - @mouseup="handleMouseUp($event, msg)" - > - <div v-if="messageContent(msg).isLoading" class="loading-message"> - <span>{{ tm("message.loading") }}</span> - </div> - - <template v-else-if="isEditingMessage(msg)"> - <div class="inline-message-editor"> - <textarea - :value="editDraft" - class="inline-message-editor-input" - rows="2" - autofocus - @input=" - emit( - 'update:editDraft', - ($event.target as HTMLTextAreaElement).value, - ) - " - @keydown.esc="emit('cancelEdit')" - ></textarea> - <div class="inline-message-editor-actions"> - <v-btn - class="inline-message-editor-action" - size="small" - variant="text" - @click="emit('cancelEdit')" - > - {{ t("core.common.cancel") }} - </v-btn> - <v-btn - class="inline-message-editor-action" - size="small" - color="primary" - variant="tonal" - :loading="savingEdit" - @click="emit('saveEdit')" - > - {{ t("core.common.save") }} - </v-btn> - </div> - </div> - </template> - - <template v-else> - <template - v-for="(block, blockIndex) in renderBlocks(msg)" - :key="`${msgIndex}-block-${blockIndex}-${block.kind}`" - > - <ReasoningBlock - v-if="block.kind === 'thinking'" - :parts="block.parts" - :is-dark="isDark" - :initial-expanded="false" - :is-streaming="isMessageStreaming(msg, msgIndex)" - :has-non-reasoning-content=" - hasFollowingContentBlock(msg, blockIndex) - " - :open-in-sidebar="variant === 'main'" - @open="emit('openReasoning', { message: msg, blockIndex })" - /> - - <template v-else> - <template - v-for="(part, partIndex) in block.parts" - :key="`${msgIndex}-${blockIndex}-${partIndex}-${part.type}`" - > - <button - v-if="part.type === 'reply'" - class="reply-quote" - type="button" - @click="scrollToMessage(part.message_id)" - > - <v-icon size="15">mdi-reply</v-icon> - <span>{{ replyPreview(part.message_id, part.selected_text) }}</span> - </button> - - <div - v-else-if="part.type === 'plain' && isUserMessage(msg)" - class="plain-content" - > - {{ part.text || "" }} - </div> - - <div - v-else-if="part.type === 'plain' && messageThreads(msg).length" - class="threaded-message-content" - > - <ThreadedMarkdownMessagePart - :text="part.text || ''" - :threads="messageThreads(msg)" - :refs="resolvedMessageRefs(msg)" - :is-dark="isDark" - :custom-html-tags="customMarkdownTags" - :is-streaming="isMessageStreaming(msg, msgIndex)" - @open-thread="emit('openThread', $event)" - /> - </div> - - <MarkdownMessagePart - v-else-if="part.type === 'plain'" - :content="part.text || ''" - :refs="resolvedMessageRefs(msg)" - :is-dark="isDark" - :custom-html-tags="customMarkdownTags" - :is-streaming="isMessageStreaming(msg, msgIndex)" - /> - - <button - v-else-if="part.type === 'image'" - class="image-part" - type="button" - @click="openImage(partUrl(part))" - > - <img :src="partUrl(part)" :alt="part.filename || 'image'" /> - </button> - - <audio - v-else-if="part.type === 'record'" - class="audio-part" - controls - :src="partUrl(part)" - /> - - <video - v-else-if="part.type === 'video'" - class="video-part" - controls - :src="partUrl(part)" - /> - - <div - v-else-if="part.type === 'file'" - class="file-part" - :style="{ - '--attachment-color': attachmentPresentation(part).color, - }" - > - <v-icon - class="file-part-icon" - :icon="attachmentPresentation(part).icon" - size="24" - /> - <div class="file-part-meta"> - <span class="file-part-name"> - {{ attachmentName(part) }} - </span> - <span class="file-part-kind"> - {{ attachmentPresentation(part).label }} - </span> - </div> - <v-btn - class="file-part-action" - icon="mdi-download" - size="x-small" - variant="text" - :loading=" - downloadingFiles.has( - part.attachment_id || - part.stored_filename || - part.filename || - '', - ) - " - @click="downloadPart(part)" - /> - </div> - - <div v-else-if="part.type === 'tool_call'" class="tool-call-block"> - <template - v-for="tool in part.tool_calls || []" - :key="tool.id || tool.name" - > - <ToolCallItem v-if="isIPythonToolCall(tool)" :is-dark="isDark"> - <template #label> - <v-icon size="16">mdi-code-json</v-icon> - <span>{{ tool.name || "python" }}</span> - <span class="tool-call-inline-status"> - {{ toolCallStatusText(tool) }} - </span> - </template> - <template #details> - <IPythonToolBlock - :tool-call="normalizeToolCall(tool)" - :is-dark="isDark" - :show-header="false" - :force-expanded="true" - /> - </template> - </ToolCallItem> - <ToolCallCard - v-else - :tool-call="normalizeToolCall(tool)" - :is-dark="isDark" - /> - </template> - </div> - - <div v-else class="unknown-part"> - {{ formatJson(part) }} - </div> - </template> - </template> - </template> - </template> - </div> - - <div v-if="showMessageMeta(msg, msgIndex)" class="message-meta"> - <span v-if="msg.created_at">{{ formatTime(msg.created_at) }}</span> - <v-btn - v-if="canEditMessage(msg, msgIndex)" - icon="mdi-pencil-outline" - size="x-small" - variant="text" - @click="emit('openEdit', msg)" - /> - <RegenerateMenu - v-if="canRegenerateMessage(msg, msgIndex)" - @retry="emit('regenerate', msg)" - @retry-with-model="emit('regenerateWithModel', msg, $event)" - /> - <v-btn - v-if="enableCopy && !isUserMessage(msg)" - icon="mdi-content-copy" - size="x-small" - variant="text" - @click="copyMessage(msg)" - /> - <v-menu - v-if="messageContent(msg).agentStats" - location="bottom" - transition="none" - > - <template #activator="{ props: statsProps }"> - <v-btn - v-bind="statsProps" - icon="mdi-information-outline" - size="x-small" - variant="text" - /> - </template> - <v-card class="stats-card" elevation="4"> - <div - v-if="cachedInputTokens(messageContent(msg).agentStats) > 0" - class="stats-row" - > - <span>{{ tm("stats.cachedTokens") }}</span> - <strong>{{ - cachedInputTokens(messageContent(msg).agentStats) - }}</strong> - </div> - <div class="stats-row"> - <span>{{ tm("stats.inputTokens") }}</span> - <strong>{{ inputTokens(messageContent(msg).agentStats) }}</strong> - </div> - <div class="stats-row"> - <span>{{ tm("stats.outputTokens") }}</span> - <strong>{{ outputTokens(messageContent(msg).agentStats) }}</strong> - </div> - <div v-if="agentTtft(messageContent(msg).agentStats)" class="stats-row"> - <span>{{ tm("stats.ttft") }}</span> - <strong>{{ agentTtft(messageContent(msg).agentStats) }}</strong> - </div> - <div class="stats-row"> - <span>{{ tm("stats.duration") }}</span> - <strong>{{ agentDuration(messageContent(msg).agentStats) }}</strong> - </div> - </v-card> - </v-menu> - <StyledMenu - v-if="messageThreads(msg).length" - location="bottom" - transition="none" - no-border - > - <template #activator="{ props: threadMenuProps }"> - <button - v-bind="threadMenuProps" - class="message-thread-meta" - type="button" - > - <v-icon size="14">mdi-source-branch</v-icon> - <span>{{ threadCountLabel(messageThreads(msg).length) }}</span> - </button> - </template> - <v-list-item - v-for="thread in messageThreads(msg)" - :key="thread.thread_id" - class="styled-menu-item thread-menu-item" - rounded="md" - @click="emit('openThread', thread)" - > - <template #prepend> - <v-icon size="16">mdi-source-branch</v-icon> - </template> - <v-list-item-title class="thread-menu-title"> - {{ threadPreview(thread) }} - </v-list-item-title> - </v-list-item> - </StyledMenu> - <div v-if="messageRefs(msg).length" class="message-meta-refs"> - <ActionRef - :refs="resolvedMessageRefs(msg)" - @open-refs="handleOpenRefs" - /> - </div> - </div> - </div> - </div> - </div> - - <RefsSidebar - v-if="manageRefsSidebar" - v-model="refsSidebarOpen" - :refs="selectedRefs" - /> - - <v-overlay - v-model="imagePreview.visible" - class="image-preview-overlay" - scrim="rgba(0, 0, 0, 0.86)" - @click="closeImage" - > - <img - :src="imagePreview.url" - class="preview-image" - alt="preview" - @click.stop - /> - </v-overlay> - </div> -</template> - -<script setup lang="ts"> -import { computed, nextTick, reactive, ref } from "vue"; -import axios from "axios"; -import { fileApi } from "@/api/v1"; -import { setCustomComponents } from "markstream-vue"; -import "markstream-vue/index.css"; -import RegenerateMenu, { - type RegenerateModelSelection, -} from "@/components/chat/RegenerateMenu.vue"; -import ThreadedMarkdownMessagePart from "@/components/chat/ThreadedMarkdownMessagePart.vue"; -import ReasoningBlock from "@/components/chat/message_list_comps/ReasoningBlock.vue"; -import ToolCallCard from "@/components/chat/message_list_comps/ToolCallCard.vue"; -import ToolCallItem from "@/components/chat/message_list_comps/ToolCallItem.vue"; -import IPythonToolBlock from "@/components/chat/message_list_comps/IPythonToolBlock.vue"; -import RefsSidebar from "@/components/chat/message_list_comps/RefsSidebar.vue"; -import RefNode from "@/components/chat/message_list_comps/RefNode.vue"; -import ThreadNode from "@/components/chat/message_list_comps/ThreadNode.vue"; -import ActionRef from "@/components/chat/message_list_comps/ActionRef.vue"; -import MarkdownMessagePart from "@/components/chat/message_list_comps/MarkdownMessagePart.vue"; -import ThemeAwareMarkdownCodeBlock from "@/components/shared/ThemeAwareMarkdownCodeBlock.vue"; -import StyledMenu from "@/components/shared/StyledMenu.vue"; -import { - attachmentName, - attachmentPresentation, -} from "@/components/chat/attachmentPresentation"; -import { - displayParts as displayMessageParts, - messageBlocks as buildMessageBlocks, - type MessageDisplayBlock, -} from "@/composables/useMessages"; -import type { - ChatContent, - ChatRecord, - ChatThread, - MessagePart, -} from "@/composables/useMessages"; -import { useI18n, useModuleI18n } from "@/i18n/composables"; -import { copyToClipboard } from "@/utils/clipboard"; - -const props = withDefaults( - defineProps<{ - messages: ChatRecord[]; - isDark?: boolean; - isStreaming?: boolean; - variant?: "main" | "thread"; - enableEdit?: boolean; - enableRegenerate?: boolean; - enableThreadSelection?: boolean; - enableCopy?: boolean; - manageRefsSidebar?: boolean; - editingMessageId?: string | number | null; - editDraft?: string; - savingEdit?: boolean; - }>(), - { - isDark: false, - isStreaming: false, - variant: "main", - enableEdit: false, - enableRegenerate: false, - enableThreadSelection: false, - enableCopy: true, - manageRefsSidebar: true, - editingMessageId: null, - editDraft: "", - savingEdit: false, - }, -); - -const emit = defineEmits<{ - "update:editDraft": [value: string]; - openEdit: [message: ChatRecord]; - cancelEdit: []; - saveEdit: []; - regenerate: [message: ChatRecord]; - regenerateWithModel: [ - message: ChatRecord, - selection: RegenerateModelSelection, - ]; - selectBotText: [event: MouseEvent, message: ChatRecord]; - openThread: [thread: ChatThread]; - openReasoning: [payload: { message: ChatRecord; blockIndex: number }]; - openRefs: [refs: unknown]; -}>(); - -setCustomComponents("chat-message", { - ref: RefNode, - thread: ThreadNode, - code_block: ThemeAwareMarkdownCodeBlock, -}); - -const { t } = useI18n(); -const { tm } = useModuleI18n("features/chat"); -const customMarkdownTags = ["ref"]; -const downloadingFiles = ref(new Set<string>()); -const imagePreview = reactive({ visible: false, url: "" }); -const refsSidebarOpen = ref(false); -const selectedRefs = ref<Record<string, unknown> | null>(null); -const listRoot = ref<HTMLElement | null>(null); -const avatarSize = computed(() => (props.variant === "thread" ? 36 : 56)); - -function isUserMessage(message: ChatRecord) { - return messageContent(message).type === "user"; -} - -function messageContent(message: ChatRecord): ChatContent { - return message.content || { type: "bot", message: [] }; -} - -function messageParts(message: ChatRecord): MessagePart[] { - const parts = messageContent(message).message; - if (Array.isArray(parts)) return parts; - if (typeof parts === "string") return [{ type: "plain", text: parts }]; - return []; -} - -function isAttachmentPart(part: MessagePart) { - return ["image", "record", "video", "file"].includes(part.type); -} - -function userAttachmentParts(message: ChatRecord) { - if (!isUserMessage(message)) return []; - return messageParts(message).filter(isAttachmentPart); -} - -function hasImageOnlyAttachments(message: ChatRecord) { - const attachments = userAttachmentParts(message); - return ( - attachments.length > 0 && - attachments.every((part) => part.type === "image") - ); -} - -function bubbleParts(message: ChatRecord) { - if (!isUserMessage(message)) return displayMessageParts(messageContent(message)); - return messageParts(message).filter((part) => !isAttachmentPart(part)); -} - -function shouldShowMessageBubble(message: ChatRecord) { - return ( - !isUserMessage(message) || - isEditingMessage(message) || - messageContent(message).isLoading || - bubbleParts(message).length > 0 - ); -} - -function isMessageStreaming(message: ChatRecord, messageIndex: number) { - return ( - props.isStreaming && - !isUserMessage(message) && - messageIndex === props.messages.length - 1 - ); -} - -function isEditingMessage(message: ChatRecord) { - return ( - props.editingMessageId != null && - message.id != null && - String(props.editingMessageId) === String(message.id) - ); -} - -function canEditMessage(message: ChatRecord, messageIndex: number) { - return ( - props.enableEdit && - isUserMessage(message) && - messageIndex === latestEditableUserIndex() && - message.id != null && - !String(message.id).startsWith("local-") - ); -} - -function latestEditableUserIndex() { - for (let index = props.messages.length - 1; index >= 0; index -= 1) { - const message = props.messages[index]; - if ( - isUserMessage(message) && - message.id != null && - !String(message.id).startsWith("local-") - ) { - return index; - } - } - return -1; -} - -function canRegenerateMessage(message: ChatRecord, messageIndex: number) { - return ( - props.enableRegenerate && - !isUserMessage(message) && - messageIndex === props.messages.length - 1 && - !isMessageStreaming(message, messageIndex) && - Boolean(message.llm_checkpoint_id) - ); -} - -function showMessageMeta(message: ChatRecord, messageIndex: number) { - return ( - !messageContent(message).isLoading && - !isMessageStreaming(message, messageIndex) - ); -} - -function hasNonReasoningContent(message: ChatRecord) { - return renderBlocks(message).some((block) => block.kind === "content"); -} - -function renderBlocks(message: ChatRecord): MessageDisplayBlock[] { - if (isUserMessage(message)) { - const parts = bubbleParts(message); - return parts.length ? [{ kind: "content", parts }] : []; - } - return buildMessageBlocks(messageContent(message)); -} - -function hasFollowingContentBlock(message: ChatRecord, blockIndex: number) { - return renderBlocks(message) - .slice(blockIndex + 1) - .some((block) => block.kind === "content"); -} - -function handleMouseUp(event: MouseEvent, message: ChatRecord) { - if (props.enableThreadSelection && !isUserMessage(message)) { - emit("selectBotText", event, message); - } -} - -function messageThreads(message: ChatRecord) { - return message.threads || []; -} - -function threadCountLabel(count: number) { - return tm("thread.count", { count }); -} - -function threadPreview(thread: ChatThread) { - return truncate(thread.selected_text || tm("thread.title"), 48); -} - -function partUrl(part: MessagePart) { - if (part.embedded_url) return part.embedded_url; - if (part.embedded_file?.url) return part.embedded_file.url; - if (part.attachment_id) { - return fileApi.contentUrl(part.attachment_id); - } - const lookupFilename = part.stored_filename || part.filename; - if (lookupFilename) { - return fileApi.byNameUrl(lookupFilename); - } - return ""; -} - -function plainTextFromMessage(message: ChatRecord) { - return messageParts(message) - .filter((part) => part.type === "plain" && part.text) - .map((part) => part.text) - .join("\n"); -} - -function replyPreview(messageId?: string | number, fallback?: string) { - if (fallback) return truncate(fallback, 80); - const found = props.messages.find( - (message) => String(message.id) === String(messageId), - ); - const text = found ? plainTextFromMessage(found) : ""; - return text ? truncate(text, 80) : tm("reply.replyTo"); -} - -function truncate(value: string, max: number) { - return value.length > max ? `${value.slice(0, max)}...` : value; -} - -function scrollToMessage(messageId?: string | number) { - if (!messageId) return; - const index = props.messages.findIndex( - (message) => String(message.id) === String(messageId), - ); - if (index < 0) return; - nextTick(() => { - listRoot.value - ?.querySelectorAll(".message-row") - [index]?.scrollIntoView({ behavior: "smooth", block: "center" }); - }); -} - -function formatJson(value: unknown) { - if (typeof value === "string") { - const parsed = parseJsonSafe(value); - if (parsed !== value) return JSON.stringify(parsed, null, 2); - return value; - } - try { - return JSON.stringify(value, null, 2); - } catch { - return String(value ?? ""); - } -} - -function parseJsonSafe(value: unknown) { - if (typeof value !== "string") return value; - try { - return JSON.parse(value); - } catch { - return value; - } -} - -function messageRefs(message: ChatRecord) { - return resolvedMessageRefs(message).used; -} - -function resolvedMessageRefs(message: ChatRecord) { - return normalizeRefs(messageContent(message).refs); -} - -function normalizeRefs(refs: unknown) { - if (!refs) return { used: [] as Array<Record<string, unknown>> }; - const used = Array.isArray((refs as any)?.used) - ? (refs as any).used - : Array.isArray(refs) - ? refs - : []; - return { used: normalizeRefItems(used) }; -} - -function normalizeRefItems(items: unknown[]) { - return items - .map((item: any) => ({ - index: item?.index, - title: item?.title || item?.url || tm("refs.title"), - url: item?.url, - snippet: item?.snippet, - favicon: item?.favicon, - })) - .filter((item) => item.url); -} - -function handleOpenRefs(refs: unknown) { - if (!props.manageRefsSidebar) { - emit("openRefs", refs); - return; - } - selectedRefs.value = - refs && typeof refs === "object" ? (refs as Record<string, unknown>) : null; - refsSidebarOpen.value = true; -} - -function normalizeToolCall(tool: Record<string, unknown>) { - const normalized = { ...tool }; - normalized.args = normalized.args ?? normalized.arguments ?? {}; - normalized.ts = normalized.ts ?? Date.now() / 1000; - if (normalized.result && typeof normalized.result === "object") { - normalized.result = JSON.stringify(normalized.result, null, 2); - } - return normalized; -} - -function isIPythonToolCall(tool: Record<string, unknown>) { - const name = String(tool.name || "").toLowerCase(); - return name.includes("python") || name.includes("ipython"); -} - -function toolCallStatusText(tool: Record<string, unknown>) { - if (tool.finished_ts) return tm("toolStatus.done"); - return tm("toolStatus.running"); -} - -async function copyMessage(message: ChatRecord) { - const text = plainTextFromMessage(message); - if (!text) return; - await copyToClipboard(text); -} - -async function downloadPart(part: MessagePart) { - const key = part.attachment_id || part.stored_filename || part.filename || ""; - if (!key) return; - downloadingFiles.value = new Set(downloadingFiles.value).add(key); - try { - const response = await axios.get(partUrl(part), { responseType: "blob" }); - const url = URL.createObjectURL(response.data); - const anchor = document.createElement("a"); - anchor.href = url; - anchor.download = part.filename || "file"; - anchor.click(); - URL.revokeObjectURL(url); - } finally { - const next = new Set(downloadingFiles.value); - next.delete(key); - downloadingFiles.value = next; - } -} - -function openImage(url: string) { - imagePreview.url = url; - imagePreview.visible = true; -} - -function closeImage() { - imagePreview.visible = false; - imagePreview.url = ""; -} - -function formatTime(value: string) { - const date = new Date(value); - if (Number.isNaN(date.getTime())) return ""; - return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); -} - -function inputTokens(stats: any) { - const usage = stats?.token_usage || {}; - return usage.input_other || 0; -} - -function outputTokens(stats: any) { - return stats?.token_usage?.output || 0; -} - -function cachedInputTokens(stats: any) { - return stats?.token_usage?.input_cached || 0; -} - -function agentDuration(stats: any) { - const directDuration = readPositiveNumber(stats, [ - "duration", - "total_duration", - ]); - if (directDuration !== null) return formatDuration(directDuration); - - const startTime = readPositiveNumber(stats, ["start_time"]); - const endTime = readPositiveNumber(stats, ["end_time"]); - if (startTime === null || endTime === null || endTime < startTime) return "-"; - return formatDuration(endTime - startTime); -} - -function agentTtft(stats: any) { - const ttft = readPositiveNumber(stats, [ - "time_to_first_token", - "ttft", - "first_token_latency", - ]); - if (ttft === null) return ""; - return formatDuration(ttft); -} - -function readPositiveNumber(source: any, keys: string[]) { - for (const key of keys) { - const value = Number(source?.[key]); - if (Number.isFinite(value) && value > 0) return value; - } - return null; -} - -function formatDuration(seconds: number) { - if (seconds < 1) return `${Math.round(seconds * 1000)}ms`; - if (seconds < 60) return `${seconds.toFixed(1)}s`; - const minutes = Math.floor(seconds / 60); - const restSeconds = Math.round(seconds % 60); - return `${minutes}m ${restSeconds}s`; -} -</script> - -<style scoped> -.chat-message-list { - --chat-border: rgba(var(--v-border-color), 0.16); - --chat-muted: rgba(var(--v-theme-on-surface), 0.62); - width: 100%; - color: rgb(var(--v-theme-on-surface)); -} - -.chat-message-list.is-dark { - --chat-border: rgba(255, 255, 255, 0.1); -} - -.messages-list { - display: flex; - flex-direction: column; - gap: 22px; -} - -.message-row { - display: flex; - gap: 10px; - max-width: 100%; -} - -.message-row.from-user { - justify-content: flex-end; -} - -.message-stack { - display: flex; - flex-direction: column; - max-width: min(760px, 82%); -} - -.from-user .message-stack { - align-items: flex-end; - max-width: 60%; -} - -.sent-attachments { - display: flex; - max-width: 100%; - gap: 10px; - margin-bottom: 8px; - padding: 2px 2px 4px; - overflow-x: auto; - overflow-y: hidden; - scrollbar-width: thin; -} - -.sent-attachment-card { - --attachment-color: #607d8b; - position: relative; - display: inline-flex; - flex: 0 0 auto; - align-items: center; - justify-content: flex-start; - gap: 8px; - height: 60px; - overflow: hidden; - border: 0; - border-radius: 8px; - background: rgba(var(--v-theme-on-surface), 0.055); - color: rgb(var(--v-theme-on-surface)); -} - -.sent-image-card { - width: 64px; - padding: 0; - border: 0; - cursor: zoom-in; -} - -.sent-image-card img { - width: 100%; - height: 100%; - border-radius: 8px; - object-fit: cover; -} - -.sent-attachments.images-only { - max-width: min(420px, 100%); -} - -.sent-attachments.images-only .sent-image-card { - width: 180px; - height: 180px; -} - -.sent-attachments.images-only .sent-image-card img { - object-fit: cover; - background: rgba(var(--v-theme-on-surface), 0.04); -} - -.sent-file-card { - width: 236px; - padding: 8px 10px; - background: rgba(var(--v-theme-on-surface), 0.055); - background: linear-gradient( - 90deg, - color-mix(in srgb, var(--attachment-color) 14%, transparent), - rgba(var(--v-theme-on-surface), 0.055) 62% - ); -} - -.sent-attachment-icon { - display: inline-flex; - flex-shrink: 0; - min-width: 36px; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 1px; - color: var(--attachment-color); -} - -.sent-attachment-icon-symbol { - color: var(--attachment-color); -} - -.sent-attachment-ext { - max-width: 58px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 10px; - font-weight: 700; - line-height: 12px; - color: var(--attachment-color); -} - -.sent-attachment-name { - min-width: 0; - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 13px; - line-height: 18px; -} - -.bot-avatar { - margin-top: 2px; - color: rgb(var(--v-theme-primary)); - user-select: none; -} - -.bot-streaming-spinner { - margin-top: -4px; -} - -.bot-avatar-symbol { - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 30px; - margin-top: -2px; - line-height: 0; - pointer-events: none; - user-select: none; -} - -.message-bubble { - border-radius: 8px; - padding: 10px 14px; - line-height: 1.65; - overflow-wrap: anywhere; -} - -.message-bubble.user { - color: var(--v-theme-primaryText); - padding: 12px 18px; - font-size: 15px; - max-width: 100%; - border-radius: 1.5rem; - background: rgba(var(--v-theme-primary), 0.12); -} - -.message-bubble.bot { - background: transparent; - padding-left: 0; -} - -.plain-content { - white-space: pre-wrap; -} - -.inline-message-editor { - display: flex; - flex-direction: column; - gap: 6px; - min-width: min(420px, 72vw); -} - -.inline-message-editor-input { - width: 100%; - min-height: 0; - max-height: 220px; - padding: 0; - border: 0; - outline: 0; - resize: vertical; - background: transparent; - color: inherit; - font: inherit; - line-height: 1.65; - white-space: pre-wrap; -} - -.inline-message-editor-actions { - display: flex; - justify-content: flex-end; - gap: 6px; - margin-top: 2px; -} - -.inline-message-editor-action { - min-height: 34px; - padding: 0 14px; - border-radius: 14px; -} - -.loading-message { - display: flex; - align-items: center; - gap: 8px; - margin-top: 4px; - color: var(--chat-muted); -} - -.chat-message-list :deep(.markdown-content p) { - margin: 0.25rem 0; -} - -.unknown-part { - max-width: 100%; - overflow-x: auto; - border-radius: 8px; - padding: 10px; - background: rgba(var(--v-theme-on-surface), 0.06); - font-size: 13px; - line-height: 1.5; -} - -.reply-quote { - width: 100%; - display: flex; - align-items: center; - gap: 6px; - border: 0; - border-left: 3px solid rgb(var(--v-theme-primary)); - border-radius: 6px; - padding: 7px 9px; - margin-bottom: 8px; - background: rgba(var(--v-theme-primary), 0.08); - color: inherit; - cursor: pointer; - text-align: left; -} - -.image-part { - display: block; - border: 0; - padding: 0; - margin-top: 8px; - background: transparent; - cursor: zoom-in; -} - -.image-part img { - max-width: min(420px, 100%); - max-height: 360px; - border-radius: 8px; - object-fit: contain; -} - -.audio-part, -.video-part { - display: block; - max-width: 100%; - margin-top: 8px; -} - -.video-part { - max-height: 360px; - border-radius: 8px; -} - -.file-part { - --attachment-color: #607d8b; - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: 10px; - width: min(420px, 100%); - margin-top: 8px; - padding: 9px 8px 9px 10px; - border: 0; - border-radius: 8px; - background: rgba(var(--v-theme-on-surface), 0.055); - background: linear-gradient( - 90deg, - color-mix(in srgb, var(--attachment-color) 13%, transparent), - rgba(var(--v-theme-on-surface), 0.055) 58% - ); -} - -.file-part-icon { - color: var(--attachment-color); -} - -.file-part-meta { - min-width: 0; - display: flex; - flex-direction: column; - gap: 1px; -} - -.file-part-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 14px; - font-weight: 500; - line-height: 20px; -} - -.file-part-kind { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--attachment-color); - font-size: 11px; - font-weight: 700; - line-height: 14px; -} - -.file-part-action { - color: rgb(var(--v-theme-on-surface)); - opacity: 0.72; -} - -.file-part:hover .file-part-action { - opacity: 1; -} - -.tool-call-block { - margin: 8px 0; - display: flex; - flex-direction: column; - gap: 6px; -} - -.message-bubble.bot - > .tool-call-block:first-child - :deep(.tool-call-card:first-child) { - margin-top: 0; -} - -.tool-call-inline-status { - color: var(--chat-muted); - font-size: 12px; -} - -.message-meta { - display: flex; - align-items: center; - gap: 2px; - min-height: 24px; - color: var(--chat-muted); - font-size: 12px; -} - -.message-meta-refs { - display: flex; - align-items: center; -} - -.message-thread-meta { - min-height: 24px; - display: inline-flex; - align-items: center; - gap: 4px; - border: 0; - border-radius: 8px; - padding: 0 6px; - background: transparent; - color: inherit; - font: inherit; - font-size: 12px; - line-height: 24px; - cursor: pointer; -} - -.message-thread-meta:hover { - background: rgba(var(--v-theme-on-surface), 0.06); -} - -.thread-menu-item { - max-width: min(320px, 72vw); -} - -.thread-menu-title { - max-width: 240px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 13px; -} - -.from-user .message-meta { - justify-content: flex-end; -} - -.stats-card { - min-width: 150px; - padding: 8px 10px; -} - -.stats-row { - display: flex; - justify-content: space-between; - margin-bottom: 4px; - padding: 2px 0; - font-size: 13px; - line-height: 1.35; -} - -.stats-row span { - color: var(--chat-muted); -} - -.stats-row strong { - font-size: 12px; - font-weight: 600; -} - -.threaded-message-content { - color: inherit; -} - -.image-preview-overlay { - display: flex; - align-items: center; - justify-content: center; -} - -.preview-image { - max-width: min(92vw, 1200px); - max-height: 90vh; - object-fit: contain; - cursor: zoom-out; -} - -.variant-thread .messages-list { - gap: 14px; -} - -.variant-thread .message-stack { - max-width: min(300px, 86%); -} - -.variant-thread .from-user .message-stack { - max-width: 78%; -} - -.variant-thread .bot-avatar-symbol { - font-size: 24px; -} - -.variant-thread .from-bot .bot-avatar { - display: none; -} - -.variant-thread .from-bot .message-stack { - max-width: 100%; -} - -.variant-thread .message-bubble { - padding: 9px 12px; - border-radius: 18px; - font-size: 14px; -} - -.variant-thread .message-bubble.user { - border: 1px solid rgba(var(--v-theme-on-surface), 0.1); - background: rgb(var(--v-theme-on-surface)); - color: rgb(var(--v-theme-surface)); -} - -.variant-thread .message-bubble.bot { - border: 0; - background: transparent; - padding-left: 12px; -} - -@media (max-width: 760px) { - .messages-list { - gap: 18px; - } - - .message-row.from-bot { - flex-direction: column; - gap: 2px; - } - - .message-row.from-bot .bot-avatar { - display: none; - } - - .message-row.from-bot .message-stack { - max-width: 100%; - } - - .message-stack { - max-width: 88%; - } - - .from-user .message-stack { - max-width: 82%; - } - - .sent-file-card { - width: min(220px, calc(100vw - 28px)); - height: 58px; - } - - .sent-image-card { - width: 58px; - height: 58px; - } - - .sent-attachments.images-only .sent-image-card { - width: min(180px, calc(100vw - 52px)); - height: min(180px, calc(100vw - 52px)); - } - - .message-bubble { - padding: 9px 12px; - } - - .message-bubble.user { - padding: 10px 14px; - } - - .inline-message-editor { - min-width: min(100%, calc(100vw - 36px)); - } - - .image-part img { - max-width: min(100%, calc(100vw - 36px)); - max-height: 300px; - } - - .video-part { - max-height: 300px; - } - - .variant-thread .messages-list { - gap: 12px; - } - - .variant-thread .message-stack, - .variant-thread .from-bot .message-stack { - max-width: 100%; - } - - .variant-thread .from-user .message-stack { - max-width: 82%; - } - - .variant-thread .message-bubble.bot { - padding-left: 0; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/ChatUILogo.vue b/legacy-dashboard/src/components/chat/ChatUILogo.vue deleted file mode 100644 index bdbb248f..00000000 --- a/legacy-dashboard/src/components/chat/ChatUILogo.vue +++ /dev/null @@ -1,23 +0,0 @@ -<template> - <svg - viewBox="0 0 24 24" - aria-hidden="true" - focusable="false" - xmlns="http://www.w3.org/2000/svg" - > - <g transform="matrix(0.048 0 0 0.048 1.8 0.16)"> - <g transform="translate(0.8 0.9)"> - <g transform="translate(0 32)"> - <path - d="m246.3 328.1-17.8 41.2c-6.4 14.8-26.9 14.8-33.3 0l-17.8-41.2c-14.9-34.2-41.8-61.4-75.3-76.3l-48.8-21.6c-14.7-6.5-14.7-27.9 0-34.4l47.2-21c34.4-15.3 61.7-43.5 76.4-78.8l18-43.7c6.3-15.2 27.3-15.2 33.6 0l18 43.7c14.7 35.3 42 63.6 76.4 78.8l47.2 21c14.7 6.5 14.7 27.9 0 34.4l-48.8 21.6c-33.5 14.8-60.4 42.1-75.3 76.2z" - fill="#2f86bd" - transform="translate(0 35)" /> - <path - d="m402.2 449.3-5.3 12.2c-3.5 7.9-14.4 7.9-17.9 0l-5.3-12.2c-8.4-19.3-23.6-34.6-42.4-43l-15.4-6.9c-7.9-3.5-7.9-14.9 0-18.4l14.5-6.5c19.4-8.6 34.8-24.5 43.1-44.5l5.4-13.1c3.4-8.1 14.6-8.1 18 0l5.4 13.1c8.3 19.9 23.7 35.8 43.1 44.5l14.5 6.5c7.9 3.5 7.9 14.9 0 18.4l-15.4 6.9c-19 8.3-34.1 23.7-42.5 43z" - fill="#2f86bd" - transform="matrix(0.95 0 0 0.95 22 -278)" /> - </g> - </g> - </g> - </svg> -</template> diff --git a/legacy-dashboard/src/components/chat/CommandSuggestion.vue b/legacy-dashboard/src/components/chat/CommandSuggestion.vue deleted file mode 100644 index 87035ef0..00000000 --- a/legacy-dashboard/src/components/chat/CommandSuggestion.vue +++ /dev/null @@ -1,273 +0,0 @@ -<template> - <div - v-if="visible && filteredCommands.length > 0" - class="command-suggestion-panel" - :class="{ 'is-dark': isDark }" - :style="panelStyle" - > - <div class="command-suggestion-list"> - <div - v-for="(cmd, index) in filteredCommands" - :key="`${cmd.handler_full_name}:${cmd.effective_command}`" - class="command-suggestion-item" - :class="{ active: index === selectedIndex }" - @click="handleSelect(index)" - @mouseenter="handleMouseEnter(index)" - @mousemove="handleMouseMove" - @mouseleave="handleMouseLeave" - > - <div class="command-suggestion-main"> - <span class="command-name">{{ cmd.effective_command }}</span> - <span v-if="cmd.plugin_display_name" class="command-plugin"> - {{ cmd.plugin_display_name }} - </span> - </div> - <div v-if="cmd.description" class="command-description"> - {{ cmd.description }} - </div> - </div> - </div> - <div class="command-suggestion-hint"> - <span>↑↓ {{ tm("commandSuggestion.navigate") }}</span> - <span>Enter {{ tm("commandSuggestion.select") }}</span> - <span>Esc {{ tm("commandSuggestion.close") }}</span> - </div> - </div> - <!-- Tooltip: 鼠标悬停时显示完整用途 --> - <Teleport to="body"> - <div - v-if="tooltip.visible" - class="command-tooltip" - :class="{ 'is-dark': isDark }" - :style="tooltipStyle" - > - {{ tooltip.text }} - </div> - </Teleport> -</template> - -<script setup lang="ts"> -import { computed, reactive } from "vue"; -import { useModuleI18n } from "@/i18n/composables"; - -export interface SuggestionCommand { - handler_full_name: string; - effective_command: string; - description: string; - plugin_display_name: string | null; - enabled: boolean; - reserved: boolean; -} - -interface Props { - visible: boolean; - commands: SuggestionCommand[]; - selectedIndex: number; - isDark: boolean; - caretPosition?: { left: number; top: number } | null; -} - -const props = withDefaults(defineProps<Props>(), { - caretPosition: null, -}); - -const emit = defineEmits<{ - select: [command: SuggestionCommand]; - updateSelectedIndex: [index: number]; -}>(); - -const { tm } = useModuleI18n("features/chat"); - -const filteredCommands = computed(() => props.commands); - -// Tooltip 状态:鼠标悬停在指令上时显示完整用途 -const tooltip = reactive({ - visible: false, - text: "", - x: 0, - y: 0, -}); - -const tooltipStyle = computed(() => ({ - position: "fixed" as const, - left: `${tooltip.x + 12}px`, - top: `${tooltip.y + 12}px`, - zIndex: 10000, -})); - -const panelStyle = computed(() => { - if (props.caretPosition) { - return { - position: "absolute" as const, - left: `${props.caretPosition.left}px`, - bottom: `${props.caretPosition.top + 8}px`, - zIndex: 1000, - }; - } - return { - position: "absolute" as const, - left: "18px", - bottom: "100%", - marginBottom: "8px", - zIndex: 1000, - }; -}); - -function handleSelect(index: number) { - const cmd = props.commands[index]; - if (cmd) { - emit("select", cmd); - } -} - -function handleMouseEnter(index: number) { - emit("updateSelectedIndex", index); - // 显示 tooltip - const cmd = props.commands[index]; - if (cmd?.description) { - tooltip.text = cmd.description; - tooltip.visible = true; - } -} - -function handleMouseMove(e: MouseEvent) { - tooltip.x = e.clientX; - tooltip.y = e.clientY; -} - -function handleMouseLeave() { - tooltip.visible = false; -} -</script> - -<style scoped> -.command-suggestion-panel { - background: #ffffff; - border: 1px solid #e0e0e0; - border-radius: 12px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); - max-height: 280px; - width: 320px; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.command-suggestion-panel.is-dark { - background: #1e1e1e; - border-color: #404040; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4); -} - -.command-suggestion-list { - overflow-y: auto; - flex: 1; - padding: 4px; -} - -.command-suggestion-item { - padding: 8px 12px; - border-radius: 8px; - cursor: pointer; - transition: background-color 0.15s ease; -} - -.command-suggestion-item:hover, -.command-suggestion-item.active { - background-color: rgba(var(--v-theme-primary), 0.1); -} - -.is-dark .command-suggestion-item:hover, -.is-dark .command-suggestion-item.active { - background-color: rgba(255, 255, 255, 0.08); -} - -.command-suggestion-main { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; -} - -.command-name { - font-family: "Fira Code", "Consolas", monospace; - font-size: 13px; - font-weight: 600; - color: rgb(var(--v-theme-primary)); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.command-plugin { - font-size: 11px; - color: #888; - background: rgba(0, 0, 0, 0.05); - padding: 1px 6px; - border-radius: 4px; - white-space: nowrap; - flex-shrink: 0; -} - -.is-dark .command-plugin { - color: #aaa; - background: rgba(255, 255, 255, 0.08); -} - -.command-description { - font-size: 12px; - color: #666; - margin-top: 2px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.is-dark .command-description { - color: #999; -} - -.command-suggestion-hint { - display: flex; - gap: 12px; - padding: 6px 12px; - border-top: 1px solid #f0f0f0; - font-size: 11px; - color: #aaa; - flex-shrink: 0; -} - -.is-dark .command-suggestion-hint { - border-top-color: #333; - color: #666; -} - -.command-suggestion-hint span { - white-space: nowrap; -} -</style> - -<!-- 非 scoped 样式:tooltip 通过 Teleport 渲染到 body,scoped 无法生效 --> -<style> -.command-tooltip { - max-width: 360px; - padding: 8px 12px; - background: #ffffff; - border: 1px solid #e0e0e0; - border-radius: 8px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - font-size: 13px; - color: #333; - line-height: 1.5; - word-break: break-word; - pointer-events: none; - white-space: normal; -} - -.command-tooltip.is-dark { - background: #2d2d2d; - border-color: #404040; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.5); - color: #e0e0e0; -} -</style> diff --git a/legacy-dashboard/src/components/chat/ConfigSelector.vue b/legacy-dashboard/src/components/chat/ConfigSelector.vue deleted file mode 100644 index 8f8a1557..00000000 --- a/legacy-dashboard/src/components/chat/ConfigSelector.vue +++ /dev/null @@ -1,319 +0,0 @@ -<template> - <div> - <v-list-item - class="styled-menu-item" - rounded="md" - @click="openDialog" - :disabled="loadingConfigs || saving" - > - <template v-slot:prepend> - <v-icon icon="mdi-cog-outline" size="small"></v-icon> - </template> - <v-list-item-title> - {{ tm('config.title') }} - </v-list-item-title> - <v-list-item-subtitle class="text-caption"> - {{ selectedConfigLabel }} - </v-list-item-subtitle> - <template v-slot:append> - <v-icon icon="mdi-chevron-right" size="small" class="text-medium-emphasis"></v-icon> - </template> - </v-list-item> - - <v-dialog v-model="dialog" max-width="480"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center justify-space-between"> - <span>选择配置文件</span> - <v-btn icon variant="text" @click="closeDialog"> - <v-icon>mdi-close</v-icon> - </v-btn> - </v-card-title> - <v-card-text> - <div v-if="loadingConfigs" class="text-center py-6"> - <v-progress-circular indeterminate color="primary"></v-progress-circular> - </div> - - <v-list v-else class="config-list" density="comfortable"> - <v-list-item - v-for="config in configOptions" - :key="config.id" - :active="tempSelectedConfig === config.id" - rounded="lg" - variant="text" - @click="tempSelectedConfig = config.id" - > - <v-list-item-title>{{ config.name }}</v-list-item-title> - <v-list-item-subtitle class="text-caption text-grey"> - {{ config.id }} - </v-list-item-subtitle> - <template #append> - <v-icon v-if="tempSelectedConfig === config.id" color="primary">mdi-check</v-icon> - </template> - </v-list-item> - <div v-if="configOptions.length === 0" class="text-center text-body-2 text-medium-emphasis"> - 暂无可选配置,请先在配置页创建。 - </div> - </v-list> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="closeDialog">取消</v-btn> - <v-btn - color="primary" - variant="tonal" - @click="confirmSelection" - :disabled="!tempSelectedConfig" - :loading="saving" - > - 应用 - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - </div> -</template> - -<script setup lang="ts"> -import { computed, onMounted, ref, watch } from 'vue'; -import { configProfileApi, configRouteApi } from '@/api/v1'; -import { useToast } from '@/utils/toast'; -import { useModuleI18n } from '@/i18n/composables'; -import { - getStoredDashboardUsername, - getStoredSelectedChatConfigId, - setStoredSelectedChatConfigId -} from '@/utils/chatConfigBinding'; - -interface ConfigInfo { - id: string; - name: string; -} - -interface ConfigChangedPayload { - configId: string; - agentRunnerType: string; -} - -const props = withDefaults(defineProps<{ - sessionId?: string | null; - platformId?: string; - isGroup?: boolean; - initialConfigId?: string | null; -}>(), { - sessionId: null, - platformId: 'webchat', - isGroup: false, - initialConfigId: null -}); - -const emit = defineEmits<{ 'config-changed': [ConfigChangedPayload] }>(); - -const { tm } = useModuleI18n('features/chat'); - -const configOptions = ref<ConfigInfo[]>([]); -const loadingConfigs = ref(false); -const dialog = ref(false); -const tempSelectedConfig = ref(''); -const selectedConfigId = ref('default'); -const agentRunnerType = ref('local'); -const saving = ref(false); -const pendingSync = ref(false); -const routingEntries = ref<Array<{ pattern: string; confId: string }>>([]); -const configCache = ref<Record<string, string>>({}); - -const toast = useToast(); - -const normalizedSessionId = computed(() => { - const id = props.sessionId?.trim(); - return id ? id : null; -}); - -const hasActiveSession = computed(() => !!normalizedSessionId.value); - -const messageType = computed(() => (props.isGroup ? 'GroupMessage' : 'FriendMessage')); - -const username = computed(() => getStoredDashboardUsername()); - -const sessionKey = computed(() => { - if (!normalizedSessionId.value) { - return null; - } - return `${props.platformId}!${username.value}!${normalizedSessionId.value}`; -}); - -const targetUmo = computed(() => { - if (!sessionKey.value) { - return null; - } - return `${props.platformId}:${messageType.value}:${sessionKey.value}`; -}); - -const selectedConfigLabel = computed(() => { - const target = configOptions.value.find((item) => item.id === selectedConfigId.value); - return target?.name || selectedConfigId.value || 'default'; -}); - -function openDialog() { - tempSelectedConfig.value = selectedConfigId.value; - dialog.value = true; -} - -function closeDialog() { - dialog.value = false; -} - -async function fetchConfigList() { - loadingConfigs.value = true; - try { - const res = await configProfileApi.list(); - configOptions.value = (res.data.data?.info_list || []).map((item: any) => ({ - id: String(item.id || ''), - name: String(item.name || item.id || 'default') - })); - } catch (error) { - console.error('加载配置文件列表失败', error); - configOptions.value = []; - } finally { - loadingConfigs.value = false; - } -} - -async function fetchRoutingEntries() { - try { - const res = await configRouteApi.list(); - const routing = res.data.data?.routing || {}; - routingEntries.value = Object.entries(routing).map(([pattern, confId]) => ({ - pattern, - confId: confId as string - })); - } catch (error) { - console.error('获取配置路由失败', error); - routingEntries.value = []; - } -} - -function matchesPattern(pattern: string, target: string): boolean { - const parts = pattern.split(':'); - const targetParts = target.split(':'); - if (parts.length !== 3 || targetParts.length !== 3) { - return false; - } - return parts.every((part, index) => part === '' || part === '*' || part === targetParts[index]); -} - -function resolveConfigId(umo: string | null): string { - if (!umo) { - return 'default'; - } - for (const entry of routingEntries.value) { - if (matchesPattern(entry.pattern, umo)) { - return entry.confId; - } - } - return 'default'; -} - -async function getAgentRunnerType(confId: string): Promise<string> { - if (configCache.value[confId]) { - return configCache.value[confId]; - } - try { - const res = await configProfileApi.get(confId); - const config = ((res.data.data as any).config || {}) as any; - const type = config?.provider_settings?.agent_runner_type || 'local'; - configCache.value[confId] = type; - return type; - } catch (error) { - console.error('获取配置文件详情失败', error); - return 'local'; - } -} - -async function setSelection(confId: string) { - const normalized = confId || 'default'; - selectedConfigId.value = normalized; - const runnerType = await getAgentRunnerType(normalized); - agentRunnerType.value = runnerType; - emit('config-changed', { - configId: normalized, - agentRunnerType: runnerType - }); -} - -async function applySelectionToBackend(confId: string): Promise<boolean> { - if (!targetUmo.value) { - pendingSync.value = true; - return true; - } - saving.value = true; - try { - await configRouteApi.upsert(targetUmo.value, { config_id: confId }); - const filtered = routingEntries.value.filter((entry) => entry.pattern !== targetUmo.value); - if (confId !== 'default') { - filtered.push({ pattern: targetUmo.value, confId }); - } - routingEntries.value = filtered; - return true; - } catch (error) { - const err = error as any; - console.error('更新配置文件失败', err); - toast.error(err?.response?.data?.message || '配置文件应用失败'); - return false; - } finally { - saving.value = false; - } -} - -async function confirmSelection() { - if (!tempSelectedConfig.value) { - return; - } - const previousId = selectedConfigId.value; - await setSelection(tempSelectedConfig.value); - setStoredSelectedChatConfigId(tempSelectedConfig.value); - const applied = await applySelectionToBackend(tempSelectedConfig.value); - if (!applied) { - setStoredSelectedChatConfigId(previousId); - await setSelection(previousId); - } - dialog.value = false; -} - -async function syncSelectionForSession() { - if (!targetUmo.value) { - pendingSync.value = true; - return; - } - if (pendingSync.value) { - pendingSync.value = false; - await applySelectionToBackend(selectedConfigId.value); - return; - } - await fetchRoutingEntries(); - const resolved = resolveConfigId(targetUmo.value); - await setSelection(resolved); - setStoredSelectedChatConfigId(resolved); -} - -watch( - () => [props.sessionId, props.platformId, props.isGroup], - async () => { - await syncSelectionForSession(); - } -); - -onMounted(async () => { - await fetchConfigList(); - const stored = props.initialConfigId || getStoredSelectedChatConfigId(); - selectedConfigId.value = stored; - await setSelection(stored); - await syncSelectionForSession(); -}); -</script> - -<style scoped> -.config-list { - max-height: 360px; - overflow-y: auto; -} -</style> diff --git a/legacy-dashboard/src/components/chat/LiveMode.vue b/legacy-dashboard/src/components/chat/LiveMode.vue deleted file mode 100644 index 654ffb49..00000000 --- a/legacy-dashboard/src/components/chat/LiveMode.vue +++ /dev/null @@ -1,682 +0,0 @@ -<template> - <div class="live-mode-container"> - <div class="header-controls"> - <v-btn icon="mdi-close" @click="handleClose" flat variant="text" /> - <v-btn :icon="isCodeMode ? 'mdi-code-tags-check' : 'mdi-code-tags'" @click="toggleCodeMode" flat - variant="text" :color="isCodeMode ? 'primary' : ''" /> - <v-btn :icon="isNervousMode ? 'mdi-emoticon-confused' : 'mdi-emoticon-confused-outline'" - @click="toggleNervousMode" flat variant="text" :color="isNervousMode ? 'primary' : ''" /> - </div> - - <span style="color: gray; padding-left: 16px;">We're developing Astr Live Mode on ChatUI & Desktop right now. Stay tuned!</span> - - <div class="live-mode-content"> - <div class="center-circle-container" @click="handleCircleClick"> - <!-- 爆炸效果层 --> - <div v-if="isExploding" class="explosion-wave"></div> - - <SiriOrb :energy="orbEnergy" :mode="isActive ? orbMode : 'idle'" :is-dark="isDark" - :code-mode="isCodeMode" :nervous-mode="isNervousMode" class="siri-orb" /> - </div> - <div class="status-text"> - {{ statusText }} - </div> - <div class="messages-container" v-if="messages.length > 0"> - <div v-for="(msg, index) in messages" :key="index" class="message-item" :class="msg.type"> - <div class="message-content"> - {{ msg.text }} - </div> - </div> - </div> - - <div class="metrics-container" v-if="Object.keys(metrics).length > 0"> - <span v-if="metrics.wav_assemble_time">WAV Assemble: {{ (metrics.wav_assemble_time * 1000).toFixed(0) - }}ms</span> - <span v-if="metrics.llm_ttft">LLM First Token Latency: {{ (metrics.llm_ttft * 1000).toFixed(0) - }}ms</span> - <span v-if="metrics.llm_total_time">LLM Total Latency: {{ (metrics.llm_total_time * 1000).toFixed(0) - }}ms</span> - <span v-if="metrics.tts_first_frame_time">TTS First Frame Latency: {{ (metrics.tts_first_frame_time * - 1000).toFixed(0) }}ms</span> - <span v-if="metrics.tts_total_time">TTS Total Larency: {{ (metrics.tts_total_time * 1000).toFixed(0) - }}ms</span> - <span v-if="metrics.speak_to_first_frame">Speak -> First TTS Frame: {{ (metrics.speak_to_first_frame * - 1000).toFixed(0) }}ms</span> - <span v-if="metrics.wav_to_tts_total_time">Speak -> End: {{ (metrics.wav_to_tts_total_time * - 1000).toFixed(0) }}ms</span> - <span v-if="metrics.stt">STT Provider: {{ metrics.stt }}</span> - <span v-if="metrics.tts">TTS Provider: {{ metrics.tts }}</span> - <span v-if="metrics.chat_model">Chat Model: {{ metrics.chat_model }}</span> - </div> - </div> - </div> -</template> - -<script setup lang="ts"> -import { ref, computed, onBeforeUnmount, watch } from 'vue'; -import { useTheme } from 'vuetify'; -import { chatApi } from '@/api/v1'; -import { useVADRecording } from '@/composables/useVADRecording'; -import SiriOrb from './LiveOrb.vue'; - -const emit = defineEmits<{ - 'close': []; -}>(); - -const theme = useTheme(); -const isDark = computed(() => theme.global.current.value.dark); - -// 使用 VAD Recording composable -const vadRecording = useVADRecording(); - -// 状态 -const isActive = ref(false); // Live Mode 是否激活 -const isExploding = ref(false); // 是否正在展示爆炸动画 -const isCodeMode = ref(false); // 是否开启代码模式 -const isNervousMode = ref(false); // 是否开启紧张模式 -// 使用 VAD 提供的 isSpeaking 状态 -const isSpeaking = computed(() => vadRecording.isSpeaking.value); -const isListening = ref(false); // 是否在监听 -const isProcessing = ref(false); // 是否在处理 - -// WebSocket -let ws: WebSocket | null = null; - -// 音频相关 -let audioContext: AudioContext | null = null; -let analyser: AnalyserNode | null = null; -const botEnergy = ref(0); -let energyLoopId: number; -let isPlaying = ref(false); // UI 状态:是否正在播放 - -// 音频播放队列管理 -const rawAudioQueue: Uint8Array[] = []; // 待解码队列 -const audioBufferQueue: AudioBuffer[] = []; // 待播放队列 -let isDecoding = false; -let isPlayingAudio = false; // 内部状态:是否正在播放音频 -let currentSource: AudioBufferSourceNode | null = null; - - -// 消息历史 -const messages = ref<Array<{ type: 'user' | 'bot', text: string }>>([]); - -interface LiveMetrics { - wav_assemble_time?: number; - speak_to_first_frame?: number; - llm_ttft?: number; - llm_total_time?: number; - tts_first_frame_time?: number; - tts_total_time?: number; - wav_to_tts_total_time?: number; - stt?: string; - tts?: string; - chat_model?: string; -} -const metrics = ref<LiveMetrics>({}); - -// 当前语音片段标记 -let currentStamp = ''; - -const statusText = computed(() => { - if (!isActive.value) return 'Astr Live'; - if (isProcessing.value) return '正在处理...'; - if (isSpeaking.value) return '正在说话...'; - if (isListening.value) return '正在听...'; - return '准备就绪'; -}); - -const getIcon = computed(() => { - if (!isActive.value) return 'mdi-microphone'; - if (isSpeaking.value) return 'mdi-account-voice'; - if (isProcessing.value) return 'mdi-loading'; - return 'mdi-check'; -}); - -const getIconColor = computed(() => { - if (!isActive.value) return isDark.value ? 'white' : 'black'; - if (isSpeaking.value) return 'success'; - if (isProcessing.value) return 'warning'; - return 'primary'; -}); - -const orbEnergy = computed(() => { - if (isPlaying.value) return botEnergy.value; - if (isSpeaking.value || isListening.value) return vadRecording.audioEnergy.value; - return 0; -}); - -const orbMode = computed(() => { - if (isProcessing.value) return 'processing'; - if (isPlaying.value) return 'speaking'; - if (isSpeaking.value || isListening.value) return 'listening'; - return 'idle'; -}); - -async function handleCircleClick() { - if (!isActive.value) { - // 触发爆炸动画 - isExploding.value = true; - setTimeout(() => { - isExploding.value = false; - }, 1000); - - await startLiveMode(); - } else { - await stopLiveMode(); - } -} - -async function startLiveMode() { - try { - // 1. 建立 WebSocket 连接 - await connectWebSocket(); - - // 2. 初始化音频上下文(用于播放回复音频) - audioContext = new AudioContext({ sampleRate: 16000 }); - analyser = audioContext.createAnalyser(); - analyser.fftSize = 256; - analyser.smoothingTimeConstant = 0.5; - - // 启动能量更新循环 - updateBotEnergy(); - - // 3. 启动 VAD 录音 - await vadRecording.startRecording( - // onSpeechStart 回调 - () => { - console.log('[Live Mode] VAD 检测到开始说话'); - isListening.value = false; - currentStamp = generateStamp(); - - // 发送开始说话消息 - if (ws && ws.readyState === WebSocket.OPEN) { - metrics.value = {}; // Reset metrics - ws.send(JSON.stringify({ - t: 'start_speaking', - stamp: currentStamp - })); - } - }, - // onSpeechEnd 回调 - (audio: Float32Array) => { - console.log('[Live Mode] VAD 检测到语音结束,音频长度:', audio.length); - - // 将完整音频转换为 PCM16 并发送 - if (ws && ws.readyState === WebSocket.OPEN) { - const pcm16 = new Int16Array(audio.length); - for (let i = 0; i < audio.length; i++) { - const s = Math.max(-1, Math.min(1, audio[i])); - pcm16[i] = s < 0 ? s * 0x8000 : s * 0x7FFF; - } - - // Base64 编码(分块处理以避免堆栈溢出) - const uint8 = new Uint8Array(pcm16.buffer); - let base64 = ''; - const chunkSize = 0x8000; // 32KB chunks - for (let i = 0; i < uint8.length; i += chunkSize) { - const chunk = uint8.subarray(i, Math.min(i + chunkSize, uint8.length)); - base64 += String.fromCharCode.apply(null, Array.from(chunk)); - } - base64 = btoa(base64); - - // 发送完整音频 - ws.send(JSON.stringify({ - t: 'speaking_part', - data: base64 - })); - - // 发送结束说话消息 - ws.send(JSON.stringify({ - t: 'end_speaking', - stamp: currentStamp - })); - - isProcessing.value = true; - } - } - ); - - isActive.value = true; - isListening.value = true; - - } catch (error) { - console.error('启动 Live Mode 失败:', error); - alert('启动失败,请检查麦克风权限或网络连接'); - await stopLiveMode(); - } -} - -async function stopLiveMode() { - cancelAnimationFrame(energyLoopId); - - // 停止 VAD 录音 - vadRecording.stopRecording(); - - // 停止音频播放 - stopAudioPlayback(); - - // 关闭音频上下文 - if (audioContext) { - await audioContext.close(); - audioContext = null; - } - - // 关闭 WebSocket - if (ws) { - ws.close(); - ws = null; - } - - isActive.value = false; - isListening.value = false; - isProcessing.value = false; -} - -function connectWebSocket(): Promise<void> { - return new Promise((resolve, reject) => { - // 获取存储的 token - const token = localStorage.getItem('token'); - if (!token) { - reject(new Error('未登录,请先登录')); - return; - } - - const wsUrl = chatApi.liveWebSocketUrl(token); - - ws = new WebSocket(wsUrl); - - ws.onopen = () => { - console.log('[Live Mode] WebSocket 连接成功'); - resolve(); - }; - - ws.onerror = (error) => { - console.error('[Live Mode] WebSocket 错误:', error); - reject(error); - }; - - ws.onmessage = handleWebSocketMessage; - - ws.onclose = () => { - console.log('[Live Mode] WebSocket 连接关闭'); - }; - - // 超时处理 - setTimeout(() => { - if (ws?.readyState !== WebSocket.OPEN) { - reject(new Error('WebSocket 连接超时')); - } - }, 5000); - }); -} - -// 这些函数不再需要,VAD 库会自动处理语音检测和音频上传 - -function handleWebSocketMessage(event: MessageEvent) { - try { - const message = JSON.parse(event.data); - const msgType = message.t; - - switch (msgType) { - case 'user_msg': - messages.value.push({ - type: 'user', - text: message.data.text - }); - break; - - case 'bot_text_chunk': - messages.value.push({ - type: 'bot', - text: message.data.text - }); - break; - - case 'bot_msg': - messages.value.push({ - type: 'bot', - text: message.data.text - }); - isProcessing.value = false; - isListening.value = true; - break; - - case 'response': - // 音频数据 - playAudioChunk(message.data); - break; - - case 'stop_play': - // 停止播放 - stopAudioPlayback(); - break; - - case 'end': - // 处理完成 - isProcessing.value = false; - isListening.value = true; - break; - - case 'error': - console.error('[Live Mode] 错误:', message.data); - alert('处理出错: ' + message.data); - isProcessing.value = false; - isListening.value = true; - break; - - case 'metrics': - metrics.value = { ...metrics.value, ...message.data }; - break; - } - } catch (error) { - console.error('[Live Mode] 处理消息失败:', error); - } -} - -function playAudioChunk(base64Data: string) { - if (!audioContext) return; - - try { - // 解码 base64 - const binaryString = atob(base64Data); - const bytes = new Uint8Array(binaryString.length); - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - - // 放入待解码队列 - rawAudioQueue.push(bytes); - - // 触发解码处理 - processRawAudioQueue(); - - } catch (error) { - console.error('[Live Mode] 接收音频数据失败:', error); - } -} - -async function processRawAudioQueue() { - if (isDecoding || rawAudioQueue.length === 0) return; - - isDecoding = true; - - try { - while (rawAudioQueue.length > 0) { - const bytes = rawAudioQueue.shift(); - if (!bytes || !audioContext) continue; - - try { - // 解码 - const audioBuffer = await audioContext.decodeAudioData(bytes.buffer as ArrayBuffer); - audioBufferQueue.push(audioBuffer); - - // 如果当前没有播放,立即开始播放 - if (!isPlayingAudio) { - playNextAudio(); - } - } catch (err) { - console.error('[Live Mode] 解码音频失败:', err); - } - } - } finally { - isDecoding = false; - // 如果在解码过程中又有新数据进来,继续处理 - if (rawAudioQueue.length > 0) { - processRawAudioQueue(); - } - } -} - -function playNextAudio() { - if (audioBufferQueue.length === 0) { - isPlayingAudio = false; - isPlaying.value = false; - return; - } - - if (!audioContext) return; - - isPlayingAudio = true; - isPlaying.value = true; - - try { - const audioBuffer = audioBufferQueue.shift(); - if (!audioBuffer) return; - - const source = audioContext.createBufferSource(); - source.buffer = audioBuffer; - - // 连接到分析器 - if (analyser) { - source.connect(analyser); - analyser.connect(audioContext.destination); - } else { - source.connect(audioContext.destination); - } - - currentSource = source; - source.start(); - - source.onended = () => { - currentSource = null; - playNextAudio(); - }; - - } catch (error) { - console.error('[Live Mode] 播放音频失败:', error); - isPlayingAudio = false; - isPlaying.value = false; - playNextAudio(); // 尝试播放下一个 - } -} - -function stopAudioPlayback() { - // 停止当前播放源 - if (currentSource) { - try { - currentSource.stop(); - currentSource.disconnect(); - } catch (e) { - // ignore - } - currentSource = null; - } - - // 清空队列 - rawAudioQueue.length = 0; - audioBufferQueue.length = 0; - - // 重置状态 - isPlayingAudio = false; - isPlaying.value = false; - isDecoding = false; -} - -function generateStamp(): string { - return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; -} - -function updateBotEnergy() { - if (analyser && isPlaying.value) { - const dataArray = new Uint8Array(analyser.frequencyBinCount); - analyser.getByteFrequencyData(dataArray); - - let sum = 0; - // 只计算低频到中频部分,通常人声集中在这里 - const range = Math.floor(dataArray.length * 0.7); - for (let i = 0; i < range; i++) { - sum += dataArray[i]; - } - const average = sum / range; - // 归一化并放大一点 - botEnergy.value = Math.min(1, (average / 255) * 2.0); - } else { - botEnergy.value = Math.max(0, botEnergy.value - 0.1); - } - - if (isActive.value) { - energyLoopId = requestAnimationFrame(updateBotEnergy); - } -} - -function handleClose() { - stopLiveMode(); - emit('close'); -} - -function toggleCodeMode() { - isCodeMode.value = !isCodeMode.value; -} - -function toggleNervousMode() { - isNervousMode.value = !isNervousMode.value; -} - -// 监听用户打断 -watch(isSpeaking, (newVal) => { - if (newVal && isPlaying.value) { - // 用户在播放时开始说话,发送打断信号 - if (ws && ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ t: 'interrupt' })); - } - // 本地立即停止播放 - stopAudioPlayback(); - } -}); - -onBeforeUnmount(() => { - stopLiveMode(); -}); -</script> - -<style scoped> -.live-mode-container { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - background: linear-gradient(135deg, rgba(103, 58, 183, 0.05) 0%, rgba(63, 81, 181, 0.05) 100%); -} - -.header-controls { - display: flex; - padding: 8px; - gap: 8px; -} - -.live-mode-content { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - position: relative; - padding: 40px; -} - -.center-circle-container { - position: relative; - display: flex; - align-items: center; - justify-content: center; - margin-bottom: 40px; - cursor: pointer; - /* 给一个最小尺寸,避免在加载或切换时跳动 */ - min-width: 250px; - min-height: 250px; -} - -.siri-orb { - /* 移除绝对定位,让 Orb 自然占据空间 */ - z-index: 10; - position: relative; -} - -.orb-overlay { - position: absolute; - /* 绝对定位,覆盖在 Orb 上 */ - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 20; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - pointer-events: none; - width: 100%; - height: 100%; -} - -.explosion-wave { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 150px; - height: 150px; - border-radius: 50%; - opacity: 0.8; - background: radial-gradient(circle, transparent 50%, rgba(125, 80, 201, 0.8) 70%, transparent 100%); - animation: explode 3s cubic-bezier(0.16, 1, 0.3, 1) forwards; - filter: blur(30px); - z-index: 0; - pointer-events: none; -} - -@keyframes explode { - 0% { - transform: translate(-50%, -50%) scale(1); - opacity: 0.8; - } - - 100% { - transform: translate(-50%, -50%) scale(50); - opacity: 0; - } -} - -.status-text { - font-size: 24px; - color: var(--v-theme-on-surface); - margin-bottom: 40px; - font-family: 'Outfit', sans-serif; -} - -.messages-container { - position: absolute; - bottom: 40px; - left: 40px; - right: 40px; - max-height: 300px; - overflow-y: auto; - display: flex; - flex-direction: column; - gap: 12px; -} - -.message-item { - color: rgb(var(--v-theme-on-surface)); - display: flex; - align-items: flex-end; - align-self: flex-end; - gap: 12px; -} - -.message-content { - flex: 1; - word-wrap: break-word; -} - -.metrics-container { - position: absolute; - bottom: 10px; - left: 10px; - display: flex; - flex-direction: column; - gap: 4px; - font-size: 12px; - color: rgba(var(--v-theme-on-surface), 0.6); - z-index: 100; -} -</style> diff --git a/legacy-dashboard/src/components/chat/LiveOrb.vue b/legacy-dashboard/src/components/chat/LiveOrb.vue deleted file mode 100644 index 175f18c2..00000000 --- a/legacy-dashboard/src/components/chat/LiveOrb.vue +++ /dev/null @@ -1,494 +0,0 @@ -<template> - <div class="live-orb-container" ref="containerRef" :class="{ 'dark': isDark }" :style="styleVars"> - <div class="live-orb"> - </div> - <div class="eyes-container"> - <div class="eye" :class="{ 'blink': isBlinking, 'nervous': nervousMode }"> - <!-- Nervous Mode > --> - <div v-if="nervousMode" class="nervous-eye-content"> - <svg viewBox="0 0 30 60" width="100%" height="100%"> - <path d="M 0 10 L 30 30 L 0 50" fill="none" stroke="#7d80e4" stroke-width="8" /> - </svg> - </div> - - <!-- Code Mode Layer --> - <transition name="fade"> - <div v-if="codeMode && !nervousMode" class="code-rain-container"> - <div v-for="(col, i) in codeColumns" :key="i" class="code-column" :style="col.style"> - {{ col.content }} - </div> - </div> - </transition> - </div> - <div class="eye" :class="{ 'blink': isBlinking, 'nervous': nervousMode }"> - <!-- Nervous Mode < --> - <div v-if="nervousMode" class="nervous-eye-content"> - <svg viewBox="0 0 30 60" width="100%" height="100%"> - <path d="M 30 10 L 0 30 L 30 50" fill="none" stroke="#7d80e4" stroke-width="8" /> - </svg> - </div> - - <!-- Code Mode Layer --> - <transition name="fade"> - <div v-if="codeMode && !nervousMode" class="code-rain-container"> - <div v-for="(col, i) in codeColumns" :key="i" class="code-column" :style="col.style"> - {{ col.content }} - </div> - </div> - </transition> - </div> - </div> - - <!-- Hair Accessory Star --> - <div class="accessory-star"> - <svg viewBox="0 0 24 24" width="100%" height="100%"> - <path d="M12 2l2.4 7.2h7.6l-6 4.8 2.4 7.2-6-4.8-6 4.8 2.4-7.2-6-4.8h7.6z" - fill="rgba(125, 128, 228, 0.4)" stroke="rgba(180, 182, 255, 0.6)" stroke-width="3" - stroke-linejoin="round" /> - </svg> - </div> - </div> -</template> - -<script setup lang="ts"> -import { computed, onMounted, onBeforeUnmount, ref, watch } from 'vue'; - -const props = defineProps<{ - energy: number; // 0.0 - 1.0 - mode: 'idle' | 'listening' | 'speaking' | 'processing'; - isDark?: boolean; - codeMode?: boolean; - nervousMode?: boolean; -}>(); - -// 内部状态 -const containerRef = ref<HTMLElement | null>(null); -const currentAngle = ref(Math.random() * 360); -const smoothedSpeed = ref(0.2); // 初始速度 -const currentScale = ref(1.0); // 当前缩放 -const isBlinking = ref(false); // 是否正在眨眼 -// 眼睛注视偏移 -const eyeOffset = ref({ x: 0, y: 0 }); -const targetEyeOffset = { x: 0, y: 0 }; - -let animationFrameId: number; -let blinkTimeoutId: any; - -// 颜色配置 -const colorConfigs = { - idle: { - c1: "rgba(100, 100, 255, 0.6)", // 柔和蓝 - c2: "rgba(200, 100, 255, 0.6)", // 柔和紫 - c3: "rgba(100, 200, 255, 0.6)", // 柔和青 - }, - listening: { // 用户说话 - 活跃的蓝色系 - c1: "rgba(60, 130, 246, 0.8)", // 亮蓝 - c2: "rgba(34, 211, 238, 0.8)", // 青色 - c3: "rgba(147, 51, 234, 0.8)", // 紫色 - }, - speaking: { // Bot 说话 - 活跃的紫红色系 - c1: "rgba(236, 72, 153, 0.8)", // 粉红 - c2: "rgba(168, 85, 247, 0.8)", // 紫色 - c3: "rgba(244, 63, 94, 0.8)", // 玫瑰红 - }, - processing: { // 处理中 - 优雅的青/白/紫流转 - c1: "rgba(255, 255, 255, 0.6)", // 纯净白 - c2: "rgba(168, 85, 247, 0.6)", // 神秘紫 - c3: "rgba(34, 211, 238, 0.6)", // 智慧青 - } -}; - -// 动画逻辑 -const animate = () => { - // 基础速度 - let targetSpeed = 0.1; // idle - 非常慢的流动 - if (props.mode === 'processing') targetSpeed = 0.3; // 思考时稍微活跃 - else if (props.mode === 'listening') targetSpeed = 0.2; // 倾听时轻微波动 - else if (props.mode === 'speaking') targetSpeed = 0.4; // 说话时稍快 - - // 能量影响速度:能量越高转得越快,但也减弱影响系数 - targetSpeed += (props.energy * 0.4); - - // 速度平滑插值 (Lerp),避免旋转速度突变 - smoothedSpeed.value += (targetSpeed - smoothedSpeed.value) * 0.05; - - // 让角度无限累加,不要取模 - currentAngle.value = currentAngle.value + smoothedSpeed.value; - - // 计算目标缩放 - let targetScale = 1.0; - const e = Math.max(0, Math.min(1, props.energy)); - targetScale += e * 0.15; // 基础能量缩放 - - // Processing 模式下的呼吸效果 - if (props.mode === 'processing') { - const breathing = (Math.sin(Date.now() / 800 * Math.PI) + 1) * 0.03; - targetScale += breathing; - } - - // 缩放平滑插值 - currentScale.value += (targetScale - currentScale.value) * 0.1; - - // 眼睛偏移平滑插值 - eyeOffset.value.x += (targetEyeOffset.x - eyeOffset.value.x) * 0.1; - eyeOffset.value.y += (targetEyeOffset.y - eyeOffset.value.y) * 0.1; - - animationFrameId = requestAnimationFrame(animate); -}; - -const handleMouseMove = (e: MouseEvent) => { - if (!containerRef.value) return; - - const rect = containerRef.value.getBoundingClientRect(); - const centerX = rect.left + rect.width / 2; - const centerY = rect.top + rect.height / 2; - - // 鼠标相对于中心的偏移 - const dx = e.clientX - centerX; - const dy = e.clientY - centerY; - - // 计算距离和角度 - const dist = Math.sqrt(dx * dx + dy * dy); - const maxDist = Math.min(window.innerWidth, window.innerHeight) / 2; - - // 限制最大移动范围(像素) - const maxEyeMove = 20; - - // 归一化距离因子 (0 ~ 1) - const factor = Math.min(dist / maxDist, 1); - - const angle = Math.atan2(dy, dx); - - targetEyeOffset.x = Math.cos(angle) * factor * maxEyeMove; - targetEyeOffset.y = Math.sin(angle) * factor * maxEyeMove; -}; - -// Code Mode Helpers -const codeColumns = ref<Array<{ content: string, style: any }>>([]); - -onMounted(() => { - animationFrameId = requestAnimationFrame(animate); - scheduleBlink(); - window.addEventListener('mousemove', handleMouseMove); - - // Code Rain Generator - const chars = '01{}<>;/[]*+-~^QWERTYUIOPASDFGHJKLZXCVBNM'; - const cols = 10; - for (let i = 0; i < cols; i++) { - let content = ''; - for (let j = 0; j < 20; j++) { - // 有概率生成空行,增加呼吸感 - if (Math.random() > 0.7) { - content += '\n'; - } else { - content += chars[Math.floor(Math.random() * chars.length)] + '\n'; - } - } - // Repeat once to make it seamless - content += content; - - // Partition distribution to avoid overlap - const section = 100 / cols; - // Randomly in the respective areas, leaving some margin - const left = i * section + Math.random() * (section * 0.6); - - codeColumns.value.push({ - content, - style: { - left: `${left}%`, - animationDuration: `${0.5 + Math.random() * 2.2}s`, - animationDelay: `-${Math.random() * 2}s`, - fontSize: `${8 + Math.random() * 4}px`, // 8-12px - opacity: 0.3 + Math.random() * 0.5, - } - }); - } -}); - -onBeforeUnmount(() => { - cancelAnimationFrame(animationFrameId); - clearTimeout(blinkTimeoutId); - window.removeEventListener('mousemove', handleMouseMove); -}); - -// 眨眼逻辑 -const scheduleBlink = () => { - const delay = Math.random() * 4000 + 2000; // 2s - 6s 随机间隔 - blinkTimeoutId = setTimeout(() => { - triggerBlink(); - scheduleBlink(); - }, delay); -}; - -const triggerBlink = () => { - if (props.nervousMode) return; - isBlinking.value = true; - setTimeout(() => { - isBlinking.value = false; - }, 150); // 眨眼持续 150ms -}; - -const styleVars = computed(() => { - const baseSize = 250; - const blurAmount = Math.max(baseSize * 0.04, 10); - const contrastAmount = Math.max(baseSize * 0.003, 1.2); - const colors = colorConfigs[props.mode] || colorConfigs.idle; - - return { - '--size': `${baseSize}px`, - '--scale': currentScale.value, - '--angle': `${currentAngle.value}deg`, - '--c1': colors.c1, - '--c2': colors.c2, - '--c3': colors.c3, - '--blur-amount': `${blurAmount}px`, - '--contrast-amount': contrastAmount, - '--eye-x': `${eyeOffset.value.x}px`, - '--eye-y': `${eyeOffset.value.y}px`, - } as Record<string, string | number>; -}); - -</script> - -<style scoped> -/* 注册 CSS 变量以支持动画插值 */ -@property --c1 { - syntax: "<color>"; - inherits: true; - initial-value: rgba(0, 0, 0, 0); -} - -@property --c2 { - syntax: "<color>"; - inherits: true; - initial-value: rgba(0, 0, 0, 0); -} - -@property --c3 { - syntax: "<color>"; - inherits: true; - initial-value: rgba(0, 0, 0, 0); -} - -/* --angle 不需要注册为 property 也能在 JS 中更新,但注册更规范 */ -@property --angle { - syntax: "<angle>"; - inherits: true; - initial-value: 0deg; -} - -.live-orb-container { - width: var(--size); - height: var(--size); - position: relative; - display: flex; - align-items: center; - justify-content: center; - transform: scale(var(--scale)); - /* 增加 transition 时间,让缩放更柔和 */ - transition: transform 0.2s ease-out, - --c1 1s ease, - --c2 1s ease, - --c3 1s ease; -} - -.live-orb { - width: 100%; - height: 100%; - display: grid; - grid-template-areas: "stack"; - overflow: hidden; - border-radius: 50%; - position: relative; - background: radial-gradient(circle, - rgba(0, 0, 0, 0.05) 0%, - rgba(0, 0, 0, 0.02) 30%, - transparent 70%); - transition: all 0.5s ease; -} - -.dark .live-orb { - background: radial-gradient(circle, - rgba(255, 255, 255, 0.1) 0%, - rgba(255, 255, 255, 0.05) 30%, - transparent 70%); -} - -.live-orb::before { - content: ""; - display: block; - grid-area: stack; - width: 100%; - height: 100%; - border-radius: 50%; - /* 使用 CSS 变量,这里的颜色会自动跟随父容器的 transition */ - background: - /* 层1:慢速逆时针 - 基底 */ - conic-gradient(from calc(var(--angle) * -0.5 + 45deg) at 40% 55%, - var(--c3) 0deg, - transparent 60deg 300deg, - var(--c3) 360deg), - /* 层2:中速顺时针 - 纹理 */ - conic-gradient(from calc(var(--angle) * 0.8) at 60% 45%, - var(--c2) 0deg, - transparent 45deg 315deg, - var(--c2) 360deg), - /* 层3:快速逆时针 - 扰动 */ - conic-gradient(from calc(var(--angle) * -1.2 + 120deg) at 35% 65%, - var(--c1) 0deg, - transparent 80deg 280deg, - var(--c1) 360deg), - /* 层4:慢速顺时针 - 补色 */ - conic-gradient(from calc(var(--angle) * 0.6 + 200deg) at 65% 35%, - var(--c2) 0deg, - transparent 50deg 310deg, - var(--c2) 360deg), - /* 层5:微弱的旋转底纹 */ - conic-gradient(from calc(var(--angle) * 0.3 + 90deg) at 50% 50%, - var(--c1) 0deg, - transparent 120deg 240deg, - var(--c1) 360deg), - /* 核心高光 - 稍微偏离中心 */ - radial-gradient(ellipse 120% 100% at 45% 55%, - var(--c3) 0%, - transparent 50%); - - filter: blur(var(--blur-amount)) contrast(var(--contrast-amount)) saturate(1.5); - /* 移除 animation,改用 JS 驱动 --angle */ - transform: translateZ(0); - will-change: transform, background; - opacity: 0.8; -} - -.live-orb::after { - content: ""; - display: block; - grid-area: stack; - width: 100%; - height: 100%; - border-radius: 50%; - background: radial-gradient(circle at 45% 55%, - rgba(255, 255, 255, 0.4) 0%, - rgba(255, 255, 255, 0.1) 30%, - transparent 60%); - mix-blend-mode: overlay; - pointer-events: none; -} - -.eyes-container { - position: absolute; - display: flex; - gap: 60px; - z-index: 5; - /* Center it */ - top: 42%; - left: 50%; - transform: translate(calc(-50% + var(--eye-x)), calc(-50% + var(--eye-y))); - pointer-events: none; -} - -.eye { - width: 28px; - height: 60px; - background-color: #7d80e4; - border-radius: 20px; - opacity: 0.8; - transition: transform 0.1s ease-in-out; - transform-origin: center; - position: relative; - overflow: hidden; -} - -.eye.blink { - transform: scaleY(0.1); -} - -.eye.nervous { - background-color: transparent; - display: flex; - align-items: center; - justify-content: center; - box-shadow: none; -} - -.nervous-eye-content { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; -} - -.code-rain-container { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 2; - pointer-events: none; - mix-blend-mode: hard-light; -} - -.code-column { - position: absolute; - top: 0; - color: rgba(180, 255, 255, 0.9); - font-family: 'Courier New', monospace; - font-weight: bold; - line-height: 1.2; - white-space: pre; - text-align: center; - animation: scrollUp linear infinite; - text-shadow: 0 0 5px rgba(100, 200, 255, 0.8); -} - -@keyframes scrollUp { - from { - transform: translateY(0); - } - - to { - transform: translateY(-50%); - } -} - -.fade-enter-active, -.fade-leave-active { - transition: opacity 0.5s ease; -} - -.fade-enter-from, -.fade-leave-to { - opacity: 0; -} - -.accessory-star { - position: absolute; - width: 15px; - height: 15px; - top: 20%; - right: 20%; - transform: rotate(5deg); - z-index: -100; - opacity: 0.8; - filter: drop-shadow(0 0 5px rgba(180, 182, 255, 0.4)); - animation: starFloat 4s ease-in-out infinite; - pointer-events: none; - mix-blend-mode: screen; -} - -@keyframes starFloat { - - 0%, - 100% { - transform: rotate(5deg) translateY(0) scale(1); - opacity: 0.3; - } - - 50% { - transform: rotate(10deg) translateY(-3px) scale(1.05); - opacity: 0.5; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/MessageList.vue b/legacy-dashboard/src/components/chat/MessageList.vue deleted file mode 100644 index 46f8a927..00000000 --- a/legacy-dashboard/src/components/chat/MessageList.vue +++ /dev/null @@ -1,894 +0,0 @@ -<template> - <div - ref="messageListRoot" - class="message-list-root" - :class="{ 'is-dark': isDark }" - > - <div v-if="isLoadingMessages" class="center-state"> - <v-progress-circular indeterminate size="32" width="3" /> - </div> - - <div v-else class="messages-list"> - <div - v-for="(msg, msgIndex) in messages" - :key="msg.id || `${msgIndex}-${msg.created_at || ''}`" - class="message-row" - :class="isUserMessage(msg) ? 'from-user' : 'from-bot'" - > - <v-avatar v-if="!isUserMessage(msg)" class="bot-avatar" size="48"> - <v-progress-circular - v-if="isMessageStreaming(msgIndex)" - indeterminate - size="22" - width="2" - /> - <span v-else class="bot-avatar-symbol" aria-hidden="true">✦</span> - </v-avatar> - - <div class="message-stack"> - <div - class="message-bubble" - :class="{ user: isUserMessage(msg), bot: !isUserMessage(msg) }" - > - <div v-if="messageContent(msg).isLoading" class="loading-message"> - <span>{{ tm("message.loading") }}</span> - </div> - - <template v-else> - <template - v-for="(block, blockIndex) in renderBlocks(msg)" - :key="`${msgIndex}-block-${blockIndex}-${block.kind}`" - > - <ReasoningBlock - v-if="block.kind === 'thinking'" - :parts="block.parts" - :is-dark="isDark" - :initial-expanded="false" - :is-streaming="isMessageStreaming(msgIndex)" - :has-non-reasoning-content=" - hasFollowingContentBlock(msg, blockIndex) - " - /> - - <template v-else> - <template - v-for="(part, partIndex) in block.parts" - :key="`${msgIndex}-${blockIndex}-${partIndex}-${part.type}`" - > - <button - v-if="part.type === 'reply'" - class="reply-quote" - type="button" - @click="scrollToMessage(part.message_id)" - > - <v-icon size="15">mdi-reply</v-icon> - <span>{{ - replyPreview(part.message_id, part.selected_text) - }}</span> - </button> - - <div - v-else-if="part.type === 'plain' && isUserMessage(msg)" - class="plain-content" - > - {{ part.text || "" }} - </div> - - <MarkdownMessagePart - v-else-if="part.type === 'plain'" - :content="part.text || ''" - :refs="resolvedMessageRefs(msg)" - :is-dark="isDark" - :custom-html-tags="customMarkdownTags" - :is-streaming="isMessageStreaming(msgIndex)" - /> - - <button - v-else-if="part.type === 'image'" - class="image-part" - type="button" - @click="openImage(partUrl(part))" - > - <img :src="partUrl(part)" :alt="part.filename || 'image'" /> - </button> - - <audio - v-else-if="part.type === 'record'" - class="audio-part" - controls - :src="partUrl(part)" - /> - - <video - v-else-if="part.type === 'video'" - class="video-part" - controls - :src="partUrl(part)" - /> - - <div - v-else-if="part.type === 'file'" - class="file-part" - :style="{ - '--attachment-color': attachmentPresentation(part).color, - }" - > - <v-icon - class="file-part-icon" - :icon="attachmentPresentation(part).icon" - size="24" - /> - <div class="file-part-meta"> - <span class="file-part-name"> - {{ attachmentName(part) }} - </span> - <span class="file-part-kind"> - {{ attachmentPresentation(part).label }} - </span> - </div> - <v-btn - class="file-part-action" - icon="mdi-download" - size="x-small" - variant="text" - :loading=" - downloadingFiles.has( - part.attachment_id || - part.stored_filename || - part.filename || - '', - ) - " - @click="downloadPart(part)" - /> - </div> - - <div - v-else-if="part.type === 'tool_call'" - class="tool-call-block" - > - <template - v-for="tool in part.tool_calls || []" - :key="tool.id || tool.name" - > - <ToolCallItem - v-if="isIPythonToolCall(tool)" - :is-dark="isDark" - > - <template #label> - <v-icon size="16">mdi-code-json</v-icon> - <span>{{ tool.name || "python" }}</span> - <span class="tool-call-inline-status"> - {{ toolCallStatusText(tool) }} - </span> - </template> - <template #details> - <IPythonToolBlock - :tool-call="normalizeToolCall(tool)" - :is-dark="isDark" - :show-header="false" - :force-expanded="true" - /> - </template> - </ToolCallItem> - <ToolCallCard - v-else - :tool-call="normalizeToolCall(tool)" - :is-dark="isDark" - /> - </template> - </div> - - <div v-else class="unknown-part"> - {{ formatJson(part) }} - </div> - </template> - </template> - </template> - </template> - </div> - - <div v-if="showMessageMeta(msg, msgIndex)" class="message-meta"> - <span v-if="msg.created_at">{{ formatTime(msg.created_at) }}</span> - <v-btn - v-if="!isUserMessage(msg)" - icon="mdi-content-copy" - size="x-small" - variant="text" - @click="copyMessage(msg)" - /> - <v-menu v-if="messageContent(msg).agentStats" location="bottom"> - <template #activator="{ props: statsProps }"> - <v-btn - v-bind="statsProps" - icon="mdi-information-outline" - size="x-small" - variant="text" - /> - </template> - <v-card class="stats-card" elevation="4"> - <div - v-if="cachedInputTokens(messageContent(msg).agentStats) > 0" - class="stats-row" - > - <span>{{ tm("stats.cachedTokens") }}</span> - <strong>{{ - cachedInputTokens(messageContent(msg).agentStats) - }}</strong> - </div> - <div class="stats-row"> - <span>{{ tm("stats.inputTokens") }}</span> - <strong>{{ - inputTokens(messageContent(msg).agentStats) - }}</strong> - </div> - <div class="stats-row"> - <span>{{ tm("stats.outputTokens") }}</span> - <strong>{{ - outputTokens(messageContent(msg).agentStats) - }}</strong> - </div> - <div - v-if="agentTtft(messageContent(msg).agentStats)" - class="stats-row" - > - <span>{{ tm("stats.ttft") }}</span> - <strong>{{ - agentTtft(messageContent(msg).agentStats) - }}</strong> - </div> - <div class="stats-row"> - <span>{{ tm("stats.duration") }}</span> - <strong>{{ - agentDuration(messageContent(msg).agentStats) - }}</strong> - </div> - </v-card> - </v-menu> - <div v-if="messageRefs(msg).length" class="message-meta-refs"> - <ActionRef - :refs="resolvedMessageRefs(msg)" - @open-refs="openRefsSidebar" - /> - </div> - </div> - </div> - </div> - </div> - - <RefsSidebar v-model="refsSidebarOpen" :refs="selectedRefs" /> - - <v-overlay - v-model="imagePreview.visible" - class="image-preview-overlay" - scrim="rgba(0, 0, 0, 0.86)" - @click="closeImage" - > - <img - class="preview-image" - :src="imagePreview.url" - alt="preview" - @click.stop - /> - </v-overlay> - </div> -</template> - -<script setup lang="ts"> -import { computed, nextTick, reactive, ref } from "vue"; -import axios from "axios"; -import { fileApi } from "@/api/v1"; -import { setCustomComponents } from "markstream-vue"; -import "markstream-vue/index.css"; -import IPythonToolBlock from "@/components/chat/message_list_comps/IPythonToolBlock.vue"; -import MarkdownMessagePart from "@/components/chat/message_list_comps/MarkdownMessagePart.vue"; -import ReasoningBlock from "@/components/chat/message_list_comps/ReasoningBlock.vue"; -import RefNode from "@/components/chat/message_list_comps/RefNode.vue"; -import RefsSidebar from "@/components/chat/message_list_comps/RefsSidebar.vue"; -import ToolCallCard from "@/components/chat/message_list_comps/ToolCallCard.vue"; -import ToolCallItem from "@/components/chat/message_list_comps/ToolCallItem.vue"; -import ActionRef from "@/components/chat/message_list_comps/ActionRef.vue"; -import ThemeAwareMarkdownCodeBlock from "@/components/shared/ThemeAwareMarkdownCodeBlock.vue"; -import { - attachmentName, - attachmentPresentation, -} from "@/components/chat/attachmentPresentation"; -import { - displayParts as displayMessageParts, - messageBlocks as buildMessageBlocks, - type MessageDisplayBlock, -} from "@/composables/useMessages"; -import type { - ChatContent, - ChatRecord, - MessagePart, -} from "@/composables/useMessages"; -import { useModuleI18n } from "@/i18n/composables"; -import { copyToClipboard } from "@/utils/clipboard"; - -const props = withDefaults( - defineProps<{ - messages: ChatRecord[]; - isDark?: boolean; - isStreaming?: boolean; - isLoadingMessages?: boolean; - }>(), - { - isDark: false, - isStreaming: false, - isLoadingMessages: false, - }, -); - -setCustomComponents("chat-message", { - ref: RefNode, - code_block: ThemeAwareMarkdownCodeBlock, -}); - -const { tm } = useModuleI18n("features/chat"); -const customMarkdownTags = ["ref"]; -const downloadingFiles = ref(new Set<string>()); -const messageListRoot = ref<HTMLElement | null>(null); -const imagePreview = reactive({ visible: false, url: "" }); -const refsSidebarOpen = ref(false); -const selectedRefs = ref<Record<string, unknown> | null>(null); - -const messages = computed(() => props.messages || []); - -function isUserMessage(message: ChatRecord) { - return messageContent(message).type === "user"; -} - -function messageContent(message: ChatRecord): ChatContent { - return message.content || { type: "bot", message: [] }; -} - -function messageParts(message: ChatRecord): MessagePart[] { - return displayMessageParts(messageContent(message)); -} - -function isMessageStreaming(messageIndex: number) { - return props.isStreaming && messageIndex === messages.value.length - 1; -} - -function hasNonReasoningContent(message: ChatRecord) { - return renderBlocks(message).some((block) => block.kind === "content"); -} - -function renderBlocks(message: ChatRecord): MessageDisplayBlock[] { - if (isUserMessage(message)) { - const parts = messageParts(message); - return parts.length ? [{ kind: "content", parts }] : []; - } - return buildMessageBlocks(messageContent(message)); -} - -function hasFollowingContentBlock(message: ChatRecord, blockIndex: number) { - return renderBlocks(message) - .slice(blockIndex + 1) - .some((block) => block.kind === "content"); -} - -function partUrl(part: MessagePart) { - if (part.embedded_url) return part.embedded_url; - if (part.embedded_file?.url) return part.embedded_file.url; - if (part.attachment_id) { - return fileApi.contentUrl(part.attachment_id); - } - const lookupFilename = part.stored_filename || part.filename; - if (lookupFilename) { - return fileApi.byNameUrl(lookupFilename); - } - return ""; -} - -function formatJson(value: unknown) { - if (typeof value === "string") { - const parsed = parseJsonSafe(value); - if (parsed !== value) return JSON.stringify(parsed, null, 2); - return value; - } - try { - return JSON.stringify(value, null, 2); - } catch { - return String(value ?? ""); - } -} - -function replyPreview(messageId?: string | number, fallback?: string) { - if (fallback) return truncate(fallback, 80); - const found = messages.value.find( - (message) => String(message.id) === String(messageId), - ); - const text = found ? plainTextFromMessage(found) : ""; - return text ? truncate(text, 80) : tm("reply.replyTo"); -} - -function plainTextFromMessage(message: ChatRecord) { - return messageParts(message) - .filter((part) => part.type === "plain" && part.text) - .map((part) => part.text) - .join("\n"); -} - -function truncate(value: string, max: number) { - return value.length > max ? `${value.slice(0, max)}...` : value; -} - -function scrollToMessage(messageId?: string | number) { - if (!messageId) return; - const index = messages.value.findIndex( - (message) => String(message.id) === String(messageId), - ); - if (index < 0) return; - nextTick(() => { - const rows = messageListRoot.value?.querySelectorAll(".message-row"); - rows?.[index]?.scrollIntoView({ behavior: "smooth", block: "center" }); - }); -} - -function showMessageMeta(message: ChatRecord, msgIndex: number) { - return !messageContent(message).isLoading && !isMessageStreaming(msgIndex); -} - -function messageRefs(message: ChatRecord) { - return resolvedMessageRefs(message).used; -} - -function resolvedMessageRefs(message: ChatRecord) { - return normalizeRefs(messageContent(message).refs); -} - -function normalizeRefs(refs: unknown) { - if (!refs) return { used: [] as Array<Record<string, unknown>> }; - const refsValue = refs as { used?: unknown }; - const used = Array.isArray(refsValue.used) - ? refsValue.used - : Array.isArray(refs) - ? refs - : []; - - return { - used: normalizeRefItems(used), - }; -} - -function normalizeRefItems(items: unknown[]) { - return items - .map((item: any) => ({ - index: item?.index, - title: item?.title || item?.url || tm("refs.title"), - url: item?.url, - snippet: item?.snippet, - favicon: item?.favicon, - })) - .filter((item) => item.url); -} - -function openRefsSidebar(refs: unknown) { - selectedRefs.value = - refs && typeof refs === "object" ? (refs as Record<string, unknown>) : null; - refsSidebarOpen.value = true; -} - -function normalizeToolCall(tool: Record<string, unknown>) { - const normalized = { ...tool }; - normalized.args = parseJsonSafe( - normalized.args ?? normalized.arguments ?? {}, - ); - normalized.result = parseJsonSafe(normalized.result); - normalized.ts = normalized.ts ?? Date.now() / 1000; - if (normalized.result && typeof normalized.result === "object") { - normalized.result = JSON.stringify(normalized.result, null, 2); - } - return normalized; -} - -function isIPythonToolCall(tool: Record<string, unknown>) { - const name = String(tool.name || "").toLowerCase(); - return name.includes("python") || name.includes("ipython"); -} - -function toolCallStatusText(tool: Record<string, unknown>) { - if (tool.finished_ts) return tm("toolStatus.done"); - return tm("toolStatus.running"); -} - -function parseJsonSafe(value: unknown) { - if (typeof value !== "string") return value; - try { - return JSON.parse(value); - } catch { - return value; - } -} - -async function copyMessage(message: ChatRecord) { - const text = plainTextFromMessage(message); - if (!text) return; - await copyToClipboard(text, { container: messageListRoot.value }); -} - -async function downloadPart(part: MessagePart) { - const key = part.attachment_id || part.stored_filename || part.filename || ""; - if (!key) return; - downloadingFiles.value = new Set(downloadingFiles.value).add(key); - try { - const response = await axios.get(partUrl(part), { responseType: "blob" }); - const url = URL.createObjectURL(response.data); - const anchor = document.createElement("a"); - anchor.href = url; - anchor.download = part.filename || "file"; - anchor.click(); - URL.revokeObjectURL(url); - } finally { - const next = new Set(downloadingFiles.value); - next.delete(key); - downloadingFiles.value = next; - } -} - -function openImage(url: string) { - if (!url) return; - imagePreview.url = url; - imagePreview.visible = true; -} - -function closeImage() { - imagePreview.visible = false; - imagePreview.url = ""; -} - -function formatTime(value: string) { - const date = new Date(value); - if (Number.isNaN(date.getTime())) return ""; - return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }); -} - -function inputTokens(stats: any) { - const usage = stats?.token_usage || {}; - return usage.input_other || 0; -} - -function outputTokens(stats: any) { - return stats?.token_usage?.output || 0; -} - -function cachedInputTokens(stats: any) { - return stats?.token_usage?.input_cached || 0; -} - -function agentDuration(stats: any) { - const directDuration = readPositiveNumber(stats, [ - "duration", - "total_duration", - ]); - if (directDuration !== null) return formatDuration(directDuration); - - const startTime = readPositiveNumber(stats, ["start_time"]); - const endTime = readPositiveNumber(stats, ["end_time"]); - if (startTime === null || endTime === null || endTime < startTime) return "-"; - return formatDuration(endTime - startTime); -} - -function agentTtft(stats: any) { - const ttft = readPositiveNumber(stats, [ - "time_to_first_token", - "ttft", - "first_token_latency", - ]); - if (ttft === null) return ""; - return formatDuration(ttft); -} - -function readPositiveNumber(source: any, keys: string[]) { - for (const key of keys) { - const value = Number(source?.[key]); - if (Number.isFinite(value) && value > 0) return value; - } - return null; -} - -function formatDuration(seconds: number) { - if (seconds < 1) return `${Math.round(seconds * 1000)}ms`; - if (seconds < 60) return `${seconds.toFixed(1)}s`; - const minutes = Math.floor(seconds / 60); - const restSeconds = Math.round(seconds % 60); - return `${minutes}m ${restSeconds}s`; -} -</script> - -<style scoped> -.message-list-root { - --chat-border: rgba(var(--v-border-color), 0.16); - --chat-muted: rgba(var(--v-theme-on-surface), 0.62); - width: 100%; - min-height: 0; - color: rgb(var(--v-theme-on-surface)); -} - -.message-list-root.is-dark { - --chat-border: rgba(255, 255, 255, 0.1); -} - -.center-state { - display: flex; - min-height: 160px; - align-items: center; - justify-content: center; -} - -.messages-list { - display: flex; - flex-direction: column; - gap: 22px; -} - -.message-row { - display: flex; - gap: 10px; - max-width: 100%; -} - -.message-row.from-user { - justify-content: flex-end; -} - -.message-stack { - max-width: min(760px, 82%); -} - -.from-user .message-stack { - align-items: flex-end; - max-width: 60%; -} - -.bot-avatar { - margin-top: 2px; - color: rgb(var(--v-theme-primary)); - user-select: none; -} - -.bot-avatar-symbol { - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 28px; - line-height: 0; - margin-top: -2px; - pointer-events: none; - user-select: none; -} - -.message-bubble { - max-width: 100%; - border-radius: 8px; - padding: 10px 14px; - line-height: 1.65; - overflow-wrap: anywhere; -} - -.message-bubble.user { - color: var(--v-theme-primaryText); - padding: 12px 18px; - font-size: 15px; - border-radius: 1.5rem; - background: rgba(var(--v-theme-primary), 0.12); -} - -.message-bubble.bot { - background: transparent; - padding-left: 0; -} - -.plain-content { - white-space: pre-wrap; -} - -.loading-message { - display: flex; - align-items: center; - gap: 8px; - margin-top: 4px; - color: var(--chat-muted); -} - -.reply-quote { - width: 100%; - display: flex; - align-items: center; - gap: 6px; - border: 0; - border-left: 3px solid rgb(var(--v-theme-primary)); - border-radius: 6px; - padding: 7px 9px; - margin-bottom: 8px; - background: rgba(var(--v-theme-primary), 0.08); - color: inherit; - cursor: pointer; - text-align: left; -} - -.image-part { - display: block; - border: 0; - padding: 0; - margin-top: 8px; - background: transparent; - cursor: zoom-in; -} - -.image-part img { - max-width: min(420px, 100%); - max-height: 360px; - border-radius: 8px; - object-fit: contain; -} - -.audio-part, -.video-part { - display: block; - max-width: 100%; - margin-top: 8px; -} - -.video-part { - max-height: 360px; - border-radius: 8px; -} - -.file-part { - --attachment-color: #607d8b; - display: grid; - grid-template-columns: auto minmax(0, 1fr) auto; - align-items: center; - gap: 10px; - width: min(420px, 100%); - margin-top: 8px; - padding: 9px 8px 9px 10px; - border: 0; - border-radius: 8px; - background: rgba(var(--v-theme-on-surface), 0.055); - background: linear-gradient( - 90deg, - color-mix(in srgb, var(--attachment-color) 13%, transparent), - rgba(var(--v-theme-on-surface), 0.055) 58% - ); -} - -.file-part-icon { - color: var(--attachment-color); -} - -.file-part-meta { - min-width: 0; - display: flex; - flex-direction: column; - gap: 1px; -} - -.file-part-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 14px; - font-weight: 500; - line-height: 20px; -} - -.file-part-kind { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--attachment-color); - font-size: 11px; - font-weight: 700; - line-height: 14px; -} - -.file-part-action { - color: rgb(var(--v-theme-on-surface)); - opacity: 0.72; -} - -.file-part:hover .file-part-action { - opacity: 1; -} - -.tool-call-block { - margin: 8px 0; - display: flex; - flex-direction: column; - gap: 6px; -} - -.tool-call-inline-status { - color: var(--chat-muted); - font-size: 12px; -} - -.unknown-part { - max-width: 100%; - overflow-x: auto; - border-radius: 8px; - padding: 10px; - background: rgba(var(--v-theme-on-surface), 0.06); - font-size: 13px; - line-height: 1.5; - white-space: pre-wrap; -} - -.message-meta { - display: flex; - align-items: center; - gap: 2px; - min-height: 24px; - color: var(--chat-muted); - font-size: 12px; -} - -.message-meta-refs { - display: flex; - align-items: center; -} - -.from-user .message-meta { - justify-content: flex-end; -} - -.stats-card { - min-width: 150px; - padding: 8px 10px; -} - -.stats-row { - display: flex; - justify-content: space-between; - gap: 14px; - padding: 2px 0; - font-size: 12px; - line-height: 1.35; -} - -.stats-row span { - color: var(--chat-muted); -} - -.stats-row strong { - font-size: 12px; - font-weight: 600; -} - -.image-preview-overlay { - display: flex; - align-items: center; - justify-content: center; -} - -.preview-image { - max-width: min(92vw, 1200px); - max-height: 88vh; - border-radius: 8px; - object-fit: contain; -} - -@media (max-width: 760px) { - .message-stack, - .from-user .message-stack { - max-width: 88%; - } - - .message-row.from-bot { - flex-direction: column; - gap: 2px; - } - - .message-row.from-bot .message-stack { - max-width: 100%; - } - - .message-row.from-bot .bot-avatar { - display: none; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/MessageListDEPRECATED.vue b/legacy-dashboard/src/components/chat/MessageListDEPRECATED.vue deleted file mode 100644 index bc9d44ee..00000000 --- a/legacy-dashboard/src/components/chat/MessageListDEPRECATED.vue +++ /dev/null @@ -1,1463 +0,0 @@ -<template> - <div class="messages-container" ref="messageContainer" :class="{ 'is-dark': isDark }"> - <!-- 加载指示器 --> - <div v-if="isLoadingMessages" class="loading-overlay" :class="{ 'is-dark': isDark }"> - <v-progress-circular indeterminate size="48" width="4" color="primary"></v-progress-circular> - </div> - <!-- 聊天消息列表 --> - <div class="message-list" :class="{ 'loading-blur': isLoadingMessages }" @mouseup="handleTextSelection"> - <div class="message-item fade-in" v-for="(msg, index) in messages" :key="index"> - <!-- 用户消息 --> - <div v-if="msg.content.type == 'user'" class="user-message"> - <div class="message-bubble user-bubble" :class="{ 'has-audio': hasAudio(msg.content.message) }" - :style="{ backgroundColor: isDark ? '#2d2e30' : '#e7ebf4' }"> - <!-- 遍历 message parts --> - <template v-for="(part, partIndex) in msg.content.message" :key="partIndex"> - <!-- 引用消息 --> - <div v-if="part.type === 'reply'" class="reply-quote" - @click="scrollToMessage(part.message_id)"> - <v-icon size="small" class="reply-quote-icon">mdi-reply</v-icon> - <span class="reply-quote-text">{{ getReplyContent(part.message_id) }}</span> - </div> - - <!-- 纯文本 --> - <pre v-else-if="part.type === 'plain' && part.text" - style="font-family: inherit; white-space: pre-wrap; word-wrap: break-word;">{{ part.text }}</pre> - - <!-- 图片附件 --> - <div v-else-if="part.type === 'image' && part.embedded_url" class="image-attachments"> - <div class="image-attachment"> - <img :src="part.embedded_url" class="attached-image" - @click="openImagePreview(part.embedded_url)" /> - </div> - </div> - - <!-- 音频附件 --> - <div v-else-if="part.type === 'record' && part.embedded_url" class="audio-attachment"> - <audio controls class="audio-player"> - <source :src="part.embedded_url" type="audio/wav"> - {{ t('messages.errors.browser.audioNotSupported') }} - </audio> - </div> - - <!-- 文件附件 --> - <div v-else-if="part.type === 'file' && part.embedded_file" class="file-attachments"> - <div class="file-attachment"> - <a v-if="part.embedded_file.url" :href="part.embedded_file.url" - :download="part.embedded_file.filename" class="file-link" - :class="{ 'is-dark': isDark }" :style="isDark ? { - backgroundColor: 'rgba(255, 255, 255, 0.05)', - borderColor: 'rgba(255, 255, 255, 0.1)', - color: 'var(--v-theme-secondary)' - } : {}"> - <v-icon size="small" class="file-icon" - :style="isDark ? { color: 'var(--v-theme-secondary)' } : {}">mdi-file-document-outline</v-icon> - <span class="file-name">{{ part.embedded_file.filename }}</span> - </a> - <a v-else @click="downloadFile(part.embedded_file)" - class="file-link file-link-download" :class="{ 'is-dark': isDark }" :style="isDark ? { - backgroundColor: 'rgba(255, 255, 255, 0.05)', - borderColor: 'rgba(255, 255, 255, 0.1)', - color: 'var(--v-theme-secondary)' - } : {}"> - <v-icon size="small" class="file-icon" - :style="isDark ? { color: 'var(--v-theme-secondary)' } : {}">mdi-file-document-outline</v-icon> - <span class="file-name">{{ part.embedded_file.filename }}</span> - <v-icon v-if="downloadingFiles.has(part.embedded_file.attachment_id)" - size="small" class="download-icon">mdi-loading mdi-spin</v-icon> - <v-icon v-else size="small" class="download-icon">mdi-download</v-icon> - </a> - </div> - </div> - </template> - </div> - </div> - - <!-- Bot Messages --> - <div v-else class="bot-message"> - <v-avatar class="bot-avatar" size="36"> - <v-progress-circular :index="index" v-if="isStreaming && index === messages.length - 1" - indeterminate size="28" width="2"></v-progress-circular> - <v-icon v-else-if="messages[index - 1]?.content.type !== 'bot'" size="64" - color="#8fb6d2">mdi-star-four-points-small</v-icon> - </v-avatar> - <div class="bot-message-content"> - <div class="message-bubble bot-bubble"> - <!-- Loading state --> - <div v-if="msg.content.isLoading" class="loading-container"> - <span class="loading-text">{{ tm('message.loading') }}</span> - </div> - - <template v-else> - <!-- Reasoning Block (Collapsible) - 放在最前面 --> - <ReasoningBlock v-if="msg.content.reasoning && msg.content.reasoning.trim()" - :reasoning="msg.content.reasoning" :is-dark="isDark" - class="mt-2" - :initial-expanded="isReasoningExpanded(index)" /> - - <MessagePartsRenderer :parts="msg.content.message" :is-dark="isDark" - :current-time="currentTime" :downloading-files="downloadingFiles" - @open-image-preview="openImagePreview" @download-file="downloadFile" /> - </template> - </div> - <div class="message-actions" v-if="!msg.content.isLoading || index === messages.length - 1"> - <span class="message-time" v-if="msg.created_at">{{ formatMessageTime(msg.created_at) - }}</span> - <!-- Agent Stats Menu --> - <v-menu v-if="msg.content.agentStats" location="bottom" open-on-hover - :close-on-content-click="false"> - <template v-slot:activator="{ props }"> - <v-icon v-bind="props" size="x-small" - class="stats-info-icon">mdi-information-outline</v-icon> - </template> - <v-card class="stats-menu-card" variant="elevated" elevation="3"> - <v-card-text class="stats-menu-content"> - <div class="stats-menu-row"> - <span class="stats-menu-label">{{ tm('stats.inputTokens') }}</span> - <span class="stats-menu-value">{{ - getInputTokens(msg.content.agentStats.token_usage) }}</span> - </div> - <div class="stats-menu-row"> - <span class="stats-menu-label">{{ tm('stats.outputTokens') }}</span> - <span class="stats-menu-value">{{ msg.content.agentStats.token_usage.output - || 0 }}</span> - </div> - <div class="stats-menu-row" - v-if="msg.content.agentStats.token_usage.input_cached > 0"> - <span class="stats-menu-label">{{ tm('stats.cachedTokens') }}</span> - <span class="stats-menu-value">{{ - msg.content.agentStats.token_usage.input_cached }}</span> - </div> - <div class="stats-menu-row" - v-if="msg.content.agentStats.time_to_first_token > 0"> - <span class="stats-menu-label">{{ tm('stats.ttft') }}</span> - <span class="stats-menu-value">{{ - formatTTFT(msg.content.agentStats.time_to_first_token) }}</span> - </div> - <div class="stats-menu-row"> - <span class="stats-menu-label">{{ tm('stats.duration') }}</span> - <span class="stats-menu-value">{{ - formatAgentDuration(msg.content.agentStats) }}</span> - </div> - </v-card-text> - </v-card> - </v-menu> - <v-btn :icon="getCopyIcon(index)" size="x-small" variant="text" class="copy-message-btn" - :class="{ 'copy-success': isCopySuccess(index), 'copy-failed': isCopyFailure(index) }" - @click="copyBotMessage(msg.content.message, index)" :title="getCopyTitle(index)" /> - <v-btn icon="mdi-reply-outline" size="x-small" variant="text" class="reply-message-btn" - @click="$emit('replyMessage', msg, index)" :title="tm('actions.reply')" /> - - <!-- Refs Visualization --> - <ActionRef :refs="msg.content.refs" @open-refs="openRefsSidebar" /> - </div> - </div> - </div> - </div> - </div> - - <!-- 浮动引用按钮 --> - <div v-if="selectedText.content && selectedText.messageIndex !== null" class="selection-quote-button" :style="{ - top: selectedText.position.top + 'px', - left: selectedText.position.left + 'px', - position: 'fixed' - }"> - <v-btn size="large" rounded="xl" @click="handleQuoteSelected" class="quote-btn" - :class="{ 'dark-mode': isDark }"> - <v-icon left small>mdi-reply</v-icon> - 引用 - </v-btn> - </div> - </div> - - <!-- 图片预览 Overlay --> - <v-overlay v-model="imagePreview.show" class="image-preview-overlay" @click="closeImagePreview"> - <div class="image-preview-container" @click.stop> - <img :src="imagePreview.url" class="preview-image" @click="closeImagePreview" /> - </div> - </v-overlay> -</template> - -<script> -import { useI18n, useModuleI18n } from '@/i18n/composables'; -import { enableKatex, enableMermaid, MarkdownCodeBlockNode, setCustomComponents } from 'markstream-vue' -import 'markstream-vue/index.css' -import 'katex/dist/katex.min.css' -import axios from 'axios'; -import { fileApi } from '@/api/v1'; -import { useToast } from '@/utils/toast' -import ReasoningBlock from './message_list_comps/ReasoningBlock.vue'; -import MessagePartsRenderer from './message_list_comps/MessagePartsRenderer.vue'; -import RefNode from './message_list_comps/RefNode.vue'; -import ActionRef from './message_list_comps/ActionRef.vue'; - -enableKatex(); -enableMermaid(); - -// 注册 message-list 专用组件:引用节点 + Shiki 代码块渲染 -setCustomComponents('message-list', { - ref: RefNode, - code_block: MarkdownCodeBlockNode -}); - -export default { - name: 'MessageList', - components: { - ReasoningBlock, - MessagePartsRenderer, - RefNode, - ActionRef - }, - props: { - messages: { - type: Array, - required: true - }, - isDark: { - type: Boolean, - default: false - }, - isStreaming: { - type: Boolean, - default: false - }, - isLoadingMessages: { - type: Boolean, - default: false - } - }, - emits: ['openImagePreview', 'replyMessage', 'replyWithText', 'openRefs'], - setup() { - const { t } = useI18n(); - const { tm } = useModuleI18n('features/chat'); - const toast = useToast() - - return { - t, - tm, - toast - }; - }, - provide() { - return { - isDark: this.isDark, - webSearchResults: () => this.webSearchResults - }; - }, - data() { - return { - copiedMessages: new Set(), - copyFailedMessages: new Set(), - isUserNearBottom: true, - scrollThreshold: 1, - scrollTimer: null, - expandedReasoning: new Set(), // Track which reasoning blocks are expanded - downloadingFiles: new Set(), // Track which files are being downloaded - elapsedTimeTimer: null, // Timer for updating elapsed time - currentTime: Date.now() / 1000, // Current time for elapsed time calculation - // 选中文本相关状态 - selectedText: { - content: '', - messageIndex: null, - position: { top: 0, left: 0 } - }, - // 图片预览 - imagePreview: { - show: false, - url: '' - }, - // Web search results mapping: { 'uuid.idx': { url, title, snippet } } - webSearchResults: {} - }; - }, - async mounted() { - this.initCodeCopyButtons(); - this.initImageClickEvents(); - this.addScrollListener(); - this.scrollToBottom(); - this.startElapsedTimeTimer(); - this.extractWebSearchResults(); - }, - updated() { - this.initCodeCopyButtons(); - this.initImageClickEvents(); - if (this.isUserNearBottom) { - this.scrollToBottom(); - } - this.extractWebSearchResults(); - }, - methods: { - // 从消息中提取 web_search_tavily 的搜索结果 - extractWebSearchResults() { - const results = {}; - - this.messages.forEach(msg => { - if (msg.content.type !== 'bot' || !Array.isArray(msg.content.message)) { - return; - } - - msg.content.message.forEach(part => { - if (part.type !== 'tool_call' || !Array.isArray(part.tool_calls)) { - return; - } - - part.tool_calls.forEach(toolCall => { - // 检查是否是支持引用解析的 web_search 工具调用 - if ( - !['web_search_baidu', 'web_search_tavily', 'web_search_bocha', 'web_search_brave', 'web_search_firecrawl'].includes(toolCall.name) || - !toolCall.result - ) { - return; - } - - try { - // 解析工具调用结果 - const resultData = typeof toolCall.result === 'string' - ? JSON.parse(toolCall.result) - : toolCall.result; - - if (resultData.results && Array.isArray(resultData.results)) { - resultData.results.forEach(item => { - if (item.index) { - results[item.index] = { - url: item.url, - title: item.title, - snippet: item.snippet - }; - } - }); - } - } catch (e) { - console.error('Failed to parse web search result:', e); - } - }); - }); - }); - - this.webSearchResults = results; - }, - - // 处理文本选择 - handleTextSelection() { - const selection = window.getSelection(); - const selectedText = selection.toString(); - - if (!selectedText.trim()) { - // 清除选中状态 - this.selectedText.content = ''; - this.selectedText.messageIndex = null; - return; - } - - // 获取被选中的元素,找到对应的message-item - const range = selection.getRangeAt(0); - const startContainer = range.startContainer; - let messageItem = null; - let node = startContainer.parentElement; - - // 遍历DOM树向上查找message-item - while (node && !node.classList.contains('message-item')) { - node = node.parentElement; - } - - messageItem = node; - - if (!messageItem) { - this.selectedText.content = ''; - this.selectedText.messageIndex = null; - return; - } - - // 获取message-item在messages数组中的索引 - const messageItems = this.$refs.messageContainer?.querySelectorAll('.message-item'); - let messageIndex = -1; - if (messageItems) { - for (let i = 0; i < messageItems.length; i++) { - if (messageItems[i] === messageItem) { - messageIndex = i; - break; - } - } - } - - if (messageIndex === -1) { - this.selectedText.content = ''; - this.selectedText.messageIndex = null; - return; - } - - // 获取选中文本的位置(相对于viewport) - const rect = selection.getRangeAt(0).getBoundingClientRect(); - - this.selectedText.content = selectedText; - this.selectedText.messageIndex = messageIndex; - this.selectedText.position = { - top: Math.max(0, rect.bottom + 5), - left: Math.max(0, (rect.left + rect.right) / 2) - }; - }, - - // 处理引用选中的文本 - handleQuoteSelected() { - if (this.selectedText.messageIndex === null) return; - - const msg = this.messages[this.selectedText.messageIndex]; - if (!msg || !msg.id) return; - - // 触发replyWithText事件,传递选中的文本内容 - this.$emit('replyWithText', { - messageId: msg.id, - selectedText: this.selectedText.content, - messageIndex: this.selectedText.messageIndex - }); - - // 清除选中状态 - this.selectedText.content = ''; - this.selectedText.messageIndex = null; - window.getSelection().removeAllRanges(); - }, - - // 检查 message 中是否有音频 - hasAudio(messageParts) { - if (!Array.isArray(messageParts)) return false; - return messageParts.some(part => part.type === 'record' && part.embedded_url); - }, - - // 获取被引用消息的内容 - getReplyContent(messageId) { - const replyMsg = this.messages.find(m => m.id === messageId); - if (!replyMsg) { - return this.tm('reply.notFound'); - } - let content = ''; - if (Array.isArray(replyMsg.content.message)) { - const textParts = replyMsg.content.message - .filter(part => part.type === 'plain' && part.text) - .map(part => part.text); - content = textParts.join(''); - } - // 截断过长内容 - if (content.length > 50) { - content = content.substring(0, 50) + '...'; - } - return content || '[媒体内容]'; - }, - - // 滚动到指定消息 - scrollToMessage(messageId) { - const msgIndex = this.messages.findIndex(m => m.id === messageId); - if (msgIndex === -1) return; - - const container = this.$refs.messageContainer; - const messageItems = container?.querySelectorAll('.message-item'); - if (messageItems && messageItems[msgIndex]) { - messageItems[msgIndex].scrollIntoView({ behavior: 'smooth', block: 'center' }); - // 高亮一下 - messageItems[msgIndex].classList.add('highlight-message'); - setTimeout(() => { - messageItems[msgIndex].classList.remove('highlight-message'); - }, 2000); - } - }, - - // Toggle reasoning expansion state - toggleReasoning(messageIndex) { - if (this.expandedReasoning.has(messageIndex)) { - this.expandedReasoning.delete(messageIndex); - } else { - this.expandedReasoning.add(messageIndex); - } - // Force reactivity - this.expandedReasoning = new Set(this.expandedReasoning); - }, - - // Check if reasoning is expanded - isReasoningExpanded(messageIndex) { - return this.expandedReasoning.has(messageIndex); - }, - - // 下载文件 - async downloadFile(file) { - if (!file.attachment_id) return; - - // 标记为下载中 - this.downloadingFiles.add(file.attachment_id); - this.downloadingFiles = new Set(this.downloadingFiles); - - try { - const response = await axios.get(fileApi.contentUrl(file.attachment_id), { - responseType: 'blob' - }); - - const url = URL.createObjectURL(response.data); - const a = document.createElement('a'); - a.href = url; - a.download = file.filename || 'file'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - setTimeout(() => URL.revokeObjectURL(url), 100); - } catch (err) { - console.error('Download file failed:', err); - } finally { - this.downloadingFiles.delete(file.attachment_id); - this.downloadingFiles = new Set(this.downloadingFiles); - } - }, - - // 复制代码到剪贴板 - tryExecCommandCopy(text) { - let textArea = null; - try { - textArea = document.createElement('textarea'); - textArea.value = text; - document.body.appendChild(textArea); - textArea.focus(); - textArea.select(); - const ok = document.execCommand('copy'); - return ok; - } catch (_) { - return false; - } finally { - try { - textArea?.remove?.(); - } catch (_) { - // ignore cleanup errors - } - } - }, - - async copyTextToClipboard(text) { - // 优先使用同步复制,尽量保留用户手势上下文; - // 在非安全来源(例如通过局域网 IP + vite --host)时成功率更高。 - if (this.tryExecCommandCopy(text)) { - return { ok: true, method: 'execCommand' }; - } - - if (navigator.clipboard?.writeText) { - try { - await navigator.clipboard.writeText(text); - return { ok: true, method: 'clipboard' }; - } catch (error) { - return { ok: false, method: 'clipboard', error }; - } - } - - return { ok: false, method: 'unavailable' }; - }, - - async copyWithFeedback(text, messageIndex = null) { - const result = await this.copyTextToClipboard(text); - const ok = !!result?.ok; - - if (messageIndex !== null && messageIndex !== undefined) { - if (ok) this.showCopySuccess(messageIndex); - else this.showCopyFailure(messageIndex); - } - - if (ok) { - this.toast?.success?.(this.t('core.common.copied')); - } else { - this.toast?.error?.(this.t('core.common.copyFailed')); - } - - return result; - }, - - buildCopyTextFromParts(messageParts) { - if (typeof messageParts === 'string') { - return messageParts.trim(); - } - if (!Array.isArray(messageParts)) { - return ''; - } - - const textContents = messageParts - .filter(part => part && typeof part === 'object' && part.type === 'plain' && part.text) - .map(part => part.text); - - let textToCopy = textContents.join('\n'); - - const imageCount = messageParts.filter(part => part?.type === 'image' && part.embedded_url).length; - if (imageCount > 0) { - if (textToCopy) textToCopy += '\n\n'; - textToCopy += `[包含 ${imageCount} 张图片]`; - } - - const hasAudio = messageParts.some(part => part?.type === 'record' && part.embedded_url); - if (hasAudio) { - if (textToCopy) textToCopy += '\n\n'; - textToCopy += '[包含音频内容]'; - } - - return String(textToCopy || '').trim(); - }, - - async copyCodeToClipboard(code) { - const text = String(code ?? ''); - if (!text) return { ok: false, method: 'empty' }; - return await this.copyWithFeedback(text, null); - }, - - // 复制bot消息到剪贴板 - async copyBotMessage(messageParts, messageIndex) { - let textToCopy = this.buildCopyTextFromParts(messageParts); - if (!textToCopy) textToCopy = '[媒体内容]'; - await this.copyWithFeedback(textToCopy, messageIndex); - }, - - // 显示复制成功提示 - showCopySuccess(messageIndex) { - if (this.copyFailedMessages.has(messageIndex)) { - this.copyFailedMessages.delete(messageIndex); - this.copyFailedMessages = new Set(this.copyFailedMessages); - } - this.copiedMessages.add(messageIndex); - this.copiedMessages = new Set(this.copiedMessages); - - // 2秒后移除成功状态 - setTimeout(() => { - this.copiedMessages.delete(messageIndex); - this.copiedMessages = new Set(this.copiedMessages); - }, 2000); - }, - - // 显示复制失败提示 - showCopyFailure(messageIndex) { - if (this.copiedMessages.has(messageIndex)) { - this.copiedMessages.delete(messageIndex); - this.copiedMessages = new Set(this.copiedMessages); - } - this.copyFailedMessages.add(messageIndex); - this.copyFailedMessages = new Set(this.copyFailedMessages); - - setTimeout(() => { - this.copyFailedMessages.delete(messageIndex); - this.copyFailedMessages = new Set(this.copyFailedMessages); - }, 2000); - }, - - // 获取复制按钮图标 - getCopyIcon(messageIndex) { - if (this.copiedMessages.has(messageIndex)) return 'mdi-check'; - if (this.copyFailedMessages.has(messageIndex)) return 'mdi-alert-circle-outline'; - return 'mdi-content-copy'; - }, - - // 检查是否为复制成功状态 - isCopySuccess(messageIndex) { - return this.copiedMessages.has(messageIndex); - }, - - // 检查是否为复制失败状态 - isCopyFailure(messageIndex) { - return this.copyFailedMessages.has(messageIndex); - }, - - // 获取复制按钮提示文本 - getCopyTitle(messageIndex) { - if (this.isCopySuccess(messageIndex)) return this.t('core.common.copied'); - if (this.isCopyFailure(messageIndex)) return this.t('core.common.copyFailed'); - return this.t('core.common.copy'); - }, - - // 获取复制图标SVG - getCopyIconSvg() { - return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>'; - }, - - // 获取成功图标SVG - getSuccessIconSvg() { - return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20,6 9,17 4,12"></polyline></svg>'; - }, - - // 获取失败图标SVG - getErrorIconSvg() { - return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="13"></line><circle cx="12" cy="16.5" r="1"></circle></svg>'; - }, - - // 初始化代码块复制按钮 - initCodeCopyButtons() { - this.$nextTick(() => { - const codeBlocks = this.$refs.messageContainer?.querySelectorAll('pre code') || []; - codeBlocks.forEach((codeBlock, index) => { - const pre = codeBlock.parentElement; - if (pre && !pre.querySelector('.copy-code-btn')) { - const button = document.createElement('button'); - button.className = 'copy-code-btn'; - button.innerHTML = this.getCopyIconSvg(); - button.title = this.t('core.common.copy'); - button.addEventListener('click', async () => { - const res = await this.copyCodeToClipboard(codeBlock.textContent || ''); - const ok = !!res?.ok; - button.innerHTML = ok ? this.getSuccessIconSvg() : this.getErrorIconSvg(); - button.style.color = ok - ? 'rgb(var(--v-theme-success))' - : 'rgb(var(--v-theme-error))'; - button.setAttribute("title", this.t(`core.common.${ok ? "copied" : "copyFailed"}`)); - setTimeout(() => { - button.innerHTML = this.getCopyIconSvg(); - button.style.color = ''; - button.setAttribute("title", this.t('core.common.copy')); - }, 2000); - }); - pre.style.position = 'relative'; - pre.appendChild(button); - } - }); - }); - }, - - initImageClickEvents() { - this.$nextTick(() => { - // 查找所有动态生成的图片(在markdown-content中) - const images = document.querySelectorAll('.markdown-content img'); - images.forEach((img) => { - if (!img.hasAttribute('data-click-enabled')) { - img.style.cursor = 'pointer'; - img.setAttribute('data-click-enabled', 'true'); - img.onclick = () => this.openImagePreview(img.src); - } - }); - }); - }, - - scrollToBottom() { - this.$nextTick(() => { - const container = this.$refs.messageContainer; - if (container) { - container.scrollTop = container.scrollHeight; - this.isUserNearBottom = true; // 程序滚动到底部后标记用户在底部 - } - }); - }, - - // 添加滚动事件监听器 - addScrollListener() { - const container = this.$refs.messageContainer; - if (container) { - container.addEventListener('scroll', this.throttledHandleScroll); - } - }, - - // 节流处理滚动事件 - throttledHandleScroll() { - if (this.scrollTimer) return; - - this.scrollTimer = setTimeout(() => { - this.handleScroll(); - this.scrollTimer = null; - }, 50); // 50ms 节流 - }, - - // 处理滚动事件 - handleScroll() { - const container = this.$refs.messageContainer; - if (container) { - const { scrollTop, scrollHeight, clientHeight } = container; - const distanceFromBottom = scrollHeight - (scrollTop + clientHeight); - - // 判断用户是否在底部附近 - this.isUserNearBottom = distanceFromBottom <= this.scrollThreshold; - } - }, - - // 组件销毁时移除监听器 - beforeUnmount() { - const container = this.$refs.messageContainer; - if (container) { - container.removeEventListener('scroll', this.throttledHandleScroll); - } - // 清理定时器 - if (this.scrollTimer) { - clearTimeout(this.scrollTimer); - this.scrollTimer = null; - } - // 清理 elapsed time 计时器 - if (this.elapsedTimeTimer) { - clearInterval(this.elapsedTimeTimer); - this.elapsedTimeTimer = null; - } - }, - - // 格式化消息时间,支持别名显示 - formatMessageTime(dateStr) { - if (!dateStr) return ''; - - const date = new Date(dateStr); - const now = new Date(); - - // 获取本地时间的日期部分 - const dateDay = new Date(date.getFullYear(), date.getMonth(), date.getDate()); - const todayDay = new Date(now.getFullYear(), now.getMonth(), now.getDate()); - const yesterdayDay = new Date(todayDay); - yesterdayDay.setDate(yesterdayDay.getDate() - 1); - - // 格式化时间 HH:MM - const hours = date.getHours().toString().padStart(2, '0'); - const minutes = date.getMinutes().toString().padStart(2, '0'); - const timeStr = `${hours}:${minutes}`; - - // 判断是今天、昨天还是更早 - if (dateDay.getTime() === todayDay.getTime()) { - return `${this.tm('time.today')} ${timeStr}`; - } else if (dateDay.getTime() === yesterdayDay.getTime()) { - return `${this.tm('time.yesterday')} ${timeStr}`; - } else { - // 更早的日期显示完整格式 - const month = (date.getMonth() + 1).toString().padStart(2, '0'); - const day = date.getDate().toString().padStart(2, '0'); - return `${month}-${day} ${timeStr}`; - } - }, - - // Start timer for updating elapsed time - startElapsedTimeTimer() { - // Update every 12ms for sub-second precision, then every second after 1s - let fastUpdateCount = 0; - const fastUpdateInterval = 12; - const slowUpdateInterval = 1000; - - const updateTime = () => { - this.currentTime = Date.now() / 1000; - - // Check if there are any running tool calls - const hasRunningToolCalls = this.messages.some(msg => - Array.isArray(msg.content.message) && msg.content.message.some(part => - part.type === 'tool_call' && part.tool_calls?.some(tc => !tc.finished_ts) - ) - ); - - if (hasRunningToolCalls) { - // Check if any running tool call is under 1 second - const hasSubSecondToolCall = this.messages.some(msg => - Array.isArray(msg.content.message) && msg.content.message.some(part => - part.type === 'tool_call' && part.tool_calls?.some(tc => - !tc.finished_ts && (this.currentTime - tc.ts) < 1 - ) - ) - ); - - if (hasSubSecondToolCall) { - fastUpdateCount++; - this.elapsedTimeTimer = setTimeout(updateTime, fastUpdateInterval); - } else { - this.elapsedTimeTimer = setTimeout(updateTime, slowUpdateInterval); - } - } else { - // No running tool calls, check again after 1 second - this.elapsedTimeTimer = setTimeout(updateTime, slowUpdateInterval); - } - }; - - updateTime(); - }, - - // Get elapsed time string for a tool call - getElapsedTime(startTs) { - const elapsed = this.currentTime - startTs; - return this.formatDuration(elapsed); - }, - - // Format duration in seconds to human readable string - formatDuration(seconds) { - if (seconds < 1) { - return `${Math.round(seconds * 1000)}ms`; - } else if (seconds < 60) { - return `${seconds.toFixed(1)}s`; - } else { - const minutes = Math.floor(seconds / 60); - const secs = Math.round(seconds % 60); - return `${minutes}m ${secs}s`; - } - }, - - // Get input tokens (input_other + input_cached) - getInputTokens(tokenUsage) { - if (!tokenUsage) return 0; - return (tokenUsage.input_other || 0) + (tokenUsage.input_cached || 0); - }, - - // Format agent duration - formatAgentDuration(agentStats) { - if (!agentStats) return ''; - const duration = agentStats.end_time - agentStats.start_time; - return this.formatDuration(duration); - }, - - // Format time to first token - formatTTFT(ttft) { - if (!ttft || ttft <= 0) return ''; - return this.formatDuration(ttft); - }, - - // 打开图片预览 - openImagePreview(url) { - this.imagePreview.url = url; - this.imagePreview.show = true; - }, - - // 关闭图片预览 - closeImagePreview() { - this.imagePreview.show = false; - setTimeout(() => { - this.imagePreview.url = ''; - }, 300); - }, - - // Open refs sidebar - openRefsSidebar(refs) { - this.$emit('openRefs', refs); - } - } -} -</script> - -<style scoped> -:deep(.hr-node) { - margin-top: 1.25rem; - margin-bottom: 1.25rem; - opacity: 0.5; - border-top-width: .3px; -} - -:deep(.paragraph-node) { - margin: .5rem 0; - line-height: 1.7; - margin-block: 1rem; -} - -:deep(.list-node) { - margin-top: .5rem; - margin-bottom: .5rem; -} - -:deep(.mermaid-block-header) { - gap: 8px; -} - -:deep(code.bg-secondary) { - background-color: #ececec !important; - color: #0d0d0d !important; -} - -:deep(code.rounded) { - border-radius: 6px !important; -} - -.messages-container.is-dark :deep(code.bg-secondary) { - background-color: #424242 !important; - color: #ffffff !important; -} - -.messages-container.is-dark :deep(.code-block-container) { - background-color: #1f1f1f !important; -} - -/* 基础动画 */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(0); - } - - to { - opacity: 1; - transform: translateY(0); - } -} - -.messages-container { - height: 100%; - max-height: 100%; - overflow-y: auto; - overscroll-behavior-y: contain; - padding: 16px; - display: flex; - flex-direction: column; - flex: 1; - min-height: 0; - position: relative; -} - -.loading-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - display: flex; - justify-content: center; - align-items: center; - z-index: 10; - background-color: rgba(255, 255, 255, 0.7); - transition: opacity 0.3s ease; -} - -.loading-overlay.is-dark { - background-color: rgba(30, 30, 30, 0.7); -} - -.message-list.loading-blur { - opacity: 0.5; - transition: opacity 0.3s ease; - pointer-events: none; -} - -.message-bubble { - padding: 2px 16px; - border-radius: 12px; -} - -.loading-container { - display: flex; - align-items: center; - gap: 12px; - padding: 8px 0; - margin-top: 8px; -} - -.loading-text { - font-size: 14px; - color: var(--v-theme-secondaryText); - animation: pulse 1.5s ease-in-out infinite; -} - -@keyframes pulse { - - 0%, - 100% { - opacity: 0.6; - } - - 50% { - opacity: 1; - } -} - - - -@media (max-width: 768px) { - .messages-container { - padding: 8px; - } - - .message-list { - max-width: 100%; - } - - .message-item { - padding: 0; - } - - .message-bubble { - padding: 2px 12px; - } - - .bot-message { - flex-direction: column; - align-items: flex-start; - gap: 8px; - width: 100%; - } - - .bot-message-content { - max-width: 100% !important; - width: 100% !important; - } - - .bot-bubble { - width: 100% !important; - max-width: 100% !important; - } - - .bot-avatar { - margin-left: 4px; - } -} - -/* 消息列表样式 */ -.message-list { - max-width: 900px; - margin: 0 auto; - width: 100%; -} - -.message-item { - margin-bottom: 12px; - animation: fadeIn 0.3s ease-out; -} - -.user-message { - display: flex; - justify-content: flex-end; - align-items: flex-start; - gap: 12px; -} - -.bot-message { - display: flex; - justify-content: flex-start; - align-items: flex-start; - gap: 12px; -} - -.bot-message-content { - display: flex; - flex-direction: column; - align-items: flex-start; - max-width: 80%; - position: relative; -} - -.message-actions { - display: flex; - align-items: center; - gap: 8px; - opacity: 0; - transition: opacity 0.2s ease; - margin-left: 16px; -} - -/* 最后一条消息始终显示操作按钮 */ -.message-item:last-child .message-actions { - opacity: 1; -} - -.message-time { - font-size: 12px; - color: var(--v-theme-secondaryText); - opacity: 0.7; - white-space: nowrap; -} - -/* Agent Stats Info Icon */ -.stats-info-icon { - margin-left: 6px; - color: var(--v-theme-secondaryText); - opacity: 0.6; - cursor: pointer; - transition: opacity 0.2s ease; -} - -.stats-info-icon:hover { - opacity: 1; -} - -.bot-message:hover .message-actions { - opacity: 1; -} - -.copy-message-btn { - opacity: 0.6; - transition: all 0.2s ease; - color: var(--v-theme-secondary); -} - -.copy-message-btn:hover { - opacity: 1; - background-color: rgba(103, 58, 183, 0.1); -} - -.copy-message-btn.copy-success { - color: rgb(var(--v-theme-success)); - opacity: 1; -} - -.copy-message-btn.copy-success:hover { - color: rgb(var(--v-theme-success)); - background-color: rgba(var(--v-theme-success), 0.1); -} - -.copy-message-btn.copy-failed { - color: rgb(var(--v-theme-error)); - opacity: 1; -} - -.copy-message-btn.copy-failed:hover { - color: rgb(var(--v-theme-error)); - background-color: rgba(var(--v-theme-error), 0.1); -} - -.reply-message-btn { - opacity: 0.6; - transition: all 0.2s ease; - color: var(--v-theme-secondary); -} - -.reply-message-btn:hover { - opacity: 1; - background-color: rgba(103, 58, 183, 0.1); -} - -/* 引用消息显示样式 */ -.reply-quote { - display: flex; - align-items: center; - gap: 6px; - padding: 6px 10px; - margin-bottom: 8px; - background-color: rgba(103, 58, 183, 0.08); - border-left: 3px solid var(--v-theme-secondary); - border-radius: 4px; - cursor: pointer; - transition: background-color 0.2s ease; -} - -.reply-quote:hover { - background-color: rgba(103, 58, 183, 0.15); -} - -.reply-quote-icon { - color: var(--v-theme-secondary); - flex-shrink: 0; -} - -.reply-quote-text { - font-size: 13px; - color: var(--v-theme-secondaryText); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* 消息高亮动画 */ -.highlight-message { - animation: highlightPulse 2s ease-out; -} - -@keyframes highlightPulse { - 0% { - background-color: rgba(103, 58, 183, 0.3); - } - - 100% { - background-color: transparent; - } -} - - -.user-bubble { - color: var(--v-theme-primaryText); - padding: 12px 18px; - font-size: 15px; - max-width: 60%; - border-radius: 1.5rem; -} - -.bot-bubble { - border: 1px solid var(--v-theme-border); - color: var(--v-theme-primaryText); - font-size: 16px; - max-width: 100%; - padding-left: 12px; -} - -.user-avatar, -.bot-avatar { - align-self: flex-start; - margin-top: 12px; -} - -/* 附件样式 */ -.image-attachments { - display: flex; - gap: 8px; - margin-top: 8px; - flex-wrap: wrap; -} - -.image-attachment { - position: relative; - display: inline-block; -} - -.attached-image { - width: 120px; - height: 120px; - object-fit: cover; - border-radius: 12px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - transition: transform 0.2s ease; -} - -.audio-attachment { - margin-top: 8px; - min-width: 250px; -} - -/* 包含音频的消息气泡最小宽度 */ -.message-bubble.has-audio { - min-width: 280px; -} - -.audio-player { - width: 100%; - height: 36px; - border-radius: 18px; -} - -/* 文件附件样式 */ -.file-attachments, -.embedded-files { - margin-top: 8px; - display: flex; - flex-direction: column; - gap: 6px; -} - -.file-attachment, -.embedded-file { - display: flex; - align-items: center; -} - -.file-link { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 8px 12px; - background-color: rgba(var(--v-theme-primary), 0.08); - border: 1px solid rgba(var(--v-theme-primary), 0.2); - border-radius: 8px; - color: rgb(var(--v-theme-primary)); - text-decoration: none; - font-size: 14px; - transition: all 0.2s ease; - max-width: 300px; -} - -.file-link-download { - cursor: pointer; -} - -.download-icon { - margin-left: 4px; - opacity: 0.7; -} - -.file-icon { - flex-shrink: 0; - color: rgb(var(--v-theme-primary)); -} - -.file-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.file-link.is-dark:hover { - background-color: rgba(255, 255, 255, 0.1) !important; - border-color: rgba(255, 255, 255, 0.2) !important; -} - -/* 动画类 */ -.fade-in { - animation: fadeIn 0.3s ease-in-out; -} - -/* 浮动引用按钮样式 */ -.selection-quote-button { - position: fixed; - z-index: 1000; - display: flex; - align-items: center; - gap: 8px; - pointer-events: all; -} - -.quote-btn { - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - font-size: 14px; - padding: 4px 24px; - background-color: #f6f4fa !important; - color: #333333 !important; -} - -.quote-btn:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); - background-color: #f6f4fa !important; -} - -/* 深色主题 */ -.quote-btn.dark-mode { - background-color: #2d2d2d !important; - color: #ffffff !important; -} - - - -</style> - -<style> -.markdown-content { - max-width: 100%; - line-height: 1.6; -} - - -/* Stats Menu 样式 */ -.stats-menu-card { - border-radius: 8px !important; - min-width: 160px; -} - -.stats-menu-content { - padding: 12px 16px !important; - display: flex; - flex-direction: column; - gap: 8px; -} - -.stats-menu-row { - display: flex; - justify-content: space-between; - align-items: center; - gap: 20px; -} - -.stats-menu-label { - font-size: 13px; - color: var(--v-theme-secondaryText); -} - -.stats-menu-value { - font-size: 13px; - font-weight: 600; - font-family: 'Fira Code', 'Consolas', monospace; - color: var(--v-theme-primaryText); -} - -/* 图片预览样式 */ -.image-preview-overlay { - z-index: 9999; - display: flex; - align-items: center; - justify-content: center; -} - -.image-preview-container { - position: relative; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; -} - -.preview-image { - max-width: 90vw; - max-height: 90vh; - object-fit: contain; - border-radius: 8px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); - cursor: pointer; -} - -.close-preview-btn { - position: fixed; - top: 20px; - right: 20px; -} -</style> diff --git a/legacy-dashboard/src/components/chat/ProjectDialog.vue b/legacy-dashboard/src/components/chat/ProjectDialog.vue deleted file mode 100644 index 3a00d545..00000000 --- a/legacy-dashboard/src/components/chat/ProjectDialog.vue +++ /dev/null @@ -1,162 +0,0 @@ -<template> - <v-dialog v-model="isOpen" max-width="640" @update:model-value="handleDialogChange"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ isEditing ? tm('project.edit') : tm('project.create') }} - </v-card-title> - <v-card-text> - <v-text-field v-model="form.emoji" :label="tm('project.emoji')" variant="outlined" hide-details class="mb-3" /> - <v-text-field v-model="form.title" :label="tm('project.name')" variant="outlined" hide-details class="mb-3" autofocus - @keyup.enter="handleSave" /> - <v-textarea v-model="form.description" :label="tm('project.description')" variant="outlined" hide-details rows="3" /> - <v-divider class="my-4" /> - <v-select v-model="form.workspace_type" :items="workspaceTypeItems" item-title="label" item-value="value" - :label="tm('project.workspace.type')" variant="outlined" hide-details class="mb-3" /> - <v-text-field v-if="form.workspace_type === 'custom'" v-model="form.workspace_path" - :label="tm('project.workspace.path')" variant="outlined" hide-details class="mb-1" /> - <v-alert - v-if="props.errorMessage" - class="mt-3" - type="error" - variant="tonal" - density="compact" - > - {{ props.errorMessage }} - </v-alert> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="handleCancel" color="grey-darken-1" :disabled="props.saving">{{ t('core.common.cancel') }}</v-btn> - <v-btn variant="text" @click="handleSave" color="primary" :disabled="!canSave || props.saving" :loading="props.saving">{{ t('core.common.save') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup lang="ts"> -import { computed, ref, watch } from 'vue'; -import { useI18n, useModuleI18n } from '@/i18n/composables'; - -export type WorkspaceType = 'session' | 'project' | 'custom'; - -export interface Project { - project_id: string; - title: string; - emoji?: string; - description?: string; - workspace_type?: WorkspaceType; - workspace_path?: string | null; - resolved_workspace_path?: string | null; - created_at: string; - updated_at: string; -} - -export interface ProjectFormData { - emoji: string; - title: string; - description: string; - workspace_type: WorkspaceType; - workspace_path: string; -} - -interface Props { - modelValue: boolean; - project?: Project | null; - errorMessage?: string; - saving?: boolean; -} - -const props = withDefaults(defineProps<Props>(), { - modelValue: false, - project: null, - errorMessage: '', - saving: false -}); - -const emit = defineEmits<{ - 'update:modelValue': [value: boolean]; - save: [formData: ProjectFormData, projectId?: string]; -}>(); - -const { t } = useI18n(); -const { tm } = useModuleI18n('features/chat'); - -const isOpen = ref(props.modelValue); -const isEditing = ref(false); -const form = ref<ProjectFormData>({ - emoji: '📁', - title: '', - description: '', - workspace_type: 'project', - workspace_path: '' -}); -const workspaceTypeItems = computed(() => [ - { label: tm('project.workspace.project'), value: 'project' }, - { label: tm('project.workspace.session'), value: 'session' }, - { label: tm('project.workspace.custom'), value: 'custom' } -]); -const canSave = computed(() => { - if (!form.value.title.trim()) return false; - if (form.value.workspace_type !== 'custom') return true; - return form.value.workspace_path.trim().length > 0; -}); - -watch(() => props.modelValue, (newVal) => { - isOpen.value = newVal; - if (newVal) { - if (props.project) { - isEditing.value = true; - form.value = { - emoji: props.project.emoji || '📁', - title: props.project.title, - description: props.project.description || '', - workspace_type: props.project.workspace_type || 'session', - workspace_path: props.project.workspace_path || '' - }; - } else { - isEditing.value = false; - form.value = { - emoji: '📁', - title: '', - description: '', - workspace_type: 'project', - workspace_path: '' - }; - } - } -}); - -watch(() => form.value.workspace_type, (workspaceType) => { - if (workspaceType !== 'custom') { - form.value.workspace_path = ''; - } -}); - -function handleDialogChange(value: boolean) { - emit('update:modelValue', value); -} - -function handleCancel() { - isOpen.value = false; - emit('update:modelValue', false); -} - -function handleSave() { - if (!canSave.value) { - return; - } - - emit('save', { - ...form.value, - workspace_path: form.value.workspace_path.trim() - }, props.project?.project_id); -} - -</script> - -<style scoped> -.dialog-title { - font-size: 22px; - font-weight: 500; -} -</style> diff --git a/legacy-dashboard/src/components/chat/ProjectList.vue b/legacy-dashboard/src/components/chat/ProjectList.vue deleted file mode 100644 index 8e55fe68..00000000 --- a/legacy-dashboard/src/components/chat/ProjectList.vue +++ /dev/null @@ -1,462 +0,0 @@ -<template> - <section class="sidebar-section project-list-shell"> - <div class="sidebar-section-header"> - <span>{{ tm("project.title") }}</span> - <v-btn - icon - size="x-small" - variant="text" - class="section-action-btn" - :title="tm('project.create')" - @click="$emit('createProject')" - > - <Plus :size="18" /> - </v-btn> - </div> - - <div class="project-list-wrap"> - <div v-for="project in projects" :key="project.project_id"> - <div - class="project-row project-item" - :class="{ active: selectedProjectId === project.project_id }" - role="button" - tabindex="0" - @click="handleProjectClick(project)" - @keydown.enter="handleProjectClick(project)" - @keydown.space.prevent="handleProjectClick(project)" - > - <span class="project-emoji">{{ project.emoji || "📁" }}</span> - <span class="project-title-wrap"> - <span class="project-title">{{ project.title }}</span> - <ChevronDown - v-if="isProjectExpanded(project.project_id)" - :size="16" - class="project-chevron" - /> - <ChevronRight v-else :size="16" class="project-chevron" /> - </span> - <span class="project-actions" @click.stop> - <v-btn - icon - size="x-small" - variant="text" - class="project-action-btn" - :title="tm('project.edit')" - @click="$emit('editProject', project)" - > - <Pencil :size="15" /> - </v-btn> - <v-btn - icon - size="x-small" - variant="text" - class="project-action-btn" - :title="tm('actions.deleteChat')" - @click="handleDeleteProject(project)" - > - <Trash2 :size="15" /> - </v-btn> - </span> - </div> - - <Transition name="project-session-fade"> - <div - v-if="isProjectExpanded(project.project_id)" - class="project-session-list" - > - <div - v-if="loadingProjectIds.includes(project.project_id)" - class="project-session-empty" - > - {{ tm("project.loadingSessions") }} - </div> - <template v-else-if="projectSessionList(project.project_id).length"> - <div - v-for="session in projectSessionList(project.project_id)" - :key="session.session_id" - class="project-session-row" - :class="{ active: activeSessionId === session.session_id }" - role="button" - tabindex="0" - @click="$emit('selectSession', session.session_id)" - @keydown.enter="$emit('selectSession', session.session_id)" - @keydown.space.prevent="$emit('selectSession', session.session_id)" - > - <span class="project-session-title"> - {{ sessionTitle(session) }} - </span> - <span class="project-session-actions" @click.stop> - <v-btn - icon - size="x-small" - variant="text" - class="project-action-btn" - :title="tm('conversation.editDisplayName')" - @click=" - $emit( - 'editSessionTitle', - session.session_id, - session.display_name || '', - ) - " - > - <Pencil :size="15" /> - </v-btn> - <v-btn - icon - size="x-small" - variant="text" - class="project-action-btn" - :title="tm('actions.deleteChat')" - @click="handleDeleteSession(project.project_id, session)" - > - <Trash2 :size="15" /> - </v-btn> - </span> - <v-progress-circular - v-if="sessionRunning(session.session_id)" - class="project-session-progress" - indeterminate - size="14" - width="2" - /> - </div> - </template> - <div v-else class="project-session-empty"> - {{ tm("project.noSessions") }} - </div> - </div> - </Transition> - </div> - </div> - </section> -</template> - -<script setup lang="ts"> -import { ref, watch } from "vue"; -import { - ChevronDown, - ChevronRight, - Pencil, - Plus, - Trash2, -} from "@lucide/vue"; -import { useModuleI18n } from "@/i18n/composables"; -import { askForConfirmation, useConfirmDialog } from "@/utils/confirmDialog"; - -export interface Project { - project_id: string; - title: string; - emoji?: string; - description?: string; - workspace_type?: "session" | "project" | "custom"; - workspace_path?: string | null; - resolved_workspace_path?: string | null; - created_at: string; - updated_at: string; -} - -export interface ProjectSession { - session_id: string; - display_name?: string | null; - updated_at: string; -} - -interface Props { - projects: Project[]; - projectSessions: Record<string, ProjectSession[]>; - loadingProjectIds: string[]; - selectedProjectId?: string | null; - activeSessionId?: string | null; - isSessionRunning?: (sessionId: string) => boolean; -} - -const props = withDefaults(defineProps<Props>(), { - selectedProjectId: null, - activeSessionId: null, -}); - -const emit = defineEmits<{ - selectProject: [projectId: string]; - createProject: []; - editProject: [project: Project]; - deleteProject: [projectId: string]; - toggleProject: [projectId: string, expanded: boolean]; - selectSession: [sessionId: string]; - editSessionTitle: [sessionId: string, title: string]; - deleteSession: [sessionId: string, projectId: string]; -}>(); - -const { tm } = useModuleI18n("features/chat"); -const confirmDialog = useConfirmDialog(); - -const expandedProjectIds = ref<Set<string>>(readExpandedProjectIds()); - -watch( - () => props.selectedProjectId, - (projectId) => { - if (projectId) { - setProjectExpanded(projectId, true); - } - }, -); - -watch( - () => props.projects.map((project) => project.project_id).join(","), - () => { - const validProjectIds = new Set( - props.projects.map((project) => project.project_id), - ); - expandedProjectIds.value.forEach((projectId) => { - if (validProjectIds.has(projectId)) { - emit("toggleProject", projectId, true); - } - }); - }, - { immediate: true }, -); - -function readExpandedProjectIds() { - try { - const raw = localStorage.getItem("chat.projectExpandedIds"); - const parsed = raw ? JSON.parse(raw) : []; - return new Set(Array.isArray(parsed) ? parsed.filter(Boolean) : []); - } catch { - return new Set<string>(); - } -} - -function persistExpandedProjectIds() { - localStorage.setItem( - "chat.projectExpandedIds", - JSON.stringify([...expandedProjectIds.value]), - ); -} - -function isProjectExpanded(projectId: string) { - return expandedProjectIds.value.has(projectId); -} - -function setProjectExpanded(projectId: string, expanded: boolean) { - const next = new Set(expandedProjectIds.value); - if (expanded) { - next.add(projectId); - } else { - next.delete(projectId); - } - expandedProjectIds.value = next; - persistExpandedProjectIds(); - emit("toggleProject", projectId, expanded); -} - -function handleProjectClick(project: Project) { - const nextExpanded = !isProjectExpanded(project.project_id); - setProjectExpanded(project.project_id, nextExpanded); - emit("selectProject", project.project_id); -} - -function projectSessionList(projectId: string) { - return props.projectSessions[projectId] || []; -} - -function sessionRunning(sessionId: string) { - return props.isSessionRunning?.(sessionId) || false; -} - -function sessionTitle(session: ProjectSession) { - return session.display_name?.trim() || tm("conversation.newConversation"); -} - -async function handleDeleteProject(project: Project) { - const message = tm("project.confirmDelete", { title: project.title }); - if (await askForConfirmation(message, confirmDialog)) { - emit("deleteProject", project.project_id); - } -} - -async function handleDeleteSession(projectId: string, session: ProjectSession) { - const message = tm("conversation.confirmDelete", { - name: sessionTitle(session), - }); - if (await askForConfirmation(message, confirmDialog)) { - emit("deleteSession", session.session_id, projectId); - } -} - -</script> - -<style scoped> -.project-list-shell { - margin-top: 2px; -} - -.sidebar-section-header { - min-height: 24px; - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 10px 4px; - color: var(--chat-section-label); - font-size: 12px; - font-weight: 500; -} - -.section-action-btn, -.project-action-btn { - color: var(--chat-muted); -} - -.section-action-btn :deep(svg), -.project-action-btn :deep(svg), -.project-chevron { - flex: 0 0 auto; - stroke-width: 2; -} - -.section-action-btn { - width: 36px; - height: 36px; - min-width: 36px; -} - -.section-action-btn:hover, -.project-action-btn:hover { - color: rgb(var(--v-theme-on-surface)); -} - -.project-list-wrap, -.project-session-list { - display: flex; - flex-direction: column; - gap: 2px; -} - -.project-row, -.project-session-row { - width: 100%; - min-height: 30px; - border: 0; - border-radius: 8px; - background: transparent; - color: inherit; - display: flex; - align-items: center; - gap: 8px; - padding: 4px 56px 4px 10px; - position: relative; - box-sizing: border-box; - cursor: pointer; - text-align: left; -} - -.project-row:hover, -.project-row.active, -.project-session-row:hover, -.project-session-row.active { - background: var(--chat-session-active-bg); -} - -.project-emoji { - width: 18px; - flex: 0 0 18px; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 15px; -} - -.project-title-wrap { - min-width: 0; - flex: 1; - display: inline-flex; - align-items: center; - gap: 3px; -} - -.project-title, -.project-session-title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 14px; - font-weight: 500; -} - -.project-title { - flex: 0 1 auto; -} - -.project-session-title { - flex: 1; -} - -.project-chevron { - flex: 0 0 auto; - color: var(--chat-muted); -} - -.project-actions, -.project-session-actions { - display: flex; - align-items: center; - gap: 2px; - flex-shrink: 0; - opacity: 0; - pointer-events: none; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - visibility: hidden; -} - -.project-row:hover .project-actions, -.project-row:focus-within .project-actions, -.project-session-row:hover .project-session-actions, -.project-session-row:focus-within .project-session-actions { - opacity: 1; - pointer-events: auto; - visibility: visible; -} - -.project-session-list { - padding: 2px 0 4px 26px; -} - -.project-session-fade-enter-active { - transition: opacity 0.1s ease-out; -} - -.project-session-fade-leave-active { - transition: opacity 0.08s ease-in; -} - -.project-session-fade-enter-from, -.project-session-fade-leave-to { - opacity: 0; -} - -.project-session-progress { - position: absolute; - right: 4px; - top: 50%; - transform: translateY(-50%); - flex-shrink: 0; - transition: - opacity 0.14s ease, - visibility 0.14s ease; -} - -.project-session-row:hover .project-session-progress, -.project-session-row:focus-within .project-session-progress { - opacity: 0; - visibility: hidden; -} - -.project-session-empty { - padding: 5px 10px; - color: var(--chat-muted); - font-size: 13px; -} -</style> diff --git a/legacy-dashboard/src/components/chat/ProjectView.vue b/legacy-dashboard/src/components/chat/ProjectView.vue deleted file mode 100644 index 04a499ba..00000000 --- a/legacy-dashboard/src/components/chat/ProjectView.vue +++ /dev/null @@ -1,363 +0,0 @@ -<template> - <div class="project-sessions-container fade-in"> - <section class="project-header"> - <div class="project-title-row"> - <span class="project-header-emoji" aria-hidden="true"> - {{ project?.emoji || "📁" }} - </span> - <div class="project-title-copy"> - <h2 class="project-header-title">{{ project?.title }}</h2> - <p class="project-header-description" v-if="project?.description"> - {{ project.description }} - </p> - </div> - </div> - <div - v-if="workspaceSummary" - class="project-workspace-summary" - :title="workspaceSummary" - > - <FolderCog :size="15" /> - <span>{{ workspaceSummary }}</span> - </div> - </section> - - <div class="project-input-slot"> - <slot></slot> - </div> - - <section class="project-sessions-list"> - <div v-if="sessions.length > 0" class="project-session-list"> - <button - v-for="session in sessions" - :key="session.session_id" - type="button" - @click="$emit('selectSession', session.session_id)" - class="project-session-item" - > - <span class="project-session-copy"> - <span class="project-session-title"> - {{ session.display_name || tm("conversation.newConversation") }} - </span> - <span class="project-session-time"> - {{ formatDate(session.updated_at) }} - </span> - </span> - <span class="session-actions"> - <button - type="button" - class="project-session-action" - :title="tm('conversation.editDisplayName')" - @click.stop=" - $emit( - 'editSessionTitle', - session.session_id, - session.display_name ?? '', - ) - " - > - <Pencil :size="17" /> - </button> - <button - type="button" - class="project-session-action delete-session-btn" - :title="tm('actions.deleteChat')" - @click.stop="handleDeleteSession(session)" - > - <Trash2 :size="17" /> - </button> - </span> - </button> - </div> - <div v-else class="no-sessions-in-project"> - <MessageSquare :size="22" /> - <p>{{ tm("project.noSessions") }}</p> - </div> - </section> - </div> -</template> - -<script setup lang="ts"> -import { computed } from "vue"; -import { FolderCog, MessageSquare, Pencil, Trash2 } from "@lucide/vue"; -import { useModuleI18n } from "@/i18n/composables"; -import type { Project } from "@/components/chat/ProjectList.vue"; -import { askForConfirmation, useConfirmDialog } from "@/utils/confirmDialog"; - -interface Session { - session_id: string; - display_name?: string | null; - updated_at: string; -} - -interface Props { - project?: Project | null; - sessions: Session[]; -} - -const props = defineProps<Props>(); - -const emit = defineEmits<{ - selectSession: [sessionId: string]; - editSessionTitle: [sessionId: string, title: string]; - deleteSession: [sessionId: string]; -}>(); - -const { tm } = useModuleI18n("features/chat"); - -const confirmDialog = useConfirmDialog(); - -const workspaceSummary = computed(() => { - const project = props.project; - if (!project) return ""; - const workspaceType = project.workspace_type || "session"; - if (workspaceType === "session") { - return tm("project.workspace.session"); - } - const path = project.resolved_workspace_path || project.workspace_path || ""; - if (workspaceType === "project") { - return path - ? `${tm("project.workspace.project")} · ${path}` - : tm("project.workspace.project"); - } - return path - ? `${tm("project.workspace.custom")} · ${path}` - : tm("project.workspace.custom"); -}); - -function formatDate(dateString: string): string { - return new Date(dateString).toLocaleString(); -} - -async function handleDeleteSession(session: Session) { - const sessionTitle = - session.display_name || tm("conversation.newConversation"); - const message = tm("conversation.confirmDelete", { name: sessionTitle }); - if (await askForConfirmation(message, confirmDialog)) { - emit("deleteSession", session.session_id); - } -} -</script> - -<style scoped> -.project-sessions-container { - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - padding: clamp(44px, 8vh, 88px) 32px 48px; - overflow-y: auto; -} - -.project-header { - width: var(--chat-content-width, 76%); - max-width: var(--chat-content-max-width, 760px); - margin: 0 auto 18px; -} - -.project-title-row { - display: flex; - align-items: center; - gap: 10px; - min-width: 0; -} - -.project-header-emoji { - flex: 0 0 auto; - font-size: 28px; - line-height: 1; -} - -.project-title-copy { - min-width: 0; -} - -.project-header-title { - margin: 0; - overflow: hidden; - color: rgb(var(--v-theme-on-surface)); - font-size: 28px; - font-weight: 750; - line-height: 1.12; - text-overflow: ellipsis; - white-space: nowrap; -} - -.project-header-description { - margin: 5px 0 0; - color: rgba(var(--v-theme-on-surface), 0.56); - font-size: 14px; - line-height: 1.45; -} - -.project-workspace-summary { - display: flex; - align-items: center; - gap: 8px; - max-width: 100%; - margin-top: 12px; - color: rgba(var(--v-theme-on-surface), 0.52); - font-size: 12px; - line-height: 18px; -} - -.project-workspace-summary svg { - flex: 0 0 auto; - stroke-width: 2; -} - -.project-workspace-summary span { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.project-input-slot { - width: 100%; - margin-bottom: 30px; -} - -.project-sessions-list { - width: var(--chat-content-width, 76%); - max-width: var(--chat-content-max-width, 760px); - background-color: transparent !important; -} - -.project-session-list { - display: flex; - flex-direction: column; - gap: 2px; -} - -.project-session-item { - width: 100%; - min-height: 54px; - border: 0; - border-radius: 10px; - background: transparent !important; - color: rgb(var(--v-theme-on-surface)); - display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - padding: 8px 10px 8px 0; - cursor: pointer; - text-align: left; -} - -.project-session-item:hover { - background-color: rgba(var(--v-theme-on-surface), 0.04) !important; -} - -.project-session-item:hover .session-actions { - opacity: 1; - visibility: visible; -} - -.project-session-copy { - min-width: 0; - display: flex; - flex: 1; - flex-direction: column; - gap: 2px; -} - -.project-session-title { - overflow: hidden; - font-size: 15px; - font-weight: 520; - line-height: 20px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.project-session-time { - color: rgba(var(--v-theme-on-surface), 0.52); - font-size: 13px; - line-height: 18px; -} - -.session-actions { - display: flex; - align-items: center; - gap: 4px; - flex: 0 0 auto; - opacity: 0; - transition: opacity 0.14s ease; - visibility: hidden; -} - -.project-session-action { - width: 32px; - height: 32px; - border: 0; - border-radius: 8px; - background: transparent; - color: rgba(var(--v-theme-on-surface), 0.72); - display: inline-flex; - align-items: center; - justify-content: center; - cursor: pointer; -} - -.project-session-action:hover { - background: rgba(var(--v-theme-on-surface), 0.06); - color: rgb(var(--v-theme-on-surface)); -} - -.delete-session-btn { - color: rgb(var(--v-theme-error)); -} - -.no-sessions-in-project { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 24px 0; - color: rgba(var(--v-theme-on-surface), 0.52); -} - -.no-sessions-in-project p { - margin: 8px 0 0; - font-size: 14px; -} - -@media (max-width: 768px) { - .project-sessions-container { - padding: 28px 14px 32px; - } - - .project-header, - .project-sessions-list { - width: calc(100% - 20px); - max-width: 100%; - } - - .project-header-title { - font-size: 24px; - } - - .session-actions { - opacity: 1; - visibility: visible; - } -} - -.fade-in { - animation: fadeIn 0.3s ease-in-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(10px); - } - - to { - opacity: 1; - transform: translateY(0); - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/ProviderConfigDialog.vue b/legacy-dashboard/src/components/chat/ProviderConfigDialog.vue deleted file mode 100644 index 308d181b..00000000 --- a/legacy-dashboard/src/components/chat/ProviderConfigDialog.vue +++ /dev/null @@ -1,98 +0,0 @@ -<template> - <v-dialog - v-model="dialog" - max-width="1600" - > - <v-card class="provider-config-dialog"> - <div class="provider-config-dialog__body"> - <ProviderChatCompletionPanel - class="provider-config-dialog__page" - :show-border="false" - /> - </div> - </v-card> - </v-dialog> -</template> - -<script setup> -import { computed } from 'vue' -import ProviderChatCompletionPanel from '@/components/provider/ProviderChatCompletionPanel.vue' - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - } -}) - -const emit = defineEmits(['update:modelValue']) -const dialog = computed({ - get: () => props.modelValue, - set: (val) => emit('update:modelValue', val) -}) -</script> - -<style scoped> -.provider-config-dialog { - width: 100%; - height: 100%; - max-height: 100%; - display: flex; - flex-direction: column; - overflow: hidden; - border-radius: 28px; -} - -.provider-config-dialog__body { - flex: 1; - display: flex; - min-height: 0; - overflow: hidden; -} - -.provider-config-dialog__page { - flex: 1; - display: flex; - width: 100%; - height: 100%; - min-height: 0; -} - -:deep(.v-overlay__content) { - width: min(1600px, 70vw); - height: min(920px, 70dvh); - max-width: 70vw; - max-height: 70dvh; - margin: 0; -} - -@media (max-width: 960px) { - .provider-config-dialog { - border-radius: 20px; - } - - :deep(.v-overlay__content) { - width: calc(100dvw - 24px); - height: calc(100dvh - 24px); - max-width: calc(100dvw - 24px); - max-height: calc(100dvh - 24px); - } -} - -@media (max-width: 600px) { - .provider-config-dialog { - border-radius: 0; - } - - .provider-config-dialog__body { - overflow: auto; - } - - :deep(.v-overlay__content) { - width: 100dvw; - height: 100dvh; - max-width: 100dvw; - max-height: 100dvh; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/ProviderModelMenu.vue b/legacy-dashboard/src/components/chat/ProviderModelMenu.vue deleted file mode 100644 index 53f75d66..00000000 --- a/legacy-dashboard/src/components/chat/ProviderModelMenu.vue +++ /dev/null @@ -1,534 +0,0 @@ -<template> - <v-menu - v-model="menuOpen" - :close-on-content-click="false" - :location="menuLocation" - offset="10" - transition="none" - @update:model-value="handleMenuToggle" - > - <template #activator="{ props: menuProps }"> - <button - v-bind="menuProps" - class="provider-trigger" - :class="`provider-trigger--${variant}`" - type="button" - > - <span class="provider-trigger-copy"> - <span class="provider-trigger-title">{{ triggerTitle }}</span> - <span v-if="triggerMeta" class="provider-trigger-meta"> - {{ triggerMeta }} - </span> - </span> - <v-icon class="provider-trigger-chevron" size="18"> - mdi-chevron-down - </v-icon> - </button> - </template> - - <v-card class="provider-menu-card" elevation="0"> - <div class="provider-menu-body"> - <v-text-field - v-model="searchQuery" - placeholder="Search models" - hide-details - variant="outlined" - density="compact" - prepend-inner-icon="mdi-magnify" - class="provider-search" - clearable - /> - - <v-list density="compact" nav class="provider-menu-list"> - <v-list-item - v-for="provider in filteredProviders" - :key="provider.id" - :active="selectedProviderId === provider.id" - rounded="lg" - class="provider-menu-item" - @click="selectProvider(provider)" - > - <v-list-item-title class="provider-item-title"> - {{ provider.id }} - </v-list-item-title> - <v-list-item-subtitle class="provider-subtitle"> - <span class="model-name">{{ provider.model }}</span> - <span class="meta-icons"> - <v-tooltip - v-for="item in capabilityBadges(provider)" - :key="item.key" - location="top" - max-width="320" - > - <template #activator="{ props: badgeTooltipProps }"> - <span - v-bind="badgeTooltipProps" - class="meta-icon-badge" - :class="{ 'meta-icon-badge--disabled': !item.enabled }" - @click.stop - > - <v-icon size="13">{{ item.icon }}</v-icon> - </span> - </template> - <span>{{ item.tooltip }}</span> - </v-tooltip> - <v-tooltip - v-if="formatContextLimit(provider, metadataForProvider(provider))" - location="top" - max-width="320" - > - <template #activator="{ props: contextTooltipProps }"> - <span - v-bind="contextTooltipProps" - class="meta-context-badge" - @click.stop - > - {{ formatContextLimit(provider, metadataForProvider(provider)) }} - </span> - </template> - <span>{{ - tm("models.metadata.context", { - tokens: formatContextLimit( - provider, - metadataForProvider(provider), - ), - }) - }}</span> - </v-tooltip> - </span> - </v-list-item-subtitle> - <template #append> - <div class="provider-menu-actions" @click.stop> - <v-tooltip location="top"> - <template #activator="{ props: testTooltipProps }"> - <v-btn - v-bind="testTooltipProps" - icon="mdi-connection" - size="x-small" - variant="text" - :loading="testingProviderIds.includes(provider.id)" - :disabled="testingProviderIds.includes(provider.id)" - @click.stop="testProvider(provider)" - /> - </template> - <span>{{ tm("models.testButton") }}</span> - </v-tooltip> - <v-icon - v-if="selectedProviderId === provider.id" - class="provider-selected-icon" - size="18" - > - mdi-check - </v-icon> - </div> - </template> - </v-list-item> - </v-list> - - <div v-if="loadingProviders" class="empty-hint"> - Loading models... - </div> - <div v-else-if="filteredProviders.length === 0" class="empty-hint"> - No available models - </div> - </div> - </v-card> - </v-menu> -</template> - -<script setup lang="ts"> -import { ref, computed, onMounted } from "vue"; -import { providerApi } from "@/api/v1"; -import { useModuleI18n } from "@/i18n/composables"; -import { useToast } from "@/utils/toast"; -import { - formatContextLimit, - providerCapabilityBadges, - type ProviderModelMetadata, - type ProviderMetadataSource, -} from "@/utils/providerMetadata"; - -interface ProviderConfig extends ProviderMetadataSource { - id: string; - model: string; - api_base?: string; - enable?: boolean; -} - -const props = withDefaults( - defineProps<{ - variant?: "input" | "header"; - }>(), - { - variant: "input", - }, -); - -const SELECTED_PROVIDER_KEY = "selectedProvider"; -const SELECTED_PROVIDER_MODEL_KEY = "selectedProviderModel"; -const providerConfigs = ref<ProviderConfig[]>([]); -const selectedProviderId = ref(""); -const selectedModelName = ref(""); -const searchQuery = ref(""); -const menuOpen = ref(false); -const loadingProviders = ref(false); -const providersLoaded = ref(false); -const testingProviderIds = ref<string[]>([]); -const modelMetadata = ref<Record<string, ProviderModelMetadata>>({}); -const { tm } = useModuleI18n("features/provider"); -const { success: toastSuccess, error: toastError } = useToast(); - -const variant = computed(() => props.variant); -const menuLocation = computed(() => - props.variant === "header" ? "bottom start" : "top", -); - -const selectedProvider = computed(() => - providerConfigs.value.find( - (provider) => provider.id === selectedProviderId.value, - ), -); - -const triggerTitle = computed(() => { - if (selectedProvider.value?.id) return selectedProvider.value.id; - if (selectedProviderId.value) return selectedProviderId.value; - return props.variant === "header" ? "Default model" : "Model"; -}); - -const triggerMeta = computed(() => { - const model = selectedProvider.value?.model || selectedModelName.value; - if (!model || model === triggerTitle.value) return ""; - return model; -}); - -const filteredProviders = computed(() => { - if (!searchQuery.value) { - return providerConfigs.value; - } - const query = searchQuery.value.toLowerCase(); - return providerConfigs.value.filter( - (provider) => - provider.id.toLowerCase().includes(query) || - provider.model.toLowerCase().includes(query), - ); -}); - -function loadFromStorage() { - const savedProvider = localStorage.getItem(SELECTED_PROVIDER_KEY); - const savedModel = localStorage.getItem(SELECTED_PROVIDER_MODEL_KEY); - if (savedProvider) { - selectedProviderId.value = savedProvider; - } - if (savedModel) { - selectedModelName.value = savedModel; - } -} - -function saveToStorage(provider: ProviderConfig) { - localStorage.setItem(SELECTED_PROVIDER_KEY, provider.id); - localStorage.setItem(SELECTED_PROVIDER_MODEL_KEY, provider.model || ""); -} - -async function loadProviderConfigs(force = false) { - if (loadingProviders.value || (providersLoaded.value && !force)) return; - loadingProviders.value = true; - try { - const response = await providerApi.listByProviderType("chat_completion"); - if (response.data.status === "ok") { - modelMetadata.value = ( - response.data.model_metadata || {} - ) as Record<string, ProviderModelMetadata>; - providerConfigs.value = ( - (response.data.data || []) as unknown as ProviderConfig[] - ).filter((provider: ProviderConfig) => provider.enable !== false); - providersLoaded.value = true; - const selected = selectedProvider.value; - if (selected) { - selectedModelName.value = selected.model || ""; - saveToStorage(selected); - } - } - } catch (error) { - console.error("Failed to load provider list:", error); - } finally { - loadingProviders.value = false; - } -} - -function selectProvider(provider: ProviderConfig) { - selectedProviderId.value = provider.id; - selectedModelName.value = provider.model || ""; - saveToStorage(provider); - menuOpen.value = false; -} - -function capabilityBadges(provider: ProviderConfig) { - return providerCapabilityBadges(provider, metadataForProvider(provider), tm); -} - -function metadataForProvider(provider: ProviderConfig) { - return provider.model ? modelMetadata.value[provider.model] || null : null; -} - -async function testProvider(provider: ProviderConfig) { - if (testingProviderIds.value.includes(provider.id)) return; - testingProviderIds.value.push(provider.id); - try { - const startTime = performance.now(); - const response = await providerApi.test(provider.id); - if (response.data.status === "ok" && response.data.data.error === null) { - const latency = Math.max(0, Math.round(performance.now() - startTime)); - toastSuccess( - tm("models.testSuccessWithLatency", { - id: provider.id, - latency, - }), - ); - } else { - throw new Error(response.data.data.error || tm("models.testError")); - } - } catch (error: any) { - toastError( - error.response?.data?.message || error.message || tm("models.testError"), - ); - } finally { - testingProviderIds.value = testingProviderIds.value.filter( - (id) => id !== provider.id, - ); - } -} - -function getCurrentSelection() { - return { - providerId: selectedProviderId.value, - modelName: selectedProvider.value?.model || selectedModelName.value || "", - }; -} - -function handleMenuToggle(isOpen: boolean) { - if (isOpen) { - loadProviderConfigs(true); - } -} - -onMounted(() => { - loadFromStorage(); - loadProviderConfigs(); -}); - -defineExpose({ - getCurrentSelection, -}); -</script> - -<style scoped> -.provider-trigger { - display: inline-flex; - max-width: 100%; - min-width: 0; - align-items: center; - justify-content: flex-start; - gap: 6px; - border: 0; - background: transparent; - color: rgb(var(--v-theme-on-surface)); - cursor: pointer; - font: inherit; - letter-spacing: 0; - text-align: left; -} - -.provider-trigger-copy { - display: inline-flex; - min-width: 0; - align-items: baseline; - gap: 10px; -} - -.provider-trigger-title, -.provider-trigger-meta { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.provider-trigger--header { - height: 24px; - margin-top: 2px; - padding: 0; -} - -.provider-trigger--header .provider-trigger-title { - font-size: 17px; - font-weight: 620; - line-height: 24px; -} - -.provider-trigger--header .provider-trigger-meta { - color: rgba(var(--v-theme-on-surface), 0.55); - font-size: 13px; - font-weight: 500; - line-height: 18px; -} - -.provider-trigger--input { - height: 40px; - max-width: min(280px, 42vw); - padding: 0 12px; - border: 1px solid rgba(var(--v-theme-on-surface), 0.18); - border-radius: 999px; -} - -.provider-trigger--input:hover { - border-color: rgba(var(--v-theme-on-surface), 0.34); - background: rgba(var(--v-theme-on-surface), 0.04); -} - -.provider-trigger--input .provider-trigger-title { - font-size: 14px; - font-weight: 500; -} - -.provider-trigger--input .provider-trigger-meta { - display: none; -} - -.provider-trigger-chevron { - flex: 0 0 auto; - opacity: 0.64; -} - -.provider-menu-card { - width: min(420px, calc(100vw - 24px)); - overflow: hidden; - border: 0; - border-radius: 14px !important; - background: rgb(var(--v-theme-surface)); - box-shadow: 0 12px 28px rgba(0, 0, 0, 0.08) !important; -} - -.provider-menu-body { - padding: 10px; -} - -.provider-search { - margin-bottom: 8px; -} - -.provider-search :deep(.v-field) { - border-radius: 10px; - box-shadow: none; -} - -.provider-search :deep(.v-field__outline) { - color: rgba(var(--v-theme-on-surface), 0.16); -} - -.provider-menu-list { - max-height: min(360px, 58vh); - overflow-y: auto; - padding: 0; -} - -.provider-menu-item { - min-height: 54px !important; - margin-bottom: 2px; - border-radius: 10px !important; -} - -.provider-menu-item:hover { - background: rgba(var(--v-theme-on-surface), 0.05); -} - -.provider-menu-item.v-list-item--active { - background: rgba(var(--v-theme-primary), 0.08); - color: rgb(var(--v-theme-on-surface)); -} - -.provider-item-title { - font-size: 14px; - font-weight: 600; - line-height: 18px; -} - -.provider-subtitle { - display: flex; - min-width: 0; - align-items: center; - gap: 8px; -} - -.model-name { - min-width: 0; - overflow: hidden; - color: rgba(var(--v-theme-on-surface), 0.58); - font-size: 12px; - line-height: 16px; - text-overflow: ellipsis; - white-space: nowrap; -} - -.meta-icons { - display: flex; - flex: 0 0 auto; - align-items: center; - gap: 4px; - color: rgba(var(--v-theme-on-surface), 0.5); -} - -.meta-icon-badge { - display: inline-flex; - align-items: center; - color: rgba(var(--v-theme-on-surface), 0.72); -} - -.meta-icon-badge--disabled { - color: rgba(var(--v-theme-on-surface), 0.34); -} - -.meta-context-badge { - display: inline-flex; - align-items: center; - height: 16px; - padding: 0 5px; - border-radius: 999px; - background: rgba(var(--v-theme-on-surface), 0.06); - color: rgba(var(--v-theme-on-surface), 0.72); - font-size: 10px; - font-weight: 650; - line-height: 16px; -} - -.provider-menu-actions { - display: inline-flex; - align-items: center; - gap: 2px; -} - -.provider-selected-icon { - color: rgba(var(--v-theme-on-surface), 0.72); -} - -.empty-hint { - padding: 16px; - color: rgba(var(--v-theme-on-surface), 0.5); - font-size: 12px; - text-align: center; -} - -@media (max-width: 768px) { - .provider-trigger--header .provider-trigger-title { - font-size: 16px; - } - - .provider-trigger--header .provider-trigger-meta { - display: none; - } - - .provider-trigger--input { - height: 38px; - max-width: 48vw; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/ReasoningSidebar.vue b/legacy-dashboard/src/components/chat/ReasoningSidebar.vue deleted file mode 100644 index b7d276b7..00000000 --- a/legacy-dashboard/src/components/chat/ReasoningSidebar.vue +++ /dev/null @@ -1,134 +0,0 @@ -<template> - <transition name="slide-left"> - <aside v-if="modelValue" class="reasoning-sidebar"> - <div class="reasoning-sidebar-header"> - <div class="reasoning-sidebar-title">{{ reasoningTitle }}</div> - <v-btn icon="mdi-close" size="small" variant="text" @click="close" /> - </div> - - <div class="reasoning-sidebar-body"> - <ReasoningTimeline - v-if="parts.length || reasoning" - :parts="parts" - :reasoning="reasoning" - :is-dark="isDark" - /> - <div v-else class="reasoning-sidebar-empty"> - {{ reasoningTitle }} - </div> - </div> - </aside> - </transition> -</template> - -<script setup lang="ts"> -import { computed } from "vue"; -import { - reasoningActivityCounts, - reasoningActivityTitle, - type MessagePart, -} from "@/composables/useMessages"; -import { useModuleI18n } from "@/i18n/composables"; -import ReasoningTimeline from "@/components/chat/message_list_comps/ReasoningTimeline.vue"; - -const props = defineProps<{ - modelValue: boolean; - parts: MessagePart[]; - reasoning?: string; - isDark?: boolean; -}>(); - -const emit = defineEmits<{ - "update:modelValue": [value: boolean]; -}>(); - -const { tm } = useModuleI18n("features/chat"); - -const activityCounts = computed(() => - reasoningActivityCounts(props.parts, props.reasoning || ""), -); - -const reasoningTitle = computed(() => - reasoningActivityTitle(activityCounts.value, tm), -); - -function close() { - emit("update:modelValue", false); -} -</script> - -<style scoped> -.reasoning-sidebar { - width: 380px; - height: calc(100% - var(--chat-panel-top-offset, 0px)); - margin-top: var(--chat-panel-top-offset, 0px); - border-left: 1px solid var(--chat-border, rgba(var(--v-theme-on-surface), 0.1)); - background: var(--chat-page-bg, rgb(var(--v-theme-surface))); - color: rgb(var(--v-theme-on-surface)); - display: flex; - flex-direction: column; - flex-shrink: 0; -} - -.slide-left-enter-active, -.slide-left-leave-active { - transition: all 0.2s ease; -} - -.slide-left-enter-from, -.slide-left-leave-to { - transform: translateX(100%); - opacity: 0; -} - -.reasoning-sidebar-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 14px 16px 8px; -} - -.reasoning-sidebar-title { - font-size: 16px; - font-weight: 600; - line-height: 1.4; - color: rgb(var(--v-theme-on-surface)); -} - -.reasoning-sidebar-body { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 0 14px 12px; - font-size: 14.5px; - line-height: 1.62; -} - -.reasoning-sidebar-empty { - padding: 12px 2px; - color: rgba(var(--v-theme-on-surface), 0.54); - font-size: 13px; -} - -@media (max-width: 760px) { - .reasoning-sidebar { - position: fixed; - inset: 0; - z-index: 1300; - width: 100vw; - height: 100dvh; - margin-top: 0; - border-left: 0; - } - - .reasoning-sidebar-header { - min-height: 52px; - padding: calc(10px + env(safe-area-inset-top)) 12px 8px; - border-bottom: 1px solid var(--chat-border, rgba(var(--v-border-color), 0.12)); - } - - .reasoning-sidebar-body { - padding: 0 12px calc(12px + env(safe-area-inset-bottom)); - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/RegenerateMenu.vue b/legacy-dashboard/src/components/chat/RegenerateMenu.vue deleted file mode 100644 index fd2811be..00000000 --- a/legacy-dashboard/src/components/chat/RegenerateMenu.vue +++ /dev/null @@ -1,289 +0,0 @@ -<template> - <StyledMenu - location="end" - offset="6" - transition="none" - no-border - :close-on-content-click="true" - @update:model-value="handleMenuToggle" - > - <template #activator="{ props: menuProps }"> - <v-btn - v-bind="menuProps" - icon="mdi-refresh" - size="x-small" - variant="text" - /> - </template> - - <v-list-item - class="styled-menu-item" - rounded="md" - @click="emit('retry')" - > - <template #prepend> - <v-icon size="18">mdi-refresh</v-icon> - </template> - <v-list-item-title>{{ tm("actions.retry") }}</v-list-item-title> - </v-list-item> - - <v-menu - location="end" - offset="8" - transition="none" - open-on-hover - :close-on-content-click="true" - @update:model-value="handleModelMenuToggle" - > - <template #activator="{ props: modelMenuProps }"> - <v-list-item - v-bind="modelMenuProps" - class="styled-menu-item" - rounded="md" - > - <template #prepend> - <v-icon size="18">mdi-creation</v-icon> - </template> - <v-list-item-title>{{ tm("actions.retryWithModel") }}</v-list-item-title> - <template #append> - <v-progress-circular - v-if="loadingProviders" - indeterminate - size="16" - width="2" - /> - <v-icon v-else size="18">mdi-chevron-right</v-icon> - </template> - </v-list-item> - </template> - - <v-card - class="styled-menu-card styled-menu-card-borderless regenerate-model-card" - elevation="8" - rounded="lg" - > - <v-list density="compact" class="styled-menu-list pa-1"> - <v-list-item - v-for="provider in providerConfigs" - :key="provider.id" - class="styled-menu-item regenerate-model-item" - rounded="md" - @click="retryWithModel(provider)" - > - <v-list-item-title class="text-body-2"> - {{ provider.id }} - </v-list-item-title> - <v-list-item-subtitle class="regenerate-model-subtitle"> - <span class="regenerate-model-name">{{ provider.model }}</span> - <span class="regenerate-model-icons"> - <v-tooltip - v-for="item in capabilityBadges(provider)" - :key="item.key" - location="top" - max-width="320" - > - <template #activator="{ props: badgeTooltipProps }"> - <span - v-bind="badgeTooltipProps" - class="regenerate-model-icon-badge" - :class="{ - 'regenerate-model-icon-badge--disabled': !item.enabled, - }" - @click.stop - > - <v-icon size="12">{{ item.icon }}</v-icon> - </span> - </template> - <span>{{ item.tooltip }}</span> - </v-tooltip> - <v-tooltip - v-if="formatContextLimit(provider, metadataForProvider(provider))" - location="top" - max-width="320" - > - <template #activator="{ props: contextTooltipProps }"> - <span - v-bind="contextTooltipProps" - class="regenerate-model-context-badge" - @click.stop - > - {{ formatContextLimit(provider, metadataForProvider(provider)) }} - </span> - </template> - <span>{{ - providerTm("models.metadata.context", { - tokens: formatContextLimit( - provider, - metadataForProvider(provider), - ), - }) - }}</span> - </v-tooltip> - </span> - </v-list-item-subtitle> - </v-list-item> - - <div v-if="!loadingProviders && !providerConfigs.length" class="regenerate-empty"> - {{ tm("actions.noAvailableModels") }} - </div> - </v-list> - </v-card> - </v-menu> - </StyledMenu> -</template> - -<script setup lang="ts"> -import { ref } from "vue"; -import { providerApi } from "@/api/v1"; -import StyledMenu from "@/components/shared/StyledMenu.vue"; -import { useModuleI18n } from "@/i18n/composables"; -import { - formatContextLimit, - providerCapabilityBadges, - type ProviderModelMetadata, - type ProviderMetadataSource, -} from "@/utils/providerMetadata"; - -interface ProviderConfig extends ProviderMetadataSource { - id: string; - model: string; - enable?: boolean; -} - -export interface RegenerateModelSelection { - providerId: string; - modelName: string; -} - -const emit = defineEmits<{ - retry: []; - retryWithModel: [selection: RegenerateModelSelection]; -}>(); - -const { tm } = useModuleI18n("features/chat"); -const { tm: providerTm } = useModuleI18n("features/provider"); -const providerConfigs = ref<ProviderConfig[]>([]); -const loadingProviders = ref(false); -const providersLoaded = ref(false); -const modelMetadata = ref<Record<string, ProviderModelMetadata>>({}); - -async function loadProviderConfigs(force = false) { - if (loadingProviders.value || (providersLoaded.value && !force)) return; - loadingProviders.value = true; - try { - const response = await providerApi.listByProviderType("chat_completion"); - if (response.data.status === "ok") { - modelMetadata.value = ( - response.data.model_metadata || {} - ) as Record<string, ProviderModelMetadata>; - providerConfigs.value = ( - (response.data.data || []) as unknown as ProviderConfig[] - ).filter((provider: ProviderConfig) => provider.enable !== false); - providersLoaded.value = true; - } - } catch (error) { - console.error("Failed to load provider list:", error); - } finally { - loadingProviders.value = false; - } -} - -function handleMenuToggle(isOpen: boolean) { - if (isOpen) { - loadProviderConfigs(); - } -} - -function handleModelMenuToggle(isOpen: boolean) { - if (isOpen) { - loadProviderConfigs(); - } -} - -function retryWithModel(provider: ProviderConfig) { - emit("retryWithModel", { - providerId: provider.id, - modelName: provider.model, - }); -} - -function capabilityBadges(provider: ProviderConfig) { - return providerCapabilityBadges( - provider, - metadataForProvider(provider), - providerTm, - ); -} - -function metadataForProvider(provider: ProviderConfig) { - return provider.model ? modelMetadata.value[provider.model] || null : null; -} -</script> - -<style scoped> -.regenerate-model-card { - min-width: 280px; - max-width: min(360px, 86vw); -} - -.regenerate-model-card :deep(.v-list) { - max-height: 320px; - overflow-y: auto; -} - -.regenerate-model-item { - min-height: 46px; -} - -.regenerate-model-subtitle { - display: flex; - align-items: center; - gap: 8px; -} - -.regenerate-model-name { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--chat-muted, rgba(var(--v-theme-on-surface), 0.62)); -} - -.regenerate-model-icons { - display: flex; - flex: 0 0 auto; - align-items: center; - gap: 4px; - color: var(--chat-muted, rgba(var(--v-theme-on-surface), 0.62)); -} - -.regenerate-model-icon-badge { - display: inline-flex; - align-items: center; - color: rgba(var(--v-theme-on-surface), 0.72); -} - -.regenerate-model-icon-badge--disabled { - color: rgba(var(--v-theme-on-surface), 0.34); -} - -.regenerate-model-context-badge { - display: inline-flex; - align-items: center; - height: 16px; - padding: 0 5px; - border-radius: 999px; - background: rgba(var(--v-theme-on-surface), 0.06); - color: rgba(var(--v-theme-on-surface), 0.72); - font-size: 10px; - font-weight: 650; - line-height: 16px; -} - -.regenerate-empty { - padding: 14px 16px; - color: var(--chat-muted, rgba(var(--v-theme-on-surface), 0.62)); - font-size: 12px; - text-align: center; -} -</style> diff --git a/legacy-dashboard/src/components/chat/StandaloneChat.vue b/legacy-dashboard/src/components/chat/StandaloneChat.vue deleted file mode 100644 index 6d4dd359..00000000 --- a/legacy-dashboard/src/components/chat/StandaloneChat.vue +++ /dev/null @@ -1,710 +0,0 @@ -<template> - <div class="standalone-chat"> - <section ref="messagesContainer" class="standalone-messages"> - <div v-if="initializing" class="standalone-state"> - <v-progress-circular indeterminate size="28" width="3" /> - </div> - - <div v-else-if="!activeMessages.length" class="standalone-state"> - <div class="welcome-title">{{ tm("welcome.title") }}</div> - </div> - - <div v-else class="message-list"> - <div - v-for="(msg, msgIndex) in activeMessages" - :key="msg.id || `${msgIndex}-${msg.created_at || ''}`" - class="message-row" - :class="isUserMessage(msg) ? 'from-user' : 'from-bot'" - > - <div class="message-stack"> - <div - class="message-bubble" - :class="{ user: isUserMessage(msg), bot: !isUserMessage(msg) }" - > - <div v-if="messageContent(msg).isLoading" class="loading-message"> - {{ tm("message.loading") }} - </div> - - <template v-else> - <template - v-for="(block, blockIndex) in renderBlocks(msg)" - :key="`${msgIndex}-block-${blockIndex}-${block.kind}`" - > - <ReasoningBlock - v-if="block.kind === 'thinking'" - :parts="block.parts" - :is-dark="isDark" - :initial-expanded="false" - :is-streaming="isMessageStreaming(msg, msgIndex)" - :has-non-reasoning-content=" - hasFollowingContentBlock(msg, blockIndex) - " - /> - - <template v-else> - <template - v-for="(part, partIndex) in block.parts" - :key="`${msgIndex}-${blockIndex}-${partIndex}-${part.type}`" - > - <div - v-if="part.type === 'plain' && isUserMessage(msg)" - class="plain-content" - > - {{ part.text || "" }} - </div> - - <MarkdownMessagePart - v-else-if="part.type === 'plain'" - :content="part.text || ''" - :refs="messageRefs(msg)" - :is-dark="isDark" - :custom-html-tags="customMarkdownTags" - :is-streaming="isMessageStreaming(msg, msgIndex)" - /> - - <button - v-else-if="part.type === 'image'" - class="image-part" - type="button" - @click="openImage(partUrl(part))" - > - <img :src="partUrl(part)" :alt="part.filename || 'image'" /> - </button> - - <audio - v-else-if="part.type === 'record'" - class="audio-part" - controls - :src="partUrl(part)" - /> - - <video - v-else-if="part.type === 'video'" - class="video-part" - controls - :src="partUrl(part)" - /> - - <div - v-else-if="part.type === 'file'" - class="file-part" - :style="{ - '--attachment-color': - attachmentPresentation(part).color, - }" - > - <v-icon - class="file-part-icon" - :icon="attachmentPresentation(part).icon" - size="24" - /> - <div class="file-part-meta"> - <span class="file-part-name"> - {{ attachmentName(part) }} - </span> - <span class="file-part-kind"> - {{ attachmentPresentation(part).label }} - </span> - </div> - </div> - - <div - v-else-if="part.type === 'tool_call'" - class="tool-call-block" - > - <template - v-for="tool in part.tool_calls || []" - :key="tool.id || tool.name" - > - <ToolCallItem - v-if="isIPythonToolCall(tool)" - :is-dark="isDark" - > - <template #label> - <v-icon size="16">mdi-code-json</v-icon> - <span>{{ tool.name || "python" }}</span> - <span class="tool-call-inline-status"> - {{ toolCallStatusText(tool) }} - </span> - </template> - <template #details> - <IPythonToolBlock - :tool-call="normalizeToolCall(tool)" - :is-dark="isDark" - :show-header="false" - :force-expanded="true" - /> - </template> - </ToolCallItem> - <ToolCallCard - v-else - :tool-call="normalizeToolCall(tool)" - :is-dark="isDark" - /> - </template> - </div> - - <pre v-else class="unknown-part">{{ formatJson(part) }}</pre> - </template> - </template> - </template> - </template> - </div> - </div> - </div> - </div> - </section> - - <section class="standalone-composer"> - <ChatInput - ref="inputRef" - v-model:prompt="draft" - :staged-images-url="stagedImagesUrl" - :staged-audio-url="stagedAudioUrl" - :staged-files="stagedNonImageFiles" - :disabled="sending || initializing" - :enable-streaming="enableStreaming" - :is-recording="false" - :is-running="Boolean(currSessionId && isSessionRunning(currSessionId))" - :session-id="currSessionId || null" - :current-session="currentSession" - :config-id="configId || 'default'" - send-shortcut="enter" - @send="sendCurrentMessage" - @stop="stopCurrentSession" - @toggle-streaming="enableStreaming = !enableStreaming" - @remove-image="removeImage" - @remove-audio="removeAudio" - @remove-file="removeFile" - @paste-image="handlePaste" - @file-select="handleFilesSelected" - /> - </section> - - <v-overlay - v-model="imagePreview.visible" - class="image-preview-overlay" - scrim="rgba(0, 0, 0, 0.86)" - @click="closeImage" - > - <img class="preview-image" :src="imagePreview.url" alt="preview" /> - </v-overlay> - </div> -</template> - -<script setup lang="ts"> -import { - computed, - nextTick, - onBeforeUnmount, - onMounted, - reactive, - ref, -} from "vue"; -import { chatApi, configRouteApi, fileApi } from "@/api/v1"; -import { setCustomComponents } from "markstream-vue"; -import "markstream-vue/index.css"; -import ChatInput from "@/components/chat/ChatInput.vue"; -import IPythonToolBlock from "@/components/chat/message_list_comps/IPythonToolBlock.vue"; -import MarkdownMessagePart from "@/components/chat/message_list_comps/MarkdownMessagePart.vue"; -import ReasoningBlock from "@/components/chat/message_list_comps/ReasoningBlock.vue"; -import RefNode from "@/components/chat/message_list_comps/RefNode.vue"; -import ToolCallCard from "@/components/chat/message_list_comps/ToolCallCard.vue"; -import ToolCallItem from "@/components/chat/message_list_comps/ToolCallItem.vue"; -import ThemeAwareMarkdownCodeBlock from "@/components/shared/ThemeAwareMarkdownCodeBlock.vue"; -import { - attachmentName, - attachmentPresentation, -} from "@/components/chat/attachmentPresentation"; -import { useMediaHandling } from "@/composables/useMediaHandling"; -import { - displayParts as displayMessageParts, - messageBlocks as buildMessageBlocks, - type MessageDisplayBlock, - useMessages, - type ChatRecord, - type MessagePart, - type TransportMode, -} from "@/composables/useMessages"; -import type { Session } from "@/composables/useSessions"; -import { useModuleI18n } from "@/i18n/composables"; -import { useCustomizerStore } from "@/stores/customizer"; -import { buildWebchatUmoDetails } from "@/utils/chatConfigBinding"; - -const props = withDefaults(defineProps<{ configId?: string | null }>(), { - configId: "default", -}); - -setCustomComponents("chat-message", { - ref: RefNode, - code_block: ThemeAwareMarkdownCodeBlock, -}); - -const { tm } = useModuleI18n("features/chat"); -const customizer = useCustomizerStore(); -const currSessionId = ref(""); -const currentSession = ref<Session | null>(null); -const draft = ref(""); -const initializing = ref(false); -const enableStreaming = ref(true); -const shouldStickToBottom = ref(true); -const messagesContainer = ref<HTMLElement | null>(null); -const inputRef = ref<InstanceType<typeof ChatInput> | null>(null); -const imagePreview = reactive({ visible: false, url: "" }); - -const isDark = computed(() => customizer.uiTheme === "PurpleThemeDark"); -const customMarkdownTags = ["ref"]; - -const { - stagedFiles, - stagedImagesUrl, - stagedAudioUrl, - stagedNonImageFiles, - processAndUploadImage, - processAndUploadFile, - handlePaste, - removeImage, - removeAudio, - removeFile, - clearStaged, - cleanupMediaCache, -} = useMediaHandling(); - -const { - sending, - activeMessages, - isSessionRunning, - isMessageStreaming, - isUserMessage, - messageContent, - createLocalExchange, - sendMessageStream, - stopSession, -} = useMessages({ - currentSessionId: currSessionId, - onStreamUpdate: () => { - if (shouldStickToBottom.value) { - scrollToBottom(); - } - }, -}); - -const transportMode = computed<TransportMode>(() => - (localStorage.getItem("chat.transportMode") as TransportMode) === "websocket" - ? "websocket" - : "sse", -); - -onMounted(async () => { - await ensureSession(); - inputRef.value?.focusInput(); -}); - -onBeforeUnmount(() => { - cleanupMediaCache(); -}); - -async function ensureSession() { - if (currSessionId.value) return currSessionId.value; - initializing.value = true; - try { - const response = await chatApi.createSession(); - const session = response.data?.data as Session; - currSessionId.value = session.session_id; - currentSession.value = session; - await bindConfigToSession(session.session_id); - return session.session_id; - } finally { - initializing.value = false; - } -} - -async function bindConfigToSession(sessionId: string) { - const confId = props.configId || "default"; - const umo = buildWebchatUmoDetails(sessionId, false).umo; - await configRouteApi.upsert(umo, { config_id: confId }); -} - -async function sendCurrentMessage() { - if (!draft.value.trim() && !stagedFiles.value.length) return; - const sessionId = await ensureSession(); - const text = draft.value.trim(); - const parts = buildOutgoingParts(text); - const messageId = crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`; - const selection = inputRef.value?.getCurrentSelection(); - const { botRecord } = createLocalExchange({ sessionId, messageId, parts }); - - draft.value = ""; - clearStaged({ revokeUrls: false }); - scrollToBottom(); - await focusChatInput(); - - sendMessageStream({ - sessionId, - messageId, - parts, - transport: transportMode.value, - enableStreaming: enableStreaming.value, - selectedProvider: selection?.providerId || "", - selectedModel: selection?.modelName || "", - botRecord, - }); -} - -function buildOutgoingParts(text: string): MessagePart[] { - const parts: MessagePart[] = []; - if (text) { - parts.push({ type: "plain", text }); - } - stagedFiles.value.forEach((file) => { - parts.push({ - type: file.type, - attachment_id: file.attachment_id, - filename: file.filename, - embedded_url: file.url, - }); - }); - return parts; -} - -function hasNonReasoningContent(message: ChatRecord) { - return renderBlocks(message).some((block) => block.kind === "content"); -} - -function bubbleParts(message: ChatRecord) { - return displayMessageParts(messageContent(message)); -} - -function renderBlocks(message: ChatRecord): MessageDisplayBlock[] { - if (isUserMessage(message)) { - const parts = bubbleParts(message); - return parts.length ? [{ kind: "content", parts }] : []; - } - return buildMessageBlocks(messageContent(message)); -} - -function hasFollowingContentBlock(message: ChatRecord, blockIndex: number) { - return renderBlocks(message) - .slice(blockIndex + 1) - .some((block) => block.kind === "content"); -} - -async function stopCurrentSession() { - if (!currSessionId.value) return; - await stopSession(currSessionId.value); -} - -async function handleFilesSelected(files: FileList) { - const selectedFiles = Array.from(files || []); - for (const file of selectedFiles) { - if (file.type.startsWith("image/")) { - await processAndUploadImage(file); - } else { - await processAndUploadFile(file); - } - } -} - -function scrollToBottom() { - nextTick(() => { - const container = messagesContainer.value; - if (!container) return; - container.scrollTop = container.scrollHeight; - shouldStickToBottom.value = true; - }); -} - -async function focusChatInput() { - await nextTick(); - window.requestAnimationFrame(() => { - inputRef.value?.focusInput(); - }); -} - -function messageRefs(message: ChatRecord) { - const refs = messageContent(message).refs; - if (refs && typeof refs === "object" && Array.isArray(refs.used)) { - return refs as { used?: Array<Record<string, unknown>> }; - } - return null; -} - -function partUrl(part: MessagePart) { - if (part.embedded_url) return part.embedded_url; - if (part.embedded_file?.url) return part.embedded_file.url; - if (part.attachment_id) return fileApi.contentUrl(part.attachment_id); - const lookupFilename = part.stored_filename || part.filename; - if (lookupFilename) return fileApi.byNameUrl(lookupFilename); - return ""; -} - -function normalizeToolCall(tool: Record<string, unknown>) { - const normalized = { ...tool }; - normalized.args = parseJsonSafe(normalized.args || normalized.arguments); - normalized.result = parseJsonSafe(normalized.result); - if (!normalized.ts) normalized.ts = Date.now() / 1000; - if (normalized.result && typeof normalized.result === "object") { - normalized.result = JSON.stringify(normalized.result, null, 2); - } - return normalized; -} - -function isIPythonToolCall(tool: Record<string, unknown>) { - const name = String(tool.name || "").toLowerCase(); - return name.includes("python") || name.includes("ipython"); -} - -function toolCallStatusText(tool: Record<string, unknown>) { - if (tool.finished_ts) return tm("toolStatus.done"); - return tm("toolStatus.running"); -} - -function formatJson(value: unknown) { - if (typeof value === "string") return value; - try { - return JSON.stringify(value, null, 2); - } catch { - return String(value ?? ""); - } -} - -function parseJsonSafe(value: unknown) { - if (typeof value !== "string") return value; - try { - return JSON.parse(value); - } catch { - return value; - } -} - -function openImage(url: string) { - imagePreview.url = url; - imagePreview.visible = true; -} - -function closeImage() { - imagePreview.visible = false; - imagePreview.url = ""; -} -</script> - -<style scoped> -.standalone-chat { - --standalone-muted: rgba(var(--v-theme-on-surface), 0.62); - height: 100%; - min-height: 0; - display: flex; - flex-direction: column; - background: rgb(var(--v-theme-background)); -} - -.standalone-messages { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 20px 22px 14px; -} - -.standalone-state { - height: 100%; - display: flex; - align-items: center; - justify-content: center; - text-align: center; -} - -.welcome-title { - font-family: "Outfit", "Noto Sans", sans-serif; - font-size: 24px; - font-weight: 700; -} - -.message-list { - display: flex; - flex-direction: column; - gap: 18px; - font-weight: 410; -} - -.message-row { - display: flex; -} - -.message-row.from-user { - justify-content: flex-end; -} - -.message-stack { - max-width: 88%; -} - -.from-user .message-stack { - max-width: 70%; -} - -.message-bubble { - border-radius: 8px; - padding: 10px 14px; - line-height: 1.65; - overflow-wrap: anywhere; -} - -.message-bubble.user { - padding: 12px 18px; - border-radius: 1.5rem; - background: rgba(var(--v-theme-primary), 0.12); -} - -.message-bubble.bot { - padding-left: 0; - background: transparent; -} - -.plain-content { - white-space: pre-wrap; -} - -.loading-message, -.tool-call-inline-status { - color: var(--standalone-muted); -} - -.image-part { - display: block; - border: 0; - padding: 0; - margin-top: 8px; - background: transparent; - cursor: zoom-in; -} - -.image-part img { - max-width: min(360px, 100%); - max-height: 320px; - border-radius: 8px; - object-fit: contain; -} - -.audio-part, -.video-part { - display: block; - max-width: 100%; - margin-top: 8px; -} - -.video-part { - max-height: 320px; - border-radius: 8px; -} - -.file-part { - --attachment-color: #607d8b; - display: grid; - grid-template-columns: auto minmax(0, 1fr); - align-items: center; - gap: 10px; - width: min(420px, 100%); - margin-top: 8px; - padding: 9px 10px; - border-radius: 8px; - background: rgba(var(--v-theme-on-surface), 0.055); - background: linear-gradient( - 90deg, - color-mix(in srgb, var(--attachment-color) 13%, transparent), - rgba(var(--v-theme-on-surface), 0.055) 58% - ); -} - -.file-part-icon { - color: var(--attachment-color); -} - -.file-part-meta { - min-width: 0; - display: flex; - flex-direction: column; - gap: 1px; -} - -.file-part-name { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 14px; - font-weight: 500; - line-height: 20px; -} - -.file-part-kind { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--attachment-color); - font-size: 11px; - font-weight: 700; - line-height: 14px; -} - -.tool-call-block { - margin: 8px 0; - display: flex; - flex-direction: column; - gap: 6px; -} - -.message-bubble.bot - > .tool-call-block:first-child - :deep(.tool-call-card:first-child) { - margin-top: 0; -} - -.unknown-part { - max-width: 100%; - overflow-x: auto; - border-radius: 8px; - padding: 10px; - background: rgba(var(--v-theme-on-surface), 0.06); - font-size: 13px; - line-height: 1.5; -} - -.standalone-composer { - position: relative; - z-index: 1; - padding-bottom: 10px; - background: rgb(var(--v-theme-background)); -} - -.standalone-composer::before { - content: ""; - position: absolute; - z-index: -1; - left: 0; - right: 0; - top: -32px; - height: 32px; - pointer-events: none; - background: linear-gradient( - to bottom, - rgba(var(--v-theme-background), 0), - rgb(var(--v-theme-background)) - ); -} - -.standalone-composer :deep(.input-area) { - border-top: 0; -} - -.image-preview-overlay { - display: flex; - align-items: center; - justify-content: center; -} - -.preview-image { - max-width: min(92vw, 1000px); - max-height: 88vh; - border-radius: 8px; - object-fit: contain; -} -</style> diff --git a/legacy-dashboard/src/components/chat/ThreadPanel.vue b/legacy-dashboard/src/components/chat/ThreadPanel.vue deleted file mode 100644 index e0be196f..00000000 --- a/legacy-dashboard/src/components/chat/ThreadPanel.vue +++ /dev/null @@ -1,493 +0,0 @@ -<template> - <transition name="slide-left"> - <aside v-if="modelValue && thread" class="thread-panel"> - <div class="thread-panel-header"> - <div class="thread-panel-title">{{ tm("thread.title") }}</div> - <div class="thread-panel-actions"> - <v-btn - icon="mdi-delete-outline" - class="thread-delete-button" - size="small" - variant="text" - :title="tm('thread.delete')" - :loading="deleting" - :disabled="sending || deleting" - @click="emit('delete', thread)" - /> - <v-btn icon="mdi-close" size="small" variant="text" @click="close" /> - </div> - </div> - - <blockquote class="thread-selected-text"> - {{ thread.selected_text }} - </blockquote> - - <div ref="messagesEl" class="thread-messages"> - <ChatMessageList - :messages="messages" - :is-dark="isDark" - :is-streaming="sending" - variant="thread" - /> - </div> - - <form class="thread-composer" @submit.prevent="send"> - <textarea - v-model="draft" - class="thread-input" - :placeholder="tm('thread.placeholder')" - rows="1" - :disabled="sending" - @keydown.enter.exact.prevent="send" - ></textarea> - <v-btn - class="thread-send-button" - variant="text" - :loading="sending" - :disabled="!draft.trim()" - type="submit" - > - {{ tm("input.send") }} - </v-btn> - </form> - </aside> - </transition> -</template> - -<script setup lang="ts"> -import { nextTick, ref, watch } from "vue"; -import { chatApi } from "@/api/v1"; -import { fetchWithAuth } from "@/api/http"; -import { - appendPlain, - appendReasoningPart, - extractReasoningText, - finishToolCall, - hasPlainText, - markMessageStarted, - normalizeMessageParts, - parseJsonSafe, - payloadText, - upsertToolCall, - type ChatRecord, - type MessagePart, - type ChatThread, -} from "@/composables/useMessages"; -import { useModuleI18n } from "@/i18n/composables"; -import ChatMessageList from "@/components/chat/ChatMessageList.vue"; - -const props = defineProps<{ - modelValue: boolean; - thread: ChatThread | null; - isDark: boolean; - deleting?: boolean; -}>(); - -const emit = defineEmits<{ - "update:modelValue": [value: boolean]; - delete: [thread: ChatThread]; -}>(); - -const { tm } = useModuleI18n("features/chat"); -const messages = ref<ChatRecord[]>([]); -const draft = ref(""); -const sending = ref(false); -const messagesEl = ref<HTMLElement | null>(null); - -watch( - () => props.thread?.thread_id, - (threadId) => { - if (threadId) { - loadThread(threadId); - } else { - messages.value = []; - } - }, - { immediate: true }, -); - -function close() { - emit("update:modelValue", false); -} - -async function loadThread(threadId: string) { - try { - const response = await chatApi.getThread(threadId); - const history = response.data?.data?.history || []; - messages.value = history.map(normalizeRecord); - scrollToBottom(); - } catch (error) { - console.error("Failed to load thread:", error); - messages.value = []; - } -} - -async function send() { - if (!props.thread || sending.value || !draft.value.trim()) return; - const text = draft.value.trim(); - draft.value = ""; - const messageId = crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`; - const userRecord: ChatRecord = { - id: `local-thread-user-${messageId}`, - created_at: new Date().toISOString(), - content: { - type: "user", - message: [{ type: "plain", text }], - }, - }; - const botRecord: ChatRecord = { - id: `local-thread-bot-${messageId}`, - created_at: new Date().toISOString(), - content: { - type: "bot", - message: [], - reasoning: "", - isLoading: true, - }, - }; - messages.value.push(userRecord, botRecord); - const threadUserRecord = messages.value[messages.value.length - 2]; - const threadBotRecord = messages.value[messages.value.length - 1]; - scrollToBottom(); - - const abort = new AbortController(); - sending.value = true; - try { - const response = await fetchWithAuth(chatApi.sendThreadMessageUrl(props.thread.thread_id), { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - message: [{ type: "plain", text }], - enable_streaming: true, - }), - signal: abort.signal, - }); - if (!response.ok || !response.body) { - throw new Error(`Thread request failed: ${response.status}`); - } - await readSseStream(response.body, (payload) => { - processPayload(threadBotRecord, threadUserRecord, payload); - scrollToBottom(); - }); - } catch (error) { - appendPlain( - threadBotRecord, - `\n\n${String((error as Error)?.message || error)}`, - ); - console.error("Failed to send thread message:", error); - } finally { - sending.value = false; - } -} - -function normalizeRecord(record: any): ChatRecord { - const content = record.content || {}; - const normalizedMessage = normalizeMessageParts( - content.message || [], - content.reasoning || "", - ); - return { - ...record, - content: { - type: content.type || (record.sender_id === "bot" ? "bot" : "user"), - message: normalizedMessage, - reasoning: extractReasoningText(normalizedMessage, content.reasoning || ""), - agentStats: content.agentStats || content.agent_stats, - refs: content.refs, - }, - }; -} - -async function readSseStream( - stream: ReadableStream<Uint8Array>, - onPayload: (payload: any) => void, -) { - const reader = stream.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - buffer += decoder.decode(value, { stream: true }); - const chunks = buffer.split("\n\n"); - buffer = chunks.pop() || ""; - for (const chunk of chunks) { - const data = chunk - .split("\n") - .filter((line) => line.startsWith("data:")) - .map((line) => line.slice(5).trimStart()) - .join("\n"); - if (!data) continue; - try { - onPayload(JSON.parse(data)); - } catch (error) { - console.error("Failed to parse thread SSE payload:", error, data); - } - } - } -} - -function processPayload(botRecord: ChatRecord, userRecord: ChatRecord, payload: any) { - const normalized = - payload?.ct === "chat" - ? { ...payload, type: payload.type || payload.t } - : payload; - const type = normalized?.type || normalized?.t; - const chainType = normalized?.chain_type; - const data = normalized?.data ?? ""; - - if (type === "session_id" || type === "session_bound") return; - - if (type === "user_message_saved") { - userRecord.id = data?.id || userRecord.id; - userRecord.created_at = data?.created_at || userRecord.created_at; - userRecord.llm_checkpoint_id = - data?.llm_checkpoint_id || userRecord.llm_checkpoint_id; - return; - } - - if (type === "message_saved") { - markMessageStarted(botRecord); - botRecord.id = data?.id || botRecord.id; - botRecord.created_at = data?.created_at || botRecord.created_at; - botRecord.llm_checkpoint_id = - data?.llm_checkpoint_id || botRecord.llm_checkpoint_id; - if (data?.refs) { - botRecord.content.refs = data.refs; - } - return; - } - - if (type === "agent_stats" || chainType === "agent_stats") { - markMessageStarted(botRecord); - botRecord.content.agentStats = data; - return; - } - - if (type === "error") { - markMessageStarted(botRecord); - appendPlain(botRecord, `\n\n${String(data)}`); - return; - } - - if (type === "complete" || type === "break") { - markMessageStarted(botRecord); - const finalText = payloadText(data); - if (finalText && !hasPlainText(botRecord)) { - appendPlain(botRecord, finalText, false); - } - return; - } - - if (type === "end") { - markMessageStarted(botRecord); - return; - } - - if (type === "plain") { - markMessageStarted(botRecord); - if (chainType === "reasoning") { - appendReasoningPart(botRecord, payloadText(data)); - return; - } - if (chainType === "tool_call") { - upsertToolCall(botRecord, parseJsonSafe(data)); - return; - } - if (chainType === "tool_call_result") { - finishToolCall(botRecord, parseJsonSafe(data)); - return; - } - appendPlain(botRecord, payloadText(data), normalized.streaming !== false); - return; - } - - if (["image", "record", "file", "video"].includes(type)) { - markMessageStarted(botRecord); - const rawFilename = String(data) - .replace("[IMAGE]", "") - .replace("[RECORD]", "") - .replace("[FILE]", "") - .replace("[VIDEO]", ""); - const separatorIndex = rawFilename.indexOf("|"); - const storedFilename = - separatorIndex >= 0 ? rawFilename.slice(0, separatorIndex) : rawFilename; - const displayFilename = - separatorIndex >= 0 ? rawFilename.slice(separatorIndex + 1) : storedFilename; - const filename = displayFilename || storedFilename; - const mediaPart: MessagePart = { type, filename }; - if (storedFilename && storedFilename !== filename) { - mediaPart.stored_filename = storedFilename; - } - botRecord.content.message.push(mediaPart); - } -} - -function scrollToBottom() { - nextTick(() => { - if (messagesEl.value) { - messagesEl.value.scrollTop = messagesEl.value.scrollHeight; - } - }); -} -</script> - -<style scoped> -.thread-panel { - width: 380px; - height: calc(100% - var(--chat-panel-top-offset, 0px)); - margin-top: var(--chat-panel-top-offset, 0px); - border-left: 1px solid var(--chat-border, rgba(var(--v-theme-on-surface), 0.1)); - background: var(--chat-page-bg, rgb(var(--v-theme-surface))); - color: rgb(var(--v-theme-on-surface)); - display: flex; - flex-direction: column; - flex-shrink: 0; -} - -.slide-left-enter-active, -.slide-left-leave-active { - transition: all 0.2s ease; -} - -.slide-left-enter-from, -.slide-left-leave-to { - transform: translateX(100%); - opacity: 0; -} - -.thread-panel-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 14px 16px 8px; -} - -.thread-panel-title { - font-size: 16px; - font-weight: 600; - line-height: 1.4; - color: rgb(var(--v-theme-on-surface)); -} - -.thread-panel-actions { - display: flex; - align-items: center; - gap: 4px; -} - -.thread-delete-button { - color: rgb(var(--v-theme-on-surface)); -} - -.thread-delete-button:hover { - background: transparent; -} - -.thread-selected-text { - margin: 4px 16px 12px; - padding: 12px 14px; - border: 1px solid rgba(var(--v-theme-on-surface), 0.1); - background: rgba(var(--v-theme-on-surface), 0.035); - border-radius: 18px; - color: rgba(var(--v-theme-on-surface), 0.72); - font-size: 13px; - line-height: 1.6; - max-height: 120px; - overflow-y: auto; -} - -.thread-messages { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 0 14px 12px; -} - -.thread-composer { - display: flex; - gap: 8px; - align-items: flex-end; - padding: 12px; - border-top: 1px solid rgba(var(--v-border-color), 0.14); -} - -.thread-input { - flex: 1; - box-sizing: border-box; - height: 40px; - min-height: 40px; - max-height: 140px; - padding: 9px 12px; - border: 1px solid rgba(var(--v-theme-on-surface), 0.14); - border-radius: 18px; - outline: none; - resize: none; - background: transparent; - color: inherit; - font: inherit; - line-height: 20px; -} - -.thread-input:focus { - border-color: rgba(var(--v-theme-on-surface), 0.36); -} - -.thread-send-button { - height: 40px; - min-height: 40px; - padding: 0 14px; - border: 1px solid rgba(var(--v-theme-on-surface), 0.14); - border-radius: 999px; - color: rgb(var(--v-theme-on-surface)); -} - -@media (max-width: 760px) { - .thread-panel { - position: fixed; - inset: 0; - z-index: 1300; - width: 100vw; - height: 100dvh; - margin-top: 0; - border-left: 0; - } - - .thread-panel-header { - min-height: 52px; - padding: calc(10px + env(safe-area-inset-top)) 12px 8px; - border-bottom: 1px solid var(--chat-border, rgba(var(--v-border-color), 0.12)); - } - - .thread-selected-text { - margin: 10px 12px; - padding: 10px 12px; - border-radius: 14px; - max-height: 96px; - font-size: 13px; - } - - .thread-messages { - padding: 0 12px 10px; - } - - .thread-composer { - gap: 8px; - padding: 10px 12px calc(10px + env(safe-area-inset-bottom)); - background: var(--chat-page-bg, rgb(var(--v-theme-surface))); - } - - .thread-input { - min-width: 0; - font-size: 16px; - } - - .thread-send-button { - min-width: 56px; - flex-shrink: 0; - } -} - -</style> diff --git a/legacy-dashboard/src/components/chat/ThreadedMarkdownMessagePart.vue b/legacy-dashboard/src/components/chat/ThreadedMarkdownMessagePart.vue deleted file mode 100644 index d1e4b682..00000000 --- a/legacy-dashboard/src/components/chat/ThreadedMarkdownMessagePart.vue +++ /dev/null @@ -1,94 +0,0 @@ -<template> - <MarkdownRender - custom-id="chat-message" - :content="threadedContent" - :is-dark="isDark" - :custom-html-tags="threadedCustomHtmlTags" - :final="!isStreaming" - :smooth-streaming="isStreaming ? 'auto' : false" - :fade="false" - :typewriter="false" - :max-live-nodes="MARKDOWN_RENDER_MAX_LIVE_NODES" - /> -</template> - -<script setup lang="ts"> -import { computed, provide } from "vue"; -import { MarkdownRender } from "markstream-vue"; -import { MARKDOWN_RENDER_MAX_LIVE_NODES } from "@/components/chat/markdownRenderConfig"; -import type { ChatThread } from "@/composables/useMessages"; - -const props = defineProps<{ - text: string; - threads: ChatThread[]; - refs: { used?: Array<Record<string, unknown>> } | null; - isDark: boolean; - customHtmlTags: string[]; - isStreaming?: boolean; -}>(); - -const emit = defineEmits<{ - openThread: [thread: ChatThread]; -}>(); - -const isDarkRef = computed(() => props.isDark); -const refsByIndex = computed(() => { - const refs = props.refs && Array.isArray(props.refs.used) ? props.refs.used : []; - return refs.reduce<Record<string, Record<string, unknown>>>((acc, item) => { - if (item.index != null) { - acc[String(item.index)] = item; - } - return acc; - }, {}); -}); -const threadMap = computed(() => - props.threads.reduce<Record<string, ChatThread>>((acc, thread) => { - acc[thread.thread_id] = thread; - return acc; - }, {}), -); -const threadedCustomHtmlTags = computed(() => - Array.from(new Set([...props.customHtmlTags, "thread"])), -); - -const threadedContent = computed(() => { - const source = props.text || ""; - const ranges = props.threads - .map((thread) => { - const selected = thread.selected_text || ""; - const start = selected ? source.indexOf(selected) : -1; - return { - start, - end: start + selected.length, - thread, - }; - }) - .filter((range) => range.start >= 0 && range.end > range.start) - .sort((a, b) => a.start - b.start); - - if (!ranges.length) return source; - - let cursor = 0; - let result = ""; - for (const range of ranges) { - if (range.start < cursor) continue; - result += source.slice(cursor, range.start); - result += `<thread>${escapeHtml(range.thread.thread_id)}</thread>`; - cursor = range.end; - } - result += source.slice(cursor); - return result; -}); - -provide("isDark", isDarkRef); -provide("webSearchResults", () => refsByIndex.value); -provide("chatThreadMap", () => threadMap.value); -provide("openChatThread", (thread: ChatThread) => emit("openThread", thread)); - -function escapeHtml(value: string) { - return value - .replace(/&/g, "&") - .replace(/</g, "<") - .replace(/>/g, ">"); -} -</script> diff --git a/legacy-dashboard/src/components/chat/attachmentPresentation.ts b/legacy-dashboard/src/components/chat/attachmentPresentation.ts deleted file mode 100644 index 3fb5d6e3..00000000 --- a/legacy-dashboard/src/components/chat/attachmentPresentation.ts +++ /dev/null @@ -1,130 +0,0 @@ -export interface AttachmentPresentationInput { - type?: string; - filename?: string; - original_name?: string; - embedded_file?: { - filename?: string; - }; -} - -export interface AttachmentPresentation { - color: string; - icon: string; - label: string; -} - -const fileTypeStyles: Record<string, AttachmentPresentation> = { - pdf: { color: "#c43b3b", icon: "mdi-file-pdf-box", label: "PDF" }, - doc: { color: "#2b579a", icon: "mdi-file-word-box", label: "WORD" }, - docx: { color: "#2b579a", icon: "mdi-file-word-box", label: "WORD" }, - xls: { color: "#217346", icon: "mdi-file-excel-box", label: "XLS" }, - xlsx: { color: "#217346", icon: "mdi-file-excel-box", label: "XLSX" }, - csv: { color: "#217346", icon: "mdi-file-delimited-outline", label: "CSV" }, - ppt: { color: "#d24726", icon: "mdi-file-powerpoint-box", label: "PPT" }, - pptx: { color: "#d24726", icon: "mdi-file-powerpoint-box", label: "PPT" }, - jpg: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - jpeg: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - png: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - gif: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - webp: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - svg: { color: "#c1467a", icon: "mdi-svg", label: "SVG" }, - heic: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - bmp: { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }, - mp3: { color: "#00897b", icon: "mdi-file-music-outline", label: "AUDIO" }, - wav: { color: "#00897b", icon: "mdi-file-music-outline", label: "AUDIO" }, - flac: { color: "#00897b", icon: "mdi-file-music-outline", label: "AUDIO" }, - m4a: { color: "#00897b", icon: "mdi-file-music-outline", label: "AUDIO" }, - ogg: { color: "#00897b", icon: "mdi-file-music-outline", label: "AUDIO" }, - mp4: { color: "#5e35b1", icon: "mdi-file-video-outline", label: "VIDEO" }, - mov: { color: "#5e35b1", icon: "mdi-file-video-outline", label: "VIDEO" }, - webm: { color: "#5e35b1", icon: "mdi-file-video-outline", label: "VIDEO" }, - zip: { color: "#8a6f00", icon: "mdi-folder-zip-outline", label: "ZIP" }, - rar: { color: "#8a6f00", icon: "mdi-folder-zip-outline", label: "RAR" }, - "7z": { color: "#8a6f00", icon: "mdi-folder-zip-outline", label: "7Z" }, - tar: { color: "#8a6f00", icon: "mdi-folder-zip-outline", label: "TAR" }, - gz: { color: "#8a6f00", icon: "mdi-folder-zip-outline", label: "GZ" }, - txt: { color: "#607d8b", icon: "mdi-file-document-outline", label: "TXT" }, - md: { color: "#607d8b", icon: "mdi-language-markdown-outline", label: "MD" }, - markdown: { - color: "#607d8b", - icon: "mdi-language-markdown-outline", - label: "MD", - }, -}; - -const codeFileTypes = new Set([ - "c", - "cc", - "cpp", - "cs", - "css", - "go", - "h", - "hpp", - "html", - "java", - "js", - "json", - "jsx", - "kt", - "php", - "py", - "rb", - "rs", - "scss", - "sh", - "sql", - "swift", - "ts", - "tsx", - "vue", - "xml", - "yaml", - "yml", -]); - -export function attachmentName(part: AttachmentPresentationInput) { - return ( - part.embedded_file?.filename || - part.original_name || - part.filename || - part.type || - "file" - ); -} - -export function attachmentExtension(part: AttachmentPresentationInput) { - const name = attachmentName(part); - const extension = name.split(".").pop()?.toLowerCase() || ""; - return extension === name.toLowerCase() ? "" : extension; -} - -export function attachmentPresentation( - part: AttachmentPresentationInput, -): AttachmentPresentation { - if (part.type === "image") { - return { color: "#c1467a", icon: "mdi-file-image", label: "IMAGE" }; - } - if (part.type === "record") { - return { color: "#00897b", icon: "mdi-file-music-outline", label: "AUDIO" }; - } - if (part.type === "video") { - return { color: "#5e35b1", icon: "mdi-file-video-outline", label: "VIDEO" }; - } - - const extension = attachmentExtension(part); - if (codeFileTypes.has(extension)) { - return { - color: "#6a4fb3", - icon: extension === "json" ? "mdi-code-json" : "mdi-file-code-outline", - label: extension ? extension.slice(0, 4).toUpperCase() : "CODE", - }; - } - return ( - fileTypeStyles[extension] || { - color: "#607d8b", - icon: "mdi-file-document-outline", - label: extension ? extension.slice(0, 4).toUpperCase() : "FILE", - } - ); -} diff --git a/legacy-dashboard/src/components/chat/markdownRenderConfig.ts b/legacy-dashboard/src/components/chat/markdownRenderConfig.ts deleted file mode 100644 index 1853ab05..00000000 --- a/legacy-dashboard/src/components/chat/markdownRenderConfig.ts +++ /dev/null @@ -1 +0,0 @@ -export const MARKDOWN_RENDER_MAX_LIVE_NODES = 320; diff --git a/legacy-dashboard/src/components/chat/message_list_comps/ActionRef.vue b/legacy-dashboard/src/components/chat/message_list_comps/ActionRef.vue deleted file mode 100644 index ed810842..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/ActionRef.vue +++ /dev/null @@ -1,131 +0,0 @@ -<template> - <div - v-if="refs && refs.used && refs.used.length > 0" - class="refs-container" - @click="handleClick" - > - <div class="refs-avatars"> - <div - v-for="(ref, refIdx) in refs.used.slice(0, 3)" - :key="refIdx" - class="ref-avatar" - :style="{ zIndex: 3 - refIdx }" - > - <img - v-if="ref.favicon" - :src="ref.favicon" - class="ref-favicon" - @error="(e) => (e.target.style.display = 'none')" - /> - <span v-else class="ref-initial">{{ getRefInitial(ref.title) }}</span> - </div> - <span v-if="refs.used.length > 3" class="refs-more"> - +{{ refs.used.length - 3 }} - </span> - <span class="refs-label"> - {{ tm("refs.sources") }} - </span> - </div> - </div> -</template> - -<script> -import { useModuleI18n } from "@/i18n/composables"; - -export default { - name: "ActionRef", - props: { - refs: { - type: Object, - default: null, - }, - }, - emits: ["open-refs"], - setup() { - const { tm } = useModuleI18n("features/chat"); - return { tm }; - }, - methods: { - // Get first character of ref title for fallback display - getRefInitial(title) { - if (!title) return "?"; - return title.charAt(0).toUpperCase(); - }, - - // Handle click to open refs sidebar - handleClick() { - this.$emit("open-refs", this.refs); - }, - }, -}; -</script> - -<style scoped> -.refs-container { - display: flex; - align-items: center; - min-height: 24px; - padding: 0 6px; - border-radius: 8px; - color: inherit; - cursor: pointer; - font-size: 12px; - line-height: 24px; - transition: background-color; -} - -.refs-container:hover { - background-color: rgba(103, 58, 183, 0.08); -} - -.refs-avatars { - display: flex; - align-items: center; - position: relative; - min-height: 24px; -} - -.ref-avatar { - width: 20px; - height: 20px; - border-radius: 50%; - opacity: 0.9; - display: flex; - align-items: center; - justify-content: center; - overflow: hidden; - position: relative; -} - -.ref-avatar:not(:first-child) { - margin-left: -8px; -} - -.ref-favicon { - width: 100%; - height: 100%; - object-fit: cover; -} - -.ref-initial { - font-size: 10px; - font-weight: 600; - color: white; - user-select: none; -} - -.refs-more { - margin-left: 6px; - font-size: 11px; - color: var(--v-theme-secondaryText); - opacity: 0.7; - font-weight: 500; -} - -.refs-label { - margin-left: 6px; - color: inherit; - font-size: 12px; - line-height: 24px; -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/IPythonToolBlock.vue b/legacy-dashboard/src/components/chat/message_list_comps/IPythonToolBlock.vue deleted file mode 100644 index 716d8d1b..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/IPythonToolBlock.vue +++ /dev/null @@ -1,216 +0,0 @@ -<template> - <div class="ipython-tool-block" :class="{ compact: !showHeader }"> - <div v-if="displayExpanded" class="py-3 animate-fade-in"> - <!-- Code Section --> - <div class="code-section"> - <div v-if="shikiReady && code" class="code-highlighted" - v-html="highlightedCode"></div> - <pre v-else class="code-fallback" - :class="{ 'dark-theme': isDark }">{{ code || 'No code available' }}</pre> - </div> - - <!-- Result Section --> - <div v-if="result" class="result-section"> - <div class="result-label"> - {{ tm('ipython.output') }}: - </div> - <pre class="result-content" - :class="{ 'dark-theme': isDark }">{{ formattedResult }}</pre> - </div> - </div> - </div> -</template> - -<script setup> -import { ref, computed, onMounted } from 'vue'; -import { useModuleI18n } from '@/i18n/composables'; -import { ensureShikiLanguages, escapeHtml, renderShikiCode } from '@/utils/shiki'; - -const props = defineProps({ - toolCall: { - type: Object, - required: true - }, - isDark: { - type: Boolean, - default: false - }, - initialExpanded: { - type: Boolean, - default: false - }, - showHeader: { - type: Boolean, - default: true - }, - forceExpanded: { - type: Boolean, - default: null - } -}); - -const { tm } = useModuleI18n('features/chat'); -const isExpanded = ref(props.initialExpanded); -const shikiHighlighter = ref(null); -const shikiReady = ref(false); - -const code = computed(() => { - try { - if (props.toolCall.args && props.toolCall.args.code) { - return props.toolCall.args.code; - } - } catch (err) { - console.error('Failed to get iPython code:', err); - } - return null; -}); - -const result = computed(() => props.toolCall.result); - -const formattedResult = computed(() => { - if (!result.value) return ''; - try { - const parsed = JSON.parse(result.value); - return JSON.stringify(parsed, null, 2); - } catch { - return result.value; - } -}); - -const highlightedCode = computed(() => { - if (!shikiReady.value || !shikiHighlighter.value || !code.value) { - return ''; - } - try { - return renderShikiCode( - shikiHighlighter.value, - code.value, - 'python', - props.isDark ? 'dark' : 'light' - ); - } catch (err) { - console.error('Failed to highlight code:', err); - return `<pre><code>${escapeHtml(code.value)}</code></pre>`; - } -}); - -const displayExpanded = computed(() => { - if (props.forceExpanded === null) { - return isExpanded.value; - } - return props.forceExpanded; -}); - -onMounted(async () => { - try { - shikiHighlighter.value = await ensureShikiLanguages(['python']); - shikiReady.value = true; - } catch (err) { - console.error('Failed to initialize Shiki:', err); - } -}); -</script> - -<style scoped> -.ipython-tool-block { - margin-bottom: 12px; - margin-top: 6px; - font-size: inherit; - line-height: inherit; -} - -.ipython-tool-block.compact { - margin: 0; -} - -.py-3 { - padding-top: 12px; - padding-bottom: 12px; -} - -.code-section { - margin-bottom: 12px; -} - -.code-highlighted { - border-radius: 6px; - overflow: hidden; - font-size: 12px; - line-height: 1.5; - overflow-x: auto; -} - -:deep(.code-highlighted pre.shiki) { - margin: 0; - padding: 16px; - border-radius: 6px; - overflow: auto; -} - -:deep(.code-highlighted pre.shiki code) { - display: block; - padding: 0; - background: transparent; - border-radius: 0; -} - -.code-fallback { - margin: 0; - padding: 12px; - border-radius: 6px; - overflow-x: auto; - font-size: 12px; - line-height: 1.5; - background-color: #f5f5f5; -} - -.code-fallback.dark-theme { - background-color: rgb(var(--v-theme-codeBg)); -} - -.result-section { - margin-top: 12px; -} - -.result-label { - font-size: 12px; - font-weight: 600; - color: var(--v-theme-secondaryText); - margin-bottom: 6px; - opacity: 0.8; -} - -.result-content { - margin: 0; - padding: 12px; - border-radius: 6px; - overflow-x: auto; - font-size: 12px; - line-height: 1.5; - background-color: #f5f5f5; - max-height: 300px; - overflow-y: auto; -} - -.result-content.dark-theme { - background-color: rgb(var(--v-theme-codeBg)); -} - -.animate-fade-in { - animation: fadeIn 0.2s ease-in-out; -} - -:deep(.code-highlighted pre) { - background-color: transparent !important; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/MarkdownMessagePart.vue b/legacy-dashboard/src/components/chat/message_list_comps/MarkdownMessagePart.vue deleted file mode 100644 index c3f689e6..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/MarkdownMessagePart.vue +++ /dev/null @@ -1,45 +0,0 @@ -<template> - <div class="markdown-content"> - <MarkdownRender - custom-id="chat-message" - :content="content" - :custom-html-tags="customHtmlTags" - :is-dark="isDark" - :final="!isStreaming" - :smooth-streaming="isStreaming ? 'auto' : false" - :fade="false" - :typewriter="false" - :max-live-nodes="MARKDOWN_RENDER_MAX_LIVE_NODES" - /> - </div> -</template> - -<script setup lang="ts"> -import { computed, provide } from "vue"; -import { MarkdownRender } from "markstream-vue"; -import { MARKDOWN_RENDER_MAX_LIVE_NODES } from "@/components/chat/markdownRenderConfig"; - -const props = defineProps<{ - content: string; - refs: { used?: Array<Record<string, unknown>> } | null; - isDark: boolean; - customHtmlTags: string[]; - isStreaming?: boolean; -}>(); - -const isDarkRef = computed(() => props.isDark); -const refsByIndex = computed(() => { - const messageRefs = props.refs; - const refs = - messageRefs && Array.isArray(messageRefs.used) ? messageRefs.used : []; - return refs.reduce<Record<string, Record<string, unknown>>>((acc, item) => { - if (item.index != null) { - acc[String(item.index)] = item; - } - return acc; - }, {}); -}); - -provide("isDark", isDarkRef); -provide("webSearchResults", () => refsByIndex.value); -</script> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/ReasoningBlock.vue b/legacy-dashboard/src/components/chat/message_list_comps/ReasoningBlock.vue deleted file mode 100644 index d709182f..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/ReasoningBlock.vue +++ /dev/null @@ -1,327 +0,0 @@ -<template> - <div class="reasoning-block" :class="{ 'reasoning-block--dark': isDark }"> - <button - class="reasoning-header" - :class="{ 'reasoning-header--trigger': openInSidebar }" - type="button" - @click="handlePrimaryAction" - > - <span class="reasoning-title"> - {{ reasoningTitle }} - </span> - <v-icon - size="22" - class="reasoning-icon" - :class="{ 'rotate-90': !openInSidebar && isExpanded }" - > - mdi-chevron-right - </v-icon> - </button> - - <div - v-if="!openInSidebar && isExpanded" - class="reasoning-content animate-fade-in" - > - <ReasoningTimeline - :parts="renderParts" - :reasoning="reasoning" - :is-dark="isDark" - :is-streaming="isStreaming" - /> - </div> - - <transition :name="previewTransitionName" mode="out-in"> - <div v-if="showStreamingPreview" :key="previewKey" class="reasoning-preview"> - {{ previewText }} - </div> - </transition> - </div> -</template> - -<script setup lang="ts"> -import { computed, onBeforeUnmount, ref, watch } from "vue"; -import { - reasoningActivityCounts, - reasoningActivityTitle, - type MessagePart, -} from "@/composables/useMessages"; -import { useModuleI18n } from "@/i18n/composables"; -import ReasoningTimeline from "@/components/chat/message_list_comps/ReasoningTimeline.vue"; - -const props = defineProps<{ - parts?: MessagePart[]; - reasoning?: string; - isDark?: boolean; - initialExpanded?: boolean; - isStreaming?: boolean; - hasNonReasoningContent?: boolean; - openInSidebar?: boolean; -}>(); - -const emit = defineEmits<{ - open: []; -}>(); - -const { tm } = useModuleI18n("features/chat"); -const isExpanded = ref(Boolean(props.initialExpanded)); -const previewText = ref(""); -const previewKey = ref(0); -let previewTimer: ReturnType<typeof setInterval> | null = null; -let previewStartTimer: ReturnType<typeof setTimeout> | null = null; - -const renderParts = computed<MessagePart[]>(() => { - if (props.parts?.length) return props.parts; - if (props.reasoning) { - return [{ type: "think", think: props.reasoning }]; - } - return []; -}); - -const openInSidebar = computed(() => Boolean(props.openInSidebar)); - -const activityCounts = computed(() => - reasoningActivityCounts(renderParts.value, props.reasoning || ""), -); - -const reasoningTitle = computed(() => - reasoningActivityTitle(activityCounts.value, tm), -); - -const thinkingText = computed(() => - renderParts.value - .filter((part) => part.type === "think") - .map((part) => String(part.think || "")) - .join(""), -); - -const showStreamingPreview = computed( - () => - props.isStreaming && - (openInSidebar.value || !isExpanded.value) && - !props.hasNonReasoningContent && - previewText.value, -); - -const previewTransitionName = computed(() => - props.hasNonReasoningContent - ? "reasoning-preview-collapse" - : "reasoning-preview-fade", -); - -function handlePrimaryAction() { - if (openInSidebar.value) { - emit("open"); - return; - } - isExpanded.value = !isExpanded.value; -} - -function latestReasoningPreview() { - const lines = thinkingText.value - .split(/\r?\n/) - .map((line) => line.trim()) - .filter(Boolean); - return lines.slice(-3).join("\n"); -} - -function updatePreviewLine() { - const nextText = latestReasoningPreview(); - if (!nextText || nextText === previewText.value) return; - previewText.value = nextText; - previewKey.value += 1; -} - -function stopPreviewTimer() { - if (!previewTimer) return; - clearInterval(previewTimer); - previewTimer = null; -} - -function stopPreviewStartTimer() { - if (!previewStartTimer) return; - clearTimeout(previewStartTimer); - previewStartTimer = null; -} - -function startPreviewTimer() { - updatePreviewLine(); - if (!previewTimer) { - previewTimer = setInterval(updatePreviewLine, 2000); - } -} - -function syncPreviewTimer() { - if ( - props.isStreaming && - (openInSidebar.value || !isExpanded.value) && - !props.hasNonReasoningContent - ) { - if (!previewTimer && !previewStartTimer) { - previewStartTimer = setTimeout(() => { - previewStartTimer = null; - if ( - props.isStreaming && - (openInSidebar.value || !isExpanded.value) && - !props.hasNonReasoningContent - ) { - startPreviewTimer(); - } - }, 2000); - } - return; - } - - stopPreviewStartTimer(); - stopPreviewTimer(); - if (!props.isStreaming) { - previewText.value = ""; - } -} - -watch( - () => [ - props.isStreaming, - isExpanded.value, - props.hasNonReasoningContent, - thinkingText.value, - openInSidebar.value, - ], - syncPreviewTimer, - { - immediate: true, - }, -); - -onBeforeUnmount(() => { - stopPreviewStartTimer(); - stopPreviewTimer(); -}); -</script> - -<style scoped> -.reasoning-block { - margin: 6px 0; - max-width: 100%; - color: rgba(var(--v-theme-on-surface), 0.7); - font-size: inherit; - line-height: inherit; -} - -.reasoning-header { - max-width: 100%; - border: 0; - padding: 0; - background: transparent; - color: inherit; - cursor: pointer; - user-select: none; - display: inline-flex; - align-items: center; - gap: 8px; - font: inherit; - text-align: left; -} - -.reasoning-header:hover { - color: rgba(var(--v-theme-on-surface), 0.88); -} - -.reasoning-icon { - color: currentcolor; - transition: transform 0.2s ease; - flex-shrink: 0; - align-self: center; -} - -.reasoning-title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.reasoning-content { - margin-top: 10px; - padding: 12px 14px; - border: 1px solid rgba(var(--v-theme-on-surface), 0.1); - border-radius: 18px; - background: rgb(var(--v-theme-surface)); - color: rgba(var(--v-theme-on-surface), 0.72); - animation: fadeIn 0.2s ease-in-out; - font-style: normal; -} - -.reasoning-preview { - max-width: 100%; - margin-top: 4px; - color: rgba(var(--v-theme-on-surface), 0.52); - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 3; - white-space: pre-line; - font: inherit; - font-size: 14.5px; - line-height: 1.62; - font-style: normal; -} - -.animate-fade-in { - animation: fadeIn 0.2s ease-in-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -.rotate-90 { - transform: rotate(90deg); -} - -.reasoning-preview-fade-enter-active { - transition: opacity 0.25s ease; -} - -.reasoning-preview-fade-leave-active { - transition: opacity 0.25s ease; -} - -.reasoning-preview-fade-enter-from, -.reasoning-preview-fade-leave-to { - opacity: 0; -} - -.reasoning-preview-collapse-enter-active { - transition: opacity 0.25s ease; -} - -.reasoning-preview-collapse-leave-active { - transition: - opacity 0.18s ease, - max-height 0.18s ease, - margin-top 0.18s ease; - overflow: hidden; -} - -.reasoning-preview-collapse-enter-from { - opacity: 0; -} - -.reasoning-preview-collapse-leave-from { - opacity: 1; - max-height: 6.5em; - margin-top: 4px; -} - -.reasoning-preview-collapse-leave-to { - opacity: 0; - max-height: 0; - margin-top: 0; -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/ReasoningTimeline.vue b/legacy-dashboard/src/components/chat/message_list_comps/ReasoningTimeline.vue deleted file mode 100644 index ca2aaed4..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/ReasoningTimeline.vue +++ /dev/null @@ -1,271 +0,0 @@ -<template> - <div v-if="timelineEntries.length" class="reasoning-timeline"> - <div - v-for="(entry, entryIndex) in timelineEntries" - :key="entry.key" - class="reasoning-timeline-item" - > - <div class="reasoning-timeline-rail" aria-hidden="true"> - <span class="reasoning-timeline-dot"></span> - <span - v-if="entryIndex < timelineEntries.length - 1" - class="reasoning-timeline-line" - ></span> - </div> - - <div class="reasoning-step"> - <div class="reasoning-step-meta"> - <span class="reasoning-step-title">{{ entry.title }}</span> - </div> - - <MarkdownRender - v-if="entry.kind === 'think'" - :content="entry.think || ''" - class="reasoning-text markdown-content" - :final="!isStreaming" - :smooth-streaming="isStreaming ? 'auto' : false" - :fade="false" - :typewriter="false" - :is-dark="isDark" - :max-live-nodes="MARKDOWN_RENDER_MAX_LIVE_NODES" - /> - - <div v-else-if="entry.tool" class="reasoning-tool-call-block"> - <ToolCallItem v-if="isIPythonToolCall(entry.tool)" :is-dark="isDark"> - <template #label> - <v-icon size="16">mdi-code-json</v-icon> - <span>{{ entry.tool.name || "python" }}</span> - <span class="tool-call-inline-status"> - {{ toolCallStatusText(entry.tool) }} - </span> - </template> - <template #details> - <IPythonToolBlock - :tool-call="entry.tool" - :is-dark="isDark" - :show-header="false" - :force-expanded="true" - /> - </template> - </ToolCallItem> - <ToolCallCard - v-else - :tool-call="entry.tool" - :is-dark="isDark" - /> - </div> - </div> - </div> - </div> -</template> - -<script setup lang="ts"> -import { computed } from "vue"; -import { MarkdownRender } from "markstream-vue"; -import { MARKDOWN_RENDER_MAX_LIVE_NODES } from "@/components/chat/markdownRenderConfig"; -import IPythonToolBlock from "@/components/chat/message_list_comps/IPythonToolBlock.vue"; -import ToolCallCard from "@/components/chat/message_list_comps/ToolCallCard.vue"; -import ToolCallItem from "@/components/chat/message_list_comps/ToolCallItem.vue"; -import type { MessagePart } from "@/composables/useMessages"; -import { useModuleI18n } from "@/i18n/composables"; - -const props = defineProps<{ - parts?: MessagePart[]; - reasoning?: string; - isDark?: boolean; - isStreaming?: boolean; -}>(); - -const { tm } = useModuleI18n("features/chat"); - -type NormalizedToolCall = Record<string, unknown>; - -type TimelineEntry = - | { - key: string; - kind: "think"; - title: string; - think: string; - } - | { - key: string; - kind: "tool_call"; - title: string; - tool: NormalizedToolCall; - }; - -const renderParts = computed<MessagePart[]>(() => { - if (props.parts?.length) return props.parts; - if (props.reasoning) { - return [{ type: "think", think: props.reasoning }]; - } - return []; -}); - -const timelineEntries = computed<TimelineEntry[]>(() => { - const entries: TimelineEntry[] = []; - - renderParts.value.forEach((part, partIndex) => { - if (part.type === "think") { - const think = String(part.think || ""); - if (!think.trim()) return; - entries.push({ - key: `think-${partIndex}`, - kind: "think", - title: tm("reasoning.think"), - think, - }); - return; - } - - if (part.type !== "tool_call" || !Array.isArray(part.tool_calls)) return; - - part.tool_calls.forEach((tool, toolIndex) => { - const normalizedTool = normalizeToolCall(tool); - entries.push({ - key: `tool-${String(tool.id || tool.name || `${partIndex}-${toolIndex}`)}`, - kind: "tool_call", - title: tm("reasoning.toolUsed"), - tool: normalizedTool, - }); - }); - }); - - return entries; -}); - -function normalizeToolCall(tool: Record<string, unknown>) { - const normalized = { ...tool }; - normalized.args = parseJsonSafe(normalized.args ?? normalized.arguments ?? {}); - normalized.result = parseJsonSafe(normalized.result); - normalized.ts = normalized.ts ?? Date.now() / 1000; - if (normalized.result && typeof normalized.result === "object") { - normalized.result = JSON.stringify(normalized.result, null, 2); - } - return normalized; -} - -function isIPythonToolCall(tool: Record<string, unknown>) { - const name = String(tool.name || "").toLowerCase(); - return name.includes("python") || name.includes("ipython"); -} - -function toolCallStatusText(tool: Record<string, unknown>) { - if (tool.finished_ts) return tm("toolStatus.done"); - return tm("toolStatus.running"); -} - -function parseJsonSafe(value: unknown) { - if (typeof value !== "string") return value; - try { - return JSON.parse(value); - } catch { - return value; - } -} -</script> - -<style scoped> -.reasoning-timeline { - display: flex; - flex-direction: column; - gap: 0; - padding-top: 4px; -} - -.reasoning-timeline-item { - display: grid; - grid-template-columns: 10px minmax(0, 1fr); - column-gap: 10px; - align-items: flex-start; - padding-bottom: 10px; -} - -.reasoning-timeline-item:last-child { - padding-bottom: 0; -} - -.reasoning-timeline-rail { - position: relative; - display: flex; - justify-content: center; - min-height: 100%; - padding-top: 6px; -} - -.reasoning-timeline-dot { - width: 6px; - height: 6px; - border-radius: 999px; - background: rgba(var(--v-theme-on-surface), 0.18); -} - -.reasoning-timeline-line { - position: absolute; - top: 15px; - bottom: -10px; - left: 50%; - width: 1px; - transform: translateX(-50%); - background: rgba(var(--v-theme-on-surface), 0.12); -} - -.reasoning-step { - min-width: 0; - font-size: 14.5px; - line-height: 1.62; -} - -.reasoning-step-meta { - display: flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; - margin-bottom: 8px; - color: rgba(var(--v-theme-on-surface), 0.54); - font-size: 12px; - line-height: 1.35; -} - -.reasoning-step-title { - color: rgba(var(--v-theme-on-surface), 0.76); - font-weight: 600; - font-size: 12px; -} - -.reasoning-tool-call-block { - margin-top: 4px; - font-style: normal; -} - -.reasoning-text { - font-size: inherit; - line-height: inherit; - color: rgba(var(--v-theme-on-surface), 0.72); - font-style: normal; -} - -.reasoning-step :deep(.tool-call-card), -.reasoning-step :deep(.tool-call-item), -.reasoning-step :deep(.ipython-tool-block) { - font-size: 13.5px; - line-height: 1.56; -} - -.reasoning-step :deep(.tool-call-card .detail-label) { - font-size: 11.5px; -} - -.reasoning-step :deep(.tool-call-card .detail-value), -.reasoning-step :deep(.ipython-tool-block .code-highlighted), -.reasoning-step :deep(.ipython-tool-block .code-fallback), -.reasoning-step :deep(.ipython-tool-block .result-label), -.reasoning-step :deep(.ipython-tool-block .result-content) { - font-size: 12.5px; -} - -.tool-call-inline-status { - margin-left: 4px; - color: rgba(var(--v-theme-on-surface), 0.48); -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/RefNode.vue b/legacy-dashboard/src/components/chat/message_list_comps/RefNode.vue deleted file mode 100644 index e79e2b7d..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/RefNode.vue +++ /dev/null @@ -1,91 +0,0 @@ -<template> - <v-chip - v-if="resultData" - class="ref-chip" - size="x-small" - variant="flat" - :style="chipStyle" - :href="url" - target="_blank" - clickable - > - <v-icon start size="x-small" color>mdi-link-variant</v-icon> - <span>{{ domain || resultData.title || refIndex }}</span> - </v-chip> -</template> - -<script setup> -import { computed, inject, unref, useSlots } from "vue"; - -const props = defineProps({ - node: { - type: Object, - default: null, - }, -}); - -const slots = useSlots(); -const injectedIsDark = inject("isDark", false); -const webSearchResults = inject("webSearchResults", () => ({})); - -const isDark = computed(() => Boolean(unref(injectedIsDark))); -const refIndex = computed(() => { - const nodeContent = props.node?.content?.trim(); - if (nodeContent) return nodeContent; - return slotText(slots.default?.()).trim(); -}); - -const resultData = computed(() => { - if (!refIndex.value) return null; - const results = - typeof webSearchResults === "function" - ? webSearchResults() - : webSearchResults; - return results?.[refIndex.value] || null; -}); - -const url = computed(() => resultData.value?.url || ""); - -const domain = computed(() => { - if (!url.value) return ""; - try { - const urlObj = new URL(url.value); - return urlObj.hostname.replace(/^www\./, ""); - } catch (e) { - return ""; - } -}); - -const chipStyle = computed(() => ({ - backgroundColor: isDark.value - ? "rgba(var(--v-theme-on-surface), 0.08)" - : "rgba(var(--v-theme-on-surface), 0.04)", - color: isDark.value - ? "rgba(var(--v-theme-on-surface), 0.62)" - : "rgba(var(--v-theme-on-surface), 0.72)", -})); - -function slotText(nodes = []) { - return nodes - .map((node) => { - if (typeof node.children === "string") return node.children; - if (Array.isArray(node.children)) return slotText(node.children); - return ""; - }) - .join(""); -} -</script> - -<style scoped> -.ref-chip { - margin: 0 2px; - cursor: pointer; - text-decoration: none; - transition: opacity; - margin-left: 4px; -} - -.ref-chip:hover { - opacity: 0.8; -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/RefsSidebar.vue b/legacy-dashboard/src/components/chat/message_list_comps/RefsSidebar.vue deleted file mode 100644 index 8ade49b3..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/RefsSidebar.vue +++ /dev/null @@ -1,276 +0,0 @@ -<template> - <transition name="slide-left"> - <div v-if="isOpen" class="refs-sidebar"> - <div class="sidebar-header"> - <h3 class="sidebar-title">{{ tm("refs.title") }}</h3> - <v-btn - icon="mdi-close" - size="small" - variant="text" - @click="close" - ></v-btn> - </div> - - <div class="refs-list"> - <div - v-for="(ref, index) in normalizedRefs" - :key="ref.index || index" - class="ref-item" - @click="openLink(ref.url)" - > - <div class="ref-item-icon"> - <img - v-if="ref.favicon" - :src="ref.favicon" - class="ref-item-favicon" - @error="(e) => (e.target.style.display = 'none')" - /> - <div v-else class="ref-item-initial"> - {{ getRefInitial(ref.title) }} - </div> - </div> - <div class="ref-item-content"> - <div class="ref-item-title">{{ ref.title }}</div> - <div class="ref-item-url">{{ formatUrl(ref.url) }}</div> - <div v-if="ref.snippet" class="ref-item-snippet"> - {{ ref.snippet }} - </div> - </div> - <v-icon size="small" class="ref-item-arrow">mdi-open-in-new</v-icon> - </div> - </div> - </div> - </transition> -</template> - -<script> -import { useModuleI18n } from "@/i18n/composables"; - -export default { - name: "RefsSidebar", - props: { - modelValue: { - type: Boolean, - default: false, - }, - refs: { - type: Object, - default: null, - }, - }, - emits: ["update:modelValue"], - setup() { - const { tm } = useModuleI18n("features/chat"); - return { tm }; - }, - computed: { - isOpen: { - get() { - return this.modelValue; - }, - set(value) { - this.$emit("update:modelValue", value); - }, - }, - - normalizedRefs() { - const used = Array.isArray(this.refs?.used) - ? this.refs.used - : Array.isArray(this.refs) - ? this.refs - : []; - - return used - .map((ref) => ({ - index: ref?.index, - title: ref?.title || ref?.url || "Reference", - url: ref?.url, - snippet: ref?.snippet, - favicon: ref?.favicon, - })) - .filter((ref) => ref.url); - }, - }, - methods: { - close() { - this.isOpen = false; - }, - - getRefInitial(title) { - if (!title) return "?"; - return title.charAt(0).toUpperCase(); - }, - - formatUrl(url) { - if (!url) return ""; - try { - const urlObj = new URL(url); - return urlObj.hostname; - } catch { - return url; - } - }, - - openLink(url) { - if (url) { - window.open(url, "_blank"); - } - }, - }, -}; -</script> - -<style scoped> -.refs-sidebar { - width: 360px; - height: calc(100% - var(--chat-panel-top-offset, 0px)); - margin-top: var(--chat-panel-top-offset, 0px); - background: var(--chat-page-bg, rgb(var(--v-theme-surface))); - border-left: 1px solid var(--chat-border, rgba(var(--v-border-color), 0.16)); - display: flex; - flex-direction: column; - flex-shrink: 0; - color: rgb(var(--v-theme-on-surface)); -} - -.slide-left-enter-active, -.slide-left-leave-active { - transition: all 0.3s ease; -} - -.slide-left-enter-from { - transform: translateX(100%); - opacity: 0; -} - -.slide-left-leave-to { - transform: translateX(100%); - opacity: 0; -} - -.sidebar-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 14px 16px 8px; - flex-shrink: 0; -} - -.sidebar-title { - font-size: 16px; - font-weight: 600; - color: rgb(var(--v-theme-on-surface)); - line-height: 1.4; - margin: 0; -} - -.refs-list { - padding: 12px; - padding-top: 0; - overflow-y: auto; - flex: 1; -} - -.ref-item { - display: flex; - align-items: flex-start; - gap: 12px; - padding: 12px; - margin-bottom: 8px; - border-radius: 8px; - border: 1px solid var(--v-theme-border); - cursor: pointer; - transition: all 0.2s ease; -} - -.ref-item:hover { - background-color: rgba(103, 58, 183, 0.05); - border-color: rgba(103, 58, 183, 0.3); -} - -.ref-item-icon { - flex-shrink: 0; - width: 32px; - height: 32px; - border-radius: 50%; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; -} - -.ref-item-favicon { - width: 100%; - height: 100%; - object-fit: cover; -} - -.ref-item-initial { - font-size: 14px; - font-weight: 600; - color: white; -} - -.ref-item-content { - flex: 1; - min-width: 0; -} - -.ref-item-title { - font-size: 14px; - font-weight: 500; - color: var(--v-theme-primaryText); - margin-bottom: 4px; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; -} - -.ref-item-url { - font-size: 12px; - color: var(--v-theme-secondaryText); - opacity: 0.7; - margin-bottom: 6px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.ref-item-snippet { - font-size: 12px; - color: var(--v-theme-secondaryText); - opacity: 0.8; - line-height: 1.5; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 3; - -webkit-box-orient: vertical; -} - -.ref-item-arrow { - flex-shrink: 0; - margin-top: 4px; - color: var(--v-theme-secondaryText); - opacity: 0.5; - transition: opacity 0.2s ease; -} - -.ref-item:hover .ref-item-arrow { - opacity: 1; -} - -@media (max-width: 760px) { - .refs-sidebar { - position: fixed; - inset: 0; - z-index: 1300; - width: 100vw; - height: 100dvh; - margin-top: 0; - border-left: 0; - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/ThreadNode.vue b/legacy-dashboard/src/components/chat/message_list_comps/ThreadNode.vue deleted file mode 100644 index 6e1aad62..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/ThreadNode.vue +++ /dev/null @@ -1,63 +0,0 @@ -<template> - <button class="thread-node" type="button" @click="open"> - {{ thread?.selected_text || threadId }} - </button> -</template> - -<script setup lang="ts"> -import { computed, inject, useSlots } from "vue"; -import type { ChatThread } from "@/composables/useMessages"; - -const props = defineProps<{ - node?: { - content?: string; - } | null; -}>(); - -const slots = useSlots(); -const threadMap = inject("chatThreadMap", () => ({} as Record<string, ChatThread>)); -const openThread = inject("openChatThread", (_thread: ChatThread) => {}); - -const threadId = computed(() => { - const nodeContent = props.node?.content?.trim(); - if (nodeContent) return nodeContent; - return slotText(slots.default?.()).trim(); -}); - -const thread = computed(() => { - const map = typeof threadMap === "function" ? threadMap() : threadMap; - return map[threadId.value] || null; -}); - -function open() { - if (thread.value) { - openThread(thread.value); - } -} - -function slotText(nodes: unknown[] = []): string { - return nodes - .map((node: any) => { - if (typeof node.children === "string") return node.children; - if (Array.isArray(node.children)) return slotText(node.children); - return ""; - }) - .join(""); -} -</script> - -<style scoped> -.thread-node { - padding: 0; - border: 0; - background: transparent; - color: inherit; - font: inherit; - line-height: inherit; - text-decoration-line: underline; - text-decoration-style: dotted; - text-decoration-thickness: 2px; - text-underline-offset: 3px; - cursor: pointer; -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/ToolCallCard.vue b/legacy-dashboard/src/components/chat/message_list_comps/ToolCallCard.vue deleted file mode 100644 index 7ba80905..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/ToolCallCard.vue +++ /dev/null @@ -1,271 +0,0 @@ -<template> - <div class="tool-call-card" :class="{ expanded: isExpanded }"> - <button class="tool-call-header" type="button" @click="toggleExpanded"> - <v-icon size="16" class="tool-call-icon">{{ toolCallIcon }}</v-icon> - <span class="tool-call-title"> - {{ tm("actions.toolCallUsed", { name: displayToolName }) }} - </span> - <span class="tool-call-duration">{{ toolCallDuration }}</span> - <v-icon - size="22" - class="tool-call-expand-icon" - :class="{ expanded: isExpanded }" - > - mdi-chevron-right - </v-icon> - </button> - - <div v-if="isExpanded" class="tool-call-details"> - <div v-if="toolCall.id" class="tool-call-detail-row"> - <span class="detail-label">ID:</span> - <code class="detail-value"> - {{ toolCall.id }} - </code> - </div> - - <div class="tool-call-detail-row"> - <span class="detail-label">Args:</span> - <pre class="detail-value detail-json">{{ - JSON.stringify(toolCall.args, null, 2) - }}</pre> - </div> - - <div v-if="toolCall.result" class="tool-call-detail-row"> - <span class="detail-label">Result:</span> - <pre class="detail-value detail-json detail-result">{{ - formattedResult - }}</pre> - </div> - </div> - </div> -</template> - -<script setup> -import { computed, onMounted, onUnmounted, ref } from "vue"; -import { useModuleI18n } from "@/i18n/composables"; - -const props = defineProps({ - toolCall: { - type: Object, - required: true, - }, - isDark: { - type: Boolean, - default: false, - }, - initialExpanded: { - type: Boolean, - default: false, - }, -}); - -const { tm } = useModuleI18n("features/chat"); -const isExpanded = ref(props.initialExpanded); -const currentTime = ref(Date.now() / 1000); -let timer = null; - -const elapsedTime = computed(() => { - if (props.toolCall.finished_ts) return ""; - const startTime = Number(props.toolCall.ts); - if (!Number.isFinite(startTime) || startTime <= 0) return ""; - return formatDuration(currentTime.value - startTime); -}); - -const displayToolName = computed(() => props.toolCall.name || "tool"); - -const toolCallIcon = computed(() => { - const name = String(props.toolCall.name || ""); - if (name === "astrbot_execute_ipython" || name === "astrbot_execute_python") { - return "mdi-code-json"; - } - if (name.includes("web_search") || name.includes("tavily")) { - return "mdi-web"; - } - if (name === "astrbot_execute_shell") { - return "mdi-console-line"; - } - return "mdi-wrench"; -}); - -const toolCallDuration = computed(() => { - const startTime = Number(props.toolCall.ts); - if (!Number.isFinite(startTime) || startTime <= 0) return ""; - if (props.toolCall.finished_ts) { - return formatDuration(Number(props.toolCall.finished_ts) - startTime); - } - return elapsedTime.value; -}); - -const formattedResult = computed(() => { - if (!props.toolCall.result) return ""; - try { - const parsed = JSON.parse(props.toolCall.result); - return JSON.stringify(parsed, null, 2); - } catch { - return props.toolCall.result; - } -}); - -const formatDuration = (seconds) => { - if (!Number.isFinite(seconds) || seconds < 0) return ""; - if (seconds < 1) { - return `${Math.round(seconds * 1000)}ms`; - } else if (seconds < 60) { - return `${seconds.toFixed(1)}s`; - } else { - const minutes = Math.floor(seconds / 60); - const secs = Math.round(seconds % 60); - return `${minutes}m ${secs}s`; - } -}; - -const toggleExpanded = () => { - isExpanded.value = !isExpanded.value; -}; - -const updateTime = () => { - currentTime.value = Date.now() / 1000; -}; - -onMounted(() => { - if (!props.toolCall.finished_ts) { - timer = setInterval(updateTime, 100); - } -}); - -onUnmounted(() => { - if (timer) { - clearInterval(timer); - } -}); -</script> - -<style scoped> -.tool-call-card { - margin: 6px 0; - max-width: 100%; - color: rgba(var(--v-theme-on-surface), 0.7); - font-size: inherit; - line-height: inherit; -} - -.tool-call-card.expanded { - width: 100%; -} - -.tool-call-header { - max-width: 100%; - border: 0; - padding: 0; - background: transparent; - color: inherit; - cursor: pointer; - user-select: none; - display: inline-flex; - align-items: center; - gap: 8px; - font: inherit; - text-align: left; -} - -.tool-call-header:hover { - color: rgba(var(--v-theme-on-surface), 0.88); -} - -.tool-call-expand-icon { - color: currentcolor; - transition: transform 0.2s ease; - flex-shrink: 0; -} - -.tool-call-expand-icon.expanded { - transform: rotate(90deg); -} - -.tool-call-icon { - color: currentcolor; - flex-shrink: 0; -} - -.tool-call-title { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.tool-call-duration { - flex-shrink: 0; - color: rgba(var(--v-theme-on-surface), 0.48); -} - -.tool-call-details { - margin-top: 8px; - padding-left: 26px; - animation: fadeIn 0.2s ease-in-out; -} - -.tool-call-detail-row { - display: flex; - flex-direction: column; - margin-bottom: 8px; -} - -.tool-call-detail-row:last-child { - margin-bottom: 0; -} - -.detail-label { - font-size: 11px; - font-weight: 600; - color: rgba(var(--v-theme-on-surface), 0.55); - text-transform: uppercase; - margin-bottom: 4px; -} - -.detail-value { - font-size: 12px; - color: rgba(var(--v-theme-on-surface), 0.8); - background-color: transparent; - padding: 0; - border-radius: 4px; - word-break: break-all; -} - -.detail-json { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; - white-space: pre-wrap; - max-height: 200px; - overflow-y: auto; - margin: 0; -} - -.detail-result { - max-height: 300px; - background-color: transparent; -} - -.animate-fade-in { - animation: fadeIn 0.2s ease-in-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} - -@keyframes spin { - from { - transform: rotate(0deg); - } - - to { - transform: rotate(360deg); - } -} -</style> diff --git a/legacy-dashboard/src/components/chat/message_list_comps/ToolCallItem.vue b/legacy-dashboard/src/components/chat/message_list_comps/ToolCallItem.vue deleted file mode 100644 index 29cfb125..00000000 --- a/legacy-dashboard/src/components/chat/message_list_comps/ToolCallItem.vue +++ /dev/null @@ -1,77 +0,0 @@ -<template> - <div class="tool-call-item"> - <div class="tool-call-line" role="button" tabindex="0" - @click="toggleExpanded" - @keydown.enter="toggleExpanded" - @keydown.space.prevent="toggleExpanded"> - <slot name="label" :expanded="isExpanded" /> - </div> - <transition name="tool-call-fade"> - <div v-if="isExpanded" class="tool-call-inline-details" :class="{ 'is-dark': isDark }"> - <slot name="details" /> - </div> - </transition> - </div> -</template> - -<script setup> -import { ref } from 'vue'; - -const props = defineProps({ - isDark: { - type: Boolean, - default: false - } -}); - -const isExpanded = ref(false); - -const toggleExpanded = () => { - isExpanded.value = !isExpanded.value; -}; -</script> - -<style scoped> -.tool-call-line { - font: inherit; - line-height: inherit; - color: var(--v-theme-secondaryText); - opacity: 0.85; - cursor: pointer; - user-select: none; - transition: color 0.2s ease, opacity 0.2s ease; - display: inline-flex; - align-items: center; - gap: 6px; -} - -.tool-call-line:hover { - color: var(--v-theme-secondary); - opacity: 1; -} - -.tool-call-inline-details { - margin-top: 6px; - padding: 8px 10px; - border-left: 2px solid var(--v-theme-border); - border-radius: 6px; - background-color: rgba(0, 0, 0, 0.02); - font-size: inherit; - line-height: inherit; -} - -.tool-call-inline-details.is-dark { - background-color: rgba(255, 255, 255, 0.04); - border-left-color: rgba(255, 255, 255, 0.15); -} - -.tool-call-fade-enter-active, -.tool-call-fade-leave-active { - transition: opacity 0.1s ease; -} - -.tool-call-fade-enter-from, -.tool-call-fade-leave-to { - opacity: 0; -} -</style> diff --git a/legacy-dashboard/src/components/config/AstrBotCoreConfigWrapper.vue b/legacy-dashboard/src/components/config/AstrBotCoreConfigWrapper.vue deleted file mode 100644 index b485a783..00000000 --- a/legacy-dashboard/src/components/config/AstrBotCoreConfigWrapper.vue +++ /dev/null @@ -1,180 +0,0 @@ -<template> - <div :class="$vuetify.display.mobile ? '' : 'd-flex'"> - <v-tabs v-model="tab" :direction="$vuetify.display.mobile ? 'horizontal' : 'vertical'" - :align-tabs="$vuetify.display.mobile ? 'left' : 'start'" color="deep-purple-accent-4" class="config-tabs"> - <v-tab v-for="section in visibleSections" :key="section.key" :value="section.key" - style="font-weight: 1000; font-size: 15px"> - {{ tm(section.value['name']) }} - </v-tab> - </v-tabs> - <v-tabs-window v-model="tab" class="config-tabs-window" :style="readonly ? 'pointer-events: none; opacity: 0.6;' : ''"> - <v-tabs-window-item v-for="section in visibleSections" :key="section.key" :value="section.key"> - <v-container fluid> - <div v-for="(val2, key2, index2) in section.value['metadata']" :key="key2"> - <!-- Support both traditional and JSON selector metadata --> - <AstrBotConfigV4 - :metadata="{ [key2]: section.value['metadata'][key2] }" - :iterable="config_data" - :metadataKey="key2" - :search-keyword="searchKeyword" - > - </AstrBotConfigV4> - </div> - </v-container> - </v-tabs-window-item> - - - <div style="margin-left: 16px; padding-bottom: 16px"> - <small>{{ tm('help.helpPrefix') }} - <a href="https://docs.astrbot.app/" target="_blank">{{ tm('help.documentation') }}</a> - {{ tm('help.helpMiddle') }} - <a href="https://qm.qq.com/cgi-bin/qm/qr?k=EYGsuUTfe00_iOu9JTXS7_TEpMkXOvwv&jump_from=webapi&authKey=uUEMKCROfsseS+8IzqPjzV3y1tzy4AkykwTib2jNkOFdzezF9s9XknqnIaf3CDft" - target="_blank">{{ tm('help.support') }}</a>{{ tm('help.helpSuffix') }} - </small> - </div> - - </v-tabs-window> - </div> - <v-container v-if="visibleSections.length === 0" fluid class="px-0"> - <v-alert type="info" variant="tonal"> - {{ tm('search.noResult') }} - </v-alert> - </v-container> -</template> - -<script> -import AstrBotConfigV4 from '@/components/shared/AstrBotConfigV4.vue'; -import { useModuleI18n } from '@/i18n/composables'; - -export default { - name: 'AstrBotCoreConfigWrapper', - components: { - AstrBotConfigV4 - }, - props: { - metadata: { - type: Object, - required: true, - default: () => ({}) - }, - config_data: { - type: Object, - required: true, - default: () => ({}) - }, - readonly: { - type: Boolean, - default: false - }, - searchKeyword: { - type: String, - default: '' - } - }, - setup() { - const { tm: tmConfig } = useModuleI18n('features/config'); - const { tm: tmMetadata } = useModuleI18n('features/config-metadata'); - - const tm = (key) => { - const metadataResult = tmMetadata(key); - if (!metadataResult.startsWith('[MISSING:') && !metadataResult.startsWith('[INVALID:')) { - return metadataResult; - } - return tmConfig(key); - }; - - return { - tm - }; - }, - data() { - return { - tab: null, // 当前激活的配置标签页 key - } - }, - computed: { - normalizedSearchKeyword() { - return String(this.searchKeyword || '').trim().toLowerCase(); - }, - visibleSections() { - if (!this.metadata || typeof this.metadata !== 'object') { - return []; - } - const allSections = Object.entries(this.metadata).map(([key, value]) => ({ key, value })); - if (!this.normalizedSearchKeyword) { - return allSections; - } - return allSections.filter((section) => this.sectionHasSearchMatch(section.value)); - } - }, - watch: { - visibleSections(newSections) { - const sectionKeys = newSections.map((section) => section.key); - if (!sectionKeys.includes(this.tab)) { - this.tab = sectionKeys[0] ?? null; - } - } - }, - mounted() { - const sectionKeys = this.visibleSections.map((section) => section.key); - this.tab = sectionKeys[0] ?? null; - }, - methods: { - sectionHasSearchMatch(section) { - const keyword = this.normalizedSearchKeyword; - if (!keyword) { - return true; - } - const sectionMetadata = section?.metadata || {}; - return Object.values(sectionMetadata).some((metaItem) => this.metaObjectHasSearchMatch(metaItem, keyword)); - }, - metaObjectHasSearchMatch(metaObject, keyword) { - if (!metaObject || typeof metaObject !== 'object') { - return false; - } - const target = [ - this.tm(metaObject.description || ''), - this.tm(metaObject.hint || ''), - ...Object.entries(metaObject.items || {}).flatMap(([itemKey, itemMeta]) => ([ - itemKey, - this.tm(itemMeta?.description || ''), - this.tm(itemMeta?.hint || '') - ])) - ] - .join(' ') - .toLowerCase(); - - return target.includes(keyword); - } - } -} -</script> - -<style> -@media (min-width: 768px) { - .config-tabs { - display: flex; - margin: 16px 16px 0 0; - } - - .config-tabs-window { - flex: 1; - } - - .config-tabs .v-tab { - justify-content: flex-start !important; - text-align: left; - min-height: 48px; - } -} - -@media (max-width: 767px) { - .config-tabs { - width: 100%; - } - - .config-tabs-window { - margin-top: 16px; - } -} -</style> diff --git a/legacy-dashboard/src/components/config/UnsavedChangesConfirmDialog.vue b/legacy-dashboard/src/components/config/UnsavedChangesConfirmDialog.vue deleted file mode 100644 index 413218a6..00000000 --- a/legacy-dashboard/src/components/config/UnsavedChangesConfirmDialog.vue +++ /dev/null @@ -1,98 +0,0 @@ -<template> - <v-dialog v-model="isOpen" max-width="480" persistent> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center justify-space-between"> - <span>{{ title }}</span> - <v-btn icon="mdi-close" variant="text" @click="handleClose"></v-btn> - </v-card-title> - <v-card-text> - <div class="message-text">{{ message }}</div> - <div class="action-hints"> - <span class="hint-item">{{ confirmHint }}</span> - <span class="hint-item">{{ cancelHint }}</span> - <span class="hint-item">{{ closeHint }}</span> - </div> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn color="gray" variant="text" @click="handleCancel">{{ t('core.common.dialog.cancelButton') }}</v-btn> - <v-btn color="red" variant="tonal" @click="handleConfirm" class="confirm-button">{{ t('core.common.dialog.confirmButton') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref } from "vue"; -import { useI18n } from '@/i18n/composables'; - -const { t } = useI18n(); - -const isOpen = ref(false); -const title = ref(""); -const message = ref(""); -const confirmHint = ref(""); -const cancelHint = ref(""); -const closeHint = ref(""); -let resolvePromise = null; - -const open = (options) => { - title.value = options.title || t('core.common.dialog.confirmTitle'); - message.value = options.message || t('core.common.dialog.confirmMessage'); - confirmHint.value = options.confirmHint || ""; - cancelHint.value = options.cancelHint || ""; - closeHint.value = options.closeHint || ""; - isOpen.value = true; - - return new Promise((resolve) => { - resolvePromise = resolve; - }); -}; - -const handleConfirm = () => { - isOpen.value = false; - if (resolvePromise) resolvePromise(true); -}; - -const handleCancel = () => { - isOpen.value = false; - if (resolvePromise) resolvePromise(false); -}; - -const handleClose = () => { - isOpen.value = false; - if (resolvePromise) resolvePromise('close'); -}; - -defineExpose({ open }); -</script> - - -<style scoped> -.message-text { - margin-bottom: 8px; - line-height: 1.5; - font-size: 16px; - font-weight: 600; -} - -.action-hints { - display: flex; - gap: 15px; -} - -.hint-item { - color: var(--v-theme-secondaryText, #666); - font-size: 12px; - opacity: 0.7; -} - -.dialog-title { - font-size: 20px; - font-weight: 500; -} - -.confirm-button { - color: rgb(239, 68, 68); -} -</style> diff --git a/legacy-dashboard/src/components/extension/MarketPluginCard.vue b/legacy-dashboard/src/components/extension/MarketPluginCard.vue deleted file mode 100644 index 990584a9..00000000 --- a/legacy-dashboard/src/components/extension/MarketPluginCard.vue +++ /dev/null @@ -1,384 +0,0 @@ -<script setup> -import { computed } from "vue"; -import { useModuleI18n } from "@/i18n/composables"; -import PluginPlatformChip from "@/components/shared/PluginPlatformChip.vue"; -import { usePluginI18n } from "@/utils/pluginI18n"; - -const { tm } = useModuleI18n("features/extension"); -const { pluginShortDesc } = usePluginI18n(); - -const props = defineProps({ - plugin: { - type: Object, - required: true, - }, - defaultPluginIcon: { - type: String, - required: true, - }, - showPluginFullName: { - type: Boolean, - default: false, - }, -}); - -const emit = defineEmits(["install", "open"]); - -const normalizePlatformList = (platforms) => { - if (!Array.isArray(platforms)) return []; - return platforms.filter((item) => typeof item === "string"); -}; - -const platformDisplayList = computed(() => - normalizePlatformList(props.plugin?.support_platforms), -); - -const cardDescription = computed(() => - pluginShortDesc( - props.plugin, - props.plugin?.short_desc || props.plugin?.desc || "", - ), -); - -const canInstallPlugin = computed(() => { - return Boolean(props.plugin?.market_plugin_id); -}); - -const hasDownloadCount = computed(() => { - return ( - props.plugin?.download_count !== undefined && - props.plugin?.download_count !== null - ); -}); - -const handleInstall = (plugin) => { - if (!canInstallPlugin.value) return; - emit("install", plugin); -}; - -const handleOpen = () => { - emit("open", props.plugin); -}; -</script> - -<template> - <v-card - class="rounded-lg d-flex flex-column plugin-card" - variant="outlined" - elevation="0" - :ripple="false" - @click="handleOpen" - > - <v-card-text class="plugin-card-content"> - <div class="plugin-cover"> - <img - :src="plugin?.logo || defaultPluginIcon" - :alt="plugin.name" - class="plugin-cover__image" - /> - </div> - - <div class="plugin-info"> - <div class="d-flex align-center plugin-title-row"> - <div class="font-weight-bold plugin-title"> - {{ - plugin.display_name?.length - ? plugin.display_name - : showPluginFullName - ? plugin.name - : plugin.trimmedName - }} - </div> - <v-chip - v-if="plugin?.pinned" - color="warning" - size="x-small" - label - class="market-recommended-chip" - > - {{ tm("market.recommended") }} - </v-chip> - <v-chip - v-if="plugin?.astrbot_version_supported === false" - color="error" - size="x-small" - label - class="market-unsupported-chip" - > - {{ tm("status.unsupported") }} - </v-chip> - </div> - - <div class="d-flex align-center plugin-meta"> - <v-icon - icon="mdi-account" - size="x-small" - style="color: rgba(var(--v-theme-on-surface), 0.5)" - ></v-icon> - <a - v-if="plugin?.social_link" - :href="plugin.social_link" - target="_blank" - @click.stop - class="text-subtitle-2 font-weight-medium" - style=" - text-decoration: none; - color: rgb(var(--v-theme-primary)); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - " - > - {{ plugin.author }} - </a> - <span - v-else - class="text-subtitle-2 font-weight-medium" - style=" - color: rgb(var(--v-theme-primary)); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - " - > - {{ plugin.author }} - </span> - <div - v-if="plugin.stars !== undefined" - class="d-flex align-center text-subtitle-2 ml-2 market-stat" - > - <v-icon - icon="mdi-star" - size="x-small" - style="margin-right: 2px" - ></v-icon> - <span>{{ plugin.stars }}</span> - </div> - <div - v-if="hasDownloadCount" - class="d-flex align-center text-subtitle-2 ml-2 market-stat" - > - <v-icon - icon="mdi-download" - size="x-small" - style="margin-right: 2px" - ></v-icon> - <span>{{ plugin.download_count }}</span> - </div> - </div> - - <div class="text-caption plugin-description"> - {{ cardDescription }} - </div> - - <div class="plugin-stats"></div> - </div> - </v-card-text> - - <v-card-actions - style="gap: 6px; padding: 8px 12px; padding-top: 0" - @click.stop - > - <div v-if="platformDisplayList.length" class="plugin-badges"> - <PluginPlatformChip - :platforms="plugin.support_platforms" - size="x-small" - :chip-style="{ height: '20px' }" - /> - </div> - <v-spacer></v-spacer> - <v-btn - v-if="plugin?.repo" - color="secondary" - size="small" - variant="tonal" - class="market-action-btn" - :href="plugin.repo" - target="_blank" - style="height: 32px" - > - <v-icon icon="mdi-github" start size="small"></v-icon> - {{ tm("buttons.viewRepo") }} - </v-btn> - <v-tooltip - v-if="!plugin?.installed" - location="top" - :disabled="canInstallPlugin" - :text="tm('messages.missingMarketPluginId')" - > - <template v-slot:activator="{ props: tooltipProps }"> - <div v-bind="tooltipProps"> - <v-btn - color="primary" - size="small" - @click="handleInstall(plugin)" - variant="flat" - class="market-action-btn" - style="height: 32px" - :disabled="!canInstallPlugin" - > - {{ tm("buttons.install") }} - </v-btn> - </div> - </template> - </v-tooltip> - <v-btn - v-else - color="success" - size="small" - variant="flat" - disabled - class="market-action-btn" - style="height: 32px" - > - ✓ {{ tm("status.installed") }} - </v-btn> - </v-card-actions> - </v-card> -</template> - -<style scoped> -.plugin-card { - background: rgb(var(--v-theme-surface)); - cursor: pointer; - transition: background-color 0.16s ease; -} - -.plugin-card:hover, -.plugin-card:focus-within { - background: rgba(var(--v-theme-on-surface), 0.04); -} - -.plugin-card :deep(.v-card__overlay), -.plugin-card :deep(.v-ripple__container) { - display: none; -} - -.plugin-card-content { - padding: 12px; - padding-bottom: 8px; - display: flex; - flex-direction: row; - gap: 12px; - width: 100%; - flex: 1; - overflow: hidden; - min-height: 0; -} - -.plugin-cover { - flex-shrink: 0; - width: 76px; - height: 76px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 8px; - background: transparent; -} - -.plugin-cover__image { - width: 76px; - height: 76px; - border-radius: 8px; - object-fit: cover; -} - -.plugin-info { - display: flex; - flex-direction: column; - flex: 1; - overflow: hidden; -} - -.plugin-title-row { - margin-bottom: 4px; - gap: 8px; -} - -.market-recommended-chip { - flex-shrink: 0; - font-weight: bold; - height: 20px; -} - -.market-unsupported-chip { - flex-shrink: 0; - font-weight: 700; - height: 20px; -} - -.plugin-title { - line-height: 1.3; - font-size: 1rem; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.plugin-meta { - gap: 4px; - margin-bottom: 6px; - flex-wrap: nowrap; -} - -.market-stat { - color: rgba(var(--v-theme-on-surface), 0.7); - flex-shrink: 0; -} - -.plugin-description { - color: rgba(var(--v-theme-on-surface), 0.6); - line-height: 1.3; - margin-bottom: 6px; - flex: 1; - overflow: hidden; - display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - min-height: calc(1.3em * 2); - max-height: calc(1.3em * 2); -} - -.plugin-badges { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 4px; - margin-top: 4px; - margin-bottom: 4px; -} - -.plugin-stats { - display: flex; - align-items: center; - gap: 8px; - margin-top: auto; -} - -.plugin-description::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -.plugin-description::-webkit-scrollbar-track { - background: transparent; -} - -.plugin-description::-webkit-scrollbar-thumb { - background-color: rgba(var(--v-theme-primary-rgb), 0.4); - border-radius: 4px; - border: 2px solid transparent; - background-clip: content-box; -} - -.plugin-description::-webkit-scrollbar-thumb:hover { - background-color: rgba(var(--v-theme-primary-rgb), 0.6); -} - -.market-action-btn { - font-size: 0.9rem; - font-weight: 600; -} -</style> diff --git a/legacy-dashboard/src/components/extension/McpServersSection.vue b/legacy-dashboard/src/components/extension/McpServersSection.vue deleted file mode 100644 index 3b35e21e..00000000 --- a/legacy-dashboard/src/components/extension/McpServersSection.vue +++ /dev/null @@ -1,723 +0,0 @@ -<template> - <div class="tools-page"> - <v-container fluid class="pa-0" elevation="0"> - <!-- MCP 服务器部分 --> - <div v-if="mcpServers.length === 0" class="text-center pa-8"> - <v-icon size="64" color="grey-lighten-1">mdi-server-off</v-icon> - <p class="text-grey mt-4">{{ tm('mcpServers.empty') }}</p> - </div> - - <div v-else class="mcp-server-list"> - <OutlinedActionListItem - v-for="server in mcpServers || []" - :key="server.name" - :title="server.name" - clickable - @click="editServer(server)" - > - <div - class="mcp-server-config text-body-2 text-medium-emphasis" - :title="getServerConfigSummary(server)" - > - <v-icon - :icon="getServerConfigIcon(server)" - size="small" - class="me-1" - /> - <span>{{ getServerConfigSummary(server) }}</span> - </div> - - <div class="mcp-server-tools text-caption text-medium-emphasis"> - <template v-if="server.tools && server.tools.length > 0"> - <v-dialog max-width="600px"> - <template v-slot:activator="{ props: listToolsProps }"> - <button - v-bind="listToolsProps" - class="mcp-server-tools__button" - type="button" - @click.stop - > - <v-icon size="small" class="me-1">mdi-tools</v-icon> - {{ - tm('mcpServers.status.availableTools', { - count: server.tools.length, - }) - }} - ({{ server.tools.length }}) - </button> - </template> - <template v-slot:default="{ isActive }"> - <v-card style="padding: 16px;"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - <span>{{ tm('mcpServers.status.availableTools') }}</span> - </v-card-title> - <v-card-text> - <ul> - <li - v-for="(tool, idx) in server.tools" - :key="idx" - style="margin: 8px 0px;" - > - {{ tool }} - </li> - </ul> - </v-card-text> - <v-card-actions class="d-flex justify-end"> - <v-btn - variant="text" - color="primary" - @click="isActive.value = false" - > - Close - </v-btn> - </v-card-actions> - </v-card> - </template> - </v-dialog> - </template> - <template v-else> - <v-icon size="small" color="warning" class="me-1"> - mdi-alert-circle - </v-icon> - {{ tm('mcpServers.status.noTools') }} - </template> - </div> - - <template #actions> - <v-tooltip :text="t('core.common.itemCard.delete')" location="top"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - icon="mdi-delete-outline" - variant="text" - size="small" - class="list-action-icon-btn" - @click.stop="deleteServer(server)" - /> - </template> - </v-tooltip> - </template> - - <template #control> - <v-progress-circular - v-if="mcpServerUpdateLoaders[server.name]" - indeterminate - color="primary" - size="18" - /> - - <v-tooltip location="top"> - <template #activator="{ props }"> - <v-switch - v-bind="props" - color="primary" - density="compact" - hide-details - inset - :model-value="server.active" - :loading="mcpServerUpdateLoaders[server.name] || false" - :disabled="mcpServerUpdateLoaders[server.name] || false" - @click.stop - @update:model-value="updateServerStatus(server)" - /> - </template> - <span>{{ - server.active - ? t('core.common.itemCard.enabled') - : t('core.common.itemCard.disabled') - }}</span> - </v-tooltip> - </template> - </OutlinedActionListItem> - </div> - </v-container> - - <div class="mcp-fab-stack"> - <v-tooltip :text="tm('mcpServers.buttons.sync')" location="left"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - color="darkprimary" - icon="mdi-sync" - size="x-large" - variant="elevated" - class="mcp-fab" - @click="showSyncMcpServerDialog = true" - /> - </template> - </v-tooltip> - <v-tooltip :text="tm('mcpServers.buttons.add')" location="left"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - color="darkprimary" - icon="mdi-plus" - size="x-large" - variant="elevated" - class="mcp-fab" - @click="showMcpServerDialog = true" - /> - </template> - </v-tooltip> - </div> - - <!-- 添加/编辑 MCP 服务器对话框 --> - <v-dialog v-model="showMcpServerDialog" max-width="750px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - <v-icon class="me-2">{{ isEditMode ? 'mdi-pencil' : 'mdi-plus' }}</v-icon> - <span>{{ isEditMode ? tm('dialogs.addServer.editTitle') : tm('dialogs.addServer.title') }}</span> - </v-card-title> - - <v-card-text class="py-4"> - <v-form @submit.prevent="saveServer" ref="form"> - <v-text-field v-model="currentServer.name" :label="tm('dialogs.addServer.fields.name')" variant="outlined" - :rules="[v => !!v || tm('dialogs.addServer.fields.nameRequired')]" required class="mb-3"></v-text-field> - - <div class="mb-2 d-flex align-center"> - <span class="text-subtitle-1">{{ tm('dialogs.addServer.fields.config') }}</span> - <v-spacer></v-spacer> - <v-btn size="small" color="primary" variant="tonal" @click="setConfigTemplate('stdio')" class="me-1"> - {{ tm('mcpServers.buttons.useTemplateStdio') }} - </v-btn> - <v-btn size="small" color="primary" variant="tonal" @click="setConfigTemplate('streamable_http')" - class="me-1"> - {{ tm('mcpServers.buttons.useTemplateStreamableHttp') }} - </v-btn> - <v-btn size="small" color="primary" variant="tonal" @click="setConfigTemplate('sse')" class="me-1"> - {{ tm('mcpServers.buttons.useTemplateSse') }} - </v-btn> - </div> - - <small style="color: grey">*{{ tm('dialogs.addServer.tips.timeoutConfig') }}</small> - - <v-alert type="info" variant="tonal" density="compact" class="mt-3"> - {{ tm('dialogs.addServer.tips.transportRecommendation') }} - </v-alert> - - <div class="monaco-container" style="margin-top: 16px;"> - <VueMonacoEditor v-model:value="serverConfigJson" theme="vs-dark" language="json" :options="{ - minimap: { - enabled: false - }, - scrollBeyondLastLine: false, - automaticLayout: true, - lineNumbers: 'on', - roundedSelection: true, - tabSize: 2 - }" @change="validateJson" /> - </div> - - <div v-if="jsonError" class="mt-2 text-error"> - <v-icon color="error" size="small" class="me-1">mdi-alert-circle</v-icon> - <span>{{ jsonError }}</span> - </div> - - </v-form> - <div style="margin-top: 8px;"> - <small>{{ addServerDialogMessage }}</small> - </div> - - </v-card-text> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="closeServerDialog" :disabled="loading"> - {{ tm('dialogs.addServer.buttons.cancel') }} - </v-btn> - <v-btn variant="text" @click="testServerConnection" :disabled="loading"> - {{ tm('dialogs.addServer.buttons.testConnection') }} - </v-btn> - <v-btn color="primary" variant="tonal" @click="saveServer" :loading="loading" :disabled="!isServerFormValid"> - {{ tm('dialogs.addServer.buttons.save') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <!-- 同步 MCP 服务器对话框 --> - <v-dialog v-model="showSyncMcpServerDialog" max-width="500px" persistent> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - <span>同步外部平台 MCP 服务器</span> - </v-card-title> - - <v-card-text class="py-4"> - <v-select v-model="selectedMcpServerProvider" :items="mcpServerProviderList" - label="选择平台" variant="outlined" required></v-select> - <div v-if="selectedMcpServerProvider === 'modelscope'"> - <v-timeline align="start" side="end"> - <v-timeline-item icon="mdi-numeric-1" icon-color="rgb(var(--v-theme-background))"> - <div> - <div class="text-h4">发现 MCP 服务器</div> - <p class="mt-2"> - 访问 <a href="https://www.modelscope.cn/mcp" target="_blank">ModelScope 平台</a> 浏览需要的 MCP 服务器。 - </p> - </div> - </v-timeline-item> - - <v-timeline-item icon="mdi-numeric-2" icon-color="rgb(var(--v-theme-background))"> - <div> - <div class="text-h4">获取访问令牌</div> - <p class="mt-2"> - 从<a href="https://modelscope.cn/my/myaccesstoken" target="_blank">账户设置</a>中获取个人访问令牌。 - </p> - </div> - </v-timeline-item> - - <v-timeline-item icon="mdi-numeric-3" icon-color="rgb(var(--v-theme-background))"> - <div> - <div class="text-h4">输入您的访问令牌</div> - <p class="mt-2"> - 输入您的访问令牌以同步 MCP 服务器。 - </p> - <v-text-field v-model="mcpProviderToken" type="password" variant="outlined" - label="访问令牌" class="mt-2" hide-details/> - </div> - </v-timeline-item> - </v-timeline> - </div> - </v-card-text> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="showSyncMcpServerDialog = false" :disabled="loading"> - {{ tm('dialogs.addServer.buttons.cancel') }} - </v-btn> - <v-btn color="primary" variant="tonal" @click="syncMcpServers" :loading="loading" :disabled="loading"> - {{ tm('dialogs.addServer.buttons.sync') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <!-- 消息提示 --> - <v-snackbar :timeout="3000" elevation="6" :color="save_message_success" v-model="save_message_snack" location="top"> - {{ save_message }} - </v-snackbar> - </div> -</template> - -<script> -import { VueMonacoEditor } from '@guolao/vue-monaco-editor'; -import { mcpApi } from '@/api/v1'; -import { useI18n, useModuleI18n } from '@/i18n/composables'; -import OutlinedActionListItem from '@/components/shared/OutlinedActionListItem.vue'; -import { - askForConfirmation as askForConfirmationDialog, - useConfirmDialog -} from '@/utils/confirmDialog'; - -export default { - name: 'McpServersSection', - components: { - VueMonacoEditor, - OutlinedActionListItem - }, - setup() { - const { t } = useI18n(); - const { tm } = useModuleI18n('features/tooluse'); - const confirmDialog = useConfirmDialog(); - return { t, tm, confirmDialog }; - }, - data() { - return { - refreshInterval: null, - mcpServers: [], - showMcpServerDialog: false, - selectedMcpServerProvider: 'modelscope', - mcpServerProviderList: ['modelscope'], - mcpProviderToken: '', - showSyncMcpServerDialog: false, - addServerDialogMessage: '', - loading: false, - loadingGettingServers: false, - mcpServerUpdateLoaders: {}, - isEditMode: false, - serverConfigJson: '', - jsonError: null, - currentServer: { - name: '', - active: true, - tools: [] - }, - originalServerName: '', - save_message_snack: false, - save_message: '', - save_message_success: 'success' - }; - }, - computed: { - isServerFormValid() { - return !!this.currentServer.name && !this.jsonError; - }, - getServerConfigSummary() { - return (server) => { - if (server.transport) { - return String(server.transport).trim(); - } - if (server.command) { - return `${server.command} ${(server.args || []).join(' ')}`; - } - const configKeys = Object.keys(server).filter(key => - !['name', 'active', 'tools'].includes(key) - ); - if (configKeys.length > 0) { - return this.tm('mcpServers.status.configSummary', { keys: configKeys.join(', ') }); - } - return this.tm('mcpServers.status.noConfig'); - }; - }, - getServerConfigIcon() { - return (server) => { - const transport = String(server.transport || '').toLowerCase(); - if (transport === 'streamable_http') { - return 'mdi-web'; - } - if (transport === 'sse') { - return 'mdi-broadcast'; - } - if (server.command) { - return 'mdi-console-line'; - } - return 'mdi-file-code-outline'; - }; - } - }, - mounted() { - this.getServers(); - this.refreshInterval = setInterval(() => { - // 轮询时间延长到30秒,减少服务器压力,同时保持数据相对新鲜 - this.getServers(); - }, 30000); - }, - unmounted() { - if (this.refreshInterval) { - clearInterval(this.refreshInterval); - } - }, - methods: { - openurl(url) { - window.open(url, '_blank'); - }, - getServers() { - this.loadingGettingServers = true; - mcpApi.list() - .then(response => { - if (response.data.status === 'error') { - this.showError(response.data.message || this.tm('messages.getServersError', { error: 'Unknown error' })); - return; - } - this.mcpServers = response.data.data || []; - this.mcpServers.forEach(server => { - if (!this.mcpServerUpdateLoaders[server.name]) { - this.mcpServerUpdateLoaders[server.name] = false; - } - }); - }) - .catch(error => { - this.showError(this.tm('messages.getServersError', { error: error.message })); - }).finally(() => { - this.loadingGettingServers = false; - }); - }, - validateJson() { - try { - if (!this.serverConfigJson.trim()) { - this.jsonError = this.tm('dialogs.addServer.errors.configEmpty'); - return false; - } - JSON.parse(this.serverConfigJson); - this.jsonError = null; - return true; - } catch (e) { - this.jsonError = this.tm('dialogs.addServer.errors.jsonFormat', { error: e.message }); - return false; - } - }, - setConfigTemplate(type = 'stdio') { - let template = {}; - if (type === 'streamable_http') { - template = { - transport: 'streamable_http', - url: 'your mcp server url', - headers: {}, - timeout: 5, - sse_read_timeout: 300 - }; - } else if (type === 'sse') { - template = { - transport: 'sse', - url: 'your mcp server url', - headers: {}, - timeout: 5, - sse_read_timeout: 300 - }; - } else { - template = { - command: 'python', - args: ['-m', 'your_module'] - }; - } - this.serverConfigJson = JSON.stringify(template, null, 2); - }, - saveServer() { - if (!this.validateJson()) { - return; - } - this.loading = true; - try { - const configObj = JSON.parse(this.serverConfigJson); - const serverData = { - name: this.currentServer.name, - active: this.currentServer.active, - ...configObj - }; - if (this.isEditMode && this.originalServerName) { - serverData.oldName = this.originalServerName; - } - const request = this.isEditMode - ? mcpApi.update(this.originalServerName || serverData.name, serverData) - : mcpApi.create(serverData); - request - .then(response => { - this.loading = false; - if (response.data.status === 'error') { - this.showError(response.data.message || this.tm('messages.saveError', { error: 'Unknown error' })); - return; - } - this.showMcpServerDialog = false; - this.addServerDialogMessage = ''; - this.getServers(); - this.showSuccess(response.data.message || this.tm('messages.saveSuccess')); - this.resetForm(); - }) - .catch(error => { - this.loading = false; - this.showError(this.tm('messages.saveError', { error: error.response?.data?.message || error.message })); - }); - } catch (e) { - this.loading = false; - this.showError(this.tm('dialogs.addServer.errors.jsonParse', { error: e.message })); - } - }, - async deleteServer(server) { - const serverName = server.name || server; - const message = this.tm('dialogs.confirmDelete', { name: serverName }); - if (!(await askForConfirmationDialog(message, this.confirmDialog))) { - return; - } - - mcpApi.delete(serverName) - .then(response => { - this.getServers(); - this.showSuccess(response.data.message || this.tm('messages.deleteSuccess')); - }) - .catch(error => { - this.showError(this.tm('messages.deleteError', { error: error.response?.data?.message || error.message })); - }); - }, - editServer(server) { - const configCopy = { ...server }; - delete configCopy.name; - delete configCopy.active; - delete configCopy.tools; - delete configCopy.errlogs; - this.currentServer = { - name: server.name, - active: server.active, - tools: server.tools || [] - }; - this.originalServerName = server.name; - this.serverConfigJson = JSON.stringify(configCopy, null, 2); - this.isEditMode = true; - this.showMcpServerDialog = true; - }, - updateServerStatus(server) { - this.mcpServerUpdateLoaders[server.name] = true; - server.active = !server.active; - mcpApi.setEnabled(server.name, server.active) - .then(response => { - this.getServers(); - this.showSuccess(response.data.message || this.tm('messages.updateSuccess')); - }) - .catch(error => { - this.showError(this.tm('messages.updateError', { error: error.response?.data?.message || error.message })); - server.active = !server.active; - }) - .finally(() => { - this.mcpServerUpdateLoaders[server.name] = false; - }); - }, - closeServerDialog() { - this.showMcpServerDialog = false; - this.addServerDialogMessage = ''; - this.resetForm(); - }, - testServerConnection() { - if (!this.validateJson()) { - return; - } - this.loading = true; - let configObj; - try { - configObj = JSON.parse(this.serverConfigJson); - } catch (e) { - this.loading = false; - this.showError(this.tm('dialogs.addServer.errors.jsonParse', { error: e.message })); - return; - } - mcpApi.test(this.currentServer.name || 'draft', configObj) - .then(response => { - this.loading = false; - this.addServerDialogMessage = `${response.data.message} (tools: ${response.data.data})`; - }) - .catch(error => { - this.loading = false; - this.showError(this.tm('messages.testError', { error: error.response?.data?.message || error.message })); - }); - }, - resetForm() { - this.currentServer = { - name: '', - active: true, - tools: [] - }; - this.serverConfigJson = ''; - this.jsonError = null; - this.isEditMode = false; - this.originalServerName = ''; - }, - showSuccess(message) { - this.save_message = message; - this.save_message_success = 'success'; - this.save_message_snack = true; - }, - showError(message) { - this.save_message = message; - this.save_message_success = 'error'; - this.save_message_snack = true; - }, - async syncMcpServers() { - if (!this.selectedMcpServerProvider) { - this.showError(this.tm('syncProvider.status.selectProvider')); - return; - } - this.loading = true; - try { - const requestData = { - name: this.selectedMcpServerProvider - }; - if (this.selectedMcpServerProvider === 'modelscope') { - if (!this.mcpProviderToken.trim()) { - this.showError(this.tm('syncProvider.status.enterToken')); - this.loading = false; - return; - } - requestData.access_token = this.mcpProviderToken.trim(); - } - const response = await mcpApi.syncModelScope({ - access_token: requestData.access_token || '' - }); - if (response.data.status === 'ok') { - this.showSuccess(response.data.message || this.tm('syncProvider.messages.syncSuccess')); - this.showSyncMcpServerDialog = false; - this.mcpProviderToken = ''; - this.getServers(); - } else { - this.showError(response.data.message || this.tm('syncProvider.messages.syncError', { error: 'Unknown error' })); - } - } catch (error) { - this.showError(this.tm('syncProvider.messages.syncError', { - error: error.response?.data?.message || error.message || '网络连接或访问令牌问题' - })); - } finally { - this.loading = false; - } - } - } -}; -</script> - -<style scoped> -.tools-page { - padding: 0px; - padding-top: 8px; -} - -.mcp-server-list { - display: flex; - flex-direction: column; - gap: 12px; -} - -.mcp-server-config { - align-items: center; - display: flex; - overflow: hidden; - word-break: break-all; -} - -.mcp-server-config span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.mcp-server-tools { - align-items: center; - display: flex; - margin-top: 6px; -} - -.mcp-server-tools__button { - align-items: center; - color: rgba(var(--v-theme-on-surface), 0.62); - display: inline-flex; - text-decoration: underline; -} - -.mcp-server-tools__button:hover { - color: rgb(var(--v-theme-primary)); -} - -.list-action-icon-btn { - color: rgba(var(--v-theme-on-surface), 0.78); -} - -.list-action-icon-btn:hover { - background: rgba(var(--v-theme-on-surface), 0.08); - color: rgb(var(--v-theme-on-surface)); -} - -.mcp-fab-stack { - align-items: center; - bottom: 52px; - display: flex; - flex-direction: column; - gap: 16px; - position: fixed; - right: 52px; - z-index: 10000; -} - -.mcp-fab { - border-radius: 16px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); -} - -.mcp-fab:hover { - box-shadow: 0 12px 20px rgba(var(--v-theme-primary), 0.4); - transform: translateY(-4px) scale(1.05); -} - -.monaco-container { - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 8px; - height: 300px; - margin-top: 4px; - overflow: hidden; -} - -</style> diff --git a/legacy-dashboard/src/components/extension/PluginSortControl.vue b/legacy-dashboard/src/components/extension/PluginSortControl.vue deleted file mode 100644 index 4a14a6bb..00000000 --- a/legacy-dashboard/src/components/extension/PluginSortControl.vue +++ /dev/null @@ -1,98 +0,0 @@ -<script setup> -const props = defineProps({ - modelValue: { - type: String, - required: true, - }, - items: { - type: Array, - required: true, - }, - label: { - type: String, - required: true, - }, - order: { - type: String, - default: "desc", - }, - ascendingLabel: { - type: String, - default: "Ascending", - }, - descendingLabel: { - type: String, - default: "Descending", - }, - showOrder: { - type: Boolean, - default: false, - }, -}); - -const emit = defineEmits(["update:modelValue", "update:order"]); - -const updateSortBy = (value) => { - emit("update:modelValue", value); -}; - -const toggleOrder = () => { - emit("update:order", props.order === "desc" ? "asc" : "desc"); -}; -</script> - -<template> - <div class="plugin-sort-control"> - <v-select - :model-value="modelValue" - :items="items" - density="compact" - variant="outlined" - hide-details - :label="label" - class="plugin-sort-control__select" - @update:model-value="updateSortBy" - > - <template #prepend-inner> - <v-icon size="small">mdi-sort</v-icon> - </template> - </v-select> - - <v-btn - v-if="showOrder" - icon - variant="text" - density="compact" - @click="toggleOrder" - > - <v-icon>{{ - order === "desc" ? "mdi-arrow-down-thin" : "mdi-arrow-up-thin" - }}</v-icon> - <v-tooltip activator="parent" location="top"> - {{ order === "desc" ? descendingLabel : ascendingLabel }} - </v-tooltip> - </v-btn> - </div> -</template> - -<style scoped> -.plugin-sort-control { - display: flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -.plugin-sort-control__select { - min-width: 180px; - width: 190px; - max-width: 220px; -} - -.plugin-sort-control__select :deep(.v-field__input), -.plugin-sort-control__select :deep(.v-field-label), -.plugin-sort-control__select :deep(.v-select__selection-text), -.plugin-sort-control__select :deep(.v-field__prepend-inner) { - font-size: 0.875rem; -} -</style> diff --git a/legacy-dashboard/src/components/extension/SkillsSection.vue b/legacy-dashboard/src/components/extension/SkillsSection.vue deleted file mode 100644 index 926e1173..00000000 --- a/legacy-dashboard/src/components/extension/SkillsSection.vue +++ /dev/null @@ -1,2349 +0,0 @@ -<template> - <div class="skills-page"> - <v-container fluid class="pa-0" elevation="0"> - <v-row - v-if="neoEnabled" - class="d-flex justify-end align-center px-4 py-3 pb-4" - > - <v-btn-toggle - v-model="mode" - mandatory - divided - density="comfortable" - > - <v-btn value="local">{{ tm("skills.modeLocal") }}</v-btn> - <v-btn value="neo">{{ tm("skills.modeNeo") }}</v-btn> - </v-btn-toggle> - </v-row> - - <div v-if="mode === 'local'" class="px-2 pb-2 d-flex flex-column ga-2"> - <v-alert - v-if="runtime === 'sandbox' && !sandboxCache.ready" - type="info" - variant="tonal" - density="comfortable" - border="start" - > - {{ tm("skills.sandboxDiscoveryPending") }} - </v-alert> - </div> - - <div v-if="mode === 'neo' && !neoEnabled" class="px-3 pb-3"> - <v-alert - type="warning" - variant="tonal" - density="comfortable" - border="start" - > - {{ neoUnavailableMessage }} - </v-alert> - </div> - - <template v-if="mode === 'local'"> - <v-progress-linear - v-if="loading" - indeterminate - color="primary" - ></v-progress-linear> - - <div v-else-if="skills.length === 0" class="text-center pa-8"> - <v-icon size="64" color="grey-lighten-1">mdi-folder-open</v-icon> - <p class="text-grey mt-4">{{ tm("skills.empty") }}</p> - <small class="text-grey">{{ tm("skills.emptyHint") }}</small> - </div> - - <div v-else class="skills-list pb-3"> - <OutlinedActionListItem - v-for="skill in skills" - :key="skill.name" - :title="skill.name" - clickable - @click="openSkillEditor(skill)" - > - <template #title-extra> - <v-chip - size="x-small" - variant="tonal" - :color="sourceTypeColor(skill.source_type)" - > - {{ sourceTypeLabel(skill.source_type, skill) }} - </v-chip> - </template> - - <div class="skill-description text-body-2 text-medium-emphasis"> - {{ skill.description || tm("skills.noDescription") }} - </div> - - <div class="skill-path text-caption text-medium-emphasis"> - <v-icon size="small" class="me-1">mdi-file-document</v-icon> - {{ tm("skills.path") }}: {{ skill.path }} - </div> - - <template #actions> - <v-tooltip :text="tm('skills.download')" location="top"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - icon="mdi-download-outline" - variant="text" - size="small" - class="list-action-icon-btn" - :disabled="itemLoading[skill.name] || isReadOnlySourceSkill(skill)" - @click.stop="downloadSkill(skill)" - /> - </template> - </v-tooltip> - - <v-tooltip :text="t('core.common.itemCard.delete')" location="top"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - icon="mdi-delete-outline" - variant="text" - size="small" - class="list-action-icon-btn" - :disabled="itemLoading[skill.name] || isReadOnlySourceSkill(skill)" - @click.stop="confirmDelete(skill)" - /> - </template> - </v-tooltip> - </template> - - <template #control> - <v-tooltip location="top"> - <template #activator="{ props }"> - <v-switch - v-bind="props" - color="primary" - density="compact" - hide-details - inset - :model-value="skill.active" - :loading="itemLoading[skill.name] || false" - :disabled="itemLoading[skill.name] || isSandboxPresetSkill(skill)" - @click.stop - @update:model-value="toggleSkill(skill)" - /> - </template> - <span>{{ - skill.active - ? t("core.common.itemCard.enabled") - : t("core.common.itemCard.disabled") - }}</span> - </v-tooltip> - </template> - </OutlinedActionListItem> - </div> - </template> - - <template v-else-if="mode === 'neo' && neoEnabled"> - <v-card class="mx-3 mb-4 pa-4 neo-filter-card" variant="outlined"> - <div - class="d-flex flex-wrap justify-space-between align-center ga-2 mb-3" - > - <div> - <div class="text-subtitle-1 font-weight-bold">Neo Skills</div> - <div class="text-caption text-medium-emphasis"> - {{ tm("skills.neoFilterHint") }} - </div> - </div> - </div> - - <v-row class="ga-md-0 ga-2"> - <v-col cols="12" md="4"> - <v-text-field - v-model="neoFilters.skill_key" - :label="tm('skills.neoSkillKey')" - prepend-inner-icon="mdi-key-outline" - density="comfortable" - hide-details - variant="outlined" - /> - </v-col> - <v-col cols="12" md="4"> - <v-select - v-model="neoFilters.status" - :label="tm('skills.neoStatus')" - :items="candidateStatusItems" - item-title="title" - item-value="value" - prepend-inner-icon="mdi-progress-check" - density="comfortable" - hide-details - variant="outlined" - /> - </v-col> - <v-col cols="12" md="4"> - <v-select - v-model="neoFilters.stage" - :label="tm('skills.neoStage')" - :items="releaseStageItems" - item-title="title" - item-value="value" - prepend-inner-icon="mdi-layers-outline" - density="comfortable" - hide-details - variant="outlined" - /> - </v-col> - </v-row> - </v-card> - - <v-progress-linear - v-if="neoLoading" - indeterminate - color="primary" - ></v-progress-linear> - - <div class="mx-3 mb-3 d-flex flex-wrap ga-2"> - <v-chip size="small" color="primary" variant="tonal" - >Candidates: {{ neoCandidates.length }}</v-chip - > - <v-chip size="small" color="indigo" variant="tonal" - >Releases: {{ neoReleases.length }}</v-chip - > - <v-chip size="small" color="success" variant="tonal" - >Active: {{ activeReleaseCount }}</v-chip - > - </div> - - <v-card class="mx-3 mb-4 neo-table-card" variant="outlined"> - <v-card-title class="text-subtitle-1 font-weight-bold">{{ - tm("skills.neoCandidates") - }}</v-card-title> - <v-data-table - :headers="candidateHeaders" - :items="neoCandidates" - density="compact" - :items-per-page="10" - class="neo-data-table" - > - <template #item.latest_score="{ item }"> - {{ item.latest_score ?? "-" }} - </template> - <template #item.actions="{ item }"> - <div class="d-flex ga-1 flex-wrap"> - <v-btn - size="x-small" - color="success" - variant="tonal" - @click="evaluateCandidate(item, true)" - > - {{ tm("skills.neoPass") }} - </v-btn> - <v-btn - size="x-small" - color="warning" - variant="tonal" - @click="evaluateCandidate(item, false)" - > - {{ tm("skills.neoReject") }} - </v-btn> - <v-btn - size="x-small" - color="primary" - variant="tonal" - :loading="isCandidatePromoteLoading(item.id, 'canary')" - :disabled="isCandidatePromoting(item.id)" - @click="promoteCandidate(item, 'canary')" - > - Canary - </v-btn> - <v-btn - size="x-small" - color="primary" - variant="tonal" - :loading="isCandidatePromoteLoading(item.id, 'stable')" - :disabled="isCandidatePromoting(item.id)" - @click="promoteCandidate(item, 'stable')" - > - Stable - </v-btn> - <v-btn - size="x-small" - variant="tonal" - :disabled="!item.payload_ref" - @click="viewPayload(item.payload_ref)" - > - Payload - </v-btn> - <v-btn - size="x-small" - color="error" - variant="tonal" - @click="deleteCandidate(item)" - > - {{ tm("skills.neoDelete") }} - </v-btn> - </div> - </template> - </v-data-table> - </v-card> - - <v-card class="mx-3 mb-4 neo-table-card" variant="outlined"> - <v-card-title class="text-subtitle-1 font-weight-bold">{{ - tm("skills.neoReleases") - }}</v-card-title> - <v-data-table - :headers="releaseHeaders" - :items="neoReleases" - density="compact" - :items-per-page="10" - class="neo-data-table" - > - <template #item.is_active="{ item }"> - <v-chip - size="small" - :color="item.is_active ? 'success' : 'default'" - variant="tonal" - > - {{ item.is_active ? "active" : "inactive" }} - </v-chip> - </template> - <template #item.actions="{ item }"> - <div class="d-flex ga-1 flex-wrap"> - <v-btn - size="x-small" - color="warning" - variant="tonal" - @click="handleReleaseLifecycleAction(item)" - > - {{ - item.is_active - ? tm("skills.neoDeactivate") - : tm("skills.neoRollback") - }} - </v-btn> - <v-btn - size="x-small" - color="primary" - variant="tonal" - @click="syncRelease(item)" - > - {{ tm("skills.neoSync") }} - </v-btn> - <v-btn - size="x-small" - color="error" - variant="tonal" - @click="deleteRelease(item)" - > - {{ tm("skills.neoDelete") }} - </v-btn> - </div> - </template> - </v-data-table> - </v-card> - </template> - </v-container> - - <div class="skills-fab-stack"> - <v-tooltip :text="tm('skills.refresh')" location="left"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - color="darkprimary" - icon="mdi-refresh" - size="x-large" - variant="elevated" - class="skills-fab" - @click="refreshCurrentMode" - /> - </template> - </v-tooltip> - <v-tooltip - v-if="mode === 'local'" - :text="tm('skills.upload')" - location="left" - > - <template #activator="{ props }"> - <v-btn - v-bind="props" - color="darkprimary" - icon="mdi-upload" - size="x-large" - variant="elevated" - class="skills-fab" - @click="openUploadDialog" - /> - </template> - </v-tooltip> - </div> - - <v-dialog v-model="uploadDialog" max-width="880px" :persistent="uploading"> - <v-card class="skills-upload-dialog"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 skills-upload-dialog__header"> - <div class="skills-upload-dialog__heading"> - <div> - {{ tm("skills.uploadDialogTitle") }} - </div> - </div> - <v-btn - class="skills-upload-dialog__close" - icon="mdi-close" - variant="text" - :disabled="uploading" - @click="closeUploadDialog" - /> - </v-card-title> - - <v-card-text class="skills-upload-dialog__body px-6 pb-5 pt-2"> - <p - class="skills-upload-dialog__description skills-upload-dialog__description--body" - > - {{ tm("skills.uploadHint") }} - </p> - - <div class="skills-upload-structure-note"> - <v-icon size="18">mdi-information-outline</v-icon> - <span>{{ tm("skills.structureRequirement") }}</span> - </div> - - <div class="skills-upload-capabilities"> - <div class="skills-upload-capability"> - <div class="skills-upload-capability__icon"> - <v-icon size="18">mdi-layers-outline</v-icon> - </div> - <span>{{ tm("skills.abilityMultiple") }}</span> - </div> - <div class="skills-upload-capability"> - <div class="skills-upload-capability__icon"> - <v-icon size="18">mdi-shield-check-outline</v-icon> - </div> - <span>{{ tm("skills.abilityValidate") }}</span> - </div> - <div class="skills-upload-capability"> - <div class="skills-upload-capability__icon"> - <v-icon size="18">mdi-skip-next-circle-outline</v-icon> - </div> - <span>{{ tm("skills.abilitySkip") }}</span> - </div> - </div> - - <div - class="skills-dropzone" - :class="{ 'skills-dropzone--dragover': isUploadDragging }" - role="button" - tabindex="0" - :aria-label="tm('skills.dropzoneTitle')" - @click="openUploadPicker" - @keydown.enter="openUploadPicker" - @keydown.space.prevent="openUploadPicker" - @dragover.prevent="isUploadDragging = true" - @dragleave.prevent="isUploadDragging = false" - @drop.prevent="handleUploadDrop" - > - <div class="skills-dropzone__icon"> - <v-icon size="34">mdi-folder-zip-outline</v-icon> - </div> - <div class="text-h6 font-weight-medium"> - {{ tm("skills.dropzoneTitle") }} - </div> - <div class="skills-dropzone__subtitle"> - {{ tm("skills.dropzoneAction") }} - </div> - <div class="skills-dropzone__hint"> - {{ tm("skills.dropzoneHint") }} - </div> - <input - ref="uploadInput" - type="file" - multiple - hidden - accept=".zip" - @change="handleUploadSelection" - /> - </div> - - <div v-if="uploadItems.length > 0" class="skills-upload-summary"> - <v-chip - size="small" - variant="flat" - class="skills-upload-summary__chip" - > - {{ - tm("skills.summaryTotal", { count: uploadStateCounts.total }) - }} - </v-chip> - <v-chip - size="small" - variant="flat" - class="skills-upload-summary__chip" - > - {{ - tm("skills.summaryReady", { - count: - uploadStateCounts.waiting + uploadStateCounts.uploading, - }) - }} - </v-chip> - <v-chip - size="small" - variant="flat" - class="skills-upload-summary__chip skills-upload-summary__chip--success" - > - {{ - tm("skills.summarySuccess", { - count: uploadStateCounts.success, - }) - }} - </v-chip> - <v-chip - size="small" - variant="flat" - class="skills-upload-summary__chip skills-upload-summary__chip--error" - > - {{ - tm("skills.summaryFailed", { count: uploadStateCounts.error }) - }} - </v-chip> - <v-chip - size="small" - variant="flat" - class="skills-upload-summary__chip" - > - {{ - tm("skills.summarySkipped", { - count: uploadStateCounts.skipped, - }) - }} - </v-chip> - </div> - - <div v-if="uploadItems.length > 0" class="skills-upload-list"> - <div class="skills-upload-list__header"> - <span>{{ tm("skills.fileListTitle") }}</span> - </div> - <div - v-for="item in uploadItems" - :key="item.id" - class="skills-upload-row" - > - <div class="skills-upload-row__meta"> - <div class="skills-upload-row__name">{{ item.name }}</div> - <div class="skills-upload-row__size"> - {{ formatFileSize(item.size) }} - </div> - <div class="skills-upload-row__message"> - {{ item.validationMessage }} - </div> - </div> - <div class="skills-upload-row__actions"> - <v-chip - size="small" - variant="flat" - :class="statusChipClass(item.status)" - > - {{ uploadStatusLabel(item.status) }} - </v-chip> - <v-btn - icon="mdi-close" - size="small" - variant="text" - :disabled="uploading || item.status === 'uploading'" - @click="removeUploadItem(item.id)" - /> - </div> - </div> - </div> - <div v-else class="skills-upload-empty"> - {{ tm("skills.fileListEmpty") }} - </div> - </v-card-text> - - <v-card-actions - class="skills-upload-dialog__actions justify-end px-6 pb-3 pt-2" - > - <v-btn - class="skills-upload-dialog__action-btn" - variant="tonal" - color="secondary" - :disabled="uploading" - @click="closeUploadDialog" - > - {{ tm("skills.cancel") }} - </v-btn> - <v-btn - class="skills-upload-dialog__action-btn" - variant="tonal" - color="primary" - :loading="uploading" - :disabled="!hasUploadableItems" - @click="uploadSkillBatch" - > - {{ tm("skills.confirmUpload") }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-dialog v-model="deleteDialog" max-width="400px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm("skills.deleteTitle") }}</v-card-title> - <v-card-text>{{ tm("skills.deleteMessage") }}</v-card-text> - <v-card-actions class="d-flex justify-end"> - <v-btn variant="text" @click="deleteDialog = false">{{ - tm("skills.cancel") - }}</v-btn> - <v-btn color="error" variant="tonal" :loading="deleting" @click="deleteSkill"> - {{ t("core.common.itemCard.delete") }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-dialog - v-model="editorDialog.show" - max-width="1180px" - :persistent="editorDialog.saving" - > - <v-card class="skill-editor-dialog"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 skill-editor-dialog__header"> - <div> - <div> - {{ editorDialog.skillName }} - </div> - </div> - <v-btn - icon="mdi-close" - variant="text" - :disabled="editorDialog.saving" - @click="closeSkillEditor" - /> - </v-card-title> - - <v-card-text class="skill-editor-dialog__body"> - <div class="skill-editor"> - <div class="skill-editor__files"> - <div class="skill-editor__files-header"> - <v-btn - icon="mdi-arrow-up" - size="small" - variant="text" - :disabled="!editorDialog.currentDir || editorDialog.loadingFiles" - @click="openParentSkillDir" - /> - <span>{{ editorDialog.currentDir || "/" }}</span> - </div> - - <v-progress-linear - v-if="editorDialog.loadingFiles" - indeterminate - color="primary" - /> - - <div v-else class="skill-editor__file-list"> - <button - v-for="entry in editorDialog.entries" - :key="`${entry.type}:${entry.path}`" - class="skill-editor__file-row" - :class="{ - 'skill-editor__file-row--active': - editorDialog.filePath === entry.path, - }" - type="button" - @click="openSkillEntry(entry)" - > - <v-icon size="18"> - {{ - entry.type === "directory" - ? "mdi-folder-outline" - : "mdi-file-document-outline" - }} - </v-icon> - <span>{{ entry.name }}</span> - <v-chip - v-if="entry.type === 'file' && !entry.editable" - size="x-small" - variant="tonal" - > - {{ tm("skills.readonly") }} - </v-chip> - </button> - </div> - </div> - - <div class="skill-editor__content"> - <div class="skill-editor__content-header"> - <div class="skill-editor__path"> - {{ editorDialog.filePath || tm("skills.noFileSelected") }} - </div> - <v-chip - v-if="editorDialog.fileDirty" - size="small" - color="warning" - variant="tonal" - > - {{ tm("skills.unsaved") }} - </v-chip> - </div> - - <v-alert - v-if="editorDialog.error" - type="error" - variant="tonal" - density="compact" - class="mb-3" - > - {{ editorDialog.error }} - </v-alert> - - <div class="skill-editor__monaco"> - <VueMonacoEditor - v-model:value="editorDialog.content" - :theme="editorTheme" - :language="editorLanguage" - :options="editorOptions" - style="height: 100%; width: 100%;" - @change="editorDialog.fileDirty = true" - /> - </div> - </div> - </div> - </v-card-text> - - <v-card-actions class="skill-editor-dialog__actions"> - <v-spacer /> - <v-btn - variant="text" - :disabled="editorDialog.saving" - @click="closeSkillEditor" - > - {{ tm("skills.cancel") }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="editorDialog.saving" - :disabled=" - !editorDialog.filePath || - !editorDialog.fileEditable || - !editorDialog.fileDirty - " - @click="saveSkillFile" - > - {{ tm("skills.saveFile") }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-dialog v-model="payloadDialog.show" max-width="820px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm("skills.neoPayloadTitle") }}</v-card-title> - <v-card-text> - <pre class="payload-preview">{{ payloadDialog.content }}</pre> - </v-card-text> - <v-card-actions class="d-flex justify-end"> - <v-btn variant="text" @click="payloadDialog.show = false">{{ - tm("skills.cancel") - }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-snackbar - v-model="snackbar.show" - :timeout="3500" - :color="snackbar.color" - elevation="6" - > - {{ snackbar.message }} - </v-snackbar> - </div> -</template> - -<script> -import { computed, nextTick, onMounted, reactive, ref, watch } from "vue"; -import { VueMonacoEditor } from "@guolao/vue-monaco-editor"; -import { skillApi, systemConfigApi } from "@/api/v1"; -import { useI18n, useModuleI18n } from "@/i18n/composables"; -import OutlinedActionListItem from "@/components/shared/OutlinedActionListItem.vue"; -import { useCustomizerStore } from "@/stores/customizer"; - -const STATUS_WAITING = "waiting"; -const STATUS_UPLOADING = "uploading"; -const STATUS_SUCCESS = "success"; -const STATUS_ERROR = "error"; -const STATUS_SKIPPED = "skipped"; - -export default { - name: "SkillsSection", - components: { OutlinedActionListItem, VueMonacoEditor }, - setup() { - const { t } = useI18n(); - const { tm } = useModuleI18n("features/extension"); - const customizer = useCustomizerStore(); - - const mode = ref("local"); - const skills = ref([]); - const loading = ref(false); - const runtime = ref("local"); - const sandboxCache = reactive({ ready: false, count: 0, updated_at: null }); - const uploading = ref(false); - const uploadDialog = ref(false); - const uploadInput = ref(null); - const uploadItems = ref([]); - const isUploadDragging = ref(false); - const itemLoading = reactive({}); - const deleteDialog = ref(false); - const deleting = ref(false); - const skillToDelete = ref(null); - const snackbar = reactive({ show: false, message: "", color: "success" }); - - const neoLoading = ref(false); - const neoCandidates = ref([]); - const neoReleases = ref([]); - const neoFilters = reactive({ - skill_key: "", - status: "", - stage: "", - }); - const candidatePromoteLoading = reactive({}); - const payloadDialog = reactive({ - show: false, - content: "", - }); - const editorDialog = reactive({ - show: false, - skillName: "", - currentDir: "", - entries: [], - filePath: "", - content: "", - fileEditable: false, - fileDirty: false, - loadingFiles: false, - loadingFile: false, - saving: false, - error: "", - }); - - const neoEnabled = ref(false); - const neoUnavailableMessage = ref(""); - let nextUploadItemId = 0; - - const candidateStatusItems = computed(() => [ - { title: tm("skills.neoAll"), value: "" }, - { title: "draft", value: "draft" }, - { title: "evaluating", value: "evaluating" }, - { title: "promoted", value: "promoted" }, - { title: "promoted_canary", value: "promoted_canary" }, - { title: "promoted_stable", value: "promoted_stable" }, - { title: "rejected", value: "rejected" }, - { title: "rolled_back", value: "rolled_back" }, - ]); - - const releaseStageItems = computed(() => [ - { title: tm("skills.neoAll"), value: "" }, - { title: "canary", value: "canary" }, - { title: "stable", value: "stable" }, - ]); - - const activeReleaseCount = computed( - () => neoReleases.value.filter((item) => item?.is_active).length, - ); - const editorLanguage = computed(() => { - const path = String(editorDialog.filePath || "").toLowerCase(); - if (path.endsWith(".json")) return "json"; - if (path.endsWith(".yaml") || path.endsWith(".yml")) return "yaml"; - if (path.endsWith(".toml") || path.endsWith(".ini")) return "ini"; - if (path.endsWith(".py")) return "python"; - if (path.endsWith(".js")) return "javascript"; - if (path.endsWith(".ts")) return "typescript"; - if (path.endsWith(".html")) return "html"; - if (path.endsWith(".css")) return "css"; - if (path.endsWith(".sh")) return "shell"; - if (path.endsWith(".md") || path.endsWith(".txt")) return "markdown"; - return "plaintext"; - }); - const editorTheme = computed(() => - customizer.uiTheme === "PurpleThemeDark" ? "vs-dark" : "vs-light", - ); - const editorOptions = computed(() => ({ - automaticLayout: true, - fontSize: 13, - lineNumbers: "on", - minimap: { enabled: false }, - readOnly: !editorDialog.fileEditable || editorDialog.loadingFile, - scrollBeyondLastLine: false, - tabSize: 2, - wordWrap: "on", - })); - const uploadStateCounts = computed(() => - uploadItems.value.reduce( - (counts, item) => { - counts.total += 1; - counts[item.status] += 1; - return counts; - }, - { - total: 0, - [STATUS_WAITING]: 0, - [STATUS_UPLOADING]: 0, - [STATUS_SUCCESS]: 0, - [STATUS_ERROR]: 0, - [STATUS_SKIPPED]: 0, - }, - ), - ); - const hasUploadableItems = computed(() => - uploadItems.value.some( - (item) => - item.status === STATUS_WAITING || item.status === STATUS_ERROR, - ), - ); - - const candidateHeaders = computed(() => [ - { title: "ID", key: "id", width: "180px" }, - { title: "skill_key", key: "skill_key" }, - { title: "status", key: "status", width: "130px" }, - { title: "score", key: "latest_score", width: "90px" }, - { - title: tm("skills.actions"), - key: "actions", - sortable: false, - width: "420px", - }, - ]); - - const releaseHeaders = computed(() => [ - { title: "ID", key: "id", width: "180px" }, - { title: "skill_key", key: "skill_key" }, - { title: "stage", key: "stage", width: "100px" }, - { title: "version", key: "version", width: "90px" }, - { title: "active", key: "is_active", width: "110px" }, - { - title: tm("skills.actions"), - key: "actions", - sortable: false, - width: "220px", - }, - ]); - - const showMessage = (message, color = "success") => { - snackbar.message = message; - snackbar.color = color; - snackbar.show = true; - }; - - const normalizeSkillsPayload = (res) => { - const payload = res?.data?.data || []; - if (Array.isArray(payload)) { - runtime.value = "local"; - sandboxCache.ready = false; - sandboxCache.count = 0; - sandboxCache.updated_at = null; - return payload; - } - runtime.value = payload.runtime || "local"; - const cache = payload.sandbox_cache || {}; - sandboxCache.ready = !!cache.ready; - sandboxCache.count = Number(cache.count || 0); - sandboxCache.updated_at = cache.updated_at || null; - return payload.skills || []; - }; - - const sourceTypeLabel = (sourceType, skill = null) => { - if (sourceType === "plugin") { - return tm("skills.sourcePlugin", { - plugin: skill?.source_label || skill?.plugin_name || "", - }); - } - if (sourceType === "sandbox_only") return tm("skills.sourceSandboxOnly"); - if (sourceType === "both") return tm("skills.sourceBoth"); - return tm("skills.sourceLocalOnly"); - }; - - const sourceTypeColor = (sourceType) => { - if (sourceType === "sandbox_only") return "indigo"; - if (sourceType === "plugin") return "secondary"; - if (sourceType === "both") return "success"; - return "primary"; - }; - - const isSandboxPresetSkill = (skill) => - skill?.source_type === "sandbox_only"; - const isPluginProvidedSkill = (skill) => skill?.source_type === "plugin"; - const isReadOnlySourceSkill = (skill) => - isSandboxPresetSkill(skill) || isPluginProvidedSkill(skill); - - const normalizeNeoItemsPayload = (res) => { - const payload = res?.data?.data || []; - if (Array.isArray(payload)) return payload; - if (Array.isArray(payload.items)) return payload.items; - return []; - }; - - const formatFileSize = (size) => { - if (!Number.isFinite(size) || size <= 0) return "0 B"; - if (size < 1024) return `${size} B`; - if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`; - return `${(size / (1024 * 1024)).toFixed(1)} MB`; - }; - - const normalizeUploadName = (name) => - String(name || "") - .trim() - .toLowerCase(); - - const buildUploadItem = (file, status, validationMessage) => ({ - id: `upload-${nextUploadItemId++}`, - file, - name: file.name, - size: file.size, - status, - validationMessage, - filenameKey: normalizeUploadName(file.name), - }); - - const uploadStatusLabel = (status) => { - if (status === STATUS_UPLOADING) return tm("skills.statusUploading"); - if (status === STATUS_SUCCESS) return tm("skills.statusSuccess"); - if (status === STATUS_ERROR) return tm("skills.statusError"); - if (status === STATUS_SKIPPED) return tm("skills.statusSkipped"); - return tm("skills.statusWaiting"); - }; - - const statusChipClass = (status) => - `skills-status-chip skills-status-chip--${status}`; - - const resetUploadState = () => { - uploadItems.value = []; - isUploadDragging.value = false; - if (uploadInput.value) { - uploadInput.value.value = ""; - } - }; - - const openUploadDialog = () => { - uploadDialog.value = true; - }; - - const closeUploadDialog = () => { - if (uploading.value) return; - uploadDialog.value = false; - }; - - const openUploadPicker = () => { - if (uploading.value) return; - uploadInput.value?.click(); - }; - - const addUploadFiles = (filesToAdd) => { - const existingNames = new Set( - uploadItems.value.map((item) => item.filenameKey), - ); - const nextItems = []; - - for (const file of filesToAdd) { - if (!file?.name) continue; - const filenameKey = normalizeUploadName(file.name); - - if (existingNames.has(filenameKey)) { - nextItems.push( - buildUploadItem( - file, - STATUS_SKIPPED, - tm("skills.validationDuplicate"), - ), - ); - continue; - } - - existingNames.add(filenameKey); - if (!/\.zip$/i.test(file.name)) { - nextItems.push( - buildUploadItem( - file, - STATUS_SKIPPED, - tm("skills.validationZipOnly"), - ), - ); - continue; - } - - nextItems.push( - buildUploadItem(file, STATUS_WAITING, tm("skills.validationReady")), - ); - } - - if (nextItems.length > 0) { - uploadItems.value = [...uploadItems.value, ...nextItems]; - } - }; - - const handleUploadSelection = (event) => { - const selected = Array.from(event?.target?.files || []); - addUploadFiles(selected); - if (uploadInput.value) { - uploadInput.value.value = ""; - } - }; - - const handleUploadDrop = (event) => { - isUploadDragging.value = false; - if (uploading.value) { - return; - } - addUploadFiles(Array.from(event?.dataTransfer?.files || [])); - }; - - const removeUploadItem = (itemId) => { - uploadItems.value = uploadItems.value.filter( - (item) => item.id !== itemId, - ); - }; - - const takeFirstMatch = (matchMap, filenameKey) => { - const matches = matchMap.get(filenameKey) || []; - const entry = matches.shift() || null; - if (matches.length === 0) { - matchMap.delete(filenameKey); - } - return entry; - }; - - const buildResultMap = (items = []) => { - const resultMap = new Map(); - for (const item of items) { - const filenameKey = normalizeUploadName(item?.filename); - if (!filenameKey) continue; - if (!resultMap.has(filenameKey)) { - resultMap.set(filenameKey, []); - } - resultMap.get(filenameKey).push(item); - } - return resultMap; - }; - - const applyUploadResults = (attemptedItems, payload) => { - const succeededMap = buildResultMap(payload?.succeeded); - const failedMap = buildResultMap(payload?.failed); - const skippedMap = buildResultMap(payload?.skipped); - - for (const item of attemptedItems) { - const successEntry = takeFirstMatch(succeededMap, item.filenameKey); - if (successEntry) { - item.status = STATUS_SUCCESS; - item.validationMessage = tm("skills.validationUploadedAs", { - name: successEntry.name || item.name, - }); - continue; - } - - const skippedEntry = takeFirstMatch(skippedMap, item.filenameKey); - if (skippedEntry) { - item.status = STATUS_SKIPPED; - item.validationMessage = - skippedEntry.error || tm("skills.validationDuplicate"); - continue; - } - - const failedEntry = takeFirstMatch(failedMap, item.filenameKey); - if (failedEntry) { - item.status = STATUS_ERROR; - item.validationMessage = - failedEntry.error || tm("skills.validationUploadFailed"); - continue; - } - - item.status = STATUS_ERROR; - item.validationMessage = tm("skills.validationNoResult"); - } - }; - - const fetchSkills = async () => { - loading.value = true; - try { - const res = await skillApi.list(); - skills.value = normalizeSkillsPayload(res); - } catch (_err) { - showMessage(tm("skills.loadFailed"), "error"); - } finally { - loading.value = false; - } - }; - - const handleApiResponse = ( - res, - successMessage, - failureMessageDefault, - onSuccess, - ) => { - if (res && res.data && res.data.status === "ok") { - showMessage(successMessage, "success"); - if (onSuccess) onSuccess(); - } else { - const msg = - (res && res.data && res.data.message) || failureMessageDefault; - showMessage(msg, "error"); - } - }; - - const uploadSkillBatch = async () => { - const attemptedItems = uploadItems.value.filter( - (item) => - item.status === STATUS_WAITING || item.status === STATUS_ERROR, - ); - if (attemptedItems.length === 0) return; - - uploading.value = true; - for (const item of attemptedItems) { - item.status = STATUS_UPLOADING; - item.validationMessage = tm("skills.validationUploading"); - } - - try { - const res = await skillApi.uploadBatch( - attemptedItems.map((item) => item.file), - ); - - const payload = res?.data?.data || {}; - applyUploadResults(attemptedItems, payload); - - const succeededCount = Array.isArray(payload.succeeded) - ? payload.succeeded.length - : 0; - const failedCount = Array.isArray(payload.failed) - ? payload.failed.length - : 0; - const responseColor = - res?.data?.status === "error" - ? "error" - : failedCount > 0 - ? "warning" - : "success"; - showMessage( - res?.data?.message || tm("skills.uploadSuccess"), - responseColor, - ); - - if (succeededCount > 0) { - await fetchSkills(); - } - } catch (_err) { - for (const item of attemptedItems) { - item.status = STATUS_ERROR; - item.validationMessage = tm("skills.validationUploadFailed"); - } - showMessage(tm("skills.uploadFailed"), "error"); - } finally { - uploading.value = false; - } - }; - - const toggleSkill = async (skill) => { - if (isSandboxPresetSkill(skill)) { - showMessage(tm("skills.sandboxPresetReadonly"), "warning"); - return; - } - const nextActive = !skill.active; - itemLoading[skill.name] = true; - try { - const res = await skillApi.setEnabled(skill.name, nextActive); - handleApiResponse( - res, - tm("skills.updateSuccess"), - tm("skills.updateFailed"), - () => { - skill.active = nextActive; - }, - ); - } catch (_err) { - showMessage(tm("skills.updateFailed"), "error"); - } finally { - itemLoading[skill.name] = false; - } - }; - - const confirmDelete = (skill) => { - if (isSandboxPresetSkill(skill)) { - showMessage(tm("skills.sandboxPresetReadonly"), "warning"); - return; - } - if (isPluginProvidedSkill(skill)) { - showMessage(tm("skills.pluginReadonly"), "warning"); - return; - } - skillToDelete.value = skill; - deleteDialog.value = true; - }; - - const deleteSkill = async () => { - if (!skillToDelete.value) return; - deleting.value = true; - try { - const res = await skillApi.delete(skillToDelete.value.name); - handleApiResponse( - res, - tm("skills.deleteSuccess"), - tm("skills.deleteFailed"), - async () => { - deleteDialog.value = false; - await fetchSkills(); - }, - ); - } catch (_err) { - showMessage(tm("skills.deleteFailed"), "error"); - } finally { - deleting.value = false; - } - }; - - const downloadSkill = async (skill) => { - if (isSandboxPresetSkill(skill)) { - showMessage(tm("skills.sandboxPresetReadonly"), "warning"); - return; - } - if (isPluginProvidedSkill(skill)) { - showMessage(tm("skills.pluginReadonly"), "warning"); - return; - } - itemLoading[skill.name] = true; - try { - const res = await skillApi.download(skill.name); - const blob = new Blob([res.data], { type: "application/zip" }); - const url = window.URL.createObjectURL(blob); - const link = document.createElement("a"); - link.href = url; - link.download = `${skill.name}.zip`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - window.URL.revokeObjectURL(url); - showMessage(tm("skills.downloadSuccess"), "success"); - } catch (_err) { - showMessage(tm("skills.downloadFailed"), "error"); - } finally { - itemLoading[skill.name] = false; - } - }; - - const resetEditorDialog = () => { - editorDialog.skillName = ""; - editorDialog.currentDir = ""; - editorDialog.entries = []; - editorDialog.filePath = ""; - editorDialog.content = ""; - editorDialog.fileEditable = false; - editorDialog.fileDirty = false; - editorDialog.loadingFiles = false; - editorDialog.loadingFile = false; - editorDialog.saving = false; - editorDialog.error = ""; - }; - - const loadSkillDir = async (path = "") => { - if (!editorDialog.skillName) return []; - editorDialog.loadingFiles = true; - editorDialog.error = ""; - try { - const res = await skillApi.listFiles(editorDialog.skillName, path); - if (res?.data?.status !== "ok") { - editorDialog.error = - res?.data?.message || tm("skills.editorLoadFailed"); - return []; - } - const payload = res.data.data || {}; - editorDialog.currentDir = payload.path || ""; - editorDialog.entries = Array.isArray(payload.entries) - ? payload.entries - : []; - return editorDialog.entries; - } catch (_err) { - editorDialog.error = tm("skills.editorLoadFailed"); - return []; - } finally { - editorDialog.loadingFiles = false; - } - }; - - const loadSkillFile = async (path) => { - if (!editorDialog.skillName || !path) return; - if ( - editorDialog.fileDirty && - !window.confirm(tm("skills.discardChanges")) - ) { - return; - } - editorDialog.loadingFile = true; - editorDialog.error = ""; - try { - const res = await skillApi.getFile(editorDialog.skillName, path); - if (res?.data?.status !== "ok") { - editorDialog.error = - res?.data?.message || tm("skills.editorLoadFailed"); - return; - } - const payload = res.data.data || {}; - editorDialog.filePath = payload.path || path; - editorDialog.content = payload.content || ""; - editorDialog.fileEditable = payload.editable !== false; - await nextTick(); - editorDialog.fileDirty = false; - } catch (_err) { - editorDialog.error = tm("skills.editorLoadFailed"); - } finally { - editorDialog.loadingFile = false; - } - }; - - const openSkillEditor = async (skill) => { - if (isSandboxPresetSkill(skill)) { - showMessage(tm("skills.sandboxPresetReadonly"), "warning"); - return; - } - resetEditorDialog(); - editorDialog.skillName = skill.name; - editorDialog.show = true; - const entries = await loadSkillDir(""); - const skillMd = entries.find((entry) => entry.path === "SKILL.md"); - if (skillMd?.editable) { - await loadSkillFile(skillMd.path); - } - }; - - const closeSkillEditor = () => { - if (editorDialog.saving) return; - if ( - editorDialog.fileDirty && - !window.confirm(tm("skills.discardChanges")) - ) { - return; - } - editorDialog.show = false; - resetEditorDialog(); - }; - - const openSkillEntry = async (entry) => { - if (!entry) return; - if (entry.type === "directory") { - if ( - editorDialog.fileDirty && - !window.confirm(tm("skills.discardChanges")) - ) { - return; - } - await loadSkillDir(entry.path); - return; - } - await loadSkillFile(entry.path); - }; - - const openParentSkillDir = async () => { - if (!editorDialog.currentDir) return; - if ( - editorDialog.fileDirty && - !window.confirm(tm("skills.discardChanges")) - ) { - return; - } - const parts = editorDialog.currentDir.split("/").filter(Boolean); - parts.pop(); - await loadSkillDir(parts.join("/")); - }; - - const saveSkillFile = async () => { - if ( - !editorDialog.skillName || - !editorDialog.filePath || - !editorDialog.fileEditable - ) { - return; - } - editorDialog.saving = true; - editorDialog.error = ""; - try { - const res = await skillApi.updateFile( - editorDialog.skillName, - editorDialog.filePath, - editorDialog.content, - ); - if (res?.data?.status !== "ok") { - editorDialog.error = - res?.data?.message || tm("skills.editorSaveFailed"); - showMessage(editorDialog.error, "error"); - return; - } - editorDialog.fileDirty = false; - showMessage(tm("skills.editorSaveSuccess"), "success"); - await fetchSkills(); - } catch (_err) { - editorDialog.error = tm("skills.editorSaveFailed"); - showMessage(tm("skills.editorSaveFailed"), "error"); - } finally { - editorDialog.saving = false; - } - }; - - const fetchNeoCandidates = async () => { - const params = { - skill_key: neoFilters.skill_key || undefined, - status: neoFilters.status || undefined, - }; - const res = await skillApi.neoCandidates(params); - neoCandidates.value = normalizeNeoItemsPayload(res); - }; - - const fetchNeoReleases = async () => { - const params = { - skill_key: neoFilters.skill_key || undefined, - stage: neoFilters.stage || undefined, - }; - const res = await skillApi.neoReleases(params); - neoReleases.value = normalizeNeoItemsPayload(res).map((item) => { - if (!item || typeof item !== "object") { - return item; - } - return { - ...item, - is_active: item.is_active ?? item.active ?? false, - }; - }); - }; - - const loadNeoAvailability = async () => { - try { - const res = await systemConfigApi.get(); - const config = res?.data?.data?.config || {}; - const providerSettings = config?.provider_settings || {}; - const currentRuntime = - providerSettings?.computer_use_runtime || "local"; - const booter = providerSettings?.sandbox?.booter || ""; - neoEnabled.value = - currentRuntime === "sandbox" && booter === "shipyard_neo"; - } catch (_err) { - neoEnabled.value = false; - } - - neoUnavailableMessage.value = tm("skills.neoRuntimeRequired"); - if (!neoEnabled.value && mode.value === "neo") { - mode.value = "local"; - } - }; - - const fetchNeoData = async () => { - neoLoading.value = true; - try { - await Promise.all([fetchNeoCandidates(), fetchNeoReleases()]); - } catch (_err) { - showMessage(tm("skills.neoLoadFailed"), "error"); - } finally { - neoLoading.value = false; - } - }; - - const evaluateCandidate = async (candidate, passed) => { - try { - const res = await skillApi.evaluateNeoCandidate({ - candidate_id: candidate.id, - passed, - score: passed ? 1.0 : 0.0, - report: passed ? "approved_from_webui" : "rejected_from_webui", - }); - handleApiResponse( - res, - tm("skills.neoEvaluateSuccess"), - tm("skills.neoEvaluateFailed"), - async () => { - await fetchNeoCandidates(); - }, - ); - } catch (_err) { - showMessage(tm("skills.neoEvaluateFailed"), "error"); - } - }; - - const candidatePromoteLoadingKey = (candidateId, stage) => - `${candidateId}:${stage}`; - const isCandidatePromoteLoading = (candidateId, stage) => - !!candidatePromoteLoading[candidatePromoteLoadingKey(candidateId, stage)]; - const isCandidatePromoting = (candidateId) => - isCandidatePromoteLoading(candidateId, "canary") || - isCandidatePromoteLoading(candidateId, "stable"); - - const promoteCandidate = async (candidate, stage) => { - const candidateId = candidate?.id; - if (!candidateId) return; - const loadingKey = candidatePromoteLoadingKey(candidateId, stage); - if (candidatePromoteLoading[loadingKey]) return; - candidatePromoteLoading[loadingKey] = true; - try { - const res = await skillApi.promoteNeoCandidate({ - candidate_id: candidateId, - stage, - sync_to_local: true, - }); - const ok = res?.data?.status === "ok"; - if (!ok) { - showMessage( - res?.data?.message || tm("skills.neoPromoteFailed"), - "error", - ); - } else { - showMessage(tm("skills.neoPromoteSuccess"), "success"); - } - await fetchNeoData(); - if (stage === "stable") { - await fetchSkills(); - } - } catch (_err) { - showMessage(tm("skills.neoPromoteFailed"), "error"); - } finally { - candidatePromoteLoading[loadingKey] = false; - } - }; - - const rollbackRelease = async (release) => { - try { - const res = await skillApi.rollbackNeoRelease({ - release_id: release.id, - }); - handleApiResponse( - res, - tm("skills.neoRollbackSuccess"), - tm("skills.neoRollbackFailed"), - async () => { - await fetchNeoData(); - }, - ); - } catch (_err) { - showMessage(tm("skills.neoRollbackFailed"), "error"); - } - }; - - const deactivateRelease = async (release) => { - try { - const res = await skillApi.rollbackNeoRelease({ - release_id: release.id, - }); - handleApiResponse( - res, - tm("skills.neoDeactivateSuccess"), - tm("skills.neoDeactivateFailed"), - async () => { - await fetchNeoData(); - }, - ); - } catch (_err) { - showMessage(tm("skills.neoDeactivateFailed"), "error"); - } - }; - - const handleReleaseLifecycleAction = async (release) => { - if (release?.is_active) { - await deactivateRelease(release); - return; - } - await rollbackRelease(release); - }; - - const syncRelease = async (release) => { - try { - const res = await skillApi.syncNeoRelease({ - release_id: release.id, - }); - handleApiResponse( - res, - tm("skills.neoSyncSuccess"), - tm("skills.neoSyncFailed"), - async () => { - await fetchSkills(); - }, - ); - } catch (_err) { - showMessage(tm("skills.neoSyncFailed"), "error"); - } - }; - - const viewPayload = async (payloadRef) => { - if (!payloadRef) return; - try { - const res = await skillApi.neoPayload(payloadRef); - if (res?.data?.status !== "ok") { - showMessage( - res?.data?.message || tm("skills.neoPayloadFailed"), - "error", - ); - return; - } - const payload = res?.data?.data || {}; - payloadDialog.content = JSON.stringify(payload, null, 2); - payloadDialog.show = true; - } catch (_err) { - showMessage(tm("skills.neoPayloadFailed"), "error"); - } - }; - - const deleteCandidate = async (candidate) => { - try { - const res = await skillApi.deleteNeoCandidate({ - candidate_id: candidate.id, - reason: "deleted_from_webui", - }); - handleApiResponse( - res, - tm("skills.neoDeleteSuccess"), - tm("skills.neoDeleteFailed"), - async () => { - await fetchNeoData(); - }, - ); - } catch (_err) { - showMessage(tm("skills.neoDeleteFailed"), "error"); - } - }; - - const deleteRelease = async (release) => { - try { - const res = await skillApi.deleteNeoRelease({ - release_id: release.id, - reason: "deleted_from_webui", - }); - handleApiResponse( - res, - tm("skills.neoDeleteSuccess"), - tm("skills.neoDeleteFailed"), - async () => { - await fetchNeoData(); - }, - ); - } catch (_err) { - showMessage(tm("skills.neoDeleteFailed"), "error"); - } - }; - - const refreshCurrentMode = async () => { - if (mode.value === "neo") { - await loadNeoAvailability(); - if (neoEnabled.value) { - await fetchNeoData(); - } else { - showMessage(tm("skills.neoRuntimeRequired"), "warning"); - } - } else { - await fetchSkills(); - } - }; - - watch(mode, async (nextMode) => { - if (nextMode === "neo") { - await loadNeoAvailability(); - if (neoEnabled.value) { - await fetchNeoData(); - } - } else { - await fetchSkills(); - } - }); - - watch(uploadDialog, (isOpen) => { - if (!isOpen && !uploading.value) { - resetUploadState(); - } - }); - - onMounted(async () => { - await Promise.all([fetchSkills(), loadNeoAvailability()]); - if (neoEnabled.value) { - await fetchNeoData(); - } - }); - - return { - t, - tm, - mode, - skills, - loading, - runtime, - sandboxCache, - uploadDialog, - uploadInput, - uploadItems, - uploadStateCounts, - hasUploadableItems, - isUploadDragging, - uploading, - itemLoading, - deleteDialog, - deleting, - snackbar, - neoEnabled, - neoUnavailableMessage, - neoLoading, - neoCandidates, - neoReleases, - neoFilters, - candidateStatusItems, - releaseStageItems, - activeReleaseCount, - candidateHeaders, - releaseHeaders, - payloadDialog, - editorDialog, - editorLanguage, - editorTheme, - editorOptions, - formatFileSize, - uploadStatusLabel, - statusChipClass, - openUploadDialog, - closeUploadDialog, - openUploadPicker, - handleUploadSelection, - handleUploadDrop, - removeUploadItem, - refreshCurrentMode, - fetchNeoData, - uploadSkillBatch, - downloadSkill, - openSkillEditor, - closeSkillEditor, - openSkillEntry, - openParentSkillDir, - saveSkillFile, - toggleSkill, - confirmDelete, - deleteSkill, - evaluateCandidate, - promoteCandidate, - isCandidatePromoteLoading, - isCandidatePromoting, - rollbackRelease, - deactivateRelease, - handleReleaseLifecycleAction, - syncRelease, - viewPayload, - deleteCandidate, - deleteRelease, - sourceTypeLabel, - sourceTypeColor, - isSandboxPresetSkill, - isPluginProvidedSkill, - isReadOnlySourceSkill, - }; - }, -}; -</script> - -<style scoped> -.skills-list { - display: flex; - flex-direction: column; - gap: 12px; -} - -.list-action-icon-btn { - color: rgba(var(--v-theme-on-surface), 0.78); -} - -.list-action-icon-btn:hover { - background: rgba(var(--v-theme-on-surface), 0.08); - color: rgb(var(--v-theme-on-surface)); -} - -.skills-fab-stack { - align-items: center; - bottom: 52px; - display: flex; - flex-direction: column; - gap: 16px; - position: fixed; - right: 52px; - z-index: 10000; -} - -.skills-fab { - border-radius: 16px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); -} - -.skills-fab:hover { - box-shadow: 0 12px 20px rgba(var(--v-theme-primary), 0.4); - transform: translateY(-4px) scale(1.05); -} - -.skill-description { - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.skill-path { - display: -webkit-box; - -webkit-line-clamp: 1; - -webkit-box-orient: vertical; - margin-top: 6px; - overflow: hidden; - word-break: break-all; -} - -.skill-editor-dialog { - max-height: min(88vh, 980px); - overflow: hidden; -} - -.skill-editor-dialog__header { - align-items: flex-start; - display: flex; - justify-content: space-between; - gap: 16px; -} - -.skill-editor-dialog__body { - min-height: 0; - padding: 16px 22px; -} - -.skill-editor-dialog__actions { - border-top: 1px solid var(--v-theme-border); - padding: 12px 22px; -} - -.skill-editor { - display: grid; - grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); - gap: 16px; - min-height: 560px; -} - -.skill-editor__files { - border: 1px solid rgba(128, 128, 128, 0.28); - border-radius: 10px; - display: flex; - flex-direction: column; - min-height: 0; - overflow: hidden; -} - -.skill-editor__files-header { - align-items: center; - border-bottom: 1px solid rgba(128, 128, 128, 0.28); - display: flex; - gap: 8px; - min-height: 44px; - padding: 6px 10px; -} - -.skill-editor__files-header span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.skill-editor__file-list { - display: flex; - flex-direction: column; - gap: 6px; - overflow-y: auto; - padding: 6px; -} - -.skill-editor__file-row { - align-items: center; - border-radius: 8px; - color: rgb(var(--v-theme-on-surface)); - display: grid; - gap: 8px; - grid-template-columns: auto minmax(0, 1fr) auto; - min-height: 34px; - padding: 6px 8px; - text-align: left; -} - -.skill-editor__file-row:hover, -.skill-editor__file-row--active { - background: rgba(var(--v-theme-on-surface), 0.06); -} - -.skill-editor__file-row--active { - background: rgba(var(--v-theme-on-surface), 0.1); -} - -.skill-editor__file-row span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.skill-editor__content { - display: flex; - flex-direction: column; - min-width: 0; -} - -.skill-editor__content-header { - align-items: center; - display: flex; - gap: 10px; - justify-content: space-between; - min-height: 34px; -} - -.skill-editor__path { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.skill-editor__monaco { - border: 1px solid rgba(128, 128, 128, 0.28); - border-radius: 10px; - flex: 1 1 auto; - margin-top: 12px; - min-height: 520px; - overflow: hidden; - position: relative; -} - -.skills-upload-dialog { - display: flex; - flex-direction: column; - max-height: min(88vh, 960px); - border-radius: 24px; - background: rgb(var(--v-theme-surface)); - border: 1px solid var(--v-theme-border); - outline: 1px solid rgba(var(--v-theme-primary), 0.1); - outline-offset: -1px; - box-shadow: 0 24px 60px rgba(15, 23, 42, 0.12); - overflow: hidden; -} - -.skills-upload-dialog__header { - position: relative; - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: flex-start; - gap: 16px; - white-space: normal; - overflow: visible; -} - -.skills-upload-dialog__heading { - min-width: 0; - padding-right: 0; - white-space: normal; -} - -.skills-upload-dialog__description { - max-width: 100%; - color: var(--v-theme-secondaryText); - line-height: 1.7; - word-break: break-word; - white-space: normal; - overflow-wrap: anywhere; -} - -.skills-upload-dialog__description--body { - margin: 0 0 14px; - font-size: 15px; - line-height: 1.6; -} - -.skills-upload-dialog__close { - align-self: flex-start; -} - -.skills-upload-dialog__body { - flex: 1 1 auto; - min-height: 0; - overflow-y: auto; -} - -.skills-upload-dialog__actions { - flex: 0 0 auto; - display: flex; - align-items: center; - gap: 10px; - border-top: 1px solid var(--v-theme-border); - background: rgba(var(--v-theme-surface), 0.98); -} - -.skills-upload-dialog__action-btn { - min-width: 96px; - height: 38px; - border-radius: 10px; - font-weight: 600; - letter-spacing: 0; - text-transform: none; -} - -.skills-upload-structure-note { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 18px; - padding: 12px 14px; - border-radius: 16px; - border: 1px solid rgba(var(--v-theme-primary), 0.18); - background: rgba(var(--v-theme-surface), 0.96); - color: var(--v-theme-secondaryText); - line-height: 1.6; -} - -.skills-upload-capabilities { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 12px; - margin-bottom: 18px; -} - -.skills-upload-capability { - display: flex; - align-items: center; - gap: 10px; - min-height: 52px; - padding: 0 14px; - border-radius: 16px; - border: 1px solid rgba(var(--v-theme-primary), 0.16); - background: rgba(var(--v-theme-surface), 0.96); - color: var(--v-theme-secondaryText); -} - -.skills-upload-capability__icon { - display: flex; - align-items: center; - justify-content: center; - width: 30px; - height: 30px; - border-radius: 999px; - background: rgba(var(--v-theme-primary), 0.16); - color: rgba(var(--v-theme-primary), 0.95); -} - -.skills-dropzone { - padding: 36px 24px; - border-radius: 22px; - border: 1.5px dashed rgba(var(--v-theme-primary), 0.24); - background: rgba(var(--v-theme-surface), 0.94); - text-align: center; - cursor: pointer; - transition: - border-color 0.2s ease, - transform 0.2s ease, - background-color 0.2s ease; -} - -.skills-dropzone:hover, -.skills-dropzone--dragover { - border-color: rgba(var(--v-theme-primary), 0.52); - background: rgba(var(--v-theme-primary), 0.05); - transform: translateY(-1px); -} - -.skills-dropzone__icon { - display: flex; - align-items: center; - justify-content: center; - width: 66px; - height: 66px; - margin: 0 auto 18px; - border-radius: 20px; - background: rgba(var(--v-theme-primary), 0.15); - color: rgba(var(--v-theme-primary), 0.96); -} - -.skills-dropzone__subtitle { - margin-top: 10px; - color: var(--v-theme-secondaryText); -} - -.skills-dropzone__hint { - margin-top: 8px; - font-size: 13px; - color: var(--v-theme-secondaryText); - opacity: 0.82; -} - -.skills-upload-summary { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 18px; -} - -.skills-upload-summary__chip { - background: rgba(var(--v-theme-surface), 0.96); - border: 1px solid rgba(var(--v-theme-primary), 0.16); - color: var(--v-theme-secondaryText); -} - -.skills-upload-summary__chip--success { - background: rgba(var(--v-theme-primary), 0.18); - color: var(--v-theme-primaryText); -} - -.skills-upload-summary__chip--error { - background: #f2e6e2; - color: #8b5d54; -} - -.skills-upload-list { - margin-top: 16px; - border-radius: 20px; - border: 1px solid rgba(var(--v-theme-primary), 0.2); - background: rgba(var(--v-theme-surface), 0.94); - overflow: hidden; -} - -.skills-upload-list__header { - padding: 14px 18px; - border-bottom: 1px solid rgba(var(--v-theme-primary), 0.14); - color: var(--v-theme-primaryText); - font-weight: 600; -} - -.skills-upload-row { - display: flex; - justify-content: space-between; - gap: 16px; - padding: 16px 18px; -} - -.skills-upload-row + .skills-upload-row { - border-top: 1px solid rgba(var(--v-theme-primary), 0.12); -} - -.skills-upload-row__meta { - min-width: 0; - flex: 1; -} - -.skills-upload-row__name { - font-weight: 600; - color: var(--v-theme-primaryText); - word-break: break-all; -} - -.skills-upload-row__size { - margin-top: 4px; - font-size: 12px; - color: var(--v-theme-secondaryText); - opacity: 0.82; -} - -.skills-upload-row__message { - margin-top: 8px; - font-size: 13px; - line-height: 1.5; - color: var(--v-theme-secondaryText); -} - -.skills-upload-row__actions { - display: flex; - align-items: flex-start; - gap: 10px; -} - -.skills-status-chip { - min-width: 74px; - justify-content: center; - font-weight: 600; -} - -.skills-status-chip--waiting { - background: rgba(var(--v-theme-surface), 0.96); - border: 1px solid rgba(var(--v-theme-primary), 0.16); - color: var(--v-theme-secondaryText); -} - -.skills-status-chip--uploading { - background: rgba(var(--v-theme-primary), 0.14); - color: var(--v-theme-primaryText); -} - -.skills-status-chip--success { - background: rgba(var(--v-theme-primary), 0.2); - color: var(--v-theme-primaryText); -} - -.skills-status-chip--error { - background: #f2e6e2; - color: #8a5a50; -} - -.skills-status-chip--skipped { - background: rgba(var(--v-theme-surface), 0.96); - border: 1px solid rgba(var(--v-theme-primary), 0.16); - color: var(--v-theme-secondaryText); -} - -.skills-upload-empty { - margin-top: 16px; - padding: 20px 18px; - border-radius: 20px; - border: 1px dashed rgba(var(--v-theme-primary), 0.24); - background: rgba(var(--v-theme-surface), 0.94); - text-align: center; - color: var(--v-theme-secondaryText); -} - -.payload-preview { - max-height: 480px; - overflow: auto; - background: #111; - color: #ececec; - padding: 12px; - border-radius: 8px; - font-size: 12px; -} - -.neo-filter-card { - border-radius: 14px; - border-color: rgba(var(--v-theme-primary), 0.25); - background: linear-gradient( - 180deg, - rgba(var(--v-theme-primary), 0.03), - rgba(var(--v-theme-surface), 1) - ); -} - -.neo-table-card { - border-radius: 14px; -} - -.neo-data-table :deep(.v-data-table-header__content) { - font-weight: 700; -} - -.neo-data-table :deep(tbody tr:hover) { - background: rgba(var(--v-theme-primary), 0.04); -} - -@media (max-width: 860px) { - .skills-upload-capabilities { - grid-template-columns: 1fr; - } -} - -@media (max-width: 640px) { - .skills-upload-dialog { - max-height: 92vh; - } - - .skills-upload-dialog__header { - gap: 12px; - } - - .skills-upload-dialog__heading { - padding-right: 0; - } - - .skills-upload-row { - flex-direction: column; - } - - .skills-upload-row__actions { - justify-content: space-between; - align-items: center; - } - - .skills-upload-dialog__description--body { - font-size: 14px; - } -} -</style> diff --git a/legacy-dashboard/src/components/extension/componentPanel/components/CommandFilters.vue b/legacy-dashboard/src/components/extension/componentPanel/components/CommandFilters.vue deleted file mode 100644 index 15a6be18..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/components/CommandFilters.vue +++ /dev/null @@ -1,158 +0,0 @@ -<script setup lang="ts"> -import { computed } from 'vue'; -import { useModuleI18n } from '@/i18n/composables'; -import { normalizeTextInput } from '@/utils/inputValue'; - -const { tm } = useModuleI18n('features/command'); - -// Props -const props = defineProps<{ - availablePlugins: string[]; - hasSystemPluginConflict: boolean; - effectiveShowSystemPlugins: boolean; - pluginFilter: string; - typeFilter: string; - permissionFilter: string; - statusFilter: string; - showSystemPlugins: boolean; - searchQuery: string; -}>(); - -// Emits -const emit = defineEmits<{ - (e: 'update:pluginFilter', value: string): void; - (e: 'update:typeFilter', value: string): void; - (e: 'update:permissionFilter', value: string): void; - (e: 'update:statusFilter', value: string): void; - (e: 'update:showSystemPlugins', value: boolean): void; - (e: 'update:searchQuery', value: string): void; -}>(); - -// Computed items for selects -const pluginItems = computed(() => [ - { title: tm('filters.all'), value: 'all' }, - ...props.availablePlugins.map(p => ({ title: p, value: p })) -]); - -const typeItems = [ - { title: tm('filters.all'), value: 'all' }, - { title: tm('type.group'), value: 'group' }, - { title: tm('type.command'), value: 'command' }, - { title: tm('type.subCommand'), value: 'sub_command' } -]; - -const permissionItems = [ - { title: tm('filters.all'), value: 'all' }, - { title: tm('permission.everyone'), value: 'everyone' }, - { title: tm('permission.admin'), value: 'admin' } -]; - -const statusItems = [ - { title: tm('filters.all'), value: 'all' }, - { title: tm('filters.enabled'), value: 'enabled' }, - { title: tm('filters.disabled'), value: 'disabled' }, - { title: tm('filters.conflict'), value: 'conflict' } -]; - -</script> - -<template> - <!-- 过滤器行 --> - <v-row class="mb-4" align="center"> - <v-col cols="12" sm="6" md="3"> - <v-select - :model-value="pluginFilter" - @update:model-value="emit('update:pluginFilter', $event)" - :items="pluginItems" - :label="tm('filters.byPlugin')" - density="compact" - variant="outlined" - hide-details - /> - </v-col> - <v-col cols="12" sm="6" md="2"> - <v-select - :model-value="typeFilter" - @update:model-value="emit('update:typeFilter', $event)" - :items="typeItems" - :label="tm('filters.byType')" - density="compact" - variant="outlined" - hide-details - /> - </v-col> - <v-col cols="12" sm="6" md="2"> - <v-select - :model-value="permissionFilter" - @update:model-value="emit('update:permissionFilter', $event)" - :items="permissionItems" - :label="tm('filters.byPermission')" - density="compact" - variant="outlined" - hide-details - /> - </v-col> - <v-col cols="12" sm="6" md="2"> - <v-select - :model-value="statusFilter" - @update:model-value="emit('update:statusFilter', $event)" - :items="statusItems" - :label="tm('filters.byStatus')" - density="compact" - variant="outlined" - hide-details - /> - </v-col> - </v-row> - - <!-- 搜索栏 + 统计信息行 --> - <div class="mb-4 d-flex flex-wrap align-center ga-4"> - <div style="min-width: 200px; max-width: 350px; flex: 1; border: 1px solid #B9B9B9; border-radius: 16px;"> - <v-text-field - :model-value="searchQuery" - @update:model-value="emit('update:searchQuery', normalizeTextInput($event))" - density="compact" - :label="tm('search.placeholder')" - prepend-inner-icon="mdi-magnify" - clearable - variant="solo-filled" - flat - hide-details - single-line - /> - </div> - <div class="d-flex align-center ga-4"> - <slot name="stats"></slot> - <v-divider vertical class="mx-1" style="height: 20px;" /> - <v-checkbox - :model-value="effectiveShowSystemPlugins" - @update:model-value="emit('update:showSystemPlugins', !!$event)" - :label="tm('filters.showSystemPlugins')" - density="compact" - hide-details - :disabled="hasSystemPluginConflict" - class="system-plugin-checkbox" - > - <template v-slot:label> - <span class="text-body-2">{{ tm('filters.showSystemPlugins') }}</span> - <v-tooltip v-if="hasSystemPluginConflict" location="top"> - <template v-slot:activator="{ props: tooltipProps }"> - <v-icon v-bind="tooltipProps" size="16" color="warning" class="ml-1">mdi-alert-circle</v-icon> - </template> - {{ tm('filters.systemPluginConflictHint') }} - </v-tooltip> - </template> - </v-checkbox> - </div> - </div> -</template> - -<style scoped> -.system-plugin-checkbox { - flex: none; -} - -.system-plugin-checkbox :deep(.v-selection-control) { - min-height: auto; -} -</style> diff --git a/legacy-dashboard/src/components/extension/componentPanel/components/CommandTable.vue b/legacy-dashboard/src/components/extension/componentPanel/components/CommandTable.vue deleted file mode 100644 index be2ae989..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/components/CommandTable.vue +++ /dev/null @@ -1,289 +0,0 @@ -<script setup lang="ts"> -import { computed } from 'vue'; -import { useModuleI18n } from '@/i18n/composables'; -import type { CommandItem, TypeInfo, StatusInfo } from '../types'; - -const { tm } = useModuleI18n('features/command'); - -// Props -const props = defineProps<{ - items: CommandItem[]; - expandedGroups: Set<string>; - loading?: boolean; -}>(); - -// Emits -const emit = defineEmits<{ - (e: 'toggle-expand', cmd: CommandItem): void; - (e: 'toggle-command', cmd: CommandItem): void; - (e: 'rename', cmd: CommandItem): void; - (e: 'view-details', cmd: CommandItem): void; - (e: 'update-permission', cmd: CommandItem, permission: 'admin' | 'member'): void; -}>(); - -// 表格表头 -const commandHeaders = computed(() => [ - { title: tm('table.headers.command'), key: 'effective_command', minWidth: '100px' }, - { title: tm('table.headers.type'), key: 'type', sortable: false, width: '100px' }, - { title: tm('table.headers.plugin'), key: 'plugin', width: '140px' }, - { title: tm('table.headers.description'), key: 'description', sortable: false }, - { title: tm('table.headers.permission'), key: 'permission', sortable: false, width: '100px' }, - { title: tm('table.headers.status'), key: 'enabled', sortable: false, width: '100px' }, - { title: tm('table.headers.actions'), key: 'actions', sortable: false, width: '140px' } -]); - -// 检查组是否展开 -const isGroupExpanded = (cmd: CommandItem): boolean => { - return props.expandedGroups.has(cmd.handler_full_name); -}; - -// 获取类型信息 -const getTypeInfo = (type: string): TypeInfo => { - switch (type) { - case 'group': - return { text: tm('type.group'), color: 'info', icon: 'mdi-folder-outline' }; - case 'sub_command': - return { text: tm('type.subCommand'), color: 'secondary', icon: 'mdi-subdirectory-arrow-right' }; - default: - return { text: tm('type.command'), color: 'primary', icon: 'mdi-console-line' }; - } -}; - -// 获取权限颜色 -const getPermissionColor = (permission: string): string => { - switch (permission) { - case 'admin': return 'error'; - default: return 'success'; - } -}; - -// 获取权限标签 -const getPermissionLabel = (permission: string): string => { - switch (permission) { - case 'admin': return tm('permission.admin'); - default: return tm('permission.everyone'); - } -}; - -// 获取状态信息 -const getStatusInfo = (cmd: CommandItem): StatusInfo => { - if (cmd.has_conflict) { - return { text: tm('status.conflict'), color: 'warning', variant: 'flat' }; - } - if (cmd.enabled) { - return { text: tm('status.enabled'), color: 'success', variant: 'flat' }; - } - return { text: tm('status.disabled'), color: 'error', variant: 'outlined' }; -}; - -// 获取行属性 -const getRowProps = ({ item }: { item: CommandItem }) => { - const classes: string[] = []; - if (item.has_conflict) { - classes.push('conflict-row'); - } - if (item.type === 'sub_command') { - classes.push('sub-command-row'); - } - if (item.is_group) { - classes.push('group-row'); - } - return classes.length > 0 ? { class: classes.join(' ') } : {}; -}; -</script> - -<template> - <v-card class="rounded-lg overflow-hidden elevation-1"> - <v-data-table - :headers="commandHeaders" - :items="items" - item-key="handler_full_name" - hover - :row-props="getRowProps" - :loading="props.loading" - > - <template v-slot:item.effective_command="{ item }"> - <div class="d-flex align-center py-2"> - <!-- 展开/折叠按钮(针对指令组) --> - <v-btn - v-if="item.is_group && item.sub_commands?.length > 0" - icon - variant="text" - size="x-small" - class="mr-1" - @click.stop="emit('toggle-expand', item)" - > - <v-icon size="18">{{ isGroupExpanded(item) ? 'mdi-chevron-down' : 'mdi-chevron-right' }}</v-icon> - </v-btn> - <!-- 子指令缩进 --> - <div v-else-if="item.type === 'sub_command'" class="ml-6"></div> - <div> - <div class="text-subtitle-1 font-weight-medium"> - <code :class="{ 'sub-command-code': item.type === 'sub_command' }">{{ item.effective_command }}</code> - </div> - </div> - </div> - </template> - - <template v-slot:item.type="{ item }"> - <v-chip - :color="getTypeInfo(item.type).color" - size="small" - variant="tonal" - > - <v-icon start size="14">{{ getTypeInfo(item.type).icon }}</v-icon> - {{ getTypeInfo(item.type).text }}{{ item.is_group && item.sub_commands?.length > 0 ? `(${item.sub_commands.length})` : '' }} - </v-chip> - </template> - - <template v-slot:item.plugin="{ item }"> - <div class="text-body-2">{{ item.plugin_display_name || item.plugin }}</div> - </template> - - <template v-slot:item.description="{ item }"> - <div class="text-body-2 text-medium-emphasis" style="max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" :title="item.description"> - {{ item.description || '-' }} - </div> - </template> - - <template v-slot:item.permission="{ item }"> - <v-menu location="bottom"> - <template v-slot:activator="{ props }"> - <v-chip - v-bind="props" - :color="getPermissionColor(item.permission)" - size="small" - class="font-weight-medium cursor-pointer" - link - > - {{ getPermissionLabel(item.permission) }} - <v-icon end size="14">mdi-chevron-down</v-icon> - </v-chip> - </template> - <v-list density="compact"> - <v-list-item - :value="'member'" - @click="$emit('update-permission', item, 'member')" - :active="item.permission !== 'admin'" - > - <v-list-item-title>{{ tm('permission.everyone') }}</v-list-item-title> - </v-list-item> - <v-list-item - :value="'admin'" - @click="$emit('update-permission', item, 'admin')" - :active="item.permission === 'admin'" - > - <v-list-item-title>{{ tm('permission.admin') }}</v-list-item-title> - </v-list-item> - </v-list> - </v-menu> - </template> - - <template v-slot:item.enabled="{ item }"> - <v-chip - :color="getStatusInfo(item).color" - size="small" - class="font-weight-medium" - :variant="getStatusInfo(item).variant" - > - {{ getStatusInfo(item).text }} - </v-chip> - </template> - - <template v-slot:item.actions="{ item }"> - <div class="d-flex align-center"> - <v-btn-group density="default" variant="text" color="primary"> - <v-btn - v-if="!item.enabled" - icon - size="small" - color="success" - @click="emit('toggle-command', item)" - > - <v-icon size="22">mdi-play</v-icon> - <v-tooltip activator="parent" location="top">{{ tm('tooltips.enable') }}</v-tooltip> - </v-btn> - <v-btn - v-else - icon - size="small" - color="error" - @click="emit('toggle-command', item)" - > - <v-icon size="22">mdi-pause</v-icon> - <v-tooltip activator="parent" location="top">{{ tm('tooltips.disable') }}</v-tooltip> - </v-btn> - - <v-btn icon size="small" color="warning" @click="emit('rename', item)"> - <v-icon size="22">mdi-pencil</v-icon> - <v-tooltip activator="parent" location="top">{{ tm('tooltips.rename') }}</v-tooltip> - </v-btn> - - <v-btn icon size="small" @click="emit('view-details', item)"> - <v-icon size="22">mdi-information</v-icon> - <v-tooltip activator="parent" location="top">{{ tm('tooltips.viewDetails') }}</v-tooltip> - </v-btn> - </v-btn-group> - </div> - </template> - - <template v-slot:no-data> - <div class="text-center pa-8"> - <v-icon size="64" color="info" class="mb-4">mdi-console-line</v-icon> - <div class="text-h5 mb-2">{{ tm('empty.noCommands') }}</div> - <div class="text-body-1 mb-4">{{ tm('empty.noCommandsDesc') }}</div> - </div> - </template> - </v-data-table> - </v-card> -</template> - -<style scoped> -code { - background-color: rgba(var(--v-theme-primary), 0.1); - padding: 2px 6px; - border-radius: 4px; - font-size: 0.9em; - white-space: nowrap; -} - -code.sub-command-code { - background-color: rgba(var(--v-theme-secondary), 0.1); - color: rgb(var(--v-theme-secondary)); -} -</style> - -<style> -/* 冲突行高亮 */ -.v-data-table .conflict-row { - background: linear-gradient(90deg, rgba(var(--v-theme-warning), 0.15) 0%, rgba(var(--v-theme-warning), 0.05) 100%) !important; - border-left: 3px solid rgb(var(--v-theme-warning)) !important; -} - -.v-data-table .conflict-row:hover { - background: linear-gradient(90deg, rgba(var(--v-theme-warning), 0.25) 0%, rgba(var(--v-theme-warning), 0.1) 100%) !important; -} - -/* 指令组行样式 */ -.v-data-table .group-row { - background-color: rgba(var(--v-theme-info), 0.05); -} - -.v-data-table .group-row:hover { - background-color: rgba(var(--v-theme-info), 0.08) !important; -} - -/* 子指令行样式 */ -.v-data-table .sub-command-row { - background-color: rgba(var(--v-theme-info), 0.05); -} - -.v-data-table .sub-command-row:hover { - background-color: rgba(var(--v-theme-info), 0.08) !important; -} - -.cursor-pointer { - cursor: pointer; -} -</style> - diff --git a/legacy-dashboard/src/components/extension/componentPanel/components/DetailsDialog.vue b/legacy-dashboard/src/components/extension/componentPanel/components/DetailsDialog.vue deleted file mode 100644 index 6cbc4197..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/components/DetailsDialog.vue +++ /dev/null @@ -1,143 +0,0 @@ -<script setup lang="ts"> -import { useI18n, useModuleI18n } from '@/i18n/composables'; -import type { CommandItem, TypeInfo } from '../types'; - -const { t } = useI18n(); -const { tm } = useModuleI18n('features/command'); - -// Props -defineProps<{ - show: boolean; - command: CommandItem | null; -}>(); - -// Emits -const emit = defineEmits<{ - (e: 'update:show', value: boolean): void; -}>(); - -// 获取类型信息 -const getTypeInfo = (type: string): TypeInfo => { - switch (type) { - case 'group': - return { text: tm('type.group'), color: 'info', icon: 'mdi-folder-outline' }; - case 'sub_command': - return { text: tm('type.subCommand'), color: 'secondary', icon: 'mdi-subdirectory-arrow-right' }; - default: - return { text: tm('type.command'), color: 'primary', icon: 'mdi-console-line' }; - } -}; - -// 获取权限颜色 -const getPermissionColor = (permission: string): string => { - switch (permission) { - case 'admin': return 'error'; - default: return 'success'; - } -}; - -// 获取权限标签 -const getPermissionLabel = (permission: string): string => { - switch (permission) { - case 'admin': return tm('permission.admin'); - default: return tm('permission.everyone'); - } -}; -</script> - -<template> - <v-dialog :model-value="show" @update:model-value="emit('update:show', $event)" max-width="500"> - <v-card v-if="command"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('dialogs.details.title') }}</v-card-title> - <v-card-text> - <v-list density="compact"> - <v-list-item> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.type') }}</v-list-item-title> - <v-list-item-subtitle> - <v-chip - :color="getTypeInfo(command.type).color" - size="small" - variant="tonal" - > - <v-icon start size="14">{{ getTypeInfo(command.type).icon }}</v-icon> - {{ getTypeInfo(command.type).text }} - </v-chip> - </v-list-item-subtitle> - </v-list-item> - <v-list-item> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.handler') }}</v-list-item-title> - <v-list-item-subtitle><code>{{ command.handler_name }}</code></v-list-item-subtitle> - </v-list-item> - <v-list-item> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.module') }}</v-list-item-title> - <v-list-item-subtitle><code>{{ command.module_path }}</code></v-list-item-subtitle> - </v-list-item> - <v-list-item> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.originalCommand') }}</v-list-item-title> - <v-list-item-subtitle><code>{{ command.original_command }}</code></v-list-item-subtitle> - </v-list-item> - <v-list-item> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.effectiveCommand') }}</v-list-item-title> - <v-list-item-subtitle><code>{{ command.effective_command }}</code></v-list-item-subtitle> - </v-list-item> - <v-list-item v-if="command.parent_signature"> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.parentGroup') }}</v-list-item-title> - <v-list-item-subtitle><code>{{ command.parent_signature }}</code></v-list-item-subtitle> - </v-list-item> - <v-list-item v-if="command.aliases.length > 0"> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.aliases') }}</v-list-item-title> - <v-list-item-subtitle> - <v-chip v-for="alias in command.aliases" :key="alias" size="small" class="mr-1"> - {{ alias }} - </v-chip> - </v-list-item-subtitle> - </v-list-item> - <v-list-item v-if="command.is_group && command.sub_commands?.length > 0"> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.subCommands') }}</v-list-item-title> - <v-list-item-subtitle> - <div class="d-flex flex-wrap ga-1 mt-1"> - <v-chip - v-for="sub in command.sub_commands" - :key="sub.handler_full_name" - size="small" - variant="outlined" - > - {{ sub.current_fragment }} - </v-chip> - </div> - </v-list-item-subtitle> - </v-list-item> - <v-list-item> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.permission') }}</v-list-item-title> - <v-list-item-subtitle> - <v-chip :color="getPermissionColor(command.permission)" size="small"> - {{ getPermissionLabel(command.permission) }} - </v-chip> - </v-list-item-subtitle> - </v-list-item> - <v-list-item v-if="command.has_conflict"> - <v-list-item-title class="font-weight-bold">{{ tm('dialogs.details.conflictStatus') }}</v-list-item-title> - <v-list-item-subtitle> - <v-chip color="warning" size="small">{{ tm('status.conflict') }}</v-chip> - </v-list-item-subtitle> - </v-list-item> - </v-list> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn color="primary" variant="text" @click="emit('update:show', false)"> - {{ t('core.actions.close') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<style scoped> -code { - background-color: rgba(var(--v-theme-primary), 0.1); - padding: 2px 6px; - border-radius: 4px; - font-size: 0.9em; -} -</style> diff --git a/legacy-dashboard/src/components/extension/componentPanel/components/RenameDialog.vue b/legacy-dashboard/src/components/extension/componentPanel/components/RenameDialog.vue deleted file mode 100644 index 14cc8278..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/components/RenameDialog.vue +++ /dev/null @@ -1,131 +0,0 @@ -<script setup lang="ts"> -import { computed, ref, watch } from 'vue'; -import { useModuleI18n } from '@/i18n/composables'; -import type { CommandItem } from '../types'; - -const { tm } = useModuleI18n('features/command'); - -// Props -const props = defineProps<{ - show: boolean; - command: CommandItem | null; - newName: string; - aliases: string[]; - loading: boolean; -}>(); - -// Emits -const emit = defineEmits<{ - (e: 'update:show', value: boolean): void; - (e: 'update:newName', value: string): void; - (e: 'update:aliases', value: string[]): void; - (e: 'confirm'): void; -}>(); - -const addAlias = () => { - emit('update:aliases', [...props.aliases, '']); -}; - -const removeAlias = (index: number) => { - const newAliases = [...props.aliases]; - newAliases.splice(index, 1); - emit('update:aliases', newAliases); -}; - -const updateAlias = (index: number, value: string) => { - const newAliases = [...props.aliases]; - newAliases[index] = value; - emit('update:aliases', newAliases); -}; - -const hasAliases = computed(() => (props.aliases || []).some(a => (a ?? '').toString().trim())); -const showAliasEditor = ref(false); -const aliasEditorEverOpened = ref(false); - -watch( - () => props.show, - (open) => { - if (!open) return; - // 如果已有别名则默认展开,否则默认收起 - showAliasEditor.value = hasAliases.value; - }, -); - -watch(showAliasEditor, (open) => { - if (open) aliasEditorEverOpened.value = true; -}); -</script> - -<template> - <v-dialog :model-value="show" @update:model-value="emit('update:show', $event)" max-width="500"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('dialogs.rename.title') }}</v-card-title> - <v-card-text> - <v-text-field - :model-value="newName" - @update:model-value="emit('update:newName', $event)" - :label="tm('dialogs.rename.newName')" - variant="outlined" - density="compact" - autofocus - class="mb-2" - /> - - <v-card variant="outlined" class="mt-2" elevation="0"> - <div - class="d-flex align-center justify-space-between px-4 py-3" - role="button" - tabindex="0" - @click="showAliasEditor = !showAliasEditor" - @keydown.enter.prevent="showAliasEditor = !showAliasEditor" - @keydown.space.prevent="showAliasEditor = !showAliasEditor" - > - <div class="text-subtitle-1">{{ tm('dialogs.rename.aliases') }}</div> - <v-icon size="20">{{ showAliasEditor ? 'mdi-chevron-up' : 'mdi-chevron-down' }}</v-icon> - </div> - <v-divider v-if="showAliasEditor" /> - <v-slide-y-transition> - <div v-if="aliasEditorEverOpened" v-show="showAliasEditor" class="px-4 py-3"> - <div v-for="(alias, index) in aliases" :key="index" class="d-flex align-center mb-2"> - <v-text-field - :model-value="alias" - @update:model-value="updateAlias(index, $event)" - variant="outlined" - density="compact" - hide-details - class="flex-grow-1 mr-2" - /> - <v-btn icon="mdi-delete" variant="text" color="error" density="compact" @click="removeAlias(index)" /> - </div> - <v-btn - prepend-icon="mdi-plus" - variant="tonal" - color="primary" - block - size="small" - class="mt-2" - @click="addAlias" - > - {{ tm('dialogs.rename.addAlias') }} - </v-btn> - </div> - </v-slide-y-transition> - </v-card> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn color="grey" variant="text" @click="emit('update:show', false)"> - {{ tm('dialogs.rename.cancel') }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="loading" - @click="emit('confirm')" - > - {{ tm('dialogs.rename.confirm') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> diff --git a/legacy-dashboard/src/components/extension/componentPanel/components/ToolTable.vue b/legacy-dashboard/src/components/extension/componentPanel/components/ToolTable.vue deleted file mode 100644 index 75e59f30..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/components/ToolTable.vue +++ /dev/null @@ -1,291 +0,0 @@ -<script setup lang="ts"> -import { computed } from 'vue'; -import { useModuleI18n } from '@/i18n/composables'; -import type { BuiltinToolConfigTag, ToolConfigCondition, ToolItem } from '../types'; - -const { tm: tmTool } = useModuleI18n('features/tooluse'); - -const props = defineProps<{ - items: ToolItem[]; - loading?: boolean; -}>(); - -const emit = defineEmits<{ - (e: 'toggle-tool', tool: ToolItem): void; - (e: 'update-permission', tool: ToolItem, permission: 'admin' | 'member'): void; -}>(); - -const toolHeaders = computed(() => [ - { title: tmTool('functionTools.title'), key: 'name', minWidth: '320px' }, - { title: tmTool('functionTools.description'), key: 'description' }, - { title: tmTool('functionTools.table.origin'), key: 'origin', sortable: false, width: '100px' }, - { title: tmTool('functionTools.table.originName'), key: 'origin_name', sortable: false, width: '140px' }, - { title: tmTool('functionTools.table.permission'), key: 'permission', sortable: false, width: '110px' }, - { title: tmTool('functionTools.table.actions'), key: 'actions', sortable: false, width: '100px' } -]); - -const parameterEntries = (tool: ToolItem) => Object.entries(tool.parameters?.properties || {}); - -const formatConfigValue = (value: unknown) => { - if (Array.isArray(value)) { - return value.map(item => String(item)).join(', '); - } - if (typeof value === 'boolean') { - return value ? 'true' : 'false'; - } - if (value === null || value === undefined || value === '') { - return '-'; - } - return String(value); -}; - -const formatCondition = (condition: ToolConfigCondition) => { - if (condition.message) { - return condition.message; - } - - switch (condition.operator) { - case 'truthy': - return tmTool('functionTools.configTags.conditions.truthy', { - key: condition.key - }); - case 'equals': - return tmTool('functionTools.configTags.conditions.equals', { - key: condition.key, - expected: formatConfigValue(condition.expected) - }); - case 'in': - return tmTool('functionTools.configTags.conditions.in', { - key: condition.key, - expected: formatConfigValue(condition.expected) - }); - default: - return tmTool('functionTools.configTags.conditions.fallback', { - key: condition.key, - actual: formatConfigValue(condition.actual) - }); - } -}; - -const enabledConfigTags = (tool: ToolItem): BuiltinToolConfigTag[] => { - if (tool.origin !== 'builtin') return []; - return (tool.builtin_config_tags || []).filter(tag => tag.enabled); -}; - -const getPermissionColor = (permission?: string): string => { - switch (permission) { - case 'admin': - return 'error'; - default: - return 'success'; - } -}; - -const getPermissionLabel = (permission?: string): string => { - switch (permission) { - case 'admin': - return tmTool('functionTools.table.permissionAdmin'); - default: - return tmTool('functionTools.table.permissionEveryone'); - } -}; -</script> - -<template> - <v-card class="rounded-lg overflow-hidden elevation-1"> - <v-data-table - :headers="toolHeaders" - :items="items" - item-value="name" - hover - show-expand - class="tool-table" - :loading="props.loading" - > - <template #item.name="{ item }"> - <div class="py-2"> - <div class="d-flex flex-wrap align-center ga-1"> - <div class="tool-name text-body-2 font-weight-medium">{{ item.name }}</div> - <v-tooltip - v-for="tag in enabledConfigTags(item)" - :key="`${item.name}-${tag.conf_id}`" - location="top" - > - <template #activator="{ props: tooltipProps }"> - <v-chip - v-bind="tooltipProps" - size="x-small" - variant="tonal" - color="secondary" - class="text-caption font-weight-medium" - > - {{ tag.conf_name }} - </v-chip> - </template> - - <div class="tool-config-tooltip"> - <div class="text-body-2 font-weight-medium mb-2"> - {{ tmTool('functionTools.configTags.tooltipTitle', { config: tag.conf_name }) }} - </div> - <div - v-for="(condition, index) in tag.matched_conditions" - :key="`${tag.conf_id}-${index}-${condition.key}`" - class="text-body-2 text-medium-emphasis mb-1" - > - {{ formatCondition(condition) }} - </div> - </div> - </v-tooltip> - </div> - </div> - </template> - - <template #item.description="{ item }"> - <div class="text-body-2 text-medium-emphasis" style="max-width: 320px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" :title="item.description"> - {{ item.description || '-' }} - </div> - </template> - - <template #item.origin="{ item }"> - <v-chip size="x-small" variant="tonal" color="info" class="text-caption font-weight-medium"> - {{ item.origin || '-' }} - </v-chip> - </template> - - <template #item.origin_name="{ item }"> - <div class="text-body-2 text-medium-emphasis" style="max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" :title="item.origin_name"> - {{ item.origin_name || '-' }} - </div> - </template> - - <template #item.permission="{ item }"> - <!-- Builtin tools: non-clickable badge --> - <v-chip - v-if="item.origin === 'builtin'" - size="small" - variant="tonal" - class="font-weight-medium text-medium-emphasis" - > - {{ tmTool('functionTools.table.permissionBuiltin') }} - </v-chip> - <!-- Other tools: clickable dropdown --> - <v-menu v-else location="bottom"> - <template v-slot:activator="{ props: menuProps }"> - <v-chip - v-bind="menuProps" - :color="getPermissionColor(item.permission)" - size="small" - class="font-weight-medium cursor-pointer" - link - > - {{ getPermissionLabel(item.permission) }} - <v-icon end size="14">mdi-chevron-down</v-icon> - </v-chip> - </template> - <v-list density="compact"> - <v-list-item - :value="'member'" - @click="emit('update-permission', item, 'member')" - :active="item.permission !== 'admin'" - > - <v-list-item-title>{{ tmTool('functionTools.table.permissionEveryone') }}</v-list-item-title> - </v-list-item> - <v-list-item - :value="'admin'" - @click="emit('update-permission', item, 'admin')" - :active="item.permission === 'admin'" - > - <v-list-item-title>{{ tmTool('functionTools.table.permissionAdmin') }}</v-list-item-title> - </v-list-item> - </v-list> - </v-menu> - </template> - - <template #item.actions="{ item }"> - <span v-if="item.readonly" class="text-medium-emphasis">-</span> - <v-switch - v-else - :model-value="item.active" - color="primary" - density="compact" - hide-details - inset - @update:model-value="emit('toggle-tool', item)" - /> - </template> - - <template #no-data> - <div class="text-center pa-8"> - <v-icon size="64" color="info" class="mb-4">mdi-function-variant</v-icon> - <div class="text-h5 mb-2">{{ tmTool('functionTools.empty') }}</div> - </div> - </template> - - <template #expanded-row="{ item }"> - <td :colspan="toolHeaders.length + 1" class="pa-4"> - <div class="d-flex align-start ga-4"> - <v-icon size="20" color="primary">mdi-code-json</v-icon> - <div class="flex-1"> - <div class="text-subtitle-2 font-weight-medium mb-2">{{ tmTool('functionTools.parameters') }}</div> - <div v-if="parameterEntries(item).length === 0" class="text-caption text-medium-emphasis"> - {{ tmTool('functionTools.noParameters') }} - </div> - <v-table - v-else - density="compact" - class="param-table" - > - <thead> - <tr> - <th class="text-left text-caption text-medium-emphasis">{{ tmTool('functionTools.table.paramName') }}</th> - <th class="text-left text-caption text-medium-emphasis" style="width: 140px;">{{ tmTool('functionTools.table.type') }}</th> - <th class="text-left text-caption text-medium-emphasis">{{ tmTool('functionTools.table.description') }}</th> - </tr> - </thead> - <tbody> - <tr v-for="([paramName, param]) in parameterEntries(item)" :key="paramName"> - <td class="font-weight-medium text-body-2">{{ paramName }}</td> - <td class="text-body-2"> - <v-chip size="x-small" color="primary" class="text-caption"> - {{ param?.type || '-' }} - </v-chip> - </td> - <td class="text-body-2 text-medium-emphasis">{{ param?.description || '-' }}</td> - </tr> - </tbody> - </v-table> - </div> - </div> - </td> - </template> - </v-data-table> - </v-card> -</template> - -<style scoped> -.param-table { - border: 1px solid rgba(0, 0, 0, 0.06); - border-radius: 8px; -} - -.tool-table :deep(.v-data-table__td) { - vertical-align: middle; -} - -.tool-name { - font-size: 0.9rem; - line-height: 1.35; -} - -.tool-config-tooltip { - max-width: 360px; - padding: 4px 0; - color: rgba(255, 255, 255, 0.92); -} - -.tool-config-tooltip :deep(.text-body-2), -.tool-config-tooltip :deep(.text-medium-emphasis), -.tool-config-tooltip :deep(.font-weight-medium) { - color: inherit !important; -} -</style> diff --git a/legacy-dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts b/legacy-dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts deleted file mode 100644 index 607372c7..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/composables/useCommandActions.ts +++ /dev/null @@ -1,202 +0,0 @@ -/** - * 指令操作方法 Composable - */ -import { reactive } from 'vue'; -import { commandApi } from '@/api/v1'; -import type { CommandItem, RenameDialogState, DetailsDialogState, TypeInfo, StatusInfo } from '../types'; - -export function useCommandActions( - toast: (message: string, color?: string) => void, - fetchCommands: () => Promise<void> -) { - // 重命名对话框状态 - const renameDialog = reactive<RenameDialogState>({ - show: false, - command: null, - newName: '', - aliases: [], - loading: false - }); - - // 详情对话框状态 - const detailsDialog = reactive<DetailsDialogState>({ - show: false, - command: null - }); - - /** - * 切换指令启用/禁用状态 - */ - const toggleCommand = async ( - cmd: CommandItem, - successMessage: string, - errorMessage: string - ) => { - try { - const res = await commandApi.update(cmd.handler_full_name, { - enabled: !cmd.enabled - }); - if (res.data.status === 'ok') { - toast(successMessage, 'success'); - await fetchCommands(); - } else { - toast(res.data.message || errorMessage, 'error'); - } - } catch (err: any) { - toast(err?.message || errorMessage, 'error'); - } - }; - - /** - * 打开重命名对话框 - */ - const openRenameDialog = (cmd: CommandItem) => { - renameDialog.command = cmd; - renameDialog.newName = cmd.current_fragment || ''; - renameDialog.aliases = [...(cmd.aliases || [])]; - renameDialog.show = true; - }; - - /** - * 确认重命名 - */ - const confirmRename = async (successMessage: string, errorMessage: string) => { - if (!renameDialog.command || !renameDialog.newName.trim()) return; - - renameDialog.loading = true; - try { - const res = await commandApi.update(renameDialog.command.handler_full_name, { - alias: renameDialog.newName.trim(), - aliases: renameDialog.aliases.filter(a => a.trim()) - }); - if (res.data.status === 'ok') { - toast(successMessage, 'success'); - renameDialog.show = false; - await fetchCommands(); - } else { - toast(res.data.message || errorMessage, 'error'); - } - } catch (err: any) { - toast(err?.message || errorMessage, 'error'); - } finally { - renameDialog.loading = false; - } - }; - - /** - * 打开详情对话框 - */ - const openDetailsDialog = (cmd: CommandItem) => { - detailsDialog.command = cmd; - detailsDialog.show = true; - }; - - /** - * 获取类型显示信息 - */ - const getTypeInfo = (type: string, translations: { group: string; subCommand: string; command: string }): TypeInfo => { - switch (type) { - case 'group': - return { text: translations.group, color: 'info', icon: 'mdi-folder-outline' }; - case 'sub_command': - return { text: translations.subCommand, color: 'secondary', icon: 'mdi-subdirectory-arrow-right' }; - default: - return { text: translations.command, color: 'primary', icon: 'mdi-console-line' }; - } - }; - - /** - * 获取权限颜色 - */ - const getPermissionColor = (permission: string): string => { - switch (permission) { - case 'admin': return 'error'; - default: return 'success'; - } - }; - - /** - * 获取权限标签 - */ - const getPermissionLabel = (permission: string, translations: { admin: string; everyone: string }): string => { - switch (permission) { - case 'admin': return translations.admin; - default: return translations.everyone; - } - }; - - /** - * 获取状态显示信息 - */ - const getStatusInfo = ( - cmd: CommandItem, - translations: { conflict: string; enabled: string; disabled: string } - ): StatusInfo => { - if (cmd.has_conflict) { - return { text: translations.conflict, color: 'warning', variant: 'flat' }; - } - if (cmd.enabled) { - return { text: translations.enabled, color: 'success', variant: 'flat' }; - } - return { text: translations.disabled, color: 'error', variant: 'outlined' }; - }; - - /** - * 获取表格行属性(用于冲突高亮和子指令样式) - */ - const getRowProps = ({ item }: { item: CommandItem }) => { - const classes: string[] = []; - if (item.has_conflict) { - classes.push('conflict-row'); - } - if (item.type === 'sub_command') { - classes.push('sub-command-row'); - } - if (item.is_group) { - classes.push('group-row'); - } - return classes.length > 0 ? { class: classes.join(' ') } : {}; - }; - - /** - * 更新指令权限 - */ - const updatePermission = async ( - cmd: CommandItem, - permission: 'admin' | 'member', - successMessage: string, - errorMessage: string - ) => { - try { - const res = await commandApi.update(cmd.handler_full_name, { - permission_group: permission - }); - if (res.data.status === 'ok') { - toast(successMessage, 'success'); - await fetchCommands(); - } else { - toast(res.data.message || errorMessage, 'error'); - } - } catch (err: any) { - toast(err?.message || errorMessage, 'error'); - } - }; - - return { - // 状态 - renameDialog, - detailsDialog, - - // 方法 - toggleCommand, - updatePermission, - openRenameDialog, - confirmRename, - openDetailsDialog, - getTypeInfo, - getPermissionColor, - getPermissionLabel, - getStatusInfo, - getRowProps - }; -} diff --git a/legacy-dashboard/src/components/extension/componentPanel/composables/useCommandFilters.ts b/legacy-dashboard/src/components/extension/componentPanel/composables/useCommandFilters.ts deleted file mode 100644 index 414e8437..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/composables/useCommandFilters.ts +++ /dev/null @@ -1,187 +0,0 @@ -/** - * 指令过滤逻辑 Composable - */ -import { ref, computed, type Ref } from 'vue'; -import type { CommandItem, FilterState } from '../types'; -import { normalizeTextInput } from '@/utils/inputValue'; - -export function useCommandFilters(commands: Ref<CommandItem[]>) { - // 过滤状态 - const searchQuery = ref(''); - const pluginFilter = ref('all'); - const permissionFilter = ref('all'); - const statusFilter = ref('all'); - const typeFilter = ref('all'); - const showSystemPlugins = ref(false); - - // 展开的指令组 - const expandedGroups = ref<Set<string>>(new Set()); - - /** - * 检查是否有涉及系统插件的冲突 - */ - const hasSystemPluginConflict = computed(() => { - return commands.value.some(cmd => cmd.has_conflict && cmd.reserved); - }); - - /** - * 实际是否显示系统插件(如果有系统插件冲突则强制显示) - */ - const effectiveShowSystemPlugins = computed(() => { - return showSystemPlugins.value || hasSystemPluginConflict.value; - }); - - /** - * 获取可用的插件列表(用于过滤下拉框) - */ - const availablePlugins = computed(() => { - const plugins = new Set( - commands.value - .filter(cmd => effectiveShowSystemPlugins.value || !cmd.reserved) - .map(cmd => cmd.plugin) - ); - return Array.from(plugins).sort(); - }); - - /** - * 检查指令是否匹配过滤条件 - */ - const matchesFilters = (cmd: CommandItem, query: string): boolean => { - // 系统插件过滤(除非显示系统插件) - if (!effectiveShowSystemPlugins.value && cmd.reserved) { - return false; - } - - // 搜索过滤 - if (query) { - const matchesSearch = - cmd.effective_command?.toLowerCase().includes(query) || - cmd.description?.toLowerCase().includes(query) || - cmd.plugin?.toLowerCase().includes(query); - if (!matchesSearch) return false; - } - - // 插件过滤 - if (pluginFilter.value !== 'all' && cmd.plugin !== pluginFilter.value) { - return false; - } - - // 权限过滤 - if (permissionFilter.value !== 'all') { - if (permissionFilter.value === 'everyone') { - if (cmd.permission !== 'everyone' && cmd.permission !== 'member') return false; - } else if (cmd.permission !== permissionFilter.value) { - return false; - } - } - - // 状态过滤 - if (statusFilter.value !== 'all') { - if (statusFilter.value === 'enabled' && !cmd.enabled) return false; - if (statusFilter.value === 'disabled' && cmd.enabled) return false; - if (statusFilter.value === 'conflict' && !cmd.has_conflict) return false; - } - - // 类型过滤 - if (typeFilter.value !== 'all') { - if (typeFilter.value === 'group' && cmd.type !== 'group') return false; - if (typeFilter.value === 'command' && cmd.type !== 'command') return false; - if (typeFilter.value === 'sub_command' && cmd.type !== 'sub_command') return false; - } - - return true; - }; - - /** - * 过滤后的指令列表(支持层级结构) - */ - const filteredCommands = computed(() => { - const query = normalizeTextInput(searchQuery.value).toLowerCase(); - const conflictCmds: CommandItem[] = []; - const normalCmds: CommandItem[] = []; - - for (const cmd of commands.value) { - // 对于指令组,检查组本身或子指令是否匹配 - if (cmd.is_group) { - const groupMatches = matchesFilters(cmd, query); - const matchingSubCmds = (cmd.sub_commands || []).filter(sub => matchesFilters(sub, query)); - - // 如果组匹配或有匹配的子指令,则包含它 - if (groupMatches || matchingSubCmds.length > 0) { - if (cmd.has_conflict) { - conflictCmds.push(cmd); - } else { - normalCmds.push(cmd); - } - - // 如果组已展开,添加匹配的子指令 - if (expandedGroups.value.has(cmd.handler_full_name)) { - const subsToShow = query ? matchingSubCmds : (cmd.sub_commands || []); - for (const sub of subsToShow) { - if (sub.has_conflict) { - conflictCmds.push(sub); - } else { - normalCmds.push(sub); - } - } - } - } - } else if (cmd.type !== 'sub_command') { - // 普通指令(子指令通过组处理) - if (matchesFilters(cmd, query)) { - if (cmd.has_conflict) { - conflictCmds.push(cmd); - } else { - normalCmds.push(cmd); - } - } - } - } - - // 按 effective_command 排序冲突指令,使其分组在一起 - conflictCmds.sort((a, b) => (a.effective_command || '').localeCompare(b.effective_command || '')); - - return [...conflictCmds, ...normalCmds]; - }); - - /** - * 切换指令组的展开/折叠状态 - */ - const toggleGroupExpand = (cmd: CommandItem) => { - if (!cmd.is_group) return; - if (expandedGroups.value.has(cmd.handler_full_name)) { - expandedGroups.value.delete(cmd.handler_full_name); - } else { - expandedGroups.value.add(cmd.handler_full_name); - } - }; - - /** - * 检查指令组是否已展开 - */ - const isGroupExpanded = (cmd: CommandItem): boolean => { - return expandedGroups.value.has(cmd.handler_full_name); - }; - - return { - // 状态 - searchQuery, - pluginFilter, - permissionFilter, - statusFilter, - typeFilter, - showSystemPlugins, - expandedGroups, - - // 计算属性 - hasSystemPluginConflict, - effectiveShowSystemPlugins, - availablePlugins, - filteredCommands, - - // 方法 - matchesFilters, - toggleGroupExpand, - isGroupExpanded - }; -} diff --git a/legacy-dashboard/src/components/extension/componentPanel/composables/useComponentData.ts b/legacy-dashboard/src/components/extension/componentPanel/composables/useComponentData.ts deleted file mode 100644 index 588461b8..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/composables/useComponentData.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * 指令数据管理 Composable - */ -import { ref, reactive } from 'vue'; -import { commandApi, toolApi } from '@/api/v1'; -import type { CommandItem, CommandSummary, SnackbarState, ToolItem } from '../types'; - -export function useComponentData() { - const loading = ref(false); - const commands = ref<CommandItem[]>([]); - const tools = ref<ToolItem[]>([]); - const toolsLoading = ref(false); - const summary = reactive<CommandSummary>({ - disabled: 0, - conflicts: 0 - }); - - const snackbar = reactive<SnackbarState>({ - show: false, - message: '', - color: 'success' - }); - - /** - * 显示 Toast 消息 - */ - const toast = (message: string, color: string = 'success') => { - snackbar.message = message; - snackbar.color = color; - snackbar.show = true; - }; - - /** - * 获取指令列表 - */ - const fetchCommands = async (errorMessage: string) => { - loading.value = true; - try { - const res = await commandApi.list(); - if (res.data.status === 'ok') { - commands.value = res.data.data.items || []; - const s = res.data.data.summary || {}; - summary.disabled = s.disabled || 0; - summary.conflicts = s.conflicts || 0; - } else { - toast(res.data.message || errorMessage, 'error'); - } - } catch (err: any) { - toast(err?.message || errorMessage, 'error'); - } finally { - loading.value = false; - } - }; - - const fetchTools = async (errorMessage: string) => { - toolsLoading.value = true; - try { - const res = await toolApi.list(); - if (res.data.status === 'ok') { - tools.value = res.data.data || []; - } else { - toast(res.data.message || errorMessage, 'error'); - } - } catch (err: any) { - toast(err?.message || errorMessage, 'error'); - } finally { - toolsLoading.value = false; - } - }; - - return { - loading, - commands, - tools, - toolsLoading, - summary, - snackbar, - toast, - fetchCommands, - fetchTools - }; -} diff --git a/legacy-dashboard/src/components/extension/componentPanel/composables/useToolActions.ts b/legacy-dashboard/src/components/extension/componentPanel/composables/useToolActions.ts deleted file mode 100644 index ab07ed49..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/composables/useToolActions.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Tool actions composable - */ -import { ref, computed, type Ref } from 'vue'; -import { toolApi } from '@/api/v1'; -import { normalizeTextInput } from '@/utils/inputValue'; -import type { ToolItem, ToolSummary } from '../types'; - -export function useToolActions( - tools: Ref<ToolItem[]>, - toast: (message: string, color?: string) => void -) { - const toolSearch = ref(''); - const showBuiltinTools = ref(true); - - const filteredTools = computed(() => { - let result = tools.value; - - // Filter builtin tools - if (!showBuiltinTools.value) { - result = result.filter(tool => tool.origin !== 'builtin'); - } - - // Filter by search query - const query = normalizeTextInput(toolSearch.value).trim().toLowerCase(); - if (query) { - result = result.filter( - tool => - tool.name?.toLowerCase().includes(query) || - tool.description?.toLowerCase().includes(query) - ); - } - - return result; - }); - - const toolSummary = computed<ToolSummary>(() => { - const all = tools.value; - return { - total: all.length, - active: all.filter(t => t.active).length, - inactive: all.filter(t => !t.active).length, - }; - }); - - /** - * Toggle a tool's active state (optimistic update). - */ - const toggleTool = async ( - tool: ToolItem, - readonlyMessage: string, - successMessage: string, - errorMessage: string - ) => { - if (tool.readonly) { - toast(readonlyMessage, 'info'); - return; - } - const previous = tool.active; - tool.active = !tool.active; - try { - const res = await toolApi.setEnabled(tool.name, tool.active); - if (res.data.status === 'ok') { - toast(res.data.message || successMessage); - } else { - tool.active = previous; - toast(res.data.message || errorMessage, 'error'); - } - } catch (error: any) { - tool.active = previous; - toast( - error?.response?.data?.message || - error?.message || - errorMessage, - 'error' - ); - } - }; - - /** - * Update a tool's permission level. - */ - const updateToolPermission = async ( - tool: ToolItem, - permission: 'admin' | 'member', - successMessage: string, - builtinMessage: string, - errorMessage: string - ) => { - if (tool.origin === 'builtin') { - toast(builtinMessage, 'info'); - return; - } - try { - const res = await toolApi.setPermission(tool.name, permission); - if (res.data.status === 'ok') { - tool.permission = permission; - tool.permission_configured = true; - toast(successMessage, 'success'); - } else { - toast(res.data.message || errorMessage, 'error'); - } - } catch (error: any) { - toast( - error?.response?.data?.message || - error?.message || - errorMessage, - 'error' - ); - } - }; - - return { - toolSearch, - showBuiltinTools, - filteredTools, - toolSummary, - toggleTool, - updateToolPermission, - }; -} diff --git a/legacy-dashboard/src/components/extension/componentPanel/index.vue b/legacy-dashboard/src/components/extension/componentPanel/index.vue deleted file mode 100644 index 97e7cab8..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/index.vue +++ /dev/null @@ -1,330 +0,0 @@ -<script setup lang="ts"> -/** - * 组件管理页面 - 主入口 - * - * 模块化结构: - * - types.ts: 类型定义 - * - composables/useComponentData.ts: 数据获取和状态管理 - * - composables/useCommandFilters.ts: 过滤逻辑 - * - composables/useCommandActions.ts: 操作方法 - * - components/CommandFilters.vue: 过滤器组件 - * - components/CommandTable.vue: 表格组件 - * - components/RenameDialog.vue: 重命名对话框 - * - components/DetailsDialog.vue: 详情对话框 - */ -import { onMounted, ref, watch } from 'vue'; -import { useModuleI18n } from '@/i18n/composables'; - -// Composables -import { useComponentData } from './composables/useComponentData'; -import { useCommandFilters } from './composables/useCommandFilters'; -import { useCommandActions } from './composables/useCommandActions'; -import { useToolActions } from './composables/useToolActions'; - -// Components -import CommandFilters from './components/CommandFilters.vue'; -import CommandTable from './components/CommandTable.vue'; -import ToolTable from './components/ToolTable.vue'; -import RenameDialog from './components/RenameDialog.vue'; -import DetailsDialog from './components/DetailsDialog.vue'; - -// Types -import type { CommandItem, ToolItem } from './types'; - -defineOptions({ name: 'ComponentPanel' }); -const props = withDefaults(defineProps<{ active?: boolean }>(), { - active: true -}); - -const { tm } = useModuleI18n('features/command'); -const { tm: tmTool } = useModuleI18n('features/tooluse'); - -const viewMode = ref<'commands' | 'tools'>('commands'); - -// 数据管理 -const { - loading, - commands, - tools, - toolsLoading, - summary, - snackbar, - toast, - fetchCommands, - fetchTools -} = useComponentData(); - -// 过滤逻辑 -const { - searchQuery, - pluginFilter, - permissionFilter, - statusFilter, - typeFilter, - showSystemPlugins, - expandedGroups, - hasSystemPluginConflict, - effectiveShowSystemPlugins, - availablePlugins, - filteredCommands, - toggleGroupExpand -} = useCommandFilters(commands); - -// 操作方法 -const { - renameDialog, - detailsDialog, - toggleCommand, - updatePermission, - openRenameDialog, - confirmRename, - openDetailsDialog -} = useCommandActions(toast, () => fetchCommands(tm('messages.loadFailed'))); - -// 工具操作方法 -const { - toolSearch, - showBuiltinTools, - filteredTools, - toolSummary, - toggleTool, - updateToolPermission, -} = useToolActions(tools, toast); - -// 处理切换指令状态 -const handleToggleCommand = async (cmd: CommandItem) => { - await toggleCommand(cmd, tm('messages.toggleSuccess'), tm('messages.toggleFailed')); -}; - -const handleUpdatePermission = async (cmd: CommandItem, permission: 'admin' | 'member') => { - await updatePermission(cmd, permission, tm('messages.updateSuccess'), tm('messages.updateFailed')); -}; - -const handleToggleTool = async (tool: ToolItem) => { - await toggleTool(tool, tmTool('messages.toggleToolReadonly'), tmTool('messages.toggleToolSuccess'), tmTool('messages.toggleToolError', { error: '' })); -}; - -const handleUpdateToolPermission = async (tool: ToolItem, permission: 'admin' | 'member') => { - await updateToolPermission(tool, permission, tmTool('messages.updateToolPermissionSuccess', { name: tool.name }), tmTool('messages.updateToolPermissionBuiltin'), tmTool('messages.updateToolPermissionFailed')); -}; - -// 处理确认重命名 -const handleConfirmRename = async () => { - await confirmRename(tm('messages.renameSuccess'), tm('messages.renameFailed')); -}; - -// 生命周期 -onMounted(async () => { - await Promise.all([ - fetchCommands(tm('messages.loadFailed')), - fetchTools(tmTool('messages.getToolsError', { error: '' })) - ]); -}); - -watch(() => props.active, async (isActive) => { - if (!isActive) return; - if (viewMode.value === 'commands') { - await fetchCommands(tm('messages.loadFailed')); - } else { - await fetchTools(tmTool('messages.getToolsError', { error: '' })); - } -}); - -watch(viewMode, async (mode) => { - if (mode === 'commands') { - await fetchCommands(tm('messages.loadFailed')); - } else { - await fetchTools(tmTool('messages.getToolsError', { error: '' })); - } -}); -</script> - -<template> - <v-row> - <v-col cols="12"> - <v-card variant="flat" style="background-color: transparent"> - <v-card-text style="padding: 20px 12px; padding-top: 0px;"> - <div class="d-flex justify-space-between align-center mb-6 flex-wrap ga-3"> - <v-btn-toggle v-model="viewMode" color="primary" variant="outlined" density="comfortable" mandatory> - <v-btn value="commands"> - <v-icon size="18" class="mr-1">mdi-console-line</v-icon> - {{ tm('type.command') }} - </v-btn> - <v-btn value="tools"> - <v-icon size="18" class="mr-1">mdi-function-variant</v-icon> - {{ tmTool('functionTools.title') }} - </v-btn> - </v-btn-toggle> - <v-progress-linear - v-if="viewMode === 'commands' && loading" - indeterminate - color="primary" - style="max-width: 220px; flex: 1;" - /> - <v-progress-linear - v-else-if="viewMode === 'tools' && toolsLoading" - indeterminate - color="primary" - style="max-width: 220px; flex: 1;" - /> - </div> - - <div v-if="viewMode === 'commands'"> - <CommandFilters - :plugin-filter="pluginFilter" - @update:plugin-filter="pluginFilter = $event" - :type-filter="typeFilter" - @update:type-filter="typeFilter = $event" - :permission-filter="permissionFilter" - @update:permission-filter="permissionFilter = $event" - :status-filter="statusFilter" - @update:status-filter="statusFilter = $event" - :show-system-plugins="showSystemPlugins" - @update:show-system-plugins="showSystemPlugins = $event" - :search-query="searchQuery" - @update:search-query="searchQuery = $event" - :available-plugins="availablePlugins" - :has-system-plugin-conflict="hasSystemPluginConflict" - :effective-show-system-plugins="effectiveShowSystemPlugins" - > - <template #stats> - <div class="d-flex align-center"> - <v-icon size="18" color="primary" class="mr-1">mdi-console-line</v-icon> - <span class="text-body-2 text-medium-emphasis mr-1">{{ tm('summary.total') }}:</span> - <span class="text-body-1 font-weight-bold text-primary">{{ filteredCommands.length }}</span> - </div> - <v-divider vertical class="mx-1" style="height: 20px;" /> - <div class="d-flex align-center"> - <v-icon size="18" color="error" class="mr-1">mdi-close-circle-outline</v-icon> - <span class="text-body-2 text-medium-emphasis mr-1">{{ tm('summary.disabled') }}:</span> - <span class="text-body-1 font-weight-bold text-error">{{ summary.disabled }}</span> - </div> - </template> - </CommandFilters> - - <v-alert - v-if="summary.conflicts > 0" - type="error" - variant="tonal" - class="mb-4" - prominent - border="start" - > - <template v-slot:prepend> - <v-icon size="28">mdi-alert-circle</v-icon> - </template> - <v-alert-title class="text-subtitle-1 font-weight-bold"> - {{ tm('conflictAlert.title') }} - </v-alert-title> - <div class="text-body-2 mt-1"> - {{ tm('conflictAlert.description', { count: summary.conflicts }) }} - </div> - <div class="text-body-2 mt-2"> - <v-icon size="16" class="mr-1">mdi-lightbulb-outline</v-icon> - {{ tm('conflictAlert.hint') }} - </div> - </v-alert> - - <CommandTable - :items="filteredCommands" - :expanded-groups="expandedGroups" - :loading="loading" - @toggle-expand="toggleGroupExpand" - @toggle-command="handleToggleCommand" - @rename="openRenameDialog" - @view-details="openDetailsDialog" - @update-permission="handleUpdatePermission" - /> - </div> - - <div v-else> - <div class="d-flex flex-wrap align-center ga-4 mb-4"> - <div style="min-width: 240px; max-width: 380px; flex: 1;"> - <v-text-field - v-model="toolSearch" - prepend-inner-icon="mdi-magnify" - :label="tmTool('functionTools.search')" - variant="outlined" - density="compact" - hide-details - clearable - /> - </div> - - <div class="d-flex align-center ga-4"> - <div class="d-flex align-center"> - <v-icon size="18" color="primary" class="mr-1">mdi-function-variant</v-icon> - <span class="text-body-2 text-medium-emphasis mr-1">{{ tmTool('functionTools.summary.total') }}:</span> - <span class="text-body-1 font-weight-bold text-primary">{{ toolSummary.total }}</span> - </div> - <v-divider vertical class="mx-1" style="height: 20px;" /> - <div class="d-flex align-center"> - <v-icon size="18" color="success" class="mr-1">mdi-check-circle-outline</v-icon> - <span class="text-body-2 text-medium-emphasis mr-1">{{ tmTool('functionTools.summary.active') }}:</span> - <span class="text-body-1 font-weight-bold text-success">{{ toolSummary.active }}</span> - </div> - <v-divider vertical class="mx-1" style="height: 20px;" /> - <div class="d-flex align-center"> - <v-icon size="18" color="error" class="mr-1">mdi-close-circle-outline</v-icon> - <span class="text-body-2 text-medium-emphasis mr-1">{{ tmTool('functionTools.summary.inactive') }}:</span> - <span class="text-body-1 font-weight-bold text-error">{{ toolSummary.inactive }}</span> - </div> - - <v-divider vertical class="mx-1" style="height: 20px;" /> - <v-checkbox - v-model="showBuiltinTools" - :label="tmTool('functionTools.filter.showBuiltin')" - density="compact" - hide-details - class="builtin-tools-checkbox" - /> - </div> - </div> - - <ToolTable - :items="filteredTools" - :loading="toolsLoading" - @toggle-tool="handleToggleTool" - @update-permission="handleUpdateToolPermission" - /> - </div> - </v-card-text> - </v-card> - </v-col> - </v-row> - - <!-- 重命名对话框 --> - <RenameDialog - :show="renameDialog.show" - @update:show="renameDialog.show = $event" - :new-name="renameDialog.newName" - @update:new-name="renameDialog.newName = $event" - :aliases="renameDialog.aliases" - @update:aliases="renameDialog.aliases = $event" - :command="renameDialog.command" - :loading="renameDialog.loading" - @confirm="handleConfirmRename" - /> - - <!-- 详情对话框 --> - <DetailsDialog - :show="detailsDialog.show" - @update:show="detailsDialog.show = $event" - :command="detailsDialog.command" - /> - - <!-- Snackbar --> - <v-snackbar :timeout="2000" elevation="6" :color="snackbar.color" v-model="snackbar.show"> - {{ snackbar.message }} - </v-snackbar> -</template> - -<style scoped> -.builtin-tools-checkbox { - flex: none; -} - -.builtin-tools-checkbox :deep(.v-selection-control) { - min-height: auto; -} -</style> diff --git a/legacy-dashboard/src/components/extension/componentPanel/types.ts b/legacy-dashboard/src/components/extension/componentPanel/types.ts deleted file mode 100644 index cb96f33a..00000000 --- a/legacy-dashboard/src/components/extension/componentPanel/types.ts +++ /dev/null @@ -1,133 +0,0 @@ -/** - * 指令管理模块 - 类型定义 - */ - -/** 指令项接口 */ -export interface CommandItem { - handler_full_name: string; - handler_name: string; - plugin: string; - plugin_display_name: string | null; - module_path: string; - description: string; - type: CommandType; - parent_signature: string; - parent_group_handler: string; - original_command: string; - current_fragment: string; - effective_command: string; - aliases: string[]; - permission: PermissionType; - enabled: boolean; - is_group: boolean; - has_conflict: boolean; - reserved: boolean; - sub_commands: CommandItem[]; -} - -/** 指令类型 */ -export type CommandType = 'command' | 'group' | 'sub_command'; - -/** 权限类型 */ -export type PermissionType = 'admin' | 'everyone' | 'member'; - -/** 指令摘要统计 */ -export interface CommandSummary { - disabled: number; - conflicts: number; -} - -/** 工具摘要统计 */ -export interface ToolSummary { - total: number; - active: number; - inactive: number; -} - -/** 过滤器状态 */ -export interface FilterState { - searchQuery: string; - pluginFilter: string; - permissionFilter: string; - statusFilter: string; - typeFilter: string; - showSystemPlugins: boolean; -} - -/** 重命名对话框状态 */ -export interface RenameDialogState { - show: boolean; - command: CommandItem | null; - newName: string; - aliases: string[]; - loading: boolean; -} - -/** 详情对话框状态 */ -export interface DetailsDialogState { - show: boolean; - command: CommandItem | null; -} - -/** Toast 消息状态 */ -export interface SnackbarState { - show: boolean; - message: string; - color: string; -} - -/** 类型信息展示 */ -export interface TypeInfo { - text: string; - color: string; - icon: string; -} - -/** 状态信息展示 */ -export interface StatusInfo { - text: string; - color: string; - variant: 'flat' | 'outlined' | 'text' | 'elevated' | 'tonal' | 'plain'; -} - -/** MCP/函数工具参数定义 */ -export interface ToolParameter { - type?: string; - description?: string; -} - -export interface ToolConfigCondition { - key: string; - operator: 'truthy' | 'equals' | 'in' | 'custom' | string; - expected?: unknown; - actual?: unknown; - matched: boolean; - message?: string | null; -} - -export interface BuiltinToolConfigTag { - conf_id: string; - conf_name: string; - enabled: boolean; - matched_conditions: ToolConfigCondition[]; - failed_conditions: ToolConfigCondition[]; -} - -/** MCP/函数工具对象 */ -export interface ToolItem { - name: string; - description: string; - active: boolean; - readonly?: boolean; - parameters?: { - properties?: Record<string, ToolParameter>; - }; - origin?: string; - origin_name?: string; - builtin_config_statuses?: BuiltinToolConfigTag[]; - builtin_config_tags?: BuiltinToolConfigTag[]; - /** Per-tool permission level ("admin" | "member"). Builtin tools omit this. */ - permission?: 'admin' | 'member'; - /** True when permission was explicitly configured rather than a fallback default. */ - permission_configured?: boolean; -} diff --git a/legacy-dashboard/src/components/folder/BaseCreateFolderDialog.vue b/legacy-dashboard/src/components/folder/BaseCreateFolderDialog.vue deleted file mode 100644 index 46d55754..00000000 --- a/legacy-dashboard/src/components/folder/BaseCreateFolderDialog.vue +++ /dev/null @@ -1,132 +0,0 @@ -<template> - <v-dialog v-model="showDialog" max-width="450px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - <v-icon class="mr-2">mdi-folder-plus</v-icon> - {{ labels.title }} - </v-card-title> - <v-card-text> - <v-form ref="form" v-model="formValid" @submit.prevent="submitForm" :disabled="loading"> - <v-text-field v-model="formData.name" :label="mergedLabels.nameLabel" - :rules="[(v: any) => !!v || mergedLabels.nameRequired]" variant="outlined" - density="comfortable" autofocus class="mb-3" /> - - <v-textarea v-model="formData.description" :label="labels.descriptionLabel" variant="outlined" - rows="3" density="comfortable" hide-details /> - </v-form> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn variant="text" @click="closeDialog"> - {{ labels.cancelButton }} - </v-btn> - <v-btn color="primary" variant="tonal" @click="submitForm" :loading="loading" :disabled="!formValid"> - {{ labels.createButton }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { CreateFolderData } from './types'; - -interface DefaultLabels { - title: string; - nameLabel: string; - descriptionLabel: string; - nameRequired: string; - cancelButton: string; - createButton: string; -} - -const defaultLabels: DefaultLabels = { - title: '创建文件夹', - nameLabel: '名称', - descriptionLabel: '描述', - nameRequired: '请输入文件夹名称', - cancelButton: '取消', - createButton: '创建' -}; - -export default defineComponent({ - name: 'BaseCreateFolderDialog', - props: { - modelValue: { - type: Boolean, - default: false - }, - parentFolderId: { - type: String as PropType<string | null>, - default: null - }, - labels: { - type: Object as PropType<Partial<DefaultLabels>>, - default: () => ({}) - } - }, - emits: ['update:modelValue', 'create'], - data() { - return { - formValid: false, - loading: false, - formData: { - name: '', - description: '' - } - }; - }, - computed: { - showDialog: { - get(): boolean { - return this.modelValue; - }, - set(value: boolean) { - this.$emit('update:modelValue', value); - } - }, - mergedLabels(): DefaultLabels { - return { ...defaultLabels, ...this.labels }; - } - }, - watch: { - modelValue(newValue: boolean) { - if (newValue) { - this.resetForm(); - } - } - }, - methods: { - resetForm() { - this.formData = { - name: '', - description: '' - }; - if (this.$refs.form) { - (this.$refs.form as any).resetValidation(); - } - }, - - closeDialog() { - this.showDialog = false; - }, - - async submitForm() { - if (!this.formValid) return; - - const data: CreateFolderData = { - name: this.formData.name, - description: this.formData.description || undefined, - parent_id: this.parentFolderId - }; - - this.$emit('create', data); - }, - - setLoading(value: boolean) { - this.loading = value; - } - } -}); -</script> diff --git a/legacy-dashboard/src/components/folder/BaseFolderBreadcrumb.vue b/legacy-dashboard/src/components/folder/BaseFolderBreadcrumb.vue deleted file mode 100644 index 037d0ff2..00000000 --- a/legacy-dashboard/src/components/folder/BaseFolderBreadcrumb.vue +++ /dev/null @@ -1,84 +0,0 @@ -<template> - <v-breadcrumbs :items="computedItems" class="base-folder-breadcrumb pa-0"> - <template v-slot:prepend> - <v-icon size="small" class="mr-1">mdi-folder-outline</v-icon> - </template> - <template v-slot:item="{ item }"> - <v-breadcrumbs-item :disabled="(item as any).disabled" @click="!(item as any).disabled && handleClick((item as any).folderId)" - :class="{ 'breadcrumb-link': !(item as any).disabled }"> - <v-icon v-if="(item as any).isRoot" size="small" class="mr-1">mdi-home</v-icon> - {{ (item as any).title }} - </v-breadcrumbs-item> - </template> - <template v-slot:divider> - <v-icon size="small">mdi-chevron-right</v-icon> - </template> - </v-breadcrumbs> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { BreadcrumbItem, FolderTreeNode } from './types'; - -export default defineComponent({ - name: 'BaseFolderBreadcrumb', - props: { - breadcrumbPath: { - type: Array as PropType<FolderTreeNode[]>, - required: true - }, - currentFolderId: { - type: String as PropType<string | null>, - default: null - }, - rootFolderName: { - type: String, - default: '根目录' - } - }, - emits: ['navigate'], - computed: { - computedItems(): BreadcrumbItem[] { - const items: BreadcrumbItem[] = [ - { - title: this.rootFolderName, - folderId: null, - disabled: this.currentFolderId === null, - isRoot: true - } - ]; - - this.breadcrumbPath.forEach((folder, index) => { - items.push({ - title: folder.name, - folderId: folder.folder_id, - disabled: index === this.breadcrumbPath.length - 1, - isRoot: false - }); - }); - - return items; - } - }, - methods: { - handleClick(folderId: string | null) { - this.$emit('navigate', folderId); - } - } -}); -</script> - -<style scoped> -.base-folder-breadcrumb { - font-size: 14px; -} - -.breadcrumb-link { - cursor: pointer; - transition: color 0.2s; -} - -.breadcrumb-link:hover { - color: rgb(var(--v-theme-primary)); -} -</style> diff --git a/legacy-dashboard/src/components/folder/BaseFolderCard.vue b/legacy-dashboard/src/components/folder/BaseFolderCard.vue deleted file mode 100644 index af011131..00000000 --- a/legacy-dashboard/src/components/folder/BaseFolderCard.vue +++ /dev/null @@ -1,139 +0,0 @@ -<template> - <v-card class="base-folder-card" :class="{ 'drag-over': isDragOver }" rounded="lg" @click="$emit('click')" @contextmenu.prevent="$emit('contextmenu', $event)" - elevation="0" @dragover.prevent="handleDragOver" @dragleave="handleDragLeave" @drop.prevent="handleDrop"> - <v-card-text class="d-flex align-center pa-3"> - <v-icon size="40" color="amber-darken-2" class="mr-3">mdi-folder</v-icon> - <div class="folder-info flex-grow-1 overflow-hidden"> - <div class="text-subtitle-1 font-weight-medium text-truncate">{{ folder.name }}</div> - <div v-if="folder.description" class="text-body-2 text-medium-emphasis text-truncate"> - {{ folder.description }} - </div> - </div> - <v-menu offset-y> - <template v-slot:activator="{ props }"> - <v-btn icon="mdi-dots-vertical" variant="text" size="small" v-bind="props" @click.stop /> - </template> - <v-list density="compact"> - <v-list-item @click.stop="$emit('open')"> - <template v-slot:prepend> - <v-icon size="small">mdi-folder-open</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.open }}</v-list-item-title> - </v-list-item> - <v-list-item @click.stop="$emit('rename')"> - <template v-slot:prepend> - <v-icon size="small">mdi-pencil</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.rename }}</v-list-item-title> - </v-list-item> - <v-list-item @click.stop="$emit('move')"> - <template v-slot:prepend> - <v-icon size="small">mdi-folder-move</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.moveTo }}</v-list-item-title> - </v-list-item> - <v-divider class="my-1" /> - <v-list-item @click.stop="$emit('delete')" class="text-error"> - <template v-slot:prepend> - <v-icon size="small" color="error">mdi-delete</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.delete }}</v-list-item-title> - </v-list-item> - </v-list> - </v-menu> - </v-card-text> - </v-card> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { Folder } from './types'; - -interface DefaultLabels { - open: string; - rename: string; - moveTo: string; - delete: string; -} - -const defaultLabels: DefaultLabels = { - open: '打开', - rename: '重命名', - moveTo: '移动到...', - delete: '删除' -}; - -export default defineComponent({ - name: 'BaseFolderCard', - props: { - folder: { - type: Object as PropType<Folder>, - required: true - }, - acceptDropTypes: { - type: Array as PropType<string[]>, - default: () => [] - }, - labels: { - type: Object as PropType<Partial<DefaultLabels>>, - default: () => ({}) - } - }, - emits: ['click', 'contextmenu', 'open', 'rename', 'move', 'delete', 'item-dropped'], - data() { - return { - isDragOver: false - }; - }, - computed: { - mergedLabels(): DefaultLabels { - return { ...defaultLabels, ...this.labels }; - } - }, - methods: { - handleDragOver(event: DragEvent) { - if (!event.dataTransfer) return; - event.dataTransfer.dropEffect = 'move'; - this.isDragOver = true; - }, - handleDragLeave() { - this.isDragOver = false; - }, - handleDrop(event: DragEvent) { - this.isDragOver = false; - if (!event.dataTransfer) return; - - try { - const data = JSON.parse(event.dataTransfer.getData('application/json')); - if (this.acceptDropTypes.length === 0 || this.acceptDropTypes.includes(data.type)) { - this.$emit('item-dropped', { - item_id: data.id || data.persona_id || data.item_id, - item_type: data.type, - target_folder_id: this.folder.folder_id, - source_data: data - }); - } - } catch (e) { - console.error('Failed to parse drop data:', e); - } - } - } -}); -</script> - -<style scoped> -.base-folder-card { - cursor: pointer; - transition: all 0.2s ease; -} - -.base-folder-card.drag-over { - background-color: rgba(var(--v-theme-primary), 0.15); - border: 2px dashed rgb(var(--v-theme-primary)); - transform: scale(1.02); -} - -.folder-info { - min-width: 0; -} -</style> diff --git a/legacy-dashboard/src/components/folder/BaseFolderItemSelector.vue b/legacy-dashboard/src/components/folder/BaseFolderItemSelector.vue deleted file mode 100644 index b7a40bc4..00000000 --- a/legacy-dashboard/src/components/folder/BaseFolderItemSelector.vue +++ /dev/null @@ -1,607 +0,0 @@ -<template> - <div class="folder-item-selector"> - <!-- 触发按钮区域 --> - <div class="d-flex align-center justify-space-between"> - <span v-if="!modelValue" style="color: rgb(var(--v-theme-primaryText));"> - {{ labels.notSelected || '未选择' }} - </span> - <span v-else> - {{ displayValue }} - </span> - <v-btn size="small" color="primary" variant="tonal" @click="openDialog"> - {{ labels.buttonText || '选择...' }} - </v-btn> - </div> - - <!-- 选择对话框 --> - <v-dialog - v-model="dialog" - :max-width="isCompactLayout ? '96vw' : '1000px'" - :min-width="isCompactLayout ? undefined : '800px'" - > - <v-card class="selector-dialog-card"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - <v-icon class="mr-3" color="primary">mdi-account-circle</v-icon> - <span>{{ labels.dialogTitle || '选择项目' }}</span> - </v-card-title> - - <v-divider /> - - <v-card-text class="pa-0 selector-content"> - <div class="selector-layout"> - <!-- 左侧文件夹树 --> - <div v-if="!isCompactLayout" class="folder-sidebar"> - <div class="sidebar-header pa-3 pb-2"> - <span class="text-caption text-medium-emphasis font-weight-medium"> - <v-icon size="small" class="mr-1">mdi-folder-multiple</v-icon> - 文件夹 - </span> - </div> - <v-list density="compact" nav class="tree-list pa-2" bg-color="transparent"> - <!-- 根目录 --> - <v-list-item :active="currentFolderId === null" @click="navigateToFolder(null)" - rounded="lg" class="mb-1 root-item"> - <template v-slot:prepend> - <v-icon size="20" :color="currentFolderId === null ? 'primary' : ''">mdi-home</v-icon> - </template> - <v-list-item-title class="text-body-2">{{ labels.rootFolder || '根目录' }}</v-list-item-title> - </v-list-item> - - <!-- 文件夹树 --> - <template v-if="!treeLoading"> - <BaseMoveTargetNode v-for="folder in folderTree" :key="folder.folder_id" - :folder="folder" :depth="0" :selected-folder-id="currentFolderId" - :disabled-folder-ids="[]" @select="navigateToFolder" /> - </template> - - <div v-if="treeLoading" class="text-center pa-4"> - <v-progress-circular indeterminate size="20" color="primary" /> - </div> - </v-list> - </div> - - <!-- 右侧项目列表 --> - <div class="items-panel"> - <div v-if="isCompactLayout" class="mobile-folder-bar px-4 py-2"> - <v-btn icon="mdi-arrow-left" size="small" variant="text" - :disabled="currentFolderId === null" @click="navigateToParentFolder" /> - <v-btn size="small" variant="tonal" color="primary" prepend-icon="mdi-home" - @click="navigateToFolder(null)"> - {{ labels.rootFolder || '根目录' }} - </v-btn> - <span class="text-caption text-medium-emphasis text-truncate mobile-folder-label"> - {{ currentFolderLabel }} - </span> - </div> - - <v-divider v-if="isCompactLayout" /> - - <!-- 面包屑导航 --> - <div class="breadcrumb-bar px-4 py-3"> - <v-breadcrumbs :items="breadcrumbItems" density="compact" class="pa-0"> - <template v-slot:item="{ item }"> - <v-breadcrumbs-item :disabled="(item as any).disabled" - @click="!(item as any).disabled && navigateToFolder((item as any).folderId)" - :class="{ 'breadcrumb-link': !(item as any).disabled }"> - <v-icon v-if="(item as any).isRoot" size="small" - class="mr-1">mdi-home</v-icon> - {{ item.title }} - </v-breadcrumbs-item> - </template> - <template v-slot:divider> - <v-icon size="small" color="grey">mdi-chevron-right</v-icon> - </template> - </v-breadcrumbs> - </div> - - <v-divider /> - - <!-- 项目列表 --> - <div class="items-list"> - <v-progress-linear v-if="itemsLoading" indeterminate - color="primary" height="2"></v-progress-linear> - - <!-- 子文件夹 --> - <v-list v-if="!itemsLoading" lines="two" class="pa-3 items-content"> - <template v-if="currentSubFolders.length > 0"> - <div class="section-label text-caption text-medium-emphasis mb-2 px-2">子文件夹</div> - <v-list-item v-for="folder in currentSubFolders" :key="'folder-' + folder.folder_id" - @click="navigateToFolder(folder.folder_id)" rounded="lg" class="mb-1 folder-item"> - <template v-slot:prepend> - <v-avatar size="36" color="amber-lighten-4" class="mr-3"> - <v-icon color="amber-darken-2" size="20">mdi-folder</v-icon> - </v-avatar> - </template> - <v-list-item-title class="font-weight-medium">{{ folder.name }}</v-list-item-title> - <template v-slot:append> - <v-icon size="20" color="grey">mdi-chevron-right</v-icon> - </template> - </v-list-item> - </template> - - <!-- 项目列表 --> - <template v-if="currentItems.length > 0"> - <div class="section-label text-caption text-medium-emphasis mb-2 px-2" :class="{ 'mt-4': currentSubFolders.length > 0 }">可选项目</div> - <v-list-item v-for="item in currentItems" :key="'item-' + getItemId(item)" - :value="getItemId(item)" @click="selectItem(item)" - :active="selectedItemId === getItemId(item)" rounded="lg" class="mb-1 persona-item" - :class="{ 'selected-item': selectedItemId === getItemId(item) }"> - <template v-slot:prepend> - <v-avatar size="36" :color="selectedItemId === getItemId(item) ? 'primary-lighten-4' : 'grey-lighten-3'" class="mr-3"> - <v-icon :color="selectedItemId === getItemId(item) ? 'primary' : 'grey-darken-1'" size="20">mdi-account</v-icon> - </v-avatar> - </template> - <v-list-item-title class="font-weight-medium">{{ getItemName(item) }}</v-list-item-title> - <v-list-item-subtitle v-if="getItemDescription(item)" class="text-truncate"> - {{ truncateText(getItemDescription(item), 80) }} - </v-list-item-subtitle> - - <template v-slot:append> - <div class="d-flex align-center ga-1"> - <v-btn v-if="showEditButton && !isDefaultItem(item)" - icon="mdi-pencil" - size="small" - variant="text" - @click.stop="handleEditItem(item)" - :title="labels.editButton || 'Edit'" - /> - <v-icon v-if="selectedItemId === getItemId(item)" - color="primary" size="22">mdi-check-circle</v-icon> - </div> - </template> - </v-list-item> - </template> - - <!-- 空状态 --> - <div v-if="currentSubFolders.length === 0 && currentItems.length === 0" - class="empty-state text-center py-12"> - <v-icon size="64" color="grey-lighten-2">mdi-folder-open-outline</v-icon> - <p class="text-grey mt-4 text-body-2">{{ labels.emptyFolder || labels.noItems || '此文件夹为空' }}</p> - </div> - </v-list> - </div> - </div> - </div> - </v-card-text> - - <v-card-actions class="pa-4"> - <v-btn v-if="showCreateButton" variant="text" color="primary" prepend-icon="mdi-plus" - @click="$emit('create')"> - {{ labels.createButton || '新建' }} - </v-btn> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelSelection">{{ labels.cancelButton || '取消' }}</v-btn> - <v-btn color="primary" variant="tonal" @click="confirmSelection" :disabled="!selectedItemId"> - {{ labels.confirmButton || '确认' }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - </div> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import BaseMoveTargetNode from './BaseMoveTargetNode.vue'; -import type { FolderTreeNode, FolderItemSelectorLabels, SelectableItem } from './types'; - -export default defineComponent({ - name: 'BaseFolderItemSelector', - components: { - BaseMoveTargetNode - }, - props: { - modelValue: { - type: String, - default: '' - }, - // 文件夹树数据 - folderTree: { - type: Array as PropType<FolderTreeNode[]>, - default: () => [] - }, - // 当前项目列表 - items: { - type: Array as PropType<SelectableItem[]>, - default: () => [] - }, - // 加载状态 - treeLoading: { - type: Boolean, - default: false - }, - itemsLoading: { - type: Boolean, - default: false - }, - // 标签配置 - labels: { - type: Object as PropType<Partial<FolderItemSelectorLabels>>, - default: () => ({}) - }, - // 是否显示创建按钮 - showCreateButton: { - type: Boolean, - default: false - }, - // 是否显示编辑按钮 - showEditButton: { - type: Boolean, - default: false - }, - // 默认项(如 "默认人格") - defaultItem: { - type: Object as PropType<SelectableItem | null>, - default: null - }, - // 项目字段映射 - itemIdField: { - type: String, - default: 'id' - }, - itemNameField: { - type: String, - default: 'name' - }, - itemDescriptionField: { - type: String, - default: 'description' - }, - // 显示值的格式化函数(用于显示选中项的名称) - displayValueFormatter: { - type: Function as unknown as PropType<((value: string) => string) | null>, - default: null - } - }, - emits: ['update:modelValue', 'navigate', 'create', 'edit'], - data() { - return { - dialog: false, - selectedItemId: '' as string, - currentFolderId: null as string | null, - breadcrumbPath: [] as FolderTreeNode[] - }; - }, - computed: { - isCompactLayout(): boolean { - return this.$vuetify.display.smAndDown; - }, - - currentFolderLabel(): string { - if (this.currentFolderId === null) { - return this.labels.rootFolder || '根目录'; - } - const currentFolder = this.breadcrumbPath[this.breadcrumbPath.length - 1]; - return currentFolder?.name || this.labels.rootFolder || '根目录'; - }, - - displayValue(): string { - if (this.displayValueFormatter) { - return this.displayValueFormatter(this.modelValue); - } - // 如果是默认项 - if (this.defaultItem && this.modelValue === this.getItemId(this.defaultItem)) { - return this.labels.defaultItem || this.getItemName(this.defaultItem); - } - return this.modelValue; - }, - - currentItems(): SelectableItem[] { - const items: SelectableItem[] = []; - - // 如果在根目录且有默认项,添加到列表开头 - if (this.currentFolderId === null && this.defaultItem) { - items.push(this.defaultItem); - } - - // 添加当前文件夹的项目 - items.push(...this.items); - - return items; - }, - - currentSubFolders(): FolderTreeNode[] { - if (this.currentFolderId === null) { - return this.folderTree; - } - const folder = this.findFolderInTree(this.currentFolderId); - return folder?.children || []; - }, - - breadcrumbItems(): any[] { - const items: any[] = [ - { - title: this.labels.rootFolder || '根目录', - folderId: null, - disabled: this.currentFolderId === null, - isRoot: true - } - ]; - - this.breadcrumbPath.forEach((folder, index) => { - items.push({ - title: folder.name, - folderId: folder.folder_id, - disabled: index === this.breadcrumbPath.length - 1, - isRoot: false - }); - }); - - return items; - } - }, - methods: { - getItemId(item: SelectableItem): string { - return String(item[this.itemIdField] || item.id || ''); - }, - - getItemName(item: SelectableItem): string { - return String(item[this.itemNameField] || item.name || ''); - }, - - getItemDescription(item: SelectableItem): string { - return String(item[this.itemDescriptionField] || item.description || ''); - }, - - truncateText(text: string, maxLength: number): string { - if (!text) return ''; - return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; - }, - - openDialog() { - this.selectedItemId = this.modelValue || ''; - this.currentFolderId = null; - this.breadcrumbPath = []; - this.dialog = true; - this.$emit('navigate', null); - }, - - navigateToFolder(folderId: string | null) { - this.currentFolderId = folderId; - this.updateBreadcrumb(folderId); - this.$emit('navigate', folderId); - }, - - navigateToParentFolder() { - if (this.currentFolderId === null) { - return; - } - - if (this.breadcrumbPath.length <= 1) { - this.navigateToFolder(null); - return; - } - - const parent = this.breadcrumbPath[this.breadcrumbPath.length - 2]; - this.navigateToFolder(parent?.folder_id ?? null); - }, - - findFolderInTree(folderId: string): FolderTreeNode | null { - const findNode = (nodes: FolderTreeNode[]): FolderTreeNode | null => { - for (const node of nodes) { - if (node.folder_id === folderId) { - return node; - } - if (node.children && node.children.length > 0) { - const found = findNode(node.children); - if (found) return found; - } - } - return null; - }; - return findNode(this.folderTree); - }, - - findPathToFolder(folderId: string): FolderTreeNode[] { - const findPath = (nodes: FolderTreeNode[], path: FolderTreeNode[]): FolderTreeNode[] | null => { - for (const node of nodes) { - if (node.folder_id === folderId) { - return [...path, node]; - } - if (node.children && node.children.length > 0) { - const result = findPath(node.children, [...path, node]); - if (result) return result; - } - } - return null; - }; - return findPath(this.folderTree, []) || []; - }, - - updateBreadcrumb(folderId: string | null) { - if (folderId === null) { - this.breadcrumbPath = []; - } else { - this.breadcrumbPath = this.findPathToFolder(folderId); - } - }, - - selectItem(item: SelectableItem) { - this.selectedItemId = this.getItemId(item); - }, - - confirmSelection() { - this.$emit('update:modelValue', this.selectedItemId); - this.dialog = false; - }, - - cancelSelection() { - this.selectedItemId = this.modelValue || ''; - this.dialog = false; - }, - - isDefaultItem(item: SelectableItem): boolean { - if (this.defaultItem === null) { - return false; - } - return this.getItemId(item) === this.getItemId(this.defaultItem); - }, - - handleEditItem(item: SelectableItem) { - this.$emit('edit', item); - } - } -}); -</script> - -<style scoped> -.selector-dialog-card { - border-radius: 12px; - overflow: hidden; -} - -.dialog-title { - font-size: 1.25rem; - font-weight: 500; -} - -.selector-layout { - display: flex; - height: 100%; - min-width: 0; -} - -.selector-content { - height: 600px; - max-height: 80vh; - overflow: hidden; -} - -.folder-sidebar { - width: 280px; - border-right: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); - overflow-y: auto; - flex-shrink: 0; - background-color: transparent; -} - -.sidebar-header { - border-bottom: 1px solid rgba(var(--v-border-color), 0.5); -} - -.items-panel { - flex: 1; - display: flex; - flex-direction: column; - min-width: 0; - background-color: rgb(var(--v-theme-surface)); -} - -.breadcrumb-bar { - background-color: transparent; - min-height: 56px; - display: flex; - align-items: center; -} - -.items-list { - flex: 1; - overflow-y: auto; -} - -.items-content { - background-color: transparent; - min-width: 0; -} - -.mobile-folder-bar { - display: flex; - align-items: center; - gap: 8px; -} - -.mobile-folder-label { - min-width: 0; - flex: 1; -} - -.tree-list { - padding: 0; -} - -.section-label { - text-transform: uppercase; - letter-spacing: 0.5px; - font-size: 0.7rem; -} - -.breadcrumb-link { - cursor: pointer; - transition: color 0.2s; -} - -.breadcrumb-link:hover { - color: rgb(var(--v-theme-primary)); -} - -.root-item { - margin-bottom: 4px; -} - -.folder-item { - transition: all 0.15s ease; -} - -.folder-item:hover { - background-color: rgba(var(--v-theme-primary), 0.06); -} - -.persona-item { - transition: all 0.15s ease; - border: 1px solid transparent; -} - -.persona-item:hover { - background-color: rgba(var(--v-theme-primary), 0.04); -} - -.persona-item.selected-item { - background-color: rgba(var(--v-theme-primary), 0.08); - border-color: rgba(var(--v-theme-primary), 0.3); -} - -.empty-state { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - min-height: 200px; -} - -.v-list-item { - transition: all 0.15s ease; -} - -.v-list-item:hover { - background-color: rgba(var(--v-theme-primary), 0.04); -} - -.v-list-item.v-list-item--active { - background-color: rgba(var(--v-theme-primary), 0.08); -} - -@media (max-width: 960px) { - .selector-layout { - flex-direction: column; - height: auto; - max-height: none; - } - - .selector-content { - max-height: 76vh; - } - - .items-list { - min-height: 0; - } - - .breadcrumb-bar { - overflow-x: auto; - } - - .breadcrumb-bar :deep(.v-breadcrumbs) { - flex-wrap: nowrap; - min-width: max-content; - } -} -</style> diff --git a/legacy-dashboard/src/components/folder/BaseFolderTree.vue b/legacy-dashboard/src/components/folder/BaseFolderTree.vue deleted file mode 100644 index 1fe92415..00000000 --- a/legacy-dashboard/src/components/folder/BaseFolderTree.vue +++ /dev/null @@ -1,272 +0,0 @@ -<template> - <div class="base-folder-tree"> - <!-- 搜索框 --> - <v-text-field v-model="searchQuery" :placeholder="labels.searchPlaceholder" prepend-inner-icon="mdi-magnify" - variant="outlined" density="compact" hide-details clearable class="mb-3" /> - - <!-- 根目录节点 --> - <v-list density="compact" nav class="tree-list" bg-color="transparent"> - <v-list-item :active="currentFolderId === null" @click="handleFolderClick(null)" rounded="lg" - :class="['root-item', { 'drag-over': isRootDragOver }]" - @dragover.prevent="handleRootDragOver" @dragleave="handleRootDragLeave" @drop.prevent="handleRootDrop"> - <template v-slot:prepend> - <v-icon>mdi-home</v-icon> - </template> - <v-list-item-title>{{ labels.rootFolder }}</v-list-item-title> - </v-list-item> - - <!-- 文件夹树 --> - <template v-if="!treeLoading"> - <BaseFolderTreeNode v-for="folder in filteredFolderTree" :key="folder.folder_id" :folder="folder" - :depth="0" :current-folder-id="currentFolderId" :search-query="searchQuery" - :expanded-folder-ids="expandedFolderIds" :accept-drop-types="acceptDropTypes" - @folder-click="handleFolderClick" @folder-context-menu="handleContextMenu" - @item-dropped="$emit('item-dropped', $event)" - @toggle-expansion="$emit('toggle-expansion', $event)" - @set-expansion="$emit('set-expansion', $event)" /> - </template> - - <!-- 加载状态 --> - <div v-if="treeLoading" class="text-center pa-4"> - <v-progress-circular indeterminate size="24" /> - </div> - - <!-- 空状态 --> - <div v-if="!treeLoading && folderTree.length === 0" class="text-center pa-4 text-medium-emphasis"> - <v-icon size="32" class="mb-2">mdi-folder-outline</v-icon> - <div class="text-body-2">{{ labels.noFolders }}</div> - </div> - </v-list> - - <!-- 右键菜单 --> - <v-menu v-model="contextMenu.show" :target="contextMenu.target as any" location="end" :close-on-content-click="true"> - <v-list density="compact"> - <v-list-item @click="openFolder"> - <template v-slot:prepend> - <v-icon size="small">mdi-folder-open</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.contextMenu.open }}</v-list-item-title> - </v-list-item> - <v-list-item @click="$emit('rename-folder', contextMenu.folder)"> - <template v-slot:prepend> - <v-icon size="small">mdi-pencil</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.contextMenu.rename }}</v-list-item-title> - </v-list-item> - <v-list-item @click="$emit('move-folder', contextMenu.folder)"> - <template v-slot:prepend> - <v-icon size="small">mdi-folder-move</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.contextMenu.moveTo }}</v-list-item-title> - </v-list-item> - <v-divider class="my-1" /> - <v-list-item @click="$emit('delete-folder', contextMenu.folder)" class="text-error"> - <template v-slot:prepend> - <v-icon size="small" color="error">mdi-delete</v-icon> - </template> - <v-list-item-title>{{ mergedLabels.contextMenu.delete }}</v-list-item-title> - </v-list-item> - </v-list> - </v-menu> - </div> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { FolderTreeNode, ContextMenuEvent } from './types'; -import BaseFolderTreeNode from './BaseFolderTreeNode.vue'; - -interface ContextMenuState { - show: boolean; - target: [number, number] | null; - folder: FolderTreeNode | null; -} - -interface Folder { - folder_id: string; - name: string; - parent_id: string | null; - description?: string | null; - sort_order?: number; - created_at?: string; - updated_at?: string; -} - -interface DefaultLabels { - searchPlaceholder: string; - rootFolder: string; - noFolders: string; - contextMenu: { - open: string; - rename: string; - moveTo: string; - delete: string; - }; -} - -const defaultLabels: DefaultLabels = { - searchPlaceholder: '搜索文件夹...', - rootFolder: '根目录', - noFolders: '暂无文件夹', - contextMenu: { - open: '打开', - rename: '重命名', - moveTo: '移动到...', - delete: '删除' - } -}; - -export default defineComponent({ - name: 'BaseFolderTree', - components: { - BaseFolderTreeNode - }, - props: { - folderTree: { - type: Array as PropType<FolderTreeNode[]>, - required: true - }, - currentFolderId: { - type: String as PropType<string | null>, - default: null - }, - expandedFolderIds: { - type: Array as PropType<string[]>, - default: () => [] - }, - treeLoading: { - type: Boolean, - default: false - }, - acceptDropTypes: { - type: Array as PropType<string[]>, - default: () => [] - }, - labels: { - type: Object as PropType<Partial<DefaultLabels>>, - default: () => ({}) - } - }, - emits: [ - 'folder-click', - 'rename-folder', - 'move-folder', - 'delete-folder', - 'item-dropped', - 'toggle-expansion', - 'set-expansion' - ], - data() { - return { - searchQuery: '', - isRootDragOver: false, - contextMenu: { - show: false, - target: null, - folder: null - } as ContextMenuState - }; - }, - computed: { - mergedLabels(): DefaultLabels { - return { - ...defaultLabels, - ...this.labels, - contextMenu: { - ...defaultLabels.contextMenu, - ...(this.labels?.contextMenu || {}) - } - }; - }, - filteredFolderTree(): FolderTreeNode[] { - if (!this.searchQuery) { - return this.folderTree; - } - const query = this.searchQuery.toLowerCase(); - return this.filterTreeBySearch(this.folderTree, query); - } - }, - methods: { - filterTreeBySearch(nodes: FolderTreeNode[], query: string): FolderTreeNode[] { - return nodes.filter(node => { - const matches = node.name.toLowerCase().includes(query); - const childMatches = this.filterTreeBySearch(node.children || [], query); - return matches || childMatches.length > 0; - }).map(node => ({ - ...node, - children: this.filterTreeBySearch(node.children || [], query) - })); - }, - - handleFolderClick(folderId: string | null) { - this.$emit('folder-click', folderId); - }, - - handleRootDragOver(event: DragEvent) { - if (!event.dataTransfer) return; - event.dataTransfer.dropEffect = 'move'; - this.isRootDragOver = true; - }, - - handleRootDragLeave() { - this.isRootDragOver = false; - }, - - handleRootDrop(event: DragEvent) { - this.isRootDragOver = false; - if (!event.dataTransfer) return; - - try { - const data = JSON.parse(event.dataTransfer.getData('application/json')); - if (this.acceptDropTypes.length === 0 || this.acceptDropTypes.includes(data.type)) { - this.$emit('item-dropped', { - item_id: data.id || data.persona_id || data.item_id, - item_type: data.type, - target_folder_id: null, - source_data: data - }); - } - } catch (e) { - console.error('Failed to parse drop data:', e); - } - }, - - handleContextMenu(eventData: ContextMenuEvent) { - const { event, folder } = eventData; - this.contextMenu.target = [event.clientX, event.clientY]; - this.contextMenu.folder = folder as FolderTreeNode; - this.contextMenu.show = true; - }, - - openFolder() { - if (this.contextMenu.folder) { - this.$emit('folder-click', this.contextMenu.folder.folder_id); - } - } - } -}); -</script> - -<style scoped> -.base-folder-tree { - height: 100%; - display: flex; - flex-direction: column; -} - -.tree-list { - flex: 1; - overflow-y: auto; -} - -.root-item { - margin-bottom: 4px; - transition: all 0.2s ease; -} - -.root-item.drag-over { - background-color: rgba(var(--v-theme-primary), 0.15); - border: 2px dashed rgb(var(--v-theme-primary)); - border-radius: 8px; -} -</style> diff --git a/legacy-dashboard/src/components/folder/BaseFolderTreeNode.vue b/legacy-dashboard/src/components/folder/BaseFolderTreeNode.vue deleted file mode 100644 index b02cd3c2..00000000 --- a/legacy-dashboard/src/components/folder/BaseFolderTreeNode.vue +++ /dev/null @@ -1,154 +0,0 @@ -<template> - <div class="base-folder-tree-node"> - <v-list-item :active="currentFolderId === folder.folder_id" @click.stop="$emit('folder-click', folder.folder_id)" - @contextmenu.prevent="handleContextMenu" rounded="lg" :style="{ paddingLeft: `${(depth + 1) * 16}px` }" - :class="['folder-item', { 'drag-over': isDragOver }]" - @dragover.prevent="handleDragOver" @dragleave="handleDragLeave" @drop.prevent="handleDrop"> - <template v-slot:prepend> - <v-btn v-if="hasChildren" icon variant="text" size="x-small" @click.stop="toggleExpand" - class="expand-btn"> - <v-icon size="16">{{ isExpanded ? 'mdi-chevron-down' : 'mdi-chevron-right' }}</v-icon> - </v-btn> - <div v-else class="expand-placeholder"></div> - <v-icon :color="currentFolderId === folder.folder_id ? 'primary' : ''"> - {{ isExpanded ? 'mdi-folder-open' : 'mdi-folder' }} - </v-icon> - </template> - <v-list-item-title class="text-truncate">{{ folder.name }}</v-list-item-title> - </v-list-item> - - <!-- 子文件夹 --> - <v-expand-transition> - <div v-show="isExpanded && hasChildren"> - <BaseFolderTreeNode v-for="child in folder.children" :key="child.folder_id" :folder="child" :depth="depth + 1" - :current-folder-id="currentFolderId" :search-query="searchQuery" - :expanded-folder-ids="expandedFolderIds" :accept-drop-types="acceptDropTypes" - @folder-click="$emit('folder-click', $event)" - @folder-context-menu="$emit('folder-context-menu', $event)" - @item-dropped="$emit('item-dropped', $event)" - @toggle-expansion="$emit('toggle-expansion', $event)" - @set-expansion="$emit('set-expansion', $event)" /> - </div> - </v-expand-transition> - </div> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { FolderTreeNode } from './types'; - -export default defineComponent({ - name: 'BaseFolderTreeNode', - props: { - folder: { - type: Object as PropType<FolderTreeNode>, - required: true - }, - depth: { - type: Number, - default: 0 - }, - currentFolderId: { - type: String as PropType<string | null>, - default: null - }, - searchQuery: { - type: String, - default: '' - }, - expandedFolderIds: { - type: Array as PropType<string[]>, - default: () => [] - }, - acceptDropTypes: { - type: Array as PropType<string[]>, - default: () => [] - } - }, - emits: ['folder-click', 'folder-context-menu', 'item-dropped', 'toggle-expansion', 'set-expansion'], - data() { - return { - isDragOver: false - }; - }, - computed: { - hasChildren(): boolean { - return this.folder.children && this.folder.children.length > 0; - }, - isExpanded(): boolean { - return this.expandedFolderIds.includes(this.folder.folder_id); - } - }, - watch: { - searchQuery: { - immediate: true, - handler(newQuery: string) { - // 搜索时自动展开匹配的节点 - if (newQuery && this.hasChildren) { - this.$emit('set-expansion', { folderId: this.folder.folder_id, expanded: true }); - } - } - } - }, - methods: { - toggleExpand() { - this.$emit('toggle-expansion', this.folder.folder_id); - }, - handleContextMenu(event: MouseEvent) { - this.$emit('folder-context-menu', { event, folder: this.folder }); - }, - handleDragOver(event: DragEvent) { - if (!event.dataTransfer) return; - event.dataTransfer.dropEffect = 'move'; - this.isDragOver = true; - }, - handleDragLeave() { - this.isDragOver = false; - }, - handleDrop(event: DragEvent) { - this.isDragOver = false; - if (!event.dataTransfer) return; - - try { - const data = JSON.parse(event.dataTransfer.getData('application/json')); - if (this.acceptDropTypes.length === 0 || this.acceptDropTypes.includes(data.type)) { - this.$emit('item-dropped', { - item_id: data.id || data.persona_id || data.item_id, - item_type: data.type, - target_folder_id: this.folder.folder_id, - source_data: data - }); - } - } catch (e) { - console.error('Failed to parse drop data:', e); - } - } - } -}); -</script> - -<style scoped> -.base-folder-tree-node { - width: 100%; -} - -.folder-item { - min-height: 36px; - transition: all 0.2s ease; -} - -.folder-item.drag-over { - background-color: rgba(var(--v-theme-primary), 0.15); - border: 2px dashed rgb(var(--v-theme-primary)); - border-radius: 8px; -} - -.expand-btn { - margin-right: 4px; -} - -.expand-placeholder { - width: 28px; - flex-shrink: 0; -} -</style> diff --git a/legacy-dashboard/src/components/folder/BaseMoveTargetNode.vue b/legacy-dashboard/src/components/folder/BaseMoveTargetNode.vue deleted file mode 100644 index 330947be..00000000 --- a/legacy-dashboard/src/components/folder/BaseMoveTargetNode.vue +++ /dev/null @@ -1,93 +0,0 @@ -<template> - <div class="base-move-target-node"> - <v-list-item :active="selectedFolderId === folder.folder_id" :disabled="isDisabled" - @click.stop="!isDisabled && $emit('select', folder.folder_id)" rounded="lg" - :style="{ paddingLeft: `${(depth + 1) * 16}px` }" class="folder-item"> - <template v-slot:prepend> - <v-btn v-if="hasChildren" icon variant="text" size="x-small" @click.stop="toggleExpand" - class="expand-btn" :disabled="isDisabled"> - <v-icon size="16">{{ isExpanded ? 'mdi-chevron-down' : 'mdi-chevron-right' }}</v-icon> - </v-btn> - <div v-else class="expand-placeholder"></div> - <v-icon :color="isDisabled ? 'grey' : (selectedFolderId === folder.folder_id ? 'primary' : '')"> - {{ isExpanded ? 'mdi-folder-open' : 'mdi-folder' }} - </v-icon> - </template> - <v-list-item-title class="text-truncate">{{ folder.name }}</v-list-item-title> - </v-list-item> - - <!-- 子文件夹 --> - <v-expand-transition> - <div v-show="isExpanded && hasChildren"> - <BaseMoveTargetNode v-for="child in folder.children" :key="child.folder_id" :folder="child" :depth="depth + 1" - :selected-folder-id="selectedFolderId" :disabled-folder-ids="disabledFolderIds" - @select="$emit('select', $event)" /> - </div> - </v-expand-transition> - </div> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { FolderTreeNode } from './types'; - -export default defineComponent({ - name: 'BaseMoveTargetNode', - props: { - folder: { - type: Object as PropType<FolderTreeNode>, - required: true - }, - depth: { - type: Number, - default: 0 - }, - selectedFolderId: { - type: String as PropType<string | null>, - default: null - }, - disabledFolderIds: { - type: Array as PropType<string[]>, - default: () => [] - } - }, - emits: ['select'], - data() { - return { - isExpanded: true - }; - }, - computed: { - hasChildren(): boolean { - return this.folder.children && this.folder.children.length > 0; - }, - isDisabled(): boolean { - return this.disabledFolderIds.includes(this.folder.folder_id); - } - }, - methods: { - toggleExpand() { - this.isExpanded = !this.isExpanded; - } - } -}); -</script> - -<style scoped> -.base-move-target-node { - width: 100%; -} - -.folder-item { - min-height: 36px; -} - -.expand-btn { - margin-right: 4px; -} - -.expand-placeholder { - width: 28px; - flex-shrink: 0; -} -</style> diff --git a/legacy-dashboard/src/components/folder/BaseMoveToFolderDialog.vue b/legacy-dashboard/src/components/folder/BaseMoveToFolderDialog.vue deleted file mode 100644 index aaec5021..00000000 --- a/legacy-dashboard/src/components/folder/BaseMoveToFolderDialog.vue +++ /dev/null @@ -1,178 +0,0 @@ -<template> - <v-dialog v-model="showDialog" max-width="500px" persistent> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - <v-icon class="mr-2">mdi-folder-move</v-icon> - {{ labels.title }} - </v-card-title> - <v-card-text> - <p class="text-body-2 text-medium-emphasis mb-4"> - {{ labels.description }} - </p> - - <!-- 文件夹选择树 --> - <div class="folder-select-tree"> - <v-list density="compact" nav class="tree-list"> - <!-- 根目录选项 --> - <v-list-item :active="selectedFolderId === null" @click="selectFolder(null)" rounded="lg" - class="mb-1"> - <template v-slot:prepend> - <v-icon>mdi-home</v-icon> - </template> - <v-list-item-title>{{ labels.rootFolder }}</v-list-item-title> - </v-list-item> - - <!-- 文件夹树 --> - <template v-if="!treeLoading"> - <BaseMoveTargetNode v-for="folder in folderTree" :key="folder.folder_id" :folder="folder" - :depth="0" :selected-folder-id="selectedFolderId" :disabled-folder-ids="disabledFolderIds" - @select="selectFolder" /> - </template> - - <!-- 加载状态 --> - <div v-if="treeLoading" class="text-center pa-4"> - <v-progress-circular indeterminate size="24" /> - </div> - </v-list> - </div> - </v-card-text> - <v-card-actions> - <v-spacer /> - <v-btn variant="text" @click="closeDialog"> - {{ labels.cancelButton }} - </v-btn> - <v-btn color="primary" variant="tonal" @click="submitMove" :loading="loading"> - {{ labels.moveButton }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script lang="ts"> -import { defineComponent, type PropType } from 'vue'; -import type { FolderTreeNode } from './types'; -import BaseMoveTargetNode from './BaseMoveTargetNode.vue'; -import { collectFolderAndChildrenIds } from './useFolderManager'; - -interface DefaultLabels { - title: string; - description: string; - rootFolder: string; - cancelButton: string; - moveButton: string; -} - -const defaultLabels: DefaultLabels = { - title: '移动到文件夹', - description: '选择目标文件夹', - rootFolder: '根目录', - cancelButton: '取消', - moveButton: '移动' -}; - -export default defineComponent({ - name: 'BaseMoveToFolderDialog', - components: { - BaseMoveTargetNode - }, - props: { - modelValue: { - type: Boolean, - default: false - }, - folderTree: { - type: Array as PropType<FolderTreeNode[]>, - required: true - }, - treeLoading: { - type: Boolean, - default: false - }, - // 当移动的是文件夹时,需要传入当前文件夹 ID 以禁用自身和子文件夹 - currentFolderId: { - type: String as PropType<string | null>, - default: null - }, - // 项目当前所在的文件夹 ID(用于初始化选择) - itemCurrentFolderId: { - type: String as PropType<string | null>, - default: null - }, - // 是否是移动文件夹(如果是,需要禁用自身和子文件夹) - isMovingFolder: { - type: Boolean, - default: false - }, - labels: { - type: Object as PropType<Partial<DefaultLabels>>, - default: () => ({}) - } - }, - emits: ['update:modelValue', 'move'], - data() { - return { - selectedFolderId: null as string | null, - loading: false - }; - }, - computed: { - showDialog: { - get(): boolean { - return this.modelValue; - }, - set(value: boolean) { - this.$emit('update:modelValue', value); - } - }, - mergedLabels(): DefaultLabels { - return { ...defaultLabels, ...this.labels }; - }, - // 禁用的文件夹 ID(不能移动到自己或子文件夹) - disabledFolderIds(): string[] { - if (!this.isMovingFolder || !this.currentFolderId) return []; - return collectFolderAndChildrenIds(this.folderTree, this.currentFolderId); - } - }, - watch: { - modelValue(newValue: boolean) { - if (newValue) { - // 初始化选中为当前所在文件夹 - this.selectedFolderId = this.itemCurrentFolderId; - } - } - }, - methods: { - selectFolder(folderId: string | null) { - // 检查是否禁用 - if (folderId && this.disabledFolderIds.includes(folderId)) return; - this.selectedFolderId = folderId; - }, - - closeDialog() { - this.showDialog = false; - }, - - submitMove() { - this.$emit('move', this.selectedFolderId); - }, - - setLoading(value: boolean) { - this.loading = value; - } - } -}); -</script> - -<style scoped> -.folder-select-tree { - max-height: 400px; - overflow-y: auto; - border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); - border-radius: 8px; -} - -.tree-list { - padding: 8px; -} -</style> diff --git a/legacy-dashboard/src/components/folder/README.md b/legacy-dashboard/src/components/folder/README.md deleted file mode 100644 index eb9fe030..00000000 --- a/legacy-dashboard/src/components/folder/README.md +++ /dev/null @@ -1,349 +0,0 @@ -# 通用文件夹管理组件库 - -这是一个可复用的文件夹管理 UI 组件库,提供了完整的文件夹树、面包屑导航、拖放操作等功能。可用于管理各种类型的项目,如 Persona、模板、知识库等。 - -## 组件列表 - -| 组件 | 说明 | -|------|------| -| `BaseFolderTree` | 文件夹树组件,支持搜索、展开/折叠、右键菜单、拖放 | -| `BaseFolderTreeNode` | 文件夹树节点组件(内部使用) | -| `BaseFolderCard` | 文件夹卡片组件,用于网格布局展示 | -| `BaseFolderBreadcrumb` | 面包屑导航组件 | -| `BaseCreateFolderDialog` | 创建文件夹对话框 | -| `BaseMoveToFolderDialog` | 移动项目到文件夹对话框 | -| `BaseMoveTargetNode` | 移动对话框中的目标文件夹节点(内部使用) | - -## Composable - -### `useFolderManager` - -提供文件夹管理的核心逻辑,包括状态管理、导航、CRUD 操作等。 - -```typescript -import { useFolderManager } from '@/components/folder'; - -const { - // 状态 - folderTree, - currentFolderId, - currentFolders, - breadcrumbPath, - expandedFolderIds, - loading, - treeLoading, - - // 计算属性 - currentFolderName, - breadcrumbItems, - - // 方法 - loadFolderTree, - navigateToFolder, - refreshCurrentFolder, - createFolder, - updateFolder, - deleteFolder, - moveFolder, - toggleFolderExpansion, - setFolderExpansion, - findFolderInTree, - findPathToFolder, - filterTreeBySearch, -} = useFolderManager({ - operations: { - loadFolderTree: async () => { - const response = await pluginExtensionApi.get('your-module/folder/tree'); - return response.data.data; - }, - loadSubFolders: async (parentId) => { - const response = await pluginExtensionApi.get('your-module/folder/list', { - params: { parent_id: parentId ?? '' } - }); - return response.data.data; - }, - createFolder: async (data) => { - const response = await pluginExtensionApi.post('your-module/folder/create', data); - return response.data.data.folder; - }, - updateFolder: async (data) => { - await pluginExtensionApi.post('your-module/folder/update', data); - }, - deleteFolder: async (folderId) => { - await pluginExtensionApi.post('your-module/folder/delete', { folder_id: folderId }); - }, - }, - rootFolderName: '根目录', - autoLoad: true, -}); -``` - -## 使用示例 - -### 基础用法 - -```vue -<template> - <div class="folder-manager"> - <!-- 侧边栏 --> - <div class="sidebar"> - <BaseFolderTree - :folder-tree="folderTree" - :current-folder-id="currentFolderId" - :expanded-folder-ids="expandedFolderIds" - :tree-loading="treeLoading" - :accept-drop-types="['item']" - :labels="treeLabels" - @folder-click="navigateToFolder" - @rename-folder="handleRenameFolder" - @move-folder="handleMoveFolder" - @delete-folder="handleDeleteFolder" - @item-dropped="handleItemDropped" - @toggle-expansion="toggleFolderExpansion" - /> - </div> - - <!-- 主内容区 --> - <div class="main-content"> - <!-- 面包屑 --> - <BaseFolderBreadcrumb - :breadcrumb-path="breadcrumbPath" - :current-folder-id="currentFolderId" - root-folder-name="根目录" - @navigate="navigateToFolder" - /> - - <!-- 文件夹卡片 --> - <v-row> - <v-col v-for="folder in currentFolders" :key="folder.folder_id" cols="3"> - <BaseFolderCard - :folder="folder" - :accept-drop-types="['item']" - :labels="cardLabels" - @click="navigateToFolder(folder.folder_id)" - @open="navigateToFolder(folder.folder_id)" - @rename="handleRenameFolder(folder)" - @move="handleMoveFolder(folder)" - @delete="handleDeleteFolder(folder)" - @item-dropped="handleItemDropped" - /> - </v-col> - </v-row> - </div> - - <!-- 创建文件夹对话框 --> - <BaseCreateFolderDialog - v-model="showCreateDialog" - :parent-folder-id="currentFolderId" - :labels="createDialogLabels" - @create="handleCreateFolder" - /> - - <!-- 移动对话框 --> - <BaseMoveToFolderDialog - v-model="showMoveDialog" - :folder-tree="folderTree" - :tree-loading="treeLoading" - :current-folder-id="movingFolder?.folder_id" - :item-current-folder-id="movingFolder?.parent_id" - :is-moving-folder="true" - :labels="moveDialogLabels" - @move="handleMove" - /> - </div> -</template> - -<script setup lang="ts"> -import { ref } from 'vue'; -import { - BaseFolderTree, - BaseFolderCard, - BaseFolderBreadcrumb, - BaseCreateFolderDialog, - BaseMoveToFolderDialog, - useFolderManager, -} from '@/components/folder'; - -const folderManager = useFolderManager({ - operations: { - // ... 实现你的 API 调用 - }, -}); - -const { - folderTree, - currentFolderId, - currentFolders, - breadcrumbPath, - expandedFolderIds, - treeLoading, - navigateToFolder, - toggleFolderExpansion, - createFolder, -} = folderManager; - -const showCreateDialog = ref(false); -const showMoveDialog = ref(false); -const movingFolder = ref(null); - -// 自定义标签 -const treeLabels = { - searchPlaceholder: '搜索文件夹...', - rootFolder: '根目录', - noFolders: '暂无文件夹', - contextMenu: { - open: '打开', - rename: '重命名', - moveTo: '移动到...', - delete: '删除', - }, -}; - -const cardLabels = { - open: '打开', - rename: '重命名', - moveTo: '移动到...', - delete: '删除', -}; - -const createDialogLabels = { - title: '创建文件夹', - nameLabel: '名称', - descriptionLabel: '描述', - nameRequired: '请输入名称', - cancelButton: '取消', - createButton: '创建', -}; - -// 处理函数 -async function handleCreateFolder(data) { - await createFolder(data); - showCreateDialog.value = false; -} - -function handleRenameFolder(folder) { - // 打开重命名对话框 -} - -function handleMoveFolder(folder) { - movingFolder.value = folder; - showMoveDialog.value = true; -} - -function handleDeleteFolder(folder) { - // 确认并删除 -} - -function handleItemDropped({ item_id, item_type, target_folder_id }) { - // 处理拖放 -} - -async function handleMove(targetFolderId) { - // 执行移动 - showMoveDialog.value = false; -} -</script> -``` - -## 类型定义 - -```typescript -// 文件夹基础接口 -interface Folder { - folder_id: string; - name: string; - parent_id: string | null; - description?: string | null; - sort_order?: number; - created_at?: string; - updated_at?: string; -} - -// 文件夹树节点接口 -interface FolderTreeNode extends Folder { - children: FolderTreeNode[]; -} - -// 拖放事件数据 -interface DropEventData { - item_id: string; - item_type: string; - target_folder_id: string | null; - source_data?: any; -} - -// 创建文件夹数据 -interface CreateFolderData { - name: string; - parent_id?: string | null; - description?: string; -} -``` - -## 国际化支持 - -所有组件都支持通过 `labels` prop 自定义文本,方便集成到不同的国际化方案中: - -```vue -<BaseFolderTree - :labels="{ - searchPlaceholder: t('folder.search'), - rootFolder: t('folder.root'), - noFolders: t('folder.empty'), - contextMenu: { - open: t('folder.menu.open'), - rename: t('folder.menu.rename'), - moveTo: t('folder.menu.move'), - delete: t('folder.menu.delete'), - }, - }" -/> -``` - -## 拖放支持 - -组件内置了拖放支持,可以通过 `acceptDropTypes` 指定接受的拖放类型: - -```vue -<!-- 只接受 'persona' 类型的拖放 --> -<BaseFolderTree - :accept-drop-types="['persona']" - @item-dropped="handleDrop" -/> - -<!-- 拖放事件处理 --> -<script setup> -function handleDrop({ item_id, item_type, target_folder_id, source_data }) { - if (item_type === 'persona') { - // 移动 persona 到目标文件夹 - movePersonaToFolder(item_id, target_folder_id); - } -} -</script> -``` - -## 与 Pinia Store 集成 - -如果你更喜欢使用 Pinia Store 管理状态,可以参考现有的 `personaStore.ts` 实现: - -```typescript -// stores/myFolderStore.ts -import { defineStore } from 'pinia'; -import type { FolderTreeNode, Folder } from '@/components/folder'; - -export const useMyFolderStore = defineStore('myFolder', { - state: () => ({ - folderTree: [] as FolderTreeNode[], - currentFolderId: null as string | null, - currentFolders: [] as Folder[], - // ... - }), - - actions: { - async loadFolderTree() { - // ... - }, - // ... - }, -}); -``` diff --git a/legacy-dashboard/src/components/folder/index.ts b/legacy-dashboard/src/components/folder/index.ts deleted file mode 100644 index 07fde831..00000000 --- a/legacy-dashboard/src/components/folder/index.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * 通用文件夹管理组件库 - * - * 提供可复用的文件夹管理 UI 组件,适用于各种需要文件夹组织功能的场景 - * 如:persona 管理、模板管理、知识库管理等 - * - * 使用示例: - * ```vue - * <script setup> - * import { - * BaseFolderTree, - * BaseFolderCard, - * BaseFolderBreadcrumb, - * BaseCreateFolderDialog, - * BaseMoveToFolderDialog, - * useFolderManager - * } from '@/components/folder'; - * - * const folderManager = useFolderManager({ - * operations: { - * loadFolderTree: async () => { ... }, - * loadSubFolders: async (parentId) => { ... }, - * createFolder: async (data) => { ... }, - * updateFolder: async (data) => { ... }, - * deleteFolder: async (folderId) => { ... }, - * } - * }); - * </script> - * ``` - */ - -// 类型导出 -export * from './types'; - -// Composable 导出 -export { useFolderManager, collectFolderAndChildrenIds } from './useFolderManager'; -export type { UseFolderManagerOptions, UseFolderManagerReturn } from './useFolderManager'; - -// 组件导出 -export { default as BaseFolderTree } from './BaseFolderTree.vue'; -export { default as BaseFolderTreeNode } from './BaseFolderTreeNode.vue'; -export { default as BaseFolderCard } from './BaseFolderCard.vue'; -export { default as BaseFolderBreadcrumb } from './BaseFolderBreadcrumb.vue'; -export { default as BaseCreateFolderDialog } from './BaseCreateFolderDialog.vue'; -export { default as BaseMoveToFolderDialog } from './BaseMoveToFolderDialog.vue'; -export { default as BaseMoveTargetNode } from './BaseMoveTargetNode.vue'; diff --git a/legacy-dashboard/src/components/folder/types.ts b/legacy-dashboard/src/components/folder/types.ts deleted file mode 100644 index 13b636fc..00000000 --- a/legacy-dashboard/src/components/folder/types.ts +++ /dev/null @@ -1,250 +0,0 @@ -/** - * 通用文件夹管理组件类型定义 - * - * 这是一个可复用的文件夹管理系统,可用于管理各种类型的项目(如 persona、模板、知识库等) - */ - -/** - * 文件夹基础接口 - */ -export interface Folder { - folder_id: string; - name: string; - parent_id: string | null; - description?: string | null; - sort_order?: number; - created_at?: string; - updated_at?: string; -} - -/** - * 文件夹树节点接口 - */ -export interface FolderTreeNode extends Folder { - children: FolderTreeNode[]; -} - -/** - * 可拖拽的项目接口(可以是文件夹或其他项目) - */ -export interface DraggableItem { - id: string; - type: string; - [key: string]: any; -} - -/** - * 拖拽放置事件数据 - */ -export interface DropEventData { - item_id: string; - item_type: string; - target_folder_id: string | null; - source_data?: any; -} - -/** - * 文件夹操作接口 - 由使用方提供具体实现 - */ -export interface FolderOperations { - // 加载文件夹树 - loadFolderTree: () => Promise<FolderTreeNode[]>; - - // 加载指定文件夹的子文件夹 - loadSubFolders: (parentId: string | null) => Promise<Folder[]>; - - // 创建文件夹 - createFolder: (data: CreateFolderData) => Promise<Folder>; - - // 更新文件夹 - updateFolder: (data: UpdateFolderData) => Promise<void>; - - // 删除文件夹 - deleteFolder: (folderId: string) => Promise<void>; - - // 移动文件夹 - moveFolder?: (folderId: string, targetParentId: string | null) => Promise<void>; -} - -/** - * 创建文件夹数据 - */ -export interface CreateFolderData { - name: string; - parent_id?: string | null; - description?: string; -} - -/** - * 更新文件夹数据 - */ -export interface UpdateFolderData { - folder_id: string; - name?: string; - description?: string; - parent_id?: string | null; -} - -/** - * 文件夹管理器状态 - */ -export interface FolderManagerState { - folderTree: FolderTreeNode[]; - currentFolderId: string | null; - currentFolders: Folder[]; - breadcrumbPath: FolderTreeNode[]; - expandedFolderIds: string[]; - loading: boolean; - treeLoading: boolean; -} - -/** - * 面包屑项接口 - */ -export interface BreadcrumbItem { - title: string; - folderId: string | null; - disabled: boolean; - isRoot: boolean; -} - -/** - * 上下文菜单事件 - */ -export interface ContextMenuEvent { - event: MouseEvent; - folder: Folder; -} - -/** - * 文件夹组件 i18n 键配置 - * 允许使用方自定义翻译键 - */ -export interface FolderI18nKeys { - // 搜索框 - searchPlaceholder?: string; - - // 根目录 - rootFolder?: string; - - // 侧边栏标题 - sidebarTitle?: string; - - // 空状态 - noFolders?: string; - - // 文件夹标题 - foldersTitle?: string; - - // 按钮 - buttons?: { - create?: string; - cancel?: string; - save?: string; - delete?: string; - move?: string; - }; - - // 表单 - form?: { - name?: string; - description?: string; - }; - - // 验证 - validation?: { - nameRequired?: string; - }; - - // 右键菜单 - contextMenu?: { - open?: string; - rename?: string; - moveTo?: string; - delete?: string; - }; - - // 对话框 - dialogs?: { - createTitle?: string; - renameTitle?: string; - deleteTitle?: string; - deleteMessage?: string; - deleteWarning?: string; - moveTitle?: string; - moveDescription?: string; - }; - - // 消息 - messages?: { - createSuccess?: string; - createError?: string; - renameSuccess?: string; - renameError?: string; - deleteSuccess?: string; - deleteError?: string; - moveSuccess?: string; - moveError?: string; - }; -} - -/** - * 通用文件夹组件 Props - */ -export interface BaseFolderProps { - // i18n 翻译函数 - t?: (key: string, params?: Record<string, any>) => string; - - // i18n 键配置 - i18nKeys?: FolderI18nKeys; -} - -/** - * 可选择的项目基础接口 - */ -export interface SelectableItem { - id: string; - name: string; - description?: string | null; - folder_id?: string | null; - [key: string]: any; -} - -/** - * 文件夹项目选择器操作接口 - */ -export interface FolderItemSelectorOperations<T extends SelectableItem> { - // 加载文件夹树 - loadFolderTree: () => Promise<FolderTreeNode[]>; - - // 加载指定文件夹下的项目 - loadItemsInFolder: (folderId: string | null) => Promise<T[]>; - - // 创建项目(可选) - createItem?: (data: any) => Promise<T>; -} - -/** - * 文件夹项目选择器标签配置 - */ -export interface FolderItemSelectorLabels { - // 对话框 - dialogTitle?: string; - notSelected?: string; - buttonText?: string; - - // 项目列表 - noItems?: string; - defaultItem?: string; - noDescription?: string; - emptyFolder?: string; - - // 按钮 - createButton?: string; - editButton?: string; - confirmButton?: string; - cancelButton?: string; - - // 文件夹 - rootFolder?: string; -} diff --git a/legacy-dashboard/src/components/folder/useFolderManager.ts b/legacy-dashboard/src/components/folder/useFolderManager.ts deleted file mode 100644 index a6c1e4b2..00000000 --- a/legacy-dashboard/src/components/folder/useFolderManager.ts +++ /dev/null @@ -1,324 +0,0 @@ -/** - * 通用文件夹管理 Composable - * - * 提供文件夹管理的核心逻辑,可以被不同的业务模块复用 - */ -import { ref, computed, reactive, type Ref, type ComputedRef } from 'vue'; -import type { - Folder, - FolderTreeNode, - FolderOperations, - CreateFolderData, - UpdateFolderData, - BreadcrumbItem, -} from './types'; - -export interface UseFolderManagerOptions { - // 文件夹操作实现 - operations: FolderOperations; - - // 根目录显示名称 - rootFolderName?: string; - - // 是否自动加载 - autoLoad?: boolean; -} - -export interface UseFolderManagerReturn { - // 状态 - folderTree: Ref<FolderTreeNode[]>; - currentFolderId: Ref<string | null>; - currentFolders: Ref<Folder[]>; - breadcrumbPath: Ref<FolderTreeNode[]>; - expandedFolderIds: Ref<string[]>; - loading: Ref<boolean>; - treeLoading: Ref<boolean>; - - // 计算属性 - currentFolderName: ComputedRef<string>; - breadcrumbItems: ComputedRef<BreadcrumbItem[]>; - - // 方法 - loadFolderTree: () => Promise<void>; - navigateToFolder: (folderId: string | null) => Promise<void>; - refreshCurrentFolder: () => Promise<void>; - - createFolder: (data: CreateFolderData) => Promise<Folder>; - updateFolder: (data: UpdateFolderData) => Promise<void>; - deleteFolder: (folderId: string) => Promise<void>; - moveFolder: (folderId: string, targetParentId: string | null) => Promise<void>; - - toggleFolderExpansion: (folderId: string) => void; - setFolderExpansion: (folderId: string, expanded: boolean) => void; - - findFolderInTree: (folderId: string) => FolderTreeNode | null; - findPathToFolder: (folderId: string) => FolderTreeNode[]; - - filterTreeBySearch: (query: string) => FolderTreeNode[]; -} - -/** - * 创建文件夹管理 composable - */ -export function useFolderManager(options: UseFolderManagerOptions): UseFolderManagerReturn { - const { operations, rootFolderName = '根目录', autoLoad = false } = options; - - // 状态 - const folderTree = ref<FolderTreeNode[]>([]); - const currentFolderId = ref<string | null>(null); - const currentFolders = ref<Folder[]>([]); - const breadcrumbPath = ref<FolderTreeNode[]>([]); - const expandedFolderIds = ref<string[]>([]); - const loading = ref(false); - const treeLoading = ref(false); - - // 计算属性 - const currentFolderName = computed(() => { - if (breadcrumbPath.value.length === 0) { - return rootFolderName; - } - return breadcrumbPath.value[breadcrumbPath.value.length - 1]?.name || rootFolderName; - }); - - const breadcrumbItems = computed((): BreadcrumbItem[] => { - const items: BreadcrumbItem[] = [ - { - title: rootFolderName, - folderId: null, - disabled: currentFolderId.value === null, - isRoot: true, - }, - ]; - - breadcrumbPath.value.forEach((folder, index) => { - items.push({ - title: folder.name, - folderId: folder.folder_id, - disabled: index === breadcrumbPath.value.length - 1, - isRoot: false, - }); - }); - - return items; - }); - - // 内部方法 - const findPathToFolderInternal = ( - nodes: FolderTreeNode[], - targetId: string, - path: FolderTreeNode[] = [] - ): FolderTreeNode[] | null => { - for (const node of nodes) { - if (node.folder_id === targetId) { - return [...path, node]; - } - if (node.children && node.children.length > 0) { - const result = findPathToFolderInternal(node.children, targetId, [...path, node]); - if (result) return result; - } - } - return null; - }; - - const updateBreadcrumb = (folderId: string | null): void => { - if (folderId === null) { - breadcrumbPath.value = []; - return; - } - - const path = findPathToFolderInternal(folderTree.value, folderId); - breadcrumbPath.value = path || []; - }; - - // 公开方法 - const loadFolderTree = async (): Promise<void> => { - treeLoading.value = true; - try { - folderTree.value = await operations.loadFolderTree(); - } finally { - treeLoading.value = false; - } - }; - - const navigateToFolder = async (folderId: string | null): Promise<void> => { - loading.value = true; - try { - currentFolderId.value = folderId; - currentFolders.value = await operations.loadSubFolders(folderId); - updateBreadcrumb(folderId); - } finally { - loading.value = false; - } - }; - - const refreshCurrentFolder = async (): Promise<void> => { - await navigateToFolder(currentFolderId.value); - }; - - const createFolder = async (data: CreateFolderData): Promise<Folder> => { - const folder = await operations.createFolder({ - ...data, - parent_id: data.parent_id ?? currentFolderId.value, - }); - - await Promise.all([refreshCurrentFolder(), loadFolderTree()]); - - return folder; - }; - - const updateFolder = async (data: UpdateFolderData): Promise<void> => { - await operations.updateFolder(data); - await Promise.all([refreshCurrentFolder(), loadFolderTree()]); - }; - - const deleteFolder = async (folderId: string): Promise<void> => { - await operations.deleteFolder(folderId); - await Promise.all([refreshCurrentFolder(), loadFolderTree()]); - }; - - const moveFolder = async (folderId: string, targetParentId: string | null): Promise<void> => { - if (operations.moveFolder) { - await operations.moveFolder(folderId, targetParentId); - } else { - // 如果没有专门的移动方法,使用更新方法 - await operations.updateFolder({ - folder_id: folderId, - parent_id: targetParentId, - }); - } - await Promise.all([refreshCurrentFolder(), loadFolderTree()]); - }; - - const toggleFolderExpansion = (folderId: string): void => { - const index = expandedFolderIds.value.indexOf(folderId); - if (index === -1) { - expandedFolderIds.value.push(folderId); - } else { - expandedFolderIds.value.splice(index, 1); - } - }; - - const setFolderExpansion = (folderId: string, expanded: boolean): void => { - const index = expandedFolderIds.value.indexOf(folderId); - if (expanded && index === -1) { - expandedFolderIds.value.push(folderId); - } else if (!expanded && index !== -1) { - expandedFolderIds.value.splice(index, 1); - } - }; - - const findFolderInTree = (folderId: string): FolderTreeNode | null => { - const findNode = (nodes: FolderTreeNode[]): FolderTreeNode | null => { - for (const node of nodes) { - if (node.folder_id === folderId) { - return node; - } - if (node.children && node.children.length > 0) { - const found = findNode(node.children); - if (found) return found; - } - } - return null; - }; - return findNode(folderTree.value); - }; - - const findPathToFolder = (folderId: string): FolderTreeNode[] => { - return findPathToFolderInternal(folderTree.value, folderId) || []; - }; - - const filterTreeBySearch = (query: string): FolderTreeNode[] => { - if (!query) return folderTree.value; - - const lowerQuery = query.toLowerCase(); - - const filterNodes = (nodes: FolderTreeNode[]): FolderTreeNode[] => { - return nodes - .filter((node) => { - const matches = node.name.toLowerCase().includes(lowerQuery); - const childMatches = filterNodes(node.children || []); - return matches || childMatches.length > 0; - }) - .map((node) => ({ - ...node, - children: filterNodes(node.children || []), - })); - }; - - return filterNodes(folderTree.value); - }; - - // 自动加载 - if (autoLoad) { - loadFolderTree(); - navigateToFolder(null); - } - - return { - // 状态 - folderTree, - currentFolderId, - currentFolders, - breadcrumbPath, - expandedFolderIds, - loading, - treeLoading, - - // 计算属性 - currentFolderName, - breadcrumbItems, - - // 方法 - loadFolderTree, - navigateToFolder, - refreshCurrentFolder, - createFolder, - updateFolder, - deleteFolder, - moveFolder, - toggleFolderExpansion, - setFolderExpansion, - findFolderInTree, - findPathToFolder, - filterTreeBySearch, - }; -} - -/** - * 收集文件夹及其所有子文件夹的 ID - * 用于禁用移动对话框中不能选择的目标 - */ -export function collectFolderAndChildrenIds( - folderTree: FolderTreeNode[], - folderId: string -): string[] { - const ids: string[] = [folderId]; - - const collectChildIds = (nodes: FolderTreeNode[]): boolean => { - for (const node of nodes) { - if (node.folder_id === folderId) { - const collectAllChildren = (children: FolderTreeNode[]) => { - for (const child of children) { - ids.push(child.folder_id); - if (child.children) { - collectAllChildren(child.children); - } - } - }; - if (node.children) { - collectAllChildren(node.children); - } - return true; - } - if (node.children && collectChildIds(node.children)) { - return true; - } - } - return false; - }; - - collectChildIds(folderTree); - return ids; -} - -export default useFolderManager; diff --git a/legacy-dashboard/src/components/platform/AddNewPlatform.vue b/legacy-dashboard/src/components/platform/AddNewPlatform.vue deleted file mode 100644 index eebfe626..00000000 --- a/legacy-dashboard/src/components/platform/AddNewPlatform.vue +++ /dev/null @@ -1,2050 +0,0 @@ -<template> - <v-dialog - v-model="showDialog" - max-width="800px" - max-height="90%" - @after-enter="prepareData" - > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ - updatingMode - ? `${tm('dialog.edit')} ${updatingPlatformConfig.id} ${tm( - 'dialog.adapter', - )}` - : tm('dialog.addPlatform') - }} - </v-card-title> - <v-card-text - ref="dialogScrollContainer" - class="pa-4 ml-2" - style="overflow-y: auto" - > - <div class="d-flex align-start" style="width: 100%"> - <div> - <v-icon icon="mdi-numeric-1-circle" class="mr-3"></v-icon> - </div> - <div style="flex: 1"> - <h3> - {{ tm("createDialog.step1Title") }} - </h3> - <small style="color: grey">{{ - tm("createDialog.step1Hint") - }}</small> - <div> - <div v-if="!updatingMode"> - <v-select - v-model="selectedPlatformType" - :items="Object.keys(platformTemplates)" - item-title="name" - item-value="name" - :label="tm('createDialog.platformTypeLabel')" - variant="outlined" - rounded="md" - dense - hide-details - class="mt-6" - style="max-width: 30%; min-width: 300px" - > - <template v-slot:item="{ props: itemProps, item }"> - <v-list-item v-bind="itemProps"> - <template v-slot:prepend> - <img - :src=" - getPlatformIcon(platformTemplates[item.raw].type) - " - style=" - width: 32px; - height: 32px; - object-fit: contain; - margin-right: 16px; - " - /> - </template> - </v-list-item> - </template> - </v-select> - <div class="mt-3" v-if="selectedPlatformConfig"> - <div v-if="isLarkPlatform"> - <div class="creation-mode-title mt-4 mb-1"> - {{ tm("registrationAction.mode.title") }} - </div> - <v-radio-group - v-model="larkCreationMode" - class="creation-mode-group" - hide-details - > - <v-radio - value="scan" - :label="tm('registrationAction.mode.scan')" - ></v-radio> - <v-radio - value="manual" - :label="tm('registrationAction.mode.larkManual')" - ></v-radio> - </v-radio-group> - - <div - v-if="larkCreationMode === 'scan'" - class="registration-inline mt-3" - > - <v-text-field - :model-value="selectedPlatformConfig.id || ''" - :label="tm('registrationAction.platformIdLabel')" - :error="Boolean(scanPlatformIdError)" - :error-messages="scanPlatformIdError" - variant="outlined" - density="compact" - hide-details="auto" - class="registration-platform-id-field" - @update:model-value="setScanPlatformId" - /> - <PlatformRegistrationAction - :platform-config="selectedPlatformConfig" - :active="larkCreationMode === 'scan'" - @created="handlePlatformRegistrationCreated" - @success="showSuccess" - @error="showError" - /> - </div> - - <div v-else-if="larkCreationMode === 'manual'" class="mt-2"> - <div class="platform-action-row"> - <v-btn - color="info" - variant="tonal" - @click="openTutorial" - class="mt-2" - > - <v-icon start>mdi-book-open-variant</v-icon> - {{ tm("dialog.viewTutorial") }} - </v-btn> - </div> - <AstrBotConfig - :iterable="selectedPlatformConfig" - :metadata="metadata['platform_group']?.metadata" - metadataKey="platform" - /> - </div> - </div> - - <div v-else-if="isDingtalkPlatform"> - <div class="creation-mode-title mt-4 mb-1"> - {{ tm("registrationAction.mode.title") }} - </div> - <v-radio-group - v-model="dingtalkCreationMode" - class="creation-mode-group" - hide-details - > - <v-radio - value="scan" - :label="tm('registrationAction.mode.scan')" - ></v-radio> - <v-radio - value="manual" - :label="tm('registrationAction.mode.manual')" - ></v-radio> - </v-radio-group> - - <div - v-if="dingtalkCreationMode === 'scan'" - class="registration-inline mt-3" - > - <v-text-field - :model-value="selectedPlatformConfig.id || ''" - :label="tm('registrationAction.platformIdLabel')" - :error="Boolean(scanPlatformIdError)" - :error-messages="scanPlatformIdError" - variant="outlined" - density="compact" - hide-details="auto" - class="registration-platform-id-field" - @update:model-value="setScanPlatformId" - /> - <PlatformRegistrationAction - :platform-config="selectedPlatformConfig" - :active="dingtalkCreationMode === 'scan'" - @created="handlePlatformRegistrationCreated" - @success="showSuccess" - @error="showError" - /> - </div> - - <div - v-else-if="dingtalkCreationMode === 'manual'" - class="mt-2" - > - <div class="platform-action-row"> - <v-btn - color="info" - variant="tonal" - @click="openTutorial" - class="mt-2" - > - <v-icon start>mdi-book-open-variant</v-icon> - {{ tm("dialog.viewTutorial") }} - </v-btn> - </div> - <AstrBotConfig - :iterable="selectedPlatformConfig" - :metadata="metadata['platform_group']?.metadata" - metadataKey="platform" - /> - </div> - </div> - - <div v-else-if="isQqOfficialPlatform"> - <div class="creation-mode-title mt-4 mb-1"> - {{ tm("registrationAction.mode.title") }} - </div> - <v-radio-group - v-model="qqOfficialCreationMode" - class="creation-mode-group" - hide-details - > - <v-radio - value="scan" - :label="tm('registrationAction.mode.scan')" - ></v-radio> - <v-radio - value="manual" - :label="tm('registrationAction.mode.manual')" - ></v-radio> - </v-radio-group> - - <div - v-if="qqOfficialCreationMode === 'scan'" - class="registration-inline mt-3" - > - <v-text-field - :model-value="selectedPlatformConfig.id || ''" - :label="tm('registrationAction.platformIdLabel')" - :error="Boolean(scanPlatformIdError)" - :error-messages="scanPlatformIdError" - variant="outlined" - density="compact" - hide-details="auto" - class="registration-platform-id-field" - @update:model-value="setScanPlatformId" - /> - <PlatformRegistrationAction - :platform-config="selectedPlatformConfig" - :active="qqOfficialCreationMode === 'scan'" - @created="handlePlatformRegistrationCreated" - @success="showSuccess" - @error="showError" - /> - </div> - - <div - v-else-if="qqOfficialCreationMode === 'manual'" - class="mt-2" - > - <div class="platform-action-row"> - <v-btn - color="info" - variant="tonal" - @click="openTutorial" - class="mt-2" - > - <v-icon start>mdi-book-open-variant</v-icon> - {{ tm("dialog.viewTutorial") }} - </v-btn> - </div> - <AstrBotConfig - :iterable="selectedPlatformConfig" - :metadata="metadata['platform_group']?.metadata" - metadataKey="platform" - /> - </div> - </div> - - <div - v-else-if="isWeixinOcPlatform" - class="registration-inline mt-4" - > - <v-text-field - :model-value="selectedPlatformConfig.id || ''" - :label="tm('registrationAction.platformIdLabel')" - :error="Boolean(scanPlatformIdError)" - :error-messages="scanPlatformIdError" - variant="outlined" - density="compact" - hide-details="auto" - class="registration-platform-id-field" - @update:model-value="setScanPlatformId" - /> - <PlatformRegistrationAction - :platform-config="selectedPlatformConfig" - :active="isWeixinOcPlatform" - @created="handlePlatformRegistrationCreated" - @success="showSuccess" - @error="showError" - /> - </div> - - <div v-else class="mt-2"> - <div class="platform-action-row"> - <v-btn - color="info" - variant="tonal" - @click="openTutorial" - class="mt-2" - > - <v-icon start>mdi-book-open-variant</v-icon> - {{ tm("dialog.viewTutorial") }} - </v-btn> - </div> - <AstrBotConfig - :iterable="selectedPlatformConfig" - :metadata="metadata['platform_group']?.metadata" - metadataKey="platform" - /> - </div> - </div> - </div> - <div v-else> - <v-text-field - :label="tm('createDialog.platformTypeLabel')" - variant="outlined" - rounded="md" - dense - hide-details - class="mt-6" - style="max-width: 30%; min-width: 300px" - v-model="updatingPlatformConfig.type" - disabled - ></v-text-field> - <div class="mt-3"> - <div class="mt-2"> - <AstrBotConfig - :iterable="updatingPlatformConfig" - :metadata="metadata['platform_group']?.metadata" - metadataKey="platform" - /> - </div> - </div> - </div> - </div> - </div> - </div> - - <div class="d-flex align-start mt-6"> - <div> - <v-icon icon="mdi-numeric-2-circle" class="mr-3"></v-icon> - </div> - <div style="flex: 1"> - <div class="d-flex align-center justify-space-between"> - <div> - <div class="d-flex align-center"> - <h3> - {{ tm("createDialog.configFileTitle") }} - </h3> - <v-chip - size="x-small" - color="primary" - variant="tonal" - rounded="sm" - class="ml-2" - v-if="!updatingMode" - >{{ tm("createDialog.optional") }}</v-chip - > - </div> - <small style="color: grey">{{ - tm("createDialog.configHint") - }}</small> - <small style="color: grey" v-if="!updatingMode">{{ - tm("createDialog.configDefaultHint") - }}</small> - </div> - <div> - <v-btn - variant="text" - icon - @click="toggleConfigSection" - class="mt-2" - > - <v-icon>{{ - showConfigSection ? "mdi-chevron-up" : "mdi-chevron-down" - }}</v-icon> - </v-btn> - </div> - </div> - - <div v-if="showConfigSection"> - <div v-if="!updatingMode"> - <v-radio-group - class="mt-2" - v-model="aBConfigRadioVal" - hide-details="true" - > - <v-radio value="0"> - <template v-slot:label> - <span>{{ tm("createDialog.useExistingConfig") }}</span> - </template> - </v-radio> - <div - class="d-flex align-center ml-10 my-2" - v-if="aBConfigRadioVal === '0'" - > - <v-select - v-model="selectedAbConfId" - :items="configInfoList" - item-title="name" - item-value="id" - :label="tm('createDialog.selectConfigLabel')" - variant="outlined" - rounded="md" - dense - hide-details - style="max-width: 30%; min-width: 200px" - > - </v-select> - <v-btn - icon - variant="text" - density="comfortable" - class="ml-2" - :disabled="!selectedAbConfId" - @click="openConfigDrawer(selectedAbConfId)" - > - <v-icon>mdi-arrow-top-right-thick</v-icon> - </v-btn> - </div> - <v-radio - value="1" - :label="tm('createDialog.createNewConfig')" - > - </v-radio> - <div - class="d-flex align-center" - v-if="aBConfigRadioVal === '1'" - > - <v-text-field - v-model="selectedAbConfId" - :label="tm('createDialog.newConfigNameLabel')" - variant="outlined" - rounded="md" - dense - hide-details - style="max-width: 30%; min-width: 200px" - class="ml-10 my-2" - > - </v-text-field> - </div> - </v-radio-group> - - <!-- 现有配置文件预览区域 --> - <!-- <div v-if="aBConfigRadioVal === '0' && selectedAbConfId" class="mt-4"> - <div v-if="configPreviewLoading" class="d-flex justify-center py-4"> - <v-progress-circular indeterminate color="primary"></v-progress-circular> - </div> - <div v-else-if="selectedConfigData && selectedConfigMetadata" class="config-preview-container"> - <h4 class="mb-3">配置文件预览</h4> - <AstrBotCoreConfigWrapper :metadata="selectedConfigMetadata" :config_data="selectedConfigData" - readonly="true" /> - </div> - <div v-else class="text-center py-4 text-grey"> - <v-icon>mdi-information-outline</v-icon> - <p class="mt-2">无法加载配置文件预览</p> - </div> - </div> --> - - <!-- 新配置文件编辑区域 --> - <div v-if="aBConfigRadioVal === '1'" class="mt-4"> - <div - v-if="newConfigLoading" - class="d-flex justify-center py-4" - > - <v-progress-circular - indeterminate - color="primary" - ></v-progress-circular> - </div> - <div - v-else-if="newConfigData && newConfigMetadata" - class="config-preview-container" - > - <h4 class="mb-3"> - {{ tm("createDialog.newConfigTitle") }} - </h4> - <AstrBotCoreConfigWrapper - :metadata="newConfigMetadata" - :config_data="newConfigData" - /> - </div> - <div v-else class="text-center py-4 text-grey"> - <v-icon>mdi-information-outline</v-icon> - <p class="mt-2"> - {{ tm("createDialog.newConfigLoadFailed") }} - </p> - </div> - </div> - </div> - - <div v-else> - <div class="mb-3 d-flex align-center justify-space-between"> - <div> - <v-btn - v-if="isEditingRoutes" - color="primary" - variant="tonal" - @click="addNewRoute" - size="small" - > - <v-icon start>mdi-plus</v-icon> - {{ tm("createDialog.addRouteRule") }} - </v-btn> - </div> - <v-btn - :color="isEditingRoutes ? 'grey' : 'primary'" - variant="tonal" - size="small" - @click="toggleEditMode" - > - <v-icon start>{{ - isEditingRoutes ? "mdi-eye" : "mdi-pencil" - }}</v-icon> - {{ - isEditingRoutes - ? tm("createDialog.viewMode") - : tm("createDialog.editMode") - }} - </v-btn> - </div> - - <v-data-table - :headers="routeTableHeaders" - :items="platformRoutes" - item-value="umop" - :no-data-text="tm('createDialog.noRouteRules')" - hide-default-footer - :items-per-page="-1" - class="mt-2" - variant="outlined" - > - <template v-slot:item.source="{ item }"> - <div class="route-source-cell"> - <div - class="d-flex align-center route-source-input-row" - :class="{ - 'route-source-input-row--editing': isEditingRoutes, - }" - style="min-width: 250px" - > - <v-autocomplete - v-if=" - isEditingRoutes && - updatingMode && - getRouteSourceMode(item) === 'known' - " - v-model="item.sourceUmo" - :items="filteredKnownRouteUmoItems" - :loading="loadingKnownRouteUmos" - variant="outlined" - density="compact" - hide-details - clearable - :placeholder=" - tm('createDialog.routeSource.selectPlaceholder') - " - :no-data-text="tm('createDialog.routeSource.noData')" - style="min-width: 260px" - @update:model-value=" - applyKnownRouteSource(item, $event) - " - @focus="loadKnownRouteUmos" - > - <template v-slot:item="{ props, item: sourceItem }"> - <v-list-item v-bind="props"> - <template v-slot:title> - <UmoDisplay - v-bind=" - getKnownRouteUmoDisplayProps(sourceItem.raw) - " - compact - :show-info="false" - /> - </template> - </v-list-item> - </template> - <template v-slot:selection="{ item: sourceItem }"> - <v-chip - v-if=" - sourceItem && - getKnownRouteUmoSelectionText(sourceItem.raw) - " - size="small" - variant="tonal" - color="primary" - class="umo-selection-chip" - > - {{ - getKnownRouteUmoSelectionText(sourceItem.raw) - }} - </v-chip> - </template> - </v-autocomplete> - <template v-else> - <v-select - v-if="isEditingRoutes" - v-model="item.messageType" - :items="messageTypeOptions" - item-title="label" - item-value="value" - variant="outlined" - density="compact" - hide-details - style="max-width: 140px" - > - </v-select> - <small v-else>{{ - getMessageTypeLabel(item.messageType) - }}</small> - <small class="mx-1">:</small> - <v-text-field - v-if="isEditingRoutes" - v-model="item.sessionId" - variant="outlined" - density="compact" - hide-details - :placeholder=" - tm('createDialog.sessionIdPlaceholder') - " - > - </v-text-field> - <small v-else>{{ - item.sessionId === "*" - ? tm("createDialog.allSessions") - : item.sessionId - }}</small> - </template> - </div> - <span - v-if="updatingMode && isEditingRoutes" - class="route-source-mode-link" - @click="toggleRouteSourceMode(item)" - > - {{ getRouteSourceModeLinkText(item) }} - </span> - </div> - </template> - - <template v-slot:item.configId="{ item }"> - <div class="d-flex align-center"> - <v-select - v-if="isEditingRoutes" - v-model="item.configId" - :items="configInfoList" - item-title="name" - item-value="id" - variant="outlined" - density="compact" - style="min-width: 200px" - hide-details - > - </v-select> - <div v-else> - <small>{{ getConfigName(item.configId) }}</small> - </div> - <v-btn - icon - variant="text" - density="compact" - class="ml-2" - :disabled="!item.configId" - @click="openConfigDrawer(item.configId)" - > - <v-icon size="18">mdi-arrow-top-right-thick</v-icon> - </v-btn> - </div> - <small - v-if=" - configInfoList.findIndex( - (c) => c.id === item.configId, - ) === -1 - " - style="color: red" - class="ml-2" - >{{ tm("createDialog.configMissing") }}</small - > - </template> - - <template v-slot:item.actions="{ item, index }"> - <div v-if="isEditingRoutes" class="d-flex align-center"> - <v-btn - icon - size="x-small" - variant="text" - @click="moveRouteUp(index)" - :disabled="index === 0" - > - <v-icon>mdi-arrow-up</v-icon> - </v-btn> - <v-btn - icon - size="x-small" - variant="text" - @click="moveRouteDown(index)" - :disabled="index === platformRoutes.length - 1" - > - <v-icon>mdi-arrow-down</v-icon> - </v-btn> - <v-btn - icon - size="x-small" - variant="text" - color="error" - @click="deleteRoute(index)" - > - <v-icon>mdi-delete</v-icon> - </v-btn> - </div> - <span v-else class="text-grey">-</span> - </template> - </v-data-table> - <small class="ml-2 mt-2 d-block" style="color: grey">{{ - tm("createDialog.routeHint") - }}</small> - </div> - </div> - </div> - </div> - </v-card-text> - - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="closeDialog">{{ tm("dialog.cancel") }}</v-btn> - <v-btn - :disabled="!canSave" - color="primary" - variant="tonal" - v-if="!updatingMode" - @click="newPlatform" - :loading="loading" - >{{ tm("dialog.save") }}</v-btn - > - <v-btn - :disabled="!selectedAbConfId" - color="primary" - variant="tonal" - v-else - @click="newPlatform" - :loading="loading" - >{{ tm("dialog.save") }}</v-btn - > - </v-card-actions> - </v-card> - </v-dialog> - - <!-- ID冲突确认对话框 --> - <v-dialog v-model="showIdConflictDialog" max-width="450" persistent> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - <v-icon start class="me-2">mdi-alert-circle-outline</v-icon> - {{ tm("dialog.idConflict.title") }} - </v-card-title> - <v-card-text class="py-4 text-body-1 text-medium-emphasis"> - {{ tm("dialog.idConflict.message", { id: conflictId }) }} - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn - color="grey" - variant="text" - @click="handleIdConflictConfirm(false)" - >{{ tm("dialog.idConflict.confirm") }}</v-btn - > - </v-card-actions> - </v-card> - </v-dialog> - - <!-- 安全警告对话框 --> - <v-dialog v-model="showOneBotEmptyTokenWarnDialog" max-width="600" persistent> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ tm("dialog.securityWarning.title") }} - </v-card-title> - <v-card-text class="py-4"> - <p>{{ tm("dialog.securityWarning.aiocqhttpTokenMissing") }}</p> - <span - ><a - href="https://docs.astrbot.app/platform/aiocqhttp.html" - target="_blank" - >{{ tm("dialog.securityWarning.learnMore") }}</a - ></span - > - </v-card-text> - <v-card-actions class="px-4 pb-4"> - <v-spacer></v-spacer> - <v-btn - color="error" - variant="tonal" - @click="handleOneBotEmptyTokenWarningDismiss(true)" - > - {{ tm("createDialog.warningContinue") }} - </v-btn> - <v-btn - color="primary" - variant="text" - @click="handleOneBotEmptyTokenWarningDismiss(false)" - > - {{ tm("createDialog.warningEditAgain") }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-overlay - v-model="showConfigDrawer" - class="config-drawer-overlay" - location="right" - transition="slide-x-reverse-transition" - :scrim="true" - @click:outside="closeConfigDrawer" - > - <v-card class="config-drawer-card" elevation="12"> - <div class="config-drawer-header"> - <div> - <span class="text-h6">{{ - tm("createDialog.configDrawerTitle") - }}</span> - <div v-if="configDrawerTargetId" class="text-caption text-grey"> - {{ tm("createDialog.configDrawerIdLabel") }}: - {{ configDrawerTargetId }} - </div> - </div> - <v-btn icon variant="text" @click="closeConfigDrawer"> - <v-icon>mdi-close</v-icon> - </v-btn> - </div> - <v-divider></v-divider> - <div class="config-drawer-content"> - <ConfigPage - v-if="showConfigDrawer" - :initial-config-id="configDrawerTargetId" - /> - </div> - </v-card> - </v-overlay> -</template> - -<script> -import { botApi, configProfileApi, configRouteApi, fileApi, sessionApi } from "@/api/v1"; -import { useModuleI18n } from "@/i18n/composables"; -import { - getPlatformIcon, - getPlatformDescription, - getTutorialLink, -} from "@/utils/platformUtils"; -import AstrBotConfig from "@/components/shared/AstrBotConfig.vue"; -import AstrBotCoreConfigWrapper from "@/components/config/AstrBotCoreConfigWrapper.vue"; -import ConfigPage from "@/views/ConfigPage.vue"; -import PlatformRegistrationAction from "@/components/platform/PlatformRegistrationAction.vue"; -import UmoDisplay from "@/components/shared/UmoDisplay.vue"; - -export default { - name: "AddNewPlatform", - components: { - AstrBotConfig, - AstrBotCoreConfigWrapper, - ConfigPage, - PlatformRegistrationAction, - UmoDisplay, - }, - emits: ["update:show", "show-toast", "refresh-config"], - props: { - show: { - type: Boolean, - default: false, - }, - metadata: { - type: Object, - default: () => ({}), - }, - config_data: { - type: Object, - default: () => ({}), - }, - updatingMode: { - type: Boolean, - default: false, - }, - updatingPlatformConfig: { - type: Object, - default: null, - }, - }, - data() { - return { - selectedPlatformType: null, - selectedPlatformConfig: null, - larkCreationMode: "", - dingtalkCreationMode: "", - qqOfficialCreationMode: "", - scanPlatformIdCustomized: false, - - aBConfigRadioVal: "0", - selectedAbConfId: "default", - configInfoList: [], - - // 选中的配置文件预览数据 - selectedConfigData: null, - selectedConfigMetadata: null, - configPreviewLoading: false, - - // 新配置文件相关数据 - newConfigData: null, - newConfigMetadata: null, - newConfigLoading: false, - - // 平台配置文件表格(已弃用,改用 platformRoutes) - platformConfigs: [], - - // 平台路由表 - platformRoutes: [], - isEditingRoutes: false, // 编辑模式开关 - knownRouteUmos: [], - knownRouteUmoInfoMap: {}, - loadingKnownRouteUmos: false, - - // ID冲突确认对话框 - showIdConflictDialog: false, - conflictId: "", - idConflictResolve: null, - - // OneBot Empty Token Warning #2639 - showOneBotEmptyTokenWarnDialog: false, - oneBotEmptyTokenWarningResolve: null, - - loading: false, - - showConfigSection: false, - - // 配置抽屉 - showConfigDrawer: false, - configDrawerTargetId: null, - - // 保存更新前的平台 ID,防止用户修改 ID 后丢失原始定位 - originalUpdatingPlatformId: null, - }; - }, - setup() { - const { tm } = useModuleI18n("features/platform"); - return { tm }; - }, - computed: { - showDialog: { - get() { - return this.show; - }, - set(value) { - this.$emit("update:show", value); - }, - }, - platformTemplates() { - return ( - this.metadata["platform_group"]?.metadata?.platform?.config_template || - {} - ); - }, - canSave() { - // 基本条件:必须选择平台类型 - if (!this.selectedPlatformType) { - return false; - } - - if (!this.isPlatformIdValid(this.selectedPlatformConfig?.id)) { - return false; - } - - if (this.isLarkPlatform && !this.larkCreationMode) { - return false; - } - - if (this.isLarkPlatform && this.larkCreationMode === "scan") { - if ( - !this.selectedPlatformConfig?.app_id || - !this.selectedPlatformConfig?.app_secret - ) { - return false; - } - } - - if (this.isDingtalkPlatform && !this.dingtalkCreationMode) { - return false; - } - - if (this.isDingtalkPlatform && this.dingtalkCreationMode === "scan") { - if ( - !this.selectedPlatformConfig?.client_id || - !this.selectedPlatformConfig?.client_secret - ) { - return false; - } - } - - if (this.isQqOfficialPlatform && !this.qqOfficialCreationMode) { - return false; - } - - if (this.isQqOfficialPlatform && this.qqOfficialCreationMode === "scan") { - if ( - !this.selectedPlatformConfig?.appid || - !this.selectedPlatformConfig?.secret - ) { - return false; - } - } - - if ( - this.isWeixinOcPlatform && - !this.selectedPlatformConfig?.weixin_oc_token - ) { - return false; - } - - // 如果是使用现有配置文件模式 - if (this.aBConfigRadioVal === "0") { - return !!this.selectedAbConfId; - } - - // 如果是创建新配置文件模式 - if (this.aBConfigRadioVal === "1") { - // 需要配置文件名称,且新配置数据已加载 - return !!(this.selectedAbConfId && this.newConfigData); - } - - return false; - }, - configTableHeaders() { - return [ - { - title: this.tm("createDialog.configTableHeaders.configId"), - key: "name", - sortable: false, - }, - { - title: this.tm("createDialog.configTableHeaders.scope"), - key: "scope", - sortable: false, - }, - ]; - }, - routeTableHeaders() { - return [ - { - title: this.tm("createDialog.routeTableHeaders.source"), - key: "source", - sortable: false, - width: "60%", - }, - { - title: this.tm("createDialog.routeTableHeaders.config"), - key: "configId", - sortable: false, - width: "20%", - }, - { - title: this.tm("createDialog.routeTableHeaders.actions"), - key: "actions", - sortable: false, - align: "center", - width: "20%", - }, - ]; - }, - messageTypeOptions() { - return [ - { label: this.tm("createDialog.messageTypeOptions.all"), value: "*" }, - { - label: this.tm("createDialog.messageTypeOptions.group"), - value: "GroupMessage", - }, - { - label: this.tm("createDialog.messageTypeOptions.friend"), - value: "FriendMessage", - }, - ]; - }, - routePlatformId() { - if (this.updatingMode) { - return ( - this.updatingPlatformConfig?.id || - this.originalUpdatingPlatformId || - "" - ); - } - return this.selectedPlatformConfig?.id || ""; - }, - filteredKnownRouteUmoItems() { - const platformId = this.routePlatformId; - return this.knownRouteUmos.filter((umo) => { - const parsed = this.parseUmop(umo); - return parsed && parsed.platform === platformId; - }); - }, - isLarkPlatform() { - return this.selectedPlatformConfig?.type === "lark"; - }, - isWeixinOcPlatform() { - return this.selectedPlatformConfig?.type === "weixin_oc"; - }, - isDingtalkPlatform() { - return this.selectedPlatformConfig?.type === "dingtalk"; - }, - isQqOfficialPlatform() { - return ["qq_official", "qq_official_webhook"].includes( - this.selectedPlatformConfig?.type, - ); - }, - scanPlatformIdError() { - const platformId = String(this.selectedPlatformConfig?.id || ""); - if (!platformId) { - return this.tm("registrationAction.platformIdRequired"); - } - if (!this.isPlatformIdValid(platformId)) { - return this.tm("registrationAction.platformIdInvalid"); - } - return ""; - }, - }, - watch: { - selectedPlatformType(newType) { - if (newType && this.platformTemplates[newType]) { - this.selectedPlatformConfig = JSON.parse( - JSON.stringify(this.platformTemplates[newType]), - ); - this.larkCreationMode = ""; - this.dingtalkCreationMode = ""; - this.qqOfficialCreationMode = ""; - this.scanPlatformIdCustomized = false; - } else { - this.selectedPlatformConfig = null; - this.larkCreationMode = ""; - this.dingtalkCreationMode = ""; - this.qqOfficialCreationMode = ""; - this.scanPlatformIdCustomized = false; - } - }, - selectedAbConfId(newConfigId) { - // 当选择配置文件改变时,获取配置文件数据用于预览 - if (!this.updatingMode && this.aBConfigRadioVal === "0" && newConfigId) { - this.getConfigForPreview(newConfigId); - } else { - this.selectedConfigData = null; - this.selectedConfigMetadata = null; - } - }, - aBConfigRadioVal(newVal) { - // 当切换到创建新配置文件时,获取默认配置模板 - if (newVal === "1") { - this.selectedConfigData = null; - this.selectedConfigMetadata = null; - this.selectedAbConfId = null; - this.getDefaultConfigTemplate(); - } else if (newVal === "0") { - // 如果切换回使用现有配置文件但没有选择配置文件,重置为默认 - this.newConfigData = null; - this.newConfigMetadata = null; - if (!this.selectedAbConfId) { - this.selectedAbConfId = "default"; - } - } - }, - showIdConflictDialog(newValue) { - if (!newValue && this.idConflictResolve) { - this.idConflictResolve(false); - this.idConflictResolve = null; - } - }, - showOneBotEmptyTokenWarnDialog(newValue) { - if (!newValue && this.oneBotEmptyTokenWarningResolve) { - this.oneBotEmptyTokenWarningResolve(true); - this.oneBotEmptyTokenWarningResolve = null; - } - }, - // 监听更新模式变化,获取相关配置文件 - updatingPlatformConfig: { - handler(newConfig) { - if (this.updatingMode && newConfig && newConfig.id) { - this.originalUpdatingPlatformId = newConfig.id; - this.getPlatformConfigs(newConfig.id); - } - }, - immediate: true, - }, - showConfigSection(newValue) { - if (newValue && !this.updatingMode && this.aBConfigRadioVal === "0") { - this.getConfigForPreview(this.selectedAbConfId); - } - if (newValue) { - this.$nextTick(() => { - this.scrollDialogToBottom(); - }); - } - }, - // 监听编辑模式变化,自动展开配置文件部分 - updatingMode: { - handler(newValue) { - if (newValue) { - this.showConfigSection = true; - // 编辑模式下默认不开启路由编辑模式,用户需要手动点击 - this.isEditingRoutes = false; - } - }, - immediate: true, - }, - }, - methods: { - getPlatformIcon(platformType) { - // Check for plugin-provided logo_token first - const template = this.platformTemplates?.[platformType]; - if (template && template.logo_token) { - return fileApi.tokenUrl(template.logo_token); - } - return getPlatformIcon(platformType); - }, - getPlatformDescription, - resetForm() { - this.selectedPlatformType = null; - this.selectedPlatformConfig = null; - this.larkCreationMode = ""; - this.dingtalkCreationMode = ""; - this.qqOfficialCreationMode = ""; - this.scanPlatformIdCustomized = false; - - this.aBConfigRadioVal = "0"; - this.selectedAbConfId = "default"; - - // 重置配置预览数据 - this.selectedConfigData = null; - this.selectedConfigMetadata = null; - this.configPreviewLoading = false; - - // 重置新配置文件数据 - this.newConfigData = null; - this.newConfigMetadata = null; - this.newConfigLoading = false; - - this.showConfigSection = false; - this.isEditingRoutes = false; // 重置编辑模式 - this.knownRouteUmos = []; - this.knownRouteUmoInfoMap = {}; - this.loadingKnownRouteUmos = false; - - this.showConfigDrawer = false; - this.configDrawerTargetId = null; - - this.originalUpdatingPlatformId = null; - }, - closeDialog() { - this.resetForm(); - - this.showDialog = false; - }, - async getConfigInfoList() { - await configProfileApi.list().then((res) => { - this.configInfoList = res.data.data.info_list; - }); - }, - - // 获取配置文件数据用于预览 - async getConfigForPreview(configId) { - if (!configId) { - this.selectedConfigData = null; - this.selectedConfigMetadata = null; - return; - } - - this.configPreviewLoading = true; - try { - const response = await configProfileApi.get(configId); - - this.selectedConfigData = response.data.data.config; - this.selectedConfigMetadata = response.data.data.metadata; - } catch (error) { - console.error("获取配置文件预览数据失败:", error); - this.selectedConfigData = null; - this.selectedConfigMetadata = null; - } finally { - this.configPreviewLoading = false; - } - }, - - // 获取默认配置模板用于创建新配置文件 - async getDefaultConfigTemplate() { - this.newConfigLoading = true; - try { - const response = await configProfileApi.schema(); - this.newConfigData = response.data.data.config; - this.newConfigMetadata = response.data.data.metadata; - } catch (error) { - console.error("获取默认配置模板失败:", error); - this.newConfigData = null; - this.newConfigMetadata = null; - } finally { - this.newConfigLoading = false; - } - }, - openTutorial() { - const tutorialUrl = getTutorialLink(this.selectedPlatformConfig.type); - window.open(tutorialUrl, "_blank"); - }, - openConfigDrawer(configId) { - const targetId = configId || "default"; - - if ( - configId && - this.configInfoList.findIndex((c) => c.id === configId) === -1 - ) { - this.showError(this.tm("messages.configNotFoundOpenConfig")); - } - - this.configDrawerTargetId = targetId; - this.showConfigDrawer = true; - }, - closeConfigDrawer() { - this.showConfigDrawer = false; - }, - newPlatform() { - this.loading = true; - if (this.updatingMode) { - if (this.updatingPlatformConfig.type === "aiocqhttp") { - const token = this.updatingPlatformConfig.ws_reverse_token; - if (!token || token.trim() === "") { - this.showOneBotEmptyTokenWarning().then((continueWithWarning) => { - if (continueWithWarning) { - this.updatePlatform(); - } else { - this.loading = false; - } - }); - return; - } - } - this.updatePlatform(); - } else { - this.savePlatform(); - } - }, - async updatePlatform() { - const id = - this.originalUpdatingPlatformId || this.updatingPlatformConfig.id; - if (!id) { - this.loading = false; - this.showError(this.tm("messages.updateMissingPlatformId")); - return; - } - - if (!this.isPlatformIdValid(id)) { - this.loading = false; - this.showError(this.tm("dialog.invalidPlatformId")); - return; - } - - try { - // 更新平台配置 - let resp = await botApi.update(id, this.updatingPlatformConfig); - - if (resp.data.status === "error") { - throw new Error( - resp.data.message || this.tm("messages.platformUpdateFailed"), - ); - } - - // 同时更新路由表 - await this.saveRoutesInternal(); - - this.loading = false; - this.showDialog = false; - this.resetForm(); - this.$emit("refresh-config"); - this.showSuccess(this.tm("messages.updateSuccess")); - } catch (err) { - this.loading = false; - this.showError(err.response?.data?.message || err.message); - } - }, - async savePlatform() { - if (!this.isPlatformIdValid(this.selectedPlatformConfig?.id)) { - this.loading = false; - this.showError(this.tm("dialog.invalidPlatformId")); - return; - } - - // 检查 ID 是否已存在 - const existingPlatform = this.config_data.platform?.find( - (p) => p.id === this.selectedPlatformConfig.id, - ); - if (existingPlatform || this.selectedPlatformConfig.id === "webchat") { - const confirmed = await this.confirmIdConflict( - this.selectedPlatformConfig.id, - ); - if (!confirmed) { - this.loading = false; - return; // 如果用户取消,则中止保存 - } - } - - // 检查 aiocqhttp 适配器的安全设置 - if (this.selectedPlatformConfig.type === "aiocqhttp") { - const token = this.selectedPlatformConfig.ws_reverse_token; - if (!token || token.trim() === "") { - const continueWithWarning = await this.showOneBotEmptyTokenWarning(); - if (!continueWithWarning) { - return; - } - } - } - - try { - // 先保存平台配置 - const res = await botApi.create(this.selectedPlatformConfig); - - // 平台保存成功后,处理配置文件 - await this.handleConfigFile(); - - this.loading = false; - this.showDialog = false; - this.resetForm(); - this.$emit("refresh-config"); - this.showSuccess( - res.data.message || this.tm("messages.addSuccessWithConfig"), - ); - } catch (err) { - this.loading = false; - this.showError(err.response?.data?.message || err.message); - } - }, - - async handleConfigFile() { - if (!this.selectedAbConfId) { - return; - } - - const platformId = this.selectedPlatformConfig.id; - // 生成默认的UMOP:平台ID:*:*(表示该平台的所有消息类型和会话) - const newUmop = `${platformId}:*:*`; - - let configId = null; - - // 第一步:创建或获取配置文件ID - if (this.aBConfigRadioVal === "0") { - // 使用现有配置文件 - configId = this.selectedAbConfId; - } else if (this.aBConfigRadioVal === "1") { - // 创建新配置文件 - configId = await this.createNewConfigFile(this.selectedAbConfId); - } - - if (!configId) { - throw new Error(this.tm("messages.configIdMissing")); - } - - // 第二步:统一更新路由表 - await this.updateRoutingTable(newUmop, configId); - }, - - async updateRoutingTable(umop, configId) { - try { - await configRouteApi.upsert(umop, { config_id: configId }); - - console.log(`成功更新路由表: ${umop} -> ${configId}`); - } catch (err) { - console.error("更新路由表失败:", err); - const errorMessage = err.response?.data?.message || err.message; - throw new Error( - this.tm("messages.routingUpdateFailed", { message: errorMessage }), - ); - } - }, - - async createNewConfigFile(configName) { - try { - // 准备配置数据,如果是创建模式且有新配置数据,使用用户填写的配置 - const configData = - this.aBConfigRadioVal === "1" && this.newConfigData - ? this.newConfigData - : undefined; - - // 创建新的配置文件(不传入umop) - const createRes = await configProfileApi.create({ - name: configName, - config: configData, // 传入用户配置的数据 - }); - - const newConfigId = createRes.data.data.conf_id; - console.log(`成功创建新配置文件 ${configName},ID: ${newConfigId}`); - - return newConfigId; - } catch (err) { - console.error("创建新配置文件失败:", err); - const errorMessage = err.response?.data?.message || err.message; - throw new Error( - this.tm("messages.createConfigFailed", { message: errorMessage }), - ); - } - }, - - confirmIdConflict(id) { - this.conflictId = id; - this.showIdConflictDialog = true; - return new Promise((resolve) => { - this.idConflictResolve = resolve; - }); - }, - - handleIdConflictConfirm(confirmed) { - if (this.idConflictResolve) { - this.idConflictResolve(confirmed); - } - this.showIdConflictDialog = false; - }, - - showOneBotEmptyTokenWarning() { - this.showOneBotEmptyTokenWarnDialog = true; - return new Promise((resolve) => { - this.oneBotEmptyTokenWarningResolve = resolve; - }); - }, - - handleOneBotEmptyTokenWarningDismiss(continueWithWarning) { - this.showOneBotEmptyTokenWarnDialog = false; - if (this.oneBotEmptyTokenWarningResolve) { - this.oneBotEmptyTokenWarningResolve(continueWithWarning); - this.oneBotEmptyTokenWarningResolve = null; - } - - if (!continueWithWarning) { - this.loading = false; - } - }, - - showSuccess(message) { - this.$emit("show-toast", { message: message, type: "success" }); - }, - - showError(message) { - this.$emit("show-toast", { message: message, type: "error" }); - }, - - setScanPlatformId(value) { - if (!this.selectedPlatformConfig) { - return; - } - this.scanPlatformIdCustomized = true; - this.selectedPlatformConfig.id = String(value || ""); - }, - - buildRandomPlatformIdSuffix() { - const letters = "abcdefghijklmnopqrstuvwxyz"; - let suffix = "_"; - for (let i = 0; i < 4; i += 1) { - suffix += letters[Math.floor(Math.random() * letters.length)]; - } - return suffix; - }, - - sanitizePlatformIdPart(value) { - return String(value || "") - .trim() - .replace(/\s+/g, "") - .replace(/[!:]/g, "_"); - }, - - handlePlatformRegistrationCreated(data) { - if (!this.selectedPlatformConfig || !data) { - return; - } - if (this.scanPlatformIdCustomized) { - return; - } - const currentId = String(this.selectedPlatformConfig.id || "").trim(); - const platformType = this.selectedPlatformConfig.type; - if (!currentId) { - return; - } - - let suffix = ""; - const explicitSuffix = this.sanitizePlatformIdPart(data.platform_id_suffix); - if (explicitSuffix) { - suffix = - explicitSuffix.startsWith("_") || explicitSuffix.startsWith("-") - ? explicitSuffix - : `_${explicitSuffix}`; - } else if (data.bot_name) { - const safeBotName = this.sanitizePlatformIdPart(data.bot_name); - if (safeBotName) { - suffix = `-${safeBotName}`; - } - } else if (platformType === "weixin_oc" || platformType === "dingtalk") { - suffix = this.buildRandomPlatformIdSuffix(); - } - - if (!suffix) { - return; - } - - if ( - (platformType === "weixin_oc" || platformType === "dingtalk") && - /_[a-z]{4}$/.test(currentId) - ) { - return; - } - - this.selectedPlatformConfig.id = currentId.endsWith(suffix) - ? currentId - : `${currentId}${suffix}`; - }, - - isPlatformIdValid(id) { - if (!id) { - return false; - } - return !/[!:\s]/.test(id); - }, - - // 获取该平台适配器使用的所有配置文件(新版本:直接操作路由表) - async getPlatformConfigs(platformId) { - if (!platformId) { - this.platformRoutes = []; - return; - } - - try { - // 获取路由表 (UMOP -> conf_id) - const routesRes = await configRouteApi.list(); - const routingTable = routesRes.data.data.routing; - - // 过滤出属于该平台的路由,并保持顺序 - const routes = []; - for (const [umop, confId] of Object.entries(routingTable)) { - const parsedUmop = this.parseUmop(umop); - if (this.isParsedUmopMatchPlatform(parsedUmop, platformId)) { - routes.push({ - umop: umop, - originalUmop: umop, // 保存原始 UMOP 用于更新时查找 - sourceMode: "manual", - sourceUmo: parsedUmop.sessionId === "*" ? "" : umop, - messageType: parsedUmop.messageType || "*", - sessionId: parsedUmop.sessionId || "*", - configId: confId, - }); - } - } - - this.platformRoutes = routes; - - // 如果没有路由,添加一个默认的空路由供用户编辑 - if (this.platformRoutes.length === 0) { - this.platformRoutes.push({ - umop: null, - originalUmop: null, - sourceMode: "manual", - sourceUmo: "", - messageType: "*", - sessionId: "*", - configId: "default", - }); - } - } catch (err) { - console.error("获取平台路由配置失败:", err); - this.platformRoutes = []; - } - }, - - async loadKnownRouteUmos() { - if (this.loadingKnownRouteUmos) { - return; - } - - this.loadingKnownRouteUmos = true; - try { - const res = await sessionApi.activeUmos(); - if (res.data.status === "ok") { - const umos = Array.isArray(res.data.data?.umos) - ? res.data.data.umos - : []; - this.knownRouteUmos = Array.from( - new Set([...this.knownRouteUmos, ...umos]), - ); - this.mergeKnownRouteUmoInfos(res.data.data?.umo_infos || []); - } - } catch (err) { - console.error("获取已有消息来源失败:", err); - } finally { - this.loadingKnownRouteUmos = false; - } - }, - - mergeKnownRouteUmoInfos(infos = []) { - const next = { ...this.knownRouteUmoInfoMap }; - for (const info of infos) { - if (info?.umo) { - next[info.umo] = { ...(next[info.umo] || {}), ...info }; - } - } - this.knownRouteUmoInfoMap = next; - }, - - getKnownRouteUmoInfo(umo) { - const parsed = this.parseUmop(umo); - return ( - this.knownRouteUmoInfoMap[umo] || { - umo, - platform: parsed?.platform || "", - message_type: parsed?.messageType || "", - session_id: parsed?.sessionId || umo, - auto_name: "", - user_alias: "", - display_name: umo, - } - ); - }, - - getKnownRouteUmoDisplayProps(umo) { - const info = this.getKnownRouteUmoInfo(umo); - const parsed = this.parseUmop(umo); - return { - umo, - platform: info.platform || parsed?.platform || "", - messageType: info.message_type || parsed?.messageType || "", - sessionId: info.session_id || parsed?.sessionId || "", - autoName: info.auto_name || "", - userAlias: info.user_alias || "", - }; - }, - - getKnownRouteUmoSelectionText(umo) { - if (!umo) return ""; - const info = this.getKnownRouteUmoInfo(umo); - const parsed = this.parseUmop(umo); - const aliasName = info.user_alias || ""; - const autoName = info.auto_name || ""; - if (aliasName && autoName && aliasName !== autoName) { - return `${aliasName}(${autoName})`; - } - return ( - aliasName || - autoName || - (parsed - ? `${this.getMessageTypeLabel(parsed.messageType)}:${ - parsed.sessionId - }` - : umo) - ); - }, - - getRouteSourceMode(route) { - return route.sourceMode || "manual"; - }, - - getRouteSourceModeLinkText(route) { - return this.getRouteSourceMode(route) === "known" - ? this.tm("createDialog.routeSource.switchToManual") - : this.tm("createDialog.routeSource.switchToKnown"); - }, - - toggleRouteSourceMode(route) { - const nextMode = - this.getRouteSourceMode(route) === "known" ? "manual" : "known"; - route.sourceMode = nextMode; - if (nextMode === "known") { - this.loadKnownRouteUmos(); - } - }, - - applyKnownRouteSource(route, umo) { - if (!umo) { - route.sourceUmo = ""; - return; - } - - const parsed = this.parseUmop(umo); - if (!parsed) { - return; - } - route.sourceUmo = umo; - route.messageType = parsed.messageType || "*"; - route.sessionId = parsed.sessionId || "*"; - }, - - // 添加新路由 - addNewRoute() { - this.platformRoutes.push({ - umop: null, - originalUmop: null, - sourceMode: "manual", - sourceUmo: "", - messageType: "*", - sessionId: "*", - configId: "default", - }); - }, - - // 删除路由 - deleteRoute(index) { - this.platformRoutes.splice(index, 1); - }, - - // 上移路由 - moveRouteUp(index) { - if (index > 0) { - const temp = this.platformRoutes[index]; - this.platformRoutes[index] = this.platformRoutes[index - 1]; - this.platformRoutes[index - 1] = temp; - // 强制更新视图 - this.platformRoutes = [...this.platformRoutes]; - } - }, - - // 下移路由 - moveRouteDown(index) { - if (index < this.platformRoutes.length - 1) { - const temp = this.platformRoutes[index]; - this.platformRoutes[index] = this.platformRoutes[index + 1]; - this.platformRoutes[index + 1] = temp; - // 强制更新视图 - this.platformRoutes = [...this.platformRoutes]; - } - }, - - // 内部保存路由表方法(不显示成功提示) - async saveRoutesInternal() { - const originalPlatformId = - this.originalUpdatingPlatformId || this.updatingPlatformConfig?.id; - const newPlatformId = - this.updatingPlatformConfig?.id || originalPlatformId; - - if (!originalPlatformId && !newPlatformId) { - throw new Error(this.tm("messages.platformIdMissing")); - } - - try { - // 获取完整的路由表 - const routesRes = await configRouteApi.list(); - const fullRoutingTable = routesRes.data.data.routing; - - // 删除该平台的所有旧路由 - for (const umop in fullRoutingTable) { - if ( - (originalPlatformId && - this.isUmopMatchPlatform(umop, originalPlatformId)) || - (newPlatformId && this.isUmopMatchPlatform(umop, newPlatformId)) - ) { - delete fullRoutingTable[umop]; - } - } - - // 添加新路由(按顺序) - for (const route of this.platformRoutes) { - const messageType = - route.messageType === "*" ? "*" : route.messageType; - const sessionId = route.sessionId === "*" ? "*" : route.sessionId; - const platformIdForRoute = newPlatformId || originalPlatformId; - const newUmop = `${platformIdForRoute}:${messageType}:${sessionId}`; - - if (route.configId) { - fullRoutingTable[newUmop] = route.configId; - } - } - - // 使用 v1 replace 更新整个路由表 - await configRouteApi.replace({ - routing: fullRoutingTable, - }); - } catch (err) { - console.error("保存路由表失败:", err); - const errorMessage = err.response?.data?.message || err.message; - throw new Error( - this.tm("messages.routingSaveFailed", { message: errorMessage }), - ); - } - }, - - // 切换编辑模式 - toggleEditMode() { - this.isEditingRoutes = !this.isEditingRoutes; - }, - toggleConfigSection() { - this.showConfigSection = !this.showConfigSection; - }, - - // 根据配置文件ID获取名称 - getConfigName(configId) { - const config = this.configInfoList.find((c) => c.id === configId); - return config ? config.name : configId; - }, - - isUmopMatchPlatform(umop, platformId) { - const parsedUmop = this.parseUmop(umop); - return this.isParsedUmopMatchPlatform(parsedUmop, platformId); - }, - - isParsedUmopMatchPlatform(parsedUmop, platformId) { - if (!parsedUmop) return false; - return ( - parsedUmop.platform === platformId || - parsedUmop.platform === "" || - parsedUmop.platform === "*" - ); - }, - - parseUmop(umop) { - if (!umop) return null; - - const firstSeparatorIndex = umop.indexOf(":"); - if (firstSeparatorIndex === -1) return null; - - const secondSeparatorIndex = umop.indexOf(":", firstSeparatorIndex + 1); - if (secondSeparatorIndex === -1) return null; - - return { - platform: umop.slice(0, firstSeparatorIndex), - messageType: umop.slice(firstSeparatorIndex + 1, secondSeparatorIndex), - sessionId: umop.slice(secondSeparatorIndex + 1), - }; - }, - - // 获取消息类型标签 - getMessageTypeLabel(messageType) { - const typeMap = { - "*": this.tm("createDialog.messageTypeLabels.all"), - "": this.tm("createDialog.messageTypeLabels.all"), - GroupMessage: this.tm("createDialog.messageTypeLabels.group"), - FriendMessage: this.tm("createDialog.messageTypeLabels.friend"), - }; - return typeMap[messageType] || messageType; - }, - - toggleShowConfigSection() { - this.showConfigSection = false; - this.showConfigSection = true; - }, - - prepareData() { - this.getConfigInfoList(); - this.getConfigForPreview(this.selectedAbConfId); - if ( - this.updatingMode && - this.updatingPlatformConfig && - this.updatingPlatformConfig.id - ) { - this.getPlatformConfigs(this.updatingPlatformConfig.id); - } - }, - scrollDialogToBottom() { - const containerRef = this.$refs.dialogScrollContainer; - const el = containerRef?.$el || containerRef; - if (!el) { - return; - } - const scrollOptions = { top: el.scrollHeight, behavior: "smooth" }; - if (typeof el.scrollTo === "function") { - el.scrollTo(scrollOptions); - } else { - el.scrollTop = el.scrollHeight; - } - }, - }, -}; -</script> - -<style> -.v-select__selection-text { - font-size: 12px; -} - -.config-drawer-overlay { - align-items: stretch; - justify-content: flex-end; -} - -.config-drawer-card { - width: clamp(320px, 60vw, 820px); - height: calc(100vh - 32px); - display: flex; - flex-direction: column; - margin: 16px; -} - -.config-drawer-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 20px 12px 20px; -} - -.config-drawer-content { - flex: 1; - overflow-y: auto; - padding: 16px 16px 24px 16px; -} - -.platform-action-row { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px; -} - -.creation-mode-group .v-label { - opacity: 0.9; -} - -.creation-mode-title { - font-size: 14px; - font-weight: 600; - color: rgba(0, 0, 0, 0.78); -} - -.route-source-cell { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 4px; - min-width: 260px; -} - -.route-source-input-row--editing { - padding-top: 20px; -} - -.route-source-mode-link { - color: #0000ee; - cursor: pointer; - font-size: 12px; - line-height: 1; - padding: 2px 0; - text-decoration: underline; -} - -.route-source-mode-link:hover { - text-decoration: underline; -} - -.umo-selection-chip { - max-width: 100%; -} - -.umo-selection-chip .v-chip__content { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.registration-inline { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: flex-start; - width: 320px; - gap: 8px; -} - -.registration-platform-id-field { - width: 300px; -} -</style> diff --git a/legacy-dashboard/src/components/platform/PlatformRegistrationAction.vue b/legacy-dashboard/src/components/platform/PlatformRegistrationAction.vue deleted file mode 100644 index 87e164f6..00000000 --- a/legacy-dashboard/src/components/platform/PlatformRegistrationAction.vue +++ /dev/null @@ -1,427 +0,0 @@ -<template> - <div v-if="action" class="platform-registration-panel"> - <div class="registration-scan-title"> - {{ tm(action.scanTitleKey) }} - </div> - - <div class="registration-scan-content"> - <div class="registration-qr-stage"> - <div - class="registration-qr-shell" - :class="{ 'registration-qr-shell-created': flow.status === 'created' }" - > - <QrCodeViewer - v-if="qrValue" - :value="qrValue" - :alt="tm(action.titleKey)" - :size="150" - :margin="1" - /> - <div v-else class="registration-qr-loading"> - <v-progress-circular indeterminate color="primary"></v-progress-circular> - </div> - </div> - - <div v-if="flow.status === 'created'" class="registration-created-overlay"> - <div class="registration-created-mark"> - <v-icon size="58" color="white">mdi-check</v-icon> - </div> - </div> - </div> - - <div class="registration-action-status mt-2"> - <v-icon size="small" class="me-1" :color="getStatusColor(flow.status)"> - {{ getStatusIcon(flow.status) }} - </v-icon> - {{ getStatusText(flow.status) }} - </div> - </div> - - <div v-if="flow.message" class="registration-action-message mt-2"> - {{ flow.message }} - </div> - </div> -</template> - -<script> -import { botApi } from '@/api/v1'; -import { useModuleI18n } from '@/i18n/composables'; -import QrCodeViewer from '@/components/shared/QrCodeViewer.vue'; - -const FEISHU_DOMAIN = 'https://open.feishu.cn'; - -const REGISTRATION_ACTIONS = { - lark: { - icon: 'mdi-qrcode', - titleKey: 'registrationAction.lark.title', - scanTitleKey: 'registrationAction.lark.scanTitle', - successKey: 'registrationAction.created', - }, - weixin_oc: { - icon: 'mdi-qrcode', - titleKey: 'registrationAction.weixinOc.title', - scanTitleKey: 'registrationAction.weixinOc.scanTitle', - successKey: 'registrationAction.weixinOc.created', - statusKeyPrefix: 'registrationAction.weixinOc.status', - }, - dingtalk: { - icon: 'mdi-qrcode', - titleKey: 'registrationAction.dingtalk.title', - scanTitleKey: 'registrationAction.dingtalk.scanTitle', - successKey: 'registrationAction.dingtalk.created', - }, - qq_official: { - icon: 'mdi-qrcode', - titleKey: 'registrationAction.qqOfficial.title', - scanTitleKey: 'registrationAction.qqOfficial.scanTitle', - successKey: 'registrationAction.qqOfficial.created', - statusKeyPrefix: 'registrationAction.qqOfficial.status', - }, - qq_official_webhook: { - icon: 'mdi-qrcode', - titleKey: 'registrationAction.qqOfficial.title', - scanTitleKey: 'registrationAction.qqOfficial.scanTitle', - successKey: 'registrationAction.qqOfficial.created', - statusKeyPrefix: 'registrationAction.qqOfficial.status', - }, -}; - -export default { - name: 'PlatformRegistrationAction', - components: { QrCodeViewer }, - emits: ['success', 'error', 'created'], - props: { - platformConfig: { - type: Object, - default: null, - }, - active: { - type: Boolean, - default: true, - }, - }, - setup() { - const { tm } = useModuleI18n('features/platform'); - return { tm }; - }, - data() { - return { - flow: { - status: 'idle', - }, - loading: false, - pollTimer: null, - }; - }, - computed: { - action() { - return REGISTRATION_ACTIONS[this.platformConfig?.type] || null; - }, - selectedDomain() { - return this.platformConfig?.domain || FEISHU_DOMAIN; - }, - qrValue() { - return this.flow.verification_uri_complete - || this.flow.qrcode_img_content - || this.flow.qrcode - || ''; - }, - }, - watch: { - active: { - immediate: true, - handler(active) { - if (active) { - this.ensureStarted(); - } else { - this.stopPolling(); - } - }, - }, - 'platformConfig.type'() { - this.resetFlow(); - this.ensureStarted(); - }, - }, - beforeUnmount() { - this.stopPolling(); - }, - methods: { - resetFlow() { - this.stopPolling(); - this.flow = { status: 'idle' }; - }, - ensureStarted() { - if (!this.active || !this.action || this.flow.status !== 'idle') { - return; - } - this.startAction(); - }, - buildPayload(action, extra = {}) { - return { - action, - platform_config: { - ...this.platformConfig, - domain: this.selectedDomain, - }, - ...extra, - }; - }, - async startAction() { - if (!this.action || this.loading) { - return; - } - this.stopPolling(); - this.loading = true; - this.flow = { status: 'starting' }; - try { - const res = await botApi.registration( - this.platformConfig.type, - this.buildPayload('start'), - ); - if (res.data.status !== 'ok') { - throw new Error(res.data.message || this.tm('registrationAction.startFailed')); - } - this.flow = { - ...res.data.data, - status: res.data.data?.status || 'pending', - }; - if (this.flow.registration_code && this.flow.status === 'pending') { - this.schedulePoll(this.flow.interval || 5); - } - } catch (err) { - this.flow = { - status: 'error', - message: err.response?.data?.message || err.message || this.tm('registrationAction.startFailed'), - }; - this.$emit('error', this.flow.message); - } finally { - this.loading = false; - } - }, - schedulePoll(intervalSeconds) { - this.stopPolling(); - const seconds = Math.max(Number(intervalSeconds || 3), 1); - this.pollTimer = setTimeout(() => { - this.pollAction(); - }, seconds * 1000); - }, - stopPolling() { - if (this.pollTimer) { - clearTimeout(this.pollTimer); - this.pollTimer = null; - } - }, - async pollAction() { - if (!this.action || !this.flow.registration_code) { - return; - } - const pollPayload = { - registration_code: this.flow.registration_code, - }; - if (this.flow.task_id) { - pollPayload.task_id = this.flow.task_id; - } - if (this.flow.bind_key) { - pollPayload.bind_key = this.flow.bind_key; - } - try { - const res = await botApi.registration( - this.platformConfig.type, - this.buildPayload('poll', pollPayload), - ); - if (res.data.status !== 'ok') { - throw new Error(res.data.message || this.tm('registrationAction.pollFailed')); - } - const data = res.data.data || {}; - this.flow = { - ...this.flow, - ...data, - status: data.status || 'error', - }; - if (this.flow.status === 'created') { - this.applyRegistrationResult(data); - this.stopPolling(); - this.$emit('created', data); - this.$emit('success', this.tm(this.action.successKey || 'registrationAction.created')); - return; - } - if (this.flow.status === 'pending' || this.flow.status === 'slow_down') { - const nextInterval = this.flow.status === 'slow_down' - ? Number(this.flow.interval || 5) + 5 - : Number(this.flow.interval || 5); - this.flow.interval = nextInterval; - this.schedulePoll(nextInterval); - return; - } - this.stopPolling(); - } catch (err) { - this.flow = { - ...this.flow, - status: 'error', - message: err.response?.data?.message || err.message || this.tm('registrationAction.pollFailed'), - }; - this.$emit('error', this.flow.message); - this.stopPolling(); - } - }, - applyRegistrationResult(data) { - if (!this.platformConfig || !data) { - return; - } - if (data.app_id) { - this.platformConfig.app_id = data.app_id; - } - if (data.app_secret) { - this.platformConfig.app_secret = data.app_secret; - } - if (data.appid) { - this.platformConfig.appid = data.appid; - } - if (data.secret) { - this.platformConfig.secret = data.secret; - } - if (data.domain) { - this.platformConfig.domain = data.domain; - } - if (data.weixin_oc_token) { - this.platformConfig.weixin_oc_token = data.weixin_oc_token; - } - if (data.weixin_oc_account_id) { - this.platformConfig.weixin_oc_account_id = data.weixin_oc_account_id; - } - if (data.weixin_oc_base_url) { - this.platformConfig.weixin_oc_base_url = data.weixin_oc_base_url; - } - if (data.client_id) { - this.platformConfig.client_id = data.client_id; - } - if (data.client_secret) { - this.platformConfig.client_secret = data.client_secret; - } - }, - getStatusText(status) { - const normalizedStatus = status || 'idle'; - if (this.action?.statusKeyPrefix) { - const platformStatusKey = `${this.action.statusKeyPrefix}.${normalizedStatus}`; - const platformStatusText = this.tm(platformStatusKey); - if (platformStatusText && platformStatusText !== platformStatusKey) { - return platformStatusText; - } - } - return this.tm(`registrationAction.status.${normalizedStatus}`); - }, - getStatusColor(status) { - switch (status) { - case 'created': return 'success'; - case 'error': - case 'denied': - case 'expired': return 'error'; - case 'starting': - case 'pending': - case 'slow_down': return 'warning'; - default: return 'grey'; - } - }, - getStatusIcon(status) { - switch (status) { - case 'created': return 'mdi-check-circle'; - case 'error': - case 'denied': - case 'expired': return 'mdi-alert-circle'; - case 'starting': return 'mdi-loading'; - case 'pending': - case 'slow_down': return 'mdi-timer-sand'; - default: return 'mdi-circle-outline'; - } - }, - }, -}; -</script> - -<style scoped> -.platform-registration-panel { - width: 320px; - display: flex; - flex-direction: column; - align-items: flex-start; -} - -.registration-scan-title { - width: 190px; - margin-bottom: 4px; - font-size: 14px; - font-weight: 600; - text-align: left; - color: rgba(0, 0, 0, 0.78); -} - -.registration-scan-content { - margin-left: 8px; -} - -.registration-qr-stage { - position: relative; - width: 190px; - min-height: 190px; - display: flex; - align-items: center; - justify-content: center; -} - -.registration-qr-shell { - width: 190px; - min-height: 190px; - display: flex; - align-items: center; - justify-content: center; - transition: filter 160ms ease, opacity 160ms ease; -} - -.registration-qr-shell :deep(.qr-code-image) { - width: 190px; -} - -.registration-qr-shell-created { - filter: blur(2px); - opacity: 0.32; -} - -.registration-qr-loading { - width: 160px; - height: 160px; - display: flex; - align-items: center; - justify-content: center; - border: 1px solid rgba(0, 0, 0, 0.12); - border-radius: 8px; -} - -.registration-created-overlay { - position: absolute; - inset: 0; - display: flex; - align-items: center; - justify-content: center; - pointer-events: none; -} - -.registration-created-mark { - width: 86px; - height: 86px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - background: rgb(var(--v-theme-success)); -} - -.registration-action-status, -.registration-action-message { - width: 190px; - text-align: center; - font-size: 13px; - color: rgba(0, 0, 0, 0.72); - word-break: break-word; -} -</style> diff --git a/legacy-dashboard/src/components/provider/AddNewProvider.vue b/legacy-dashboard/src/components/provider/AddNewProvider.vue deleted file mode 100644 index 156c95c4..00000000 --- a/legacy-dashboard/src/components/provider/AddNewProvider.vue +++ /dev/null @@ -1,244 +0,0 @@ -<template> - <v-dialog v-model="showDialog" max-width="1000px" > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('dialogs.addProvider.title') }}</v-card-title> - <v-card-text style="overflow-y: auto;"> - <v-tabs v-model="activeProviderTab" grow> - <v-tab value="agent_runner" class="font-weight-medium px-3"> - <v-icon start>mdi-cogs</v-icon> - {{ tm('dialogs.addProvider.tabs.agentRunner') }} - </v-tab> - <v-tab value="speech_to_text" class="font-weight-medium px-3"> - <v-icon start>mdi-microphone-message</v-icon> - {{ tm('dialogs.addProvider.tabs.speechToText') }} - </v-tab> - <v-tab value="text_to_speech" class="font-weight-medium px-3"> - <v-icon start>mdi-volume-high</v-icon> - {{ tm('dialogs.addProvider.tabs.textToSpeech') }} - </v-tab> - <v-tab value="embedding" class="font-weight-medium px-3"> - <v-icon start>mdi-code-json</v-icon> - {{ tm('dialogs.addProvider.tabs.embedding') }} - </v-tab> - <v-tab value="rerank" class="font-weight-medium px-3"> - <v-icon start>mdi-compare-vertical</v-icon> - {{ tm('dialogs.addProvider.tabs.rerank') }} - </v-tab> - </v-tabs> - - <v-window v-model="activeProviderTab" class="mt-4"> - <v-window-item - v-for="tabType in ['chat_completion', 'agent_runner', 'speech_to_text', 'text_to_speech', 'embedding', 'rerank']" - :key="tabType" :value="tabType"> - <v-row class="mt-1"> - <v-col v-for="(template, name) in getTemplatesByType(tabType)" :key="name" cols="12" sm="6" - md="4"> - <v-card variant="outlined" hover class="provider-card" - @click="selectProviderTemplate(name)"> - <div class="provider-card-content"> - <div class="provider-card-text"> - <v-card-title class="provider-card-title">{{ name }}</v-card-title> - <v-card-text - class="text-caption text-medium-emphasis provider-card-description"> - {{ getProviderDescription(template, name) }} - </v-card-text> - </div> - <div class="provider-card-logo"> - <img :src="getProviderIcon(template.provider)" - v-if="getProviderIcon(template.provider)" class="provider-logo-img"> - <div v-else class="provider-logo-fallback"> - {{ name[0].toUpperCase() }} - </div> - </div> - </div> - </v-card> - </v-col> - <v-col v-if="Object.keys(getTemplatesByType(tabType)).length === 0" cols="12"> - <v-alert type="info" variant="tonal"> - {{ tm('dialogs.addProvider.noTemplates') }} - </v-alert> - </v-col> - </v-row> - </v-window-item> - </v-window> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="closeDialog">{{ tm('dialogs.config.cancel') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script> -import { useModuleI18n } from '@/i18n/composables'; -import { getProviderIcon, getProviderDescription } from '@/utils/providerUtils'; - -const AVAILABLE_PROVIDER_TABS = ['agent_runner', 'speech_to_text', 'text_to_speech', 'embedding', 'rerank']; - -export default { - name: 'AddNewProvider', - props: { - show: { - type: Boolean, - default: false - }, - metadata: { - type: Object, - default: () => ({}) - }, - currentProviderType: { - type: String, - default: 'agent_runner' - } - }, - emits: ['update:show', 'select-template'], - setup() { - const { tm } = useModuleI18n('features/provider'); - return { tm }; - }, - data() { - return { - activeProviderTab: 'agent_runner' - }; - }, - computed: { - showDialog: { - get() { - return this.show; - }, - set(value) { - this.$emit('update:show', value); - } - }, - }, - watch: { - show(value) { - if (value) { - this.syncActiveProviderTab(); - } - }, - currentProviderType() { - if (this.showDialog) { - this.syncActiveProviderTab(); - } - } - }, - methods: { - syncActiveProviderTab() { - this.activeProviderTab = AVAILABLE_PROVIDER_TABS.includes(this.currentProviderType) - ? this.currentProviderType - : 'agent_runner'; - }, - - closeDialog() { - this.showDialog = false; - }, - - // 按提供商类型获取模板列表 - getTemplatesByType(type) { - const templates = this.metadata.provider.config_template || {}; - const filtered = {}; - - for (const [name, template] of Object.entries(templates)) { - if (template.provider_type === type) { - filtered[name] = template; - } - } - - return filtered; - }, - - // 从工具函数导入 - getProviderIcon, - - // 获取提供商简介 - getProviderDescription(template, name) { - return getProviderDescription(template, name, this.tm); - }, - - // 选择提供商模板 - selectProviderTemplate(name) { - this.$emit('select-template', name); - this.closeDialog(); - } - } -} -</script> - -<style scoped> -.provider-card { - transition: all 0.3s ease; - height: 100%; - cursor: pointer; - overflow: hidden; - position: relative; -} - -.provider-card:hover { - transform: translateY(-4px); - box-shadow: 0 4px 25px 0 rgba(0, 0, 0, 0.05); - border-color: var(--v-primary-base); -} - -.provider-card-content { - display: flex; - align-items: center; - height: 100px; - padding: 16px; - position: relative; - z-index: 2; -} - -.provider-card-text { - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; -} - -.provider-card-title { - font-size: 15px; - font-weight: 600; - margin-bottom: 4px; - padding: 0; -} - -.provider-card-description { - padding: 0; - margin: 0; -} - -.provider-card-logo { - position: absolute; - right: 0; - top: 0; - bottom: 0; - width: 80px; - display: flex; - align-items: center; - justify-content: center; - z-index: 1; -} - -.provider-logo-img { - width: 60px; - height: 60px; - opacity: 0.6; - object-fit: contain; -} - -.provider-logo-fallback { - width: 50px; - height: 50px; - border-radius: 50%; - background-color: var(--v-primary-base); - color: white; - display: flex; - align-items: center; - justify-content: center; - font-size: 24px; - font-weight: bold; - opacity: 0.3; -} -</style> diff --git a/legacy-dashboard/src/components/provider/ProviderChatCompletionPanel.vue b/legacy-dashboard/src/components/provider/ProviderChatCompletionPanel.vue deleted file mode 100644 index 8be1c136..00000000 --- a/legacy-dashboard/src/components/provider/ProviderChatCompletionPanel.vue +++ /dev/null @@ -1,490 +0,0 @@ -<template> - <div class="provider-chat-panel"> - <div - class="provider-workbench" - :class="{ 'provider-workbench--borderless': !props.showBorder }" - > - <div class="provider-workbench__sidebar"> - <ProviderSourcesPanel - :displayed-provider-sources="displayedProviderSources" - :selected-provider-source="selectedProviderSource" - :available-source-types="availableSourceTypes" - :tm="tm" - :resolve-source-icon="resolveSourceIcon" - :get-source-display-name="getSourceDisplayName" - @add-provider-source="addProviderSource" - @select-provider-source="selectProviderSource" - @delete-provider-source="deleteProviderSource" - /> - </div> - - <div class="provider-workbench__divider"></div> - - <div class="provider-workbench__main"> - <div v-if="selectedProviderSource" class="provider-config-shell"> - <div class="provider-config-header"> - <div class="provider-config-headline"> - <div class="provider-config-title">{{ selectedProviderSource.id }}</div> - <div class="provider-config-subtitle"> - {{ selectedProviderSource.api_base || 'N/A' }} - </div> - </div> - - <div class="provider-config-actions"> - <v-btn - color="primary" - prepend-icon="mdi-content-save-outline" - :loading="savingSource" - :disabled="!isSourceModified" - variant="tonal" - rounded="xl" - @click="saveProviderSource" - > - {{ tm('providerSources.save') }} - </v-btn> - </div> - </div> - - <v-divider></v-divider> - - <div class="provider-config-body"> - <section class="provider-section"> - <div class="provider-section-head"> - <div class="provider-section-title">{{ tm('providers.settings') }}</div> - </div> - <AstrBotConfig - v-if="basicSourceConfig" - :iterable="basicSourceConfig" - :metadata="providerSourceSchema" - metadataKey="provider" - :is-editing="true" - /> - </section> - - <v-divider v-if="advancedSourceConfig"></v-divider> - - <section v-if="advancedSourceConfig" class="provider-section"> - <div class="provider-section-head"> - <div class="provider-section-title">{{ tm('providerSources.advancedConfig') }}</div> - </div> - <AstrBotConfig - :iterable="advancedSourceConfig" - :metadata="providerSourceSchema" - metadataKey="provider" - :is-editing="true" - /> - </section> - - <v-divider></v-divider> - - <section class="provider-section provider-section--models"> - <ProviderModelsPanel - :entries="filteredMergedModelEntries" - :available-count="availableModels.length" - v-model:model-search="modelSearch" - :loading-models="loadingModels" - :is-source-modified="isSourceModified" - :supports-image-input="supportsImageInput" - :supports-audio-input="supportsAudioInput" - :supports-tool-call="supportsToolCall" - :supports-reasoning="supportsReasoning" - :format-context-limit="formatContextLimit" - :saving-providers="savingProviderToggles" - :testing-providers="testingProviders" - :tm="tm" - @fetch-models="fetchAvailableModels" - @open-manual-model="openManualModelDialog" - @open-provider-edit="openProviderEdit" - @toggle-provider-enable="toggleProviderEnable" - @test-provider="testProvider" - @delete-provider="deleteProvider" - @add-model-provider="openModelAddDialog" - /> - </section> - </div> - </div> - - <div v-else class="provider-empty-state"> - <v-icon size="48" color="grey-lighten-1">mdi-cursor-default-click</v-icon> - <p class="mt-2">{{ tm('providerSources.selectHint') }}</p> - </div> - </div> - </div> - - <v-dialog v-model="showManualModelDialog" max-width="400"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ tm('models.manualDialogTitle') }} - </v-card-title> - <v-card-text class="py-4"> - <v-text-field - v-model="manualModelId" - :label="tm('models.manualDialogModelLabel')" - flat - variant="solo-filled" - autofocus - clearable - ></v-text-field> - <v-text-field - :model-value="manualProviderId" - flat - variant="solo-filled" - :label="tm('models.manualDialogPreviewLabel')" - persistent-hint - :hint="tm('models.manualDialogPreviewHint')" - ></v-text-field> - </v-card-text> - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="showManualModelDialog = false">取消</v-btn> - <v-btn color="primary" variant="tonal" @click="confirmManualModel">添加</v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-dialog v-model="showProviderEditDialog" width="800"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ providerEditDialogTitle }} - </v-card-title> - <v-card-text class="py-4"> - <AstrBotConfig - v-if="providerEditData" - :iterable="providerEditData" - :metadata="providerModelConfigSchema" - metadataKey="provider" - :is-editing="true" - /> - </v-card-text> - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn - variant="text" - :disabled="savingProviders.includes(providerEditData?.id)" - @click="showProviderEditDialog = false" - > - {{ tm('dialogs.config.cancel') }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="savingProviders.includes(providerEditData?.id)" - @click="saveEditedProvider" - > - {{ tm('dialogs.config.save') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-snackbar v-model="snackbar.show" :color="snackbar.color" :timeout="3000" location="top"> - {{ snackbar.message }} - </v-snackbar> - </div> -</template> - -<script setup> -import { ref } from 'vue' -import { useModuleI18n } from '@/i18n/composables' -import AstrBotConfig from '@/components/shared/AstrBotConfig.vue' -import ProviderModelsPanel from '@/components/provider/ProviderModelsPanel.vue' -import ProviderSourcesPanel from '@/components/provider/ProviderSourcesPanel.vue' -import { useProviderModelConfigDialog } from '@/composables/useProviderModelConfigDialog' -import { useProviderSources } from '@/composables/useProviderSources' - -const props = defineProps({ - showBorder: { - type: Boolean, - default: true - } -}) - -const { tm } = useModuleI18n('features/provider') - -const snackbar = ref({ - show: false, - message: '', - color: 'success' -}) - -function showMessage(message, color = 'success') { - snackbar.value = { show: true, message, color } -} - -const { - selectedProviderSource, - availableModels, - loadingModels, - savingSource, - savingProviderToggles, - testingProviders, - isSourceModified, - configSchema, - providerSourceSchema, - manualModelId, - modelSearch, - availableSourceTypes, - displayedProviderSources, - filteredMergedModelEntries, - basicSourceConfig, - advancedSourceConfig, - manualProviderId, - resolveSourceIcon, - getSourceDisplayName, - supportsImageInput, - supportsAudioInput, - supportsToolCall, - supportsReasoning, - formatContextLimit, - selectProviderSource, - addProviderSource, - deleteProviderSource, - saveProviderSource, - fetchAvailableModels, - buildModelProviderConfig, - deleteProvider, - testProvider, - toggleProviderEnable, - loadConfig, - modelAlreadyConfigured -} = useProviderSources({ - defaultTab: 'chat_completion', - tm, - showMessage -}) - -const showManualModelDialog = ref(false) - -const { - showProviderEditDialog, - providerEditData, - savingProviders, - providerModelConfigSchema, - providerEditDialogTitle, - openProviderEdit, - openModelAddDialog, - saveEditedProvider -} = useProviderModelConfigDialog({ - selectedProviderSource, - configSchema, - buildModelProviderConfig, - modelAlreadyConfigured, - loadConfig, - tm, - showMessage -}) - -function openManualModelDialog() { - if (!selectedProviderSource.value) { - showMessage(tm('providerSources.selectHint'), 'error') - return - } - manualModelId.value = '' - showManualModelDialog.value = true -} - -async function confirmManualModel() { - const modelId = manualModelId.value.trim() - if (!selectedProviderSource.value) { - showMessage(tm('providerSources.selectHint'), 'error') - return - } - if (!modelId) { - showMessage(tm('models.manualModelRequired'), 'error') - return - } - if (modelAlreadyConfigured(modelId)) { - showMessage(tm('models.manualModelExists'), 'error') - return - } - showManualModelDialog.value = false - openModelAddDialog(modelId) -} - -</script> - -<style scoped> -.provider-chat-panel { - flex: 1; - height: 100%; - display: flex; - flex-direction: column; - min-height: 0; - overflow: hidden; -} - -.provider-workbench { - flex: 1; - border: 1px solid rgba(var(--v-theme-on-surface), 0.08); - border-radius: 24px; - background: rgb(var(--v-theme-surface)); - display: grid; - grid-template-columns: minmax(280px, 320px) 1px minmax(0, 1fr); - min-height: 0; - overflow: hidden; -} - -.provider-workbench--borderless { - border: 0; - border-radius: 0; - background: transparent; -} - -.provider-workbench__sidebar, -.provider-workbench__main { - min-width: 0; - min-height: 0; -} - -.provider-workbench__sidebar, -.provider-workbench__main, -.provider-workbench__divider { - height: 100%; -} - -.provider-workbench__divider { - background: rgba(var(--v-theme-on-surface), 0.08); -} - -.provider-workbench__main { - display: flex; - overflow: hidden; -} - -.provider-config-shell { - flex: 1; - min-height: 0; - display: flex; - flex-direction: column; -} - -.provider-config-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 16px; - padding: 18px 22px 14px; -} - -.provider-config-headline { - min-width: 0; -} - -.provider-config-title { - font-size: 21px; - line-height: 1.1; - font-weight: 680; - letter-spacing: -0.03em; - overflow-wrap: anywhere; -} - -.provider-config-subtitle { - margin-top: 6px; - color: rgba(var(--v-theme-on-surface), 0.62); - font-size: 13px; - line-height: 1.6; - overflow-wrap: anywhere; -} - -.provider-config-actions { - flex-shrink: 0; -} - -.provider-config-body { - flex: 1; - min-height: 0; - overflow-y: auto; -} - -.provider-section { - padding: 18px 22px; -} - -.provider-section--models { - padding-top: 16px; -} - -.provider-section-head { - margin-bottom: 10px; -} - -.provider-section-title { - font-size: 16px; - font-weight: 650; - line-height: 1.4; -} - -.provider-empty-state { - flex: 1; - min-height: 420px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - color: rgba(var(--v-theme-on-surface), 0.56); -} - -@media (max-width: 960px) { - .provider-workbench { - grid-template-columns: 1fr; - grid-template-rows: auto 1px minmax(0, 1fr); - } - - .provider-workbench__sidebar, - .provider-workbench__main, - .provider-workbench__divider { - height: auto; - } - - .provider-workbench__divider { - min-height: 1px; - } - - .provider-config-header { - align-items: stretch; - flex-direction: column; - gap: 12px; - padding: 16px; - } - - .provider-config-actions :deep(.v-btn) { - width: 100%; - } - - .provider-section { - padding: 16px; - } -} - -@media (max-width: 600px) { - .provider-chat-panel { - overflow: auto; - } - - .provider-workbench { - border-radius: 16px; - overflow: visible; - } - - .provider-workbench--borderless { - border-radius: 0; - } - - .provider-workbench__main { - overflow: visible; - } - - .provider-config-body { - overflow-y: visible; - } - - .provider-config-title { - font-size: 18px; - } - - .provider-empty-state { - min-height: 260px; - padding: 24px; - } -} -</style> diff --git a/legacy-dashboard/src/components/provider/ProviderModelsPanel.vue b/legacy-dashboard/src/components/provider/ProviderModelsPanel.vue deleted file mode 100644 index 0420e93b..00000000 --- a/legacy-dashboard/src/components/provider/ProviderModelsPanel.vue +++ /dev/null @@ -1,661 +0,0 @@ -<template> - <div class="provider-models-panel"> - <div class="provider-models-toolbar"> - <div class="provider-models-title-wrap"> - <h3 class="provider-models-title">{{ tm('models.title') }}</h3> - <small class="provider-models-subtitle">{{ tm('models.available') }} {{ availableCount }}</small> - </div> - - <div class="provider-models-toolbar__actions"> - <v-text-field - v-model="modelSearchProxy" - density="compact" - prepend-inner-icon="mdi-magnify" - clearable - hide-details - variant="solo-filled" - flat - class="provider-models-search" - :placeholder="tm('models.searchPlaceholder')" - /> - - <v-btn - color="primary" - prepend-icon="mdi-download" - :loading="loadingModels" - variant="tonal" - rounded="xl" - @click="emit('fetch-models')" - > - {{ isSourceModified ? tm('providerSources.saveAndFetchModels') : tm('providerSources.fetchModels') }} - </v-btn> - - <v-btn - color="primary" - prepend-icon="mdi-pencil-plus" - variant="text" - rounded="xl" - @click="emit('open-manual-model')" - > - {{ tm('models.manualAddButton') }} - </v-btn> - </div> - </div> - - <div class="provider-models-sections"> - <section class="provider-models-section"> - <div class="provider-models-section__head"> - <div class="provider-models-section__title">{{ tm('models.configured') }}</div> - <v-chip size="x-small" variant="tonal" label>{{ configuredEntries.length }}</v-chip> - </div> - - <div v-if="configuredEntries.length" class="provider-models-list"> - <v-tooltip - v-for="entry in configuredEntries" - :key="entry.provider.id" - location="top" - max-width="400" - > - <template #activator="{ props: tooltipProps }"> - <div v-bind="tooltipProps" class="provider-model-row"> - <button - type="button" - class="provider-model-row__main" - @click="emit('open-provider-edit', entry.provider)" - > - <div class="provider-model-row__title">{{ entry.provider.id }}</div> - <div class="provider-model-row__subtitle">{{ entry.provider.model }}</div> - <div class="provider-model-row__meta"> - <v-tooltip - v-for="item in capabilityBadges(entry)" - :key="item.key" - location="top" - max-width="320" - > - <template #activator="{ props: badgeTooltipProps }"> - <span - v-bind="badgeTooltipProps" - class="provider-model-row__badge" - :class="{ - 'provider-model-row__badge--enabled': item.enabled, - 'provider-model-row__badge--disabled': !item.enabled - }" - @click.stop - > - <v-icon size="14">{{ item.icon }}</v-icon> - </span> - </template> - <span>{{ item.tooltip }}</span> - </v-tooltip> - <v-tooltip - v-if="formatContextLimit(entry.metadata)" - location="top" - max-width="320" - > - <template #activator="{ props: contextTooltipProps }"> - <span - v-bind="contextTooltipProps" - class="provider-model-row__badge provider-model-row__badge--text provider-model-row__badge--enabled" - @click.stop - > - {{ formatContextLimit(entry.metadata) }} - </span> - </template> - <span>{{ - tm('models.metadata.context', { - tokens: formatContextLimit(entry.metadata) - }) - }}</span> - </v-tooltip> - </div> - </button> - - <div class="provider-model-row__actions" @click.stop> - <v-switch - :model-value="entry.provider.enable" - density="compact" - inset - hide-details - color="primary" - class="provider-model-row__switch" - :disabled="isProviderSaving(entry.provider.id)" - @update:modelValue="emit('toggle-provider-enable', entry.provider, $event)" - ></v-switch> - - <v-tooltip location="top"> - <template #activator="{ props: testTooltipProps }"> - <v-btn - v-bind="testTooltipProps" - icon="mdi-connection" - size="small" - variant="text" - :disabled="!entry.provider.enable || isProviderSaving(entry.provider.id)" - :loading="isProviderTesting(entry.provider.id)" - @click.stop="emit('test-provider', entry.provider)" - ></v-btn> - </template> - <span>{{ tm('models.testButton') }}</span> - </v-tooltip> - <v-btn - icon="mdi-cog-outline" - size="small" - variant="text" - @click.stop="emit('open-provider-edit', entry.provider)" - ></v-btn> - <v-btn - icon="mdi-delete-outline" - size="small" - variant="text" - @click.stop="emit('delete-provider', entry.provider)" - ></v-btn> - </div> - </div> - </template> - <div> - <strong>{{ tm('models.tooltips.providerId') }}:</strong> - {{ entry.provider.id }} - </div> - <div> - <strong>{{ tm('models.tooltips.modelId') }}:</strong> - {{ entry.provider.model }} - </div> - </v-tooltip> - </div> - - <div v-else class="provider-models-empty"> - <v-icon size="36" color="grey-lighten-1">mdi-package-variant-closed</v-icon> - <p>{{ tm('models.empty') }}</p> - </div> - </section> - - <v-divider></v-divider> - - <section class="provider-models-section provider-models-section--available"> - <div class="provider-models-section__head"> - <div class="provider-models-section__title">{{ tm('models.available') }}</div> - <v-chip size="x-small" variant="tonal" label>{{ availableEntries.length }}</v-chip> - </div> - - <div v-if="availableEntries.length" class="provider-models-list provider-models-list--available"> - <v-tooltip - v-for="entry in availableEntries" - :key="entry.model" - location="top" - max-width="400" - > - <template #activator="{ props: tooltipProps }"> - <div v-bind="tooltipProps" class="provider-model-row"> - <button - type="button" - class="provider-model-row__main" - @click="emit('add-model-provider', entry.model)" - > - <div class="provider-model-row__title provider-model-row__title--mono">{{ entry.model }}</div> - <div class="provider-model-row__meta"> - <v-tooltip - v-for="item in capabilityBadges(entry)" - :key="item.key" - location="top" - max-width="320" - > - <template #activator="{ props: badgeTooltipProps }"> - <span - v-bind="badgeTooltipProps" - class="provider-model-row__badge" - :class="{ - 'provider-model-row__badge--enabled': item.enabled, - 'provider-model-row__badge--disabled': !item.enabled - }" - @click.stop - > - <v-icon size="14">{{ item.icon }}</v-icon> - </span> - </template> - <span>{{ item.tooltip }}</span> - </v-tooltip> - <v-tooltip - v-if="formatContextLimit(entry.metadata)" - location="top" - max-width="320" - > - <template #activator="{ props: contextTooltipProps }"> - <span - v-bind="contextTooltipProps" - class="provider-model-row__badge provider-model-row__badge--text provider-model-row__badge--enabled" - @click.stop - > - {{ formatContextLimit(entry.metadata) }} - </span> - </template> - <span>{{ - tm('models.metadata.context', { - tokens: formatContextLimit(entry.metadata) - }) - }}</span> - </v-tooltip> - </div> - </button> - - <div class="provider-model-row__actions"> - <v-btn - icon="mdi-plus" - size="small" - variant="text" - color="primary" - @click.stop="emit('add-model-provider', entry.model)" - ></v-btn> - </div> - </div> - </template> - <div> - <strong>{{ tm('models.tooltips.modelId') }}:</strong> - {{ entry.model }} - </div> - </v-tooltip> - </div> - - <div v-else class="provider-models-empty provider-models-empty--small"> - <v-icon size="36" color="grey-lighten-1">mdi-database-search-outline</v-icon> - <p>{{ tm('models.noModelsFound') }}</p> - </div> - </section> - </div> - </div> -</template> - -<script setup> -import { computed } from 'vue' -import { normalizeTextInput } from '@/utils/inputValue' - -const props = defineProps({ - entries: { - type: Array, - default: () => [] - }, - availableCount: { - type: Number, - default: 0 - }, - modelSearch: { - type: String, - default: '' - }, - loadingModels: { - type: Boolean, - default: false - }, - isSourceModified: { - type: Boolean, - default: false - }, - supportsImageInput: { - type: Function, - required: true - }, - supportsAudioInput: { - type: Function, - required: true - }, - supportsToolCall: { - type: Function, - required: true - }, - supportsReasoning: { - type: Function, - required: true - }, - formatContextLimit: { - type: Function, - required: true - }, - testingProviders: { - type: Array, - default: () => [] - }, - savingProviders: { - type: Array, - default: () => [] - }, - tm: { - type: Function, - required: true - } -}) - -const emit = defineEmits([ - 'update:modelSearch', - 'fetch-models', - 'open-manual-model', - 'open-provider-edit', - 'toggle-provider-enable', - 'test-provider', - 'delete-provider', - 'add-model-provider' -]) - -const modelSearchProxy = computed({ - get: () => props.modelSearch, - set: (val) => emit('update:modelSearch', normalizeTextInput(val)) -}) - -const configuredEntries = computed(() => - (props.entries || []).filter((entry) => entry.type === 'configured') -) - -const availableEntries = computed(() => - (props.entries || []).filter((entry) => entry.type === 'available') -) - -const capabilityBadges = (entry) => { - const metadata = entry?.metadata - const provider = entry?.provider - const modalities = Array.isArray(provider?.modalities) ? provider.modalities : [] - const isConfigured = entry?.type === 'configured' - const hasModelMetadata = Boolean(entry?.hasModelMetadata) - const definitions = [ - { - key: 'image', - icon: 'mdi-image-outline', - supported: props.supportsImageInput(metadata), - enabled: !isConfigured || modalities.includes('image'), - label: props.tm('models.metadata.image') - }, - { - key: 'audio', - icon: 'mdi-music-note-outline', - supported: props.supportsAudioInput(metadata), - enabled: !isConfigured || modalities.includes('audio'), - label: props.tm('models.metadata.audio') - }, - { - key: 'tool_use', - icon: 'mdi-wrench-outline', - supported: props.supportsToolCall(metadata), - enabled: !isConfigured || modalities.includes('tool_use'), - label: props.tm('models.metadata.toolUse') - }, - { - key: 'reasoning', - icon: 'mdi-brain', - supported: props.supportsReasoning(metadata), - enabled: !isConfigured || Boolean(provider?.reasoning), - label: props.tm('models.metadata.reasoning') - } - ] - - return definitions - .filter((item) => item.supported || (isConfigured && item.enabled)) - .map((item) => { - const enabled = !isConfigured || !hasModelMetadata || item.enabled - let tooltip = props.tm('models.metadata.available', { - capability: item.label - }) - if (isConfigured) { - tooltip = enabled - ? props.tm('models.metadata.enabled', { capability: item.label }) - : props.tm('models.metadata.supportedDisabled', { - capability: item.label - }) - } - return { - key: item.key, - icon: item.icon, - enabled, - tooltip - } - }) -} - -const isProviderTesting = (providerId) => props.testingProviders.includes(providerId) -const isProviderSaving = (providerId) => props.savingProviders.includes(providerId) -</script> - -<style scoped> -.provider-models-panel { - display: grid; - gap: 18px; -} - -.provider-models-toolbar { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - flex-wrap: nowrap; -} - -.provider-models-title { - margin: 0; - font-size: 18px; - font-weight: 650; - line-height: 1.3; -} - -.provider-models-title-wrap { - min-width: 0; - flex-shrink: 0; -} - -.provider-models-subtitle { - display: block; - margin-top: 6px; - color: rgba(var(--v-theme-on-surface), 0.56); - font-size: 12px; -} - -.provider-models-toolbar__actions { - display: flex; - align-items: center; - gap: 8px; - flex: 1; - min-width: 0; - justify-content: flex-end; - flex-wrap: nowrap; -} - -.provider-models-search { - flex: 0 1 240px; - min-width: 180px; - max-width: 260px; -} - -.provider-models-sections { - display: flex; - flex-direction: column; -} - -.provider-models-section { - padding: 4px 0; -} - -.provider-models-section--available { - padding-top: 22px; -} - -.provider-models-section__head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - margin-bottom: 12px; -} - -.provider-models-section__title { - font-size: 14px; - font-weight: 650; -} - -.provider-models-list { - display: flex; - flex-direction: column; -} - -.provider-models-list--available { - max-height: min(420px, 52vh); - overflow-y: auto; - padding-right: 4px; -} - -.provider-model-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 12px 0; - border-bottom: 1px solid rgba(var(--v-theme-on-surface), 0.06); -} - -.provider-model-row:last-child { - border-bottom: 0; -} - -.provider-model-row__main { - flex: 1; - min-width: 0; - border: 0; - background: none; - color: inherit; - padding: 0; - text-align: left; - cursor: pointer; -} - -.provider-model-row__title { - font-size: 14px; - font-weight: 600; - line-height: 1.4; - overflow-wrap: anywhere; -} - -.provider-model-row__title--mono { - font-family: - ui-monospace, - SFMono-Regular, - Menlo, - Monaco, - Consolas, - "Liberation Mono", - "Courier New", - monospace; -} - -.provider-model-row__subtitle { - margin-top: 4px; - color: rgba(var(--v-theme-on-surface), 0.56); - font-size: 12px; - line-height: 1.45; - overflow-wrap: anywhere; -} - -.provider-model-row__meta { - display: flex; - align-items: center; - gap: 6px; - flex-wrap: wrap; - margin-top: 8px; -} - -.provider-model-row__badge { - width: 24px; - height: 24px; - border-radius: 999px; - display: inline-flex; - align-items: center; - justify-content: center; -} - -.provider-model-row__badge--enabled { - background: rgba(var(--v-theme-on-surface), 0.06); - color: rgba(var(--v-theme-on-surface), 0.72); -} - -.provider-model-row__badge--disabled { - background: rgba(var(--v-theme-on-surface), 0.04); - color: rgba(var(--v-theme-on-surface), 0.34); -} - -.provider-model-row__badge--text { - width: auto; - padding: 0 8px; - font-size: 11px; - font-weight: 600; -} - -.provider-model-row__actions { - display: flex; - align-items: center; - gap: 2px; - flex-shrink: 0; -} - -.provider-model-row__switch { - margin-right: 2px; -} - -.provider-models-empty { - min-height: 160px; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 12px; - color: rgba(var(--v-theme-on-surface), 0.56); - text-align: center; - font-size: 13px; -} - -.provider-models-empty--small { - min-height: 120px; -} - -@media (max-width: 760px) { - .provider-models-toolbar { - align-items: stretch; - flex-direction: column; - } - - .provider-models-title-wrap { - flex-shrink: 1; - } - - .provider-models-toolbar__actions { - align-items: stretch; - justify-content: stretch; - flex-wrap: wrap; - } - - .provider-models-search { - flex: 1 1 100%; - min-width: 0; - max-width: none; - } - - .provider-models-toolbar__actions :deep(.v-btn) { - flex: 1 1 160px; - min-width: 0; - } - - .provider-models-toolbar__actions :deep(.v-btn__content) { - white-space: normal; - } -} - -@media (max-width: 600px) { - .provider-models-panel { - gap: 14px; - } - - .provider-model-row { - align-items: stretch; - flex-direction: column; - gap: 10px; - padding: 14px 0; - } - - .provider-model-row__actions { - align-self: flex-end; - flex-wrap: wrap; - justify-content: flex-end; - } -} -</style> diff --git a/legacy-dashboard/src/components/provider/ProviderSourcesPanel.vue b/legacy-dashboard/src/components/provider/ProviderSourcesPanel.vue deleted file mode 100644 index c2c7c9d2..00000000 --- a/legacy-dashboard/src/components/provider/ProviderSourcesPanel.vue +++ /dev/null @@ -1,428 +0,0 @@ -<template> - <div class="provider-sources-panel"> - <div class="provider-sources-head"> - <div class="provider-sources-head__copy"> - <h3 class="provider-sources-title">{{ tm('providerSources.title') }}</h3> - </div> - - <div class="provider-sources-controls"> - <div class="provider-sources-mobile-select"> - <v-select - :model-value="selectedSourceValue" - :items="sourceOptions" - item-title="title" - item-value="value" - density="compact" - variant="solo-filled" - flat - hide-details - :placeholder="tm('providerSources.selectHint')" - @update:model-value="selectSourceByValue" - > - <template #selection="{ item }"> - <div class="provider-source-select-value"> - <v-avatar size="22" rounded="lg" class="provider-source-avatar"> - <v-img - v-if="item.raw.source?.provider" - :src="resolveSourceIcon(item.raw.source)" - alt="provider logo" - cover - ></v-img> - <v-icon v-else size="14">mdi-creation</v-icon> - </v-avatar> - <span>{{ item.raw.title }}</span> - </div> - </template> - - <template #item="{ props: itemProps, item }"> - <v-list-item - v-bind="itemProps" - :subtitle="item.raw.subtitle" - > - <template #prepend> - <v-avatar size="24" rounded="lg" class="provider-source-avatar me-2"> - <v-img - v-if="item.raw.source?.provider" - :src="resolveSourceIcon(item.raw.source)" - alt="provider logo" - cover - ></v-img> - <v-icon v-else size="14">mdi-creation</v-icon> - </v-avatar> - </template> - </v-list-item> - </template> - </v-select> - </div> - - <v-btn - v-if="canDeleteSelectedSource" - class="provider-sources-mobile-delete" - icon="mdi-delete-outline" - variant="text" - size="small" - color="error" - :aria-label="tm('providerSources.delete')" - :title="tm('providerSources.delete')" - @click.stop="deleteSelectedSource" - ></v-btn> - - <StyledMenu> - <template #activator="{ props }"> - <v-btn - v-bind="props" - prepend-icon="mdi-plus" - color="primary" - variant="text" - size="small" - rounded="xl" - > - {{ tm('providerSources.add') }} - </v-btn> - </template> - - <v-list-item - v-for="sourceType in availableSourceTypes" - :key="sourceType.value" - class="styled-menu-item" - @click="emitAddSource(sourceType.value)" - > - <template #prepend> - <v-avatar size="18" rounded="0" class="me-2 provider-source-avatar"> - <v-img - v-if="sourceType.icon" - :src="sourceType.icon" - alt="provider icon" - cover - ></v-img> - <v-icon v-else size="16">mdi-shape-outline</v-icon> - </v-avatar> - </template> - <v-list-item-title>{{ sourceType.label }}</v-list-item-title> - </v-list-item> - </StyledMenu> - </div> - </div> - - <div v-if="displayedProviderSources.length > 0" class="provider-sources-list"> - <button - v-for="source in displayedProviderSources" - :key="source.isPlaceholder ? `template-${source.templateKey}` : source.id" - type="button" - :class="[ - 'provider-source-item', - { - 'provider-source-item--active': isActive(source) - } - ]" - @click="emitSelectSource(source)" - > - <v-avatar size="28" rounded="lg" class="provider-source-item__avatar provider-source-avatar"> - <v-img - v-if="source?.provider" - :src="resolveSourceIcon(source)" - alt="provider logo" - cover - ></v-img> - <v-icon v-else size="16">mdi-creation</v-icon> - </v-avatar> - - <div class="provider-source-item__content"> - <div class="provider-source-item__title"> - {{ getSourceDisplayName(source) }} - </div> - <div class="provider-source-item__subtitle"> - {{ source.api_base || sourceBadge(source) }} - </div> - </div> - - <div class="provider-source-item__actions"> - <v-btn - v-if="!source.isPlaceholder" - icon="mdi-delete-outline" - variant="text" - size="small" - :aria-label="tm('providerSources.delete')" - :title="tm('providerSources.delete')" - @click.stop="emitDeleteSource(source)" - ></v-btn> - </div> - </button> - </div> - - <div v-else class="provider-sources-empty"> - <v-icon size="44" color="grey-lighten-1">mdi-api-off</v-icon> - <p class="provider-sources-empty__text">{{ tm('providerSources.empty') }}</p> - </div> - </div> -</template> - -<script setup> -import { computed } from 'vue' -import StyledMenu from '@/components/shared/StyledMenu.vue' - -const props = defineProps({ - displayedProviderSources: { - type: Array, - default: () => [] - }, - selectedProviderSource: { - type: Object, - default: null - }, - availableSourceTypes: { - type: Array, - default: () => [] - }, - tm: { - type: Function, - required: true - }, - resolveSourceIcon: { - type: Function, - required: true - }, - getSourceDisplayName: { - type: Function, - required: true - } -}) - -const emit = defineEmits([ - 'add-provider-source', - 'select-provider-source', - 'delete-provider-source' -]) - -const selectedId = computed(() => props.selectedProviderSource?.id || null) -const canDeleteSelectedSource = computed(() => - Boolean(props.selectedProviderSource && !props.selectedProviderSource.isPlaceholder) -) - -const isActive = (source) => { - if (source.isPlaceholder) return false - return selectedId.value !== null && selectedId.value === source.id -} - -const sourceBadge = (source) => source.provider || source.templateKey || 'source' - -const sourceValue = (source) => ( - source.isPlaceholder ? `template:${source.templateKey}` : `source:${source.id}` -) - -const sourceOptions = computed(() => - props.displayedProviderSources.map((source) => ({ - title: props.getSourceDisplayName(source), - subtitle: source.api_base || sourceBadge(source), - value: sourceValue(source), - source - })) -) - -const selectedSourceValue = computed(() => { - if (!props.selectedProviderSource) return null - return sourceValue(props.selectedProviderSource) -}) - -const emitAddSource = (type) => emit('add-provider-source', type) -const emitSelectSource = (source) => emit('select-provider-source', source) -const emitDeleteSource = (source) => emit('delete-provider-source', source) - -const deleteSelectedSource = () => { - if (canDeleteSelectedSource.value) { - emitDeleteSource(props.selectedProviderSource) - } -} - -const selectSourceByValue = (value) => { - const option = sourceOptions.value.find((item) => item.value === value) - if (option?.source) { - emitSelectSource(option.source) - } -} -</script> - -<style scoped> -.provider-sources-panel { - height: 100%; - min-height: 0; - display: flex; - flex-direction: column; -} - -.provider-sources-head { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 20px 20px 12px; -} - -.provider-sources-head__copy { - min-width: 0; -} - -.provider-sources-controls { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; -} - -.provider-sources-title { - margin: 0; - font-size: 16px; - font-weight: 650; - line-height: 1.3; -} - -.provider-sources-mobile { - padding: 8px 20px 16px; -} - -.provider-sources-mobile-select { - display: none; - min-width: 0; - flex: 1; -} - -.provider-sources-mobile-delete { - display: none; - flex-shrink: 0; -} - -.provider-source-select-value { - min-width: 0; - display: inline-flex; - align-items: center; - gap: 8px; -} - -.provider-source-select-value span { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.provider-sources-list { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 6px 12px 16px; - display: flex; - flex-direction: column; - gap: 4px; -} - -.provider-source-item { - width: 100%; - border: 0; - border-radius: 12px; - background: transparent; - color: inherit; - display: flex; - align-items: center; - gap: 10px; - padding: 10px 12px; - cursor: pointer; - text-align: left; -} - -.provider-source-item:hover, -.provider-source-item--active { - background: rgba(var(--v-theme-on-surface), 0.05); -} - -.provider-source-avatar { - background: transparent !important; -} - -.provider-source-item__content { - min-width: 0; - flex: 1; -} - -.provider-source-item__title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 14px; - font-weight: 600; -} - -.provider-source-item__subtitle { - margin-top: 4px; - color: rgba(var(--v-theme-on-surface), 0.54); - font-size: 12px; - line-height: 1.4; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.provider-source-item__actions { - opacity: 0; -} - -.provider-source-item:hover .provider-source-item__actions, -.provider-source-item--active .provider-source-item__actions { - opacity: 1; -} - -.provider-sources-empty { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 12px; - padding: 24px; - text-align: center; -} - -.provider-sources-empty__text { - margin: 0; - color: rgba(var(--v-theme-on-surface), 0.56); - font-size: 13px; -} - -@media (max-width: 960px) { - .provider-sources-panel { - height: auto; - } - - .provider-sources-head { - padding: 16px 16px 8px; - align-items: stretch; - flex-direction: column; - gap: 10px; - } - - .provider-sources-mobile-select { - display: block; - } - - .provider-sources-mobile-delete { - display: inline-flex; - } - - .provider-sources-controls { - width: 100%; - } - - .provider-sources-list { - display: none; - } - - .provider-sources-empty { - min-height: 160px; - } -} - -@media (max-width: 600px) { - .provider-sources-controls :deep(.v-btn) { - min-width: max-content; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/AstrBotConfig.vue b/legacy-dashboard/src/components/shared/AstrBotConfig.vue deleted file mode 100644 index af6a1155..00000000 --- a/legacy-dashboard/src/components/shared/AstrBotConfig.vue +++ /dev/null @@ -1,504 +0,0 @@ -<script setup> -import { VueMonacoEditor } from '@guolao/vue-monaco-editor' -import { ref, computed } from 'vue' -import ConfigItemRenderer from './ConfigItemRenderer.vue' -import TemplateListEditor from './TemplateListEditor.vue' -import { useI18n, useModuleI18n } from '@/i18n/composables' -import { useConfigTextResolver } from '@/composables/useConfigTextResolver' -import { useToast } from '@/utils/toast' -import { providerApi } from '@/api/v1' - -const props = defineProps({ - metadata: { - type: Object, - required: true - }, - iterable: { - type: Object, - required: true - }, - metadataKey: { - type: String, - required: true - }, - pluginName: { - type: String, - default: '' - }, - pluginI18n: { - type: Object, - default: () => ({}) - }, - pathPrefix: { - type: String, - default: '' - }, - isEditing: { - type: Boolean, - default: false - } -}) - -const { t } = useI18n() -const { getRaw } = useModuleI18n('features/config-metadata') -const { translateIfKey, resolveConfigText } = useConfigTextResolver(props) -const currentConfigPath = computed(() => props.pathPrefix || props.metadataKey) - -const filteredIterable = computed(() => { - if (!props.iterable) return {} - const { hint, ...rest } = props.iterable - return rest -}) - -const providerHint = computed(() => { - const hint = props.iterable?.hint - if (typeof hint !== 'string' || !hint) return '' - - if ( - hint === 'provider_group.provider.openai_embedding.hint' - || hint === 'provider_group.provider.gemini_embedding.hint' - ) { - return '' - } - - return hint -}) - -const getItemHint = (itemKey, itemMeta) => { - if (itemMeta?.hint) return itemMeta.hint - - if (itemKey !== 'embedding_api_base') return '' - - const providerType = props.iterable?.type - if (providerType === 'openai_embedding') { - return getRaw('provider_group.provider.openai_embedding.hint') - ? 'provider_group.provider.openai_embedding.hint' - : '' - } - if (providerType === 'gemini_embedding') { - return getRaw('provider_group.provider.gemini_embedding.hint') - ? 'provider_group.provider.gemini_embedding.hint' - : '' - } - - return '' -} - -const dialog = ref(false) -const currentEditingKey = ref('') -const currentEditingLanguage = ref('json') -const currentEditingTheme = ref('vs-light') -let currentEditingKeyIterable = null -const loadingEmbeddingDim = ref(false) - -function openEditorDialog(key, value, theme, language) { - currentEditingKey.value = key - currentEditingLanguage.value = language || 'json' - currentEditingTheme.value = theme || 'vs-light' - currentEditingKeyIterable = value - dialog.value = true -} - - -function saveEditedContent() { - dialog.value = false -} - -async function getEmbeddingDimensions(providerConfig) { - if (loadingEmbeddingDim.value) return - - loadingEmbeddingDim.value = true - try { - const providerId = String(providerConfig?.id || '') - if (!providerId) { - useToast().error('缺少提供商 ID') - return - } - const response = await providerApi.embeddingDimension(providerId, providerConfig) - - if (response.data.status != "error" && response.data.data?.embedding_dimensions) { - console.log(response.data.data.embedding_dimensions) - providerConfig.embedding_dimensions = response.data.data.embedding_dimensions - useToast().success("获取成功: " + response.data.data.embedding_dimensions) - } else { - useToast().error(response.data.message) - } - } catch (error) { - console.error('Error getting embedding dimensions:', error) - } finally { - loadingEmbeddingDim.value = false - } -} - -function getValueBySelector(obj, selector) { - const keys = selector.split('.') - let current = obj - for (const key of keys) { - if (current && typeof current === 'object' && key in current) { - current = current[key] - } else { - return undefined - } - } - return current -} - -function shouldShowItem(itemMeta, itemKey) { - if (!itemMeta?.condition) { - return true - } - for (const [conditionKey, expectedValue] of Object.entries(itemMeta.condition)) { - const actualValue = getValueBySelector(props.iterable, conditionKey) - if (actualValue !== expectedValue) { - return false - } - } - return true -} - -function getItemPath(key) { - return props.pathPrefix ? `${props.pathPrefix}.${key}` : key -} - -function hasVisibleItemsAfter(items, currentIndex) { - const itemEntries = Object.entries(items) - - // 检查当前索引之后是否还有可见的配置项 - for (let i = currentIndex + 1; i < itemEntries.length; i++) { - const [itemKey, itemValue] = itemEntries[i] - const itemMeta = props.metadata[props.metadataKey].items[itemKey] - if (!itemMeta?.invisible && shouldShowItem(itemMeta, itemKey)) { - return true - } - } - - return false -} -</script> - -<template> - <div class="config-section" v-if="iterable && metadata[metadataKey]?.type === 'object'"> - <v-list-item-title class="config-title"> - {{ resolveConfigText(currentConfigPath, 'description', metadata[metadataKey]?.description) }} <span class="metadata-key">({{ metadataKey }})</span> - </v-list-item-title> - <v-list-item-subtitle class="config-hint"> - <span v-if="metadata[metadataKey]?.obvious_hint && metadata[metadataKey]?.hint" class="important-hint">‼️</span> - {{ resolveConfigText(currentConfigPath, 'hint', metadata[metadataKey]?.hint) }} - </v-list-item-subtitle> - </div> - - <v-card-text class="px-0 py-1"> - <!-- Object Type Configuration --> - <div v-if="metadata[metadataKey]?.type === 'object' || metadata[metadataKey]?.config_template" class="object-config"> - <!-- Provider-level hint --> - <v-alert - v-if="providerHint" - type="info" - variant="tonal" - class="mb-4" - border="start" - density="compact" - > - {{ translateIfKey(providerHint) }} - </v-alert> - - <div v-for="(val, key, index) in filteredIterable" :key="key" class="config-item"> - <!-- Nested Object --> - <div v-if="metadata[metadataKey].items[key]?.type === 'object'" class="nested-object"> - <div v-if="metadata[metadataKey].items[key] && !metadata[metadataKey].items[key]?.invisible && shouldShowItem(metadata[metadataKey].items[key], key)" class="nested-container"> - <v-expand-transition> - <AstrBotConfig - :metadata="metadata[metadataKey].items" - :iterable="iterable[key]" - :metadataKey="key" - :pluginName="pluginName" - :pluginI18n="pluginI18n" - :pathPrefix="getItemPath(key)" - > - </AstrBotConfig> - </v-expand-transition> - </div> - </div> - - <!-- Template List --> - <div v-else-if="metadata[metadataKey].items[key]?.type === 'template_list'" class="nested-object w-100"> - <div v-if="!metadata[metadataKey].items[key]?.invisible && shouldShowItem(metadata[metadataKey].items[key], key)" class="nested-container"> - <div class="config-section mb-2"> - <v-list-item-title class="config-title"> - <span v-if="metadata[metadataKey].items[key]?.description"> - {{ resolveConfigText(getItemPath(key), 'description', metadata[metadataKey].items[key]?.description) }} - <span class="property-key">({{ key }})</span> - </span> - <span v-else>{{ key }}</span> - </v-list-item-title> - <v-list-item-subtitle class="config-hint"> - <span v-if="metadata[metadataKey].items[key]?.obvious_hint && metadata[metadataKey].items[key]?.hint" class="important-hint">‼️</span> - {{ resolveConfigText(getItemPath(key), 'hint', metadata[metadataKey].items[key]?.hint) }} - </v-list-item-subtitle> - </div> - <TemplateListEditor - v-model="iterable[key]" - :templates="metadata[metadataKey].items[key]?.templates || {}" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-path="getItemPath(key)" - class="config-field" - /> - </div> - </div> - - <!-- Regular Property --> - <template v-else> - <v-row v-if="!metadata[metadataKey].items[key]?.invisible && shouldShowItem(metadata[metadataKey].items[key], key)" class="config-row"> - <v-col cols="12" sm="6" class="property-info"> - <v-list-item density="compact"> - <v-list-item-title class="property-name"> - <span v-if="metadata[metadataKey].items[key]?.description"> - {{ resolveConfigText(getItemPath(key), 'description', metadata[metadataKey].items[key]?.description) }} - <span class="property-key">({{ key }})</span> - </span> - <span v-else>{{ key }}</span> - </v-list-item-title> - - <v-list-item-subtitle class="property-hint"> - <span v-if="metadata[metadataKey].items[key]?.obvious_hint && getItemHint(key, metadata[metadataKey].items[key])" - class="important-hint">‼️</span> - {{ resolveConfigText(getItemPath(key), 'hint', getItemHint(key, metadata[metadataKey].items[key])) }} - </v-list-item-subtitle> - </v-list-item> - </v-col> - - <v-col cols="12" sm="6" class="config-input"> - <ConfigItemRenderer - v-model="iterable[key]" - :item-meta="metadata[metadataKey].items[key] || null" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-key="getItemPath(key)" - :loading="loadingEmbeddingDim" - :show-fullscreen-btn="!!metadata[metadataKey].items[key]?.editor_mode" - @get-embedding-dim="getEmbeddingDimensions(iterable)" - @open-fullscreen="openEditorDialog(key, iterable, metadata[metadataKey].items[key]?.editor_theme, metadata[metadataKey].items[key]?.editor_language)" - /> - </v-col> - </v-row> - - <v-divider - v-if="hasVisibleItemsAfter(filteredIterable, index) && !metadata[metadataKey].items[key]?.invisible && shouldShowItem(metadata[metadataKey].items[key], key)" - class="config-divider" - ></v-divider> - </template> - </div> - </div> - - <!-- Simple Value Configuration --> - <div v-else class="simple-config"> - <v-row class="config-row"> - <v-col cols="12" sm="7" class="property-info"> - <v-list-item density="compact"> - <v-list-item-title class="property-name"> - {{ resolveConfigText(getItemPath(metadataKey), 'description', metadata[metadataKey]?.description) }} - <span class="property-key">({{ metadataKey }})</span> - </v-list-item-title> - - <v-list-item-subtitle class="property-hint"> - <span v-if="metadata[metadataKey]?.obvious_hint && metadata[metadataKey]?.hint" class="important-hint">‼️</span> - {{ resolveConfigText(getItemPath(metadataKey), 'hint', metadata[metadataKey]?.hint) }} - </v-list-item-subtitle> - </v-list-item> - </v-col> - - <v-col cols="12" sm="5" class="config-input"> - <TemplateListEditor - v-if="metadata[metadataKey]?.type === 'template_list' && !metadata[metadataKey]?.invisible" - v-model="iterable[metadataKey]" - :templates="metadata[metadataKey]?.templates || {}" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-path="getItemPath(metadataKey)" - class="config-field" - /> - <ConfigItemRenderer - v-else - v-model="iterable[metadataKey]" - :item-meta="metadata[metadataKey]" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-key="getItemPath(metadataKey)" - /> - </v-col> - </v-row> - - <v-divider class="my-2 config-divider"></v-divider> - </div> - </v-card-text> - - <!-- Full Screen Editor Dialog --> - <v-dialog v-model="dialog" fullscreen transition="dialog-bottom-transition" scrollable> - <v-card> - <v-toolbar color="primary" dark> - <v-btn icon @click="dialog = false"> - <v-icon>mdi-close</v-icon> - </v-btn> - <v-toolbar-title>{{ t('core.common.editor.editingTitle') }} - {{ currentEditingKey }}</v-toolbar-title> - <v-spacer></v-spacer> - <v-toolbar-items> - <v-btn variant="text" @click="saveEditedContent">{{ t('core.common.save') }}</v-btn> - </v-toolbar-items> - </v-toolbar> - <v-card-text class="pa-0"> - <VueMonacoEditor - :theme="currentEditingTheme" - :language="currentEditingLanguage" - style="height: calc(100vh - 64px);" - v-model:value="currentEditingKeyIterable[currentEditingKey]" - > - </VueMonacoEditor> - </v-card-text> - </v-card> - </v-dialog> -</template> - - - -<style scoped> -.config-section { - margin-bottom: 12px; -} - -.config-title { - font-weight: 600; - font-size: 1rem; - color: var(--v-theme-primaryText); -} - -.config-hint { - font-size: 0.75rem; - color: var(--v-theme-secondaryText); - margin-top: 2px; -} - -.metadata-key, .property-key { - font-size: 0.85em; - opacity: 0.7; - font-weight: normal; - display: none; -} - -.important-hint { - opacity: 1; - margin-right: 4px; -} - -.object-config, .simple-config { - width: 100%; -} - -.config-item { - margin-bottom: 2px; -} - -.nested-object { - padding-left: 16px; -} - -.nested-container { - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 8px; - padding: 12px; - margin: 12px 0; - background-color: rgba(0, 0, 0, 0.02); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.config-row { - margin: 0; - align-items: center; - padding: 4px 8px; - border-radius: 4px; -} - -.config-row:hover { - background-color: rgba(0, 0, 0, 0.03); -} - -.property-info { - padding: 0; -} - -.property-name { - font-size: 0.875rem; - font-weight: 600; - color: var(--v-theme-primaryText); -} - -.property-hint { - font-size: 0.75rem; - color: var(--v-theme-secondaryText); - margin-top: 2px; -} - -.type-indicator { - display: flex; - justify-content: center; -} - -.config-input { - padding: 4px 8px; -} - -.config-field { - margin-bottom: 0; -} - -.config-divider { - border-color: rgba(0, 0, 0, 0.05); - margin: 0px 16px; -} - -.editor-container { - position: relative; - display: flex; - width: 100%; -} - -.editor-fullscreen-btn { - position: absolute; - top: 4px; - right: 4px; - z-index: 10; - background-color: rgba(0, 0, 0, 0.3); - border-radius: 4px; -} - -.editor-fullscreen-btn:hover { - background-color: rgba(0, 0, 0, 0.5); -} - -@media (max-width: 600px) { - .nested-object { - padding-left: 8px; - } - - .config-row { - padding: 8px 0; - } - - .property-info { - padding: 4px 4px; - } - - .property-info :deep(.v-list-item) { - padding-inline: 0; - } - - .type-indicator, - .config-input { - padding: 4px; - } - - .config-divider { - display: none; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/AstrBotConfigV4.vue b/legacy-dashboard/src/components/shared/AstrBotConfigV4.vue deleted file mode 100644 index 13b65e88..00000000 --- a/legacy-dashboard/src/components/shared/AstrBotConfigV4.vue +++ /dev/null @@ -1,651 +0,0 @@ -<script setup> -import MarkdownIt from 'markdown-it' -import { VueMonacoEditor } from '@guolao/vue-monaco-editor' -import { ref, computed } from 'vue' -import ConfigItemRenderer from './ConfigItemRenderer.vue' -import TemplateListEditor from './TemplateListEditor.vue' -import PersonaQuickPreview from './PersonaQuickPreview.vue' -import { useI18n, useModuleI18n } from '@/i18n/composables' -import { useConfigTextResolver } from '@/composables/useConfigTextResolver' - - -const props = defineProps({ - metadata: { - type: Object, - required: true - }, - iterable: { - type: Object, - required: true - }, - metadataKey: { - type: String, - required: true - }, - searchKeyword: { - type: String, - default: '' - }, - pluginName: { - type: String, - default: '' - }, - pluginI18n: { - type: Object, - default: () => ({}) - }, - pathPrefix: { - type: String, - default: '' - } -}) - -const { t } = useI18n() -const { getRaw } = useModuleI18n('features/config-metadata') -const { tm: tmConfig } = useModuleI18n('features/config') -const { translateIfKey, resolveConfigText } = useConfigTextResolver(props) - -const hintMarkdown = new MarkdownIt({ - linkify: true, - breaks: true -}) - -const renderHint = (value) => { - const text = translateIfKey(value) - if (!text) return '' - return hintMarkdown.renderInline(text) -} - -// 处理labels翻译 - labels可以是数组或国际化键 -const getTranslatedLabels = (itemMeta) => { - if (!itemMeta?.labels) return null - - // 如果labels是字符串(国际化键) - if (typeof itemMeta.labels === 'string') { - const translatedLabels = getRaw(itemMeta.labels) - // 如果翻译成功且是数组,返回翻译结果 - if (Array.isArray(translatedLabels)) { - return translatedLabels - } - } - - // 如果labels是数组,直接返回 - if (Array.isArray(itemMeta.labels)) { - return itemMeta.labels - } - - return null -} - -const dialog = ref(false) -const showCollapsedItems = ref(false) -const currentEditingKey = ref('') -const currentEditingLanguage = ref('json') -const currentEditingTheme = ref('vs-light') -let currentEditingKeyIterable = null - -function getValueBySelector(obj, selector) { - const keys = selector.split('.') - let current = obj - for (const key of keys) { - if (current && typeof current === 'object' && key in current) { - current = current[key] - } else { - return undefined - } - } - return current -} - -function setValueBySelector(obj, selector, value) { - const keys = selector.split('.') - let current = obj - - // 创建嵌套对象路径 - for (let i = 0; i < keys.length - 1; i++) { - const key = keys[i] - if (!current[key] || typeof current[key] !== 'object') { - current[key] = {} - } - current = current[key] - } - - // 设置最终值 - current[keys[keys.length - 1]] = value -} - -// 创建一个计算属性来处理 JSON selector 的获取和设置 -function createSelectorModel(selector) { - return computed({ - get() { - return getValueBySelector(props.iterable, selector) - }, - set(value) { - setValueBySelector(props.iterable, selector, value) - } - }) -} - -function getItemPath(key) { - return props.pathPrefix ? `${props.pathPrefix}.${key}` : key -} - -function getItemDescription(itemKey, itemMeta) { - return resolveConfigText(getItemPath(itemKey), 'description', itemMeta?.description) || itemKey -} - -function getItemHint(itemKey, itemMeta) { - return resolveConfigText(getItemPath(itemKey), 'hint', itemMeta?.hint) -} - -function openEditorDialog(key, value, theme, language) { - currentEditingKey.value = key - currentEditingLanguage.value = language || 'json' - currentEditingTheme.value = theme || 'vs-light' - currentEditingKeyIterable = value - dialog.value = true -} - -function saveEditedContent() { - dialog.value = false -} - -function shouldShowItem(itemMeta, itemKey) { - if (itemMeta?.condition) { - for (const [conditionKey, expectedValue] of Object.entries(itemMeta.condition)) { - const actualValue = getValueBySelector(props.iterable, conditionKey) - if (actualValue !== expectedValue) { - return false - } - } - } - - const keyword = String(props.searchKeyword || '').trim().toLowerCase() - if (!keyword) { - return true - } - - const searchableText = [ - itemKey, - getItemDescription(itemKey, itemMeta), - getItemHint(itemKey, itemMeta) - ].join(' ').toLowerCase() - - return searchableText.includes(keyword) -} - -function getVisibleItemEntries(collapsed = false) { - const sectionItems = props.metadata?.[props.metadataKey]?.items || {} - return Object.entries(sectionItems).filter(([itemKey, itemMeta]) => { - const isCollapsed = Boolean(itemMeta?.collapsed) - return isCollapsed === collapsed && shouldShowItem(itemMeta, itemKey) - }) -} - -function hasCollapsedItems() { - return getVisibleItemEntries(true).length > 0 -} - -function hasVisibleEntriesAfter(entries, currentIndex) { - return currentIndex < entries.length - 1 -} - -function areCollapsedItemsVisible() { - if (!hasCollapsedItems()) { - return false - } - if (String(props.searchKeyword || '').trim()) { - return true - } - return showCollapsedItems.value -} - -function toggleCollapsedItems() { - showCollapsedItems.value = !showCollapsedItems.value -} - -// 检查最外层的 object 是否应该显示 -function shouldShowSection() { - const sectionMeta = props.metadata[props.metadataKey] - if (!sectionMeta?.condition) { - return true - } - for (const [conditionKey, expectedValue] of Object.entries(sectionMeta.condition)) { - const actualValue = getValueBySelector(props.iterable, conditionKey) - if (actualValue !== expectedValue) { - return false - } - } - - const sectionItems = props.metadata?.[props.metadataKey]?.items || {} - const hasVisibleItems = Object.entries(sectionItems).some(([itemKey, itemMeta]) => shouldShowItem(itemMeta, itemKey)) - return hasVisibleItems -} - -function hasVisibleItemsAfter(items, currentIndex) { - const itemEntries = Object.entries(items) - - // 检查当前索引之后是否还有可见的配置项 - for (let i = currentIndex + 1; i < itemEntries.length; i++) { - const [itemKey, itemMeta] = itemEntries[i] - if (shouldShowItem(itemMeta, itemKey)) { - return true - } - } - - return false -} - -function parseSpecialValue(value) { - if (!value || typeof value !== 'string') { - return { name: '', subtype: '' } - } - const [name, ...rest] = value.split(':') - return { - name, - subtype: rest.join(':') || '' - } -} - -function getSpecialName(value) { - return parseSpecialValue(value).name -} - -function getSpecialSubtype(value) { - return parseSpecialValue(value).subtype -} - -</script> - -<template> - - - <v-card v-if="shouldShowSection()" style="margin-bottom: 16px; padding-bottom: 8px; background-color: rgb(var(--v-theme-background));" - rounded="md" variant="outlined"> - <v-card-text class="config-section" v-if="metadata[metadataKey]?.type === 'object'" style="padding-bottom: 8px;"> - <v-list-item-title class="config-title"> - {{ translateIfKey(metadata[metadataKey]?.description) }} - </v-list-item-title> - <v-list-item-subtitle class="config-hint"> - <span v-if="metadata[metadataKey]?.obvious_hint && metadata[metadataKey]?.hint" class="important-hint">‼️</span> - <span v-html="renderHint(metadata[metadataKey]?.hint)"></span> - </v-list-item-subtitle> - </v-card-text> - - <!-- Object Type Configuration with JSON Selector Support --> - <div v-if="metadata[metadataKey]?.type === 'object'" class="object-config"> - <div - v-for="([itemKey, itemMeta], index) in getVisibleItemEntries(false)" - :key="itemKey" - class="config-item" - > - <v-row v-if="!itemMeta?.invisible" class="config-row"> - <v-col cols="12" sm="6" class="property-info"> - <v-list-item density="compact"> - <v-list-item-title class="property-name"> - {{ getItemDescription(itemKey, itemMeta) }} - <span class="property-key">({{ itemKey }})</span> - </v-list-item-title> - - <v-list-item-subtitle class="property-hint"> - <span v-if="itemMeta?.obvious_hint && itemMeta?.hint" class="important-hint">‼️</span> - <span v-html="renderHint(getItemHint(itemKey, itemMeta))"></span> - </v-list-item-subtitle> - </v-list-item> - </v-col> - <v-col cols="12" sm="6" class="config-input"> - <TemplateListEditor - v-if="itemMeta?.type === 'template_list'" - v-model="createSelectorModel(itemKey).value" - :templates="itemMeta?.templates || {}" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-path="getItemPath(itemKey)" - class="config-field" - /> - <ConfigItemRenderer - v-else - v-model="createSelectorModel(itemKey).value" - :item-meta="itemMeta || null" - :config-root="iterable" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-key="getItemPath(itemKey)" - :show-fullscreen-btn="!!itemMeta?.editor_mode" - @open-fullscreen="openEditorDialog(itemKey, iterable, itemMeta?.editor_theme, itemMeta?.editor_language)" - /> - </v-col> - </v-row> - - <v-row v-if="!itemMeta?.invisible && itemMeta?._special === 'select_plugin_set'" - class="plugin-set-display-row"> - <v-col cols="12" class="plugin-set-display"> - <div v-if="createSelectorModel(itemKey).value && createSelectorModel(itemKey).value.length > 0" - class="selected-plugins-full-width"> - <div class="plugins-header"> - <small class="text-grey">{{ t('core.shared.pluginSetSelector.selectedPluginsLabel') }}</small> - </div> - <div class="d-flex flex-wrap ga-2 mt-2"> - <v-chip v-for="plugin in (createSelectorModel(itemKey).value || [])" :key="plugin" size="small" label - color="primary" variant="outlined"> - {{ plugin === '*' ? t('core.shared.pluginSetSelector.allPluginsLabel') : plugin }} - </v-chip> - </div> - </div> - </v-col> - </v-row> - - <v-row - v-if="!itemMeta?.invisible && itemMeta?._special === 'select_persona' && itemKey === 'provider_settings.default_personality'" - class="persona-preview-row" - > - <v-col cols="12" class="persona-preview-display"> - <PersonaQuickPreview :model-value="createSelectorModel(itemKey).value" /> - </v-col> - </v-row> - - <v-divider class="config-divider" - v-if="hasVisibleEntriesAfter(getVisibleItemEntries(false), index)"></v-divider> - </div> - - <div v-if="hasCollapsedItems()" class="collapsed-config-section"> - <div class="collapsed-config-toggle-row"> - <span - class="collapsed-config-toggle" - @click="toggleCollapsedItems" - > - {{ tmConfig('sections.moreConfig') }} - <v-icon end size="18"> - {{ areCollapsedItemsVisible() ? 'mdi-chevron-up' : 'mdi-chevron-down' }} - </v-icon> - </span> - </div> - <div v-if="areCollapsedItemsVisible()"> - <div - v-for="([itemKey, itemMeta], index) in getVisibleItemEntries(true)" - :key="itemKey" - class="config-item" - > - <v-row v-if="!itemMeta?.invisible" class="config-row"> - <v-col cols="12" sm="6" class="property-info"> - <v-list-item density="compact"> - <v-list-item-title class="property-name"> - {{ getItemDescription(itemKey, itemMeta) }} - <span class="property-key">({{ itemKey }})</span> - </v-list-item-title> - - <v-list-item-subtitle class="property-hint"> - <span v-if="itemMeta?.obvious_hint && itemMeta?.hint" class="important-hint">‼️</span> - <span v-html="renderHint(getItemHint(itemKey, itemMeta))"></span> - </v-list-item-subtitle> - </v-list-item> - </v-col> - <v-col cols="12" sm="6" class="config-input"> - <TemplateListEditor - v-if="itemMeta?.type === 'template_list'" - v-model="createSelectorModel(itemKey).value" - :templates="itemMeta?.templates || {}" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-path="getItemPath(itemKey)" - class="config-field" - /> - <ConfigItemRenderer - v-else - v-model="createSelectorModel(itemKey).value" - :item-meta="itemMeta || null" - :config-root="iterable" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-key="getItemPath(itemKey)" - :show-fullscreen-btn="!!itemMeta?.editor_mode" - @open-fullscreen="openEditorDialog(itemKey, iterable, itemMeta?.editor_theme, itemMeta?.editor_language)" - /> - </v-col> - </v-row> - - <v-row v-if="!itemMeta?.invisible && itemMeta?._special === 'select_plugin_set'" - class="plugin-set-display-row"> - <v-col cols="12" class="plugin-set-display"> - <div v-if="createSelectorModel(itemKey).value && createSelectorModel(itemKey).value.length > 0" - class="selected-plugins-full-width"> - <div class="plugins-header"> - <small class="text-grey">{{ t('core.shared.pluginSetSelector.selectedPluginsLabel') }}</small> - </div> - <div class="d-flex flex-wrap ga-2 mt-2"> - <v-chip v-for="plugin in (createSelectorModel(itemKey).value || [])" :key="plugin" size="small" label - color="primary" variant="outlined"> - {{ plugin === '*' ? t('core.shared.pluginSetSelector.allPluginsLabel') : plugin }} - </v-chip> - </div> - </div> - </v-col> - </v-row> - - <v-row - v-if="!itemMeta?.invisible && itemMeta?._special === 'select_persona' && itemKey === 'provider_settings.default_personality'" - class="persona-preview-row" - > - <v-col cols="12" class="persona-preview-display"> - <PersonaQuickPreview :model-value="createSelectorModel(itemKey).value" /> - </v-col> - </v-row> - - <v-divider class="config-divider" - v-if="hasVisibleEntriesAfter(getVisibleItemEntries(true), index)"></v-divider> - </div> - </div> - </div> - - </div> - </v-card> - - <!-- Full Screen Editor Dialog --> - <v-dialog v-model="dialog" fullscreen transition="dialog-bottom-transition" scrollable> - <v-card> - <v-toolbar color="primary" dark> - <v-btn icon @click="dialog = false"> - <v-icon>mdi-close</v-icon> - </v-btn> - <v-toolbar-title>{{ t('core.common.editor.editingTitle') }} - {{ currentEditingKey }}</v-toolbar-title> - <v-spacer></v-spacer> - <v-toolbar-items> - <v-btn variant="text" @click="saveEditedContent">{{ t('core.common.save') }}</v-btn> - </v-toolbar-items> - </v-toolbar> - <v-card-text class="pa-0"> - <VueMonacoEditor :theme="currentEditingTheme" :language="currentEditingLanguage" - style="height: calc(100vh - 64px);" v-model:value="currentEditingKeyIterable[currentEditingKey]"> - </VueMonacoEditor> - </v-card-text> - </v-card> - </v-dialog> -</template> - - - -<style scoped> -.config-section { - margin-bottom: 4px; -} - -.config-title { - /* font-weight: 600; */ - font-size: 1.3rem; - color: var(--v-theme-primaryText); -} - -.config-hint { - font-size: 0.75rem; - color: var(--v-theme-secondaryText); - margin-top: 2px; -} - -.config-hint :deep(a), -.property-hint :deep(a) { - color: var(--v-theme-primary); - text-decoration: underline; -} - -.metadata-key, -.property-key { - font-size: 0.85em; - opacity: 0.7; - font-weight: normal; - display: none; -} - -.important-hint { - opacity: 1; - margin-right: 4px; -} - -.object-config, -.simple-config { - width: 100%; -} - -.nested-object { - padding-left: 16px; -} - -.nested-container { - border: 1px solid rgba(0, 0, 0, 0.1); - border-radius: 8px; - padding: 12px; - margin: 12px 0; - background-color: rgba(0, 0, 0, 0.02); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); -} - -.config-row { - margin: 0; - align-items: center; - padding: 8px 8px; - border-radius: 4px; -} - -.config-row:hover { - background-color: rgba(0, 0, 0, 0.03); -} - -.property-info { - padding: 0; -} - -.property-name { - font-size: 0.875rem; - /* font-weight: 600; */ - color: var(--v-theme-primaryText); -} - -.property-hint { - font-size: 0.75rem; - color: var(--v-theme-secondaryText); - margin-top: 2px; -} - -.type-indicator { - display: flex; - justify-content: center; -} - -.config-input { - padding: 4px 8px; -} - -.config-field { - margin-bottom: 0; -} - -.config-divider { - border-color: rgba(0, 0, 0, 0.1); - margin-left: 24px; -} - -.editor-container { - position: relative; - display: flex; - width: 100%; -} - -.editor-fullscreen-btn { - position: absolute; - top: 4px; - right: 4px; - z-index: 10; - background-color: rgba(0, 0, 0, 0.3); - border-radius: 4px; -} - -.editor-fullscreen-btn:hover { - background-color: rgba(0, 0, 0, 0.5); -} - -.plugin-set-display-row { - margin: 16px; - margin-top: 0; -} - -.plugin-set-display { - padding: 0 8px; -} - -.persona-preview-row { - margin: 16px; - margin-top: 0; -} - -.persona-preview-display { - padding: 0 8px; -} - -.selected-plugins-full-width { - background-color: rgba(var(--v-theme-primary), 0.05); - border: 1px solid rgba(var(--v-theme-primary), 0.1); - border-radius: 8px; - padding: 12px; -} - -.collapsed-config-section { - width: 100%; -} - -.collapsed-config-toggle-row { - padding: 8px 8px 4px; -} - -.collapsed-config-toggle { - min-height: auto; - cursor: pointer; - margin-left: 16px; - font-size: 0.875rem; -} - -.plugins-header { - margin-bottom: 4px; -} - -@media (max-width: 600px) { - .nested-object { - padding-left: 8px; - } - - .config-row { - padding: 8px 0; - } - - .type-indicator { - padding: 4px 8px; - } - - .config-input { - padding-left: 16px; - padding-right: 16px; - } - - .config-divider { - display: none; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/BackupDialog.vue b/legacy-dashboard/src/components/shared/BackupDialog.vue deleted file mode 100644 index 2fc89e9a..00000000 --- a/legacy-dashboard/src/components/shared/BackupDialog.vue +++ /dev/null @@ -1,985 +0,0 @@ -<template> - <v-dialog v-model="isOpen" persistent max-width="700" scrollable> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - <v-icon class="mr-2">mdi-backup-restore</v-icon> - {{ t('features.settings.backup.dialog.title') }} - </v-card-title> - - <v-card-text class="pa-6"> - <!-- 选项卡 --> - <v-tabs v-model="activeTab" color="primary" class="mb-4"> - <v-tab value="export"> - <v-icon class="mr-2">mdi-export</v-icon> - {{ t('features.settings.backup.tabs.export') }} - </v-tab> - <v-tab value="import"> - <v-icon class="mr-2">mdi-import</v-icon> - {{ t('features.settings.backup.tabs.import') }} - </v-tab> - <v-tab value="list"> - <v-icon class="mr-2">mdi-format-list-bulleted</v-icon> - {{ t('features.settings.backup.tabs.list') }} - </v-tab> - </v-tabs> - - <v-window v-model="activeTab"> - <!-- 导出标签页 --> - <v-window-item value="export"> - <div v-if="exportStatus === 'idle'" class="text-center py-8"> - <v-icon size="64" color="primary" class="mb-4">mdi-cloud-upload</v-icon> - <h3 class="mb-4">{{ t('features.settings.backup.export.title') }}</h3> - <p class="mb-4 text-grey">{{ t('features.settings.backup.export.description') }}</p> - <v-alert type="info" variant="tonal" class="mb-4 text-left"> - <template v-slot:prepend> - <v-icon>mdi-information</v-icon> - </template> - {{ t('features.settings.backup.export.includes') }} - </v-alert> - <v-btn color="primary" variant="tonal" size="large" @click="startExport" :loading="exportStatus === 'processing'"> - <v-icon class="mr-2">mdi-export</v-icon> - {{ t('features.settings.backup.export.button') }} - </v-btn> - </div> - - <div v-else-if="exportStatus === 'processing'" class="text-center py-8"> - <v-progress-circular indeterminate color="primary" size="64" class="mb-4"></v-progress-circular> - <h3 class="mb-4">{{ t('features.settings.backup.export.processing') }}</h3> - <p class="text-grey">{{ exportProgress.message || t('features.settings.backup.export.wait') }}</p> - <v-progress-linear :model-value="exportProgress.current" :max="exportProgress.total" class="mt-4" color="primary"></v-progress-linear> - </div> - - <div v-else-if="exportStatus === 'completed'" class="text-center py-8"> - <v-icon size="64" color="success" class="mb-4">mdi-check-circle</v-icon> - <h3 class="mb-4">{{ t('features.settings.backup.export.completed') }}</h3> - <p class="mb-4">{{ exportResult?.filename }}</p> - <v-btn color="primary" variant="tonal" @click="downloadBackup(exportResult?.filename)" class="mr-2"> - <v-icon class="mr-2">mdi-download</v-icon> - {{ t('features.settings.backup.export.download') }} - </v-btn> - <v-btn color="grey" variant="text" @click="resetExport"> - {{ t('features.settings.backup.export.another') }} - </v-btn> - </div> - - <div v-else-if="exportStatus === 'failed'" class="text-center py-8"> - <v-icon size="64" color="error" class="mb-4">mdi-alert-circle</v-icon> - <h3 class="mb-4">{{ t('features.settings.backup.export.failed') }}</h3> - <v-alert type="error" variant="tonal" class="mb-4"> - {{ exportError }} - </v-alert> - <v-btn color="primary" variant="tonal" @click="resetExport"> - {{ t('features.settings.backup.export.retry') }} - </v-btn> - </div> - </v-window-item> - - <!-- 导入标签页 --> - <v-window-item value="import"> - <!-- 步骤1: 选择文件 --> - <div v-if="importStatus === 'idle'" class="py-4"> - <v-alert type="warning" variant="tonal" class="mb-4"> - <template v-slot:prepend> - <v-icon>mdi-alert</v-icon> - </template> - {{ t('features.settings.backup.import.warning') }} - </v-alert> - - <v-file-input - v-model="importFile" - :label="t('features.settings.backup.import.selectFile')" - accept=".zip" - prepend-icon="mdi-file-upload" - show-size - class="mb-4" - ></v-file-input> - - <div class="d-flex justify-center"> - <v-btn - color="primary" - variant="tonal" - size="large" - @click="uploadAndCheck" - :disabled="!importFile" - :loading="importStatus === 'uploading'" - > - <v-icon class="mr-2">mdi-upload</v-icon> - {{ t('features.settings.backup.import.uploadAndCheck') }} - </v-btn> - </div> - </div> - - <!-- 步骤1.5: 上传中 --> - <div v-else-if="importStatus === 'uploading'" class="text-center py-8"> - <v-icon size="64" color="primary" class="mb-4">mdi-cloud-upload</v-icon> - <h3 class="mb-4">{{ t('features.settings.backup.import.uploading') }}</h3> - <p class="text-grey mb-2"> - {{ uploadProgress.message || t('features.settings.backup.import.uploadWait') }} - </p> - <p class="text-grey-darken-1 mb-4"> - {{ formatFileSize(uploadProgress.uploaded) }} / {{ formatFileSize(uploadProgress.total) }} - ({{ uploadProgress.percent }}%) - </p> - <v-progress-linear - :model-value="uploadProgress.percent" - :max="100" - class="mt-2" - color="primary" - height="8" - rounded - ></v-progress-linear> - </div> - - <!-- 步骤2: 确认导入 --> - <div v-else-if="importStatus === 'confirm'" class="py-4"> - <v-alert - :type="versionAlertType" - variant="tonal" - class="mb-4" - > - <template v-slot:prepend> - <v-icon>{{ versionAlertIcon }}</v-icon> - </template> - <div class="confirm-message"> - <div class="text-h6 mb-2">{{ versionAlertTitle }}</div> - <div class="mb-2"> - <strong>{{ t('features.settings.backup.import.version.backupVersion') }}:</strong> {{ checkResult?.backup_version }}<br> - <strong>{{ t('features.settings.backup.import.version.currentVersion') }}:</strong> {{ checkResult?.current_version }} - </div> - <div v-if="checkResult?.backup_time && checkResult?.backup_time !== '未知'" class="mb-2"> - <strong>{{ t('features.settings.backup.import.version.backupTime') }}:</strong> {{ formatISODate(checkResult?.backup_time) }} - </div> - <div class="mt-3" style="white-space: pre-line;">{{ versionAlertMessage }}</div> - </div> - </v-alert> - - <!-- 备份摘要 --> - <v-card variant="outlined" class="mb-4" v-if="checkResult?.backup_summary"> - <v-card-title class="text-subtitle-1"> - <v-icon class="mr-2">mdi-package-variant</v-icon> - {{ t('features.settings.backup.import.backupContents') }} - </v-card-title> - <v-card-text> - <div class="d-flex flex-wrap ga-2"> - <v-chip v-if="checkResult.backup_summary.tables?.length" size="small" color="primary" variant="tonal" :ripple="false" class="non-interactive-chip"> - {{ checkResult.backup_summary.tables.length }} {{ t('features.settings.backup.import.tables') }} - </v-chip> - <v-chip v-if="checkResult.backup_summary.has_knowledge_bases" size="small" color="success" variant="tonal" :ripple="false" class="non-interactive-chip"> - {{ t('features.settings.backup.import.knowledgeBases') }} - </v-chip> - <v-chip v-if="checkResult.backup_summary.has_config" size="small" color="info" variant="tonal" :ripple="false" class="non-interactive-chip"> - {{ t('features.settings.backup.import.configFiles') }} - </v-chip> - <v-chip v-for="dir in (checkResult.backup_summary.directories || [])" :key="dir" size="small" color="warning" variant="tonal" :ripple="false" class="non-interactive-chip"> - {{ dir }} - </v-chip> - </div> - </v-card-text> - </v-card> - - <!-- 警告信息 --> - <v-alert v-if="checkResult?.warnings?.length" type="warning" variant="tonal" class="mb-4"> - <div v-for="(warning, idx) in checkResult.warnings" :key="idx">{{ warning }}</div> - </v-alert> - - <div class="d-flex justify-center align-center mt-4" style="gap: 16px;"> - <v-btn - color="grey-darken-1" - variant="text" - size="large" - @click="resetImport" - > - <v-icon class="mr-2">mdi-close</v-icon> - {{ t('core.common.cancel') }} - </v-btn> - <v-btn - v-if="checkResult?.can_import" - color="error" - size="large" - variant="tonal" - @click="confirmImport" - > - <v-icon class="mr-2">mdi-alert</v-icon> - {{ t('features.settings.backup.import.confirmImport') }} - </v-btn> - </div> - </div> - - <!-- 步骤3: 导入进行中 --> - <div v-else-if="importStatus === 'processing'" class="text-center py-8"> - <v-progress-circular indeterminate color="primary" size="64" class="mb-4"></v-progress-circular> - <h3 class="mb-4">{{ t('features.settings.backup.import.processing') }}</h3> - <p class="text-grey">{{ importProgress.message || t('features.settings.backup.import.wait') }}</p> - <v-progress-linear :model-value="importProgress.current" :max="importProgress.total" class="mt-4" color="primary"></v-progress-linear> - </div> - - <div v-else-if="importStatus === 'completed'" class="text-center py-8"> - <v-icon size="64" color="success" class="mb-4">mdi-check-circle</v-icon> - <h3 class="mb-4">{{ t('features.settings.backup.import.completed') }}</h3> - <v-alert type="info" variant="tonal" class="mb-4"> - {{ t('features.settings.backup.import.restartRequired') }} - </v-alert> - <v-btn color="primary" variant="tonal" @click="restartAstrBot" class="mr-2"> - <v-icon class="mr-2">mdi-restart</v-icon> - {{ t('features.settings.backup.import.restartNow') }} - </v-btn> - <v-btn color="grey" variant="text" @click="resetImport"> - {{ t('core.common.close') }} - </v-btn> - </div> - - <div v-else-if="importStatus === 'failed'" class="text-center py-8"> - <v-icon size="64" color="error" class="mb-4">mdi-alert-circle</v-icon> - <h3 class="mb-4">{{ t('features.settings.backup.import.failed') }}</h3> - <v-alert type="error" variant="tonal" class="mb-4"> - {{ importError }} - </v-alert> - <v-btn color="primary" variant="tonal" @click="resetImport"> - {{ t('features.settings.backup.import.retry') }} - </v-btn> - </div> - </v-window-item> - - <!-- 备份列表标签页 --> - <v-window-item value="list"> - <div v-if="loadingList" class="text-center py-8"> - <v-progress-circular indeterminate color="primary"></v-progress-circular> - </div> - - <div v-else-if="backupList.length === 0" class="text-center py-8"> - <v-icon size="64" color="grey" class="mb-4">mdi-folder-open-outline</v-icon> - <p class="text-grey">{{ t('features.settings.backup.list.empty') }}</p> - </div> - - <v-list v-else lines="two"> - <v-list-item - v-for="backup in backupList" - :key="backup.filename" - > - <template v-slot:prepend> - <v-icon :color="backup.type === 'uploaded' ? 'orange' : 'primary'"> - {{ backup.type === 'uploaded' ? 'mdi-upload' : 'mdi-zip-box' }} - </v-icon> - </template> - - <v-list-item-title>{{ backup.filename }}</v-list-item-title> - <v-list-item-subtitle> - {{ formatFileSize(backup.size) }} · {{ formatDate(backup.created_at) }} - <v-chip size="x-small" color="primary" variant="tonal" class="ml-2"> - v{{ backup.astrbot_version }} - </v-chip> - <v-chip v-if="backup.type === 'uploaded'" size="x-small" color="orange" variant="tonal" class="ml-1"> - {{ t('features.settings.backup.list.uploaded') }} - </v-chip> - </v-list-item-subtitle> - - <template v-slot:append> - <v-btn - icon="mdi-restore" - variant="text" - size="small" - color="success" - :title="t('features.settings.backup.list.restore')" - @click="restoreFromList(backup.filename)" - ></v-btn> - <v-btn - icon="mdi-pencil" - variant="text" - size="small" - :title="t('features.settings.backup.list.rename')" - @click="openRenameDialog(backup.filename)" - ></v-btn> - <v-btn icon="mdi-download" variant="text" size="small" @click="downloadBackup(backup.filename)"></v-btn> - <v-btn icon="mdi-delete" variant="text" size="small" color="error" @click="deleteBackup(backup.filename)"></v-btn> - </template> - </v-list-item> - </v-list> - - <div class="d-flex justify-center mt-4"> - <v-btn color="primary" variant="text" @click="loadBackupList"> - <v-icon class="mr-2">mdi-refresh</v-icon> - {{ t('features.settings.backup.list.refresh') }} - </v-btn> - </div> - - <!-- 提示信息 --> - <p class="text-caption text-grey text-center mt-4"> - <v-icon size="small" class="mr-1">mdi-information-outline</v-icon> - {{ t('features.settings.backup.list.ftpHint') }} - </p> - </v-window-item> - </v-window> - </v-card-text> - - <v-card-actions class="px-6 py-4"> - <v-spacer></v-spacer> - <v-btn color="grey" variant="text" @click="handleClose" :disabled="isProcessing"> - {{ t('core.common.close') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <!-- 重命名对话框 --> - <v-dialog v-model="renameDialogOpen" max-width="450" persistent> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - <v-icon class="mr-2">mdi-pencil</v-icon> - {{ t('features.settings.backup.list.renameTitle') }} - </v-card-title> - <v-card-text> - <v-text-field - v-model="renameNewName" - :label="t('features.settings.backup.list.newName')" - :rules="[renameValidationRule]" - :error-messages="renameError" - variant="outlined" - density="comfortable" - autofocus - @keyup.enter="confirmRename" - > - <template v-slot:append-inner> - <span class="text-grey">.zip</span> - </template> - </v-text-field> - <p class="text-caption text-grey mt-1"> - {{ t('features.settings.backup.list.renameHint') }} - </p> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn color="grey" variant="text" @click="closeRenameDialog"> - {{ t('core.common.cancel') }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - @click="confirmRename" - :loading="renameLoading" - :disabled="!renameNewName || !!renameError" - > - {{ t('core.common.confirm') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <WaitingForRestart ref="wfr"></WaitingForRestart> -</template> - -<script setup> -import { ref, computed, watch } from 'vue' -import { backupApi } from '@/api/v1' -import { useI18n } from '@/i18n/composables' -import { askForConfirmation, useConfirmDialog } from '@/utils/confirmDialog' -import { restartAstrBot as restartAstrBotRuntime } from '@/utils/restartAstrBot' -import WaitingForRestart from './WaitingForRestart.vue' - -const { t } = useI18n() - -const confirmDialog = useConfirmDialog() - -const isOpen = ref(false) -const activeTab = ref('export') -const wfr = ref(null) - -// 导出状态 -const exportStatus = ref('idle') // idle, processing, completed, failed -const exportTaskId = ref(null) -const exportProgress = ref({ current: 0, total: 100, message: '' }) -const exportResult = ref(null) -const exportError = ref('') - -// 导入状态 -const importStatus = ref('idle') // idle, uploading, confirm, processing, completed, failed -const importFile = ref(null) -const importTaskId = ref(null) -const importProgress = ref({ current: 0, total: 100, message: '' }) -const importError = ref('') -const uploadedFilename = ref('') // 已上传的文件名 -const checkResult = ref(null) // 预检查结果 - -// 分片上传状态 -const CONCURRENT_UPLOADS = 5 // 并发上传数 -const uploadId = ref('') -const chunkSize = ref(0) // 分片大小(从后端获取) -const uploadProgress = ref({ - uploaded: 0, - total: 0, - percent: 0, - message: '' -}) - -// 备份列表 -const loadingList = ref(false) -const backupList = ref([]) - -// 重命名对话框状态 -const renameDialogOpen = ref(false) -const renameOldFilename = ref('') -const renameNewName = ref('') -const renameLoading = ref(false) -const renameError = ref('') - -// 计算属性 -const isProcessing = computed(() => { - return exportStatus.value === 'processing' || - importStatus.value === 'processing' || - importStatus.value === 'uploading' -}) - -// 版本检查相关的计算属性 -const versionAlertType = computed(() => { - const status = checkResult.value?.version_status - if (status === 'major_diff') return 'error' - if (status === 'minor_diff') return 'warning' - return 'info' -}) - -const versionAlertIcon = computed(() => { - const status = checkResult.value?.version_status - if (status === 'major_diff') return 'mdi-close-circle' - if (status === 'minor_diff') return 'mdi-alert' - return 'mdi-check-circle' -}) - -const versionAlertTitle = computed(() => { - const status = checkResult.value?.version_status - if (status === 'major_diff') return t('features.settings.backup.import.version.majorDiffTitle') - if (status === 'minor_diff') return t('features.settings.backup.import.version.minorDiffTitle') - return t('features.settings.backup.import.version.matchTitle') -}) - -const versionAlertMessage = computed(() => { - const status = checkResult.value?.version_status - if (status === 'major_diff') return t('features.settings.backup.import.version.majorDiffMessage') - if (status === 'minor_diff') return t('features.settings.backup.import.version.minorDiffMessage') - return t('features.settings.backup.import.version.matchMessage') -}) - -// 监听对话框打开 -watch(isOpen, (newVal) => { - if (newVal) { - loadBackupList() - } else { - resetAll() - } -}) - -// 监听标签页切换 -watch(activeTab, (newVal) => { - if (newVal === 'list') { - loadBackupList() - } -}) - -// 加载备份列表 -const loadBackupList = async () => { - loadingList.value = true - try { - const response = await backupApi.list() - if (response.data.status === 'ok') { - backupList.value = response.data.data.items || [] - } - } catch (error) { - console.error('Failed to load backup list:', error) - } finally { - loadingList.value = false - } -} - -// 开始导出 -const startExport = async () => { - exportStatus.value = 'processing' - exportProgress.value = { current: 0, total: 100, message: '' } - - try { - const response = await backupApi.create() - if (response.data.status === 'ok') { - exportTaskId.value = response.data.data.task_id - pollExportProgress() - } else { - throw new Error(response.data.message) - } - } catch (error) { - exportStatus.value = 'failed' - exportError.value = error.message || 'Export failed' - } -} - -// 轮询导出进度 -const pollExportProgress = async () => { - if (!exportTaskId.value) return - - try { - const response = await backupApi.progress(exportTaskId.value) - - if (response.data.status === 'ok') { - const data = response.data.data - - if (data.status === 'processing' && data.progress) { - exportProgress.value = { - current: data.progress.current || 0, - total: data.progress.total || 100, - message: data.progress.message || '' - } - setTimeout(pollExportProgress, 1000) - } else if (data.status === 'completed') { - exportStatus.value = 'completed' - exportResult.value = data.result - loadBackupList() - } else if (data.status === 'failed') { - exportStatus.value = 'failed' - exportError.value = data.error || 'Export failed' - } else { - setTimeout(pollExportProgress, 1000) - } - } - } catch (error) { - exportStatus.value = 'failed' - exportError.value = error.message || 'Failed to get export progress' - } -} - -// 重置导出状态 -const resetExport = () => { - exportStatus.value = 'idle' - exportTaskId.value = null - exportProgress.value = { current: 0, total: 100, message: '' } - exportResult.value = null - exportError.value = '' -} - -/** - * 并发上传分片 - * - * 使用并发控制同时上传多个分片,提升上传速度。 - * 后端按分片索引命名文件(如 0.part, 1.part),合并时按顺序读取, - * 因此分片到达顺序不影响最终结果。 - */ -const uploadChunksInParallel = async (file, totalChunks, currentUploadId, currentChunkSize) => { - // 跟踪已完成的字节数(使用原子操作避免并发问题) - let completedBytes = 0 - const chunkSizes = [] - - // 预计算每个分片的大小(使用后端返回的 chunk_size) - for (let i = 0; i < totalChunks; i++) { - const start = i * currentChunkSize - const end = Math.min(start + currentChunkSize, file.size) - chunkSizes[i] = end - start - } - - // 上传单个分片的函数 - const uploadSingleChunk = async (chunkIndex) => { - const start = chunkIndex * currentChunkSize - const end = Math.min(start + currentChunkSize, file.size) - const chunk = file.slice(start, end) - - const response = await backupApi.uploadChunk({ - upload_id: currentUploadId, - chunk_index: chunkIndex, - chunk - }) - - if (response.data.status !== 'ok') { - throw new Error(response.data.message) - } - - // 更新进度(累加已完成字节) - completedBytes += chunkSizes[chunkIndex] - uploadProgress.value.uploaded = completedBytes - uploadProgress.value.percent = Math.round((completedBytes / file.size) * 100) - - return response - } - - // 创建分片索引队列 - const pendingChunks = Array.from({ length: totalChunks }, (_, i) => i) - const activePromises = [] - - // 处理队列中的分片 - while (pendingChunks.length > 0 || activePromises.length > 0) { - // 填充并发槽位 - while (pendingChunks.length > 0 && activePromises.length < CONCURRENT_UPLOADS) { - const chunkIndex = pendingChunks.shift() - const promise = uploadSingleChunk(chunkIndex).then(() => { - // 完成后从活动列表移除 - const idx = activePromises.indexOf(promise) - if (idx > -1) activePromises.splice(idx, 1) - }) - activePromises.push(promise) - } - - // 等待至少一个完成 - if (activePromises.length > 0) { - await Promise.race(activePromises) - } - } -} - -// 上传并检查 -const uploadAndCheck = async () => { - if (!importFile.value) return - - importStatus.value = 'uploading' - const file = importFile.value - - try { - // 初始化上传进度 - uploadProgress.value = { - uploaded: 0, - total: file.size, - percent: 0, - message: t('features.settings.backup.import.uploadInit') - } - - // 步骤1: 初始化分片上传(后端计算并返回 chunk_size 和 total_chunks) - const initResponse = await backupApi.initUpload({ - filename: file.name, - total_size: file.size - }) - - if (initResponse.data.status !== 'ok') { - throw new Error(initResponse.data.message) - } - - uploadId.value = initResponse.data.data.upload_id - chunkSize.value = initResponse.data.data.chunk_size - const totalChunks = initResponse.data.data.total_chunks - - // 步骤2: 并行分片上传(5个并发连接) - uploadProgress.value.message = t('features.settings.backup.import.uploadingChunks') - - await uploadChunksInParallel(file, totalChunks, uploadId.value, chunkSize.value) - - // 步骤3: 完成上传 - uploadProgress.value.message = t('features.settings.backup.import.uploadComplete') - - const completeResponse = await backupApi.completeUpload({ - upload_id: uploadId.value - }) - - if (completeResponse.data.status !== 'ok') { - throw new Error(completeResponse.data.message) - } - - uploadedFilename.value = completeResponse.data.data.filename - - // 步骤4: 预检查 - uploadProgress.value.message = t('features.settings.backup.import.checking') - - const checkResponse = await backupApi.check(uploadedFilename.value) - - if (checkResponse.data.status !== 'ok') { - throw new Error(checkResponse.data.message) - } - - checkResult.value = checkResponse.data.data - - // 检查是否有效 - if (!checkResult.value.valid) { - importStatus.value = 'failed' - importError.value = checkResult.value.error || t('features.settings.backup.import.invalidBackup') - return - } - - // 显示确认对话框 - importStatus.value = 'confirm' - - } catch (error) { - // 上传失败时尝试清理已上传的分片 - if (uploadId.value) { - try { - await backupApi.abortUpload({ - upload_id: uploadId.value - }) - } catch (abortError) { - console.error('Failed to abort upload:', abortError) - } - } - - importStatus.value = 'failed' - importError.value = error.response?.data?.message || error.message || 'Upload failed' - } -} - -// 确认导入 -const confirmImport = async () => { - if (!uploadedFilename.value) return - - importStatus.value = 'processing' - importProgress.value = { current: 0, total: 100, message: '' } - - try { - const response = await backupApi.import(uploadedFilename.value, true) - - if (response.data.status === 'ok') { - importTaskId.value = response.data.data.task_id - pollImportProgress() - } else { - throw new Error(response.data.message) - } - } catch (error) { - importStatus.value = 'failed' - importError.value = error.response?.data?.message || error.message || 'Import failed' - } -} - -// 轮询导入进度 -const pollImportProgress = async () => { - if (!importTaskId.value) return - - try { - const response = await backupApi.progress(importTaskId.value) - - if (response.data.status === 'ok') { - const data = response.data.data - - if (data.status === 'processing' && data.progress) { - importProgress.value = { - current: data.progress.current || 0, - total: data.progress.total || 100, - message: data.progress.message || '' - } - setTimeout(pollImportProgress, 1000) - } else if (data.status === 'completed') { - importStatus.value = 'completed' - } else if (data.status === 'failed') { - importStatus.value = 'failed' - importError.value = data.error || 'Import failed' - } else { - setTimeout(pollImportProgress, 1000) - } - } - } catch (error) { - importStatus.value = 'failed' - importError.value = error.message || 'Failed to get import progress' - } -} - -// 重置导入状态 -const resetImport = async () => { - // 如果有进行中的上传,先取消 - if (uploadId.value && importStatus.value === 'uploading') { - try { - await backupApi.abortUpload({ - upload_id: uploadId.value - }) - } catch (error) { - console.error('Failed to abort upload:', error) - } - } - - importStatus.value = 'idle' - importFile.value = null - importTaskId.value = null - importProgress.value = { current: 0, total: 100, message: '' } - importError.value = '' - uploadedFilename.value = '' - checkResult.value = null - uploadId.value = '' - chunkSize.value = 0 - uploadProgress.value = { uploaded: 0, total: 0, percent: 0, message: '' } -} - -// 下载备份(使用浏览器原生下载,可显示下载进度) -const downloadBackup = (filename) => { - // 获取 token 用于鉴权(因为浏览器原生下载无法携带 Authorization header) - const token = localStorage.getItem('token') - if (!token) { - alert(t('core.common.unauthorized')) - return - } - - // 直接使用浏览器下载,这样可以看到原生下载进度条 - const downloadUrl = backupApi.downloadUrl(filename, token) - - // 创建隐藏的 a 标签触发下载 - const link = document.createElement('a') - link.href = downloadUrl - link.download = filename - link.style.display = 'none' - document.body.appendChild(link) - link.click() - document.body.removeChild(link) -} - -// 从列表中恢复备份 -const restoreFromList = async (filename) => { - // 切换到导入标签页并设置文件名 - uploadedFilename.value = filename - - // 预检查 - try { - const checkResponse = await backupApi.check(filename) - - if (checkResponse.data.status !== 'ok') { - throw new Error(checkResponse.data.message) - } - - checkResult.value = checkResponse.data.data - - if (!checkResult.value.valid) { - alert(checkResult.value.error || t('features.settings.backup.import.invalidBackup')) - return - } - - // 切换到导入标签页并显示确认 - activeTab.value = 'import' - importStatus.value = 'confirm' - - } catch (error) { - alert(error.response?.data?.message || error.message || 'Check failed') - } -} - -// 删除备份 -const deleteBackup = async (filename) => { - if (!(await askForConfirmation(t('features.settings.backup.list.confirmDelete'), confirmDialog))) return - - try { - const response = await backupApi.delete(filename) - if (response.data.status === 'ok') { - loadBackupList() - } else { - alert(response.data.message || 'Delete failed') - } - } catch (error) { - alert(error.message || 'Delete failed') - } -} - -// 重命名相关函数 -const openRenameDialog = (filename) => { - renameOldFilename.value = filename - // 移除 .zip 后缀,只显示文件名部分 - renameNewName.value = filename.replace(/\.zip$/i, '') - renameError.value = '' - renameDialogOpen.value = true -} - -const closeRenameDialog = () => { - renameDialogOpen.value = false - renameOldFilename.value = '' - renameNewName.value = '' - renameError.value = '' -} - -// 文件名验证规则 -const renameValidationRule = (value) => { - if (!value) return t('features.settings.backup.list.renameRequired') - // 检查是否包含非法字符 - if (/[\\/:*?"<>|]/.test(value)) { - return t('features.settings.backup.list.renameInvalidChars') - } - // 检查是否包含路径遍历字符 - if (value.includes('..')) { - return t('features.settings.backup.list.renameInvalidChars') - } - return true -} - -const confirmRename = async () => { - if (!renameNewName.value || renameError.value) return - - // 前端验证 - const validationResult = renameValidationRule(renameNewName.value) - if (validationResult !== true) { - renameError.value = validationResult - return - } - - renameLoading.value = true - renameError.value = '' - - try { - const response = await backupApi.rename(renameOldFilename.value, { - new_name: renameNewName.value - }) - - if (response.data.status === 'ok') { - closeRenameDialog() - loadBackupList() - } else { - renameError.value = response.data.message || t('features.settings.backup.list.renameFailed') - } - } catch (error) { - renameError.value = error.response?.data?.message || error.message || t('features.settings.backup.list.renameFailed') - } finally { - renameLoading.value = false - } -} - -// 格式化文件大小 -const formatFileSize = (bytes) => { - if (bytes === 0) return '0 B' - const k = 1024 - const sizes = ['B', 'KB', 'MB', 'GB'] - const i = Math.floor(Math.log(bytes) / Math.log(k)) - return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] -} - -// 格式化日期(从时间戳) -const formatDate = (timestamp) => { - return new Date(timestamp * 1000).toLocaleString() -} - -// 格式化 ISO 日期字符串 -const formatISODate = (isoString) => { - if (!isoString) return '' - try { - return new Date(isoString).toLocaleString() - } catch { - return isoString - } -} - -// 重启 AstrBot -const restartAstrBot = async () => { - try { - await restartAstrBotRuntime(wfr.value) - } catch (error) { - console.error(error) - } -} - -// 重置所有状态 -const resetAll = async () => { - resetExport() - await resetImport() - activeTab.value = 'export' -} - -// 关闭对话框 -const handleClose = () => { - if (isProcessing.value) return - isOpen.value = false -} - -// 打开对话框 -const open = () => { - isOpen.value = true -} - -defineExpose({ open }) -</script> - -<style scoped> -.v-list-item { - border-bottom: 1px solid rgba(0, 0, 0, 0.08); -} - -.v-list-item:last-child { - border-bottom: none; -} - -/* 禁用 Chip 的交互效果 */ -.non-interactive-chip { - pointer-events: none; - cursor: default; -} - -.non-interactive-chip:hover { - box-shadow: none !important; -} -</style> diff --git a/legacy-dashboard/src/components/shared/ChangelogDialog.vue b/legacy-dashboard/src/components/shared/ChangelogDialog.vue deleted file mode 100644 index e9d95e61..00000000 --- a/legacy-dashboard/src/components/shared/ChangelogDialog.vue +++ /dev/null @@ -1,229 +0,0 @@ -<script setup> -import { ref, watch, computed } from 'vue'; -import { changelogApi, statsApi } from '@/api/v1'; -import { useI18n } from '@/i18n/composables'; -import { MarkdownRender, enableKatex, enableMermaid } from 'markstream-vue'; -import 'markstream-vue/index.css'; -import 'katex/dist/katex.min.css'; - -enableKatex(); -enableMermaid(); - -const { t } = useI18n(); - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - } -}); - -const emit = defineEmits(['update:modelValue']); - -const dialog = computed({ - get: () => props.modelValue, - set: (value) => emit('update:modelValue', value) -}); - -const changelogContent = ref(''); -const changelogLoading = ref(false); -const changelogError = ref(''); -const changelogVersion = ref(''); -const selectedVersion = ref(''); -const availableVersions = ref([]); -const loadingVersions = ref(false); -const scrollContainer = ref(null); - -// 获取当前版本号(从版本信息中提取) -async function getCurrentVersion() { - try { - const res = await statsApi.version(); - const version = res.data.data?.version || ''; - changelogVersion.value = version; - selectedVersion.value = version; - return version; - } catch (err) { - console.error('Failed to get version:', err); - return ''; - } -} - -// 加载更新日志 -async function loadChangelog(version) { - const targetVersion = version || selectedVersion.value || changelogVersion.value; - if (!targetVersion) { - changelogError.value = t('core.navigation.changelogDialog.selectVersion'); - return; - } - - changelogLoading.value = true; - changelogError.value = ''; - changelogContent.value = ''; - - try { - const res = await changelogApi.get(targetVersion); - - if (res.data.status === 'ok') { - changelogContent.value = res.data.data.content; - selectedVersion.value = targetVersion; - } else { - changelogError.value = res.data.message || t('core.navigation.changelogDialog.error'); - } - } catch (err) { - console.error('Failed to load changelog:', err); - if (err.response?.status === 404 || err.response?.data?.message?.includes('not found')) { - changelogError.value = t('core.navigation.changelogDialog.notFound'); - } else { - changelogError.value = t('core.navigation.changelogDialog.error'); - } - } finally { - changelogLoading.value = false; - } -} - -// 获取所有可用版本列表 -async function loadAvailableVersions() { - loadingVersions.value = true; - try { - const res = await changelogApi.listVersions(); - if (res.data.status === 'ok') { - availableVersions.value = res.data.data.versions || []; - } - } catch (err) { - console.error('Failed to load versions:', err); - } finally { - loadingVersions.value = false; - } -} - -// 版本选择变化时加载对应的更新日志 -function onVersionChange() { - if (selectedVersion.value) { - loadChangelog(selectedVersion.value); - } -} - -function handleChangelogClick(event) { - const targetElement = event.target instanceof Element ? event.target : null; - const anchor = targetElement?.closest('a[href^="#"]'); - if (!anchor) return; - - const rawHref = anchor.getAttribute('href'); - const targetId = rawHref ? decodeURIComponent(rawHref.slice(1)) : ''; - if (!targetId) return; - - const target = scrollContainer.value?.querySelector( - `#${CSS.escape(targetId)}`, - ); - if (!target) return; - - event.preventDefault(); - target.scrollIntoView({ behavior: 'smooth', block: 'start' }); -} - -// 监听对话框打开,初始化数据 -watch(dialog, async (newValue) => { - if (newValue) { - // 加载版本列表 - await loadAvailableVersions(); - - // 获取当前版本 - if (!changelogVersion.value) { - await getCurrentVersion(); - } - - // 如果当前版本在列表中,默认选择当前版本 - if (changelogVersion.value && availableVersions.value.includes(changelogVersion.value)) { - selectedVersion.value = changelogVersion.value; - await loadChangelog(); - } else if (availableVersions.value.length > 0) { - // 否则选择第一个(最新的) - selectedVersion.value = availableVersions.value[0]; - await loadChangelog(availableVersions.value[0]); - } - } else { - // 关闭时重置状态 - changelogContent.value = ''; - changelogError.value = ''; - } -}); - -// 初始化时获取版本号 -getCurrentVersion(); -</script> - -<template> - <v-dialog - :model-value="dialog" - @update:model-value="dialog = $event" - :width="$vuetify.display.smAndDown ? '100%' : '800'" - :fullscreen="$vuetify.display.xs" - max-width="1000" - > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex justify-space-between align-center"> - <span>{{ t('core.navigation.changelogDialog.title') }}</span> - <v-btn icon @click="dialog = false" variant="text"> - <v-icon>mdi-close</v-icon> - </v-btn> - </v-card-title> - <v-card-text class="pb-5"> - <!-- 版本选择器 --> - <div class="mb-4"> - <v-select - v-model="selectedVersion" - :items="availableVersions" - :label="t('core.navigation.changelogDialog.selectVersion')" - :loading="loadingVersions" - variant="outlined" - density="compact" - @update:model-value="onVersionChange" - > - <template v-slot:item="{ item, props }"> - <v-list-item v-bind="props" :title="`v${item.value}`"> - <template v-slot:append v-if="item.value === changelogVersion"> - <v-chip size="x-small" color="primary" variant="tonal"> - {{ t('core.navigation.changelogDialog.current') }} - </v-chip> - </template> - </v-list-item> - </template> - <template v-slot:selection="{ item }"> - <span>v{{ item.value }}</span> - </template> - </v-select> - </div> - - <!-- 更新日志内容 --> - <div - ref="scrollContainer" - style="max-height: 70vh; overflow-y: auto;" - @click="handleChangelogClick" - > - <div v-if="changelogLoading" class="text-center py-8"> - <v-progress-circular indeterminate color="primary"></v-progress-circular> - <div class="mt-4">{{ t('core.navigation.changelogDialog.loading') }}</div> - </div> - <v-alert v-else-if="changelogError" type="error" variant="tonal" border="start"> - {{ changelogError }} - </v-alert> - <div v-else-if="changelogContent" class="changelog-content"> - <MarkdownRender :content="changelogContent" :typewriter="false" class="markdown-content" /> - </div> - </div> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn color="blue-darken-1" variant="text" @click="dialog = false"> - {{ t('core.common.close') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<style> -.changelog-content { - padding: 8px 0; -} -</style> diff --git a/legacy-dashboard/src/components/shared/ConfigItemRenderer.vue b/legacy-dashboard/src/components/shared/ConfigItemRenderer.vue deleted file mode 100644 index f791dd54..00000000 --- a/legacy-dashboard/src/components/shared/ConfigItemRenderer.vue +++ /dev/null @@ -1,437 +0,0 @@ -<template> - <div class="w-100"> - <!-- Special handling for specific metadata types --> - <template v-if="itemMeta?._special === 'select_provider'"> - <ProviderSelector :model-value="modelValue" @update:model-value="emitUpdate" :provider-type="'chat_completion'" /> - </template> - <template v-else-if="itemMeta?._special === 'select_provider_stt'"> - <ProviderSelector :model-value="modelValue" @update:model-value="emitUpdate" :provider-type="'speech_to_text'" /> - </template> - <template v-else-if="itemMeta?._special === 'select_provider_tts'"> - <ProviderSelector :model-value="modelValue" @update:model-value="emitUpdate" :provider-type="'text_to_speech'" /> - </template> - <template v-else-if="itemMeta?._special === 'select_providers'"> - <ProviderSelector - :model-value="modelValue" - @update:model-value="emitUpdate" - :provider-type="'chat_completion'" - :multiple="true" - /> - </template> - <template v-else-if="getSpecialName(itemMeta?._special) === 'select_agent_runner_provider'"> - <ProviderSelector - :model-value="modelValue" - @update:model-value="emitUpdate" - :provider-type="'agent_runner'" - :provider-subtype="getSpecialSubtype(itemMeta?._special)" - /> - </template> - <template v-else-if="itemMeta?._special === 'provider_pool'"> - <ProviderSelector :model-value="modelValue" @update:model-value="emitUpdate" :provider-type="'chat_completion'" - :button-text="t('core.shared.providerSelector.selectProviderPool')" /> - </template> - <template v-else-if="itemMeta?._special === 'select_persona'"> - <PersonaSelector :model-value="modelValue" @update:model-value="emitUpdate" /> - </template> - <template v-else-if="itemMeta?._special === 'persona_pool'"> - <PersonaSelector :model-value="modelValue" @update:model-value="emitUpdate" :button-text="t('core.shared.personaSelector.selectPersonaPool')" /> - </template> - <template v-else-if="itemMeta?._special === 'select_knowledgebase'"> - <KnowledgeBaseSelector :model-value="modelValue" @update:model-value="emitUpdate" /> - </template> - <template v-else-if="itemMeta?._special === 'select_plugin_set'"> - <PluginSetSelector :model-value="modelValue" @update:model-value="emitUpdate" /> - </template> - <template v-else-if="itemMeta?._special === 't2i_template'"> - <T2ITemplateEditor /> - </template> - <template v-else-if="itemMeta?._special === 'dashboard_totp_manager'"> - <DashboardTotpManager - :model-value="Boolean(modelValue)" - :config-root="configRoot" - @update:model-value="emitUpdate" - /> - </template> - <template v-else-if="itemMeta?._special === 'get_embedding_dim'"> - <div class="d-flex align-center gap-2"> - <v-text-field - :model-value="modelValue" - @update:model-value="emitUpdate" - density="compact" - variant="outlined" - class="config-field" - type="number" - hide-details - ></v-text-field> - <v-btn - color="primary" - variant="tonal" - size="small" - @click="$emit('get-embedding-dim')" - :loading="loading" - class="ml-2" - > - {{ t('core.common.autoDetect') }} - </v-btn> - </div> - </template> - - <div - v-else-if="itemMeta?.type === 'list' && itemMeta?.options && itemMeta?.render_type === 'checkbox'" - class="checkbox-group d-flex flex-wrap" - > - <v-checkbox - v-for="(option, optionIndex) in itemMeta.options" - :key="optionIndex" - :model-value="modelValue" - @update:model-value="emitUpdate" - :label="getLabel(itemMeta, optionIndex, option)" - :value="option" - class="config-checkbox" - color="primary" - density="compact" - hide-details - ></v-checkbox> - </div> - - <v-autocomplete - v-else-if="itemMeta?.type === 'list' && itemMeta?.options" - :model-value="modelValue" - @update:model-value="val => { emitUpdate(val); listSearchText = '' }" - v-model:search="listSearchText" - :items="listSelectItems" - item-title="title" - item-value="value" - :disabled="itemMeta?.readonly" - density="compact" - variant="outlined" - class="config-field" - hide-details - chips - multiple - ></v-autocomplete> - - <v-select - v-else-if="itemMeta?.options" - :model-value="modelValue" - @update:model-value="emitUpdate" - :items="getSelectItems(itemMeta)" - :disabled="itemMeta?.readonly" - density="compact" - variant="outlined" - class="config-field" - hide-details - ></v-select> - - <div v-else-if="itemMeta?.editor_mode" class="editor-container"> - <VueMonacoEditor - :theme="itemMeta?.editor_theme || 'vs-light'" - :language="itemMeta?.editor_language || 'json'" - style="min-height: 100px; flex-grow: 1; border: 1px solid rgba(0, 0, 0, 0.1);" - :value="modelValue" - @update:value="emitUpdate" - > - </VueMonacoEditor> - <v-btn v-if="showFullscreenBtn" icon size="small" variant="text" color="primary" class="editor-fullscreen-btn" - @click="$emit('open-fullscreen')" - :title="t('core.common.editor.fullscreen')"> - <v-icon>mdi-fullscreen</v-icon> - </v-btn> - </div> - - <v-text-field - v-else-if="itemMeta?.type === 'string'" - :model-value="modelValue" - @update:model-value="emitUpdate" - density="compact" - variant="outlined" - class="config-field" - hide-details - ></v-text-field> - - <div - v-else-if="itemMeta?.type === 'int' || itemMeta?.type === 'float'" - class="d-flex align-center gap-3" - > - <v-slider - v-if="itemMeta?.slider" - :model-value="toNumber(numericTemp ?? modelValue)" - @update:model-value="val => { numericTemp = val; emitUpdate(toNumber(val)) }" - @end="numericTemp = null" - :min="itemMeta?.slider?.min ?? 0" - :max="itemMeta?.slider?.max ?? 100" - :step="itemMeta?.slider?.step ?? 1" - color="primary" - density="compact" - hide-details - style="flex: 1" - ></v-slider> - <v-text-field - :model-value="numericTemp ?? modelValue" - @update:model-value="val => (numericTemp = val)" - @blur="() => { if (numericTemp != null) { emitUpdate(toNumber(numericTemp)) } numericTemp = null }" - density="compact" - variant="outlined" - class="config-field" - type="number" - hide-details - style="flex: 1" - ></v-text-field> - </div> - - <v-textarea - v-else-if="itemMeta?.type === 'text'" - :model-value="modelValue" - @update:model-value="emitUpdate" - variant="outlined" - rows="3" - class="config-field" - hide-details - ></v-textarea> - - <v-switch - v-else-if="itemMeta?.type === 'bool'" - :model-value="modelValue" - @update:model-value="emitUpdate" - color="primary" - inset - density="compact" - hide-details - ></v-switch> - - <FileConfigItem - v-else-if="itemMeta?.type === 'file'" - :model-value="modelValue" - :item-meta="itemMeta" - :plugin-name="pluginName" - :config-key="configKey" - @update:model-value="emitUpdate" - class="config-field" - /> - - <ListConfigItem - v-else-if="itemMeta?.type === 'list'" - :model-value="modelValue" - @update:model-value="emitUpdate" - class="config-field" - /> - - <ObjectEditor - v-else-if="itemMeta?.type === 'dict'" - :model-value="modelValue" - :item-meta="itemMeta" - :plugin-name="pluginName" - :plugin-i18n="pluginI18n" - :config-key="configKey" - @update:model-value="emitUpdate" - class="config-field" - /> - - <v-text-field - v-else - :model-value="modelValue" - @update:model-value="emitUpdate" - density="compact" - variant="outlined" - class="config-field" - hide-details - ></v-text-field> - </div> -</template> - -<script setup> -import { VueMonacoEditor } from '@guolao/vue-monaco-editor' -import ListConfigItem from './ListConfigItem.vue' -import FileConfigItem from './FileConfigItem.vue' -import ObjectEditor from './ObjectEditor.vue' -import ProviderSelector from './ProviderSelector.vue' -import PersonaSelector from './PersonaSelector.vue' -import KnowledgeBaseSelector from './KnowledgeBaseSelector.vue' -import PluginSetSelector from './PluginSetSelector.vue' -import T2ITemplateEditor from './T2ITemplateEditor.vue' -import DashboardTotpManager from './DashboardTotpManager.vue' -import { computed, ref } from 'vue' -import { useI18n, useModuleI18n } from '@/i18n/composables' -import { usePluginI18n } from '@/utils/pluginI18n' - -const numericTemp = ref(null) -const listSearchText = ref('') - -const props = defineProps({ - modelValue: { - type: [String, Number, Boolean, Array, Object], - default: null - }, - itemMeta: { - type: Object, - default: null - }, - pluginName: { - type: String, - default: '' - }, - pluginI18n: { - type: Object, - default: () => ({}) - }, - configKey: { - type: String, - default: '' - }, - loading: { - type: Boolean, - default: false - }, - showFullscreenBtn: { - type: Boolean, - default: false - }, - configRoot: { - type: Object, - default: null - } -}) - -const emit = defineEmits(['update:modelValue', 'get-embedding-dim', 'open-fullscreen']) -const { t } = useI18n() -const { getRaw } = useModuleI18n('features/config-metadata') -const { configText } = usePluginI18n() - -function emitUpdate(val) { - emit('update:modelValue', val) -} - -const listSelectItems = computed(() => - props.itemMeta?.type === 'list' && props.itemMeta?.options - ? getSelectItems(props.itemMeta) - : [] -) - -function toNumber(val) { - const n = parseFloat(val) - return isNaN(n) ? 0 : n -} - -function getLabel(itemMeta, index, option) { - const labels = getTranslatedLabels(itemMeta) - return labels ? labels[index] : option -} - -function getTranslatedLabels(itemMeta) { - if ( - props.pluginName - && props.configKey - && props.pluginI18n - && Object.keys(props.pluginI18n).length > 0 - ) { - const translatedLabels = configText(props.pluginI18n, props.configKey, 'labels', null) - if (Array.isArray(translatedLabels)) { - return translatedLabels - } - } - if (!itemMeta?.labels) return null - if (typeof itemMeta.labels === 'string') { - const translatedLabels = getRaw(itemMeta.labels) - if (Array.isArray(translatedLabels)) { - return translatedLabels - } - } - if (Array.isArray(itemMeta.labels)) { - return itemMeta.labels - } - return null -} - -function getSelectItems(itemMeta) { - const labels = getTranslatedLabels(itemMeta) - if (labels && itemMeta.options) { - return itemMeta.options.map((value, index) => ({ - title: labels[index] || value, - value: value - })) - } - return itemMeta.options || [] -} - -function parseSpecialValue(value) { - if (!value || typeof value !== 'string') { - return { name: '', subtype: '' } - } - const [name, ...rest] = value.split(':') - return { - name, - subtype: rest.join(':') || '' - } -} - -function getSpecialName(value) { - return parseSpecialValue(value).name -} - -function getSpecialSubtype(value) { - return parseSpecialValue(value).subtype -} -</script> - -<style scoped> -.config-field { - margin-bottom: 0; -} - -.editor-container { - position: relative; - display: flex; - width: 100%; -} - -.editor-fullscreen-btn { - position: absolute; - top: 4px; - right: 4px; - z-index: 10; - background-color: rgba(0, 0, 0, 0.3); - border-radius: 4px; -} - -.editor-fullscreen-btn:hover { - background-color: rgba(0, 0, 0, 0.5); -} - -.checkbox-group { - gap: 6px 12px; -} - -.config-checkbox { - margin-right: 0; -} - -.config-checkbox :deep(.v-selection-control) { - min-height: 28px; -} - -.config-checkbox :deep(.v-selection-control__wrapper) { - width: 18px; - height: 18px; -} - -.config-checkbox :deep(.v-icon) { - font-size: 18px; -} - -.config-checkbox :deep(.v-label) { - font-size: 0.9rem; -} - -:deep(.v-field__input) { - font-size: 14px; -} - -:deep(.config-field input[type='number']::-webkit-inner-spin-button), -:deep(.config-field input[type='number']::-webkit-outer-spin-button) { - -webkit-appearance: none; -} - -:deep(.config-field input[type='number']) { - -moz-appearance: textfield; -} -</style> diff --git a/legacy-dashboard/src/components/shared/ConsoleDisplayer.vue b/legacy-dashboard/src/components/shared/ConsoleDisplayer.vue deleted file mode 100644 index fac250c8..00000000 --- a/legacy-dashboard/src/components/shared/ConsoleDisplayer.vue +++ /dev/null @@ -1,427 +0,0 @@ -<script setup> -import { useCommonStore } from '@/stores/common'; -import { logApi } from '@/api/v1'; -import { EventSourcePolyfill } from 'event-source-polyfill'; -</script> - -<template> - <div class="console-displayer-wrapper" id="console-wrapper"> - <div class="filter-controls mb-2" v-if="showLevelBtns"> - <v-chip-group v-model="selectedLevels" column multiple> - <v-chip v-for="level in logLevels" :key="level" :color="getLevelColor(level)" filter variant="flat" size="small" - :text-color="level === 'DEBUG' || level === 'INFO' ? 'black' : 'white'" class="font-weight-medium"> - {{ level }} - </v-chip> - </v-chip-group> - <v-spacer></v-spacer> - <v-btn - :icon="isFullscreen ? 'mdi-fullscreen-exit' : 'mdi-fullscreen'" - variant="text" - density="compact" - class="me-4 fullscreen-btn" - @click="toggleFullscreen" - ></v-btn> - </div> - - <div id="term" class="console-term"> - </div> - </div> -</template> - -<script> -export default { - name: 'ConsoleDisplayer', - data() { - return { - autoScroll: true, - isFullscreen: false, - logColorAnsiMap: { - '\u001b[1;34m': 'color: #6cb6d9; font-weight: bold;', - '\u001b[1;36m': 'color: #72c4cc; font-weight: bold;', - '\u001b[1;33m': 'color: #d4b95e; font-weight: bold;', - '\u001b[31m': 'color: #d46a6a;', - '\u001b[1;31m': 'color: #e06060; font-weight: bold;', - '\u001b[0m': 'color: inherit; font-weight: normal;', - '\u001b[32m': 'color: #6cc070;', - 'default': 'color: #c8c8c8;' - }, - logLevels: ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'], - selectedLevels: [0, 1, 2, 3, 4], - levelColors: { - 'DEBUG': 'grey', - 'INFO': 'blue-lighten-3', - 'WARNING': 'amber', - 'ERROR': 'red', - 'CRITICAL': 'purple' - }, - localLogCache: [], - eventSource: null, - retryTimer: null, - retryAttempts: 0, - maxRetryAttempts: 10, - baseRetryDelay: 1000, - lastEventId: null, - } - }, - computed: { - commonStore() { - return useCommonStore(); - }, - }, - props: { - historyNum: { - type: String, - default: "-1" - }, - showLevelBtns: { - type: Boolean, - default: true - } - }, - watch: { - selectedLevels: { - handler() { - this.refreshDisplay(); - }, - deep: true - } - }, - async mounted() { - await this.fetchLogHistory(); - this.connectSSE(); - document.addEventListener('fullscreenchange', this.handleFullscreenChange); - }, - beforeUnmount() { - document.removeEventListener('fullscreenchange', this.handleFullscreenChange); - if (this.eventSource) { - this.eventSource.close(); - this.eventSource = null; - } - if (this.retryTimer) { - clearTimeout(this.retryTimer); - this.retryTimer = null; - } - this.retryAttempts = 0; - }, - methods: { - connectSSE() { - if (this.eventSource) { - this.eventSource.close(); - this.eventSource = null; - } - - console.log(`正在连接日志流... (尝试次数: ${this.retryAttempts})`); - - const token = localStorage.getItem('token'); - - this.eventSource = new EventSourcePolyfill(logApi.liveUrl(), { - headers: { - 'Authorization': token ? `Bearer ${token}` : '' - }, - heartbeatTimeout: 300000, - withCredentials: true - }); - - this.eventSource.onopen = () => { - console.log('日志流连接成功!'); - this.retryAttempts = 0; - - if (!this.lastEventId) { - this.fetchLogHistory(); - } - }; - - this.eventSource.onmessage = (event) => { - try { - if (event.lastEventId) { - this.lastEventId = event.lastEventId; - } - - const payload = JSON.parse(event.data); - this.processNewLogs([payload]); - } catch (e) { - console.error('解析日志失败:', e); - } - }; - - this.eventSource.onerror = (err) => { - - if (err.status === 401) { - console.error('鉴权失败 (401),可能是 Token 过期了。'); - - } else { - console.warn('日志流连接错误:', err); - } - - if (this.eventSource) { - this.eventSource.close(); - this.eventSource = null; - } - - if (this.retryAttempts >= this.maxRetryAttempts) { - console.error('❌ 已达到最大重试次数,停止重连。请刷新页面重试。'); - return; - } - - const delay = Math.min( - this.baseRetryDelay * Math.pow(2, this.retryAttempts), - 30000 - ); - - console.log(`⏳ ${delay}ms 后尝试第 ${this.retryAttempts + 1} 次重连...`); - - if (this.retryTimer) { - clearTimeout(this.retryTimer); - this.retryTimer = null; - } - - this.retryTimer = setTimeout(async () => { - this.retryAttempts++; - - if (!this.lastEventId) { - await this.fetchLogHistory(); - } - - this.connectSSE(); - }, delay); - }; - }, - - processNewLogs(newLogs) { - if (!newLogs || newLogs.length === 0) return; - - let hasUpdate = false; - - newLogs.forEach(log => { - - const exists = this.localLogCache.some(existing => - existing.time === log.time && - existing.data === log.data && - existing.level === log.level - ); - - if (!exists) { - this.localLogCache.push(log); - hasUpdate = true; - - if (this.isLevelSelected(log.level)) { - this.printLog(log.data); - } - } - }); - - if (hasUpdate) { - this.localLogCache.sort((a, b) => a.time - b.time); - - const maxSize = this.commonStore.log_cache_max_len || 200; - if (this.localLogCache.length > maxSize) { - this.localLogCache.splice(0, this.localLogCache.length - maxSize); - } - } - }, - - async fetchLogHistory() { - try { - const res = await logApi.history(); - if (res.data.data.logs && res.data.data.logs.length > 0) { - this.processNewLogs(res.data.data.logs); - } - } catch (err) { - console.error('Failed to fetch log history:', err); - } - }, - - getLevelColor(level) { - return this.levelColors[level] || 'grey'; - }, - - isLevelSelected(level) { - for (let i = 0; i < this.selectedLevels.length; ++i) { - let level_ = this.logLevels[this.selectedLevels[i]] - if (level_ === level) { - return true; - } - } - return false; - }, - - refreshDisplay() { - const termElement = document.getElementById('term'); - if (termElement) { - termElement.innerHTML = ''; - - if (this.localLogCache && this.localLogCache.length > 0) { - this.localLogCache.forEach(logItem => { - if (this.isLevelSelected(logItem.level)) { - this.printLog(logItem.data); - } - }); - } - } - }, - - toggleAutoScroll() { - this.autoScroll = !this.autoScroll; - }, - - toggleFullscreen() { - const container = document.getElementById('console-wrapper'); - if (!document.fullscreenElement) { - container.requestFullscreen().catch(err => { - console.error(`Error attempting to enable full-screen mode: ${err.message}`); - }); - } else { - document.exitFullscreen(); - } - }, - - handleFullscreenChange() { - this.isFullscreen = !!document.fullscreenElement; - }, - - appendLogContent(element, log) { - const levelMatch = log.match(/\[(DEBG|INFO|WARN|ERRO|CRIT|DEBUG|WARNING|ERROR|CRITICAL)\]/); - if (!levelMatch) { - element.innerText = `${log}`; - return; - } - - const levelStart = levelMatch.index; - const levelEnd = levelStart + levelMatch[0].length; - const prefix = log.slice(0, levelStart).trimEnd(); - const message = log.slice(levelEnd).trimStart(); - - const prefixSpan = document.createElement('span'); - prefixSpan.className = 'console-log-prefix'; - prefixSpan.innerText = prefix; - - const levelSpan = document.createElement('span'); - levelSpan.className = 'console-log-level'; - levelSpan.innerText = levelMatch[0]; - - const messageSpan = document.createElement('span'); - messageSpan.className = 'console-log-message'; - messageSpan.innerText = message; - - element.classList.add('console-log-line--structured'); - element.appendChild(prefixSpan); - element.appendChild(levelSpan); - element.appendChild(messageSpan); - }, - - printLog(log) { - let ele = document.getElementById('term') - if (!ele) { - return; - } - - let span = document.createElement('pre') - let style = this.logColorAnsiMap['default'] - for (let key in this.logColorAnsiMap) { - if (log.startsWith(key)) { - style = this.logColorAnsiMap[key] - log = log.replace(key, '').replace('\u001b[0m', '') - break - } - } - - span.style = style - span.classList.add('console-log-line', 'fade-in') - this.appendLogContent(span, log); - ele.appendChild(span) - if (this.autoScroll) { - ele.scrollTop = ele.scrollHeight - } - } - }, -} -</script> - -<style scoped> -.console-displayer-wrapper { - height: 100%; - display: flex; - flex-direction: column; -} - -#console-wrapper:fullscreen { - background-color: #1e1e1e; - padding: 20px; -} - -.filter-controls { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 8px; -} - -.console-term { - background-color: #1e1e1e; - border-radius: 8px; - height: 100%; - overflow-y: auto; - overflow-x: auto; - padding: 16px; -} - -.fullscreen-btn { - color: rgba(255, 255, 255, 0.7) !important; /* 提高在深色背景下的对比度 */ -} - -:deep(.console-log-line) { - display: block; - margin: 0 0 2px; - font-family: SFMono-Regular, Menlo, Monaco, Consolas, var(--astrbot-font-cjk-mono), monospace; - font-size: 12px; - white-space: pre-wrap; -} - -:deep(.console-log-line--structured) { - display: grid; - grid-template-columns: max-content max-content minmax(0, 1fr); - column-gap: 8px; - align-items: start; - white-space: normal; -} - -:deep(.console-log-prefix), -:deep(.console-log-level), -:deep(.console-log-message) { - min-width: 0; - white-space: pre-wrap; -} - -:deep(.console-log-level) { - font-variant-numeric: tabular-nums; -} - -:deep(.console-log-message) { - overflow-wrap: anywhere; -} - -@media (max-width: 768px) { - :deep(.console-log-line--structured) { - grid-template-columns: 1fr; - } - :deep(.console-log-prefix:empty), - :deep(.console-log-level:empty) { - display: none; - } -} - -:deep(.fade-in) { - animation: fadeIn 0.3s; -} - -@keyframes fadeIn { - from { - opacity: 0; - } - - to { - opacity: 1; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/DashboardTotpManageDialog.vue b/legacy-dashboard/src/components/shared/DashboardTotpManageDialog.vue deleted file mode 100644 index e5fb9ace..00000000 --- a/legacy-dashboard/src/components/shared/DashboardTotpManageDialog.vue +++ /dev/null @@ -1,101 +0,0 @@ -<template> - <v-dialog - :model-value="modelValue" - max-width="520" - @update:model-value="val => emit('update:modelValue', val)" - > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - {{ tm('system_group.system.dashboard.totp.configuration') }} - <v-spacer></v-spacer> - <v-btn icon variant="text" size="small" @click="emit('update:modelValue', false)"> - <v-icon>mdi-close</v-icon> - </v-btn> - </v-card-title> - <v-divider></v-divider> - <v-card-text class="pa-4"> - <div class="totp-dialog-subtitle mb-3"> - {{ tm('system_group.system.dashboard.totp.activeSubtitle') }} - </div> - <div class="text-center"> - <QrCodeViewer - v-if="totpProvisioningUri" - :value="totpProvisioningUri" - alt="TOTP QR Code" - :size="220" - /> - <div class="totp-current-secret-wrap mt-3"> - <code class="totp-secret">{{ totpSecret }}</code> - </div> - </div> - <div class="d-flex justify-center ga-3 mt-4"> - <v-btn - color="primary" - variant="tonal" - @click="emit('rotate')" - > - <v-icon class="mr-1" size="16">mdi-shield-key</v-icon> - {{ tm('system_group.system.dashboard.totp.rotate') }} - </v-btn> - <v-btn - color="secondary" - variant="tonal" - @click="emit('rotate-recovery')" - > - <v-icon class="mr-1" size="16">mdi-key-variant</v-icon> - {{ tm('system_group.system.dashboard.totp.rotateRecovery') }} - </v-btn> - </div> - </v-card-text> - </v-card> - </v-dialog> -</template> - -<script setup> -import { computed } from 'vue' -import QrCodeViewer from './QrCodeViewer.vue' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - }, - configRoot: { - type: Object, - default: null - } -}) - -const emit = defineEmits(['update:modelValue', 'rotate', 'rotate-recovery']) -const { tm } = useModuleI18n('features/config-metadata') - -const totpSecret = computed(() => props.configRoot?.dashboard?.totp?.secret || '') - -const totpProvisioningUri = computed(() => { - if (!totpSecret.value) return '' - const label = encodeURIComponent(props.configRoot?.dashboard?.username || 'AstrBot') - const issuer = encodeURIComponent('AstrBot') - return `otpauth://totp/${label}?secret=${encodeURIComponent(totpSecret.value)}&issuer=${issuer}` -}) -</script> - -<style scoped> -.totp-dialog-subtitle { - font-size: 0.9rem; - color: rgba(var(--v-theme-on-surface), 0.68); -} - -.totp-current-secret-wrap { - background: rgba(var(--v-theme-on-surface), 0.04); - border: 1px solid rgba(var(--v-theme-on-surface), 0.12); - border-radius: 8px; - padding: 10px; -} - -.totp-secret { - word-break: break-all; - font-size: 0.8rem; - font-weight: 500; -} -</style> diff --git a/legacy-dashboard/src/components/shared/DashboardTotpManager.vue b/legacy-dashboard/src/components/shared/DashboardTotpManager.vue deleted file mode 100644 index d7e1c667..00000000 --- a/legacy-dashboard/src/components/shared/DashboardTotpManager.vue +++ /dev/null @@ -1,187 +0,0 @@ -<template> - <div class="totp-manager"> - <v-switch - :model-value="modelValue" - @update:model-value="onTotpToggle" - color="primary" - inset - density="compact" - hide-details - ></v-switch> - <div v-if="modelValue" class="totp-manager-actions"> - <v-chip - size="small" - :color="isTotpInitialSetup ? 'warning' : 'success'" - variant="tonal" - > - <v-icon start size="14"> - {{ isTotpInitialSetup ? 'mdi-alert-circle-outline' : 'mdi-check-circle-outline' }} - </v-icon> - {{ isTotpInitialSetup - ? tm('system_group.system.dashboard.totp.statusPending') - : tm('system_group.system.dashboard.totp.statusEnabled') }} - </v-chip> - <v-btn - color="primary" - variant="tonal" - size="small" - @click="openTotpDialog" - > - {{ tm('system_group.system.dashboard.totp.manage') }} - </v-btn> - </div> - </div> - <div v-if="modelValue && isTotpInitialSetup" class="totp-setup-hint"> - {{ tm('system_group.system.dashboard.totp.setupRequiredHint') }} - </div> - - <DashboardTotpSetupDialog - v-model="setupDialogVisible" - :config-root="configRoot" - :mode="setupDialogMode" - @setup-complete="onSetupComplete" - /> - <DashboardTotpRecoveryDialog - v-model="recoveryDialogVisible" - :recovery-code="pendingRecoveryCode" - @close="recoveryDialogVisible = false" - /> - <DashboardTotpManageDialog - v-model="manageDialogVisible" - :config-root="configRoot" - @rotate="onStartRotate" - @rotate-recovery="onStartRotateRecovery" - /> -</template> - -<script setup> -import { computed, ref } from 'vue' -import DashboardTotpSetupDialog from './DashboardTotpSetupDialog.vue' -import DashboardTotpRecoveryDialog from './DashboardTotpRecoveryDialog.vue' -import DashboardTotpManageDialog from './DashboardTotpManageDialog.vue' -import { authApi } from '@/api/v1' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - }, - configRoot: { - type: Object, - default: null - } -}) - -const emit = defineEmits(['update:modelValue']) -const { tm } = useModuleI18n('features/config-metadata') - -const setupDialogVisible = ref(false) -const recoveryDialogVisible = ref(false) -const manageDialogVisible = ref(false) -const setupDialogMode = ref('setup') -const pendingRecoveryCode = ref('') - -const totpSecret = computed(() => props.configRoot?.dashboard?.totp?.secret || '') -const totpRecoveryCodeHash = computed( - () => props.configRoot?.dashboard?.totp?.recovery_code_hash || '' -) -const isTotpInitialSetup = computed( - () => - props.modelValue === true - && (!totpSecret.value || !totpRecoveryCodeHash.value) -) - -function emitUpdate(val) { - emit('update:modelValue', val) -} - -function clearTotpConfig() { - if (props.configRoot?.dashboard?.totp) { - props.configRoot.dashboard.totp.enable = false - } -} - -function writeTotpSecretToConfig(secret, recoveryCodeHash = '') { - if (!props.configRoot?.dashboard) return - if (!props.configRoot.dashboard.totp) { - props.configRoot.dashboard.totp = {} - } - props.configRoot.dashboard.totp.enable = true - props.configRoot.dashboard.totp.secret = secret - props.configRoot.dashboard.totp.recovery_code_hash = recoveryCodeHash -} - -function onTotpToggle(val) { - if (!val) { - clearTotpConfig() - emitUpdate(val) - return - } - if (!totpSecret.value || !totpRecoveryCodeHash.value) { - setupDialogMode.value = 'setup' - setupDialogVisible.value = true - } - emitUpdate(true) -} - -function openTotpDialog() { - if (isTotpInitialSetup.value) { - setupDialogMode.value = 'setup' - setupDialogVisible.value = true - return - } - manageDialogVisible.value = true -} - -function onSetupComplete({ secret, recoveryCode, recoveryCodeHash }) { - writeTotpSecretToConfig(secret, recoveryCodeHash) - pendingRecoveryCode.value = recoveryCode - recoveryDialogVisible.value = true -} - -function onStartRotate() { - manageDialogVisible.value = false - setupDialogMode.value = 'rotate' - setupDialogVisible.value = true -} - -async function onStartRotateRecovery() { - manageDialogVisible.value = false - if (!totpSecret.value) return - try { - const res = await authApi.recoverTotp() - if (res.data.status !== 'ok') return - const { recovery_code: recoveryCode, recovery_code_hash: recoveryCodeHash } = res.data.data || {} - if (!recoveryCode || !recoveryCodeHash) return - if (!props.configRoot?.dashboard?.totp) return - props.configRoot.dashboard.totp.recovery_code_hash = recoveryCodeHash - pendingRecoveryCode.value = recoveryCode - recoveryDialogVisible.value = true - } catch { - // silently fail - } -} -</script> - -<style scoped> -.totp-manager { - display: flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -.totp-manager-actions { - display: inline-flex; - align-items: center; - gap: 8px; - flex-wrap: wrap; -} - -.totp-setup-hint { - margin-top: 6px; - font-size: 0.8rem; - color: rgba(var(--v-theme-warning), 0.95); -} -</style> diff --git a/legacy-dashboard/src/components/shared/DashboardTotpRecoveryDialog.vue b/legacy-dashboard/src/components/shared/DashboardTotpRecoveryDialog.vue deleted file mode 100644 index 3182b84e..00000000 --- a/legacy-dashboard/src/components/shared/DashboardTotpRecoveryDialog.vue +++ /dev/null @@ -1,101 +0,0 @@ -<template> - <v-dialog - :model-value="modelValue" - max-width="520" - persistent - @update:model-value="val => emit('update:modelValue', val)" - > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - {{ tm('system_group.system.dashboard.totp.recoveryTitle') }} - <v-spacer></v-spacer> - </v-card-title> - <v-divider></v-divider> - <v-card-text class="pa-4"> - <div class="totp-dialog-subtitle mb-3"> - {{ tm('system_group.system.dashboard.totp.recoverySubtitle') }} - </div> - <v-alert - type="warning" - variant="tonal" - density="compact" - class="mb-3" - :text="tm('system_group.system.dashboard.totp.recoveryWarning')" - hide-details - ></v-alert> - <div class="totp-recovery-card"> - <code class="totp-recovery-text">{{ recoveryCode }}</code> - </div> - <v-checkbox - v-model="acknowledged" - :label="tm('system_group.system.dashboard.totp.recoveryAcknowledge')" - color="primary" - density="comfortable" - hide-details - class="mt-2" - ></v-checkbox> - <div class="d-flex justify-end mt-4"> - <v-btn - color="primary" - variant="tonal" - :disabled="!acknowledged" - @click="onClose" - > - {{ tm('system_group.system.dashboard.totp.recoveryClose') }} - </v-btn> - </div> - </v-card-text> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref } from 'vue' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - }, - recoveryCode: { - type: String, - default: '' - } -}) - -const emit = defineEmits(['update:modelValue', 'close']) -const { tm } = useModuleI18n('features/config-metadata') - -const acknowledged = ref(false) - -function onClose() { - acknowledged.value = false - emit('close') - emit('update:modelValue', false) -} -</script> - -<style scoped> -.totp-dialog-subtitle { - font-size: 0.9rem; - color: rgba(var(--v-theme-on-surface), 0.68); -} - -.totp-recovery-card { - background: rgba(var(--v-theme-on-surface), 0.04); - border: 1px solid rgba(var(--v-theme-on-surface), 0.12); - border-radius: 8px; - padding: 12px; - text-align: center; - margin: 8px 0; -} - -.totp-recovery-text { - font-size: 1rem; - font-weight: 600; - letter-spacing: 1.3px; - word-break: keep-all; - user-select: all; -} -</style> diff --git a/legacy-dashboard/src/components/shared/DashboardTotpSetupDialog.vue b/legacy-dashboard/src/components/shared/DashboardTotpSetupDialog.vue deleted file mode 100644 index 65d6413f..00000000 --- a/legacy-dashboard/src/components/shared/DashboardTotpSetupDialog.vue +++ /dev/null @@ -1,309 +0,0 @@ -<template> - <v-dialog - :model-value="modelValue" - max-width="520" - @update:model-value="onVisibilityChange" - @click:outside="onCancel" - > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - {{ cardTitle }} - <v-spacer></v-spacer> - <v-btn icon variant="text" size="small" @click="onCancel"> - <v-icon>mdi-close</v-icon> - </v-btn> - </v-card-title> - <v-divider></v-divider> - <v-card-text class="pa-4"> - <template v-if="step === 'verify'"> - <div class="totp-dialog-subtitle mb-3"> - 输入当前认证器应用中的验证码以验证身份。 - </div> - <div class="text-center"> - <v-text-field - v-model="verifyCode" - label="当前验证码" - variant="outlined" - density="compact" - class="totp-code-input" - maxlength="6" - :error-messages="verifyError" - :loading="verifyingIdentity" - hide-details="auto" - prepend-inner-icon="mdi-shield-key" - @keyup.enter="verifyIdentity" - ></v-text-field> - </div> - <div class="d-flex justify-end ga-2 mt-4"> - <v-btn - variant="text" - :disabled="verifyingIdentity" - @click="onCancel" - > - 取消 - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="verifyingIdentity" - :disabled="!verifyCode || verifyCode.length < 6" - @click="verifyIdentity" - > - 验证 - </v-btn> - </div> - </template> - <template v-else> - <div class="totp-dialog-subtitle mb-3"> - {{ dialogSubtitle }} - </div> - <div class="text-center"> - <v-alert - type="info" - variant="tonal" - density="compact" - class="mb-3 text-start" - :text="tm('system_group.system.dashboard.totp.rotateCodeHint')" - hide-details - ></v-alert> - <QrCodeViewer - v-if="totpProvisioningUri" - :value="totpProvisioningUri" - alt="TOTP QR Code" - :size="220" - /> - <div class="totp-new-secret-wrap mt-3"> - <code class="totp-secret">{{ newSecret }}</code> - </div> - <v-text-field - v-model="code" - :label="tm('system_group.system.dashboard.totp.rotateCode')" - variant="outlined" - density="compact" - class="totp-code-input mt-3" - maxlength="6" - :error-messages="codeError" - :loading="verifying" - hide-details="auto" - prepend-inner-icon="mdi-shield-key" - @keyup.enter="confirmSetup" - ></v-text-field> - </div> - <div class="d-flex justify-end ga-2 mt-4"> - <v-btn - variant="text" - :disabled="verifying" - @click="onCancel" - > - {{ tm('system_group.system.dashboard.totp.rotateCancel') }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="verifying" - :disabled="!code || code.length < 6" - @click="confirmSetup" - > - {{ confirmLabel }} - </v-btn> - </div> - </template> - </v-card-text> - </v-card> - </v-dialog> -</template> - -<script setup> -import { computed, ref, watch } from 'vue' -import { authApi } from '@/api/v1' -import QrCodeViewer from './QrCodeViewer.vue' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - }, - configRoot: { - type: Object, - default: null - }, - mode: { - type: String, - default: 'setup', - validator: (v) => ['setup', 'rotate'].includes(v) - } -}) - -const emit = defineEmits(['update:modelValue', 'setupComplete']) -const { tm } = useModuleI18n('features/config-metadata') - -const step = ref('verify') -const loading = ref(false) -const newSecret = ref('') -const code = ref('') -const codeError = ref('') -const verifying = ref(false) - -const verifyCode = ref('') -const verifyError = ref('') -const verifyingIdentity = ref(false) - -const cardTitle = computed(() => { - if (step.value === 'verify') { - return '验证当前 TOTP' - } - return props.mode === 'rotate' - ? tm('system_group.system.dashboard.totp.rotateTitle') - : tm('system_group.system.dashboard.totp.setupTitle') -}) - -const dialogSubtitle = computed(() => { - return props.mode === 'rotate' - ? tm('system_group.system.dashboard.totp.rotateSubtitle') - : tm('system_group.system.dashboard.totp.setupSubtitle') -}) - -const confirmLabel = computed(() => { - return props.mode === 'rotate' - ? tm('system_group.system.dashboard.totp.rotateConfirm') - : tm('system_group.system.dashboard.totp.setupConfirm') -}) - -const totpProvisioningUri = computed(() => { - if (!newSecret.value) return '' - const label = encodeURIComponent(props.configRoot?.dashboard?.username || 'AstrBot') - const issuer = encodeURIComponent('AstrBot') - return `otpauth://totp/${label}?secret=${encodeURIComponent(newSecret.value)}&issuer=${issuer}` -}) - -watch( - () => props.modelValue, - (visible) => { - if (visible) { - if (props.mode === 'rotate') { - step.value = 'verify' - return - } - step.value = 'setup' - void fetchNewSecret() - return - } - resetState() - } -) - -function resetState() { - step.value = 'verify' - newSecret.value = '' - code.value = '' - codeError.value = '' - verifyCode.value = '' - verifyError.value = '' - verifyingIdentity.value = false -} - -function onVisibilityChange(val) { - if (!val) { - resetState() - } - emit('update:modelValue', val) -} - -function onCancel() { - resetState() - emit('update:modelValue', false) -} - -async function fetchNewSecret() { - if (loading.value || newSecret.value) { - return - } - loading.value = true - try { - const res = await authApi.setupTotp() - if (res.data.status !== 'ok') { - return - } - newSecret.value = res.data.data?.secret || '' - code.value = '' - codeError.value = '' - } finally { - loading.value = false - } -} - -async function verifyIdentity() { - if (!verifyCode.value) return - verifyingIdentity.value = true - verifyError.value = '' - try { - const res = await authApi.setupTotp({ code: verifyCode.value }) - if (res.data.status !== 'ok') { - verifyError.value = res.data.message || '验证失败' - return - } - newSecret.value = res.data.data?.secret || '' - step.value = 'setup' - } catch { - verifyError.value = '验证失败' - } finally { - verifyingIdentity.value = false - } -} - -async function confirmSetup() { - if (!code.value || code.value.length < 6) return - verifying.value = true - codeError.value = '' - try { - const res = await authApi.setupTotp({ - secret: newSecret.value, - code: code.value, - }) - if (res.data.status !== 'ok') { - codeError.value = res.data.message || tm('system_group.system.dashboard.totp.rotateError') - return - } - const recoveryCode = String(res.data.data?.recovery_code || '') - const recoveryCodeHash = String(res.data.data?.recovery_code_hash || '') - const secret = newSecret.value - resetState() - emit('setupComplete', { - secret, - recoveryCode, - recoveryCodeHash, - }) - emit('update:modelValue', false) - } catch { - codeError.value = tm('system_group.system.dashboard.totp.rotateError') - } finally { - verifying.value = false - } -} -</script> - -<style scoped> -.totp-dialog-subtitle { - font-size: 0.9rem; - color: rgba(var(--v-theme-on-surface), 0.68); -} - -.totp-new-secret-wrap { - background: rgba(var(--v-theme-on-surface), 0.04); - border: 1px solid rgba(var(--v-theme-on-surface), 0.12); - border-radius: 8px; - padding: 10px; -} - -.totp-secret { - word-break: break-all; - font-size: 0.8rem; - font-weight: 500; -} - -.totp-code-input { - max-width: 240px; - margin: 0 auto; -} -</style> diff --git a/legacy-dashboard/src/components/shared/DashboardTwoFactorDialog.vue b/legacy-dashboard/src/components/shared/DashboardTwoFactorDialog.vue deleted file mode 100644 index f05d5560..00000000 --- a/legacy-dashboard/src/components/shared/DashboardTwoFactorDialog.vue +++ /dev/null @@ -1,130 +0,0 @@ -<template> - <v-dialog - :model-value="modelValue" - max-width="520" - @update:model-value="onVisibilityChange" - @click:outside="onCancel" - > - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - {{ tm('system_group.system.dashboard.totp.configSaveTitle') }} - <v-spacer></v-spacer> - <v-btn icon variant="text" size="small" @click="onCancel"> - <v-icon>mdi-close</v-icon> - </v-btn> - </v-card-title> - <v-divider></v-divider> - <v-card-text class="pa-4"> - <div class="totp-dialog-subtitle mb-3"> - {{ tm('system_group.system.dashboard.totp.configSaveSubtitle') }} - </div> - <div v-if="rotationHint" class="totp-dialog-rotation-hint mb-3"> - {{ rotationHint }} - </div> - <v-text-field - v-model="code" - :label="tm('system_group.system.dashboard.totp.configSaveCode')" - variant="outlined" - density="compact" - class="totp-code-input" - maxlength="6" - :error-messages="errorMessage" - :loading="saving" - hide-details="auto" - prepend-inner-icon="mdi-shield-key" - @keyup.enter="confirm" - ></v-text-field> - <div class="d-flex justify-end ga-2 mt-4"> - <v-btn - variant="text" - :disabled="saving" - @click="onCancel" - > - {{ tm('system_group.system.dashboard.totp.configSaveCancel') }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - :loading="saving" - :disabled="!code || code.length < 6" - @click="confirm" - > - {{ tm('system_group.system.dashboard.totp.configSaveConfirm') }} - </v-btn> - </div> - </v-card-text> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref } from 'vue' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Boolean, - default: false - }, - errorMessage: { - type: String, - default: '' - }, - saving: { - type: Boolean, - default: false - }, - rotationHint: { - type: String, - default: '' - } -}) - -const emit = defineEmits(['update:modelValue', 'confirm', 'cancel']) -const { tm } = useModuleI18n('features/config-metadata') - -const code = ref('') - -function resetState() { - code.value = '' -} - -function onVisibilityChange(val) { - if (!val) { - resetState() - } - emit('update:modelValue', val) -} - -function onCancel() { - resetState() - emit('cancel') - emit('update:modelValue', false) -} - -function confirm() { - if (!code.value) return - emit('confirm', code.value) -} -</script> - -<style scoped> -.totp-dialog-subtitle { - font-size: 0.9rem; - color: rgba(var(--v-theme-on-surface), 0.68); -} - -.totp-dialog-rotation-hint { - font-size: 0.82rem; - color: rgba(var(--v-theme-info, 33, 150, 243), 0.78); - background: rgba(var(--v-theme-info, 33, 150, 243), 0.08); - padding: 8px 12px; - border-radius: 6px; - line-height: 1.4; -} - -.totp-code-input { - max-width: 240px; - margin: 0 auto; -} -</style> diff --git a/legacy-dashboard/src/components/shared/ExtensionCard.vue b/legacy-dashboard/src/components/shared/ExtensionCard.vue deleted file mode 100644 index 52d9e57f..00000000 --- a/legacy-dashboard/src/components/shared/ExtensionCard.vue +++ /dev/null @@ -1,632 +0,0 @@ -<script setup lang="ts"> -import { ref, computed, watch, useAttrs } from "vue"; -import { useCustomizerStore } from "@/stores/customizer"; -import { useModuleI18n } from "@/i18n/composables"; -import UninstallConfirmDialog from "./UninstallConfirmDialog.vue"; -import PluginPlatformChip from "./PluginPlatformChip.vue"; -import StyledMenu from "./StyledMenu.vue"; -import defaultPluginIcon from "@/assets/images/plugin_icon.png"; -import { usePluginI18n } from "@/utils/pluginI18n"; - -const props = defineProps({ - extension: { - type: Object, - required: true, - }, - marketMode: { - type: Boolean, - default: false, - }, - highlight: { - type: Boolean, - default: false, - }, - isPinned: { - type: Boolean, - default: false, - }, -}); - -// 定义要发送到父组件的事件 -const emit = defineEmits([ - "configure", - "update", - "reload", - "uninstall", - "toggle-activation", - "view-handlers", - "view-readme", - "view-changelog", - "toggle-pin", - "open-webui", - "change-source", -]); - -const attrs = useAttrs(); - -// 国际化 -const { tm } = useModuleI18n("features/extension"); -const { pluginName, pluginDesc } = usePluginI18n(); - -const hasPages = computed(() => { - return ( - Array.isArray(props.extension?.pages) && props.extension.pages.length > 0 - ); -}); - -const updateDisabledReason = computed(() => { - return ( - props.extension?.update_disabled_reason || tm("messages.updateDisabled") - ); -}); - -const hasKnownInstallSource = computed(() => { - const source = props.extension?.install_source; - const installMethod = String(source?.install_method || "") - .trim() - .toLowerCase(); - return Boolean( - source && - source.implicit !== true && - ["market", "github"].includes(installMethod), - ); -}); - -const hasUsableRepo = computed(() => { - const source = props.extension?.install_source; - return Boolean(String(props.extension?.repo || source?.repo || "").trim()); -}); - -const canUpdateExtension = computed(() => { - return ( - props.marketMode || - (!props.extension?.reserved && - (hasKnownInstallSource.value || hasUsableRepo.value)) - ); -}); - -const canChangePluginSource = computed(() => { - return hasUsableRepo.value; -}); - -const showUninstallDialog = ref(false); - -const supportPlatforms = computed(() => { - const platforms = props.extension?.support_platforms; - if (!Array.isArray(platforms)) { - return []; - } - return platforms.filter((item) => typeof item === "string"); -}); - -const astrbotVersionRequirement = computed(() => { - const versionSpec = props.extension?.astrbot_version; - return typeof versionSpec === "string" && versionSpec.trim().length - ? versionSpec.trim() - : ""; -}); - -const logoLoadFailed = ref(false); - -const logoSrc = computed(() => { - const logo = props.extension?.logo; - if (logoLoadFailed.value) { - return defaultPluginIcon; - } - return typeof logo === "string" && logo.trim().length - ? logo - : defaultPluginIcon; -}); - -const localizedName = computed(() => pluginName(props.extension)); - -const localizedDesc = computed(() => pluginDesc(props.extension)); - -watch( - () => props.extension?.logo, - () => { - logoLoadFailed.value = false; - }, -); - -// 操作函数 -const configure = () => { - emit("configure", props.extension); -}; - -const updateExtension = () => { - if (!canUpdateExtension.value) return; - emit("update", props.extension); -}; - -const reloadExtension = () => { - emit("reload", props.extension); -}; - -const changePluginSource = () => { - if (!canChangePluginSource.value) return; - emit("change-source", props.extension); -}; - -const uninstallExtension = async () => { - showUninstallDialog.value = true; -}; - -const handleUninstallConfirm = (options: { - deleteConfig: boolean; - deleteData: boolean; -}) => { - emit("uninstall", props.extension, options); -}; - -const toggleActivation = () => { - emit("toggle-activation", props.extension); -}; - -const viewHandlers = () => { - emit("view-handlers", props.extension); -}; - -const viewReadme = () => { - emit("view-readme", props.extension); -}; - -const viewChangelog = () => { - emit("view-changelog", props.extension); -}; - -const togglePin = () => { - emit("toggle-pin", props.extension); -}; - -const openWebui = () => { - emit("open-webui", props.extension); -}; -</script> - -<template> - <v-card - v-bind="attrs" - class="extension-card mx-auto d-flex flex-column h-100" - elevation="0" - height="100%" - :ripple="false" - variant="outlined" - :style="{ - position: 'relative', - backgroundColor: - useCustomizerStore().uiTheme === 'PurpleTheme' - ? marketMode - ? '#f8f0dd' - : '#ffffff' - : marketMode - ? '#3a3425' - : '#282833', - color: - useCustomizerStore().uiTheme === 'PurpleTheme' - ? '#000000dd' - : '#ffffffdd', - }" - > - <v-card-text class="extension-card-text"> - <div class="extension-content-row"> - <div class="extension-image-container"> - <img - :src="logoSrc" - :alt="extension.name" - class="extension-logo" - @error="logoLoadFailed = true" - /> - </div> - - <div class="extension-meta-group"> - <div class="extension-title-row"> - <p - class="text-h3 font-weight-black extension-title" - :class="{ 'text-h4': $vuetify.display.xs }" - > - <v-tooltip - location="top" - :text=" - localizedName?.length && localizedName !== extension.name - ? `${localizedName} (${extension.name})` - : extension.name - " - > - <template v-slot:activator="{ props: titleTooltipProps }"> - <span - v-bind="titleTooltipProps" - class="extension-title__text" - >{{ localizedName }}</span - > - </template> - </v-tooltip> - <span v-if="extension.version" class="extension-version"> - {{ extension.version }} - </span> - <v-chip - v-if="extension.reserved" - color="primary" - size="x-small" - class="extension-system-chip" - > - {{ tm("status.system") }} - </v-chip> - <v-tooltip - location="top" - v-if="extension?.has_update && !marketMode" - > - <template v-slot:activator="{ props: tooltipProps }"> - <v-icon - v-bind="tooltipProps" - color="warning" - class="ml-2" - icon="mdi-update" - size="small" - style="cursor: pointer" - @click.stop="updateExtension" - ></v-icon> - </template> - <span - >{{ tm("card.status.hasUpdate") }}: - {{ extension.online_version }}</span - > - </v-tooltip> - </p> - - <template v-if="!marketMode"> - <v-tooltip location="left"> - <template v-slot:activator="{ props: tooltipProps }"> - <div class="extension-switch-wrap" @click.stop> - <div - v-bind="tooltipProps" - style="display: inline-flex; align-items: center" - > - <v-switch - :model-value="extension.activated" - color="success" - density="compact" - hide-details - inset - @update:model-value="toggleActivation" - ></v-switch> - </div> - </div> - </template> - <span>{{ - extension.activated - ? tm("buttons.disable") - : tm("buttons.enable") - }}</span> - </v-tooltip> - </template> - </div> - - <div class="extension-chip-group d-flex flex-wrap"> - <v-chip - v-if="extension?.has_update" - color="warning" - label - size="small" - style="cursor: pointer" - @click.stop="updateExtension" - > - <v-icon icon="mdi-arrow-up-bold" start></v-icon> - {{ extension.online_version }} - </v-chip> - <v-chip - v-for="tag in extension.tags" - :key="tag" - :color="tag === 'danger' ? 'error' : 'primary'" - label - size="small" - > - {{ tag === "danger" ? tm("tags.danger") : tag }} - </v-chip> - <PluginPlatformChip :platforms="supportPlatforms" /> - <v-chip - v-if="astrbotVersionRequirement" - color="secondary" - variant="outlined" - label - size="small" - > - AstrBot: {{ astrbotVersionRequirement }} - </v-chip> - </div> - - <div - class="extension-desc" - :class="{ 'text-caption': $vuetify.display.xs }" - > - {{ localizedDesc }} - </div> - </div> - </div> - </v-card-text> - - <v-card-actions class="extension-actions"> - <template v-if="!marketMode"> - <v-spacer></v-spacer> - <v-tooltip location="top"> - <template v-slot:activator="{ props: pinTooltipProps }"> - <v-btn - v-bind="pinTooltipProps" - :aria-label="isPinned ? tm('buttons.unpin') : tm('buttons.pin')" - :color="isPinned ? 'primary' : 'secondary'" - :icon="isPinned ? 'mdi-pin' : 'mdi-pin-outline'" - size="small" - variant="tonal" - class="extension-pin-btn" - @click.stop="togglePin" - ></v-btn> - </template> - <span>{{ isPinned ? tm("buttons.unpin") : tm("buttons.pin") }}</span> - </v-tooltip> - - <v-tooltip location="top" :text="tm('buttons.viewDocs')"> - <template v-slot:activator="{ props: actionProps }"> - <v-btn - v-bind="actionProps" - icon="mdi-book-open-page-variant" - size="small" - variant="tonal" - color="info" - @click.stop="viewReadme" - ></v-btn> - </template> - </v-tooltip> - - <v-tooltip - v-if="hasPages" - location="top" - :text="tm('buttons.openWebui')" - > - <template v-slot:activator="{ props: actionProps }"> - <v-btn - v-bind="actionProps" - icon="mdi-monitor-dashboard" - size="small" - variant="tonal" - color="primary" - :disabled="!extension.activated" - @click.stop="openWebui" - ></v-btn> - </template> - </v-tooltip> - - <v-tooltip location="top" :text="tm('card.actions.pluginConfig')"> - <template v-slot:activator="{ props: actionProps }"> - <v-btn - v-bind="actionProps" - icon="mdi-cog" - size="small" - variant="tonal" - color="primary" - @click.stop="configure" - ></v-btn> - </template> - </v-tooltip> - - <v-tooltip location="top" :text="tm('card.actions.reloadPlugin')"> - <template v-slot:activator="{ props: actionProps }"> - <v-btn - v-bind="actionProps" - icon="mdi-refresh" - size="small" - variant="tonal" - color="primary" - @click.stop="reloadExtension" - ></v-btn> - </template> - </v-tooltip> - - <StyledMenu location="top end" offset="8"> - <template #activator="{ props: menuProps }"> - <v-btn - v-bind="menuProps" - icon="mdi-dots-horizontal" - size="small" - variant="tonal" - color="secondary" - @click.stop - ></v-btn> - </template> - - <v-list-item - class="styled-menu-item" - prepend-icon="mdi-information" - @click.stop="viewHandlers" - > - <v-list-item-title>{{ tm("buttons.viewInfo") }}</v-list-item-title> - </v-list-item> - - <v-tooltip - location="left" - :disabled="canUpdateExtension" - :text="updateDisabledReason" - > - <template v-slot:activator="{ props: tooltipProps }"> - <div v-bind="tooltipProps"> - <v-list-item - class="styled-menu-item" - prepend-icon="mdi-update" - :disabled="!canUpdateExtension" - @click.stop="updateExtension" - > - <v-list-item-title>{{ - extension.has_update - ? tm("card.actions.updateTo") + - " " + - extension.online_version - : tm("card.actions.reinstall") - }}</v-list-item-title> - </v-list-item> - </div> - </template> - </v-tooltip> - - <v-tooltip - location="left" - :disabled="canChangePluginSource" - :text="tm('messages.changeSourceDisabled')" - > - <template v-slot:activator="{ props: tooltipProps }"> - <div v-bind="tooltipProps"> - <v-list-item - class="styled-menu-item" - prepend-icon="mdi-source-branch" - :disabled="!canChangePluginSource" - @click.stop="changePluginSource" - > - <v-list-item-title>{{ - tm("card.actions.changeSource") - }}</v-list-item-title> - </v-list-item> - </div> - </template> - </v-tooltip> - - <v-list-item - class="styled-menu-item" - prepend-icon="mdi-delete" - @click.stop="uninstallExtension" - > - <v-list-item-title class="text-error">{{ - tm("card.actions.uninstallPlugin") - }}</v-list-item-title> - </v-list-item> - </StyledMenu> - </template> - <template v-else> - <v-btn color="primary" size="small" @click.stop="viewReadme"> - {{ tm("buttons.viewDocs") }} - </v-btn> - </template> - </v-card-actions> - </v-card> - - <!-- 卸载确认对话框 --> - <UninstallConfirmDialog - v-model="showUninstallDialog" - @confirm="handleUninstallConfirm" - /> -</template> - -<style scoped> -.extension-card-text { - padding: 12px 14px 8px; - width: 100%; -} - -.extension-card { - cursor: pointer; -} - -.extension-image-container { - display: flex; - align-items: flex-start; - flex-shrink: 0; -} - -.extension-logo { - width: 64px; - height: 64px; - border-radius: 10px; - object-fit: cover; -} - -.extension-content-row { - display: flex; - gap: 14px; - align-items: flex-start; -} - -.extension-meta-group { - flex: 1; - min-width: 0; -} - -.extension-chip-group { - gap: 6px; -} - -.extension-desc { - margin-top: 6px; - font-size: 90%; - display: -webkit-box; - line-clamp: 2; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.extension-title { - display: flex; - align-items: center; - min-width: 0; - flex: 1; - margin: 0; -} - -.extension-title-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - -.extension-title__text { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.extension-version { - color: rgba(var(--v-theme-on-surface), 0.48); - flex-shrink: 0; - font-size: 0.875rem; - font-weight: 500; - margin-left: 10px; - white-space: nowrap; -} - -.extension-system-chip { - flex-shrink: 0; - margin-left: 8px; -} - -.extension-switch-wrap { - display: flex; - align-items: center; - flex-shrink: 0; -} - -.extension-pin-btn { - flex-shrink: 0; -} - -.extension-switch-wrap :deep(.v-switch) { - margin: 0; -} - -@media (max-width: 600px) { - .extension-content-row { - gap: 10px; - } - - .extension-logo { - width: 52px; - height: 52px; - } -} - -.extension-actions { - margin-top: auto; - gap: 6px; - justify-content: flex-end; - min-height: 42px; - padding: 0 12px 10px; -} -</style> diff --git a/legacy-dashboard/src/components/shared/FileConfigItem.vue b/legacy-dashboard/src/components/shared/FileConfigItem.vue deleted file mode 100644 index ee688a65..00000000 --- a/legacy-dashboard/src/components/shared/FileConfigItem.vue +++ /dev/null @@ -1,394 +0,0 @@ -<template> - <div class="file-config-item"> - <div class="d-flex align-center gap-2"> - <v-btn size="small" color="primary" variant="tonal" @click="dialog = true"> - {{ tm('fileUpload.button') }} - </v-btn> - <span class="text-caption text-medium-emphasis ml-2"> - {{ fileCountText }} - </span> - </div> - - <v-dialog v-model="dialog" max-width="700"> - <v-card class="file-dialog-card" variant="flat"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center"> - <span>{{ tm('fileUpload.dialogTitle') }}</span> - <v-spacer /> - <v-btn icon="mdi-close" variant="text" @click="dialog = false" /> - </v-card-title> - - <v-card-text class="file-dialog-body"> - <div v-if="mergedFileItems.length === 0" class="empty-text"> - {{ tm('fileUpload.empty') }} - </div> - - <v-list density="compact" lines="one"> - <v-list-item v-for="item in mergedFileItems" :key="item.path"> - <template #prepend> - <v-icon size="18">mdi-file</v-icon> - </template> - <v-list-item-title class="file-name"> - {{ getDisplayName(item.path) }} - </v-list-item-title> - <template #append> - <div class="d-flex align-center gap-1"> - <v-chip v-if="item.status !== 'ok'" size="x-small" :color="getStatusColor(item.status)" - variant="tonal"> - {{ getStatusText(item.status) }} - </v-chip> - <v-btn v-if="item.status === 'unconfigured'" icon="mdi-plus" size="x-small" variant="text" - @click="addToConfig(item.path)" /> - <v-btn icon="mdi-delete" size="x-small" variant="text" - @click="item.status === 'unconfigured' ? deletePhysicalFile(item.path) : deleteFile(item.path)" /> - </div> - </template> - </v-list-item> - - <v-divider v-if="mergedFileItems.length > 0" class="my-2" /> - - <v-list-item class="upload-item" :class="{ dragover: isDragging }" @drop.prevent="handleDrop" - @dragover.prevent="isDragging = true" @dragleave="isDragging = false" @click="openFilePicker"> - <template #prepend> - <v-icon size="18" color="primary">mdi-plus</v-icon> - </template> - <v-list-item-title>{{ tm('fileUpload.dropzone') }}</v-list-item-title> - <v-list-item-subtitle v-if="allowedTypesText" class="upload-hint"> - {{ tm('fileUpload.allowedTypes', { types: allowedTypesText }) }} - </v-list-item-subtitle> - </v-list-item> - </v-list> - - <input ref="fileInput" type="file" multiple hidden :accept="acceptAttr" @change="handleFileSelect" /> - </v-card-text> - - <v-card-actions class="file-dialog-actions"> - <v-spacer /> - <v-btn color="primary" variant="tonal" @click="dialog = false"> - {{ tm('fileUpload.done') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - </div> -</template> - -<script setup> -import { computed, ref, watch } from 'vue' -import { pluginApi } from '@/api/v1' -import { useToast } from '@/utils/toast' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Array, - default: () => [] - }, - itemMeta: { - type: Object, - default: null - }, - pluginName: { - type: String, - default: '' - }, - configKey: { - type: String, - default: '' - } -}) - -const emit = defineEmits(['update:modelValue']) -const { tm } = useModuleI18n('features/config') -const toast = useToast() - -const dialog = ref(false) -const isDragging = ref(false) -const fileInput = ref(null) -const uploading = ref(false) -const loadingFiles = ref(false) -const MAX_FILE_BYTES = 500 * 1024 * 1024 -const MAX_FILE_MB = 500 -const directoryFiles = ref([]) - -const fileList = computed({ - get: () => (Array.isArray(props.modelValue) ? props.modelValue : []), - set: (val) => emit('update:modelValue', val) -}) - -const mergedFileItems = computed(() => { - const configured = new Set(fileList.value) - const existing = new Set(directoryFiles.value) - const items = [] - - for (const path of fileList.value) { - items.push({ - path, - status: existing.has(path) ? 'ok' : 'missing' - }) - } - - for (const path of directoryFiles.value) { - if (!configured.has(path)) { - items.push({ - path, - status: 'unconfigured' - }) - } - } - - return items -}) - -const acceptAttr = computed(() => { - const types = props.itemMeta?.file_types - if (!Array.isArray(types) || types.length === 0) { - return undefined - } - return types - .map((ext) => `.${String(ext).replace(/^\\./, '')}`) - .join(',') -}) - -const allowedTypesText = computed(() => { - const types = props.itemMeta?.file_types - if (!Array.isArray(types) || types.length === 0) { - return '' - } - return types.map((ext) => String(ext).replace(/^\\./, '')).join(', ') -}) - -const fileCountText = computed(() => { - return tm('fileUpload.fileCount', { count: fileList.value.length }) -}) - -const getStatusText = (status) => { - if (status === 'missing') { - return tm('fileUpload.statusMissing') - } - if (status === 'unconfigured') { - return tm('fileUpload.statusUnconfigured') - } - return '' -} - -const getStatusColor = (status) => { - if (status === 'missing') { - return 'error' - } - if (status === 'unconfigured') { - return 'warning' - } - return 'primary' -} - -const openFilePicker = () => { - fileInput.value?.click() -} - -const loadDirectoryFiles = async () => { - if (!props.pluginName || !props.configKey || loadingFiles.value) { - return - } - - loadingFiles.value = true - try { - const response = await pluginApi.listConfigFiles( - props.pluginName, - props.configKey - ) - if (response.data.status === 'ok') { - const files = response.data.data?.files || [] - directoryFiles.value = Array.from(new Set(files)) - } else { - toast.warning(response.data.message || tm('fileUpload.loadFailed')) - } - } catch (error) { - console.error('Load file list failed:', error) - toast.warning(tm('fileUpload.loadFailed')) - } finally { - loadingFiles.value = false - } -} - -const handleFileSelect = (event) => { - const target = event.target - if (target?.files && target.files.length > 0) { - uploadFiles(Array.from(target.files)) - } - if (target) { - target.value = '' - } -} - -const handleDrop = (event) => { - isDragging.value = false - if (event.dataTransfer?.files && event.dataTransfer.files.length > 0) { - uploadFiles(Array.from(event.dataTransfer.files)) - } -} - -const uploadFiles = async (files) => { - if (!props.pluginName || !props.configKey) { - toast.warning('Missing plugin config info') - return - } - if (uploading.value) { - return - } - - const oversized = files.filter((file) => file.size > MAX_FILE_BYTES) - if (oversized.length > 0) { - oversized.forEach((file) => { - toast.warning( - tm('fileUpload.fileTooLarge', { name: file.name, max: MAX_FILE_MB }) - ) - }) - } - const validFiles = files.filter((file) => file.size <= MAX_FILE_BYTES) - if (validFiles.length === 0) { - return - } - - uploading.value = true - try { - const formData = new FormData() - validFiles.forEach((file, index) => { - formData.append(`file${index}`, file) - }) - - const response = await pluginApi.uploadConfigFiles( - props.pluginName, - props.configKey, - formData - ) - - if (response.data.status === 'ok') { - const uploaded = response.data.data?.uploaded || [] - const errors = response.data.data?.errors || [] - - if (uploaded.length > 0) { - const merged = [...fileList.value] - for (const path of uploaded) { - if (!merged.includes(path)) { - merged.push(path) - } - } - fileList.value = merged - const updatedDirectory = new Set(directoryFiles.value) - uploaded.forEach((path) => updatedDirectory.add(path)) - directoryFiles.value = Array.from(updatedDirectory) - toast.success(tm('fileUpload.uploadSuccess', { count: uploaded.length })) - } - - if (errors.length > 0) { - toast.warning(errors.join('\\n')) - } - } else { - toast.error(response.data.message || tm('fileUpload.uploadFailed')) - } - } catch (error) { - console.error('File upload failed:', error) - toast.error(tm('fileUpload.uploadFailed')) - } finally { - uploading.value = false - } -} - -const addToConfig = (filePath) => { - if (!fileList.value.includes(filePath)) { - fileList.value = [...fileList.value, filePath] - toast.success(tm('fileUpload.addToConfig')) - } -} - -const deleteFile = (filePath) => { - fileList.value = fileList.value.filter((item) => item !== filePath) - directoryFiles.value = directoryFiles.value.filter((item) => item !== filePath) - - if (props.pluginName) { - pluginApi - .deleteConfigFile(props.pluginName, { path: filePath }) - .catch((error) => { - console.warn('Staged file delete failed:', error) - toast.warning(tm('fileUpload.deleteFailed')) - }) - } - - toast.success(tm('fileUpload.deleteSuccess')) -} - -const deletePhysicalFile = (filePath) => { - directoryFiles.value = directoryFiles.value.filter((item) => item !== filePath) - - if (props.pluginName) { - pluginApi - .deleteConfigFile(props.pluginName, { path: filePath }) - .catch((error) => { - console.warn('File delete failed:', error) - toast.warning(tm('fileUpload.deleteFailed')) - }) - } - - toast.success(tm('fileUpload.deleteSuccess')) -} - -const getDisplayName = (path) => { - if (!path) return '' - const parts = String(path).split('/') - return parts[parts.length - 1] || path -} - -watch( - () => dialog.value, - (value) => { - if (value) { - loadDirectoryFiles() - } - } -) -</script> - -<style scoped> -.file-config-item { - width: 100%; -} - -.file-dialog-card { - height: 70vh; - box-shadow: none; -} - -.file-dialog-body { - overflow-y: auto; - max-height: calc(70vh - 120px); -} - -.file-dialog-actions { - padding: 16px 24px 20px; -} - -.upload-hint { - font-size: 12px; - color: rgba(var(--v-theme-on-surface), 0.5); -} - -.empty-text { - font-size: 12px; - color: rgba(var(--v-theme-on-surface), 0.5); -} - -.file-name { - font-weight: 600; - word-break: break-word; -} - -.upload-item { - cursor: pointer; - transition: background 0.2s ease; -} - -.upload-item:hover, -.upload-item.dragover { - background: rgba(var(--v-theme-on-surface), 0.04); -} -</style> diff --git a/legacy-dashboard/src/components/shared/ItemCard.vue b/legacy-dashboard/src/components/shared/ItemCard.vue deleted file mode 100644 index a2ab8a72..00000000 --- a/legacy-dashboard/src/components/shared/ItemCard.vue +++ /dev/null @@ -1,173 +0,0 @@ -<template> - <v-card class="item-card hover-elevation" style="padding: 4px;" :variant="variant" elevation="0"> - <v-card-title class="d-flex justify-space-between align-center pb-1 pt-3"> - <span class="text-h2 text-truncate" :title="getItemTitle()">{{ getItemTitle() }}</span> - <v-tooltip location="top"> - <template v-slot:activator="{ props }"> - <v-switch - color="primary" - hide-details - density="compact" - :model-value="getItemEnabled()" - :loading="loading" - :disabled="loading || disableToggle" - v-bind="props" - @update:model-value="toggleEnabled" - ></v-switch> - </template> - <span>{{ getItemEnabled() ? t('core.common.itemCard.enabled') : t('core.common.itemCard.disabled') }}</span> - </v-tooltip> - </v-card-title> - - <v-card-text> - <slot name="item-details" :item="item"></slot> - </v-card-text> - - <v-card-actions style="margin: 8px;"> - <v-btn - variant="outlined" - color="error" - size="small" - rounded="xl" - :disabled="loading || disableDelete" - @click="$emit('delete', item)" - > - {{ t('core.common.itemCard.delete') }} - </v-btn> - <v-btn - v-if="showEditButton" - variant="tonal" - color="primary" - size="small" - rounded="xl" - :disabled="loading" - @click="$emit('edit', item)" - > - {{ t('core.common.itemCard.edit') }} - </v-btn> - <v-btn - v-if="showCopyButton" - variant="tonal" - color="secondary" - size="small" - rounded="xl" - :disabled="loading" - @click="$emit('copy', item)" - > - {{ t('core.common.itemCard.copy') }} - </v-btn> - <slot name="actions" :item="item"></slot> - <v-spacer></v-spacer> - </v-card-actions> - - <div class="d-flex justify-end align-center" style="position: absolute; bottom: 16px; right: 16px; opacity: 0.2;" v-if="bglogo"> - <v-img - :src="bglogo" - contain - width="120" - height="120" - ></v-img> - </div> - </v-card> -</template> - -<script> -import { useI18n } from '@/i18n/composables'; - -export default { - name: 'ItemCard', - setup() { - const { t } = useI18n(); - return { t }; - }, - props: { - item: { - type: Object, - required: true - }, - titleField: { - type: String, - default: 'id' - }, - enabledField: { - type: String, - default: 'enable' - }, - bglogo: { - type: String, - default: null - }, - loading: { - type: Boolean, - default: false - }, - showCopyButton: { - type: Boolean, - default: false - }, - showEditButton: { - type: Boolean, - default: true - }, - disableToggle: { - type: Boolean, - default: false - }, - disableDelete: { - type: Boolean, - default: false - }, - variant: { - type: String, - default: undefined - } - }, - emits: ['toggle-enabled', 'delete', 'edit', 'copy'], - methods: { - getItemTitle() { - return this.item[this.titleField]; - }, - getItemEnabled() { - return this.item[this.enabledField]; - }, - toggleEnabled() { - this.$emit('toggle-enabled', this.item); - } - } -} -</script> - -<style scoped> -.item-card { - background: rgb(var(--v-theme-surface)); - position: relative; - border-radius: 18px; - transition: background-color 0.16s ease, transform 0.3s ease; - overflow: hidden; - min-height: 220px; - height: 100%; - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.hover-elevation:hover { - background: rgba(var(--v-theme-on-surface), 0.04); - transform: translateY(-2px); -} - -.item-status-indicator { - position: absolute; - top: 8px; - left: 8px; - width: 8px; - height: 8px; - border-radius: 50%; - background-color: #ccc; - z-index: 10; -} - -.item-status-indicator.active { - background-color: #4caf50; -} -</style> diff --git a/legacy-dashboard/src/components/shared/ItemCardGrid.vue b/legacy-dashboard/src/components/shared/ItemCardGrid.vue deleted file mode 100644 index 5176c186..00000000 --- a/legacy-dashboard/src/components/shared/ItemCardGrid.vue +++ /dev/null @@ -1,143 +0,0 @@ -<template> - <div> - <v-row v-if="items.length === 0"> - <v-col cols="12" class="text-center pa-8"> - <v-icon size="64" color="grey-lighten-1">{{ emptyIcon }}</v-icon> - <p class="text-grey mt-4">{{ displayEmptyText }}</p> - </v-col> - </v-row> - - <v-row v-else> - <v-col v-for="(item, index) in items" :key="index" cols="12" md="6" lg="4" xl="3"> - <v-card class="item-card hover-elevation" style="padding: 4px;" elevation="0"> - <div class="item-status-indicator" :class="{'active': getItemEnabled(item)}"></div> - <v-card-title class="d-flex justify-space-between align-center pb-1 pt-3"> - <span class="text-h2 text-truncate" :title="getItemTitle(item)">{{ getItemTitle(item) }}</span> - <v-tooltip location="top"> - <template v-slot:activator="{ props }"> - <v-switch - color="primary" - hide-details - density="compact" - :model-value="getItemEnabled(item)" - v-bind="props" - @update:model-value="toggleEnabled(item)" - ></v-switch> - </template> - <span>{{ getItemEnabled(item) ? t('core.common.itemCard.enabled') : t('core.common.itemCard.disabled') }}</span> - </v-tooltip> - </v-card-title> - - <v-card-text> - <slot name="item-details" :item="item"></slot> - </v-card-text> - - <v-card-actions style="margin: 8px;"> - <v-btn - variant="outlined" - color="error" - rounded="xl" - @click="$emit('delete', item)" - > - {{ t('core.common.itemCard.delete') }} - </v-btn> - <v-btn - variant="tonal" - color="primary" - rounded="xl" - @click="$emit('edit', item)" - > - {{ t('core.common.itemCard.edit') }} - </v-btn> - <v-spacer></v-spacer> - </v-card-actions> - - <div class="d-flex justify-end align-center" style="position: absolute; bottom: 16px; right: 16px; opacity: 0.2;" v-if="bglogo"> - <v-img - :src="bglogo" - contain - width="120" - height="120" - class="rounded-circle" - ></v-img> - </div> - - </v-card> - </v-col> - </v-row> - </div> -</template> - -<script> -import { useI18n } from '@/i18n/composables'; - -export default { - name: 'ItemCardGrid', - setup() { - const { t } = useI18n(); - return { t }; - }, - props: { - items: { - type: Array, - required: true - }, - titleField: { - type: String, - default: 'id' - }, - enabledField: { - type: String, - default: 'enable' - }, - emptyIcon: { - type: String, - default: 'mdi-alert-circle-outline' - }, - emptyText: { - type: String, - default: null - }, - bglogo: { - type: String, - default: null - } - }, - emits: ['toggle-enabled', 'delete', 'edit'], - computed: { - displayEmptyText() { - return this.emptyText || this.t('core.common.itemCard.noData'); - } - }, - methods: { - getItemTitle(item) { - return item[this.titleField]; - }, - getItemEnabled(item) { - return item[this.enabledField]; - }, - toggleEnabled(item) { - this.$emit('toggle-enabled', item); - } - } -} -</script> - -<style> - -.item-card { - position: relative; - border-radius: 18px; - transition: all 0.3s ease; - overflow: hidden; - min-height: 220px; - margin-bottom: 16px; - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.hover-elevation:hover { - transform: translateY(-2px); -} -</style> diff --git a/legacy-dashboard/src/components/shared/KnowledgeBaseSelector.vue b/legacy-dashboard/src/components/shared/KnowledgeBaseSelector.vue deleted file mode 100644 index 2e649733..00000000 --- a/legacy-dashboard/src/components/shared/KnowledgeBaseSelector.vue +++ /dev/null @@ -1,234 +0,0 @@ -<template> - <div class="d-flex align-center justify-space-between" style="gap: 8px;"> - <div style="flex: 1; min-width: 0; overflow: hidden;"> - <span v-if="!modelValue || (Array.isArray(modelValue) && modelValue.length === 0)" - style="color: rgb(var(--v-theme-primaryText));"> - {{ tm('knowledgeBaseSelector.notSelected') }} - </span> - <div v-else class="d-flex flex-wrap gap-1"> - <v-chip - v-for="name in modelValue" - :key="name" - size="small" - color="primary" - variant="tonal" - closable - @click:close="removeKnowledgeBase(name)" - style="max-width: 100%;"> - <span class="text-truncate" style="max-width: 200px;">{{ name }}</span> - </v-chip> - </div> - </div> - <v-btn size="small" color="primary" variant="tonal" @click="openDialog" style="flex-shrink: 0;"> - {{ buttonText || tm('knowledgeBaseSelector.buttonText') }} - </v-btn> - </div> - - <!-- Knowledge Base Selection Dialog --> - <v-dialog v-model="dialog" max-width="600px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ tm('knowledgeBaseSelector.dialogTitle') }} - </v-card-title> - - <v-card-text class="pa-0" style="max-height: 400px; overflow-y: auto;"> - <v-progress-linear v-if="loading" indeterminate color="primary"></v-progress-linear> - - <!-- 知识库列表 --> - <v-list v-if="!loading" density="compact"> - <!-- 知识库选项 --> - <v-list-item - v-for="kb in knowledgeBaseList" - :key="kb.kb_id" - :value="kb.kb_name" - @click="selectKnowledgeBase(kb.kb_name)" - :active="isSelected(kb.kb_name)" - rounded="md" - class="ma-1"> - <template v-slot:prepend> - <span class="emoji-icon">{{ kb.emoji || '📚' }}</span> - </template> - <v-list-item-title>{{ kb.kb_name }}</v-list-item-title> - <v-list-item-subtitle> - {{ kb.description || tm('knowledgeBaseSelector.noDescription') }} - <span v-if="kb.doc_count !== undefined"> - {{ tm('knowledgeBaseSelector.documentCount', { count: kb.doc_count }) }}</span> - <span v-if="kb.chunk_count !== undefined"> - {{ tm('knowledgeBaseSelector.chunkCount', { count: kb.chunk_count }) }}</span> - </v-list-item-subtitle> - - <template v-slot:append> - <v-icon v-if="isSelected(kb.kb_name)" color="primary"> - mdi-checkbox-marked - </v-icon> - <v-icon v-else color="grey-lighten-1"> - mdi-checkbox-blank-outline - </v-icon> - </template> - </v-list-item> - - <!-- 当没有知识库时显示创建提示 --> - <div v-if="knowledgeBaseList.length === 0" class="text-center py-8"> - <v-icon size="64" color="grey-lighten-1">mdi-database-off</v-icon> - <p class="text-grey mt-4 mb-4">{{ tm('knowledgeBaseSelector.noKnowledgeBases') }}</p> - <v-btn color="primary" variant="tonal" @click="goToKnowledgeBasePage"> - {{ tm('knowledgeBaseSelector.createKnowledgeBase') }} - </v-btn> - </div> - </v-list> - </v-card-text> - - <v-card-actions class="pa-4"> - <div v-if="selectedKnowledgeBases.length > 0" class="text-caption text-grey"> - {{ tm('knowledgeBaseSelector.selectedCount', { count: selectedKnowledgeBases.length }) }} - </div> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelSelection">{{ tm('knowledgeBaseSelector.cancelSelection') }}</v-btn> - <v-btn - color="primary" - variant="tonal" - @click="confirmSelection"> - {{ tm('knowledgeBaseSelector.confirmSelection') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref, watch } from 'vue' -import { knowledgeApi } from '@/api/v1' -import { useRouter } from 'vue-router' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: Array, - default: () => [] - }, - buttonText: { - type: String, - default: '' - } -}) - -const emit = defineEmits(['update:modelValue']) -const router = useRouter() -const { tm } = useModuleI18n('core.shared') - -const dialog = ref(false) -const knowledgeBaseList = ref([]) -const loading = ref(false) -const selectedKnowledgeBases = ref([]) - -// 监听 modelValue 变化,同步到 selectedKnowledgeBases -watch(() => props.modelValue, (newValue) => { - selectedKnowledgeBases.value = Array.isArray(newValue) ? [...newValue] : [] -}, { immediate: true }) - -async function openDialog() { - // 初始化选中状态 - selectedKnowledgeBases.value = Array.isArray(props.modelValue) - ? [...props.modelValue] - : [] - - dialog.value = true - await loadKnowledgeBases() -} - -async function loadKnowledgeBases() { - loading.value = true - try { - const response = await knowledgeApi.list({ - page: 1, - page_size: 100 - }) - - if (response.data.status === 'ok') { - knowledgeBaseList.value = response.data.data.items || [] - } else { - console.error('加载知识库列表失败:', response.data.message) - knowledgeBaseList.value = [] - } - } catch (error) { - console.error('加载知识库列表失败:', error) - knowledgeBaseList.value = [] - } finally { - loading.value = false - } -} - -function isSelected(kbName) { - return selectedKnowledgeBases.value.includes(kbName) -} - -function selectKnowledgeBase(kbName) { - // 多选模式:切换选中状态 - const index = selectedKnowledgeBases.value.indexOf(kbName) - if (index > -1) { - selectedKnowledgeBases.value.splice(index, 1) - } else { - selectedKnowledgeBases.value.push(kbName) - } -} - -function removeKnowledgeBase(kbName) { - const index = selectedKnowledgeBases.value.indexOf(kbName) - if (index > -1) { - selectedKnowledgeBases.value.splice(index, 1) - } - - // 立即更新父组件 - emit('update:modelValue', [...selectedKnowledgeBases.value]) -} - -function confirmSelection() { - emit('update:modelValue', [...selectedKnowledgeBases.value]) - dialog.value = false -} - -function cancelSelection() { - // 恢复到原始值 - selectedKnowledgeBases.value = Array.isArray(props.modelValue) - ? [...props.modelValue] - : [] - dialog.value = false -} - -function goToKnowledgeBasePage() { - dialog.value = false - router.push('/knowledge-base') -} -</script> - -<style scoped> -.v-list-item { - transition: all 0.2s ease; -} - -.v-list-item:hover { - background-color: rgba(var(--v-theme-primary), 0.04); -} - -.v-list-item.v-list-item--active { - background-color: rgba(var(--v-theme-primary), 0.08); -} - -.emoji-icon { - font-size: 20px; - margin-right: 8px; - min-width: 28px; - display: flex; - align-items: center; - justify-content: center; -} - -.gap-1 { - gap: 4px; -} - -.text-truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - display: inline-block; -} -</style> diff --git a/legacy-dashboard/src/components/shared/LanguageSwitcher.vue b/legacy-dashboard/src/components/shared/LanguageSwitcher.vue deleted file mode 100644 index 33a9335e..00000000 --- a/legacy-dashboard/src/components/shared/LanguageSwitcher.vue +++ /dev/null @@ -1,105 +0,0 @@ -<template> - <StyledMenu offset="12" location="bottom center"> - <template v-slot:activator="{ props: activatorProps }"> - <v-btn - v-bind="activatorProps" - :variant="(props.variant === 'header' || props.variant === 'chatbox') ? 'flat' : 'text'" - :color="(props.variant === 'header' || props.variant === 'chatbox') ? 'var(--v-theme-surface)' : undefined" - :rounded="(props.variant === 'header' || props.variant === 'chatbox') ? 'sm' : undefined" - icon - size="small" - :class="['language-switcher', `language-switcher--${props.variant}`, (props.variant === 'header' || props.variant === 'chatbox') ? 'action-btn' : '']" - > - <v-icon - size="18" - :color="props.variant === 'default' ? 'rgb(var(--v-theme-primary))' : undefined" - > - mdi-translate - </v-icon> - <v-tooltip activator="parent" location="top"> - {{ t('core.common.language') }} - </v-tooltip> - </v-btn> - </template> - - <v-list-item - v-for="lang in languages" - :key="lang.code" - :value="lang.code" - @click="changeLanguage(lang.code)" - :class="{ 'styled-menu-item-active': currentLocale === lang.code }" - class="styled-menu-item" - rounded="md" - > - <template v-slot:prepend> - <span class="language-flag">{{ lang.flag }}</span> - </template> - <v-list-item-title>{{ lang.name }}</v-list-item-title> - </v-list-item> - </StyledMenu> -</template> - -<script setup lang="ts"> -import { computed } from 'vue' -import { useI18n, useLanguageSwitcher } from '@/i18n/composables' -import type { Locale } from '@/i18n/types' -import StyledMenu from '@/components/shared/StyledMenu.vue' - -// 定义props来控制样式变体 -const props = withDefaults(defineProps<{ - variant?: 'default' | 'header' | 'chatbox' -}>(), { - variant: 'default' -}) - -// 使用新的i18n系统 -const { t } = useI18n() -const { languageOptions, currentLanguage, switchLanguage, locale } = useLanguageSwitcher() - -const languages = computed(() => - languageOptions.value.map(lang => ({ - code: lang.value, - name: lang.label, - flag: lang.flag - })) -) - -const currentLocale = computed(() => locale.value) - -const changeLanguage = async (langCode: string) => { - await switchLanguage(langCode as Locale) -} -</script> - -<style scoped> -.language-flag { - font-size: 16px; - margin-right: 8px; -} - -/* 默认变体样式 - 圆形按钮用于登录页 */ -.language-switcher--default { - margin: 0 4px; - transition: all 0.3s ease; - border-radius: 50% !important; - min-width: 32px !important; - width: 32px !important; - height: 32px !important; -} - -.language-switcher--default:hover { - transform: scale(1.05); - background: rgba(var(--v-theme-primary), 0.08) !important; -} - -/* Header变体样式 - 完全继承Vuetify和action-btn的默认样式 */ -.language-switcher--header { - /* action-btn类已经处理了margin-right: 6px,不需要额外样式 */ -} - -/* ChatBox变体样式 - 与Header保持一致 */ -.language-switcher--chatbox { - /* 继承action-btn样式,与工具栏主题按钮保持一致 */ -} - -</style> \ No newline at end of file diff --git a/legacy-dashboard/src/components/shared/ListConfigItem.vue b/legacy-dashboard/src/components/shared/ListConfigItem.vue deleted file mode 100644 index 5ea6be7b..00000000 --- a/legacy-dashboard/src/components/shared/ListConfigItem.vue +++ /dev/null @@ -1,348 +0,0 @@ -<template> - <div class="d-flex align-center justify-space-between ga-2"> - <div v-if="isSingleItemMode" class="flex-grow-1 d-flex align-center ga-2"> - <v-text-field - v-model="singleItemValue" - hide-details - variant="outlined" - density="compact" - class="flex-grow-1" - ></v-text-field> - </div> - <div v-else> - <span v-if="!modelValue || modelValue.length === 0" style="color: rgb(var(--v-theme-primaryText));"> - {{ t('core.common.list.noItems') }} - </span> - <div v-else class="d-flex flex-wrap ga-2"> - <v-chip v-for="item in displayItems" :key="item" size="x-small" label color="primary"> - {{ item.length > 20 ? item.slice(0, 20) + '...' : item }} - </v-chip> - <v-chip v-if="modelValue.length > maxDisplayItems" size="x-small" label color="grey-lighten-1"> - +{{ modelValue.length - maxDisplayItems }} - </v-chip> - </div> - </div> - <v-btn size="small" color="primary" variant="tonal" @click="openDialog"> - {{ preferSingleItem ? t('core.common.list.addMore') : (buttonText || t('core.common.list.modifyButton')) }} - </v-btn> - </div> - - <!-- List Management Dialog --> - <v-dialog v-model="dialog" max-width="600px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ dialogTitle || t('core.common.list.editTitle') }} - </v-card-title> - - <!-- Add new item section - moved to top --> - <v-card-text class="pa-4 pb-2"> - <div class="d-flex align-center ga-2"> - <v-text-field - v-model="newItem" - :label="t('core.common.list.addItemPlaceholder')" - @keyup.enter="addItem" - clearable - hide-details - variant="outlined" - density="compact" - :placeholder="t('core.common.list.inputPlaceholder')" - class="flex-grow-1"> - </v-text-field> - <v-btn - @click="addItem" - variant="tonal" - color="primary" - size="small" - :disabled="!newItem.trim()"> - {{ t('core.common.list.addButton') }} - </v-btn> - <v-btn - @click="showBatchImport = true" - variant="tonal" - color="primary" - size="small"> - <v-icon size="small">mdi-import</v-icon> - {{ t('core.common.list.batchImport') }} - </v-btn> - </div> - </v-card-text> - - <v-card-text class="pa-0" style="max-height: 400px; overflow-y: auto;"> - <v-list v-if="localItems.length > 0" density="compact"> - <v-list-item - v-for="(item, index) in localItems" - :key="index" - rounded="md" - class="ma-1 list-item-clickable" - @click="startEdit(index, item)"> - <v-list-item-title v-if="editIndex !== index" class="item-text"> - {{ item }} - </v-list-item-title> - <v-text-field - v-else - v-model="editItem" - hide-details - variant="outlined" - density="compact" - @keyup.enter="saveEdit" - @keyup.esc="cancelEdit" - @click.stop - autofocus - ></v-text-field> - - <template v-slot:append> - <div class="d-flex"> - <v-btn - v-if="editIndex === index" - @click.stop="saveEdit" - variant="text" - color="success" - icon - size="small"> - <v-icon>mdi-check</v-icon> - </v-btn> - <v-btn - @click.stop="editIndex === index ? cancelEdit() : removeItem(index)" - variant="text" - :color="editIndex === index ? 'error' : 'default'" - icon - size="small"> - <v-icon>mdi-close</v-icon> - </v-btn> - </div> - </template> - </v-list-item> - </v-list> - - <div v-else class="text-center py-8"> - <v-icon size="64" color="grey-lighten-1">mdi-format-list-bulleted</v-icon> - <p class="text-grey mt-4">{{ t('core.common.list.noItemsHint') }}</p> - </div> - </v-card-text> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelDialog">{{ t('core.common.cancel') }}</v-btn> - <v-btn color="primary" variant="tonal" @click="confirmDialog">{{ t('core.common.confirm') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <!-- Batch Import Dialog --> - <v-dialog v-model="showBatchImport" max-width="600px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ t('core.common.list.batchImportTitle') }} - </v-card-title> - - <v-card-text> - <v-textarea - v-model="batchImportText" - :label="t('core.common.list.batchImportLabel')" - :placeholder="t('core.common.list.batchImportPlaceholder')" - rows="10" - variant="outlined" - :hint="t('core.common.list.batchImportHint')" - persistent-hint - ></v-textarea> - </v-card-text> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelBatchImport">{{ t('core.common.cancel') }}</v-btn> - <v-btn color="primary" variant="tonal" @click="confirmBatchImport"> - {{ t('core.common.list.batchImportButton', { count: batchImportPreviewCount }) }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref, computed, watch, nextTick } from 'vue' -import { useI18n } from '@/i18n/composables' - -const { t } = useI18n() - -const props = defineProps({ - modelValue: { - type: Array, - default: () => [] - }, - label: { - type: String, - default: '' - }, - buttonText: { - type: String, - default: '' - }, - dialogTitle: { - type: String, - default: '' - }, - maxDisplayItems: { - type: Number, - default: 1 - }, - preferSingleItem: { - type: Boolean, - default: true - } -}) - -const emit = defineEmits(['update:modelValue']) - -const dialog = ref(false) -const localItems = ref([]) -const originalItems = ref([]) -const newItem = ref('') -const editIndex = ref(-1) -const editItem = ref('') -const showBatchImport = ref(false) -const batchImportText = ref('') -const isSingleItemMode = computed(() => (props.modelValue?.length ?? 0) <= 1 && props.preferSingleItem) -const singleItemValue = computed({ - get: () => props.modelValue?.[0] ?? '', - set: (value) => { - // 仅当值为完全空字符串(未输入任何字符)时清空数组, - // 允许包含空格(如 "hello world")以及纯空格(如 " ")通过 - if (value === '') { - emit('update:modelValue', []) - return - } - - const newItems = [...(props.modelValue || [])] - if (newItems.length === 0) { - newItems.push(value) - } else { - newItems[0] = value - } - - emit('update:modelValue', newItems) - } -}) - -// 计算要显示的项目 -const displayItems = computed(() => { - return props.modelValue.slice(0, props.maxDisplayItems) -}) - -// 计算批量导入的项目数量 -const batchImportPreviewCount = computed(() => { - if (!batchImportText.value) return 0 - return batchImportText.value - .split('\n') - .map(line => line.trim()) - .filter(line => line.length > 0) - .length -}) - -// 监听 modelValue 变化,同步到 localItems,并清理空字符串 -watch(() => props.modelValue, (newValue) => { - localItems.value = [...(newValue || [])] - - // 自动清理只包含空字符串或纯空格的条目(纯空格在配置中无意义,此过滤为预期兜底行为) - if (newValue && newValue.length > 0) { - const filtered = newValue.filter(item => typeof item === 'string' ? item.trim() !== '' : true) - if (filtered.length !== newValue.length) { - // 使用 nextTick 确保父组件已准备好接收更新 - nextTick(() => { - emit('update:modelValue', filtered) - }) - } - } -}, { immediate: true }) - -function openDialog() { - localItems.value = [...(props.modelValue || [])] - originalItems.value = [...(props.modelValue || [])] - dialog.value = true - editIndex.value = -1 - editItem.value = '' - newItem.value = '' -} - -function addItem() { - if (newItem.value.trim() !== '') { - localItems.value.push(newItem.value.trim()) - newItem.value = '' - } -} - -function removeItem(index) { - localItems.value.splice(index, 1) -} - -function startEdit(index, item) { - editIndex.value = index - editItem.value = item -} - -function saveEdit() { - if (editItem.value.trim() !== '') { - localItems.value[editIndex.value] = editItem.value.trim() - cancelEdit() - } -} - -function cancelEdit() { - editIndex.value = -1 - editItem.value = '' -} - -function confirmDialog() { - // 过滤空字符串,同时处理非字符串类型 - const filteredItems = localItems.value.filter(item => typeof item === 'string' ? item.trim() !== '' : true) - emit('update:modelValue', filteredItems) - dialog.value = false -} - -function cancelDialog() { - localItems.value = [...originalItems.value] - editIndex.value = -1 - editItem.value = '' - newItem.value = '' - dialog.value = false -} - -function confirmBatchImport() { - if (batchImportText.value.trim()) { - const newItems = batchImportText.value - .split('\n') - .map(line => line.trim()) - .filter(line => line.length > 0) - - localItems.value.push(...newItems) - batchImportText.value = '' - showBatchImport.value = false - } -} - -function cancelBatchImport() { - batchImportText.value = '' - showBatchImport.value = false -} -</script> - -<style scoped> -.v-list-item { - transition: all 0.2s ease; -} - -.list-item-clickable { - cursor: pointer; -} - -.list-item-clickable:hover { - background-color: rgba(var(--v-theme-primary), 0.08); -} - -.item-text { - user-select: none; -} - -.v-chip { - margin: 2px; -} -</style> diff --git a/legacy-dashboard/src/components/shared/Logo.vue b/legacy-dashboard/src/components/shared/Logo.vue deleted file mode 100644 index 49e59ed9..00000000 --- a/legacy-dashboard/src/components/shared/Logo.vue +++ /dev/null @@ -1,121 +0,0 @@ -<template> - <div class="logo-container"> - <div class="logo-content"> - <div class="logo-image"> - <img width="80" src="@/assets/images/plugin_icon.png" alt="AstrBot Logo"> - </div> - <div class="logo-text"> - <h2 - v-html="formatTitle(title || t('core.header.logoTitle'))" - ></h2> - <h4 class="hint-text">{{ subtitle || t('core.header.accountDialog.title') }}</h4> - </div> - </div> - </div> -</template> - -<script setup lang="ts"> -import { useI18n } from '@/i18n/composables'; - -const { t } = useI18n(); - -const props = withDefaults(defineProps<{ - title?: string; - subtitle?: string; -}>(), { - title: '', // 默认为空,组件会使用翻译值 - subtitle: '' -}) - -// 智能格式化标题,在小屏幕上允许在合适位置换行 -const formatTitle = (title: string) => { - // 如果标题包含 "AstrBot" 和其他文字,在它们之间添加换行机会 - if (title.includes('AstrBot ') || title.includes('AstrBot')) { - // 处理 "AstrBot 仪表盘" 或 "AstrBot Dashboard" 等格式 - return title.replace(/(AstrBot)\s+(.+)/, '$1<wbr> $2'); - } - return title; -} -</script> - -<style scoped> -.logo-container { - display: flex; - justify-content: flex-start; - align-items: center; - width: 100%; - margin-bottom: 10px; -} - -.logo-content { - display: flex; - align-items: flex-start; - gap: 16px; - padding: 10px 0; - max-width: 100%; - overflow: visible; -} - -.logo-image { - display: flex; - justify-content: center; - align-items: center; -} - -.logo-image img { - transition: transform 0.3s ease; -} - -.logo-text { - display: flex; - flex-direction: column; - align-items: flex-start; - min-width: 0; - flex: 1; -} - -.logo-text h2 { - color: rgb(var(--v-theme-on-surface)); - margin: 0; - font-size: 1.8rem; - font-weight: 600; - letter-spacing: 0.5px; - white-space: nowrap; - min-width: fit-content; -} - -/* 在小屏幕上允许在指定位置换行 */ -@media (max-width: 420px) { - .logo-text h2 { - line-height: 1.3; - } -} - -.logo-text h4 { - color: rgba(var(--v-theme-on-surface), 0.72); - margin: 4px 0 0 0; - font-size: 1rem; - font-weight: 400; - letter-spacing: 0.3px; - white-space: nowrap; -} - -/* 响应式处理 */ -@media (max-width: 520px) { - .logo-content { - gap: 8px; - } - - .logo-text h2 { - font-size: 1.6rem; - } - - .logo-text h4 { - font-size: 0.9rem; - } - - .logo-image img { - width: 64px; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/ObjectEditor.vue b/legacy-dashboard/src/components/shared/ObjectEditor.vue deleted file mode 100644 index 04a3aeb9..00000000 --- a/legacy-dashboard/src/components/shared/ObjectEditor.vue +++ /dev/null @@ -1,556 +0,0 @@ -<template> - <div class="d-flex align-center justify-space-between"> - <div> - <span v-if="!modelValue || Object.keys(modelValue).length === 0" style="color: rgb(var(--v-theme-primaryText));"> - {{ t('core.common.objectEditor.noItems') }} - </span> - <div v-else class="d-flex flex-wrap ga-2"> - <v-chip v-for="key in displayKeys" :key="key" size="x-small" label color="primary"> - {{ key.length > 20 ? key.slice(0, 20) + '...' : key }} - </v-chip> - <v-chip v-if="Object.keys(modelValue).length > maxDisplayItems" size="x-small" label color="grey-lighten-1"> - +{{ Object.keys(modelValue).length - maxDisplayItems }} - </v-chip> - </div> - </div> - <v-btn size="small" color="primary" variant="tonal" @click="openDialog"> - {{ resolveButtonText }} - </v-btn> - </div> - - <!-- Key-Value Management Dialog --> - <v-dialog v-model="dialog" max-width="600px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ resolveDialogTitle }} - </v-card-title> - - <v-card-text class="pa-4" style="max-height: 400px; overflow-y: auto;"> - <!-- Regular key-value pairs (non-template) --> - <div v-if="nonTemplatePairs.length > 0"> - <div v-for="pair in nonTemplatePairs" :key="pair._id" class="key-value-pair"> - <v-row no-gutters align="center" class="mb-2"> - <v-col cols="4"> - <v-text-field - v-model="pair.key" - density="compact" - variant="outlined" - hide-details - :placeholder="t('core.common.objectEditor.placeholders.keyName')" - @focus="pair._originalKey = pair.key" - @blur="onKeyBlur(pair)" - ></v-text-field> - </v-col> - <v-col cols="7" class="pl-2 d-flex align-center justify-end"> - <v-text-field - v-if="pair.type === 'string'" - v-model="pair.value" - density="compact" - variant="outlined" - hide-details - :placeholder="t('core.common.objectEditor.placeholders.stringValue')" - ></v-text-field> - <div v-else-if="pair.type === 'number' || pair.type === 'float' || pair.type === 'int'" class="d-flex align-center gap-2 flex-grow-1"> - <v-slider - v-if="pair.slider" - :model-value="Number(pair.value) || 0" - @update:model-value="pair.value = $event" - :min="pair.slider.min" - :max="pair.slider.max" - :step="pair.slider.step" - color="primary" - density="compact" - hide-details - class="flex-grow-1" - ></v-slider> - <v-text-field - v-model.number="pair.value" - type="number" - density="compact" - variant="outlined" - hide-details - :placeholder="t('core.common.objectEditor.placeholders.numberValue')" - :style="pair.slider ? 'max-width: 120px;' : ''" - ></v-text-field> - </div> - <v-switch - v-else-if="pair.type === 'boolean'" - v-model="pair.value" - density="compact" - hide-details - color="primary" - ></v-switch> - <v-text-field - v-if="pair.type === 'json'" - v-model="pair.value" - density="compact" - variant="outlined" - hide-details="auto" - :placeholder="t('core.common.objectEditor.placeholders.jsonValue')" - @blur="validateJSON(pair)" - :error-messages="pair.jsonError" - ></v-text-field> - </v-col> - <v-col cols="1" class="pl-2"> - <v-btn - icon - variant="text" - size="small" - color="error" - @click="removeKeyValuePairByKey(pair.key)" - > - <v-icon>mdi-delete</v-icon> - </v-btn> - </v-col> - </v-row> - </div> - </div> - - <!-- Template schema fields --> - <div v-if="hasTemplateSchema" class="mt-4"> - <v-divider class="mb-3"></v-divider> - <div class="text-caption text-grey mb-2">{{ t('core.common.objectEditor.presets') }}</div> - <div v-for="(template, templateKey) in templateSchema" :key="templateKey" class="template-field" :class="{ 'template-field-inactive': !isTemplateKeyAdded(templateKey) }"> - <v-row no-gutters align="center" class="mb-2"> - <v-col cols="4"> - <div class="d-flex flex-column"> - <span class="text-caption font-weight-medium">{{ getTemplateTitle(template, templateKey) }}</span> - <span v-if="template.hint" class="text-caption text-grey" style="font-size: 0.7rem;">{{ resolveTemplateText(templateKey, 'hint', template.hint) }}</span> - </div> - </v-col> - <v-col cols="7" class="pl-2 d-flex align-center justify-end"> - <v-text-field - v-if="template.type === 'string'" - :model-value="getTemplateValue(templateKey)" - @update:model-value="updateTemplateValue(templateKey, $event)" - density="compact" - variant="outlined" - hide-details - :placeholder="t('core.common.objectEditor.placeholders.stringValue')" - ></v-text-field> - <div v-else-if="template.type === 'number' || template.type === 'float' || template.type === 'int'" class="d-flex align-center ga-4 flex-grow-1"> - <v-slider - v-if="template.slider" - :model-value="Number(getTemplateValue(templateKey)) || 0" - @update:model-value="updateTemplateValue(templateKey, $event)" - :min="template.slider.min" - :max="template.slider.max" - :step="template.slider.step" - color="primary" - density="compact" - hide-details - class="flex-grow-1" - ></v-slider> - <v-text-field - :model-value="getTemplateValue(templateKey)" - @update:model-value="updateTemplateValue(templateKey, $event)" - type="number" - density="compact" - variant="outlined" - hide-details - :placeholder="t('core.common.objectEditor.placeholders.numberValue')" - :style="template.slider ? 'max-width: 120px;' : ''" - ></v-text-field> - </div> - <v-switch - v-else-if="template.type === 'boolean' || template.type === 'bool'" - :model-value="getTemplateValue(templateKey)" - @update:model-value="updateTemplateValue(templateKey, $event)" - density="compact" - hide-details - color="primary" - ></v-switch> - </v-col> - <v-col cols="1" class="pl-2"> - <v-btn - v-if="isTemplateKeyAdded(templateKey)" - icon - variant="text" - size="small" - color="error" - @click="removeTemplateKey(templateKey)" - > - <v-icon>mdi-close</v-icon> - </v-btn> - </v-col> - </v-row> - </div> - </div> - - <div v-if="localKeyValuePairs.length === 0 && !hasTemplateSchema" class="text-center py-8"> - <v-icon size="64" color="grey-lighten-1">mdi-code-json</v-icon> - <p class="text-grey mt-4">{{ t('core.common.objectEditor.noParams') }}</p> - </div> - </v-card-text> - - <!-- Add new key-value pair section --> - <v-card-text class="pa-4"> - <div class="d-flex align-center ga-2"> - <v-text-field - v-model="newKey" - :label="t('core.common.objectEditor.newKeyLabel')" - density="compact" - variant="outlined" - hide-details - class="flex-grow-1" - ></v-text-field> - <v-select - v-model="newValueType" - :items="['string', 'number', 'boolean', 'json']" - :label="t('core.common.objectEditor.valueTypeLabel')" - density="compact" - variant="outlined" - hide-details - style="max-width: 120px;" - ></v-select> - <v-btn @click="addKeyValuePair" variant="tonal" color="primary"> - <v-icon>mdi-plus</v-icon> - {{ t('core.common.add') }} - </v-btn> - </div> - </v-card-text> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelDialog">{{ t('core.common.cancel') }}</v-btn> - <v-btn color="primary" variant="tonal" @click="confirmDialog">{{ t('core.common.confirm') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref, computed, watch } from 'vue' -import { useI18n } from '@/i18n/composables' -import { useToast } from '@/utils/toast' -import { useConfigTextResolver } from '@/composables/useConfigTextResolver' - -const { t } = useI18n() -const { warning: toastWarning } = useToast() - -const props = defineProps({ - modelValue: { - type: Object, - required: true - }, - itemMeta: { - type: Object, - default: null - }, - pluginName: { - type: String, - default: '' - }, - pluginI18n: { - type: Object, - default: () => ({}) - }, - configKey: { - type: String, - default: '' - }, - buttonText: { - type: String, - default: '' - }, - dialogTitle: { - type: String, - default: '' - }, - maxDisplayItems: { - type: Number, - default: 1 - } -}) - -const { translateIfKey, resolveConfigText } = useConfigTextResolver(props) - -const emit = defineEmits(['update:modelValue']) - -const resolveButtonText = computed(() => props.buttonText || t('core.common.list.modifyButton')) -const resolveDialogTitle = computed(() => props.dialogTitle || t('core.common.objectEditor.dialogTitle')) - -const dialog = ref(false) -const localKeyValuePairs = ref([]) -const originalKeyValuePairs = ref([]) -const newKey = ref('') -const newValueType = ref('string') -const nextPairId = ref(0) - -// Template schema support -const templateSchema = computed(() => { - return props.itemMeta?.template_schema || {} -}) - -const hasTemplateSchema = computed(() => { - return Object.keys(templateSchema.value).length > 0 -}) - -// 计算要显示的键名 -const displayKeys = computed(() => { - return Object.keys(props.modelValue).slice(0, props.maxDisplayItems) -}) - -// 分离模板字段和普通字段 -const nonTemplatePairs = computed(() => { - return localKeyValuePairs.value.filter(pair => !templateSchema.value[pair.key]) -}) - -// 监听 modelValue 变化,主要用于初始化 -watch(() => props.modelValue, (newValue) => { - // This watch is primarily for initialization or external changes - // The dialog-based editing handles internal updates -}, { immediate: true }) - -function createPair({ key, value, type, slider, template, jsonError = '', _originalKey }) { - return { - _id: nextPairId.value++, - key, - value, - type, - slider, - template, - jsonError, - _originalKey - } -} - -function initializeLocalKeyValuePairs() { - localKeyValuePairs.value = [] - nextPairId.value = 0 - for (const [key, value] of Object.entries(props.modelValue)) { - let _type = (typeof value) === 'object' ? 'json':(typeof value) - let _value = _type === 'json' ? JSON.stringify(value) : value - - // Check if this key has a template schema - const template = templateSchema.value[key] - if (template) { - // Use template type if available - _type = template.type || _type - // Use template default if value is missing - if (_value === undefined || _value === null) { - _value = template.default !== undefined ? template.default : _value - } - } - - localKeyValuePairs.value.push(createPair({ - key, - value: _value, - type: _type, - slider: template?.slider, - template - })) - } -} - -function openDialog() { - initializeLocalKeyValuePairs() - originalKeyValuePairs.value = localKeyValuePairs.value.map(pair => ({ ...pair })) - newKey.value = '' - newValueType.value = 'string' - dialog.value = true -} - -function addKeyValuePair() { - const key = newKey.value.trim() - if (key !== '') { - const isKeyExists = localKeyValuePairs.value.some(pair => pair.key === key) - if (isKeyExists) { - toastWarning(t('core.common.objectEditor.keyExists')) - return - } - - let defaultValue - switch (newValueType.value) { - case 'number': - defaultValue = 0 - break - case 'boolean': - defaultValue = false - break - case 'json': - defaultValue = '{}' - break - default: // string - defaultValue = '' - break - } - - localKeyValuePairs.value.push(createPair({ - key, - value: defaultValue, - type: newValueType.value - })) - newKey.value = '' - } -} - -function validateJSON(pair) { - try { - JSON.parse(pair.value) - pair.jsonError = '' - } catch (e) { - pair.jsonError = t('core.common.objectEditor.invalidJson') - } -} - -function removeKeyValuePairByKey(key) { - const index = localKeyValuePairs.value.findIndex(pair => pair.key === key) - if (index >= 0) { - localKeyValuePairs.value.splice(index, 1) - } -} - -function onKeyBlur(pair) { - const originalKey = pair._originalKey - const newKey = pair.key - if (originalKey === undefined || originalKey === newKey) return - - const isKeyExists = localKeyValuePairs.value.some(p => p !== pair && p.key === newKey) - if (isKeyExists) { - toastWarning(t('core.common.objectEditor.keyExists')) - pair.key = originalKey - return - } - - const template = templateSchema.value[newKey] - if (template) { - pair.type = template.type || pair.type - if (pair.value === undefined || pair.value === null || pair.value === '') { - pair.value = template.default !== undefined ? template.default : pair.value - } - pair.slider = template.slider - pair.template = template - } else { - pair.slider = undefined - pair.template = undefined - } -} - -function isTemplateKeyAdded(templateKey) { - return localKeyValuePairs.value.some(pair => pair.key === templateKey) -} - -function getTemplateValue(templateKey) { - const pair = localKeyValuePairs.value.find(pair => pair.key === templateKey) - if (pair) { - return pair.value - } - const template = templateSchema.value[templateKey] - return template?.default !== undefined ? template.default : getDefaultValueForType(template?.type || 'string') -} - -function updateTemplateValue(templateKey, newValue) { - const existingIndex = localKeyValuePairs.value.findIndex(pair => pair.key === templateKey) - const template = templateSchema.value[templateKey] - - if (existingIndex >= 0) { - // 更新现有值 - localKeyValuePairs.value[existingIndex].value = newValue - } else { - // 添加新字段 - const valueType = template?.type || 'string' - localKeyValuePairs.value.push(createPair({ - key: templateKey, - value: newValue, - type: valueType, - slider: template?.slider, - template - })) - } -} - -function removeTemplateKey(templateKey) { - const index = localKeyValuePairs.value.findIndex(pair => pair.key === templateKey) - if (index >= 0) { - localKeyValuePairs.value.splice(index, 1) - } -} - -function getDefaultValueForType(type) { - switch (type) { - case 'int': - case 'float': - case 'number': - return 0 - case 'bool': - case 'boolean': - return false - case 'json': - return '{}' - case 'string': - default: - return '' - } -} - -function confirmDialog() { - const updatedValue = {} - for (const pair of localKeyValuePairs.value) { - if (pair.type === 'json' && pair.jsonError) return - let convertedValue = pair.value - // 根据声明的类型进行转换 - switch (pair.type) { - case 'int': - convertedValue = parseInt(pair.value) || 0 - break - case 'float': - case 'number': - // 尝试转换为数字,如果失败则保持原值(或设为默认值0) - convertedValue = Number(pair.value) - // 可选:检查是否为有效数字,无效则设为0或报错 - // if (isNaN(convertedValue)) convertedValue = 0; - break - case 'bool': - case 'boolean': - // 布尔值通常由 v-switch 正确处理,但为保险起见可以显式转换 - // 注意:在 JavaScript 中,只有严格的 false, 0, '', null, undefined, NaN 会被转换为 false - // 这里直接赋值 pair.value 应该是安全的,因为 v-model 绑定的就是布尔值 - // convertedValue = Boolean(pair.value) - break - case 'json': - convertedValue = JSON.parse(pair.value) - break - case 'string': - default: - // 默认转换为字符串 - convertedValue = String(pair.value) - break - } - updatedValue[pair.key] = convertedValue - } - emit('update:modelValue', updatedValue) - dialog.value = false -} - -function cancelDialog() { - // Reset to original state - localKeyValuePairs.value = originalKeyValuePairs.value.map(pair => ({ ...pair })) - dialog.value = false -} - -function getTemplateTitle(template, templateKey) { - return resolveTemplateText(templateKey, 'name', template?.name || template?.description || templateKey) -} - -function resolveTemplateText(templateKey, attr, fallback) { - if (!props.configKey) { - return translateIfKey(fallback) || '' - } - return resolveConfigText(`${props.configKey}.template_schema.${templateKey}`, attr, fallback) -} -</script> - -<style scoped> -.key-value-pair { - width: 100%; -} - -.template-field { - transition: opacity 0.2s; -} - -.template-field-inactive { - opacity: 0.8; -} -</style> diff --git a/legacy-dashboard/src/components/shared/OutlinedActionListItem.vue b/legacy-dashboard/src/components/shared/OutlinedActionListItem.vue deleted file mode 100644 index d9de46f0..00000000 --- a/legacy-dashboard/src/components/shared/OutlinedActionListItem.vue +++ /dev/null @@ -1,148 +0,0 @@ -<template> - <v-card - class="outlined-action-list-item rounded-lg" - :class="{ 'outlined-action-list-item--clickable': clickable }" - variant="outlined" - :ripple="false" - @click="handleClick" - > - <div class="outlined-action-list-item__main"> - <div class="outlined-action-list-item__content"> - <div class="outlined-action-list-item__header"> - <slot name="title-prepend"></slot> - <div class="outlined-action-list-item__title"> - {{ title }} - </div> - <slot name="title-extra"></slot> - </div> - - <slot></slot> - </div> - - <div - v-if="$slots.actions || $slots.control" - class="outlined-action-list-item__actions" - > - <div - v-if="$slots.actions" - class="outlined-action-list-item__hover-actions" - > - <slot name="actions"></slot> - </div> - - <slot name="control"></slot> - </div> - </div> - </v-card> -</template> - -<script setup> -const props = defineProps({ - title: { - type: String, - required: true, - }, - clickable: { - type: Boolean, - default: false, - }, -}); - -const emit = defineEmits(["click"]); - -const handleClick = (event) => { - if (!props.clickable) return; - emit("click", event); -}; -</script> - -<style scoped> -.outlined-action-list-item { - background: rgb(var(--v-theme-surface)); - transition: background-color 0.16s ease; -} - -.outlined-action-list-item:hover, -.outlined-action-list-item:focus-within { - background: rgba(var(--v-theme-on-surface), 0.04); -} - -.outlined-action-list-item--clickable { - cursor: pointer; -} - -.outlined-action-list-item :deep(.v-card__overlay), -.outlined-action-list-item :deep(.v-ripple__container) { - display: none; -} - -.outlined-action-list-item__main { - align-items: center; - display: flex; - gap: 16px; - justify-content: space-between; - min-height: 104px; - padding: 16px; -} - -.outlined-action-list-item__content { - min-width: 0; -} - -.outlined-action-list-item__header { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 6px; -} - -.outlined-action-list-item__title { - font-size: 1rem; - font-weight: 700; - line-height: 1.35; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.outlined-action-list-item__actions { - align-items: center; - display: flex; - flex-shrink: 0; - gap: 8px; - margin-left: auto; -} - -.outlined-action-list-item__hover-actions { - align-items: center; - display: flex; - gap: 4px; - pointer-events: none; - transition: opacity 0.16s ease; -} - -.outlined-action-list-item:hover .outlined-action-list-item__hover-actions, -.outlined-action-list-item:focus-within .outlined-action-list-item__hover-actions { - opacity: 1; - pointer-events: auto; -} - -@media (max-width: 860px) { - .outlined-action-list-item__main { - align-items: stretch; - flex-direction: column; - } - - .outlined-action-list-item__actions { - flex-wrap: wrap; - justify-content: flex-end; - margin-left: 0; - } - - .outlined-action-list-item__hover-actions { - opacity: 1; - pointer-events: auto; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/PersonaForm.vue b/legacy-dashboard/src/components/shared/PersonaForm.vue deleted file mode 100644 index abbec8e8..00000000 --- a/legacy-dashboard/src/components/shared/PersonaForm.vue +++ /dev/null @@ -1,955 +0,0 @@ -<template> - <v-dialog v-model="showDialog" :max-width="$vuetify.display.smAndDown ? undefined : '1200px'" scrollable> - <v-card class="persona-form-card" :class="{ 'persona-form-card-mobile': $vuetify.display.smAndDown }"> - <v-card-title class="persona-form-title text-h3 pa-4 pb-0 pl-6"> - {{ editingPersona ? tm('dialog.edit.title') : tm('dialog.create.title') }} - </v-card-title> - - <v-card-text class="persona-form-content"> - <!-- 创建位置提示 --> - <v-alert v-if="!editingPersona" type="info" variant="tonal" density="compact" class="mb-4" - icon="mdi-folder-outline"> - {{ tm('form.createInFolder', { folder: folderDisplayName }) }} - </v-alert> - - <v-form ref="personaForm" v-model="formValid"> - <v-row class="persona-form-layout"> - <v-col cols="12" md="6" class="persona-basic-col"> - <v-text-field v-model="personaForm.persona_id" :label="tm('form.personaId')" - :rules="personaIdRules" :disabled="editingPersona" variant="outlined" - density="comfortable" class="mb-4" /> - - <v-textarea v-model="personaForm.system_prompt" :label="tm('form.systemPrompt')" - :rules="systemPromptRules" variant="outlined" rows="16" class="mb-4" /> - - <v-textarea - v-model="personaForm.custom_error_message" - :label="tm('form.customErrorMessage')" - :hint="tm('form.customErrorMessageHelp')" - variant="outlined" - rows="4" - persistent-hint - clearable - class="mb-4" - /> - </v-col> - - <v-col cols="12" md="6" class="persona-panels-col"> - <v-expansion-panels v-model="expandedPanels" multiple> - <!-- 工具选择面板 --> - <v-expansion-panel value="tools"> - <v-expansion-panel-title> - <v-icon class="mr-2">mdi-tools</v-icon> - {{ tm('form.tools') }} - <v-chip v-if="Array.isArray(personaForm.tools) && personaForm.tools.length > 0" - size="small" color="primary" variant="tonal" class="ml-2"> - {{ personaForm.tools.length }} - </v-chip> - </v-expansion-panel-title> - - <v-expansion-panel-text> - <div class="mb-3"> - <p class="text-body-2 text-medium-emphasis"> - {{ tm('form.toolsHelp') }} - </p> - </div> - - <v-radio-group class="mt-2" v-model="toolSelectValue" hide-details="true"> - <v-radio label="默认使用全部函数工具" value="0"></v-radio> - <v-radio label="选择指定函数工具" value="1"> - </v-radio> - </v-radio-group> - - <div v-if="toolSelectValue === '1'" class="mt-3 selected-config-area"> - - <!-- 工具搜索 --> - <v-text-field v-model="toolSearch" :label="tm('form.searchTools')" - prepend-inner-icon="mdi-magnify" variant="outlined" density="compact" - hide-details clearable class="mb-3" /> - - - <!-- MCP 服务器 --> - <div v-if="mcpServers.length > 0" class="mb-4"> - <h4 class="text-subtitle-2 mb-2">{{ tm('form.mcpServersQuickSelect') }}</h4> - <div class="d-flex flex-wrap ga-2"> - <v-chip v-for="server in mcpServers" :key="server.name" - :color="isServerSelected(server) ? 'primary' : 'default'" - :variant="isServerSelected(server) ? 'flat' : 'outlined'" size="small" - clickable @click="toggleMcpServer(server)" - :disabled="!server.tools || server.tools.length === 0"> - <v-icon start size="small">mdi-server</v-icon> - {{ server.name }} - <v-chip-text v-if="server.tools" class="ml-1"> - ({{ server.tools.length }}) - </v-chip-text> - </v-chip> - </div> - </div> - - <!-- 工具选择列表 --> - <div v-if="filteredTools.length > 0" class="tools-selection"> - <v-virtual-scroll :items="filteredTools" height="300" item-height="72"> - <template v-slot:default="{ item }"> - <v-tooltip - :disabled="!isBuiltinTool(item)" - location="top" - > - <template v-slot:activator="{ props: tooltipProps }"> - <div v-bind="tooltipProps"> - <v-list-item - :key="item.name" - density="comfortable" - :disabled="isBuiltinTool(item)" - @click="toggleTool(item.name)" - > - <template v-slot:prepend> - <v-checkbox-btn - v-if="!isBuiltinTool(item)" - :model-value="isToolSelected(item.name)" - @click.stop="toggleTool(item.name)" - /> - <div - v-else - class="builtin-tool-checkbox-placeholder" - /> - </template> - - <v-list-item-title> - {{ item.name }} - - <v-chip v-if="item.origin" size="x-small" color="info" class="mr-2" - variant="tonal"> - {{ item.origin }} - </v-chip> - <v-chip v-if="item.origin_name" size="x-small" color="info" - variant="outlined"> - {{ item.origin_name }} - </v-chip> - - </v-list-item-title> - - <v-list-item-subtitle v-if="item.description"> - {{ truncateText(item.description, 100) }} - </v-list-item-subtitle> - </v-list-item> - </div> - </template> - <span>{{ tm('form.builtinToolDisabledHint') }}</span> - </v-tooltip> - </template> - </v-virtual-scroll> - </div> - - <div v-else-if="!loadingTools && availableTools.length === 0" - class="text-center pa-4"> - <v-icon size="48" color="grey-lighten-2" class="mb-2">mdi-tools</v-icon> - <p class="text-body-2 text-medium-emphasis">{{ tm('form.noToolsAvailable') - }} - </p> - </div> - - <div v-else-if="!loadingTools && filteredTools.length === 0" - class="text-center pa-4"> - <v-icon size="48" color="grey-lighten-2" class="mb-2">mdi-magnify</v-icon> - <p class="text-body-2 text-medium-emphasis">{{ tm('form.noToolsFound') }} - </p> - </div> - - <!-- 加载状态 --> - <div v-if="loadingTools" class="text-center pa-4"> - <v-progress-circular indeterminate color="primary" /> - <p class="text-body-2 text-medium-emphasis mt-2">{{ tm('form.loadingTools') - }} - </p> - </div> - - <!-- 已选择的工具 --> - <div class="mt-4"> - <h4 class="text-subtitle-2 mb-2"> - {{ tm('form.selectedTools') }} - <span v-if="personaForm.tools === null" class="text-success"> - ({{ tm('form.allSelected') }}) - </span> - <span v-else-if="Array.isArray(personaForm.tools)"> - ({{ personaForm.tools.length }}) - </span> - </h4> - <div v-if="Array.isArray(personaForm.tools) && personaForm.tools.length > 0" - class="d-flex flex-wrap ga-1" style="max-height: 100px; overflow-y: auto;"> - <v-tooltip - v-for="toolName in personaForm.tools" - :key="toolName" - :disabled="!isBuiltinToolName(toolName)" - location="top" - > - <template v-slot:activator="{ props: tooltipProps }"> - <v-chip - v-bind="tooltipProps" - size="small" - color="primary" - variant="tonal" - :closable="!isBuiltinToolName(toolName)" - @click:close="removeTool(toolName)" - > - {{ toolName }} - </v-chip> - </template> - <span>{{ tm('form.builtinToolDisabledHint') }}</span> - </v-tooltip> - </div> - <div v-else class="text-body-2 text-medium-emphasis"> - {{ tm('form.noToolsSelected') }} - </div> - </div> - </div> - - </v-expansion-panel-text> - </v-expansion-panel> - - <!-- Skills 选择面板 --> - <v-expansion-panel value="skills"> - <v-expansion-panel-title> - <v-icon class="mr-2">mdi-lightning-bolt</v-icon> - {{ tm('form.skills') }} - <v-chip v-if="Array.isArray(personaForm.skills) && personaForm.skills.length > 0" - size="small" color="primary" variant="tonal" class="ml-2"> - {{ personaForm.skills.length }} - </v-chip> - </v-expansion-panel-title> - - <v-expansion-panel-text> - <div class="mb-3"> - <p class="text-body-2 text-medium-emphasis"> - {{ tm('form.skillsHelp') }} - </p> - </div> - - <v-radio-group class="mt-2" v-model="skillSelectValue" hide-details="true"> - <v-radio :label="tm('form.skillsAllAvailable')" value="0"></v-radio> - <v-radio :label="tm('form.skillsSelectSpecific')" value="1"></v-radio> - </v-radio-group> - - <div v-if="skillSelectValue === '1'" class="mt-3 selected-config-area"> - <v-text-field v-model="skillSearch" :label="tm('form.searchSkills')" - prepend-inner-icon="mdi-magnify" variant="outlined" density="compact" - hide-details clearable class="mb-3" /> - - <div v-if="filteredSkills.length > 0" class="skills-selection"> - <v-virtual-scroll :items="filteredSkills" height="240" item-height="48"> - <template v-slot:default="{ item }"> - <v-list-item :key="item.name" density="comfortable" - @click="toggleSkill(item.name)"> - <template v-slot:prepend> - <v-checkbox-btn :model-value="isSkillSelected(item.name)" - @click.stop="toggleSkill(item.name)" /> - </template> - <v-list-item-title> - {{ item.name }} - </v-list-item-title> - <v-list-item-subtitle v-if="item.description"> - {{ truncateText(item.description, 100) }} - </v-list-item-subtitle> - </v-list-item> - </template> - </v-virtual-scroll> - </div> - - <div v-else-if="!loadingSkills && availableSkills.length === 0" - class="text-center pa-4"> - <v-icon size="48" color="grey-lighten-2" - class="mb-2">mdi-lightning-bolt</v-icon> - <p class="text-body-2 text-medium-emphasis">{{ tm('form.noSkillsAvailable') }} - </p> - </div> - - <div v-else-if="!loadingSkills && filteredSkills.length === 0" - class="text-center pa-4"> - <v-icon size="48" color="grey-lighten-2" class="mb-2">mdi-magnify</v-icon> - <p class="text-body-2 text-medium-emphasis">{{ tm('form.noSkillsFound') }} - </p> - </div> - - <div v-if="loadingSkills" class="text-center pa-4"> - <v-progress-circular indeterminate color="primary" /> - <p class="text-body-2 text-medium-emphasis mt-2">{{ tm('form.loadingSkills') }} - </p> - </div> - - <div class="mt-4"> - <h4 class="text-subtitle-2 mb-2"> - {{ tm('form.selectedSkills') }} - <span v-if="personaForm.skills === null" class="text-success"> - ({{ tm('form.allSelected') }}) - </span> - <span v-else-if="Array.isArray(personaForm.skills)"> - ({{ personaForm.skills.length }}) - </span> - </h4> - <div v-if="Array.isArray(personaForm.skills) && personaForm.skills.length > 0" - class="d-flex flex-wrap ga-1" style="max-height: 100px; overflow-y: auto;"> - <v-chip v-for="skillName in personaForm.skills" :key="skillName" - size="small" color="primary" variant="tonal" closable - @click:close="removeSkill(skillName)"> - {{ skillName }} - </v-chip> - </div> - <div v-else class="text-body-2 text-medium-emphasis"> - {{ tm('form.noSkillsSelected') }} - </div> - </div> - </div> - </v-expansion-panel-text> - </v-expansion-panel> - - <!-- 预设对话面板 --> - <v-expansion-panel value="dialogs"> - <v-expansion-panel-title> - <v-icon class="mr-2">mdi-chat</v-icon> - {{ tm('form.presetDialogs') }} - <v-chip v-if="personaForm.begin_dialogs.length > 0" size="small" color="primary" - variant="tonal" class="ml-2"> - {{ personaForm.begin_dialogs.length / 2 }} - </v-chip> - </v-expansion-panel-title> - - <v-expansion-panel-text> - <div class="mb-3"> - <p class="text-body-2 text-medium-emphasis"> - {{ tm('form.presetDialogsHelp') }} - </p> - </div> - - <div v-for="(dialog, index) in personaForm.begin_dialogs" :key="index" class="mb-3"> - <v-textarea v-model="personaForm.begin_dialogs[index]" - :label="index % 2 === 0 ? tm('form.userMessage') : tm('form.assistantMessage')" - :rules="getDialogRules(index)" variant="outlined" rows="2" - density="comfortable"> - <template v-slot:append> - <v-btn icon="mdi-delete" variant="text" size="small" color="error" - @click="removeDialog(index)" /> - </template> - </v-textarea> - </div> - - <v-btn variant="tonal" prepend-icon="mdi-plus" @click="addDialogPair" block> - {{ tm('buttons.addDialogPair') }} - </v-btn> - </v-expansion-panel-text> - </v-expansion-panel> - </v-expansion-panels> - </v-col> - </v-row> - </v-form> - </v-card-text> - - <v-card-actions class="persona-form-actions"> - <v-btn v-if="editingPersona" color="error" variant="text" @click="deletePersona"> - {{ tm('buttons.delete') }} - </v-btn> - <v-spacer /> - <v-btn color="grey" variant="text" @click="closeDialog"> - {{ tm('buttons.cancel') }} - </v-btn> - <v-btn color="primary" variant="tonal" @click="savePersona" :loading="saving" :disabled="!formValid"> - {{ tm('buttons.save') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script> -import { mcpApi, personaApi, skillApi, toolApi } from '@/api/v1'; -import { useModuleI18n } from '@/i18n/composables'; -import { - askForConfirmation as askForConfirmationDialog, - useConfirmDialog -} from '@/utils/confirmDialog'; - -export default { - name: 'PersonaForm', - props: { - modelValue: { - type: Boolean, - default: false - }, - editingPersona: { - type: Object, - default: null - }, - currentFolderId: { - type: String, - default: null - }, - currentFolderName: { - type: String, - default: null - } - }, - emits: ['update:modelValue', 'saved', 'error', 'deleted'], - setup() { - const { tm } = useModuleI18n('features/persona'); - const confirmDialog = useConfirmDialog(); - return { tm, confirmDialog }; - }, - data() { - return { - toolSelectValue: '0', // 默认选择全部工具 - saving: false, - expandedPanels: [], - formValid: false, - mcpServers: [], - availableTools: [], - loadingTools: false, - availableSkills: [], - loadingSkills: false, - existingPersonaIds: [], // 已存在的人格ID列表 - personaForm: { - persona_id: '', - system_prompt: '', - custom_error_message: '', - begin_dialogs: [], - tools: [], - skills: [], - folder_id: null - }, - personaIdRules: [ - v => !!v || this.tm('validation.required'), - v => (v && v.length >= 1) || this.tm('validation.minLength', { min: 1 }), - v => this.editingPersona?.persona_id === v || !this.existingPersonaIds.includes(v) || this.tm('validation.personaIdExists'), - ], - systemPromptRules: [ - v => !!v || this.tm('validation.required'), - v => (v && v.length >= 10) || this.tm('validation.minLength', { min: 10 }) - ], - toolSearch: '', - skillSearch: '', - skillSelectValue: '0' - } - }, - - computed: { - showDialog: { - get() { - return this.modelValue; - }, - set(value) { - this.$emit('update:modelValue', value); - } - }, - filteredTools() { - if (!this.toolSearch) { - return this.availableTools; - } - const search = this.toolSearch.toLowerCase(); - return this.availableTools.filter(tool => - tool.name.toLowerCase().includes(search) || - (tool.description && tool.description.toLowerCase().includes(search)) || - (tool.mcp_server_name && tool.mcp_server_name.toLowerCase().includes(search)) - ); - }, - filteredSkills() { - if (!this.skillSearch) { - return this.availableSkills; - } - const search = this.skillSearch.toLowerCase(); - return this.availableSkills.filter(skill => - skill.name.toLowerCase().includes(search) || - (skill.description && skill.description.toLowerCase().includes(search)) - ); - }, - folderDisplayName() { - // 优先使用传入的文件夹名称 - if (this.currentFolderName) { - return this.currentFolderName; - } - // 如果没有文件夹 ID,显示根目录 - if (!this.currentFolderId) { - return this.tm('form.rootFolder'); - } - // 否则显示文件夹 ID(作为备用) - return this.currentFolderId; - } - }, - - watch: { - modelValue(newValue) { - if (newValue) { - // 只有在不是编辑状态时才初始化空表单 - if (this.editingPersona) { - this.initFormWithPersona(this.editingPersona); - } else { - this.initForm(); - // 只在创建新人格时加载已存在的人格列表 - this.loadExistingPersonaIds(); - } - this.loadMcpServers(); - this.loadTools(); - this.loadSkills(); - } - }, - editingPersona: { - immediate: true, - handler(newPersona) { - // 只有在对话框打开时才处理editingPersona的变化 - if (this.modelValue) { - if (newPersona) { - this.initFormWithPersona(newPersona); - } else { - this.initForm(); - } - } - } - }, - toolSelectValue(newValue) { - if (newValue === '0') { - // 选择全部工具 - this.personaForm.tools = null; - } else if (newValue === '1') { - // 选择指定工具,如果当前是null,则转换为空数组 - if (this.personaForm.tools === null) { - this.personaForm.tools = []; - } - } - }, - skillSelectValue(newValue) { - if (newValue === '0') { - this.personaForm.skills = null; - } else if (newValue === '1') { - if (this.personaForm.skills === null) { - this.personaForm.skills = []; - } - } - } - }, - - methods: { - initForm() { - this.personaForm = { - persona_id: '', - system_prompt: '', - custom_error_message: '', - begin_dialogs: [], - tools: [], - skills: [], - folder_id: this.currentFolderId - }; - this.toolSelectValue = '0'; - this.skillSelectValue = '0'; - this.expandedPanels = this.getDefaultExpandedPanels(); - }, - - initFormWithPersona(persona) { - this.personaForm = { - persona_id: persona.persona_id, - system_prompt: persona.system_prompt, - custom_error_message: persona.custom_error_message || '', - begin_dialogs: [...(persona.begin_dialogs || [])], - tools: persona.tools === null ? null : [...(persona.tools || [])], - skills: persona.skills === null ? null : [...(persona.skills || [])], - folder_id: persona.folder_id - }; - // 根据 tools 的值设置 toolSelectValue - this.toolSelectValue = persona.tools === null ? '0' : '1'; - this.skillSelectValue = persona.skills === null ? '0' : '1'; - this.expandedPanels = this.getDefaultExpandedPanels(); - }, - - getDefaultExpandedPanels() { - return this.$vuetify.display.smAndDown ? [] : ['tools', 'skills', 'dialogs']; - }, - - closeDialog() { - this.showDialog = false; - }, - - async loadMcpServers() { - try { - const response = await mcpApi.list(); - if (response.data.status === 'ok') { - this.mcpServers = response.data.data || []; - } else { - this.$emit('error', response.data.message || 'Failed to load MCP servers'); - } - } catch (error) { - this.$emit('error', error.response?.data?.message || 'Failed to load MCP servers'); - this.mcpServers = []; - } - }, - - async loadTools() { - this.loadingTools = true; - try { - const response = await toolApi.list(); - if (response.data.status === 'ok') { - this.availableTools = response.data.data || []; - } else { - this.$emit('error', response.data.message || 'Failed to load tools'); - } - } catch (error) { - this.$emit('error', error.response?.data?.message || 'Failed to load tools'); - this.availableTools = []; - } finally { - this.loadingTools = false; - } - }, - - async loadSkills() { - this.loadingSkills = true; - try { - const response = await skillApi.list(); - if (response.data.status === 'ok') { - const payload = response.data.data || []; - if (Array.isArray(payload)) { - this.availableSkills = payload.filter(skill => skill.active !== false); - } else { - const skills = payload.skills || []; - this.availableSkills = skills.filter(skill => skill.active !== false); - } - } else { - this.$emit('error', response.data.message || 'Failed to load skills'); - } - } catch (error) { - this.$emit('error', error.response?.data?.message || 'Failed to load skills'); - this.availableSkills = []; - } finally { - this.loadingSkills = false; - } - }, - - async loadExistingPersonaIds() { - try { - const response = await personaApi.list(); - if (response.data.status === 'ok') { - this.existingPersonaIds = (response.data.data || []).map(p => p.persona_id); - } - } catch (error) { - // 加载失败不影响表单使用,只是无法进行前端重名校验 - this.existingPersonaIds = []; - } - }, - - async savePersona() { - if (!this.formValid) return; - - // 验证预设对话不能为空 - if (this.personaForm.begin_dialogs.length > 0) { - for (let i = 0; i < this.personaForm.begin_dialogs.length; i++) { - if (!this.personaForm.begin_dialogs[i] || this.personaForm.begin_dialogs[i].trim() === '') { - const dialogType = i % 2 === 0 ? this.tm('form.userMessage') : this.tm('form.assistantMessage'); - this.$emit('error', this.tm('validation.dialogRequired', { type: dialogType })); - return; - } - } - } - - this.saving = true; - try { - const response = this.editingPersona - ? await personaApi.update(this.personaForm.persona_id, this.personaForm) - : await personaApi.create(this.personaForm); - - if (response.data.status === 'ok') { - this.$emit('saved', response.data.message || this.tm('messages.saveSuccess')); - this.closeDialog(); - } else { - this.$emit('error', response.data.message || this.tm('messages.saveError')); - } - } catch (error) { - this.$emit('error', error.response?.data?.message || this.tm('messages.saveError')); - } - this.saving = false; - }, - - async deletePersona() { - if (!this.editingPersona) return; - - if ( - !(await askForConfirmationDialog( - this.tm('messages.deleteConfirm', { id: this.editingPersona.persona_id }), - this.confirmDialog, - )) - ) { - return; - } - - this.saving = true; - try { - const response = await personaApi.delete(this.editingPersona.persona_id); - - if (response.data.status === 'ok') { - this.$emit('deleted', response.data.message || this.tm('messages.deleteSuccess')); - this.closeDialog(); - } else { - this.$emit('error', response.data.message || this.tm('messages.deleteError')); - } - } catch (error) { - this.$emit('error', error.response?.data?.message || this.tm('messages.deleteError')); - } finally { - this.saving = false; - } - }, - - addDialogPair() { - this.personaForm.begin_dialogs.push('', ''); - // 自动展开预设对话面板 - if (!this.expandedPanels.includes('dialogs')) { - this.expandedPanels.push('dialogs'); - } - }, - - removeDialog(index) { - // 如果是偶数索引(用户消息),删除用户消息和对应的助手消息 - if (index % 2 === 0 && index + 1 < this.personaForm.begin_dialogs.length) { - this.personaForm.begin_dialogs.splice(index, 2); - } - // 如果是奇数索引(助手消息),删除助手消息和对应的用户消息 - else if (index % 2 === 1 && index - 1 >= 0) { - this.personaForm.begin_dialogs.splice(index - 1, 2); - } - }, - - toggleMcpServer(server) { - if (!server.tools || server.tools.length === 0) return; - - // 如果当前是全选状态,需要先转换为具体的工具列表 - if (this.personaForm.tools === null) { - // 从全选状态转换为去除该服务器工具的状态 - this.personaForm.tools = this.availableTools.map(tool => tool.name) - .filter(toolName => !server.tools.includes(toolName)); - this.toolSelectValue = '1'; // 切换到指定工具模式 - return; - } - - // 确保tools是数组 - if (!Array.isArray(this.personaForm.tools)) { - this.personaForm.tools = []; - this.toolSelectValue = '1'; - } - - // 检查是否所有服务器的工具都已选中 - const serverTools = server.tools; - const allSelected = serverTools.every(toolName => this.personaForm.tools.includes(toolName)); - - if (allSelected) { - // 移除所有服务器工具 - this.personaForm.tools = this.personaForm.tools.filter( - toolName => !serverTools.includes(toolName) - ); - } else { - // 添加所有服务器工具 - serverTools.forEach(toolName => { - if (!this.personaForm.tools.includes(toolName)) { - this.personaForm.tools.push(toolName); - } - }); - } - }, - - toggleTool(toolName) { - if (this.isBuiltinToolName(toolName)) { - return; - } - // 如果当前是全选状态,需要先转换为具体的工具列表 - if (this.personaForm.tools === null) { - // 如果是全选状态,点击某个工具表示要取消选择该工具 - // 所以创建一个包含所有其他工具的数组 - this.personaForm.tools = this.availableTools.map(tool => tool.name).filter(name => name !== toolName); - this.toolSelectValue = '1'; // 切换到指定工具模式 - } else if (Array.isArray(this.personaForm.tools)) { - const index = this.personaForm.tools.indexOf(toolName); - if (index !== -1) { - // 如果工具已选择,移除工具 - this.personaForm.tools.splice(index, 1); - } else { - // 如果工具未选择,添加工具 - this.personaForm.tools.push(toolName); - } - } else { - // 如果tools不是数组也不是null,初始化为数组 - this.personaForm.tools = [toolName]; - this.toolSelectValue = '1'; - } - }, - - removeTool(toolName) { - if (this.isBuiltinToolName(toolName)) { - return; - } - // 如果当前是全选状态,需要先转换为具体的工具列表 - if (this.personaForm.tools === null) { - // 创建一个包含所有工具的数组,然后移除指定工具 - this.personaForm.tools = this.availableTools.map(tool => tool.name).filter(name => name !== toolName); - this.toolSelectValue = '1'; // 切换到指定工具模式 - } else if (Array.isArray(this.personaForm.tools)) { - const index = this.personaForm.tools.indexOf(toolName); - if (index !== -1) { - this.personaForm.tools.splice(index, 1); - } - } - }, - - toggleSkill(skillName) { - if (this.personaForm.skills === null) { - this.personaForm.skills = this.availableSkills.map(skill => skill.name) - .filter(name => name !== skillName); - this.skillSelectValue = '1'; - } else if (Array.isArray(this.personaForm.skills)) { - const index = this.personaForm.skills.indexOf(skillName); - if (index !== -1) { - this.personaForm.skills.splice(index, 1); - } else { - this.personaForm.skills.push(skillName); - } - } else { - this.personaForm.skills = [skillName]; - this.skillSelectValue = '1'; - } - }, - - removeSkill(skillName) { - if (this.personaForm.skills === null) { - this.personaForm.skills = this.availableSkills.map(skill => skill.name) - .filter(name => name !== skillName); - this.skillSelectValue = '1'; - } else if (Array.isArray(this.personaForm.skills)) { - const index = this.personaForm.skills.indexOf(skillName); - if (index !== -1) { - this.personaForm.skills.splice(index, 1); - } - } - }, - - truncateText(text, maxLength) { - if (!text) return ''; - return text.length > maxLength ? text.substring(0, maxLength) + '...' : text; - }, - - isBuiltinTool(tool) { - return tool?.origin === 'builtin' || tool?.readonly === true; - }, - - isBuiltinToolName(toolName) { - return this.availableTools.some(tool => tool.name === toolName && this.isBuiltinTool(tool)); - }, - - getDialogRules(index) { - const dialogType = index % 2 === 0 ? this.tm('form.userMessage') : this.tm('form.assistantMessage'); - return [ - v => !!v || this.tm('validation.dialogRequired', { type: dialogType }), - v => (v && v.trim().length > 0) || this.tm('validation.dialogRequired', { type: dialogType }) - ]; - }, - - isToolSelected(toolName) { - // 如果是全选状态,所有工具都被选中 - if (this.personaForm.tools === null) { - return true; - } - return Array.isArray(this.personaForm.tools) && this.personaForm.tools.includes(toolName); - }, - - isSkillSelected(skillName) { - if (this.personaForm.skills === null) { - return true; - } - return Array.isArray(this.personaForm.skills) && this.personaForm.skills.includes(skillName); - }, - - isServerSelected(server) { - if (!server.tools || server.tools.length === 0) return false; - - // 如果是全选状态,所有服务器都被选中 - if (this.personaForm.tools === null) { - return true; - } - - // 检查服务器的所有工具是否都已选中 - return Array.isArray(this.personaForm.tools) && - server.tools.every(toolName => this.personaForm.tools.includes(toolName)); - } - } -} -</script> - -<style scoped> -.persona-form-card { - border-radius: 12px; - overflow: hidden; -} - -.persona-form-content { - max-height: min(78vh, 760px); - overflow-y: auto; -} - -.persona-form-actions { - position: sticky; - bottom: 0; - z-index: 2; - background: rgb(var(--v-theme-surface)); - border-top: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); -} - -.selected-config-area { - margin-left: 32px; -} - -.persona-form-layout { - align-items: flex-start; -} - -.tools-selection { - max-height: 300px; - overflow-y: auto; -} - -.builtin-tool-checkbox-placeholder { - width: 40px; - height: 40px; - flex: 0 0 40px; -} - -.skills-selection { - max-height: 300px; - overflow-y: auto; -} - -.v-virtual-scroll { - padding-bottom: 16px; -} - -@media (max-width: 600px) { - .persona-form-card-mobile { - border-radius: 0; - } - - .persona-form-content { - max-height: calc(100vh - 128px); - padding: 16px !important; - } - - .persona-basic-col, - .persona-panels-col { - padding-top: 0 !important; - } - - .selected-config-area { - margin-left: 0; - } - - .tools-selection, - .skills-selection { - max-height: 38vh; - } - - .persona-form-actions { - padding: 12px 16px !important; - gap: 8px; - } - - .persona-form-actions .v-btn { - min-width: 0; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/PersonaQuickPreview.vue b/legacy-dashboard/src/components/shared/PersonaQuickPreview.vue deleted file mode 100644 index 3d1fb7b6..00000000 --- a/legacy-dashboard/src/components/shared/PersonaQuickPreview.vue +++ /dev/null @@ -1,303 +0,0 @@ -<template> - <div class="persona-preview-card"> - <div class="preview-header"> - <small>{{ tm('personaQuickPreview.title') }}</small> - </div> - - <div v-if="loading" class="preview-loading"> - <v-progress-circular indeterminate size="18" width="2" color="primary" class="mr-2" /> - <small class="text-grey">{{ tm('personaQuickPreview.loading') }}</small> - </div> - - <div v-else-if="!modelValue" class="preview-empty"> - <small class="text-grey">{{ tm('personaQuickPreview.noPersonaSelected') }}</small> - </div> - - <div v-else-if="!personaData" class="preview-empty"> - <small class="text-grey">{{ tm('personaQuickPreview.personaNotFound') }}</small> - </div> - - <div v-else class="preview-content"> - <div class="section-title">{{ tm('personaQuickPreview.systemPromptLabel') }}</div> - <pre class="prompt-content">{{ personaData.system_prompt || '' }}</pre> - - <div class="section-title mt-3">{{ tm('personaQuickPreview.toolsLabel') }}</div> - <div class="chip-wrap tools-wrap"> - <v-chip - v-if="personaData.tools === null" - size="small" - color="success" - variant="tonal" - label - > - {{ tm('personaQuickPreview.allToolsWithCount', { count: allToolsCount }) }} - </v-chip> - <div v-for="tool in resolvedTools" v-else :key="tool.name" class="tool-item"> - <v-chip - size="small" - :color="tool.active === false ? 'warning' : 'primary'" - variant="outlined" - label - > - {{ tool.name }} - </v-chip> - <v-tooltip v-if="tool.active === false" location="top"> - <template v-slot:activator="{ props: tooltipProps }"> - <small class="text-warning tool-inactive" v-bind="tooltipProps"> - {{ tm('personaQuickPreview.toolInactive') }} - </small> - </template> - {{ tm('personaQuickPreview.toolInactiveTooltip') }} - </v-tooltip> - <small v-if="tool.origin || tool.origin_name" class="text-grey tool-meta"> - <span v-if="tool.origin">{{ tm('personaQuickPreview.originLabel') }}: {{ tool.origin }}</span> - <span v-if="tool.origin_name"> | {{ tm('personaQuickPreview.originNameLabel') }}: {{ tool.origin_name }}</span> - </small> - </div> - <small v-if="personaData.tools !== null && normalizedTools.length === 0" class="text-grey"> - {{ tm('personaQuickPreview.noTools') }} - </small> - </div> - - <div class="section-title mt-3">{{ tm('personaQuickPreview.skillsLabel') }}</div> - <div class="chip-wrap"> - <v-chip - v-if="personaData.skills === null" - size="small" - color="success" - variant="tonal" - label - > - {{ tm('personaQuickPreview.allSkillsWithCount', { count: allSkillsCount }) }} - </v-chip> - <v-chip - v-for="skillName in normalizedSkills" - v-else - :key="skillName" - size="small" - color="primary" - variant="outlined" - label - > - {{ skillName }} - </v-chip> - <small v-if="personaData.skills !== null && normalizedSkills.length === 0" class="text-grey"> - {{ tm('personaQuickPreview.noSkills') }} - </small> - </div> - </div> - </div> -</template> - -<script setup> -import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue' -import { personaApi, skillApi, toolApi } from '@/api/v1' -import { useModuleI18n } from '@/i18n/composables' - -const props = defineProps({ - modelValue: { - type: String, - default: '' - } -}) - -const { tm } = useModuleI18n('core.shared') - -const loading = ref(false) -const personaData = ref(null) -const toolMetaMap = ref({}) -const availableSkills = ref([]) - -const defaultPersonaData = { - persona_id: 'default', - system_prompt: 'You are a helpful and friendly assistant.', - tools: null, - skills: null -} - -const normalizedTools = computed(() => (Array.isArray(personaData.value?.tools) ? personaData.value.tools : [])) -const normalizedSkills = computed(() => (Array.isArray(personaData.value?.skills) ? personaData.value.skills : [])) -const allToolsCount = computed(() => - Object.values(toolMetaMap.value).filter((tool) => tool.origin !== 'builtin').length -) -const allSkillsCount = computed(() => availableSkills.value.length) -const resolvedTools = computed(() => - normalizedTools.value.map((toolName) => { - const meta = toolMetaMap.value[toolName] || {} - return { - name: toolName, - origin: meta.origin || '', - origin_name: meta.origin_name || '', - active: meta.active - } - }) -) - -async function loadToolsMeta() { - try { - const response = await toolApi.list() - if (response.data?.status === 'ok') { - const tools = response.data?.data || [] - const nextMap = {} - for (const tool of tools) { - if (!tool?.name) { - continue - } - nextMap[tool.name] = { - origin: tool.origin || '', - origin_name: tool.origin_name || '', - active: tool.active - } - } - toolMetaMap.value = nextMap - } - } catch (error) { - console.error('Failed to load tools metadata:', error) - toolMetaMap.value = {} - } -} - -async function loadSkillsMeta() { - try { - const response = await skillApi.list() - if (response.data?.status === 'ok') { - const payload = response.data?.data || [] - if (Array.isArray(payload)) { - availableSkills.value = payload.filter((skill) => skill.active !== false) - } else { - const skills = payload.skills || [] - availableSkills.value = skills.filter((skill) => skill.active !== false) - } - } else { - availableSkills.value = [] - } - } catch (error) { - console.error('Failed to load skills metadata:', error) - availableSkills.value = [] - } -} - -async function loadPersonaPreview(personaId) { - if (!personaId) { - personaData.value = null - return - } - - if (personaId === 'default') { - personaData.value = defaultPersonaData - return - } - - loading.value = true - try { - const response = await personaApi.list() - if (response.data?.status === 'ok') { - const personas = response.data?.data || [] - personaData.value = personas.find((item) => item.persona_id === personaId) || null - } else { - personaData.value = null - } - } catch (error) { - console.error('Failed to load persona preview:', error) - personaData.value = null - } finally { - loading.value = false - } -} - -function handlePersonaSaved() { - if (props.modelValue) { - loadPersonaPreview(props.modelValue) - } -} - -watch( - () => props.modelValue, - (newValue) => { - loadPersonaPreview(newValue) - }, - { immediate: true } -) - -loadToolsMeta() -loadSkillsMeta() - -onMounted(() => { - window.addEventListener('astrbot:persona-saved', handlePersonaSaved) -}) - -onBeforeUnmount(() => { - window.removeEventListener('astrbot:persona-saved', handlePersonaSaved) -}) -</script> - -<style scoped> -.persona-preview-card { - background-color: rgba(var(--v-theme-primary), 0.05); - border: 1px solid rgba(var(--v-theme-primary), 0.1); - border-radius: 8px; - padding: 12px; -} - -.preview-header { - margin-bottom: 8px; -} - -.preview-loading, -.preview-empty { - display: flex; - align-items: center; - min-height: 24px; -} - -.section-title { - font-size: 0.75rem; - color: rgb(var(--v-theme-primaryText)); - opacity: 0.85; -} - -.prompt-content { - margin-top: 6px; - max-height: 180px; - overflow: auto; - font-size: 0.78rem; - line-height: 1.45; - white-space: pre-wrap; - word-break: break-word; - background: rgba(0, 0, 0, 0.03); - border-radius: 6px; - padding: 8px; -} - -.chip-wrap { - display: grid; - gap: 6px; - margin-top: 6px; -} - -.tools-wrap { - max-height: 160px; - overflow: auto; -} - -.tool-item { - display: flex; - align-items: center; - flex-wrap: wrap; - gap: 6px; -} - -.tool-meta { - font-size: 0.74rem; -} - -.tool-inactive { - font-size: 0.74rem; -} - -@media (max-width: 600px) { - .tools-wrap { - max-height: 120px; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/PersonaSelector.vue b/legacy-dashboard/src/components/shared/PersonaSelector.vue deleted file mode 100644 index a454d527..00000000 --- a/legacy-dashboard/src/components/shared/PersonaSelector.vue +++ /dev/null @@ -1,209 +0,0 @@ -<template> - <BaseFolderItemSelector - :model-value="modelValue" - @update:model-value="handleUpdate" - :folder-tree="folderTree" - :items="currentPersonas as any" - :tree-loading="treeLoading" - :items-loading="itemsLoading" - :labels="labels" - :show-create-button="true" - :show-edit-button="true" - :default-item="defaultPersona" - item-id-field="persona_id" - item-name-field="persona_id" - item-description-field="system_prompt" - :display-value-formatter="formatDisplayValue" - @navigate="handleNavigate" - @create="openCreatePersona" - @edit="openEditPersona" - /> - - <!-- 创建/编辑人格对话框 --> - <PersonaForm - v-model="showPersonaDialog" - :editing-persona="editingPersona ?? undefined" - :current-folder-id="currentFolderId ?? undefined" - :current-folder-name="currentFolderName ?? undefined" - @saved="handlePersonaSaved" - @error="handleError" /> -</template> - -<script setup lang="ts"> -import { ref, computed, onMounted } from 'vue' -import { personaApi } from '@/api/v1' -import BaseFolderItemSelector from '@/components/folder/BaseFolderItemSelector.vue' -import PersonaForm from './PersonaForm.vue' -import { useI18n, useModuleI18n } from '@/i18n/composables' -import type { FolderTreeNode, SelectableItem } from '@/components/folder/types' - -interface Persona { - persona_id: string - system_prompt: string - custom_error_message?: string | null - folder_id?: string | null - [key: string]: any -} - -const props = defineProps({ - modelValue: { - type: String, - default: '' - }, - buttonText: { - type: String, - default: '' - } -}) - -const emit = defineEmits(['update:modelValue']) -const { t } = useI18n() -const { tm } = useModuleI18n('core.shared') - -// 状态 -const folderTree = ref<FolderTreeNode[]>([]) -const currentPersonas = ref<Persona[]>([]) -const treeLoading = ref(false) -const itemsLoading = ref(false) -const showPersonaDialog = ref(false) -const editingPersona = ref<Persona | null>(null) -const currentFolderId = ref<string | null>(null) - -// 默认人格 -const defaultPersona: SelectableItem = { - id: 'default', - persona_id: 'default', - name: tm('personaSelector.defaultPersona'), - system_prompt: 'You are a helpful and friendly assistant.' -} - -// 递归查找文件夹名称 -function findFolderName(nodes: FolderTreeNode[], folderId: string): string | null { - for (const node of nodes) { - if (node.folder_id === folderId) { - return node.name - } - if (node.children && node.children.length > 0) { - const found = findFolderName(node.children, folderId) - if (found) return found - } - } - return null -} - -// 当前文件夹名称 -const currentFolderName = computed(() => { - if (!currentFolderId.value) { - return null // 根目录,PersonaForm 会使用 tm('form.rootFolder') - } - return findFolderName(folderTree.value, currentFolderId.value) -}) - -// 标签配置 -const labels = computed(() => ({ - dialogTitle: tm('personaSelector.dialogTitle'), - notSelected: tm('personaSelector.notSelected'), - buttonText: props.buttonText || tm('personaSelector.buttonText'), - noItems: tm('personaSelector.noPersonas'), - defaultItem: tm('personaSelector.defaultPersona'), - noDescription: tm('personaSelector.noDescription'), - createButton: tm('personaSelector.createPersona'), - editButton: tm('personaSelector.editPersona') || 'Edit', - confirmButton: t('core.common.confirm'), - cancelButton: t('core.common.cancel'), - rootFolder: tm('personaSelector.rootFolder') || '全部人格', - emptyFolder: tm('personaSelector.emptyFolder') || '此文件夹为空' -})) - -// 格式化显示值 -function formatDisplayValue(value: string): string { - if (value === 'default') { - return tm('personaSelector.defaultPersona') - } - return value -} - -// 处理值更新 -function handleUpdate(value: string) { - emit('update:modelValue', value) -} - -// 加载文件夹树 -async function loadFolderTree() { - treeLoading.value = true - try { - const response = await personaApi.tree() - if (response.data.status === 'ok') { - folderTree.value = response.data.data || [] - } - } catch (error) { - console.error('加载文件夹树失败:', error) - folderTree.value = [] - } finally { - treeLoading.value = false - } -} - -// 加载指定文件夹的人格 -async function loadPersonasInFolder(folderId: string | null) { - itemsLoading.value = true - try { - const response = await personaApi.list(folderId) - if (response.data.status === 'ok') { - currentPersonas.value = response.data.data || [] - } - } catch (error) { - console.error('加载人格列表失败:', error) - currentPersonas.value = [] - } finally { - itemsLoading.value = false - } -} - -// 处理文件夹导航 -async function handleNavigate(folderId: string | null) { - currentFolderId.value = folderId - await loadPersonasInFolder(folderId) -} - -// 打开创建人格对话框 -function openCreatePersona() { - editingPersona.value = null - showPersonaDialog.value = true -} - -// 打开编辑人格对话框 -function openEditPersona(persona: Persona) { - editingPersona.value = persona - showPersonaDialog.value = true -} - -// 人格保存成功(创建或编辑) -async function handlePersonaSaved(message: string) { - console.log('人格保存成功:', message) - const savedPersonaId = editingPersona.value?.persona_id || '' - showPersonaDialog.value = false - editingPersona.value = null - // 刷新当前文件夹的人格列表 - await loadPersonasInFolder(currentFolderId.value) - window.dispatchEvent( - new CustomEvent('astrbot:persona-saved', { - detail: { persona_id: savedPersonaId } - }) - ) -} - -// 错误处理 -function handleError(error: string) { - console.error('创建人格失败:', error) -} - -// 初始化加载文件夹树 -onMounted(() => { - loadFolderTree() -}) -</script> - -<style scoped> -/* 样式继承自 BaseFolderItemSelector */ -</style> diff --git a/legacy-dashboard/src/components/shared/PluginPlatformChip.vue b/legacy-dashboard/src/components/shared/PluginPlatformChip.vue deleted file mode 100644 index afb4615a..00000000 --- a/legacy-dashboard/src/components/shared/PluginPlatformChip.vue +++ /dev/null @@ -1,124 +0,0 @@ -<script setup lang="ts"> -import { ref, computed } from "vue"; -import { getPlatformDisplayName, getPlatformIcon } from "@/utils/platformUtils"; -import { useModuleI18n } from "@/i18n/composables"; - -const props = defineProps({ - platforms: { - type: Array, - default: () => [], - }, - size: { - type: String, - default: "small", - }, - chipStyle: { - type: Object, - default: () => ({}), - }, -}); - -const { tm } = useModuleI18n("features/extension"); - -const showMenu = ref(false); - -const platformDetails = computed(() => { - if (!Array.isArray(props.platforms)) return []; - return props.platforms - .filter((item) => typeof item === "string") - .map((platformId) => ({ - name: getPlatformDisplayName(platformId as string), - icon: getPlatformIcon(platformId as string), - })); -}); -</script> - -<template> - <div class="d-inline-block"> - <v-chip - v-if="platformDetails.length" - color="info" - variant="outlined" - label - :size="size" - class="plugin-platform-chip" - :style="{ cursor: 'pointer', ...chipStyle }" - @click.stop="showMenu = !showMenu" - > - <div class="d-flex align-center" style="gap: 2px"> - <!-- 显示图标,最多 5 个 --> - <div class="d-flex align-center mr-1" v-if="platformDetails.some(p => p.icon)"> - <v-avatar - v-for="(platform, index) in platformDetails.slice(0, 5)" - :key="index" - :size="size === 'x-small' ? 12 : 14" - class="platform-mini-icon" - :style="{ marginLeft: index > 0 ? '-4px' : '0', zIndex: 10 - index }" - > - <v-img v-if="platform.icon" :src="platform.icon"></v-img> - <v-icon v-else icon="mdi-circle-small" :size="size === 'x-small' ? 8 : 10"></v-icon> - </v-avatar> - </div> - - <span class="text-caption font-weight-bold"> - {{ - tm("card.status.supportPlatformsCount", { - count: platformDetails.length, - }) - }} - </span> - - <v-icon - :icon="showMenu ? 'mdi-chevron-up' : 'mdi-chevron-down'" - :size="size === 'x-small' ? 14 : 16" - class="ml-n1" - ></v-icon> - </div> - - <v-menu - v-model="showMenu" - activator="parent" - location="top" - :close-on-content-click="false" - transition="scale-transition" - open-on-hover - > - <v-list density="compact" border elevation="12" class="rounded-lg pa-1"> - <v-list-item - v-for="platform in platformDetails" - :key="platform.name" - min-height="24" - class="px-2" - > - <template v-slot:prepend> - <v-avatar size="14" class="mr-2" v-if="platform.icon"> - <v-img :src="platform.icon"></v-img> - </v-avatar> - <v-icon v-else icon="mdi-puzzle-outline" size="12" class="mr-2"></v-icon> - </template> - <v-list-item-title class="text-caption font-weight-bold" style="font-size: 0.75rem !important"> - {{ platform.name }} - </v-list-item-title> - </v-list-item> - </v-list> - </v-menu> - </v-chip> - </div> -</template> - -<style scoped> -.plugin-platform-chip { - padding-left: 6px !important; - padding-right: 4px !important; - transition: all 0.2s ease; -} - -.platform-mini-icon { - border: 1px solid rgba(var(--v-theme-info), 0.3); - background: rgba(var(--v-theme-surface)); -} - -.plugin-platform-chip:hover { - background: rgba(var(--v-theme-info), 0.08); -} -</style> diff --git a/legacy-dashboard/src/components/shared/PluginSetSelector.vue b/legacy-dashboard/src/components/shared/PluginSetSelector.vue deleted file mode 100644 index 8ab83b8e..00000000 --- a/legacy-dashboard/src/components/shared/PluginSetSelector.vue +++ /dev/null @@ -1,238 +0,0 @@ -<template> - <div> - <!-- 顶部操作区域 --> - <div class="d-flex align-center justify-space-between mb-2"> - <div class="flex-grow-1"> - <span v-if="!modelValue || modelValue.length === 0" style="color: rgb(var(--v-theme-primaryText));"> - {{ tm('pluginSetSelector.notSelected') }} - </span> - <span v-else-if="isAllPlugins" style="color: rgb(var(--v-theme-primaryText));"> - {{ tm('pluginSetSelector.allPlugins') }} - </span> - <span v-else style="color: rgb(var(--v-theme-primaryText));"> - {{ tm('pluginSetSelector.selectedCount', { count: modelValue.length }) }} - </span> - </div> - <v-btn size="small" color="primary" variant="tonal" @click="openDialog"> - {{ buttonText || tm('pluginSetSelector.buttonText') }} - </v-btn> - </div> - </div> - - <!-- Plugin Set Selection Dialog --> - <v-dialog v-model="dialog" max-width="700px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6"> - {{ tm('pluginSetSelector.dialogTitle') }} - </v-card-title> - - <v-card-text class="pa-4"> - <v-progress-linear v-if="loading" indeterminate color="primary"></v-progress-linear> - - <div v-if="!loading"> - <!-- 预设选项 --> - <v-radio-group v-model="selectionMode" class="mb-4" hide-details> - <v-radio - value="all" - :label="tm('pluginSetSelector.enableAll')" - color="primary" - ></v-radio> - <v-radio - value="none" - :label="tm('pluginSetSelector.enableNone')" - color="primary" - ></v-radio> - <v-radio - value="custom" - :label="tm('pluginSetSelector.customSelect')" - color="primary" - ></v-radio> - </v-radio-group> - - <!-- 自定义选择时显示插件列表 --> - <div v-if="selectionMode === 'custom'" style="max-height: 300px; overflow-y: auto;"> - <v-list v-if="pluginList.length > 0" density="compact"> - <v-list-item - v-for="plugin in pluginList" - :key="plugin.name" - rounded="md" - class="ma-1"> - <template v-slot:prepend> - <v-checkbox - v-model="selectedPlugins" - :value="plugin.name" - color="primary" - hide-details - ></v-checkbox> - </template> - - <v-list-item-title>{{ pluginDisplayName(plugin) }}</v-list-item-title> - <v-list-item-subtitle> - {{ pluginDescription(plugin) || tm('pluginSetSelector.noDescription') }} - <v-chip v-if="!plugin.activated" size="x-small" color="grey" class="ml-1"> - {{ tm('pluginSetSelector.notActivated') }} - </v-chip> - </v-list-item-subtitle> - </v-list-item> - - <div class="pl-8 pt-2"> - <small>{{ tm('pluginSetSelector.note') }}</small> - </div> - </v-list> - - <div v-else class="text-center py-8"> - <v-icon size="64" color="grey-lighten-1">mdi-puzzle-outline</v-icon> - <p class="text-grey mt-4">{{ tm('pluginSetSelector.noPlugins') }}</p> - </div> - </div> - </div> - </v-card-text> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelSelection">{{ tm('pluginSetSelector.cancelSelection') }}</v-btn> - <v-btn - color="primary" - variant="tonal" - @click="confirmSelection"> - {{ tm('pluginSetSelector.confirmSelection') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<script setup> -import { ref, computed, watch } from 'vue' -import { pluginApi } from '@/api/v1' -import { useModuleI18n } from '@/i18n/composables' -import { usePluginI18n } from '@/utils/pluginI18n' - -const props = defineProps({ - modelValue: { - type: Array, - default: () => [] - }, - buttonText: { - type: String, - default: '' - }, - maxDisplayItems: { - type: Number, - default: 3 - } -}) - -const emit = defineEmits(['update:modelValue']) -const { tm } = useModuleI18n('core.shared') -const { pluginName, pluginDesc } = usePluginI18n() - -const dialog = ref(false) -const pluginList = ref([]) -const loading = ref(false) -const selectionMode = ref('custom') // 'all', 'none', 'custom' -const selectedPlugins = ref([]) - -const pluginDisplayName = (plugin) => pluginName(plugin) || plugin.name -const pluginDescription = (plugin) => pluginDesc(plugin) - -// 判断是否为"所有插件"模式 -const isAllPlugins = computed(() => { - return props.modelValue && props.modelValue.length === 1 && props.modelValue[0] === '*' -}) - -// 移除插件 -function removePlugin(pluginName) { - if (props.modelValue && props.modelValue.length > 0) { - const newValue = props.modelValue.filter(name => name !== pluginName) - emit('update:modelValue', newValue) - } -} - -// 监听 modelValue 变化,同步内部状态 -watch(() => props.modelValue, (newValue) => { - if (!newValue || newValue.length === 0) { - selectionMode.value = 'none' - selectedPlugins.value = [] - } else if (newValue.length === 1 && newValue[0] === '*') { - selectionMode.value = 'all' - selectedPlugins.value = [] - } else { - selectionMode.value = 'custom' - selectedPlugins.value = [...newValue] - } -}, { immediate: true }) - -async function openDialog() { - dialog.value = true - await loadPlugins() -} - -async function loadPlugins() { - loading.value = true - try { - const response = await pluginApi.list() - if (response.data.status === 'ok') { - // 只显示已激活且非系统的插件,并按名称排序 - pluginList.value = (response.data.data || []) - .filter(plugin => plugin.activated && !plugin.reserved) - .sort((a, b) => { - const nameA = a.name || ''; - const nameB = b.name || ''; - return nameA.localeCompare(nameB); - }) - } - } catch (error) { - console.error('加载插件列表失败:', error) - pluginList.value = [] - } finally { - loading.value = false - } -} - -function confirmSelection() { - let newValue = [] - - switch (selectionMode.value) { - case 'all': - newValue = ['*'] - break - case 'none': - newValue = [] - break - case 'custom': - newValue = [...selectedPlugins.value] - break - } - - emit('update:modelValue', newValue) - dialog.value = false -} - -function cancelSelection() { - // 恢复到原始状态 - const currentValue = props.modelValue || [] - if (currentValue.length === 0) { - selectionMode.value = 'none' - selectedPlugins.value = [] - } else if (currentValue.length === 1 && currentValue[0] === '*') { - selectionMode.value = 'all' - selectedPlugins.value = [] - } else { - selectionMode.value = 'custom' - selectedPlugins.value = [...currentValue] - } - - dialog.value = false -} -</script> - -<style scoped> -.v-list-item { - transition: all 0.2s ease; -} - -.v-list-item:hover { - background-color: rgba(var(--v-theme-primary), 0.04); -} -</style> diff --git a/legacy-dashboard/src/components/shared/ProviderSelector.vue b/legacy-dashboard/src/components/shared/ProviderSelector.vue deleted file mode 100644 index 6ce9864e..00000000 --- a/legacy-dashboard/src/components/shared/ProviderSelector.vue +++ /dev/null @@ -1,618 +0,0 @@ -<template> - <div class="d-flex align-center justify-space-between"> - <span v-if="!hasSelection" style="color: rgb(var(--v-theme-primaryText));"> - {{ tm('providerSelector.notSelected') }} - </span> - <span v-else class="provider-name-text"> - <template v-if="multiple"> - {{ tm('providerSelector.selectedCount', { count: selectedProviders.length }) }} - </template> - <template v-else> - {{ modelValue }} - </template> - </span> - <v-btn size="small" color="primary" variant="tonal" @click="openDialog"> - {{ buttonText || tm('providerSelector.buttonText') }} - </v-btn> - </div> - - <div v-if="multiple && selectedProviders.length > 0" class="selected-preview mt-2"> - <v-chip - v-for="providerId in selectedProviders" - :key="`preview-${providerId}`" - size="x-small" - color="primary" - variant="tonal" - class="mr-1 mb-1" - label - > - {{ providerId }} - </v-chip> - </div> - - <!-- Provider Selection Dialog --> - <v-dialog v-model="dialog" max-width="600px"> - <v-card> - <v-card-title - class="text-h3 pa-4 pb-0 pl-6 d-flex align-center justify-space-between gap-4 flex-wrap" - > - <span>{{ tm('providerSelector.dialogTitle') }}</span> - <v-btn - size="small" - color="primary" - variant="tonal" - prepend-icon="mdi-plus" - @click="openProviderDrawer" - > - {{ tm('providerSelector.createProvider') }} - </v-btn> - </v-card-title> - - <v-card-text class="pa-0" style="max-height: 400px; overflow-y: auto;"> - <v-progress-linear v-if="loading" indeterminate color="primary"></v-progress-linear> - - <div v-if="multiple && selectedProviders.length > 0" class="pa-3"> - <div class="text-caption text-medium-emphasis mb-2"> - {{ tm('providerSelector.selectedCount', { count: selectedProviders.length }) }} - </div> - <v-list density="compact" class="selected-order-list"> - <v-list-item - v-for="(providerId, index) in selectedProviders" - :key="`selected-${providerId}-${index}`" - rounded="md" - class="ma-1" - > - <v-list-item-title>{{ providerId }}</v-list-item-title> - <template #append> - <div class="d-flex ga-1"> - <v-btn - icon="mdi-arrow-up" - size="x-small" - variant="text" - :disabled="index === 0" - @click.stop="moveSelected(index, -1)" - /> - <v-btn - icon="mdi-arrow-down" - size="x-small" - variant="text" - :disabled="index === selectedProviders.length - 1" - @click.stop="moveSelected(index, 1)" - /> - <v-btn - icon="mdi-close" - size="x-small" - variant="text" - @click.stop="removeSelected(providerId)" - /> - </div> - </template> - </v-list-item> - </v-list> - <v-divider class="ma-1"></v-divider> - </div> - - <v-list v-if="!loading && providerList.length > 0" density="compact"> - <!-- 不选择选项 --> - <v-list-item - v-if="!multiple" - key="none" - value="" - @click="selectProvider({ id: '' })" - :active="selectedProvider === ''" - rounded="md" - class="ma-1"> - <v-list-item-title>{{ tm('providerSelector.clearSelection') }}</v-list-item-title> - <v-list-item-subtitle>{{ tm('providerSelector.clearSelectionSubtitle') }}</v-list-item-subtitle> - - <template v-slot:append> - <v-icon v-if="selectedProvider === ''">mdi-check-circle</v-icon> - </template> - </v-list-item> - - <v-divider class="ma-1"></v-divider> - - <v-list-item - v-for="provider in providerList" - :key="provider.id" - :value="provider.id" - @click="selectProvider(provider)" - :active="isProviderSelected(provider.id)" - rounded="md" - class="ma-1"> - <v-list-item-title>{{ provider.id }}</v-list-item-title> - <v-list-item-subtitle> - {{ provider.type || provider.provider_type || tm('providerSelector.unknownType') }} - <span v-if="provider.model">- {{ provider.model }}</span> - <span - v-if="capabilityBadges(provider).length || formatContextLimit(provider, metadataForProvider(provider))" - class="provider-selector-meta" - > - <v-tooltip - v-for="item in capabilityBadges(provider)" - :key="item.key" - location="top" - max-width="320" - > - <template #activator="{ props: badgeTooltipProps }"> - <span - v-bind="badgeTooltipProps" - class="provider-selector-meta__badge" - :class="{ 'provider-selector-meta__badge--disabled': !item.enabled }" - @click.stop - > - <v-icon size="13">{{ item.icon }}</v-icon> - </span> - </template> - <span>{{ item.tooltip }}</span> - </v-tooltip> - <v-tooltip - v-if="formatContextLimit(provider, metadataForProvider(provider))" - location="top" - max-width="320" - > - <template #activator="{ props: contextTooltipProps }"> - <span - v-bind="contextTooltipProps" - class="provider-selector-meta__context" - @click.stop - > - {{ formatContextLimit(provider, metadataForProvider(provider)) }} - </span> - </template> - <span>{{ - providerTm('models.metadata.context', { - tokens: formatContextLimit(provider, metadataForProvider(provider)) - }) - }}</span> - </v-tooltip> - </span> - </v-list-item-subtitle> - - <template v-slot:append> - <div class="provider-selector-actions" @click.stop> - <v-tooltip location="top"> - <template #activator="{ props: testTooltipProps }"> - <v-btn - v-bind="testTooltipProps" - icon="mdi-connection" - size="x-small" - variant="text" - :loading="isProviderTesting(provider.id)" - :disabled="!isProviderTestable(provider)" - @click.stop="testProvider(provider)" - /> - </template> - <span>{{ providerTm('models.testButton') }}</span> - </v-tooltip> - <v-icon v-if="isProviderSelected(provider.id)">mdi-check-circle</v-icon> - </div> - </template> - </v-list-item> - </v-list> - - <div v-else-if="!loading && providerList.length === 0" class="text-center py-8"> - <v-icon size="64" color="grey-lighten-1">mdi-api-off</v-icon> - <p class="text-grey mt-4">{{ tm('providerSelector.noProviders') }}</p> - </div> - </v-card-text> - - <v-divider></v-divider> - - <v-card-actions class="pa-4"> - <v-spacer></v-spacer> - <v-btn variant="text" @click="cancelSelection">{{ tm('providerSelector.cancelSelection') }}</v-btn> - <v-btn - color="primary" - variant="tonal" - @click="confirmSelection"> - {{ tm('providerSelector.confirmSelection') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <v-overlay - v-model="providerDrawer" - class="provider-drawer-overlay" - location="right" - transition="slide-x-reverse-transition" - :scrim="true" - @click:outside="closeProviderDrawer" - > - <v-card class="provider-drawer-card" elevation="12"> - <div class="provider-drawer-header"> - <v-btn icon variant="text" @click="closeProviderDrawer"> - <v-icon>mdi-close</v-icon> - </v-btn> - </div> - <div class="provider-drawer-content"> - <ProviderChatCompletionPanel - v-if="defaultTab === 'chat_completion'" - /> - <ProviderPage v-else :default-tab="defaultTab" /> - </div> - </v-card> - </v-overlay> -</template> - -<script setup> -import { computed, ref, watch } from 'vue' -import { providerApi } from '@/api/v1' -import { useModuleI18n } from '@/i18n/composables' -import { useToast } from '@/utils/toast' -import { formatContextLimit, providerCapabilityBadges } from '@/utils/providerMetadata' -import ProviderChatCompletionPanel from '@/components/provider/ProviderChatCompletionPanel.vue' -import ProviderPage from '@/views/ProviderPage.vue' - -const props = defineProps({ - modelValue: { - type: [String, Array], - default: '' - }, - providerType: { - type: String, - default: 'chat_completion' - }, - providerSubtype: { - type: String, - default: '' - }, - buttonText: { - type: String, - default: '' - }, - multiple: { - type: Boolean, - default: false - } -}) - -const emit = defineEmits(['update:modelValue']) -const { tm } = useModuleI18n('core.shared') -const { tm: providerTm } = useModuleI18n('features/provider') -const { success: toastSuccess, error: toastError } = useToast() - -const dialog = ref(false) -const providerList = ref([]) -const loading = ref(false) -const selectedProvider = ref('') -const selectedProviders = ref([]) -const providerDrawer = ref(false) -const testingProviders = ref([]) -const modelMetadata = ref({}) - -const hasSelection = computed(() => { - if (props.multiple) { - return selectedProviders.value.length > 0 - } - return Boolean(props.modelValue) -}) - -const defaultTab = computed(() => { - if (props.providerType === 'agent_runner' && props.providerSubtype) { - return `select_agent_runner_provider:${props.providerSubtype}` - } - return props.providerType || 'chat_completion' -}) - -// 监听 modelValue 变化,同步到 selectedProvider -watch(() => props.modelValue, (newValue) => { - if (props.multiple) { - selectedProviders.value = Array.isArray(newValue) - ? [...newValue.filter((v) => typeof v === 'string' && v)] - : [] - return - } - selectedProvider.value = typeof newValue === 'string' ? newValue : '' -}, { immediate: true }) - -watch(providerDrawer, (isOpen, wasOpen) => { - if (!isOpen && wasOpen) { - loadProviders() - } -}) - -async function openDialog() { - if (props.multiple) { - selectedProviders.value = Array.isArray(props.modelValue) - ? [...props.modelValue.filter((v) => typeof v === 'string' && v)] - : [] - } else { - selectedProvider.value = typeof props.modelValue === 'string' ? props.modelValue : '' - } - dialog.value = true - await loadProviders() -} - -async function loadProviders() { - loading.value = true - try { - const response = await providerApi.listByProviderType(props.providerType) - if (response.data.status === 'ok') { - modelMetadata.value = response.data.model_metadata || {} - const providers = response.data.data || [] - providerList.value = props.providerSubtype - ? providers.filter((provider) => matchesProviderSubtype(provider, props.providerSubtype)) - : providers - } - } catch (error) { - console.error('加载提供商列表失败:', error) - providerList.value = [] - } finally { - loading.value = false - } -} - -function matchesProviderSubtype(provider, subtype) { - if (!subtype) { - return true - } - const normalized = String(subtype).toLowerCase() - const candidates = [provider.type, provider.provider, provider.id] - .filter(Boolean) - .map((value) => String(value).toLowerCase()) - return candidates.includes(normalized) -} - -function selectProvider(provider) { - if (props.multiple) { - if (!provider.id) { - selectedProviders.value = [] - return - } - const idx = selectedProviders.value.indexOf(provider.id) - if (idx >= 0) { - selectedProviders.value.splice(idx, 1) - } else { - selectedProviders.value.push(provider.id) - } - return - } - selectedProvider.value = provider.id -} - -function confirmSelection() { - if (props.multiple) { - emit('update:modelValue', [...selectedProviders.value]) - } else { - emit('update:modelValue', selectedProvider.value) - } - dialog.value = false -} - -function cancelSelection() { - if (props.multiple) { - selectedProviders.value = Array.isArray(props.modelValue) - ? [...props.modelValue.filter((v) => typeof v === 'string' && v)] - : [] - } else { - selectedProvider.value = typeof props.modelValue === 'string' ? props.modelValue : '' - } - dialog.value = false -} - -function isProviderSelected(providerId) { - if (props.multiple) { - return selectedProviders.value.includes(providerId) - } - return selectedProvider.value === providerId -} - -function capabilityBadges(provider) { - return providerCapabilityBadges(provider, metadataForProvider(provider), providerTm) -} - -function metadataForProvider(provider) { - return provider?.model ? modelMetadata.value[provider.model] || null : null -} - -function isProviderTesting(providerId) { - return testingProviders.value.includes(providerId) -} - -function isProviderTestable(provider) { - return Boolean(provider?.id) && provider.enable !== false && !isProviderTesting(provider.id) -} - -async function testProvider(provider) { - if (!isProviderTestable(provider)) { - return - } - testingProviders.value.push(provider.id) - try { - const startTime = performance.now() - const response = await providerApi.test(String(provider.id)) - if (response.data.status === 'ok' && response.data.data.error === null) { - const latency = Math.max(0, Math.round(performance.now() - startTime)) - toastSuccess(providerTm('models.testSuccessWithLatency', { - id: provider.id, - latency - })) - } else { - throw new Error(response.data.data.error || providerTm('models.testError')) - } - } catch (error) { - toastError(error.response?.data?.message || error.message || providerTm('models.testError')) - } finally { - testingProviders.value = testingProviders.value.filter((id) => id !== provider.id) - } -} - -function removeSelected(providerId) { - const idx = selectedProviders.value.indexOf(providerId) - if (idx >= 0) { - selectedProviders.value.splice(idx, 1) - } -} - -function moveSelected(index, delta) { - const targetIndex = index + delta - if ( - targetIndex < 0 - || targetIndex >= selectedProviders.value.length - || index < 0 - || index >= selectedProviders.value.length - ) { - return - } - const copied = [...selectedProviders.value] - const [item] = copied.splice(index, 1) - copied.splice(targetIndex, 0, item) - selectedProviders.value = copied -} - -function openProviderDrawer() { - providerDrawer.value = true -} - -function closeProviderDrawer() { - providerDrawer.value = false -} -</script> - -<style scoped> -.provider-name-text { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: calc(100% - 80px); - display: inline-block; -} - -.selected-preview { - width: 100%; - max-width: 100%; -} - -.selected-order-list { - background: rgba(var(--v-theme-surface-variant), 0.15); - border-radius: 10px; -} - -.provider-selector-meta { - display: inline-flex; - align-items: center; - gap: 4px; - margin-left: 8px; - vertical-align: middle; -} - -.provider-selector-meta__badge { - display: inline-flex; - align-items: center; - color: rgba(var(--v-theme-on-surface), 0.72); -} - -.provider-selector-meta__badge--disabled { - color: rgba(var(--v-theme-on-surface), 0.34); -} - -.provider-selector-meta__context { - display: inline-flex; - align-items: center; - height: 16px; - padding: 0 5px; - border-radius: 999px; - background: rgba(var(--v-theme-on-surface), 0.06); - color: rgba(var(--v-theme-on-surface), 0.72); - font-size: 10px; - font-weight: 650; - line-height: 16px; -} - -.provider-selector-actions { - display: inline-flex; - align-items: center; - gap: 2px; - color: rgba(var(--v-theme-on-surface), 0.72); -} - -.v-list-item { - transition: all 0.2s ease; -} - -.v-list-item:hover { - background-color: rgba(var(--v-theme-primary), 0.04); -} - -.v-list-item.v-list-item--active { - background-color: rgba(var(--v-theme-primary), 0.08); -} - -.provider-drawer-overlay { - align-items: stretch; - justify-content: flex-end; -} - -.provider-drawer-card { - width: clamp(360px, 70vw, 1200px); - height: calc(100vh - 32px); - margin: 16px; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.provider-drawer-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 16px 20px 12px 20px; -} - -.provider-drawer-content { - flex: 1; - overflow: hidden; -} - -.provider-drawer-content > * { - height: 100%; - overflow: auto; -} - -@media (max-width: 960px) { - .provider-drawer-card { - width: calc(100dvw - 24px); - height: calc(100dvh - 24px); - margin: 12px; - } -} - -@media (max-width: 600px) { - .provider-name-text { - max-width: 100%; - } - - .provider-drawer-overlay { - align-items: stretch; - justify-content: stretch; - } - - .provider-drawer-card { - width: 100dvw; - height: 100dvh; - margin: 0; - border-radius: 0; - } - - .provider-drawer-header { - padding: 8px 12px; - } - - .provider-drawer-content { - overflow: auto; - } - - :deep(.v-overlay__content) { - width: 100dvw; - max-width: 100dvw; - margin: 0; - } - - :deep(.v-dialog > .v-overlay__content) { - width: calc(100dvw - 24px); - max-width: calc(100dvw - 24px); - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/ProxySelector.vue b/legacy-dashboard/src/components/shared/ProxySelector.vue deleted file mode 100644 index 33033cfa..00000000 --- a/legacy-dashboard/src/components/shared/ProxySelector.vue +++ /dev/null @@ -1,242 +0,0 @@ -<template> - <div class="proxy-selector"> - <h5 class="proxy-selector__title">{{ tm('network.proxySelector.title') }}</h5> - <v-radio-group class="proxy-selector__mode mt-2" v-model="radioValue" hide-details="true"> - <v-radio :label="tm('network.proxySelector.noProxy')" value="0"></v-radio> - <v-radio value="1"> - <template v-slot:label> - <span>{{ tm('network.proxySelector.useProxy') }}</span> - <v-btn v-if="radioValue === '1'" class="ml-2" @click="testAllProxies" size="x-small" - variant="tonal" :loading="loadingTestingConnection"> - {{ tm('network.proxySelector.testConnection') }} - </v-btn> - </template> - </v-radio> - </v-radio-group> - <v-expand-transition> - <div v-if="radioValue === '1'" class="proxy-selector__list"> - <v-radio-group v-model="githubProxyRadioControl" class="mt-2" hide-details="true"> - <v-radio color="success" v-for="(proxy, idx) in githubProxies" :key="proxy" :value="String(idx)"> - <template v-slot:label> - <div class="proxy-selector__option-label"> - <span class="proxy-selector__url">{{ proxy }}</span> - <div v-if="proxyStatus[idx]" class="proxy-selector__status"> - <v-chip - :color="proxyStatus[idx].available ? 'success' : 'error'" - size="x-small" - class="mr-1"> - {{ proxyStatus[idx].available ? tm('network.proxySelector.available') : tm('network.proxySelector.unavailable') }} - </v-chip> - <v-chip - v-if="proxyStatus[idx].available" - color="info" - size="x-small"> - {{ proxyStatus[idx].latency }}ms - </v-chip> - </div> - </div> - </template> - </v-radio> - <v-radio color="primary" value="-1" :label="tm('network.proxySelector.custom')"> - <template v-slot:label v-if="String(githubProxyRadioControl) === '-1'"> - <v-text-field class="proxy-selector__custom-input" density="compact" v-model="selectedGitHubProxy" variant="outlined" - :placeholder="tm('network.proxySelector.custom')" hide-details="true"> - </v-text-field> - </template> - </v-radio> - </v-radio-group> - </div> - </v-expand-transition> - </div> -</template> - - -<script> -import { statsApi } from '@/api/v1'; -import { useModuleI18n } from '@/i18n/composables'; - -export default { - setup() { - const { tm } = useModuleI18n('features/settings'); - return { tm }; - }, - data() { - return { - githubProxies: [ - "https://edgeone.gh-proxy.com", - "https://hk.gh-proxy.com", - "https://gh-proxy.com", - "https://gh.dpik.top", - ], - githubProxyRadioControl: "0", // the index of the selected proxy - selectedGitHubProxy: "", - radioValue: "0", // 0: 不使用, 1: 使用 - loadingTestingConnection: false, - testingProxies: {}, - proxyStatus: {}, - initializing: true, - } - }, - methods: { - getProxyByControl(control) { - const normalizedControl = String(control); - if (normalizedControl === "-1") { - return ""; - } - const index = Number.parseInt(normalizedControl, 10); - if (Number.isNaN(index)) { - return ""; - } - return this.githubProxies[index] || ""; - }, - async testSingleProxy(idx) { - this.testingProxies[idx] = true; - - const proxy = this.githubProxies[idx]; - - try { - const response = await statsApi.testGhproxy({ - proxy_url: proxy - }); - console.log(response.data); - if (response.status === 200) { - this.proxyStatus[idx] = { - available: true, - latency: Math.round(response.data.data.latency) - }; - } else { - this.proxyStatus[idx] = { - available: false, - latency: 0 - }; - } - } catch (error) { - this.proxyStatus[idx] = { - available: false, - latency: 0 - }; - } finally { - this.testingProxies[idx] = false; - } - }, - - async testAllProxies() { - this.loadingTestingConnection = true; - - const promises = this.githubProxies.map((proxy, idx) => - this.testSingleProxy(idx) - ); - - await Promise.all(promises); - this.loadingTestingConnection = false; - }, - }, - mounted() { - this.initializing = true; - - const savedProxy = localStorage.getItem('selectedGitHubProxy') || ""; - const savedRadio = localStorage.getItem('githubProxyRadioValue') || "0"; - const savedControl = String(localStorage.getItem('githubProxyRadioControl') || "0"); - - this.radioValue = savedRadio; - this.githubProxyRadioControl = savedControl; - - if (savedRadio === "1") { - if (savedControl !== "-1") { - this.selectedGitHubProxy = this.getProxyByControl(savedControl); - } else { - this.selectedGitHubProxy = savedProxy; - } - } else { - this.selectedGitHubProxy = ""; - } - - this.initializing = false; - }, - watch: { - selectedGitHubProxy: function (newVal, oldVal) { - if (this.initializing) { - return; - } - if (!newVal) { - newVal = "" - } - localStorage.setItem('selectedGitHubProxy', newVal); - }, - radioValue: function (newVal) { - if (this.initializing) { - return; - } - localStorage.setItem('githubProxyRadioValue', newVal); - if (String(newVal) === "0") { - this.selectedGitHubProxy = ""; - } else if (String(this.githubProxyRadioControl) !== "-1") { - this.selectedGitHubProxy = this.getProxyByControl(this.githubProxyRadioControl); - } - }, - githubProxyRadioControl: function (newVal) { - if (this.initializing) { - return; - } - const normalizedVal = String(newVal); - localStorage.setItem('githubProxyRadioControl', normalizedVal); - if (String(this.radioValue) !== "1") { - this.selectedGitHubProxy = ""; - return; - } - if (normalizedVal !== "-1") { - this.selectedGitHubProxy = this.getProxyByControl(normalizedVal); - } - } - } -} -</script> - -<style scoped> -.proxy-selector { - width: 100%; - min-width: 0; -} - -.proxy-selector__title { - margin: 0; - color: rgb(var(--v-theme-on-surface)); - font-size: 0.88rem; - font-weight: 700; - line-height: 1.4; -} - -.proxy-selector__list { - margin-left: 16px; -} - -.proxy-selector :deep(.v-label) { - min-width: 0; - font-size: 0.875rem; - line-height: 1.35; - white-space: normal; -} - -.proxy-selector__option-label { - display: flex; - align-items: center; - gap: 8px; - min-width: 0; - max-width: 100%; -} - -.proxy-selector__url { - overflow-wrap: anywhere; - word-break: normal; -} - -.proxy-selector__status { - display: flex; - flex: 0 0 auto; - align-items: center; -} - -.proxy-selector__custom-input { - width: min(100%, 420px); -} -</style> diff --git a/legacy-dashboard/src/components/shared/QrCodeViewer.vue b/legacy-dashboard/src/components/shared/QrCodeViewer.vue deleted file mode 100644 index 911feec2..00000000 --- a/legacy-dashboard/src/components/shared/QrCodeViewer.vue +++ /dev/null @@ -1,94 +0,0 @@ -<template> - <div class="qr-code-viewer"> - <img - v-if="imageSrc" - :src="imageSrc" - :alt="alt" - class="qr-code-image" - /> - <div v-else class="qr-code-empty"> - {{ emptyHint }} - </div> - </div> -</template> - -<script> -import QRCode from 'qrcode'; - -export default { - name: "QrCodeViewer", - props: { - value: { - type: String, - default: "", - }, - alt: { - type: String, - default: "QR Code", - }, - size: { - type: Number, - default: 260, - }, - margin: { - type: Number, - default: 2, - }, - emptyHint: { - type: String, - default: "暂无可用二维码", - }, - }, - data() { - return { - imageSrc: "", - }; - }, - watch: { - value: { - immediate: true, - handler: "renderQRCode", - }, - }, - methods: { - async renderQRCode(rawValue) { - const value = String(rawValue || "").trim(); - if (!value) { - this.imageSrc = ""; - return; - } - - try { - this.imageSrc = await QRCode.toDataURL(value, { - margin: this.margin, - width: this.size, - errorCorrectionLevel: "M", - }); - } catch { - this.imageSrc = ""; - } - }, - }, -}; -</script> - -<style scoped> -.qr-code-viewer { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} - -.qr-code-image { - display: block; - width: 180px; - max-width: 100%; - border-radius: 8px; -} - -.qr-code-empty { - color: rgba(0, 0, 0, 0.6); - font-size: 12px; -} -</style> diff --git a/legacy-dashboard/src/components/shared/ReadmeDialog.vue b/legacy-dashboard/src/components/shared/ReadmeDialog.vue deleted file mode 100644 index e291fc5e..00000000 --- a/legacy-dashboard/src/components/shared/ReadmeDialog.vue +++ /dev/null @@ -1,738 +0,0 @@ -<script setup> -import { ref, watch, computed, onUnmounted } from "vue"; -import { useTheme } from "vuetify"; -import MarkdownIt from "markdown-it"; -import DOMPurify from "dompurify"; -import { pluginApi, statsApi } from "@/api/v1"; -import { useI18n } from "@/i18n/composables"; -import { copyToClipboard } from "@/utils/clipboard"; -import { - escapeHtml, - ensureShikiLanguages, - normalizeShikiLanguage, - renderShikiCode, -} from "@/utils/shiki"; - -// 1. 在 setup 作用域创建 MarkdownIt 实例 -const md = new MarkdownIt({ - html: true, - linkify: true, - typographer: true, - breaks: false, -}); - -md.enable(["table", "strikethrough"]); -md.renderer.rules.table_open = () => '<div class="table-container"><table>'; -md.renderer.rules.table_close = () => "</table></div>"; - -// 2. 复制按钮的 SVG 图标常量 -const ICONS = { - SUCCESS: - '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20,6 9,17 4,12"></polyline></svg>', - ERROR: - '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>', - COPY: '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>', -}; - -const props = defineProps({ - show: { type: Boolean, default: false }, - pluginName: { type: String, default: "" }, - repoUrl: { type: String, default: null }, - mode: { - type: String, - default: "readme", - validator: (value) => ["readme", "changelog", "first-notice"].includes(value), - }, -}); - -const emit = defineEmits(["update:show"]); -const { t, locale } = useI18n(); -const theme = useTheme(); - -const content = ref(null); -const error = ref(null); -const loading = ref(false); -const isEmpty = ref(false); -const copyFeedbackTimer = ref(null); -const lastRequestId = ref(0); -const lastRenderId = ref(0); -const scrollContainer = ref(null); -const renderedHtml = ref(""); -const isDark = computed(() => theme.global.current.value.dark); - -const MARKDOWN_SANITIZE_OPTIONS = { - ALLOWED_TAGS: [ - "h1", - "h2", - "h3", - "h4", - "h5", - "h6", - "p", - "br", - "hr", - "ul", - "ol", - "li", - "blockquote", - "pre", - "code", - "a", - "img", - "table", - "thead", - "tbody", - "tr", - "th", - "td", - "strong", - "em", - "del", - "s", - "details", - "summary", - "div", - "span", - "input", - "button", - "svg", - "rect", - "path", - "polyline", - ], - ALLOWED_ATTR: [ - "href", - "src", - "alt", - "title", - "class", - "id", - "target", - "rel", - "type", - "checked", - "disabled", - "open", - "align", - "width", - "height", - "viewBox", - "fill", - "stroke", - "stroke-width", - "points", - "d", - "x", - "y", - "rx", - "ry", - "data-code-block-index", - ], -}; - -const CODE_BLOCK_SANITIZE_OPTIONS = { - ALLOWED_TAGS: ["div", "span", "button", "svg", "rect", "path", "polyline", "pre", "code"], - ALLOWED_ATTR: [ - "class", - "title", - "type", - "width", - "height", - "viewBox", - "fill", - "stroke", - "stroke-width", - "points", - "d", - "x", - "y", - "rx", - "ry", - "style", - "tabindex", - ], -}; - -function slugifyHeading(text, slugCounts) { - const base = (text || "") - .trim() - .toLowerCase() - .normalize("NFKD") - .replace(/[\u0300-\u036f]/g, "") - .replace(/[^\p{Letter}\p{Number}\s-]/gu, "") - .replace(/\s+/g, "-") - .replace(/-+/g, "-"); - - if (!base) return ""; - - const count = slugCounts.get(base) || 0; - slugCounts.set(base, count + 1); - return count === 0 ? base : `${base}-${count}`; -} - -onUnmounted(() => { - if (copyFeedbackTimer.value) clearTimeout(copyFeedbackTimer.value); -}); - -function sanitizeHighlightedBlock(html) { - return DOMPurify.sanitize(html, CODE_BLOCK_SANITIZE_OPTIONS); -} - -async function updateRenderedHtml() { - const source = content.value; - const renderId = ++lastRenderId.value; - void locale?.value; - - if (!source) { - renderedHtml.value = ""; - return; - } - - let highlighter = null; - const env = {}; - const tokens = md.parse(source, env); - - try { - const languages = tokens - .filter((token) => token.type === "fence") - .map((token) => normalizeShikiLanguage(token.info)); - highlighter = await ensureShikiLanguages(languages); - } catch (err) { - console.error("Failed to initialize Shiki for README dialog:", err); - } - - if (renderId !== lastRenderId.value) return; - - const highlightedBlocks = []; - - md.renderer.rules.fence = (tokens, idx) => { - const token = tokens[idx]; - const lang = normalizeShikiLanguage(token.info); - const code = token.content; - const escapedLangLabel = - lang && lang !== "text" ? escapeHtml(lang) : ""; - const highlighted = highlighter - ? renderShikiCode(highlighter, code, lang, isDark.value ? "dark" : "light") - : `<pre class="shiki shiki-fallback"><code>${escapeHtml(code)}</code></pre>`; - const html = sanitizeHighlightedBlock(`<div class="code-block-wrapper"> - ${escapedLangLabel ? `<span class="code-lang-label">${escapedLangLabel}</span>` : ""} - <button class="copy-code-btn" title="${t("core.common.copy")}"> - <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> - </button> - ${highlighted} - </div>`); - - const placeholderIndex = highlightedBlocks.push(html) - 1; - return `<div data-code-block-index="${placeholderIndex}"></div>`; - }; - - const rawHtml = md.renderer.render(tokens, md.options, env); - - const cleanHtml = DOMPurify.sanitize(rawHtml, MARKDOWN_SANITIZE_OPTIONS); - - const tempDiv = document.createElement("div"); - tempDiv.innerHTML = cleanHtml; - - const slugCounts = new Map(); - tempDiv.querySelectorAll("h1, h2, h3, h4, h5, h6").forEach((heading) => { - if (heading.id) { - slugCounts.set(heading.id, (slugCounts.get(heading.id) || 0) + 1); - return; - } - - const slug = slugifyHeading(heading.textContent, slugCounts); - if (slug) heading.id = slug; - }); - - tempDiv.querySelectorAll("a").forEach((link) => { - const href = link.getAttribute("href"); - if (href && (href.startsWith("http") || href.startsWith("//"))) { - link.setAttribute("target", "_blank"); - link.setAttribute("rel", "noopener noreferrer"); - } - }); - - tempDiv.querySelectorAll("[data-code-block-index]").forEach((placeholder) => { - const index = Number(placeholder.getAttribute("data-code-block-index")); - placeholder.outerHTML = highlightedBlocks[index] || ""; - }); - - if (renderId === lastRenderId.value) { - renderedHtml.value = tempDiv.innerHTML; - } -} - -const modeConfig = computed(() => { - if (props.mode === "changelog") { - return { - title: t("core.common.changelog.title"), - loading: t("core.common.changelog.loading"), - emptyTitle: t("core.common.changelog.empty.title"), - emptySubtitle: t("core.common.changelog.empty.subtitle"), - showGithubButton: false, - showRefreshButton: true, - refreshLabel: t("core.common.readme.buttons.refresh"), - }; - } - - if (props.mode === "first-notice") { - return { - title: t("core.common.firstNotice.title"), - loading: t("core.common.firstNotice.loading"), - emptyTitle: t("core.common.firstNotice.empty.title"), - emptySubtitle: t("core.common.firstNotice.empty.subtitle"), - showGithubButton: false, - showRefreshButton: false, - refreshLabel: "", - }; - } - - return { - title: t("core.common.readme.title"), - loading: t("core.common.readme.loading"), - emptyTitle: t("core.common.readme.empty.title"), - emptySubtitle: t("core.common.readme.empty.subtitle"), - showGithubButton: true, - showRefreshButton: true, - refreshLabel: t("core.common.readme.buttons.refresh"), - }; -}); - -const requiresPluginName = computed( - () => props.mode === "readme" || props.mode === "changelog", -); - -async function fetchContent() { - if (requiresPluginName.value && !props.pluginName) return; - const requestId = ++lastRequestId.value; - loading.value = true; - content.value = null; - error.value = null; - isEmpty.value = false; - - try { - let res; - if (props.mode === "changelog") { - res = await pluginApi.changelog(props.pluginName); - } else if (props.mode === "readme") { - res = await pluginApi.readme(props.pluginName); - } else if (props.mode === "first-notice") { - res = await statsApi.firstNotice(locale.value); - } - if (requestId !== lastRequestId.value) return; - - if (res.data.status === "ok") { - if (res.data.data.content) content.value = res.data.data.content; - else isEmpty.value = true; - } else { - error.value = res.data.message; - } - } catch (err) { - if (requestId === lastRequestId.value) error.value = err.message; - } finally { - if (requestId === lastRequestId.value) loading.value = false; - } -} - -watch( - [() => props.show, () => props.pluginName, () => props.mode], - ([show, name]) => { - if (!show) return; - if (requiresPluginName.value && !name) return; - fetchContent(); - }, - { immediate: true }, -); - -watch([content, locale, isDark], () => { - updateRenderedHtml(); -}, { immediate: true }); - -async function handleContainerClick(event) { - const btn = event.target.closest(".copy-code-btn"); - if (btn) { - const code = btn.closest(".code-block-wrapper")?.querySelector("code"); - if (code) { - const success = await copyToClipboard(code.textContent || ""); - showCopyFeedback(btn, success); - } - return; - } - - const anchor = event.target.closest('a[href^="#"]'); - if (!anchor) return; - - const rawHref = anchor.getAttribute("href"); - const targetId = rawHref ? decodeURIComponent(rawHref.slice(1)) : ""; - if (!targetId) return; - - const target = scrollContainer.value?.querySelector( - `#${CSS.escape(targetId)}`, - ); - if (!target) return; - - event.preventDefault(); - target.scrollIntoView({ behavior: "smooth", block: "start" }); -} - -function showCopyFeedback(btn, success) { - if (copyFeedbackTimer.value) clearTimeout(copyFeedbackTimer.value); - btn.setAttribute("title", t(`core.common.${success ? "copied" : "error"}`)); - btn.innerHTML = success ? ICONS.SUCCESS : ICONS.ERROR; - btn.style.color = success ? "var(--v-theme-success)" : "var(--v-theme-error)"; - - copyFeedbackTimer.value = setTimeout(() => { - if (document.body.contains(btn)) { - btn.innerHTML = ICONS.COPY; - btn.style.color = ""; - btn.setAttribute("title", t("core.common.copy")); - } - copyFeedbackTimer.value = null; - }, 2000); -} - -const _show = computed({ - get: () => props.show, - set: (val) => emit("update:show", val), -}); - -// 安全打开外部链接 -function openExternalLink(url) { - if (!url) return; - window.open(url, "_blank", "noopener,noreferrer"); -} - -const showActionArea = computed(() => { - const hasGithub = modeConfig.value.showGithubButton && !!props.repoUrl; - return hasGithub || modeConfig.value.showRefreshButton; -}); -</script> - -<template> - <v-dialog v-model="_show" width="800"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex justify-space-between align-center"> - <span>{{ modeConfig.title }}</span> - <v-btn icon @click="_show = false" variant="text"> - <v-icon>mdi-close</v-icon> - </v-btn> - </v-card-title> - <v-card-text ref="scrollContainer" style="overflow-y: auto"> - <div v-if="showActionArea" class="d-flex justify-space-between mb-4"> - <v-btn - v-if="modeConfig.showGithubButton && repoUrl" - color="primary" - variant="tonal" - prepend-icon="mdi-github" - @click="openExternalLink(repoUrl)" - > - {{ t("core.common.readme.buttons.viewOnGithub") }} - </v-btn> - <v-btn - v-if="modeConfig.showRefreshButton" - color="secondary" - variant="tonal" - prepend-icon="mdi-refresh" - @click="fetchContent" - > - {{ modeConfig.refreshLabel }} - </v-btn> - </div> - - <div - v-if="loading" - class="d-flex flex-column align-center justify-center" - style="height: 100%" - > - <v-progress-circular - indeterminate - color="primary" - size="64" - class="mb-4" - ></v-progress-circular> - <p class="text-body-1 text-center">{{ modeConfig.loading }}</p> - </div> - - <div - v-else-if="renderedHtml" - class="markdown-body" - v-html="renderedHtml" - @click="handleContainerClick" - ></div> - - <div - v-else-if="error" - class="d-flex flex-column align-center justify-center" - style="height: 100%" - > - <v-icon size="64" color="error" class="mb-4" - >mdi-alert-circle-outline</v-icon - > - <p class="text-body-1 text-center mb-2"> - {{ t("core.common.error") }} - </p> - <p class="text-body-2 text-center text-medium-emphasis"> - {{ error }} - </p> - </div> - - <div - v-else-if="isEmpty" - class="d-flex flex-column align-center justify-center" - style="height: 100%" - > - <v-icon size="64" color="warning" class="mb-4" - >mdi-file-question-outline</v-icon - > - <p class="text-body-1 text-center mb-2"> - {{ modeConfig.emptyTitle }} - </p> - <p class="text-body-2 text-center text-medium-emphasis"> - {{ modeConfig.emptySubtitle }} - </p> - </div> - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn color="primary" variant="tonal" @click="_show = false"> - {{ t("core.common.close") }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> -</template> - -<style scoped> -:deep(.markdown-body) { - --markdown-border: rgba(128, 128, 128, 0.3); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, - sans-serif; - line-height: 1.6; - padding: 8px 0; - color: var(--v-theme-secondaryText); -} - -:deep(.markdown-body [align="center"]) { - text-align: center; -} -:deep(.markdown-body [align="right"]) { - text-align: right; -} - -:deep(.markdown-body h1), -:deep(.markdown-body h2), -:deep(.markdown-body h3), -:deep(.markdown-body h4), -:deep(.markdown-body h5), -:deep(.markdown-body h6) { - margin-top: 24px; - margin-bottom: 16px; - font-weight: 600; - line-height: 1.25; - scroll-margin-top: 12px; -} - -:deep(.markdown-body h1) { - font-size: 2em; - border-bottom: 1px solid var(--v-theme-border); - padding-bottom: 0.3em; -} -:deep(.markdown-body h2) { - font-size: 1.5em; - border-bottom: 1px solid var(--v-theme-border); - padding-bottom: 0.3em; -} -:deep(.markdown-body p) { - margin-top: 0; - margin-bottom: 16px; -} - -:deep(.markdown-body .code-block-wrapper) { - position: relative; - margin-bottom: 16px; -} -:deep(.markdown-body .code-lang-label) { - position: absolute; - top: 8px; - left: 12px; - font-size: 12px; - color: #8b949e; - text-transform: uppercase; - font-weight: 500; - z-index: 1; -} - -:deep(.markdown-body .copy-code-btn) { - position: absolute; - top: 8px; - right: 8px; - background: rgba(110, 118, 129, 0.4); - border: none; - border-radius: 6px; - padding: 6px; - cursor: pointer; - color: #c9d1d9; - display: flex; - align-items: center; - justify-content: center; - transition: - background-color 0.2s, - color 0.2s; - z-index: 1; -} - -:deep(.markdown-body .copy-code-btn:hover) { - background: rgba(110, 118, 129, 0.6); - color: #fff; -} - -:deep(.markdown-body code) { - padding: 0.2em 0.4em; - margin: 0; - background-color: rgba(110, 118, 129, 0.2); - border-radius: 6px; - font-size: 85%; - font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; -} - -:deep(.markdown-body pre.shiki) { - padding: 16px; - padding-top: 32px; - overflow: auto; - font-size: 85%; - line-height: 1.45; - border-radius: 6px; - margin: 0; - border: 1px solid rgba(128, 128, 128, 0.18); -} - -:deep(.markdown-body pre.shiki code) { - background-color: transparent; - padding: 0; - border-radius: 0; - color: inherit; -} - -:deep(.markdown-body pre.shiki .line) { - display: block; - min-height: 1.45em; -} - -:deep(.markdown-body pre.shiki.shiki-fallback) { - background-color: #f6f8fa; - color: #24292f; -} -:deep(.markdown-body ul), -:deep(.markdown-body ol) { - padding-left: 2em; - margin-bottom: 16px; -} - -:deep(.markdown-body img) { - max-width: 100%; - margin: 8px 0; - box-sizing: border-box; - background-color: var(--v-theme-background); - border-radius: 3px; -} - -:deep(.markdown-body img[src*="shields.io"]), -:deep(.markdown-body img[src*="badge"]) { - display: inline-block; - vertical-align: middle; - height: auto; - margin: 2px 4px; - background-color: transparent; -} - -:deep(.markdown-body blockquote) { - padding: 0 1em; - color: var(--v-theme-secondaryText); - border-left: 0.25em solid var(--v-theme-border); - margin-bottom: 16px; -} - -:deep(.markdown-body a) { - color: var(--v-theme-primary); - text-decoration: none; -} -:deep(.markdown-body a:hover) { - text-decoration: underline; -} - -:deep(.markdown-body table) { - border-spacing: 0; - border-collapse: collapse; - width: 100%; - margin-bottom: 0; - border: 1px solid var(--markdown-border); -} -:deep(.markdown-body .table-container) { - width: 100%; - overflow-x: auto; - margin-bottom: 16px; - border: 1px solid var(--markdown-border); - border-radius: 6px; -} - -:deep(.markdown-body table th), -:deep(.markdown-body table td) { - padding: 6px 13px; - border: 1px solid var(--markdown-border); -} -:deep(.markdown-body table th) { - font-weight: 600; - background-color: rgba(128, 128, 128, 0.1); -} -:deep(.markdown-body table tr) { - background-color: transparent; -} -:deep(.markdown-body table tr:nth-child(2n)) { - background-color: rgba(128, 128, 128, 0.05); -} - -:deep(.markdown-body hr) { - height: 0.25em; - padding: 0; - margin: 24px 0; - background-color: var(--v-theme-containerBg); - border: 0; -} - -:deep(.markdown-body details) { - margin-bottom: 16px; - border: 1px solid var(--v-theme-border); - border-radius: 6px; - padding: 8px 12px; - background-color: var(--v-theme-surface); -} - -:deep(.markdown-body details[open]) { - padding-bottom: 12px; -} -:deep(.markdown-body summary) { - cursor: pointer; - font-weight: 600; - padding: 4px 0; - list-style: none; - display: flex; - align-items: center; - gap: 6px; -} - -:deep(.markdown-body summary::before) { - content: "▶"; - font-size: 0.75em; - transition: transform 0.2s ease; -} -:deep(.markdown-body details[open] summary::before) { - transform: rotate(90deg); -} -:deep(.markdown-body summary::-webkit-details-marker) { - display: none; -} -:deep(.markdown-body details > *:not(summary)) { - margin-top: 12px; -} - -</style> diff --git a/legacy-dashboard/src/components/shared/SidebarCustomizer.vue b/legacy-dashboard/src/components/shared/SidebarCustomizer.vue deleted file mode 100644 index cf5bcc1c..00000000 --- a/legacy-dashboard/src/components/shared/SidebarCustomizer.vue +++ /dev/null @@ -1,269 +0,0 @@ -<template> - <div style="margin-top: 16px;"> - <v-btn - color="primary" - variant="tonal" - size="small" - @click="openDialog" - style="margin-bottom: 8px;" - > - {{ t('features.settings.sidebar.customize.title') }} - </v-btn> - - <v-dialog v-model="dialog" max-width="700px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex justify-space-between align-center"> - <span>{{ t('features.settings.sidebar.customize.title') }}</span> - <v-btn - icon="mdi-close" - variant="text" - @click="dialog = false" - ></v-btn> - </v-card-title> - - <v-card-text> - <p class="text-body-2 mb-4">{{ t('features.settings.sidebar.customize.subtitle') }}</p> - - <v-row> - <v-col cols="12" md="6"> - <div class="mb-2 font-weight-medium">{{ t('features.settings.sidebar.customize.mainItems') }}</div> - <v-list - density="compact" - class="custom-list" - @dragover.prevent - @drop="handleDropToList($event, 'main')" - > - <v-list-item - v-for="(item, index) in mainItems" - :key="item.title" - class="mb-1 draggable-item" - draggable="true" - @dragstart="handleDragStart($event, 'main', index)" - @dragover.prevent - @drop.stop="handleDrop($event, 'main', index)" - > - <template v-slot:prepend> - <v-icon :icon="item.icon" size="small" class="mr-2"></v-icon> - </template> - <v-list-item-title>{{ t(item.title) }}</v-list-item-title> - <template v-slot:append> - <v-btn - icon="mdi-arrow-right" - variant="text" - size="x-small" - @click="moveToMore(index)" - ></v-btn> - </template> - </v-list-item> - </v-list> - </v-col> - - <v-col cols="12" md="6"> - <div class="mb-2 font-weight-medium">{{ t('features.settings.sidebar.customize.moreItems') }}</div> - <v-list - density="compact" - class="custom-list" - @dragover.prevent - @drop="handleDropToList($event, 'more')" - > - <v-list-item - v-for="(item, index) in moreItems" - :key="item.title" - class="mb-1 draggable-item" - draggable="true" - @dragstart="handleDragStart($event, 'more', index)" - @dragover.prevent - @drop.stop="handleDrop($event, 'more', index)" - > - <template v-slot:prepend> - <v-icon :icon="item.icon" size="small" class="mr-2"></v-icon> - </template> - <v-list-item-title>{{ t(item.title) }}</v-list-item-title> - <template v-slot:append> - <v-btn - icon="mdi-arrow-left" - variant="text" - size="x-small" - @click="moveToMain(index)" - ></v-btn> - </template> - </v-list-item> - </v-list> - </v-col> - </v-row> - </v-card-text> - - <v-card-actions> - <v-btn - color="error" - variant="text" - @click="resetToDefault" - > - {{ t('features.settings.sidebar.customize.reset') }} - </v-btn> - <v-spacer></v-spacer> - <v-btn - color="primary" - variant="tonal" - @click="saveCustomization" - > - {{ t('core.actions.save') }} - </v-btn> - </v-card-actions> - </v-card> - </v-dialog> - </div> -</template> - -<script setup> -import { ref, onMounted } from 'vue'; -import { useI18n } from '@/i18n/composables'; -import sidebarItems from '@/layouts/full/vertical-sidebar/sidebarItem'; -import { - getSidebarCustomization, - setSidebarCustomization, - clearSidebarCustomization, - resolveSidebarItems -} from '@/utils/sidebarCustomization'; - -const { t } = useI18n(); - -const dialog = ref(false); -const mainItems = ref([]); -const moreItems = ref([]); -const draggedItem = ref(null); - -function initializeItems() { - const customization = getSidebarCustomization(); - const { mainItems: resolvedMain, moreItems: resolvedMore } = resolveSidebarItems( - sidebarItems, - customization - ); - mainItems.value = resolvedMain; - moreItems.value = resolvedMore; -} - -function openDialog() { - initializeItems(); - dialog.value = true; -} - -function handleDragStart(event, listType, index) { - draggedItem.value = { - type: listType, - index: index, - item: listType === 'main' ? mainItems.value[index] : moreItems.value[index] - }; - event.dataTransfer.effectAllowed = 'move'; -} - -function handleDrop(event, targetListType, targetIndex) { - event.preventDefault(); - - if (!draggedItem.value) return; - - const sourceListType = draggedItem.value.type; - const sourceIndex = draggedItem.value.index; - const item = draggedItem.value.item; - - // Remove from source - if (sourceListType === 'main') { - mainItems.value.splice(sourceIndex, 1); - } else { - moreItems.value.splice(sourceIndex, 1); - } - - // Add to target - if (targetListType === 'main') { - mainItems.value.splice(targetIndex, 0, item); - } else { - moreItems.value.splice(targetIndex, 0, item); - } - - draggedItem.value = null; -} - -function handleDropToList(event, targetListType) { - event.preventDefault(); - - if (!draggedItem.value) return; - - const sourceListType = draggedItem.value.type; - const sourceIndex = draggedItem.value.index; - const item = draggedItem.value.item; - - // Remove from source - if (sourceListType === 'main') { - mainItems.value.splice(sourceIndex, 1); - } else { - moreItems.value.splice(sourceIndex, 1); - } - - // Add to target list at the end - if (targetListType === 'main') { - mainItems.value.push(item); - } else { - moreItems.value.push(item); - } - - draggedItem.value = null; -} - -function moveToMore(index) { - const item = mainItems.value.splice(index, 1)[0]; - moreItems.value.push(item); -} - -function moveToMain(index) { - const item = moreItems.value.splice(index, 1)[0]; - mainItems.value.push(item); -} - -function saveCustomization() { - const config = { - mainItems: mainItems.value.map(item => item.title), - moreItems: moreItems.value.map(item => item.title) - }; - - setSidebarCustomization(config); - - // Notify the sidebar to reload - window.dispatchEvent(new CustomEvent('sidebar-customization-changed')); - - dialog.value = false; -} - -function resetToDefault() { - clearSidebarCustomization(); - initializeItems(); - - // Notify the sidebar to reload - window.dispatchEvent(new CustomEvent('sidebar-customization-changed')); -} - -onMounted(() => { - initializeItems(); -}); -</script> - -<style scoped> -.draggable-item { - cursor: move; - border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)); - border-radius: 4px; - background-color: rgba(var(--v-theme-surface)); - transition: all 0.2s; -} - -.draggable-item:hover { - background-color: rgba(var(--v-theme-primary), 0.1); - border-color: rgba(var(--v-theme-primary), 0.3); -} - -.custom-list { - min-height: 200px; - border: 1px dashed rgba(var(--v-border-color), var(--v-border-opacity)); - border-radius: 4px; - padding: 8px; -} -</style> diff --git a/legacy-dashboard/src/components/shared/StorageCleanupPanel.vue b/legacy-dashboard/src/components/shared/StorageCleanupPanel.vue deleted file mode 100644 index 9ecc8683..00000000 --- a/legacy-dashboard/src/components/shared/StorageCleanupPanel.vue +++ /dev/null @@ -1,240 +0,0 @@ -<template> - <div class="storage-cleanup-panel"> - <div class="storage-cleanup-card"> - <div class="storage-cleanup-row"> - <div class="storage-cleanup-info"> - <div class="storage-cleanup-title">{{ tm('system.cleanup.title') }}</div> - <div class="storage-cleanup-hint"> - {{ tm('system.cleanup.panel.subtitle', { size: formatBytes(storageStatus.total_bytes || 0) }) }} - </div> - </div> - <div class="storage-cleanup-control"> - <v-chip size="small" variant="tonal" class="storage-cleanup-chip"> - {{ formatBytes(storageStatus.total_bytes || 0) }} - </v-chip> - <v-menu location="bottom end"> - <template #activator="{ props }"> - <v-btn - v-bind="props" - size="small" - variant="tonal" - class="storage-cleanup-action" - :loading="Boolean(cleaningTarget)" - > - <v-icon class="mr-2">mdi-broom</v-icon> - {{ tm('system.cleanup.clean') }} - </v-btn> - </template> - <v-list density="compact" min-width="220"> - <v-list-item - v-for="item in storageCards" - :key="item.key" - :prepend-icon="item.icon" - :title="item.buttonText" - :subtitle="formatBytes(item.sizeBytes)" - :disabled="Boolean(cleaningTarget)" - @click="cleanupStorage(item.key)" - /> - <v-divider /> - <v-list-item - prepend-icon="mdi-delete-sweep-outline" - :title="tm('system.cleanup.cleanAll')" - :subtitle="formatBytes(storageStatus.total_bytes || 0)" - :disabled="Boolean(cleaningTarget)" - @click="cleanupStorage('all')" - /> - </v-list> - </v-menu> - </div> - </div> - </div> - </div> -</template> - -<script setup> -import { computed, onMounted, ref } from 'vue'; -import { statsApi } from '@/api/v1'; -import { useModuleI18n } from '@/i18n/composables'; -import { useToastStore } from '@/stores/toast'; -import { askForConfirmation, useConfirmDialog } from '@/utils/confirmDialog'; - -const { tm } = useModuleI18n('features/settings'); -const toastStore = useToastStore(); -const confirmDialog = useConfirmDialog(); - -const cleaningTarget = ref(''); -const storageStatus = ref({ - logs: { - size_bytes: 0, - file_count: 0, - path: '', - exists: false - }, - cache: { - size_bytes: 0, - file_count: 0, - path: '', - exists: false - }, - total_bytes: 0 -}); - -const showToast = (message, color = 'success') => { - toastStore.add({ - message, - color, - timeout: 3000 - }); -}; - -const formatBytes = (bytes) => { - const value = Number(bytes || 0); - if (value <= 0) return '0 B'; - - const units = ['B', 'KB', 'MB', 'GB', 'TB']; - let size = value; - let unitIndex = 0; - - while (size >= 1024 && unitIndex < units.length - 1) { - size /= 1024; - unitIndex += 1; - } - - const decimals = size >= 10 || unitIndex === 0 ? 0 : 1; - return `${size.toFixed(decimals)} ${units[unitIndex]}`; -}; - -const storageCards = computed(() => [ - { - key: 'cache', - buttonText: tm('system.cleanup.targets.cache.button'), - icon: 'mdi-database-refresh-outline', - sizeBytes: storageStatus.value.cache?.size_bytes || 0 - }, - { - key: 'logs', - buttonText: tm('system.cleanup.targets.logs.button'), - icon: 'mdi-file-document-outline', - sizeBytes: storageStatus.value.logs?.size_bytes || 0 - } -]); - -const loadStorageStatus = async () => { - try { - const res = await statsApi.storage(); - if (res.data.status !== 'ok') { - showToast(res.data.message || tm('system.cleanup.messages.statusFailed'), 'error'); - return; - } - storageStatus.value = res.data.data || storageStatus.value; - } catch (error) { - showToast(error?.response?.data?.message || tm('system.cleanup.messages.statusFailed'), 'error'); - } -}; - -const cleanupStorage = async (target) => { - const confirmed = await askForConfirmation( - tm('system.cleanup.confirm', { target: tm(`system.cleanup.targetNames.${target}`) }), - confirmDialog - ); - if (!confirmed) return; - - cleaningTarget.value = target; - try { - const res = await statsApi.cleanupStorage(target); - if (res.data.status !== 'ok') { - showToast(res.data.message || tm('system.cleanup.messages.cleanupFailed'), 'error'); - return; - } - - const cleanupData = res.data.data || {}; - storageStatus.value = cleanupData.status || storageStatus.value; - showToast( - tm('system.cleanup.messages.cleanupSuccess', { - size: formatBytes(cleanupData.removed_bytes || 0), - count: cleanupData.processed_files || 0 - }) - ); - } catch (error) { - showToast(error?.response?.data?.message || tm('system.cleanup.messages.cleanupFailed'), 'error'); - } finally { - cleaningTarget.value = ''; - } -}; - -onMounted(() => { - loadStorageStatus(); -}); -</script> - -<style scoped> -.storage-cleanup-panel { - margin: 0; -} - -.storage-cleanup-card { - overflow: hidden; - border: 1px solid var(--settings-border, rgba(17, 24, 39, 0.13)); - border-radius: 10px; - background: rgb(var(--v-theme-surface)); - box-shadow: none; -} - -.storage-cleanup-row { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(220px, 38%); - gap: 18px; - align-items: center; - min-height: 60px; - padding: 12px 16px; -} - -.storage-cleanup-title { - color: rgb(var(--v-theme-on-surface)); - font-size: 0.88rem; - font-weight: 700; - letter-spacing: 0; - line-height: 1.4; -} - -.storage-cleanup-hint { - margin-top: 4px; - color: rgba(var(--v-theme-on-surface), 0.76); - font-size: 0.78rem; - line-height: 1.45; -} - -.storage-cleanup-control { - display: flex; - flex-wrap: wrap; - gap: 8px; - align-items: center; - justify-content: flex-end; - min-width: 0; - justify-self: end; -} - -.storage-cleanup-control :deep(.v-btn), -.storage-cleanup-control :deep(.v-chip) { - border-radius: 10px; -} - -.storage-cleanup-chip, -.storage-cleanup-action { - background: rgba(var(--v-theme-on-surface), 0.06) !important; - color: rgba(var(--v-theme-on-surface), 0.72) !important; -} - -@media (max-width: 720px) { - .storage-cleanup-row { - grid-template-columns: 1fr; - align-items: stretch; - padding: 14px 16px; - } - - .storage-cleanup-control { - justify-content: flex-start; - justify-self: stretch; - } -} -</style> diff --git a/legacy-dashboard/src/components/shared/StyledMenu.vue b/legacy-dashboard/src/components/shared/StyledMenu.vue deleted file mode 100644 index 01c58099..00000000 --- a/legacy-dashboard/src/components/shared/StyledMenu.vue +++ /dev/null @@ -1,93 +0,0 @@ -<template> - <v-menu v-bind="$attrs" :close-on-content-click="closeOnContentClick"> - <template v-slot:activator="{ props: activatorProps }"> - <slot name="activator" :props="activatorProps"></slot> - </template> - - <v-card - class="styled-menu-card" - :class="{ 'styled-menu-card-borderless': noBorder }" - elevation="0" - rounded="lg" - > - <v-list density="compact" class="styled-menu-list pa-1"> - <slot></slot> - </v-list> - </v-card> - </v-menu> -</template> - -<script setup lang="ts"> -defineOptions({ - inheritAttrs: false -}) - -withDefaults(defineProps<{ - closeOnContentClick?: boolean - noBorder?: boolean -}>(), { - closeOnContentClick: true, - noBorder: false -}) -</script> - -<style> -.styled-menu-card { - min-width: 100px; - width: fit-content; - border: 0 !important; - background: rgba(var(--v-theme-surface), 0.98) !important; - backdrop-filter: blur(10px); - box-shadow: var(--astrbot-menu-shadow, 0 12px 28px rgba(0, 0, 0, 0.08)) !important; -} - -.styled-menu-card-borderless { - border: 0 !important; -} - -.styled-menu-list { - background: transparent !important; -} - -.styled-menu-item { - margin: 2px 0; - transition: all 0.2s ease; - border-radius: 6px; -} - -.styled-menu-item:hover { - background: rgba(var(--v-theme-primary), 0.08) !important; -} - -.styled-menu-item-active { - background: rgba(var(--v-theme-primary), 0.15) !important; - font-weight: 500; -} - -.styled-menu-item-active:hover { - background: rgba(var(--v-theme-primary), 0.2) !important; -} - -/* 深色模式下的下拉框样式 - 需要全局样式才能检测主题 */ -.v-theme--PurpleThemeDark .styled-menu-card { - background: rgba(var(--v-theme-surface), 0.98) !important; - border: 0 !important; -} - -.v-theme--PurpleThemeDark .styled-menu-card-borderless { - border: 0 !important; -} - -/* 深色模式下的列表项悬停效果 */ -.v-theme--PurpleThemeDark .styled-menu-item:hover { - background: rgba(var(--v-theme-primary), 0.12) !important; -} - -.v-theme--PurpleThemeDark .styled-menu-item-active { - background: rgba(var(--v-theme-primary), 0.2) !important; -} - -.v-theme--PurpleThemeDark .styled-menu-item-active:hover { - background: rgba(var(--v-theme-primary), 0.25) !important; -} -</style> diff --git a/legacy-dashboard/src/components/shared/T2ITemplateEditor.vue b/legacy-dashboard/src/components/shared/T2ITemplateEditor.vue deleted file mode 100644 index 8ec80070..00000000 --- a/legacy-dashboard/src/components/shared/T2ITemplateEditor.vue +++ /dev/null @@ -1,658 +0,0 @@ -<template> - <v-dialog v-model="dialog" max-width="1400px" persistent scrollable> - <template v-slot:activator="{ props }"> - <div class="t2i-template-editor-trigger"> - <v-btn - v-bind="props" - variant="tonal" - color="primary" - size="small" - class="t2i-template-editor-button" - :loading="loading" - > - <v-icon class="mr-2">mdi-code-tags</v-icon> - {{ tm('t2iTemplateEditor.buttonText') }} - </v-btn> - </div> - </template> - - <v-card class="t2i-template-editor"> - <v-card-title class="text-h3 pa-4 pb-0 pl-6 d-flex align-center justify-space-between"> - <span>{{ tm('t2iTemplateEditor.dialogTitle') }}</span> - <v-spacer></v-spacer> - <div class="d-flex align-center gap-2" style="width: 60%"> - <v-text-field - v-if="isCreatingNew" - v-model="editingName" - :label="tm('t2iTemplateEditor.newTemplateNameLabel')" - density="compact" - hide-details - variant="outlined" - class="flex-grow-1" - autofocus - :rules="[v => !!v || tm('t2iTemplateEditor.nameRequired')]" - ></v-text-field> - <v-select - v-else - v-model="selectedTemplate" - :items="templates" - item-title="name" - item-value="name" - :label="tm('t2iTemplateEditor.selectTemplateLabel')" - density="compact" - hide-details - variant="outlined" - class="flex-grow-1" - :loading="loading" - > - <template v-slot:item="{ props, item }"> - <v-list-item v-bind="props" :title="item.raw.name"> - <template v-slot:append> - <v-chip - v-if="item.raw.name === activeTemplate" - color="success" - variant="tonal" - size="small" - class="ml-2" - > - {{ tm('t2iTemplateEditor.applied') }} - </v-chip> - <v-btn - v-else - variant="text" - color="primary" - size="small" - class="ml-2" - @click.stop="setActiveTemplate(item.raw.name)" - :loading="applyLoading" - > - {{ tm('t2iTemplateEditor.apply') }} - </v-btn> - </template> - </v-list-item> - </template> - </v-select> - <v-btn - variant="text" - icon - @click="closeDialog" - > - <v-icon>mdi-close</v-icon> - </v-btn> - </div> - </v-card-title> - - <v-card-text class="pa-0"> - <v-row no-gutters style="height: 70vh;"> - <!-- 左侧编辑器 --> - <v-col cols="6" class="d-flex flex-column"> - <v-toolbar density="compact" color="surface-variant"> - <v-toolbar-title class="text-subtitle-2">{{ tm('t2iTemplateEditor.templateEditor') }}</v-toolbar-title> - <v-spacer></v-spacer> - <div class="d-flex align-center pa-1" style="border: 1px solid rgba(0,0,0,0.1); border-radius: 8px;"> - <v-btn - variant="text" - size="small" - @click="newTemplate" - color="success" - > - <v-icon left>mdi-plus</v-icon> - {{ tm('t2iTemplateEditor.new') }} - </v-btn> - <v-divider vertical class="mx-1"></v-divider> - <v-btn - variant="text" - size="small" - @click="resetToDefault" - :loading="resetLoading" - color="warning" - > - {{ tm('t2iTemplateEditor.resetBase') }} - </v-btn> - <v-btn - variant="text" - size="small" - @click="promptDelete" - color="error" - :disabled="isCreatingNew || selectedTemplate === 'base' || !selectedTemplate" - > - {{ tm('t2iTemplateEditor.delete') }} - </v-btn> - <v-divider vertical class="mx-1"></v-divider> - <v-btn - variant="text" - size="small" - @click="saveTemplate" - :loading="saveLoading" - color="primary" - :disabled="(isCreatingNew && !editingName) || (!isCreatingNew && !selectedTemplate)" - > - {{ tm('t2iTemplateEditor.save') }} - </v-btn> - </div> - </v-toolbar> - <div class="flex-grow-1" style="border-right: 1px solid rgba(0,0,0,0.1);"> - <VueMonacoEditor - v-model:value="templateContent" - :theme="editorTheme" - language="html" - :options="editorOptions" - style="height: 100%;" - /> - </div> - </v-col> - - <!-- 右侧预览 --> - <v-col cols="6" class="d-flex flex-column"> - <v-toolbar density="compact" color="surface-variant"> - <v-toolbar-title class="text-subtitle-2">{{ tm('t2iTemplateEditor.livePreview') }}</v-toolbar-title> - <v-spacer></v-spacer> - <v-btn - variant="text" - size="small" - @click="refreshPreview" - :loading="previewLoading" - > - {{ tm('t2iTemplateEditor.refreshPreview') }} - </v-btn> - </v-toolbar> - <div class="flex-grow-1 preview-container"> - <iframe - ref="previewFrame" - :srcdoc="previewContent" - style="width: 100%; height: 100%; border: none; zoom: 0.6;" - /> - </div> - </v-col> - </v-row> - </v-card-text> - - <v-card-actions class="px-6 py-4"> - <v-row no-gutters class="align-center"> - <v-col> - <div class="text-caption text-grey"> - <v-icon size="16" class="mr-1">mdi-information</v-icon> - {{ tm('t2iTemplateEditor.syntaxHint') }} - </div> - </v-col> - <v-col cols="auto"> - <v-btn - variant="text" - @click="closeDialog" - > - {{ t('core.common.cancel') }} - </v-btn> - <v-btn - color="primary" - variant="tonal" - @click="promptApplyAndClose" - :loading="saveLoading" - :disabled="isCreatingNew || !selectedTemplate" - > - {{ tm('t2iTemplateEditor.saveAndApply') }} - </v-btn> - </v-col> - </v-row> - </v-card-actions> - </v-card> - - <!-- 确认重置对话框 --> - <v-dialog v-model="resetDialog" max-width="400px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('t2iTemplateEditor.confirmReset') }}</v-card-title> - <v-card-text> - {{ tm('t2iTemplateEditor.confirmResetMessage') }} - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="resetDialog = false">{{ t('core.common.cancel') }}</v-btn> - <v-btn color="warning" variant="tonal" @click="confirmReset" :loading="resetLoading">{{ tm('t2iTemplateEditor.confirmResetButton') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <!-- 删除确认对话框 --> - <v-dialog v-model="deleteDialog" max-width="400px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('t2iTemplateEditor.confirmDelete') }}</v-card-title> - <v-card-text> - {{ tm('t2iTemplateEditor.confirmDeleteMessage', { name: selectedTemplate }) }} - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="deleteDialog = false">{{ t('core.common.cancel') }}</v-btn> - <v-btn color="error" variant="tonal" @click="confirmDelete" :loading="saveLoading">{{ tm('t2iTemplateEditor.confirmDeleteButton') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - <!-- 保存并应用确认对话框 --> - <v-dialog v-model="applyAndCloseDialog" max-width="500px"> - <v-card> - <v-card-title class="text-h3 pa-4 pb-0 pl-6">{{ tm('t2iTemplateEditor.confirmAction') }}</v-card-title> - <v-card-text> - {{ tm('t2iTemplateEditor.confirmApplyMessage', { name: selectedTemplate }) }} - </v-card-text> - <v-card-actions> - <v-spacer></v-spacer> - <v-btn variant="text" @click="applyAndCloseDialog = false">{{ t('core.common.cancel') }}</v-btn> - <v-btn color="primary" variant="tonal" @click="confirmApplyAndClose" :loading="saveLoading">{{ t('core.common.confirm') }}</v-btn> - </v-card-actions> - </v-card> - </v-dialog> - - </v-dialog> -</template> - -<script setup> -import { ref, computed, nextTick, watch } from 'vue' -import { VueMonacoEditor } from '@guolao/vue-monaco-editor' -import { useI18n, useModuleI18n } from '@/i18n/composables' -import { useToast } from '@/utils/toast' -import { statsApi, t2iApi } from '@/api/v1' - -const { t } = useI18n() -const { tm } = useModuleI18n('core.shared') -const toast = useToast() - -// --- 响应式数据 --- -const dialog = ref(false) -const loading = ref(false) // 用于加载模板列表 -const saveLoading = ref(false) -const resetLoading = ref(false) -const previewLoading = ref(false) -const applyLoading = ref(false) - -// 模板管理 -const templates = ref([]) -const activeTemplate = ref('base') -const selectedTemplate = ref(null) -const editingName = ref('') // 用于新建模式下的名称输入 -const templateContent = ref('') -const isCreatingNew = ref(false) - -// 对话框状态 -const resetDialog = ref(false) -const deleteDialog = ref(false) -const applyAndCloseDialog = ref(false) - -const previewFrame = ref(null) - -// --- 编辑器配置 --- -const editorTheme = computed(() => 'vs-light') -const editorOptions = { - automaticLayout: true, - fontSize: 12, - lineNumbers: 'on', - wordWrap: 'on', - minimap: { enabled: false }, - scrollBeyondLastLine: false, -} - -// --- 预览逻辑 --- -const previewVersion = ref('v4.0.0') -const syncPreviewVersion = async () => { - try { - const res = await statsApi.version() - const rawVersion = res?.data?.data?.version || res?.data?.version - if (rawVersion) { - previewVersion.value = rawVersion.startsWith('v') ? rawVersion : `v${rawVersion}` - } - } catch (error) { - console.warn('Failed to fetch version:', error) - } -} - -const previewData = computed(() => ({ - text: tm('t2iTemplateEditor.previewText') || '这是一个示例文本,用于预览模板效果。\n\n这里可以包含多行文本,支持换行和各种格式。', - version: previewVersion.value -})) - -const injectShikiRuntime = (content) => { - if (content.includes('astrbot-t2i-shiki-runtime')) { - return content - } - - const runtimeScript = getShikiRuntimeScript() - const headClose = content.search(/<\/head\s*>/i) - if (headClose >= 0) { - return `${content.slice(0, headClose)} ${runtimeScript}\n${content.slice(headClose)}` - } - - return `${runtimeScript}\n${content}` -} - -const getShikiRuntimeScript = () => '<script id="astrbot-t2i-shiki-runtime" src="/t2i/shiki_runtime.iife.js"></scr' + 'ipt>' - -const hasMarkdownSource = (content) => /<[^>]+\bid=["']markdown-source["']/i.test(content) - -const insertMarkdownSource = (content) => { - const sourceElement = ' <textarea id="markdown-source" hidden>{{ text | safe }}</textarea>\n' - const markedScript = content.search(/^[ \t]*<script\s+src=["']https:\/\/cdn\.jsdelivr\.net\/npm\/marked\/marked\.min\.js["']><\/script>[ \t]*\r?\n?/im) - if (markedScript >= 0) { - return `${content.slice(0, markedScript)}${sourceElement}${content.slice(markedScript)}` - } - - const bodyClose = content.search(/<\/body\s*>/i) - if (bodyClose >= 0) { - return `${content.slice(0, bodyClose)}${sourceElement}${content.slice(bodyClose)}` - } - - return `${sourceElement}${content}` -} - -const normalizeMarkdownSource = (content) => { - let normalized = content.replace( - /<script\s+id=["']markdown-source["']\s+type=["']text\/plain["']>\s*\{\{\s*text\s*\|\s*safe\s*\}\}\s*<\/script>/gi, - '<textarea id="markdown-source" hidden>{{ text | safe }}</textarea>' - ) - - normalized = normalized.replace( - /decodeBase64Utf8\("\{\{\s*text_base64\s*\}\}"\)/g, - 'document.getElementById("markdown-source").value' - ) - normalized = normalized.replace( - /document\.getElementById\(["']markdown-source["']\)\.textContent/g, - 'document.getElementById("markdown-source").value' - ) - - if (/\{\{\s*text_base64\s*\}\}/.test(normalized) && !hasMarkdownSource(normalized)) { - normalized = insertMarkdownSource(normalized) - } - - return normalized -} - -const previewContent = computed(() => { - try { - let content = normalizeMarkdownSource(templateContent.value) - content = content.replace(/\{\{\s*text\s*\|\s*safe\s*\}\}/g, () => previewData.value.text) - content = content.replace(/\{\{\s*version\s*\}\}/g, () => previewData.value.version) - let usedExistingShikiPlaceholder = false - content = content.replace(/<script\b[^>]*>\s*\{\{\s*shiki_runtime\s*\|\s*safe\s*\}\}\s*<\/script>/gi, () => { - usedExistingShikiPlaceholder = true - return getShikiRuntimeScript() - }) - content = content.replace(/\{\{\s*shiki_runtime\s*\|\s*safe\s*\}\}/g, () => { - usedExistingShikiPlaceholder = true - return getShikiRuntimeScript() - }) - return usedExistingShikiPlaceholder ? content : injectShikiRuntime(content) - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error) - return `<div style="color: red; padding: 20px;">模板渲染错误: ${errorMessage}</div>` - } -}) - -// --- API 调用方法 --- -const loadInitialData = async () => { - loading.value = true - try { - const [listRes, activeRes] = await Promise.all([ - t2iApi.listTemplates(), - t2iApi.getActiveTemplate() - ]) - - if (listRes.data.status === 'ok') { - templates.value = listRes.data.data - } else { - console.error('加载模板列表失败:', listRes.data.message) - } - - if (activeRes.data.status === 'ok') { - activeTemplate.value = activeRes.data.data.active_template - } else { - console.error('加载活动模板失败:', activeRes.data.message) - } - - // 设置初始选中的模板 - if (templates.value.length > 0) { - selectedTemplate.value = activeTemplate.value - } - - } catch (error) { - console.error('加载初始数据失败:', error) - } finally { - loading.value = false - } -} - -const loadTemplateContent = async (name) => { - if (!name) return - previewLoading.value = true - try { - const response = await t2iApi.getTemplate(name) - if (response.data.status === 'ok') { - templateContent.value = response.data.data.content - } else { - console.error(`加载模板 '${name}' 失败:`, response.data.message) - } - } catch (error) { - console.error(`加载模板 '${name}' 失败:`, error) - } finally { - previewLoading.value = false - } -} - -const saveTemplate = async () => { - saveLoading.value = true - try { - if (isCreatingNew.value) { - // --- 创建新模板 --- - if (!editingName.value) return - const response = await t2iApi.createTemplate({ - name: editingName.value, - content: templateContent.value - }) - await loadInitialData() // 重新加载所有数据 - selectedTemplate.value = response.data.data.name - isCreatingNew.value = false - } else { - // --- 更新现有模板 --- - if (!selectedTemplate.value) return - await t2iApi.updateTemplate(selectedTemplate.value, templateContent.value) - } - } catch (error) { - const msg = error?.response?.data?.message || error?.message || String(error) - console.error('保存模板失败:', msg) - toast.error(msg) - } finally { - saveLoading.value = false - } -} - -const setActiveTemplate = async (name) => { - applyLoading.value = true - try { - await t2iApi.setActiveTemplate(name) - activeTemplate.value = name - } catch (error) { - const msg = error?.response?.data?.message || error?.message || String(error) - console.error(`应用模板 '${name}' 失败:`, msg) - toast.error(msg) - } finally { - applyLoading.value = false - } -} - -const confirmDelete = async () => { - if (!selectedTemplate.value || selectedTemplate.value === 'base') return - saveLoading.value = true - try { - const nameToDelete = selectedTemplate.value - await t2iApi.deleteTemplate(nameToDelete) - deleteDialog.value = false - - // 如果删除的是当前活动模板,则将活动模板重置为base - if (activeTemplate.value === nameToDelete) { - await setActiveTemplate('base') - } - await loadInitialData() - selectedTemplate.value = 'base' - } catch (error) { - const msg = error?.response?.data?.message || error?.message || String(error) - console.error(`删除模板失败:`, msg) - toast.error(msg) - } finally { - saveLoading.value = false - } -} - -const confirmReset = async () => { - resetLoading.value = true - try { - await t2iApi.resetDefaultTemplate() - resetDialog.value = false - if (selectedTemplate.value === 'base') { - await loadTemplateContent('base') - } - if (activeTemplate.value !== 'base') { - await setActiveTemplate('base') - } - } catch (error) { - const msg = error?.response?.data?.message || error?.message || String(error) - console.error('重置模板失败:', msg) - toast.error(msg) - } finally { - resetLoading.value = false - } -} - -// --- UI 交互方法 --- - -const resetToDefault = () => { - resetDialog.value = true -} - -const newTemplate = () => { - isCreatingNew.value = true - selectedTemplate.value = null - editingName.value = '' - templateContent.value = `<!doctype html> -<html> -<head> - <meta charset="utf-8"/> - <title>New Template - - - -

{{ text | safe }}
- - -` -} - -const promptDelete = () => { - if (selectedTemplate.value && selectedTemplate.value !== 'base') { - deleteDialog.value = true - } -} - -const promptApplyAndClose = () => { - if (!isCreatingNew.value && selectedTemplate.value) { - applyAndCloseDialog.value = true - } -} - -const confirmApplyAndClose = async () => { - if (isCreatingNew.value) return - - await saveTemplate() - await setActiveTemplate(selectedTemplate.value) - applyAndCloseDialog.value = false - closeDialog() -} - -const refreshPreview = () => { - previewLoading.value = true - syncPreviewVersion() - nextTick(() => { - if (previewFrame.value) { - previewFrame.value.contentWindow.location.reload() - } - setTimeout(() => previewLoading.value = false, 500) - }) -} - -const closeDialog = () => { - dialog.value = false -} - -// --- 监听器和生命周期 --- - -watch(dialog, (newVal) => { - if (newVal) { - syncPreviewVersion() - loadInitialData() - } else { - // 关闭时重置状态 - selectedTemplate.value = null - templateContent.value = '' - isCreatingNew.value = false - } -}) - -watch(selectedTemplate, (newName) => { - if (newName) { - isCreatingNew.value = false - loadTemplateContent(newName) - } -}) - -defineExpose({ - openDialog: () => { - dialog.value = true - } -}) - - - - - diff --git a/legacy-dashboard/src/components/shared/TemplateListEditor.vue b/legacy-dashboard/src/components/shared/TemplateListEditor.vue deleted file mode 100644 index ecfda28f..00000000 --- a/legacy-dashboard/src/components/shared/TemplateListEditor.vue +++ /dev/null @@ -1,538 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/components/shared/ThemeAwareMarkdownCodeBlock.vue b/legacy-dashboard/src/components/shared/ThemeAwareMarkdownCodeBlock.vue deleted file mode 100644 index 902cc511..00000000 --- a/legacy-dashboard/src/components/shared/ThemeAwareMarkdownCodeBlock.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - diff --git a/legacy-dashboard/src/components/shared/TraceDisplayer.vue b/legacy-dashboard/src/components/shared/TraceDisplayer.vue deleted file mode 100644 index 8c35f0b5..00000000 --- a/legacy-dashboard/src/components/shared/TraceDisplayer.vue +++ /dev/null @@ -1,487 +0,0 @@ - - - - - - - diff --git a/legacy-dashboard/src/components/shared/UmoDisplay.vue b/legacy-dashboard/src/components/shared/UmoDisplay.vue deleted file mode 100644 index 4ad1873d..00000000 --- a/legacy-dashboard/src/components/shared/UmoDisplay.vue +++ /dev/null @@ -1,225 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/components/shared/UninstallConfirmDialog.vue b/legacy-dashboard/src/components/shared/UninstallConfirmDialog.vue deleted file mode 100644 index 0354ce21..00000000 --- a/legacy-dashboard/src/components/shared/UninstallConfirmDialog.vue +++ /dev/null @@ -1,135 +0,0 @@ - - - diff --git a/legacy-dashboard/src/components/shared/UpgradeRecoveryDialog.vue b/legacy-dashboard/src/components/shared/UpgradeRecoveryDialog.vue deleted file mode 100644 index 1599beb3..00000000 --- a/legacy-dashboard/src/components/shared/UpgradeRecoveryDialog.vue +++ /dev/null @@ -1,290 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/components/shared/WaitingForRestart.vue b/legacy-dashboard/src/components/shared/WaitingForRestart.vue deleted file mode 100644 index 9ba68a61..00000000 --- a/legacy-dashboard/src/components/shared/WaitingForRestart.vue +++ /dev/null @@ -1,100 +0,0 @@ - - - diff --git a/legacy-dashboard/src/composables/useConfigTextResolver.js b/legacy-dashboard/src/composables/useConfigTextResolver.js deleted file mode 100644 index 757b724b..00000000 --- a/legacy-dashboard/src/composables/useConfigTextResolver.js +++ /dev/null @@ -1,33 +0,0 @@ -import { useModuleI18n } from '@/i18n/composables' -import { usePluginI18n } from '@/utils/pluginI18n' - -export function useConfigTextResolver(props = {}) { - const { tm, getRaw } = useModuleI18n('features/config-metadata') - const { configText } = usePluginI18n() - - const translateIfKey = (value) => { - if (!value || typeof value !== 'string') return value - return getRaw(value) ? tm(value) : value - } - - const hasPluginI18n = () => { - return Boolean( - props.pluginName - && props.pluginI18n - && Object.keys(props.pluginI18n).length > 0, - ) - } - - const resolveConfigText = (path, attr, fallback) => { - const fallbackText = translateIfKey(fallback) || '' - if (!hasPluginI18n()) { - return fallbackText - } - return configText(props.pluginI18n, path, attr, fallbackText) - } - - return { - translateIfKey, - resolveConfigText, - } -} diff --git a/legacy-dashboard/src/composables/useConversations.ts b/legacy-dashboard/src/composables/useConversations.ts deleted file mode 100644 index 40e9ed3e..00000000 --- a/legacy-dashboard/src/composables/useConversations.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { ref, computed } from 'vue'; -import { useRouter } from 'vue-router'; -import { chatApi } from '@/api/v1'; - -export interface Conversation { - cid: string; - title: string; - updated_at: number; -} - -export function useConversations(chatboxMode: boolean = false) { - const router = useRouter(); - const conversations = ref([]); - const selectedConversations = ref([]); - const currCid = ref(''); - const pendingCid = ref(null); - - // 编辑标题相关 - const editTitleDialog = ref(false); - const editingTitle = ref(''); - const editingCid = ref(''); - - const getCurrentConversation = computed(() => { - if (!currCid.value) return null; - return conversations.value.find(c => c.cid === currCid.value); - }); - - async function getConversations() { - try { - const response = await chatApi.listSessions(); - conversations.value = (response.data.data || []).map((session: any) => ({ - cid: session.session_id, - title: session.display_name || session.session_id, - updated_at: Date.parse(session.updated_at || '') || 0, - })); - - // 处理待加载的会话 - if (pendingCid.value) { - const conversation = conversations.value.find(c => c.cid === pendingCid.value); - if (conversation) { - selectedConversations.value = [pendingCid.value]; - pendingCid.value = null; - } - } else if (!currCid.value && conversations.value.length > 0) { - // 默认选择第一个会话 - const firstConversation = conversations.value[0]; - selectedConversations.value = [firstConversation.cid]; - } - } catch (err: any) { - if (err.response?.status === 401) { - router.push('/auth/login?redirect=/chatbox'); - } - console.error(err); - } - } - - async function newConversation() { - try { - const response = await chatApi.createSession(); - const cid = response.data.data.session_id; - currCid.value = cid; - - // 更新 URL - const basePath = chatboxMode ? '/chatbox' : '/chat'; - router.push(`${basePath}/${cid}`); - - await getConversations(); - return cid; - } catch (err) { - console.error(err); - throw err; - } - } - - async function deleteConversation(cid: string) { - try { - await chatApi.deleteSession(cid); - await getConversations(); - currCid.value = ''; - selectedConversations.value = []; - } catch (err) { - console.error(err); - } - } - - function showEditTitleDialog(cid: string, title: string) { - editingCid.value = cid; - editingTitle.value = title || ''; - editTitleDialog.value = true; - } - - async function saveTitle() { - if (!editingCid.value) return; - - const trimmedTitle = editingTitle.value.trim(); - try { - await chatApi.updateSession(editingCid.value, { - display_name: trimmedTitle, - }); - - // 更新本地会话标题 - const conversation = conversations.value.find(c => c.cid === editingCid.value); - if (conversation) { - conversation.title = trimmedTitle; - } - editTitleDialog.value = false; - } catch (err) { - console.error('重命名对话失败:', err); - } - } - - function updateConversationTitle(cid: string, title: string) { - const conversation = conversations.value.find(c => c.cid === cid); - if (conversation) { - conversation.title = title; - } - } - - function newChat(closeMobileSidebar?: () => void) { - currCid.value = ''; - selectedConversations.value = []; - - const basePath = chatboxMode ? '/chatbox' : '/chat'; - router.push(basePath); - - if (closeMobileSidebar) { - closeMobileSidebar(); - } - } - - return { - conversations, - selectedConversations, - currCid, - pendingCid, - editTitleDialog, - editingTitle, - editingCid, - getCurrentConversation, - getConversations, - newConversation, - deleteConversation, - showEditTitleDialog, - saveTitle, - updateConversationTitle, - newChat - }; -} diff --git a/legacy-dashboard/src/composables/useMediaHandling.ts b/legacy-dashboard/src/composables/useMediaHandling.ts deleted file mode 100644 index a3bd866c..00000000 --- a/legacy-dashboard/src/composables/useMediaHandling.ts +++ /dev/null @@ -1,209 +0,0 @@ -import { ref, computed } from 'vue'; -import { fileApi } from '@/api/v1'; - -export interface StagedFileInfo { - attachment_id: string; - filename: string; - original_name: string; - url: string; // blob URL for preview - type: string; // image, record, file, video - signature?: string; -} - -export function useMediaHandling() { - const stagedFiles = ref([]); - const mediaCache = ref>({}); - const pendingFileSignatures = new Set(); - - async function getFileSignature(file: File): Promise { - if (crypto?.subtle) { - const buffer = await file.arrayBuffer(); - const digest = await crypto.subtle.digest('SHA-256', buffer); - const hash = Array.from(new Uint8Array(digest)) - .map(byte => byte.toString(16).padStart(2, '0')) - .join(''); - return `sha256:${hash}`; - } - - return `meta:${file.name}:${file.size}:${file.type}:${file.lastModified}`; - } - - function isDuplicateFile(signature: string) { - return ( - pendingFileSignatures.has(signature) || - stagedFiles.value.some(file => file.signature === signature) - ); - } - - async function getMediaFile(filename: string): Promise { - if (mediaCache.value[filename]) { - return mediaCache.value[filename]; - } - - try { - const response = await fileApi.getByName(filename); - - const blobUrl = URL.createObjectURL(response.data); - mediaCache.value[filename] = blobUrl; - return blobUrl; - } catch (error) { - console.error('Error fetching media file:', error); - return ''; - } - } - - async function uploadStagedFile(file: File): Promise { - const signature = await getFileSignature(file); - if (isDuplicateFile(signature)) return undefined; - - pendingFileSignatures.add(signature); - const formData = new FormData(); - formData.append('file', file); - - try { - const response = await fileApi.upload(formData); - - const { attachment_id, filename, type } = response.data.data; - const stagedFile = { - attachment_id, - filename, - original_name: file.name, - url: URL.createObjectURL(file), - type, - signature - }; - stagedFiles.value.push(stagedFile); - return stagedFile; - } catch (err) { - console.error('Error uploading file:', err); - return undefined; - } finally { - pendingFileSignatures.delete(signature); - } - } - - async function processAndUploadImage(file: File) { - return uploadStagedFile(file); - } - - async function processAndUploadFile(file: File) { - return uploadStagedFile(file); - } - - async function handlePaste(event: ClipboardEvent) { - const items = event.clipboardData?.items; - if (!items) return; - - for (let i = 0; i < items.length; i++) { - if (items[i].type.indexOf('image') !== -1) { - const file = items[i].getAsFile(); - if (file) { - await processAndUploadImage(file); - } - } - } - } - - function removeImage(index: number) { - // 找到第 index 个图片类型的文件 - let imageCount = 0; - for (let i = 0; i < stagedFiles.value.length; i++) { - if (stagedFiles.value[i].type === 'image') { - if (imageCount === index) { - const fileToRemove = stagedFiles.value[i]; - if (fileToRemove.url.startsWith('blob:')) { - URL.revokeObjectURL(fileToRemove.url); - } - stagedFiles.value.splice(i, 1); - return; - } - imageCount++; - } - } - } - - function removeAudio() { - for (let i = stagedFiles.value.length - 1; i >= 0; i--) { - if (stagedFiles.value[i].type !== 'record') continue; - - const fileToRemove = stagedFiles.value[i]; - if (fileToRemove.url.startsWith('blob:')) { - URL.revokeObjectURL(fileToRemove.url); - } - stagedFiles.value.splice(i, 1); - } - } - - function removeFile(index: number) { - // Find the requested non-image, non-audio attachment. - let fileCount = 0; - for (let i = 0; i < stagedFiles.value.length; i++) { - if ( - stagedFiles.value[i].type !== 'image' && - stagedFiles.value[i].type !== 'record' - ) { - if (fileCount === index) { - const fileToRemove = stagedFiles.value[i]; - if (fileToRemove.url.startsWith('blob:')) { - URL.revokeObjectURL(fileToRemove.url); - } - stagedFiles.value.splice(i, 1); - return; - } - fileCount++; - } - } - } - - function clearStaged(options: { revokeUrls?: boolean } = {}) { - const { revokeUrls = true } = options; - if (revokeUrls) { - // 清理文件的 blob URLs - stagedFiles.value.forEach(file => { - if (file.url.startsWith('blob:')) { - URL.revokeObjectURL(file.url); - } - }); - } - stagedFiles.value = []; - } - - function cleanupMediaCache() { - Object.values(mediaCache.value).forEach(url => { - if (url.startsWith('blob:')) { - URL.revokeObjectURL(url); - } - }); - mediaCache.value = {}; - } - - // 计算属性:获取图片的 URL 列表(用于预览) - const stagedImagesUrl = computed(() => - stagedFiles.value.filter(f => f.type === 'image').map(f => f.url) - ); - - const stagedAudioUrl = computed(() => - stagedFiles.value.find(f => f.type === 'record')?.url || '' - ); - - // 计算属性:获取非图片文件列表 - const stagedNonImageFiles = computed(() => - stagedFiles.value.filter(f => f.type !== 'image' && f.type !== 'record') - ); - - return { - stagedImagesUrl, - stagedAudioUrl, - stagedFiles, - stagedNonImageFiles, - getMediaFile, - processAndUploadImage, - processAndUploadFile, - handlePaste, - removeImage, - removeAudio, - removeFile, - clearStaged, - cleanupMediaCache - }; -} diff --git a/legacy-dashboard/src/composables/useMessages.ts b/legacy-dashboard/src/composables/useMessages.ts deleted file mode 100644 index ca8708db..00000000 --- a/legacy-dashboard/src/composables/useMessages.ts +++ /dev/null @@ -1,1201 +0,0 @@ -import { computed, onBeforeUnmount, reactive, ref, type Ref } from "vue"; -import { chatApi, fileApi } from "@/api/v1"; -import { fetchWithAuth } from "@/api/http"; - -export type TransportMode = "sse" | "websocket"; - -export interface MessagePart { - type: string; - text?: string; - think?: string; - message_id?: string | number; - selected_text?: string; - embedded_url?: string; - embedded_file?: { url?: string; filename?: string; attachment_id?: string }; - attachment_id?: string; - filename?: string; - stored_filename?: string; - tool_calls?: ToolCall[]; - [key: string]: unknown; -} - -export interface ToolCall { - id?: string; - name?: string; - arguments?: unknown; - result?: unknown; - ts?: number; - finished_ts?: number; - [key: string]: unknown; -} - -export interface ChatContent { - type: "user" | "bot" | string; - message: MessagePart[]; - reasoning?: string; - isLoading?: boolean; - agentStats?: any; - refs?: any; -} - -export interface MessageDisplayBlock { - kind: "thinking" | "content"; - parts: MessagePart[]; -} - -export interface ChatRecord { - id?: string | number; - content: ChatContent; - created_at?: string; - sender_id?: string; - sender_name?: string; - llm_checkpoint_id?: string | null; - threads?: ChatThread[]; -} - -export interface ChatThread { - thread_id: string; - parent_session_id: string; - parent_message_id: number; - base_checkpoint_id: string; - selected_text: string; - created_at?: string; - updated_at?: string; -} - -export interface ChatSessionProject { - project_id: string; - title: string; - emoji?: string; -} - -interface ActiveConnection { - sessionId: string; - messageId: string; - transport: TransportMode; - abort?: AbortController; - ws?: WebSocket; - botRecord?: ChatRecord; - userRecord?: ChatRecord; - completed?: boolean; - errorShown?: boolean; -} - -interface SendMessageStreamOptions { - sessionId: string; - messageId: string; - parts: MessagePart[]; - transport: TransportMode; - enableStreaming?: boolean; - selectedProvider?: string; - selectedModel?: string; - userRecord?: ChatRecord; - botRecord: ChatRecord; - skipUserHistory?: boolean; - llmCheckpointId?: string | null; -} - -interface ContinueEditedMessageOptions { - sessionId: string; - sourceRecord: ChatRecord; - enableStreaming?: boolean; - selectedProvider?: string; - selectedModel?: string; -} - -interface CreateLocalExchangeOptions { - sessionId: string; - messageId: string; - parts: MessagePart[]; -} - -interface UseMessagesOptions { - currentSessionId: Ref; - onSessionsChanged?: () => Promise | void; - onStreamUpdate?: (sessionId: string) => void; -} - -export function useMessages(options: UseMessagesOptions) { - const loadingMessages = ref(false); - const sending = ref(false); - const messagesBySession = reactive>({}); - const loadedSessions = reactive>({}); - const activeConnections = reactive>({}); - const chatWebSockets: Record = {}; - const closingChatWebSockets = new WeakSet(); - const attachmentBlobCache = new Map>(); - const sessionProjects = reactive>( - {}, - ); - - const activeMessages = computed(() => - options.currentSessionId.value - ? messagesBySession[options.currentSessionId.value] || [] - : [], - ); - - onBeforeUnmount(() => { - cleanupConnections(); - for (const promise of attachmentBlobCache.values()) { - promise.then((url) => URL.revokeObjectURL(url)).catch(() => {}); - } - attachmentBlobCache.clear(); - }); - - function isSessionRunning(sessionId: string) { - return Boolean(activeConnections[sessionId]); - } - - function isUserMessage(msg: ChatRecord) { - return messageContent(msg).type === "user"; - } - - function messageContent(msg: ChatRecord): ChatContent { - return msg.content || { type: "bot", message: [] }; - } - - function messageParts(msg: ChatRecord): MessagePart[] { - const parts = messageContent(msg).message; - if (Array.isArray(parts)) return parts; - if (typeof parts === "string") return [{ type: "plain", text: parts }]; - return []; - } - - function isMessageStreaming(msg: ChatRecord, msgIndex: number) { - if ( - !options.currentSessionId.value || - !isSessionRunning(options.currentSessionId.value) - ) { - return false; - } - return !isUserMessage(msg) && msgIndex === activeMessages.value.length - 1; - } - - async function resolvePartMedia(part: MessagePart): Promise { - if (part.embedded_url) return; - let url: string; - let cacheKey: string; - const storedFilename = - typeof part.stored_filename === "string" ? part.stored_filename : ""; - const lookupFilename = storedFilename || part.filename || ""; - if (part.attachment_id) { - cacheKey = `att:${part.attachment_id}`; - url = fileApi.contentUrl(part.attachment_id); - } else if (lookupFilename) { - cacheKey = `file:${lookupFilename}`; - url = ""; - } else { - return; - } - let promise = attachmentBlobCache.get(cacheKey); - if (!promise) { - if (!part.attachment_id && lookupFilename) { - promise = fileApi - .getByName(lookupFilename) - .then((resp) => URL.createObjectURL(resp.data)); - } else { - promise = fetchWithAuth(url).then(async (resp) => { - if (!resp.ok) throw new Error(`Media request failed: ${resp.status}`); - return URL.createObjectURL(await resp.blob()); - }); - } - attachmentBlobCache.set(cacheKey, promise); - } - try { - part.embedded_url = await promise; - } catch (e) { - attachmentBlobCache.delete(cacheKey); - console.error("Failed to resolve media:", cacheKey, e); - } - } - - async function resolveRecordMedia(records: ChatRecord[]) { - const mediaTypes = ["image", "record", "video"]; - const tasks: Promise[] = []; - for (const record of records) { - for (const part of record.content?.message || []) { - if ( - mediaTypes.includes(part.type) && - !part.embedded_url && - (part.attachment_id || part.stored_filename || part.filename) - ) { - tasks.push(resolvePartMedia(part)); - } - } - } - await Promise.all(tasks); - } - - async function loadSessionMessages(sessionId: string) { - if (!sessionId) return; - loadingMessages.value = true; - try { - const response = await chatApi.getSession(sessionId); - const payload = response.data?.data || {}; - const history = payload.history || []; - const records = history.map(normalizeHistoryRecord); - attachThreads(records, payload.threads || []); - await resolveRecordMedia(records); - messagesBySession[sessionId] = records; - sessionProjects[sessionId] = normalizeSessionProject(payload.project); - loadedSessions[sessionId] = true; - } catch (error) { - console.error("Failed to load session messages:", error); - messagesBySession[sessionId] = messagesBySession[sessionId] || []; - } finally { - loadingMessages.value = false; - } - } - - function createLocalExchange({ - sessionId, - messageId, - parts, - }: CreateLocalExchangeOptions) { - loadedSessions[sessionId] = true; - messagesBySession[sessionId] = messagesBySession[sessionId] || []; - - const userRecord: ChatRecord = { - id: `local-user-${messageId}`, - created_at: new Date().toISOString(), - content: { - type: "user", - message: parts.map(stripUploadOnlyFields), - }, - }; - - const botRecord: ChatRecord = { - id: `local-bot-${messageId}`, - created_at: new Date().toISOString(), - content: { - type: "bot", - message: [], - reasoning: "", - isLoading: true, - }, - }; - - messagesBySession[sessionId].push(userRecord, botRecord); - - const sessionMessages = messagesBySession[sessionId]; - return { - userRecord: sessionMessages[sessionMessages.length - 2], - botRecord: sessionMessages[sessionMessages.length - 1], - }; - } - - function sendMessageStream({ - sessionId, - messageId, - parts, - transport, - enableStreaming = true, - selectedProvider = "", - selectedModel = "", - botRecord, - userRecord, - skipUserHistory = false, - llmCheckpointId = null, - }: SendMessageStreamOptions) { - if (transport === "websocket") { - startWebSocketStream( - sessionId, - messageId, - parts, - botRecord, - userRecord, - enableStreaming, - selectedProvider, - selectedModel, - ); - return; - } - startSseStream( - sessionId, - messageId, - parts, - botRecord, - userRecord, - enableStreaming, - selectedProvider, - selectedModel, - skipUserHistory, - llmCheckpointId, - ); - } - - async function editMessage( - sessionId: string, - record: ChatRecord, - editedText: string, - ) { - if (!sessionId || record.id == null) return { needsRegenerate: false }; - const content = cloneContentWithEditedText(record, editedText); - const response = await chatApi.updateMessage(sessionId, record.id, { - content: content as unknown as Record, - }); - const payload = response.data?.data || {}; - const updated = payload.message ? normalizeHistoryRecord(payload.message) : null; - if (updated) { - Object.assign(record, updated); - await resolveRecordMedia([record]); - } - if (payload.truncated_after_message) { - truncateMessagesAfter(sessionId, record); - } - return { - needsRegenerate: Boolean(payload.needs_regenerate), - truncatedAfterMessage: Boolean(payload.truncated_after_message), - }; - } - - function truncateMessagesAfter(sessionId: string, record: ChatRecord) { - const records = messagesBySession[sessionId]; - if (!records?.length || record.id == null) return; - const index = records.findIndex( - (message) => String(message.id) === String(record.id), - ); - if (index < 0) return; - messagesBySession[sessionId] = records.slice(0, index + 1); - } - - function continueEditedMessage({ - sessionId, - sourceRecord, - enableStreaming = true, - selectedProvider = "", - selectedModel = "", - }: ContinueEditedMessageOptions) { - if (!sessionId) return; - const parts = messageParts(sourceRecord).map(stripUploadOnlyFields); - const messageId = crypto.randomUUID?.() || `${Date.now()}-${Math.random()}`; - messagesBySession[sessionId] = messagesBySession[sessionId] || []; - - const botRecord: ChatRecord = { - id: `local-edited-bot-${messageId}`, - created_at: new Date().toISOString(), - content: { - type: "bot", - message: [], - reasoning: "", - isLoading: true, - }, - }; - messagesBySession[sessionId].push(botRecord); - - startSseStream( - sessionId, - messageId, - parts, - botRecord, - undefined, - enableStreaming, - selectedProvider, - selectedModel, - true, - sourceRecord.llm_checkpoint_id || null, - ); - } - - async function regenerateMessage( - sessionId: string, - botRecord: ChatRecord, - selectedProvider = "", - selectedModel = "", - ) { - if (!sessionId || botRecord.id == null) return; - const targetMessageId = botRecord.id; - - botRecord.id = `local-regenerate-${Date.now()}`; - botRecord.created_at = new Date().toISOString(); - botRecord.content = { - type: "bot", - message: [], - reasoning: "", - isLoading: true, - }; - - const abort = new AbortController(); - activeConnections[sessionId] = { - sessionId, - messageId: String(botRecord.id), - transport: "sse", - abort, - }; - - try { - const response = await fetchWithAuth(chatApi.regenerateMessageUrl(sessionId, targetMessageId), { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - selected_provider: selectedProvider, - selected_model: selectedModel, - }), - signal: abort.signal, - }); - if (!response.ok || !response.body) { - throw new Error(`Regenerate failed: ${response.status}`); - } - const contentType = response.headers.get("content-type") || ""; - if (!contentType.includes("text/event-stream")) { - const payload = await response.json().catch(() => null); - throw new Error(payload?.message || "Regenerate failed."); - } - await readSseStream(response.body, (payload) => { - processStreamPayload(botRecord, payload); - options.onStreamUpdate?.(sessionId); - }); - } catch (error) { - if (!abort.signal.aborted) { - appendPlain(botRecord, `\n\n${String((error as Error)?.message || error)}`); - console.error("Regenerate failed:", error); - } - } finally { - delete activeConnections[sessionId]; - await options.onSessionsChanged?.(); - } - } - - async function stopSession(sessionId: string) { - if (!sessionId) return; - await chatApi.stopSession(sessionId); - } - - function cleanupConnections() { - Object.values(activeConnections).forEach((connection) => { - connection.abort?.abort(); - }); - Object.values(chatWebSockets).forEach(closeTrackedWebSocket); - Object.keys(activeConnections).forEach((sessionId) => { - delete activeConnections[sessionId]; - }); - Object.keys(chatWebSockets).forEach((sessionId) => { - delete chatWebSockets[sessionId]; - }); - } - - function normalizeHistoryRecord(record: any): ChatRecord { - const content = record.content || {}; - const normalizedMessage = normalizeMessageParts( - content.message || [], - content.reasoning || "", - ); - const normalizedContent: ChatContent = { - type: content.type || (record.sender_id === "bot" ? "bot" : "user"), - message: normalizedMessage, - reasoning: extractReasoningText(normalizedMessage, content.reasoning || ""), - agentStats: content.agentStats || content.agent_stats, - refs: content.refs, - }; - - return { - ...record, - content: normalizedContent, - }; - } - - function attachThreads(records: ChatRecord[], threads: ChatThread[]) { - const threadsByMessage = new Map(); - for (const thread of threads) { - const key = String(thread.parent_message_id); - const list = threadsByMessage.get(key) || []; - list.push(thread); - threadsByMessage.set(key, list); - } - for (const record of records) { - const key = record.id == null ? "" : String(record.id); - record.threads = threadsByMessage.get(key) || []; - } - } - - function startSseStream( - sessionId: string, - messageId: string, - parts: MessagePart[], - botRecord: ChatRecord, - userRecord: ChatRecord | undefined, - enableStreaming: boolean, - selectedProvider: string, - selectedModel: string, - skipUserHistory = false, - llmCheckpointId: string | null = null, - ) { - const abort = new AbortController(); - activeConnections[sessionId] = { - sessionId, - messageId, - transport: "sse", - abort, - }; - - fetchWithAuth(chatApi.sendStreamUrl(), { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - session_id: sessionId, - message: parts.map(partToPayload), - enable_streaming: enableStreaming, - selected_provider: selectedProvider, - selected_model: selectedModel, - _skip_user_history: skipUserHistory, - _llm_checkpoint_id: llmCheckpointId || undefined, - }), - signal: abort.signal, - }) - .then(async (response) => { - if (!response.ok || !response.body) { - throw new Error(`SSE connection failed: ${response.status}`); - } - await readSseStream(response.body, (payload) => { - processStreamPayload(botRecord, payload, userRecord); - options.onStreamUpdate?.(sessionId); - }); - }) - .catch((error) => { - if (abort.signal.aborted) return; - appendPlain(botRecord, `\n\n${String(error?.message || error)}`); - console.error("SSE chat failed:", error); - }) - .finally(async () => { - delete activeConnections[sessionId]; - await options.onSessionsChanged?.(); - }); - } - - function startWebSocketStream( - sessionId: string, - messageId: string, - parts: MessagePart[], - botRecord: ChatRecord, - userRecord: ChatRecord | undefined, - enableStreaming: boolean, - selectedProvider: string, - selectedModel: string, - ) { - const ws = getOrCreateChatWebSocket(sessionId); - - activeConnections[sessionId] = { - sessionId, - messageId, - transport: "websocket", - ws, - botRecord, - userRecord, - completed: false, - errorShown: false, - }; - - sendWebSocketPayload(sessionId, messageId, { - ct: "chat", - t: "send", - session_id: sessionId, - message_id: messageId, - message: parts.map(partToPayload), - enable_streaming: enableStreaming, - selected_provider: selectedProvider, - selected_model: selectedModel, - }); - } - - function getOrCreateChatWebSocket(sessionId: string) { - const existing = chatWebSockets[sessionId]; - if ( - existing && - (existing.readyState === WebSocket.OPEN || - existing.readyState === WebSocket.CONNECTING) - ) { - return existing; - } - - const token = localStorage.getItem("token") || ""; - const ws = new WebSocket(chatApi.unifiedWebSocketUrl(token)); - chatWebSockets[sessionId] = ws; - - ws.onmessage = (event) => { - handleWebSocketMessage(sessionId, event); - }; - ws.onerror = () => { - const connection = activeConnections[sessionId]; - if (connection?.transport === "websocket" && connection.botRecord) { - connection.errorShown = true; - appendPlain(connection.botRecord, "\n\nWebSocket connection failed."); - } - }; - ws.onclose = async () => { - if (chatWebSockets[sessionId] === ws) { - delete chatWebSockets[sessionId]; - } - - const connection = activeConnections[sessionId]; - if (connection?.transport !== "websocket" || connection.ws !== ws) { - return; - } - if ( - !connection.completed && - !connection.errorShown && - !closingChatWebSockets.has(ws) && - connection.botRecord - ) { - appendPlain(connection.botRecord, "\n\nWebSocket connection closed."); - } - delete activeConnections[sessionId]; - await options.onSessionsChanged?.(); - }; - return ws; - } - - function sendWebSocketPayload( - sessionId: string, - messageId: string, - payload: Record, - ) { - const ws = getOrCreateChatWebSocket(sessionId); - const send = () => { - const connection = activeConnections[sessionId]; - if ( - connection?.transport !== "websocket" || - connection.messageId !== messageId || - connection.ws !== ws - ) { - return; - } - try { - ws.send(JSON.stringify(payload)); - } catch (error) { - connection.errorShown = true; - if (connection.botRecord) { - appendPlain(connection.botRecord, "\n\nWebSocket connection failed."); - } - console.error("Failed to send WebSocket payload:", error); - void finishWebSocketStream(sessionId, messageId); - } - }; - - if (ws.readyState === WebSocket.OPEN) { - send(); - return; - } - if (ws.readyState === WebSocket.CONNECTING) { - ws.addEventListener("open", send, { once: true }); - return; - } - void finishWebSocketStream(sessionId, messageId); - } - - function handleWebSocketMessage(sessionId: string, event: MessageEvent) { - const connection = activeConnections[sessionId]; - if (connection?.transport !== "websocket" || !connection.botRecord) { - return; - } - - try { - const payload = JSON.parse(event.data); - processStreamPayload(connection.botRecord, payload, connection.userRecord); - options.onStreamUpdate?.(sessionId); - if (payload.type === "end" || payload.t === "end") { - void finishWebSocketStream(sessionId, connection.messageId); - } - } catch (error) { - console.error("Failed to parse WebSocket payload:", error); - } - } - - async function finishWebSocketStream(sessionId: string, messageId: string) { - const connection = activeConnections[sessionId]; - if ( - connection?.transport !== "websocket" || - connection.messageId !== messageId - ) { - return; - } - connection.completed = true; - delete activeConnections[sessionId]; - await options.onSessionsChanged?.(); - } - - function closeTrackedWebSocket(ws: WebSocket) { - closingChatWebSockets.add(ws); - if ( - ws.readyState === WebSocket.OPEN || - ws.readyState === WebSocket.CONNECTING - ) { - ws.close(); - } - } - - function processStreamPayload( - botRecord: ChatRecord, - payload: any, - userRecord?: ChatRecord, - ) { - const normalized = - payload?.ct === "chat" - ? { ...payload, type: payload.type || payload.t } - : payload; - const msgType = normalized?.type || normalized?.t; - const chainType = normalized?.chain_type; - const data = normalized?.data ?? ""; - - if (msgType === "session_id" || msgType === "session_bound") return; - if (msgType === "user_message_saved") { - if (userRecord) { - userRecord.id = data?.id || userRecord.id; - userRecord.created_at = data?.created_at || userRecord.created_at; - userRecord.llm_checkpoint_id = - data?.llm_checkpoint_id || userRecord.llm_checkpoint_id; - } - return; - } - if (msgType === "message_saved") { - markMessageStarted(botRecord); - botRecord.id = data?.id || botRecord.id; - botRecord.created_at = data?.created_at || botRecord.created_at; - botRecord.llm_checkpoint_id = - data?.llm_checkpoint_id || botRecord.llm_checkpoint_id; - if (data?.refs) { - messageContent(botRecord).refs = data.refs; - } - return; - } - if (msgType === "agent_stats" || chainType === "agent_stats") { - markMessageStarted(botRecord); - messageContent(botRecord).agentStats = data; - return; - } - if (msgType === "error") { - markMessageStarted(botRecord); - appendPlain(botRecord, `\n\n${String(data)}`); - return; - } - if (msgType === "complete" || msgType === "break") { - markMessageStarted(botRecord); - const finalText = payloadText(data); - if (finalText && !hasPlainText(botRecord)) { - appendPlain(botRecord, finalText, false); - } - return; - } - if (msgType === "end") { - markMessageStarted(botRecord); - return; - } - - if (msgType === "plain") { - markMessageStarted(botRecord); - if (chainType === "reasoning") { - appendReasoningPart(botRecord, payloadText(data)); - return; - } - if (chainType === "tool_call") { - upsertToolCall(botRecord, parseJsonSafe(data)); - return; - } - if (chainType === "tool_call_result") { - finishToolCall(botRecord, parseJsonSafe(data)); - return; - } - appendPlain(botRecord, payloadText(data), normalized.streaming !== false); - return; - } - - if (["image", "record", "file", "video"].includes(msgType)) { - markMessageStarted(botRecord); - const rawFilename = String(data) - .replace("[IMAGE]", "") - .replace("[RECORD]", "") - .replace("[FILE]", "") - .replace("[VIDEO]", ""); - const separatorIndex = rawFilename.indexOf("|"); - const storedFilename = - separatorIndex >= 0 ? rawFilename.slice(0, separatorIndex) : rawFilename; - const displayFilename = - separatorIndex >= 0 ? rawFilename.slice(separatorIndex + 1) : storedFilename; - const filename = displayFilename || storedFilename; - const mediaPart: MessagePart = { type: msgType, filename }; - if (storedFilename && storedFilename !== filename) { - mediaPart.stored_filename = storedFilename; - } - if (msgType !== "file") { - resolvePartMedia(mediaPart).then(() => { - messageContent(botRecord).message.push(mediaPart); - }); - } else { - messageContent(botRecord).message.push(mediaPart); - } - } - } - - return { - loadingMessages, - sending, - messagesBySession, - loadedSessions, - sessionProjects, - activeMessages, - isSessionRunning, - isUserMessage, - isMessageStreaming, - messageContent, - messageParts, - loadSessionMessages, - createLocalExchange, - sendMessageStream, - editMessage, - continueEditedMessage, - regenerateMessage, - stopSession, - cleanupConnections, - }; -} - -function cloneContentWithEditedText( - record: ChatRecord, - editedText: string, -): ChatContent { - const content = record.content || { type: "bot", message: [] }; - const message = Array.isArray(content.message) - ? content.message.map((part) => ({ ...part })) - : []; - let replaced = false; - for (const part of message) { - if (part.type === "plain") { - part.text = editedText; - replaced = true; - break; - } - } - if (!replaced && editedText) { - message.push({ type: "plain", text: editedText }); - } - return { - ...content, - message, - }; -} - -function stripUploadOnlyFields(part: MessagePart): MessagePart { - const copied = { ...part }; - delete copied.path; - return copied; -} - -function normalizeSessionProject(value: unknown): ChatSessionProject | null { - if (!value || typeof value !== "object") return null; - const project = value as Record; - if ( - typeof project.project_id !== "string" || - typeof project.title !== "string" - ) { - return null; - } - - return { - project_id: project.project_id, - title: project.title, - emoji: typeof project.emoji === "string" ? project.emoji : undefined, - }; -} - -export function normalizeMessageParts( - parts: unknown, - fallbackReasoning = "", -): MessagePart[] { - const normalizedParts = normalizePartsInternal(parts); - if (fallbackReasoning && !normalizedParts.some((part) => part.type === "think")) { - normalizedParts.unshift({ type: "think", think: fallbackReasoning }); - } - return normalizedParts; -} - -export function extractReasoningText( - parts: MessagePart[] | unknown, - fallbackReasoning = "", -) { - const normalizedParts = Array.isArray(parts) - ? parts - : normalizeMessageParts(parts, fallbackReasoning); - const text = normalizedParts - .filter((part) => part.type === "think") - .map((part) => String(part.think || "")) - .join(""); - return text || fallbackReasoning; -} - -export function reasoningActivityCounts( - parts: MessagePart[] | unknown, - fallbackReasoning = "", -) { - const normalizedParts = Array.isArray(parts) - ? parts - : normalizeMessageParts(parts, fallbackReasoning); - let thinkCount = 0; - let toolCount = 0; - - for (const part of normalizedParts) { - if (part.type === "think" && String(part.think || "").trim()) { - thinkCount += 1; - } - if (part.type === "tool_call" && Array.isArray(part.tool_calls)) { - toolCount += part.tool_calls.length; - } - } - - return { thinkCount, toolCount }; -} - -export function reasoningActivityTitle( - counts: ReturnType, - tm: (key: string, params?: Record) => string, -) { - return [ - counts.thinkCount > 0 - ? tm("reasoning.thinkSummary", { count: counts.thinkCount }) - : "", - counts.toolCount > 0 - ? tm("reasoning.toolSummary", { count: counts.toolCount }) - : "", - ] - .filter(Boolean) - .join(tm("reasoning.summarySeparator")) || tm("reasoning.thinking"); -} - -export function thinkingParts(content: ChatContent): MessagePart[] { - const firstThinkingBlock = messageBlocks(content).find( - (block) => block.kind === "thinking", - ); - if (firstThinkingBlock) return firstThinkingBlock.parts; - - const fallbackReasoning = String(content.reasoning || ""); - return fallbackReasoning ? [{ type: "think", think: fallbackReasoning }] : []; -} - -export function displayParts(content: ChatContent): MessagePart[] { - return messageBlocks(content) - .filter((block) => block.kind === "content") - .flatMap((block) => block.parts); -} - -export function messageBlocks(content: ChatContent): MessageDisplayBlock[] { - const parts = Array.isArray(content.message) - ? content.message - : normalizeMessageParts(content.message, content.reasoning || ""); - - const blocks: MessageDisplayBlock[] = []; - let currentKind: MessageDisplayBlock["kind"] | null = null; - let currentParts: MessagePart[] = []; - - for (const part of parts) { - if (isEmptyPlainPart(part)) continue; - - const nextKind: MessageDisplayBlock["kind"] = isThinkingPart(part) - ? "thinking" - : "content"; - - if (currentKind !== nextKind) { - if (currentKind && currentParts.length) { - blocks.push({ kind: currentKind, parts: currentParts }); - } - currentKind = nextKind; - currentParts = [{ ...part }]; - continue; - } - - currentParts.push({ ...part }); - } - - if (currentKind && currentParts.length) { - blocks.push({ kind: currentKind, parts: currentParts }); - } - - if (!blocks.length && content.reasoning) { - return [ - { - kind: "thinking", - parts: [{ type: "think", think: String(content.reasoning) }], - }, - ]; - } - - return blocks; -} - -function partToPayload(part: MessagePart) { - if (part.type === "plain") return { type: "plain", text: part.text || "" }; - if (part.type === "reply") { - return { - type: "reply", - message_id: part.message_id, - selected_text: part.selected_text || "", - }; - } - return { - type: part.type, - attachment_id: part.attachment_id, - filename: part.filename, - }; -} - -async function readSseStream( - body: ReadableStream, - onPayload: (payload: any) => void, -) { - const reader = body.getReader(); - const decoder = new TextDecoder(); - let buffer = ""; - - while (true) { - const { value, done } = await reader.read(); - if (done) break; - buffer += decoder.decode(value, { stream: true }); - const events = buffer.split("\n\n"); - buffer = events.pop() || ""; - - for (const event of events) { - const data = event - .split("\n") - .filter((line) => line.startsWith("data:")) - .map((line) => line.slice(5).trimStart()) - .join("\n"); - if (!data) continue; - try { - onPayload(JSON.parse(data)); - } catch (error) { - console.error("Failed to parse SSE payload:", error, data); - } - } - } -} - -function normalizePartsInternal(parts: unknown): MessagePart[] { - if (typeof parts === "string") { - return parts ? [{ type: "plain", text: parts }] : []; - } - if (!Array.isArray(parts)) return []; - return parts.map((part: any) => { - if (!part || typeof part !== "object") { - return { type: "plain", text: String(part ?? "") }; - } - if (part.type === "reasoning") { - return { - ...part, - type: "think", - think: String(part.think ?? part.text ?? ""), - }; - } - return { ...part }; - }); -} - -function isEmptyPlainPart(part: MessagePart) { - return part.type === "plain" && !String(part.text || ""); -} - -function isThinkingPart(part: MessagePart) { - return part.type === "think" || part.type === "tool_call"; -} - -function firstNonEmptyPartIndex(parts: MessagePart[]) { - return parts.findIndex((part) => !isEmptyPlainPart(part)); -} - -export function appendPlain(record: ChatRecord, text: string, append = true) { - markMessageStarted(record); - const content = record.content; - let last = content.message[content.message.length - 1]; - if (!last || last.type !== "plain") { - last = { type: "plain", text: "" }; - content.message.push(last); - } - last.text = append ? `${last.text || ""}${text}` : text; -} - -export function appendReasoningPart(record: ChatRecord, text: string) { - markMessageStarted(record); - if (!text) return; - const content = record.content; - const last = content.message[content.message.length - 1]; - if (last?.type === "think") { - last.think = `${String(last.think || "")}${text}`; - } else { - content.message.push({ type: "think", think: text }); - } - content.reasoning = extractReasoningText(content.message); -} - -export function upsertToolCall(record: ChatRecord, toolCall: any) { - markMessageStarted(record); - if (!toolCall || typeof toolCall !== "object") return; - const targetId = toolCall.id; - if (targetId != null) { - for (const part of record.content.message) { - if (part.type !== "tool_call" || !Array.isArray(part.tool_calls)) continue; - const matched = part.tool_calls.find((item) => item.id === targetId); - if (matched) { - Object.assign(matched, toolCall); - return; - } - } - } - record.content.message.push({ type: "tool_call", tool_calls: [{ ...toolCall }] }); -} - -export function finishToolCall(record: ChatRecord, result: any) { - markMessageStarted(record); - if (!result || typeof result !== "object") return; - const targetId = result.id; - for (const part of record.content.message) { - if (part.type !== "tool_call" || !Array.isArray(part.tool_calls)) continue; - const tool = part.tool_calls.find((item) => item.id === targetId); - if (tool) { - tool.result = result.result; - tool.finished_ts = result.ts || Date.now() / 1000; - return; - } - } - record.content.message.push({ - type: "tool_call", - tool_calls: [ - { - id: targetId, - result: result.result, - finished_ts: result.ts || Date.now() / 1000, - }, - ], - }); -} - -export function markMessageStarted(record: ChatRecord) { - record.content.isLoading = false; -} - -export function hasPlainText(record: ChatRecord) { - return record.content.message.some( - (part) => - part.type === "plain" && typeof part.text === "string" && part.text, - ); -} - -export function payloadText(value: unknown) { - if (typeof value === "string") return value; - if (value == null) return ""; - if (typeof value === "object") { - const payload = value as Record; - if (typeof payload.text === "string") return payload.text; - if (typeof payload.content === "string") return payload.content; - if (typeof payload.message === "string") return payload.message; - } - return String(value); -} - -export function parseJsonSafe(value: unknown) { - if (typeof value !== "string") return value; - try { - return JSON.parse(value); - } catch { - return value; - } -} diff --git a/legacy-dashboard/src/composables/usePluginSidebarItems.ts b/legacy-dashboard/src/composables/usePluginSidebarItems.ts deleted file mode 100644 index af029d08..00000000 --- a/legacy-dashboard/src/composables/usePluginSidebarItems.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { reactive, shallowRef, onMounted, watch } from "vue"; -import { pluginApi } from "@/api/v1"; -import type { menu } from "@/layouts/full/vertical-sidebar/sidebarItem"; - -const DEFAULT_ICON = "mdi-puzzle"; -const GROUP_I18N_KEY = "core.navigation.pluginWebui"; -const GROUP_ICON = "mdi-puzzle-outline"; - -interface PluginEntry { - name: string; - display_name?: string | null; - activated: boolean; - pages: string[]; -} - -/** 模块级共享状态,由 useExtensionPage.getExtensions() 更新 */ -export const pluginSidebarState = reactive<{ - plugins: PluginEntry[]; -}>({ - plugins: [], -}); - -function buildPluginItems(plugins: PluginEntry[]): menu | null { - const activeWithPages = plugins.filter( - (p) => p.activated && Array.isArray(p.pages) && p.pages.length > 0, - ); - - if (activeWithPages.length === 0) return null; - - const children: menu[] = activeWithPages.map((p) => { - const displayName = p.display_name || p.name || "Unknown Plugin"; - const firstPage = p.pages[0]; - - return { - title: displayName, - icon: DEFAULT_ICON, - to: `/plugin-page/${encodeURIComponent(p.name)}/${encodeURIComponent(firstPage)}`, - isRawTitle: true, - }; - }); - - return { - title: GROUP_I18N_KEY, - icon: GROUP_ICON, - children, - }; -} - -let initialFetched = false; - -async function initPluginState() { - if (initialFetched) return; - initialFetched = true; - try { - const res = await pluginApi.list(); - if (res.data?.status === "ok") { - pluginSidebarState.plugins = res.data.data ?? []; - } - } catch { - // 静默失败,后续 getExtensions() 会补充 - } -} - -export function usePluginSidebarItems() { - const pluginItems = shallowRef(null); - - function refreshItems() { - pluginItems.value = buildPluginItems(pluginSidebarState.plugins); - } - - onMounted(async () => { - await initPluginState(); - refreshItems(); - }); - - watch( - () => pluginSidebarState.plugins, - () => { - refreshItems(); - }, - ); - - return { pluginItems }; -} diff --git a/legacy-dashboard/src/composables/useProjects.ts b/legacy-dashboard/src/composables/useProjects.ts deleted file mode 100644 index 6d2789f0..00000000 --- a/legacy-dashboard/src/composables/useProjects.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { ref } from 'vue'; -import { chatApi } from '@/api/v1'; -import type { Project } from '@/components/chat/ProjectList.vue'; - -type WorkspaceType = 'session' | 'project' | 'custom'; - -function projectErrorMessage(error: unknown, fallback: string) { - const responseMessage = (error as any)?.response?.data?.message; - if (typeof responseMessage === 'string' && responseMessage.trim()) { - return responseMessage; - } - const message = (error as Error)?.message; - return message || fallback; -} - -export function useProjects() { - const projects = ref([]); - const selectedProjectId = ref(null); - - async function getProjects() { - try { - const res = await chatApi.listProjects(); - if (res.data.status === 'ok') { - projects.value = res.data.data || []; - - } - } catch (error) { - console.error('Failed to fetch projects:', error); - } - } - - async function createProject( - title: string, - emoji?: string, - description?: string, - workspaceType: WorkspaceType = 'project', - workspacePath?: string - ) { - try { - const res = await chatApi.createProject({ - title, - emoji: emoji || '📁', - description, - workspace_type: workspaceType, - workspace_path: workspacePath - }); - if (res.data.status === 'ok') { - await getProjects(); - return res.data.data; - } - throw new Error(res.data.message || 'Failed to create project'); - } catch (error) { - console.error('Failed to create project:', error); - throw new Error(projectErrorMessage(error, 'Failed to create project')); - } - } - - async function updateProject( - projectId: string, - title?: string, - emoji?: string, - description?: string, - workspaceType?: WorkspaceType, - workspacePath?: string - ) { - try { - const res = await chatApi.updateProject(projectId, { - title, - emoji, - description, - workspace_type: workspaceType, - workspace_path: workspacePath - }); - if (res.data.status === 'ok') { - await getProjects(); - return; - } - throw new Error(res.data.message || 'Failed to update project'); - } catch (error) { - console.error('Failed to update project:', error); - throw new Error(projectErrorMessage(error, 'Failed to update project')); - } - } - - async function deleteProject(projectId: string) { - try { - const res = await chatApi.deleteProject(projectId); - if (res.data.status === 'ok') { - await getProjects(); - if (selectedProjectId.value === projectId) { - selectedProjectId.value = null; - } - } - } catch (error) { - console.error('Failed to delete project:', error); - } - } - - async function addSessionToProject(sessionId: string, projectId: string) { - try { - const res = await chatApi.addProjectSession(projectId, sessionId); - return res.data.status === 'ok'; - } catch (error) { - console.error('Failed to add session to project:', error); - return false; - } - } - - async function removeSessionFromProject(sessionId: string) { - try { - const res = await chatApi.removeProjectSession(sessionId); - return res.data.status === 'ok'; - } catch (error) { - console.error('Failed to remove session from project:', error); - return false; - } - } - - async function getProjectSessions(projectId: string) { - try { - const res = await chatApi.listProjectSessions(projectId); - if (res.data.status === 'ok') { - return res.data.data || []; - } - return []; - } catch (error) { - console.error('Failed to fetch project sessions:', error); - return []; - } - } - - return { - projects, - selectedProjectId, - getProjects, - createProject, - updateProject, - deleteProject, - addSessionToProject, - removeSessionFromProject, - getProjectSessions - }; -} diff --git a/legacy-dashboard/src/composables/useProviderModelConfigDialog.ts b/legacy-dashboard/src/composables/useProviderModelConfigDialog.ts deleted file mode 100644 index c2c49d35..00000000 --- a/legacy-dashboard/src/composables/useProviderModelConfigDialog.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { computed, ref, type Ref } from 'vue' -import { providerApi } from '@/api/v1' - -interface UseProviderModelConfigDialogOptions { - selectedProviderSource: Ref - configSchema: Ref> - buildModelProviderConfig: (modelId: string) => any - modelAlreadyConfigured: (modelId: string) => boolean - loadConfig: () => Promise | void - tm: (key: string, params?: Record) => string - showMessage: (message: string, color?: string) => void -} - -export function useProviderModelConfigDialog(options: UseProviderModelConfigDialogOptions) { - const { - selectedProviderSource, - configSchema, - buildModelProviderConfig, - modelAlreadyConfigured, - loadConfig, - tm, - showMessage - } = options - - const showProviderEditDialog = ref(false) - const providerEditData = ref(null) - const providerEditOriginalId = ref('') - const providerEditMode = ref<'add' | 'edit'>('edit') - const savingProviders = ref([]) - - const providerModelConfigSchema = computed(() => { - if (!configSchema.value?.provider?.items) { - return configSchema.value - } - - const schema = JSON.parse(JSON.stringify(configSchema.value)) - const sourceType = selectedProviderSource.value?.type - const hiddenKeys = ['id', 'model'] - - if (sourceType === 'googlegenai_chat_completion') { - hiddenKeys.push('custom_extra_body') - } - - for (const key of hiddenKeys) { - if (schema.provider.items[key]) { - schema.provider.items[key].invisible = true - } - } - return schema - }) - - const providerEditDialogTitle = computed(() => { - const actionTitle = providerEditMode.value === 'add' - ? tm('dialogs.config.addTitle') - : tm('dialogs.config.editTitle') - const providerId = providerEditData.value?.id - return providerId ? `${actionTitle} ${providerId}` : actionTitle - }) - - function openProviderEdit(provider: any) { - const editableProvider = JSON.parse(JSON.stringify(provider)) - providerEditData.value = editableProvider - providerEditOriginalId.value = provider.id - providerEditMode.value = 'edit' - showProviderEditDialog.value = true - } - - function openModelAddDialog(modelId: string) { - if (!selectedProviderSource.value) { - showMessage(tm('providerSources.selectHint'), 'error') - return - } - if (!modelId) return - if (modelAlreadyConfigured(modelId)) { - showMessage(tm('models.manualModelExists'), 'error') - return - } - - const newProviderConfig = buildModelProviderConfig(modelId) - if (!newProviderConfig) return - - providerEditData.value = newProviderConfig - providerEditOriginalId.value = '' - providerEditMode.value = 'add' - showProviderEditDialog.value = true - } - - async function saveEditedProvider() { - if (!providerEditData.value) return - - savingProviders.value.push(providerEditData.value.id) - try { - const isAdding = providerEditMode.value === 'add' - const sourceId = providerEditData.value.provider_source_id || selectedProviderSource.value?.id - if (isAdding && !sourceId) { - throw new Error(tm('providerSources.selectHint')) - } - const res = isAdding - ? await providerApi.createInSource(sourceId, providerEditData.value) - : await providerApi.update( - providerEditOriginalId.value || providerEditData.value.id, - providerEditData.value - ) - - if (res.data.status === 'error') { - throw new Error(res.data.message || tm('providerSources.saveError')) - } - - showMessage( - res.data.message || ( - isAdding - ? tm('models.addSuccess', { model: providerEditData.value.model }) - : tm('providerSources.saveSuccess') - ) - ) - showProviderEditDialog.value = false - await loadConfig() - } catch (err: any) { - showMessage(err.response?.data?.message || err.message || tm('providerSources.saveError'), 'error') - } finally { - savingProviders.value = savingProviders.value.filter(id => id !== providerEditData.value?.id) - } - } - - return { - showProviderEditDialog, - providerEditData, - savingProviders, - providerModelConfigSchema, - providerEditDialogTitle, - openProviderEdit, - openModelAddDialog, - saveEditedProvider - } -} diff --git a/legacy-dashboard/src/composables/useProviderSources.ts b/legacy-dashboard/src/composables/useProviderSources.ts deleted file mode 100644 index d0616f3b..00000000 --- a/legacy-dashboard/src/composables/useProviderSources.ts +++ /dev/null @@ -1,772 +0,0 @@ -import { ref, computed, onMounted, nextTick, watch } from 'vue' -import { providerApi } from '@/api/v1' -import { getProviderIcon } from '@/utils/providerUtils' -import { askForConfirmation as askForConfirmationDialog, useConfirmDialog } from '@/utils/confirmDialog' -import { normalizeTextInput } from '@/utils/inputValue' - -export interface UseProviderSourcesOptions { - defaultTab?: string - tm: (key: string, params?: Record) => string - showMessage: (message: string, color?: string) => void -} - -export function resolveDefaultTab(value?: string) { - const normalized = (value || '').toLowerCase() - - if (normalized.startsWith('select_agent_runner_provider') || normalized === 'agent_runner') { - return 'agent_runner' - } - - if (normalized === 'select_provider_stt' || normalized === 'speech_to_text' || normalized.includes('stt')) { - return 'speech_to_text' - } - - if (normalized === 'select_provider_tts' || normalized === 'text_to_speech' || normalized.includes('tts')) { - return 'text_to_speech' - } - - if (normalized.includes('embedding')) { - return 'embedding' - } - - if (normalized.includes('rerank')) { - return 'rerank' - } - - return 'chat_completion' -} - -export function useProviderSources(options: UseProviderSourcesOptions) { - const { tm, showMessage } = options - - const confirmDialog = useConfirmDialog() - - async function askForConfirmation(message: string) { - return askForConfirmationDialog(message, confirmDialog) - } - - // ===== State ===== - const config = ref>({}) - const metadata = ref>({}) - const providerSources = ref([]) - const providers = ref([]) - const selectedProviderType = ref(resolveDefaultTab(options.defaultTab)) - const selectedProviderSource = ref(null) - const selectedProviderSourceOriginalId = ref(null) - const editableProviderSource = ref(null) - const availableModels = ref([]) - const modelMetadata = ref>({}) - const loadingModels = ref(false) - const savingSource = ref(false) - const savingProviderToggles = ref([]) - const testingProviders = ref([]) - const isSourceModified = ref(false) - const configSchema = ref>({}) - const providerTemplates = ref>({}) - const manualModelId = ref('') - const modelSearch = ref('') - - let suppressSourceWatch = false - - const providerTypes = computed(() => [ - { value: 'chat_completion', label: tm('providers.tabs.chatCompletion'), icon: 'mdi-message-text' }, - { value: 'agent_runner', label: tm('providers.tabs.agentRunner'), icon: 'mdi-robot' }, - { value: 'speech_to_text', label: tm('providers.tabs.speechToText'), icon: 'mdi-microphone-message' }, - { value: 'text_to_speech', label: tm('providers.tabs.textToSpeech'), icon: 'mdi-volume-high' }, - { value: 'embedding', label: tm('providers.tabs.embedding'), icon: 'mdi-code-json' }, - { value: 'rerank', label: tm('providers.tabs.rerank'), icon: 'mdi-compare-vertical' } - ]) - - // ===== Computed ===== - const availableSourceTypes = computed(() => { - if (!providerTemplates.value || Object.keys(providerTemplates.value).length === 0) { - return [] - } - - const types: Array<{ value: string; label: string; icon: string }> = [] - for (const [templateName, template] of Object.entries(providerTemplates.value)) { - if (template.provider_type === selectedProviderType.value) { - types.push({ - value: templateName, - label: templateName, - icon: getProviderIcon(template.provider) - }) - } - } - - return types - }) - - const filteredProviderSources = computed(() => { - if (!providerSources.value) return [] - - return providerSources.value.filter((source) => - source.provider_type === selectedProviderType.value || - (source.type && isTypeMatchingProviderType(source.type, selectedProviderType.value)) - ) - }) - - const displayedProviderSources = computed(() => { - return filteredProviderSources.value || [] - }) - - const sourceProviders = computed(() => { - if (!selectedProviderSource.value || !providers.value) return [] - - return providers.value.filter((p) => p.provider_source_id === selectedProviderSource.value.id) - }) - - const existingModelsForSelectedSource = computed(() => { - if (!selectedProviderSource.value) return new Set() - return new Set(sourceProviders.value.map((p: any) => p.model)) - }) - - const sortedAvailableModels = computed(() => { - const existing = existingModelsForSelectedSource.value - return [...(availableModels.value || [])].sort((a, b) => { - const aName = typeof a === 'string' ? a : a?.name - const bName = typeof b === 'string' ? b : b?.name - const aExists = existing.has(aName) - const bExists = existing.has(bName) - if (aExists && !bExists) return -1 - if (!aExists && bExists) return 1 - return 0 - }) - }) - - function buildMetadataFromProvider(provider: any) { - if (!provider) return null - const mods = provider.modalities || [] - if (!mods.length && !provider.max_context_tokens) return null - const input: string[] = [] - if (mods.includes('image')) input.push('image') - if (mods.includes('audio')) input.push('audio') - return { - modalities: { input }, - tool_call: mods.includes('tool_use'), - reasoning: Boolean(provider.reasoning), - limit: { context: provider.max_context_tokens || 0 } - } - } - - const mergedModelEntries = computed(() => { - const configuredEntries = (sourceProviders.value || []).map((provider: any) => { - const metadata = getModelMetadata(provider.model) - return { - type: 'configured', - provider, - metadata: metadata || buildMetadataFromProvider(provider), - hasModelMetadata: Boolean(metadata) - } - }) - - const availableEntries = (sortedAvailableModels.value || []) - .filter((item: any) => { - const name = typeof item === 'string' ? item : item?.name - return !existingModelsForSelectedSource.value.has(name) - }) - .map((item: any) => { - const name = typeof item === 'string' ? item : item?.name - return { - type: 'available', - model: name, - metadata: typeof item === 'object' ? item?.metadata : getModelMetadata(name), - hasModelMetadata: Boolean(typeof item === 'object' ? item?.metadata : getModelMetadata(name)) - } - }) - - return [...configuredEntries, ...availableEntries] - }) - - const filteredMergedModelEntries = computed(() => { - const term = normalizeTextInput(modelSearch.value).trim().toLowerCase() - if (!term) return mergedModelEntries.value - - return mergedModelEntries.value.filter((entry: any) => { - if (entry.type === 'configured') { - const id = entry.provider.id?.toLowerCase() || '' - const model = entry.provider.model?.toLowerCase() || '' - return id.includes(term) || model.includes(term) - } - - const model = entry.model?.toLowerCase() || '' - return model.includes(term) - }) - }) - - const manualProviderId = computed(() => { - if (!selectedProviderSource.value) return '' - const modelId = manualModelId.value.trim() - if (!modelId) return '' - return `${selectedProviderSource.value.id}/${modelId}` - }) - - const basicSourceConfig = computed(() => { - if (!editableProviderSource.value) return null - - const fields = ['id', 'key', 'api_base'] - const basic: Record = {} - - fields.forEach((field) => { - Object.defineProperty(basic, field, { - get() { - return editableProviderSource.value![field] - }, - set(val) { - editableProviderSource.value![field] = val - }, - enumerable: true - }) - }) - - return basic - }) - - const advancedSourceConfig = computed(() => { - if (!editableProviderSource.value) return null - - const excluded = new Set(['id', 'key', 'api_base', 'enable', 'type', 'provider_type', 'provider']) - const advanced: Record = {} - - for (const key of Object.keys(editableProviderSource.value)) { - Object.defineProperty(advanced, key, { - get() { - return editableProviderSource.value![key] - }, - set(val) { - editableProviderSource.value![key] = val - }, - enumerable: !excluded.has(key) - }) - } - - return advanced - }) - - const filteredProviders = computed(() => { - if (!providers.value || selectedProviderType.value === 'chat_completion') { - return [] - } - - return providers.value.filter((provider: any) => getProviderType(provider) === selectedProviderType.value) - }) - - const providerSourceSchema = computed(() => { - if (!configSchema.value || !configSchema.value.provider) { - return configSchema.value - } - - // 创建一个深拷贝以避免修改原始 schema - const customSchema = JSON.parse(JSON.stringify(configSchema.value)) - - // 为 provider source 的 id 字段添加自定义 hint - if (customSchema.provider?.items?.id) { - customSchema.provider.items.id.hint = tm('providerSources.hints.id') - customSchema.provider.items.key.hint = tm('providerSources.hints.key') - customSchema.provider.items.api_base.hint = tm('providerSources.hints.apiBase') - } - // 为 proxy 字段添加描述和提示 - if (customSchema.provider?.items?.proxy) { - customSchema.provider.items.proxy.description = tm('providerSources.labels.proxy') - customSchema.provider.items.proxy.hint = tm('providerSources.hints.proxy') - } - - return customSchema - }) - - // ===== Watches ===== - watch(editableProviderSource, () => { - if (suppressSourceWatch) return - if (!editableProviderSource.value) return - isSourceModified.value = true - }, { deep: true }) - - // ===== Helper Functions ===== - function isTypeMatchingProviderType(type?: string, providerType?: string) { - if (!type || !providerType) return false - if (providerType === 'chat_completion') { - return type.includes('chat_completion') - } - return type.includes(providerType) - } - - function resolveSourceIcon(source: any) { - if (!source) return '' - return getProviderIcon(source.provider) || '' - } - - function getSourceDisplayName(source: any) { - if (!source) return '' - if (source.isPlaceholder) return source.templateKey || source.id || '' - return source.id - } - - function getModelMetadata(modelName?: string) { - if (!modelName) return null - return modelMetadata.value?.[modelName] || null - } - - function supportsImageInput(meta: any) { - const inputs = meta?.modalities?.input || [] - return inputs.includes('image') - } - - function supportsAudioInput(meta: any) { - const inputs = meta?.modalities?.input || [] - return inputs.includes('audio') - } - - function supportsToolCall(meta: any) { - return Boolean(meta?.tool_call) - } - - function supportsReasoning(meta: any) { - return Boolean(meta?.reasoning) - } - - function formatContextLimit(meta: any) { - const ctx = meta?.limit?.context - if (!ctx || typeof ctx !== 'number') return '' - if (ctx >= 1_000_000) return `${Math.round(ctx / 1_000_000)}M` - if (ctx >= 1_000) return `${Math.round(ctx / 1_000)}K` - return `${ctx}` - } - - function getProviderType(provider: any) { - if (!provider) return undefined - if (provider.provider_type) { - return provider.provider_type - } - - const oldVersionProviderTypeMapping: Record = { - openai_chat_completion: 'chat_completion', - anthropic_chat_completion: 'chat_completion', - googlegenai_chat_completion: 'chat_completion', - zhipu_chat_completion: 'chat_completion', - dify: 'agent_runner', - coze: 'agent_runner', - dashscope: 'chat_completion', - openai_whisper_api: 'speech_to_text', - mimo_stt_api: 'speech_to_text', - openai_whisper_selfhost: 'speech_to_text', - sensevoice_stt_selfhost: 'speech_to_text', - openai_tts_api: 'text_to_speech', - mimo_tts_api: 'text_to_speech', - edge_tts: 'text_to_speech', - gsvi_tts_api: 'text_to_speech', - fishaudio_tts_api: 'text_to_speech', - dashscope_tts: 'text_to_speech', - azure_tts: 'text_to_speech', - minimax_tts_api: 'text_to_speech', - volcengine_tts: 'text_to_speech' - } - return oldVersionProviderTypeMapping[provider.type] - } - - function selectProviderSource(source: any) { - if (source?.isPlaceholder && source.templateKey) { - addProviderSource(source.templateKey) - return - } - - selectedProviderSource.value = source - selectedProviderSourceOriginalId.value = source?.id || null - suppressSourceWatch = true - editableProviderSource.value = source - ? ensureProviderSourceDefaults(JSON.parse(JSON.stringify(source))) - : null - nextTick(() => { - suppressSourceWatch = false - }) - availableModels.value = [] - modelMetadata.value = {} - isSourceModified.value = false - } - - function ensureProviderSourceDefaults(source: any) { - if (!source || typeof source !== 'object') { - return source - } - - if (source.provider === 'ollama' && source.ollama_disable_thinking === undefined) { - source.ollama_disable_thinking = false - } - - return source - } - - function extractSourceFieldsFromTemplate(template: Record) { - const sourceFields: Record = {} - const excludeKeys = ['id', 'enable', 'model', 'provider_source_id', 'modalities', 'custom_extra_body'] - - for (const [key, value] of Object.entries(template)) { - if (!excludeKeys.includes(key)) { - sourceFields[key] = value - } - } - - return sourceFields - } - - function generateUniqueSourceId(baseId: string) { - const existingIds = new Set(providerSources.value.map((s: any) => s.id)) - if (!existingIds.has(baseId)) return baseId - - let counter = 1 - let candidate = `${baseId}_${counter}` - while (existingIds.has(candidate)) { - counter += 1 - candidate = `${baseId}_${counter}` - } - - return candidate - } - - function addProviderSource(templateKey: string) { - const template = providerTemplates.value[templateKey] - if (!template) { - showMessage('未找到对应的模板配置', 'error') - return - } - - const newId = generateUniqueSourceId(template.id) - const newSource = ensureProviderSourceDefaults({ - ...extractSourceFieldsFromTemplate(template), - id: newId, - type: template.type, - provider_type: template.provider_type, - provider: template.provider, - enable: true - }) - - providerSources.value.push(newSource) - selectedProviderSource.value = newSource - selectedProviderSourceOriginalId.value = newId - editableProviderSource.value = JSON.parse(JSON.stringify(newSource)) - availableModels.value = [] - modelMetadata.value = {} - isSourceModified.value = true - } - - async function deleteProviderSource(source: any) { - const confirmed = await askForConfirmation( - tm('providerSources.deleteConfirm', { id: source.id }) - ) - if (!confirmed) return - - try { - await providerApi.deleteSource(source.id) - - providers.value = providers.value.filter((p) => p.provider_source_id !== source.id) - providerSources.value = providerSources.value.filter((s) => s.id !== source.id) - - if (selectedProviderSource.value?.id === source.id) { - selectedProviderSource.value = null - selectedProviderSourceOriginalId.value = null - editableProviderSource.value = null - } - - showMessage(tm('providerSources.deleteSuccess')) - } catch (error: any) { - showMessage(error.message || tm('providerSources.deleteError'), 'error') - } finally { - await loadConfig() - } - } - - async function saveProviderSource() { - if (!selectedProviderSource.value) return - - savingSource.value = true - const originalId = String(selectedProviderSourceOriginalId.value || selectedProviderSource.value.id || '') - try { - const response = await providerApi.upsertSource(originalId, editableProviderSource.value) - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || tm('providerSources.saveError')) - } - - if (editableProviderSource.value!.id !== originalId) { - providers.value = providers.value.map((p) => - p.provider_source_id === originalId - ? { ...p, provider_source_id: editableProviderSource.value!.id } - : p - ) - selectedProviderSourceOriginalId.value = editableProviderSource.value!.id - } - - const idx = providerSources.value.findIndex((ps) => ps.id === originalId) - if (idx !== -1) { - providerSources.value[idx] = JSON.parse(JSON.stringify(editableProviderSource.value)) - selectedProviderSource.value = providerSources.value[idx] - } - - suppressSourceWatch = true - editableProviderSource.value = selectedProviderSource.value - nextTick(() => { - suppressSourceWatch = false - }) - - isSourceModified.value = false - showMessage(response.data.message || tm('providerSources.saveSuccess')) - return true - } catch (error: any) { - showMessage(error.response?.data?.message || error.message || tm('providerSources.saveError'), 'error') - return false - } finally { - savingSource.value = false - loadConfig() - } - } - - async function fetchAvailableModels() { - if (!selectedProviderSource.value) return - - if (isSourceModified.value) { - const saved = await saveProviderSource() - if (!saved) { - return - } - } - - loadingModels.value = true - try { - const sourceId = String(editableProviderSource.value?.id || selectedProviderSource.value.id || '') - const response = await providerApi.sourceModels(sourceId) - if (response.data.status === 'ok') { - const metadataMap = (response.data.data.model_metadata || {}) as Record - modelMetadata.value = metadataMap - availableModels.value = (response.data.data.models || []).map((model: string) => ({ - name: model, - metadata: metadataMap?.[model] || null - })) - if (availableModels.value.length === 0) { - showMessage(tm('models.noModelsFound'), 'info') - } - } else { - throw new Error(response.data.message || tm('models.fetchError')) - } - } catch (error: any) { - modelMetadata.value = {} - showMessage(error.response?.data?.message || error.message || tm('models.fetchError'), 'error') - } finally { - loadingModels.value = false - } - } - - function buildModelProviderConfig(modelName: string) { - if (!selectedProviderSource.value) return - - const sourceId = editableProviderSource.value?.id || selectedProviderSource.value.id - const newId = `${sourceId}/${modelName}` - - const metadata = getModelMetadata(modelName) - let modalities: string[] - - if (!metadata) { - modalities = ['text', 'image', 'audio', 'tool_use'] - } else { - modalities = ['text'] - if (supportsImageInput(metadata)) { - modalities.push('image') - } - if (supportsAudioInput(metadata)) { - modalities.push('audio') - } - if (supportsToolCall(metadata)) { - modalities.push('tool_use') - } - } - - let max_context_tokens = 0 - if (metadata?.limit?.context && typeof metadata.limit.context === 'number') { - max_context_tokens = metadata.limit.context - } - - return { - id: newId, - enable: true, - provider_source_id: sourceId, - model: modelName, - modalities, - custom_extra_body: {}, - max_context_tokens: max_context_tokens, - reasoning: supportsReasoning(metadata) - } - } - - async function addModelProvider(modelName: string) { - const newProvider = buildModelProviderConfig(modelName) - if (!newProvider) return - - try { - const res = await providerApi.createInSource( - String(newProvider.provider_source_id), - newProvider - ) - if (res.data.status === 'error') { - throw new Error(res.data.message || tm('providerSources.saveError')) - } - providers.value.push(newProvider) - showMessage(res.data.message || tm('models.addSuccess', { model: modelName })) - } catch (error: any) { - showMessage(error.response?.data?.message || error.message || tm('providerSources.saveError'), 'error') - } finally { - await loadConfig() - } - } - - function modelAlreadyConfigured(modelName: string) { - return existingModelsForSelectedSource.value.has(modelName) - } - - async function deleteProvider(provider: any) { - const confirmed = await askForConfirmation(tm('models.deleteConfirm', { id: provider.id })) - if (!confirmed) return - - try { - await providerApi.delete(String(provider.id)) - providers.value = providers.value.filter((p) => p.id !== provider.id) - showMessage(tm('models.deleteSuccess')) - } catch (error: any) { - showMessage(error.message || tm('models.deleteError'), 'error') - } finally { - await loadConfig() - } - } - - async function toggleProviderEnable(provider: any, value: boolean) { - if (!provider?.id || savingProviderToggles.value.includes(provider.id)) { - return false - } - - savingProviderToggles.value.push(provider.id) - try { - const response = await providerApi.setEnabled(String(provider.id), { - enabled: Boolean(value) - }) - if (response.data.status === 'error') { - throw new Error(response.data.message || tm('providerSources.saveError')) - } - provider.enable = Boolean(value) - showMessage(response.data.message || tm('messages.success.statusUpdate')) - return true - } catch (error: any) { - showMessage(error.response?.data?.message || error.message || tm('providerSources.saveError'), 'error') - return false - } finally { - await loadConfig() - savingProviderToggles.value = savingProviderToggles.value.filter((id) => id !== provider.id) - } - } - - async function testProvider(provider: any) { - testingProviders.value.push(provider.id) - try { - const startTime = performance.now() - const response = await providerApi.test(String(provider.id)) - if (response.data.status === 'ok' && response.data.data.error === null) { - const latency = Math.max(0, Math.round(performance.now() - startTime)) - showMessage(tm('models.testSuccessWithLatency', { id: provider.id, latency })) - } else { - throw new Error(response.data.data.error || tm('models.testError')) - } - } catch (error: any) { - showMessage(error.response?.data?.message || error.message || tm('models.testError'), 'error') - } finally { - testingProviders.value = testingProviders.value.filter((id) => id !== provider.id) - } - } - - async function loadConfig() { - await loadProviderTemplate() - } - - async function loadProviderTemplate() { - try { - const response = await providerApi.schema() - if (response.data.status === 'ok') { - configSchema.value = response.data.data.config_schema || {} - if (configSchema.value.provider?.config_template) { - providerTemplates.value = configSchema.value.provider.config_template - } - providerSources.value = response.data.data.provider_sources || [] - modelMetadata.value = (response.data.data.model_metadata || {}) as Record - providers.value = response.data.data.providers || [] - } - } catch (error) { - console.error('Failed to load provider template:', error) - } - } - - function updateDefaultTab(value: string) { - selectedProviderType.value = resolveDefaultTab(value) - } - - onMounted(async () => { - await loadProviderTemplate() - }) - - return { - // state - config, - metadata, - providerSources, - providers, - selectedProviderType, - selectedProviderSource, - selectedProviderSourceOriginalId, - editableProviderSource, - availableModels, - modelMetadata, - loadingModels, - savingSource, - savingProviderToggles, - testingProviders, - isSourceModified, - configSchema, - providerTemplates, - manualModelId, - modelSearch, - - // computed - providerTypes, - availableSourceTypes, - displayedProviderSources, - sourceProviders, - mergedModelEntries, - filteredMergedModelEntries, - filteredProviders, - basicSourceConfig, - advancedSourceConfig, - manualProviderId, - providerSourceSchema, - - // helpers - resolveSourceIcon, - getSourceDisplayName, - getModelMetadata, - supportsImageInput, - supportsAudioInput, - supportsToolCall, - supportsReasoning, - formatContextLimit, - getProviderType, - - // methods - updateDefaultTab, - selectProviderSource, - addProviderSource, - deleteProviderSource, - saveProviderSource, - fetchAvailableModels, - buildModelProviderConfig, - addModelProvider, - deleteProvider, - modelAlreadyConfigured, - toggleProviderEnable, - testProvider, - loadConfig, - loadProviderTemplate - } -} diff --git a/legacy-dashboard/src/composables/useRecording.ts b/legacy-dashboard/src/composables/useRecording.ts deleted file mode 100644 index 0ad66aa1..00000000 --- a/legacy-dashboard/src/composables/useRecording.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { ref } from 'vue'; - -export function useRecording() { - const isRecording = ref(false); - const audioChunks = ref([]); - const mediaRecorder = ref(null); - - function getSupportedMimeType(): string { - const candidates = [ - 'audio/webm;codecs=opus', - 'audio/webm', - 'audio/ogg;codecs=opus', - 'audio/ogg', - 'audio/mp4', - 'audio/wav' - ]; - - if (typeof MediaRecorder === 'undefined' || !MediaRecorder.isTypeSupported) { - return ''; - } - - return candidates.find(type => MediaRecorder.isTypeSupported(type)) || ''; - } - - function getRecordingMimeType(): string { - const chunkType = audioChunks.value.find(chunk => chunk.type)?.type; - return chunkType || mediaRecorder.value?.mimeType || 'audio/webm'; - } - - function getRecordingFilename(mimeType: string): string { - const extensionMap: Record = { - 'audio/webm': 'webm', - 'audio/webm;codecs=opus': 'webm', - 'audio/ogg': 'ogg', - 'audio/ogg;codecs=opus': 'ogg', - 'audio/mp4': 'm4a', - 'audio/mpeg': 'mp3', - 'audio/wav': 'wav' - }; - const normalizedMimeType = mimeType.toLowerCase(); - const extension = extensionMap[normalizedMimeType] || normalizedMimeType.split('/')[1]?.split(';')[0] || 'webm'; - const id = crypto.randomUUID?.() || `${Date.now()}-${Math.random().toString(16).slice(2)}`; - return `${id}.${extension}`; - } - - async function startRecording(onStart?: (label: string) => void) { - try { - if (!navigator.mediaDevices?.getUserMedia || typeof MediaRecorder === 'undefined') { - throw new Error('Audio recording is not supported in this browser'); - } - - mediaRecorder.value?.stream.getTracks().forEach(track => track.stop()); - audioChunks.value = []; - - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); - const mimeType = getSupportedMimeType(); - mediaRecorder.value = new MediaRecorder( - stream, - mimeType ? { mimeType } : undefined - ); - - mediaRecorder.value.ondataavailable = (event) => { - if (event.data.size > 0) { - audioChunks.value.push(event.data); - } - }; - - mediaRecorder.value.start(); - isRecording.value = true; - - if (onStart) { - onStart('录音中...'); - } - } catch (error) { - console.error('Failed to start recording:', error); - isRecording.value = false; - throw error; - } - } - - async function stopRecording(onStop?: (label: string) => void): Promise { - return new Promise((resolve, reject) => { - const recorder = mediaRecorder.value; - if (!recorder) { - reject(new Error('No media recorder')); - return; - } - - isRecording.value = false; - if (onStop) { - onStop('聊天输入框'); - } - - recorder.onstop = () => { - const mimeType = getRecordingMimeType(); - const audioBlob = new Blob(audioChunks.value, { type: mimeType }); - audioChunks.value = []; - recorder.stream.getTracks().forEach(track => track.stop()); - if (mediaRecorder.value === recorder) { - mediaRecorder.value = null; - } - - if (!audioBlob.size) { - reject(new Error('Recording is empty')); - return; - } - - const filename = getRecordingFilename(mimeType); - const audioFile = new File([audioBlob], filename, { - type: mimeType, - lastModified: Date.now() - }); - resolve(audioFile); - }; - - recorder.onerror = (event) => { - recorder.stream.getTracks().forEach(track => track.stop()); - if (mediaRecorder.value === recorder) { - mediaRecorder.value = null; - } - reject(event); - }; - - try { - recorder.stop(); - } catch (error) { - recorder.stream.getTracks().forEach(track => track.stop()); - if (mediaRecorder.value === recorder) { - mediaRecorder.value = null; - } - reject(error); - } - }); - } - - return { - isRecording, - startRecording, - stopRecording - }; -} diff --git a/legacy-dashboard/src/composables/useSessions.ts b/legacy-dashboard/src/composables/useSessions.ts deleted file mode 100644 index cb81586c..00000000 --- a/legacy-dashboard/src/composables/useSessions.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { ref, computed } from 'vue'; -import { useRouter } from 'vue-router'; -import { chatApi, configRouteApi } from '@/api/v1'; -import { buildWebchatUmoDetails, getStoredSelectedChatConfigId } from '@/utils/chatConfigBinding'; - -export interface Session { - session_id: string; - display_name: string | null; - updated_at: string; - platform_id: string; - creator: string; - is_group: number; - created_at: string; -} - -export function useSessions(chatboxMode: boolean = false) { - const router = useRouter(); - const sessions = ref([]); - const selectedSessions = ref([]); - const currSessionId = ref(''); - const pendingSessionId = ref(null); - // 编辑标题相关 - const editTitleDialog = ref(false); - const editingTitle = ref(''); - const editingSessionId = ref(''); - - const getCurrentSession = computed(() => { - if (!currSessionId.value) return null; - return sessions.value.find(s => s.session_id === currSessionId.value); - }); - - - - async function getSessions() { - try { - const response = await chatApi.listSessions(); - sessions.value = response.data.data; - - - - - } catch (err: any) { - if (err.response?.status === 401) { - router.push('/auth/login?redirect=/chatbox'); - } - console.error(err); - } - } - - async function newSession() { - try { - const selectedConfigId = getStoredSelectedChatConfigId(); - const response = await chatApi.createSession(); - const sessionId = response.data.data.session_id; - const platformId = response.data.data.platform_id; - - currSessionId.value = sessionId; - - if (selectedConfigId && selectedConfigId !== 'default' && platformId === 'webchat') { - try { - const umoDetails = buildWebchatUmoDetails(sessionId, false); - await configRouteApi.upsert(umoDetails.umo, { config_id: selectedConfigId }); - } catch (err) { - console.error('Failed to bind config to session', err); - } - } - - // 更新 URL - const basePath = chatboxMode ? '/chatbox' : '/chat'; - router.push(`${basePath}/${sessionId}`); - - await getSessions(); - - // 确保新创建的会话被选中高亮 - selectedSessions.value = [sessionId]; - - return sessionId; - } catch (err) { - console.error(err); - throw err; - } - } - - async function deleteSession(sessionId: string) { - try { - await chatApi.deleteSession(sessionId); - await getSessions(); - currSessionId.value = ''; - selectedSessions.value = []; - } catch (err) { - console.error(err); - } - } - - interface BatchDeleteFailedItem { - session_id: string; - reason: string; - } - - interface BatchDeleteResult { - deleted_count: number; - failed_count: number; - failed_items: BatchDeleteFailedItem[]; - currentSessionDeleted: boolean; - } - - function isBatchDeleteResponseData(data: unknown): data is { - deleted_count: number; - failed_count: number; - failed_items: BatchDeleteFailedItem[]; - } { - if (!data || typeof data !== 'object') { - return false; - } - const payload = data as Record; - return ( - typeof payload.deleted_count === 'number' && - typeof payload.failed_count === 'number' && - Array.isArray(payload.failed_items) - ); - } - - async function batchDeleteSessions(sessionIds: string[]): Promise { - try { - const currentSessionId = currSessionId.value; - const response = await chatApi.batchDeleteSessions({ session_ids: sessionIds }); - if (response.data?.status !== 'ok') { - throw new Error(response.data?.message || 'Failed to batch delete sessions'); - } - - const data = response.data?.data; - if (!isBatchDeleteResponseData(data)) { - throw new Error('Invalid batch delete response payload'); - } - - const failedItems = data.failed_items; - const failedSessionIds = new Set(failedItems.map(item => item.session_id)); - const currentSessionDeleted = Boolean( - currentSessionId && - sessionIds.includes(currentSessionId) && - !failedSessionIds.has(currentSessionId) - ); - - if (currentSessionDeleted) { - currSessionId.value = ''; - selectedSessions.value = []; - } - await getSessions(); - - return { - deleted_count: data.deleted_count, - failed_count: data.failed_count, - failed_items: failedItems, - currentSessionDeleted, - }; - } catch (err) { - console.error(err); - throw err; - } - } - - function showEditTitleDialog(sessionId: string, title: string) { - editingSessionId.value = sessionId; - editingTitle.value = title || ''; - editTitleDialog.value = true; - } - - async function saveTitle() { - if (!editingSessionId.value) return; - - const trimmedTitle = editingTitle.value.trim(); - try { - await chatApi.updateSession(editingSessionId.value, { - display_name: trimmedTitle, - }); - - // 更新本地会话标题 - const session = sessions.value.find(s => s.session_id === editingSessionId.value); - if (session) { - session.display_name = trimmedTitle; - } - editTitleDialog.value = false; - } catch (err) { - console.error('重命名会话失败:', err); - } - } - - function updateSessionTitle(sessionId: string, title: string) { - const session = sessions.value.find(s => s.session_id === sessionId); - if (session) { - session.display_name = title; - } - } - - function newChat(closeMobileSidebar?: () => void) { - currSessionId.value = ''; - selectedSessions.value = []; - - const basePath = chatboxMode ? '/chatbox' : '/chat'; - router.push(basePath); - - if (closeMobileSidebar) { - closeMobileSidebar(); - } - } - - return { - sessions, - selectedSessions, - currSessionId, - pendingSessionId, - editTitleDialog, - editingTitle, - editingSessionId, - getCurrentSession, - getSessions, - newSession, - deleteSession, - batchDeleteSessions, - showEditTitleDialog, - saveTitle, - updateSessionTitle, - newChat - }; -} diff --git a/legacy-dashboard/src/composables/useVADRecording.ts b/legacy-dashboard/src/composables/useVADRecording.ts deleted file mode 100644 index 8d545586..00000000 --- a/legacy-dashboard/src/composables/useVADRecording.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { ref, onBeforeUnmount } from 'vue'; -import axios from 'axios'; - -interface VADOptions { - onSpeechStart?: () => void; - onSpeechRealStart?: () => void; - onSpeechEnd: (audio: Float32Array) => void; - onVADMisfire?: () => void; - onFrameProcessed?: (probabilities: { isSpeech: number; notSpeech: number }, frame: Float32Array) => void; - positiveSpeechThreshold?: number; - negativeSpeechThreshold?: number; - redemptionMs?: number; - preSpeechPadMs?: number; - minSpeechMs?: number; - submitUserSpeechOnPause?: boolean; - model?: 'v5' | string; - baseAssetPath?: string; - onnxWASMBasePath?: string; -} - -interface VADInstance { - start(): void; - pause(): void; - listening: boolean; -} - -// 声明全局 vad 对象类型 -declare global { - interface Window { - vad: { - MicVAD: { - new(options: VADOptions): Promise; - }; - }; - } -} - -/** - * 使用 VAD (Voice Activity Detection) 进行录音的 composable - * VAD 会自动检测用户何时开始和停止说话,无需手动控制 - */ -export function useVADRecording() { - const isRecording = ref(false); - const isSpeaking = ref(false); - const audioEnergy = ref(0); // 0-1 之间的能量值 - const vadInstance = ref(null); - const isInitialized = ref(false); - const onSpeechStartCallback = ref<(() => void) | null>(null); - const onSpeechEndCallback = ref<((audio: Float32Array) => void) | null>(null); - - // Live Mode 不需要上传音频,直接通过 WebSocket 实时发送 - - // 初始化 VAD - async function initVAD() { - if (!window.vad) { - console.error('VAD library not loaded. Please ensure the scripts are included in index.html'); - return; - } - - try { - vadInstance.value = await (window.vad.MicVAD as any).new({ - onSpeechStart: () => { - console.log('[VAD] Speech started'); - isSpeaking.value = true; - // 调用开始说话回调 - if (onSpeechStartCallback.value) { - onSpeechStartCallback.value(); - } - }, - onSpeechRealStart: () => { - console.log('[VAD] Real speech started'); - }, - onSpeechEnd: (audio: Float32Array) => { - console.log('[VAD] Speech ended, audio length:', audio.length); - isSpeaking.value = false; - // 调用语音结束回调,传递原始音频数据 - if (onSpeechEndCallback.value) { - onSpeechEndCallback.value(audio); - } - }, - onVADMisfire: () => { - console.log('[VAD] VAD misfire - speech segment too short'); - isSpeaking.value = false; - }, - onFrameProcessed: (probabilities: { isSpeech: number; notSpeech: number }, frame: Float32Array) => { - // 计算 RMS (Root Mean Square) 作为能量 - let sum = 0; - for (let i = 0; i < frame.length; i++) { - sum += frame[i] * frame[i]; - } - const rms = Math.sqrt(sum / frame.length); - // 简单的归一化及平滑处理,根据经验 RMS 通常较小 - // 放大系数可以根据实际情况调整 - const targetEnergy = Math.min(rms * 5, 1); - audioEnergy.value = audioEnergy.value * 0.8 + targetEnergy * 0.2; - }, - // VAD 配置参数 - positiveSpeechThreshold: 0.3, - negativeSpeechThreshold: 0.25, - redemptionMs: 1400, - preSpeechPadMs: 800, - minSpeechMs: 400, - submitUserSpeechOnPause: false, - model: 'v5', - baseAssetPath: 'https://cdn.jsdelivr.net/npm/@ricky0123/vad-web@0.0.29/dist/', - onnxWASMBasePath: 'https://cdn.jsdelivr.net/npm/onnxruntime-web@1.22.0/dist/' - }); - - isInitialized.value = true; - console.log('VAD initialized successfully'); - } catch (error) { - console.error('Failed to initialize VAD:', error); - isInitialized.value = false; - } - } - - // 开始录音(启动 VAD) - async function startRecording( - onSpeechStart: () => void, - onSpeechEnd: (audio: Float32Array) => void - ) { - // 存储回调函数 - onSpeechStartCallback.value = onSpeechStart; - onSpeechEndCallback.value = onSpeechEnd; - - if (!isInitialized.value) { - await initVAD(); - } - - if (vadInstance.value) { - vadInstance.value.start(); - isRecording.value = true; - console.log('[VAD] Started'); - } - } - - // 停止录音(暂停 VAD) - function stopRecording() { - if (vadInstance.value) { - vadInstance.value.pause(); - isRecording.value = false; - isSpeaking.value = false; - onSpeechStartCallback.value = null; - onSpeechEndCallback.value = null; - console.log('[VAD] Stopped'); - } - } - - // 清理资源 - onBeforeUnmount(() => { - if (vadInstance.value && isRecording.value) { - stopRecording(); - } - }); - - return { - isRecording, - isSpeaking, // 用户是否正在说话 - audioEnergy, // 当前音频能量 - startRecording, - stopRecording - }; -} diff --git a/legacy-dashboard/src/config.ts b/legacy-dashboard/src/config.ts deleted file mode 100644 index 99d59827..00000000 --- a/legacy-dashboard/src/config.ts +++ /dev/null @@ -1,55 +0,0 @@ -export type ThemeMode = 'light' | 'dark' | 'system'; - -export type ConfigProps = { - Sidebar_drawer: boolean; - Customizer_drawer: boolean; - mini_sidebar: boolean; - fontTheme: string; - uiTheme: string; - themeMode: ThemeMode; - inputBg: boolean; -}; - -function checkThemeMode(): ThemeMode { - const mode = localStorage.getItem('themeMode') as ThemeMode | null; - if (mode === 'light' || mode === 'dark' || mode === 'system') return mode; - - const legacyTheme = localStorage.getItem('uiTheme'); - if (legacyTheme === 'PurpleThemeDark') { - localStorage.setItem('themeMode', 'dark'); - return 'dark'; - } - if (legacyTheme === 'PurpleTheme') { - localStorage.setItem('themeMode', 'light'); - return 'light'; - } - - localStorage.setItem('themeMode', 'system'); - return 'system'; -} - -export function resolveUiTheme(mode: ThemeMode): string { - if (mode === 'dark') return 'PurpleThemeDark'; - if (mode === 'light') return 'PurpleTheme'; - const prefersDark = - typeof window !== 'undefined' && - window.matchMedia('(prefers-color-scheme: dark)').matches; - return prefersDark ? 'PurpleThemeDark' : 'PurpleTheme'; -} - -const themeMode = checkThemeMode(); -const uiTheme = resolveUiTheme(themeMode); - -localStorage.setItem('uiTheme', uiTheme); - -const config: ConfigProps = { - Sidebar_drawer: true, - Customizer_drawer: false, - mini_sidebar: false, - fontTheme: 'Roboto', - uiTheme, - themeMode, - inputBg: false, -}; - -export default config; diff --git a/legacy-dashboard/src/i18n/composables.ts b/legacy-dashboard/src/i18n/composables.ts deleted file mode 100644 index c67d7490..00000000 --- a/legacy-dashboard/src/i18n/composables.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { ref, computed } from 'vue'; -import { translations as staticTranslations } from './translations'; -import type { Locale } from './types'; - -// 全局状态 -const currentLocale = ref('zh-CN'); -const translations = ref>({}); - -/** - * 初始化i18n系统 - */ -export async function initI18n(locale: Locale = 'zh-CN') { - currentLocale.value = locale; - - // 加载静态翻译数据 - loadTranslations(locale); -} - -/** - * 加载翻译数据(现在从静态导入获取) - */ -function loadTranslations(locale: Locale) { - try { - const data = staticTranslations[locale]; - if (data) { - translations.value = data; - } else { - console.warn(`Translations not found for locale: ${locale}`); - // 回退到中文 - if (locale !== 'zh-CN') { - console.log('Falling back to zh-CN'); - translations.value = staticTranslations['zh-CN']; - } - } - } catch (error) { - console.error(`Failed to load translations for ${locale}:`, error); - // 回退到中文 - if (locale !== 'zh-CN') { - console.log('Falling back to zh-CN'); - translations.value = staticTranslations['zh-CN']; - } - } -} - -/** - * 主要的翻译函数组合 - */ -export function useI18n() { - // 翻译函数 - const t = (key: string, params?: Record): string => { - const keys = key.split('.'); - let value: any = translations.value; - - // 遍历键路径 - for (const k of keys) { - if (value && typeof value === 'object' && k in value) { - value = value[k]; - } else { - console.warn(`Translation key not found: ${key}`); - // 返回带括号的键名,便于在开发时识别缺失的翻译 - return `[MISSING: ${key}]`; - } - } - - if (typeof value !== 'string') { - console.warn(`Translation value is not string: ${key}`, value); - // 返回带括号的键名,便于在开发时识别类型错误的翻译 - return `[INVALID: ${key}]`; - } - - // 此时value确定是string类型 - let result: string = value; - - // 处理参数插值 - if (params) { - result = result.replace(/\{(\w+)\}/g, (match: string, paramKey: string) => { - return params[paramKey]?.toString() || match; - }); - } - - return result; - }; - - // 切换语言 - const setLocale = async (newLocale: Locale) => { - if (newLocale !== currentLocale.value) { - currentLocale.value = newLocale; - loadTranslations(newLocale); - - // 保存到localStorage - localStorage.setItem('astrbot-locale', newLocale); - - // 触发自定义事件,通知相关页面重新加载配置数据 - // 这是因为插件适配器的 i18n 数据是通过后端 API 注入的, - // 需要根据 Accept-Language 头重新获取 - window.dispatchEvent(new CustomEvent('astrbot-locale-changed', { - detail: { locale: newLocale } - })); - } - }; - - // 获取当前语言 - const locale = computed(() => currentLocale.value); - - // 获取可用语言列表 - const availableLocales: Locale[] = ['zh-CN', 'en-US', 'ru-RU']; - - // 检查是否已加载 - const isLoaded = computed(() => Object.keys(translations.value).length > 0); - - return { - t, - locale, - setLocale, - availableLocales, - isLoaded - }; -} - -/** - * 模块特定的翻译函数 - */ -export function useModuleI18n(moduleName: string) { - const { t } = useI18n(); - - const tm = (key: string, params?: Record): string => { - // 将斜杠转换为点号以匹配嵌套对象结构 - const normalizedModuleName = moduleName.replace(/\//g, '.'); - return t(`${normalizedModuleName}.${key}`, params); - }; - - // 获取原始翻译值(可能是字符串、数组或对象) - const getRaw = (key: string): any => { - const normalizedModuleName = moduleName.replace(/\//g, '.'); - const fullKey = `${normalizedModuleName}.${key}`; - const keys = fullKey.split('.'); - let value: any = translations.value; - - for (const k of keys) { - if (value && typeof value === 'object' && k in value) { - value = value[k]; - } else { - return null; - } - } - - return value; - }; - - return { tm, getRaw }; -} - -/** - * 语言切换器组合函数 - */ -export function useLanguageSwitcher() { - const { locale, setLocale, availableLocales } = useI18n(); - - const languageOptions = computed(() => [ - { value: 'zh-CN', label: '简体中文', flag: '🇨🇳' }, - { value: 'en-US', label: 'English', flag: '🇺🇸' }, - { value: 'ru-RU', label: 'Русский', flag: '🇷🇺' } - ]); - - const currentLanguage = computed(() => { - return languageOptions.value.find(lang => lang.value === locale.value); - }); - - const switchLanguage = async (newLocale: Locale) => { - await setLocale(newLocale); - }; - - return { - locale, - languageOptions, - currentLanguage, - switchLanguage, - availableLocales - }; -} - -/** - * 将动态翻译数据(如插件提供的 i18n)合并到当前翻译中。 - * @param modulePath 模块路径,如 'features.config-metadata' - * @param allLocaleData 所有语言的翻译数据,如 { "zh-CN": {...}, "en-US": {...} } - */ -export function mergeDynamicTranslations(modulePath: string, allLocaleData: Record) { - const locale = currentLocale.value; - const localeData = allLocaleData[locale]; - if (!localeData || typeof localeData !== 'object') return; - - const pathParts = modulePath.split('.'); - let target: any = translations.value; - for (const part of pathParts) { - if (!(part in target) || typeof target[part] !== 'object') { - target[part] = {}; - } - target = target[part]; - } - - deepMerge(target, localeData); - - // 触发响应式更新 - translations.value = { ...translations.value }; -} - -function deepMerge(target: Record, source: Record) { - for (const key of Object.keys(source)) { - if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) { - if (!(key in target) || typeof target[key] !== 'object') { - target[key] = {}; - } - deepMerge(target[key], source[key]); - } else { - target[key] = source[key]; - } - } -} - -// 初始化函数(在应用启动时调用) -export async function setupI18n() { - // 从localStorage获取保存的语言设置 - const savedLocale = localStorage.getItem('astrbot-locale') as Locale; - const initialLocale = savedLocale && ['zh-CN', 'en-US', 'ru-RU'].includes(savedLocale) - ? savedLocale - : 'zh-CN'; - - await initI18n(initialLocale); -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/loader.ts b/legacy-dashboard/src/i18n/loader.ts deleted file mode 100644 index e5a1998a..00000000 --- a/legacy-dashboard/src/i18n/loader.ts +++ /dev/null @@ -1,301 +0,0 @@ -/** - * Dynamic I18n Loader - * 动态国际化加载器,支持按需加载和缓存机制 - */ - -export interface LoaderCache { - [key: string]: any; -} - -export interface ModuleInfo { - name: string; - path: string; - loaded: boolean; - data?: any; -} - -export class I18nLoader { - private cache: Map = new Map(); - private moduleRegistry: Map = new Map(); - - constructor() { - this.registerModules(); - } - - /** - * 注册所有可用的翻译模块 - */ - private registerModules(): void { - const modules = [ - // 核心模块 - { name: 'core/common', path: 'core/common.json' }, - { name: 'core/actions', path: 'core/actions.json' }, - { name: 'core/status', path: 'core/status.json' }, - { name: 'core/navigation', path: 'core/navigation.json' }, - { name: 'core/header', path: 'core/header.json' }, - { name: 'core/shared', path: 'core/shared.json' }, - - // 功能模块 - { name: 'features/chat', path: 'features/chat.json' }, - { name: 'features/extension', path: 'features/extension.json' }, - { name: 'features/conversation', path: 'features/conversation.json' }, - { name: 'features/session-management', path: 'features/session-management.json' }, - { name: 'features/tooluse', path: 'features/tool-use.json' }, - { name: 'features/provider', path: 'features/provider.json' }, - { name: 'features/platform', path: 'features/platform.json' }, - { name: 'features/config', path: 'features/config.json' }, - { name: 'features/config-metadata', path: 'features/config-metadata.json' }, - { name: 'features/console', path: 'features/console.json' }, - { name: 'features/trace', path: 'features/trace.json' }, - { name: 'features/about', path: 'features/about.json' }, - { name: 'features/settings', path: 'features/settings.json' }, - { name: 'features/auth', path: 'features/auth.json' }, - { name: 'features/chart', path: 'features/chart.json' }, - { name: 'features/dashboard', path: 'features/dashboard.json' }, - { name: 'features/cron', path: 'features/cron.json' }, - { name: 'features/subagent', path: 'features/subagent.json' }, - { name: 'features/alkaid/index', path: 'features/alkaid/index.json' }, - { name: 'features/alkaid/knowledge-base', path: 'features/alkaid/knowledge-base.json' }, - { name: 'features/alkaid/memory', path: 'features/alkaid/memory.json' }, - { name: 'features/persona', path: 'features/persona.json' }, - { name: 'features/welcome', path: 'features/welcome.json' }, - - // 消息模块 - { name: 'messages/errors', path: 'messages/errors.json' }, - { name: 'messages/success', path: 'messages/success.json' }, - { name: 'messages/validation', path: 'messages/validation.json' } - ]; - - modules.forEach(module => { - this.moduleRegistry.set(module.name, { - name: module.name, - path: module.path, - loaded: false - }); - }); - } - - /** - * 加载单个模块 - */ - async loadModule(locale: string, moduleName: string): Promise { - const cacheKey = `${locale}:${moduleName}`; - - // 检查缓存 - if (this.cache.has(cacheKey)) { - return this.cache.get(cacheKey); - } - - const moduleInfo = this.moduleRegistry.get(moduleName); - if (!moduleInfo) { - console.warn(`模块 ${moduleName} 未注册`); - return {}; - } - - try { - // 使用动态import加载JSON文件,兼容构建和开发环境 - const modulePath = `../locales/${locale}/${moduleInfo.path}`; - const module = await import(/* @vite-ignore */ modulePath); - const data = module.default || module; - - // 缓存结果 - this.cache.set(cacheKey, data); - - // 更新模块信息 - moduleInfo.loaded = true; - moduleInfo.data = data; - - return data; - } catch (error) { - console.error(`加载模块 ${moduleName} 失败:`, error); - - // 回退方案:尝试使用fetch(开发环境) - try { - const modulePath = `/src/i18n/locales/${locale}/${moduleInfo.path}`; - const response = await fetch(modulePath); - - if (!response.ok) { - throw new Error(`HTTP ${response.status}: ${response.statusText}`); - } - - const data = await response.json(); - - // 缓存结果 - this.cache.set(cacheKey, data); - - // 更新模块信息 - moduleInfo.loaded = true; - moduleInfo.data = data; - - return data; - } catch (fetchError) { - console.error(`回退fetch加载也失败:`, fetchError); - return {}; - } - } - } - - /** - * 通用模块加载器 - 减少重复代码,提高可维护性 - */ - private async loadModules( - locale: string, - prefix: string, - overrideList: string[] = [] - ): Promise { - // 使用覆盖列表或从注册表中筛选符合前缀的模块名 - const moduleNames = overrideList.length > 0 - ? overrideList - : Array.from(this.moduleRegistry.keys()).filter(key => key.startsWith(prefix)); - - const results = await Promise.all( - moduleNames.map(module => this.loadModule(locale, module)) - ); - - return this.mergeModules(results, moduleNames); - } - - /** - * 加载核心模块(最高优先级) - */ - async loadCoreModules(locale: string): Promise { - return this.loadModules(locale, 'core'); - } - - /** - * 加载功能模块 - */ - async loadFeatureModules(locale: string, features?: string[]): Promise { - return this.loadModules(locale, 'features', features || []); - } - - /** - * 加载消息模块 - */ - async loadMessageModules(locale: string): Promise { - return this.loadModules(locale, 'messages'); - } - - /** - * 加载所有模块 - */ - async loadAllModules(locale: string): Promise { - const [core, features, messages] = await Promise.all([ - this.loadCoreModules(locale), - this.loadFeatureModules(locale), - this.loadMessageModules(locale) - ]); - - return { - ...core, - ...features, - ...messages - }; - } - - /** - * 加载完整语言包(所有模块合并) - */ - async loadLocale(locale: string): Promise { - return this.loadAllModules(locale); - } - - /** - * 合并多个模块数据 - */ - private mergeModules(modules: any[], moduleNames: string[]): any { - const result: any = {}; - const pathRegistry = new Map(); - - modules.forEach((module, index) => { - const moduleName = moduleNames[index]; - const nameParts = moduleName.split('/'); - - // 构建嵌套对象结构(对所有模块统一处理) - let current = result; - for (let i = 0; i < nameParts.length - 1; i++) { - if (!current[nameParts[i]]) { - current[nameParts[i]] = {}; - } - current = current[nameParts[i]]; - } - - // 冲突检测:检查最终键是否已存在 - const finalKey = nameParts[nameParts.length - 1]; - const fullPath = nameParts.join('.'); - - if (current[finalKey] && pathRegistry.has(fullPath)) { - const existingModule = pathRegistry.get(fullPath); - console.warn(`⚠️ I18n模块路径冲突: "${fullPath}" 已被模块 "${existingModule}" 占用,模块 "${moduleName}" 可能会覆盖部分键值`); - } - - // 记录路径和模块名的映射 - pathRegistry.set(fullPath, moduleName); - - // 设置最终值(保持原有的浅合并行为) - current[finalKey] = { ...current[finalKey], ...module }; - }); - - return result; - } - - /** - * 预加载关键模块 - */ - async preloadEssentials(locale: string): Promise { - const essentials = [ - 'core/common', - 'core/navigation', - 'features/chat' - ]; - - await Promise.all( - essentials.map(module => this.loadModule(locale, module)) - ); - } - - /** - * 清理缓存 - */ - clearCache(locale?: string): void { - if (locale) { - // 清理特定语言的缓存 - const keys = Array.from(this.cache.keys()).filter((key: string) => key.startsWith(`${locale}:`)); - keys.forEach((key: string) => this.cache.delete(key)); - } else { - // 清理所有缓存 - this.cache.clear(); - } - } - - /** - * 获取加载状态 - */ - getLoadingStatus(): { total: number; loaded: number; modules: ModuleInfo[] } { - const modules = Array.from(this.moduleRegistry.values()); - const loaded = modules.filter(m => m.loaded).length; - - return { - total: modules.length, - loaded, - modules - }; - } - - /** - * 热重载模块 - */ - async reloadModule(locale: string, moduleName: string): Promise { - const cacheKey = `${locale}:${moduleName}`; - this.cache.delete(cacheKey); - - const moduleInfo = this.moduleRegistry.get(moduleName); - if (moduleInfo) { - moduleInfo.loaded = false; - } - - return this.loadModule(locale, moduleName); - } - - -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/core/actions.json b/legacy-dashboard/src/i18n/locales/en-US/core/actions.json deleted file mode 100644 index d53a0cac..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/core/actions.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "create": "Create", - "read": "Read", - "update": "Update", - "delete": "Delete", - "search": "Search", - "filter": "Filter", - "sort": "Sort", - "export": "Export", - "import": "Import", - "backup": "Backup", - "restore": "Restore", - "copy": "Copy", - "paste": "Paste", - "cut": "Cut", - "undo": "Undo", - "redo": "Redo", - "refresh": "Refresh", - "submit": "Submit", - "reset": "Reset", - "clear": "Clear", - "save": "Save", - "close": "Close" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/core/common.json b/legacy-dashboard/src/i18n/locales/en-US/core/common.json deleted file mode 100644 index 017d0629..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/core/common.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "save": "Save", - "cancel": "Cancel", - "close": "Close", - "copy": "Copy", - "copied": "Copied", - "copyFailed": "Copy failed", - "delete": "Delete", - "edit": "Edit", - "add": "Add", - "confirm": "Confirm", - "loading": "Loading...", - "success": "Success", - "error": "Error", - "warning": "Warning", - "info": "Info", - "name": "Name", - "description": "Description", - "author": "Author", - "status": "Status", - "actions": "Actions", - "enable": "Enable", - "disable": "Disable", - "enabled": "Enabled", - "disabled": "Disabled", - "reload": "Reload", - "configure": "Configure", - "install": "Install", - "uninstall": "Uninstall", - "update": "Update", - "language": "Language", - "settings": "Settings", - "locale": "en-US", - "type": "Type", - "press": "Press", - "longPress": "Long press", - "yes": "Yes", - "no": "No", - "imagePreview": "Image Preview", - "autoDetect": "Auto Detect", - "dialog": { - "confirmTitle": "Confirm Action", - "confirmMessage": "Are you sure you want to perform this action?", - "confirmButton": "Confirm", - "cancelButton": "Cancel" - }, - "restart": { - "waiting": "Waiting for AstrBot to restart...", - "maxRetriesReached": "Maximum retry attempts reached, please check manually." - }, - "upgradeRecovery": { - "title": "Upgrade Needs Restart", - "description": "The WebUI has been updated to {dashboardVersion}, but AstrBot Core is still {coreVersion}. This usually means the restart flow was interrupted by refreshing the page during upgrade. If restarting still does not resolve it, try deleting the data/dist folder under the AstrBot runtime directory, then restart AstrBot.", - "hint": "Restart the backend to finish the upgrade. This page will reload automatically after AstrBot is back.", - "restartButton": "Restart Backend", - "laterButton": "Later", - "restarting": "Requesting backend restart...", - "waiting": "Restart requested. Waiting for the backend to recover...", - "failed": "Restart request failed. Please restart AstrBot manually." - }, - "readme": { - "title": "Extension Documentation", - "buttons": { - "viewOnGithub": "View Repository on GitHub", - "refresh": "Refresh Documentation" - }, - "loading": "Loading README documentation...", - "errors": { - "fetchFailed": "Failed to fetch README", - "fetchError": "Error occurred while fetching README" - }, - "empty": { - "title": "This extension does not provide documentation link or GitHub repository address.", - "subtitle": "Please check the extension marketplace or contact the extension author for more information." - } - }, - "changelog": { - "title": "Changelog", - "loading": "Loading changelog...", - "empty": { - "title": "No changelog available for this plugin", - "subtitle": "Developers can add a CHANGELOG.md file in the plugin directory to provide changelog" - } - }, - "editor": { - "fullscreen": "Fullscreen Edit", - "editingTitle": "Editing Content" - }, - "templateList": { - "addEntry": "Add Entry", - "empty": "No entries yet, pick a template to add", - "missingTemplate": "Template not found, please remove and add again.", - "unknownTemplate": "Template not specified" - }, - "list": { - "addItemPlaceholder": "Add new item, press Enter to confirm", - "addButton": "Add", - "addMore": "Add More", - "batchImport": "Batch Import", - "batchImportTitle": "Batch Import", - "batchImportLabel": "One item per line", - "batchImportPlaceholder": "Example:\nItem 1\nItem 2\nItem 3\nItem 4", - "batchImportHint": "Each line will be treated as a separate item, empty lines will be ignored", - "batchImportButton": "Import {count} items", - "noItems": "No items", - "noItemsHint": "No items yet, type in the input above and press Enter to add", - "inputPlaceholder": "Type and press Enter to add", - "editTitle": "Edit List Items", - "modifyButton": "Modify" - }, - "itemCard": { - "enabled": "Enabled", - "disabled": "Disabled", - "delete": "Delete", - "edit": "Edit", - "copy": "Copy", - "noData": "No data available" - }, - "objectEditor": { - "dialogTitle": "Edit Key-Value Pairs", - "noItems": "No items", - "noParams": "No parameters", - "presets": "Presets", - "newKeyLabel": "New key", - "valueTypeLabel": "Value type", - "keyExists": "Key already exists", - "invalidJson": "Invalid JSON format", - "placeholders": { - "keyName": "Key", - "stringValue": "String value", - "numberValue": "Numeric value", - "jsonValue": "JSON" - } - }, - "firstNotice": { - "title": "First Notice", - "loading": "Loading first notice...", - "empty": { - "title": "No first notice content available", - "subtitle": "FIRST_NOTICE.md was not found or is empty." - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/core/header.json b/legacy-dashboard/src/i18n/locales/en-US/core/header.json deleted file mode 100644 index 824e2dec..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/core/header.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "logoTitle": "AstrBot Dashboard", - "version": { - "hasNewVersion": "AstrBot has a new version!", - "dashboardHasNewVersion": "WebUI has a new version!" - }, - "buttons": { - "update": "Update", - "account": "Account", - "theme": { - "title": "Theme", - "light": "Light Mode", - "dark": "Dark Mode", - "system": "Follow System" - } - }, - "updateDialog": { - "title": "Update AstrBot", - "currentVersion": "Current Version", - "status": { - "checking": "Checking for updates...", - "switching": "Switching version...", - "updating": "Updating..." - }, - "tabs": { - "release": "😊 Release" - }, - "advancedSettings": "Advanced settings", - "releases": "Releases", - "updateToLatest": "Update to Latest Version", - "showPreReleases": "Show pre-release versions", - "preRelease": "Pre-release", - "preReleaseWarning": { - "title": "Pre-release Version Notice", - "description": "Versions marked as pre-release may contain unknown issues or bugs and are not recommended for production use. If you encounter any problems, please visit ", - "issueLink": "GitHub Issues" - }, - "tip": "💡 TIP:", - "tipContinue": "By default, the corresponding version of the WebUI files will be downloaded when switching versions. The WebUI code is located in the dashboard directory of the project, and you can use npm to build it yourself.", - "dockerTip": "When switching versions, it will try to update both the bot main program and the dashboard. If you are using Docker deployment, you can also re-pull the image or use", - "dockerTipLink": "watchtower", - "dockerTipContinue": "to automatically monitor and pull.", - "table": { - "tag": "Tag", - "publishDate": "Publish Date", - "content": "Content", - "sourceUrl": "Source URL", - "actions": "Actions", - "view": "View", - "switch": "Switch" - }, - "releaseNotes": { - "title": "Release Notes", - "latestLabel": "Latest version" - }, - "progress": { - "target": "Target version: ", - "preparing": "Preparing update...", - "dashboard": "Download WebUI", - "core": "Download project code", - "dependencies": "Updating dependencies...", - "restart": "Preparing restart...", - "restarting": "Restarting AstrBot", - "completed": "Update complete", - "successReady": "Update successful", - "autoReloadIn": "Refreshing automatically in {seconds}s", - "reloadNow": "Refresh now", - "failed": "Update failed" - }, - "redirectConfirm": { - "title": "Leaving AstrBot", - "message": "You are about to open the GitHub Releases page. Continue?", - "latestLabel": "Latest", - "targetVersion": "Target version: ", - "currentVersion": "Current version: ", - "guideTitle": "Recommended after opening:", - "guideStep1": "Download the installer that matches your OS and architecture.", - "guideStep2": "Install it and restart AstrBot.", - "guideStep3": "If you use Docker, prefer the image update path." - }, - "desktopApp": { - "title": "Update Desktop App", - "message": "Check and upgrade the AstrBot desktop application.", - "currentVersion": "Current version: ", - "latestVersion": "Latest version: ", - "checking": "Checking desktop app updates...", - "hasNewVersion": "A new version is available. Click confirm to upgrade.", - "isLatest": "Already on the latest version", - "installing": "Downloading and installing update. The app will restart automatically...", - "checkFailed": "Failed to check updates. Please try again later.", - "installFailed": "Upgrade failed. Please try again later." - }, - "dashboardUpdate": { - "title": "Update Dashboard to Latest Version Only", - "currentVersion": "Current Version", - "hasNewVersion": "New version available!", - "isLatest": "Already the latest version.", - "fallback": "Use this to re-download the WebUI if the full update path fails.", - "downloadAndUpdate": "Download and Update" - } - }, - "accountDialog": { - "title": "Modify Account", - "securityWarning": "Security Reminder: Please change your password to secure your account", - "securityWarningMd5": "The new AstrBot version has improved security. Please change your password.", - "securityWarningUpgrade": "MD5 password storage detected. Please change your password to complete the security upgrade.", - "form": { - "currentPassword": "Current Password", - "newPassword": "New Password", - "confirmPassword": "Confirm New Password", - "newUsername": "New Username (Optional)", - "passwordHint": "At least 8 characters, including uppercase, lowercase letters, and digits", - "confirmPasswordHint": "Please enter new password again to confirm", - "usernameHint": "Leave blank to keep current username", - "defaultCredentials": "The default username is astrbot. An initial password is generated on first startup and shown in the console." - }, - "validation": { - "passwordRequired": "Please enter password", - "passwordMinLength": "Password must be at least 8 characters", - "passwordUppercase": "Password must include at least one uppercase letter", - "passwordLowercase": "Password must include at least one lowercase letter", - "passwordDigit": "Password must include at least one digit", - "passwordMatch": "Passwords do not match", - "usernameMinLength": "Username must be at least 3 characters" - }, - "actions": { - "save": "Save Changes", - "cancel": "Cancel" - }, - "messages": { - "updateFailed": "Update failed, please try again" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/core/navigation.json b/legacy-dashboard/src/i18n/locales/en-US/core/navigation.json deleted file mode 100644 index e0e39639..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/core/navigation.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "welcome": "Welcome", - "dashboard": "Dashboard", - "platforms": "Platforms", - "providers": "Providers", - "commands": "Commands", - "persona": "Persona", - "subagent": "SubAgents", - "toolUse": "MCP Tools", - "config": "Config", - "chat": "Chat", - "cron": "Future Tasks", - "extension": "Extensions", - "extensionTabs": { - "installed": "AstrBot Plugins", - "market": "Plugin Market", - "mcp": "MCP Servers", - "skills": "Skills", - "components": "Handlers" - }, - "conversation": "Conversations", - "sessionManagement": "Custom Rules", - "console": "Console", - "trace": "Trace", - "alkaid": "Alkaid Lab", - "knowledgeBase": "Knowledge Base", - "about": "About", - "settings": "Settings", - "changelog": "Changelog", - "documentation": "Documentation", - "faq": "FAQ", - "github": "GitHub", - "drag": "Drag", - "groups": { - "more": "More Features" - }, - "changelogDialog": { - "title": "Changelog", - "loading": "Loading...", - "error": "Failed to load", - "notFound": "Changelog for this version not found", - "selectVersion": "Select Version", - "current": "Current" - }, - "configTabs": { - "normal": "Normal Config", - "system": "System Config" - }, - "pluginWebui": "Plugin Pages" -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/core/shared.json b/legacy-dashboard/src/i18n/locales/en-US/core/shared.json deleted file mode 100644 index 800c2617..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/core/shared.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "knowledgeBaseSelector": { - "notSelected": "Not selected", - "buttonText": "Select Knowledge Base...", - "dialogTitle": "Select Knowledge Base", - "loading": "Loading...", - "noKnowledgeBases": "No knowledge bases available", - "createKnowledgeBase": "Create Knowledge Base", - "selectedCount": "{count} knowledge base(s) selected", - "confirmSelection": "Confirm Selection", - "cancelSelection": "Cancel", - "noDescription": "No description", - "documentCount": "{count} document(s)", - "chunkCount": "{count} chunk(s)" - }, - "pluginSetSelector": { - "notSelected": "No plugins enabled", - "allPlugins": "All plugins enabled (*)", - "selectedCount": "{count} plugin(s) selected", - "buttonText": "Select Plugin Set...", - "dialogTitle": "Select Plugin Set", - "loading": "Loading...", - "enableAll": "Enable all plugins", - "enableNone": "Disable all plugins", - "customSelect": "Custom selection", - "noPlugins": "No plugins available", - "confirmSelection": "Confirm Selection", - "cancelSelection": "Cancel", - "noDescription": "No description", - "notActivated": "Not activated", - "note": "*System plugins and disabled plugins are not shown.", - "selectedPluginsLabel": "Selected Plugins:", - "allPluginsLabel": "All Plugins" - }, - "providerSelector": { - "notSelected": "Not selected", - "buttonText": "Select Provider...", - "dialogTitle": "Select Provider", - "loading": "Loading...", - "noProviders": "No providers available", - "confirmSelection": "Confirm Selection", - "cancelSelection": "Cancel", - "clearSelection": "None", - "clearSelectionSubtitle": "Clear current selection", - "unknownType": "Unknown type", - "createProvider": "Create Provider", - "manageProviders": "Provider Management", - "selectProviderPool": "Select Provider Pool...", - "selectedCount": "{count} provider(s) selected" - }, - "personaSelector": { - "notSelected": "Not selected", - "defaultPersona": "Default Persona", - "buttonText": "Select Persona...", - "dialogTitle": "Select Persona", - "noDescription": "No description", - "noPersonas": "No personas available", - "createPersona": "Create New Persona", - "cancelSelection": "Cancel", - "confirmSelection": "Confirm Selection", - "selectPersonaPool": "Select Persona Pool...", - "rootFolder": "All Personas", - "emptyFolder": "This folder is empty" - }, - "personaQuickPreview": { - "title": "Quick Persona Preview", - "loading": "Loading...", - "noPersonaSelected": "No persona selected", - "personaNotFound": "Persona details not found", - "systemPromptLabel": "System Prompt", - "toolsLabel": "Tools", - "skillsLabel": "Skills", - "originLabel": "Origin", - "originNameLabel": "Origin Name", - "toolInactive": "Disabled", - "toolInactiveTooltip": "This tool is disabled. Re-enable it in Extensions -> Handlers -> Function Tools.", - "allTools": "All tools available", - "allToolsWithCount": "All tools available ({count})", - "noTools": "No tools configured", - "allSkills": "All Skills available", - "allSkillsWithCount": "All Skills available ({count})", - "noSkills": "No Skills configured" - }, - "t2iTemplateEditor": { - "buttonText": "Customize T2I Template", - "dialogTitle": "Customize Text-to-Image HTML Template", - "newTemplateNameLabel": "Enter new template name", - "nameRequired": "Name is required", - "selectTemplateLabel": "Select Template", - "applied": "Applied", - "apply": "Apply", - "templateEditor": "Template Editor", - "new": "New", - "resetBase": "Reset Base", - "delete": "Delete", - "save": "Save", - "livePreview": "Live Preview (may differ)", - "refreshPreview": "Refresh Preview", - "previewText": "This is a sample text used to preview the template output.\n\nIt can contain multiple lines and various formatting.", - "syntaxHint": "Supports jinja2 syntax. Available variables: text | safe (text to render), version (AstrBot version). If the preview test content does not load or looks wrong, click Reset Base to restore the default template.", - "saveAndApply": "Save and Apply Current Template", - "confirmReset": "Confirm Reset", - "confirmResetMessage": "Are you sure you want to reset the 'base' template to default content? Any unsaved changes in the editor will be lost. This action cannot be undone.", - "confirmResetButton": "Confirm Reset", - "confirmDelete": "Confirm Delete", - "confirmDeleteMessage": "Are you sure you want to delete template '{name}'? This action cannot be undone.", - "confirmDeleteButton": "Confirm Delete", - "confirmAction": "Confirm Action", - "confirmApplyMessage": "Are you sure you want to save changes to '{name}' and set it as the active template?" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/core/status.json b/legacy-dashboard/src/i18n/locales/en-US/core/status.json deleted file mode 100644 index e64e9973..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/core/status.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "loading": "Loading", - "success": "Success", - "error": "Error", - "warning": "Warning", - "info": "Info", - "pending": "Pending", - "processing": "Processing", - "completed": "Completed", - "failed": "Failed", - "cancelled": "Cancelled", - "timeout": "Timeout", - "connecting": "Connecting", - "connected": "Connected", - "disconnected": "Disconnected", - "online": "Online", - "offline": "Offline", - "active": "Active", - "inactive": "Inactive", - "ready": "Ready", - "busy": "Busy" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/about.json b/legacy-dashboard/src/i18n/locales/en-US/features/about.json deleted file mode 100644 index 57cc5583..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/about.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "hero": { - "title": "AstrBot", - "subtitle": "A project out of interests and loves ❤️", - "starButton": "Star this project! 🌟", - "issueButton": "Submit Issue" - }, - "contributors": { - "title": "Contributors", - "description": "This project is maintained by many open source community members. Thanks to every contributor for their dedication!", - "viewLink": "View AstrBot Contributors" - }, - "stats": { - "title": "Global Deployment", - "license": "AstrBot is open source under AGPL v3 license" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/index.json b/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/index.json deleted file mode 100644 index 2455986e..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/index.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "title": "Alkaid Laboratory", - "subtitle": "Explore cutting-edge AI features", - "comingSoon": "The world ahead, let's explore it later!", - "page": { - "title": "The Alkaid Project.", - "subtitle": "AstrBot Alpha Project", - "navigation": { - "knowledgeBase": "Knowledge Base (Plugin)", - "longTermMemory": "Long-term Memory", - "other": "..." - } - }, - "features": { - "knowledgeBase": "Knowledge Base", - "longTermMemory": "Long-term Memory", - "advancedChat": "Advanced Chat", - "multiModal": "Multi-modal Interaction" - }, - "status": { - "experimental": "Experimental", - "beta": "Beta", - "stable": "Stable", - "deprecated": "Deprecated" - }, - "sigma": { - "subtitle": "AstrBot Experimental Project", - "visualization": "Visualization", - "filterUserId": "Filter User ID", - "filter": "Filter", - "resetFilter": "Reset Filter", - "refreshGraph": "Refresh Graph", - "nodeDetails": "Node Details", - "id": "ID", - "type": "Type", - "name": "Name", - "userId": "User ID", - "timestamp": "Timestamp", - "graphStats": "Graph Statistics", - "nodeCount": "Node Count", - "edgeCount": "Edge Count", - "inDevelopment": "Under Development" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/knowledge-base.json b/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/knowledge-base.json deleted file mode 100644 index 98d5aea3..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/knowledge-base.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "title": "Knowledge Base", - "subtitle": "Manage and query knowledge base content", - "documents": { - "title": "Document List", - "name": "Document Name", - "size": "Size", - "uploadTime": "Upload Time", - "status": "Status", - "actions": "Actions" - }, - "management": { - "delete": "Delete", - "preview": "Preview", - "download": "Download", - "reindex": "Reindex" - }, - "notInstalled": { - "title": "Knowledge base plugin is not installed yet", - "install": "Install now" - }, - "empty": { - "title": "No knowledge base yet, create one now! 🙂", - "create": "Create Knowledge Base" - }, - "list": { - "title": "Knowledge Base List", - "create": "Create Knowledge Base", - "config": "Configure", - "checkUpdate": "Check Plugin Update", - "updatePlugin": "Update Plugin to {version}", - "knowledgeCount": "knowledge items", - "tips": "Tips: Learn how to use through /kb command in chat page!" - }, - "createDialog": { - "title": "Create New Knowledge Base", - "nameLabel": "Knowledge Base Name", - "descriptionLabel": "Description", - "descriptionPlaceholder": "Brief description of the knowledge base...", - "embeddingModelLabel": "Embedding Model", - "rerankModelLabel": "Rerank Model", - "providerInfo": "Provider ID: {id} | Embedding Model Dimensions: {dimensions}", - "rerankProviderInfo": "Provider ID: {id}", - "tips": "Tips: Once you choose an embedding model for a knowledge base, please do not modify the provider's model or vector dimension information, otherwise it will seriously affect the recall rate of the knowledge base or even cause errors.", - "cancel": "Cancel", - "create": "Create" - }, - "emojiPicker": { - "title": "Select Emoji", - "close": "Close", - "categories": { - "emotions": "Smileys and Emotions", - "animals": "Animals and Nature", - "food": "Food and Drink", - "activities": "Activities and Objects", - "travel": "Travel and Places", - "symbols": "Symbols and Flags" - } - }, - "contentDialog": { - "title": "Knowledge Base Management", - "embeddingModel": "Embedding Model", - "vectorDimension": "Vector Dimension", - "usage": "Usage: Enter \"/kb use {name}\" in the chat page", - "tabs": { - "upload": "Upload Files", - "search": "Search Content", - "fromURL": "From URL" - } - }, - "upload": { - "title": "Upload Files to Knowledge Base", - "subtitle": "Supports txt, pdf, epub, word, excel and other formats", - "dropzone": "Drag and drop files here or click to upload", - "chunkSettings": { - "title": "Chunk Settings", - "tooltip": "Chunk size determines the size of each text block, overlap length determines the overlap between adjacent text blocks.\nSmaller chunks are more precise but increase quantity, appropriate overlap can improve retrieval accuracy.", - "chunkSizeLabel": "Chunk Size", - "chunkSizeHint": "Control the size of each text block, leave empty to use default value", - "overlapLabel": "Overlap Length", - "overlapHint": "Control the overlap between adjacent text blocks, leave empty to use default value" - }, - "upload": "Upload File", - "uploading": "Uploading..." - }, - "search": { - "queryLabel": "Search Knowledge Base Content", - "queryPlaceholder": "Enter keywords to search knowledge base content...", - "resultCountLabel": "Result Count", - "searching": "Searching...", - "resultsTitle": "Search Results", - "relevance": "Relevance", - "noResults": "No matching content found" - }, - "deleteDialog": { - "title": "Confirm Delete", - "confirmText": "Are you sure you want to delete knowledge base {name}?", - "warning": "This operation is irreversible, all knowledge base content will be permanently deleted.", - "cancel": "Cancel", - "delete": "Delete" - }, - "messages": { - "pluginNotAvailable": "Plugin not installed or unavailable", - "pluginNotActivated": "astrbot_plugin_knowledge_base plugin not activated, please activate it in the plugin management page and restart AstrBot", - "checkPluginFailed": "Failed to check plugin", - "installFailed": "Installation failed", - "installPluginFailed": "Failed to install plugin", - "getKnowledgeBaseListFailed": "Failed to get knowledge base list", - "knowledgeBaseCreated": "Knowledge base created successfully", - "createFailed": "Creation failed", - "createKnowledgeBaseFailed": "Failed to create knowledge base", - "pleaseEnterKnowledgeBaseName": "Please enter knowledge base name", - "pleaseSelectFile": "Please select a file first", - "operationSuccess": "Operation successful: {message}", - "uploadFailed": "Upload failed", - "fileUploadFailed": "File upload failed", - "pleaseEnterSearchContent": "Please enter search content", - "noMatchingContent": "No matching content found", - "searchFailed": "Search failed", - "searchKnowledgeBaseFailed": "Failed to search knowledge base", - "deleteTargetNotExists": "Delete target does not exist", - "knowledgeBaseDeleted": "Knowledge base deleted successfully", - "deleteFailed": "Deletion failed", - "deleteKnowledgeBaseFailed": "Failed to delete knowledge base", - "getEmbeddingModelListFailed": "Failed to get embedding model list", - "updateAvailable": "New version available: {current} -> {latest}", - "pluginUpToDate": "Plugin is up to date", - "pluginNotFoundInMarket": "Plugin not found in market", - "checkUpdateFailed": "Failed to check for updates", - "updateSuccess": "Plugin updated successfully", - "updateFailed": "Update failed", - "updatePluginFailed": "Failed to update plugin" - }, - "importFromUrl": { - "title": "Import from URL", - "urlLabel": "Web Page URL", - "urlPlaceholder": "Enter the URL of the web page to extract knowledge from", - "optionsTitle": "Import Options", - "tooltip": "These options control how text is extracted and processed from the URL content.\nLeave blank to use the plugin's default settings.\nEnabling LLM text repair and summary may take a long time.", - "useLlmRepairLabel": "Enable LLM Text Repair", - "useClusteringSummaryLabel": "Enable Clustering Summary", - "repairLlmProviderIdLabel": "Text Repair Model", - "summarizeLlmProviderIdLabel": "Summarize Model", - "embeddingProviderIdLabel": "Embedding Model", - "chunkSizeLabel": "Chunk Size", - "chunkOverlapLabel": "Chunk Overlap", - "startImport": "Start Import", - "importing": "Importing...", - "importSuccess": "Import Successful", - "importFailed": "Import Failed", - "uploadingChunks": "Content extracted successfully, uploading chunks...", - "preRequisite": "Hint: Please go to the plugin market to install astrbot_plugin_url_2_knowledge_base and follow the instructions in the plugin documentation to complete the playwright installation before using this feature.", - "allChunksUploaded": "All chunks uploaded successfully" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/memory.json b/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/memory.json deleted file mode 100644 index 69ecb72f..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/alkaid/memory.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "title": "Long-term Memory", - "subtitle": "AI assistant's long-term memory management", - "memories": { - "title": "Memory List", - "content": "Memory Content", - "importance": "Importance Level", - "createTime": "Create Time", - "lastAccess": "Last Access", - "category": "Category" - }, - "categories": { - "personal": "Personal Information", - "preferences": "Preference Settings", - "conversations": "Conversation History", - "facts": "Factual Information", - "skills": "Skill Knowledge" - }, - "importance": { - "high": "High", - "medium": "Medium", - "low": "Low" - }, - "actions": { - "view": "View Details", - "edit": "Edit", - "delete": "Delete", - "pin": "Pin", - "unpin": "Unpin" - }, - "filters": { - "all": "All", - "category": "By Category", - "importance": "By Importance", - "dateRange": "By Date Range", - "title": "Filters", - "userIdLabel": "Filter by User ID", - "filterButton": "Filter", - "resetButton": "Reset Filter", - "refreshButton": "Refresh Graph" - }, - "search": { - "title": "Search Memory", - "userIdLabel": "User ID", - "queryLabel": "Enter keywords", - "searchButton": "Search", - "resultsTitle": "Search Results", - "noResults": "No relevant memory content found", - "similarity": "Relevance", - "noTextContent": "No text content" - }, - "addMemory": { - "title": "Add Memory Data", - "textLabel": "Enter text content", - "userIdLabel": "User ID", - "summarizeLabel": "Need summary", - "addButton": "Add Data" - }, - "nodeDetails": { - "title": "Node Details", - "id": "ID", - "type": "Type", - "name": "Name", - "userId": "User ID", - "timestamp": "Timestamp" - }, - "graphStats": { - "title": "Graph Statistics", - "nodeCount": "Node Count", - "edgeCount": "Edge Count" - }, - "factDialog": { - "title": "Memory Fact", - "id": "ID", - "docId": "Document ID", - "createdAt": "Created At", - "updatedAt": "Updated At", - "metadata": "Metadata", - "metadataKey": "Key", - "metadataValue": "Value", - "loading": "Loading...", - "close": "Close", - "noValue": "None", - "unknown": "Unknown" - }, - "messages": { - "searchQueryRequired": "Please enter search keywords", - "searchSuccess": "Found {count} relevant memories", - "searchNoResults": "No relevant memory content found", - "searchError": "Search failed", - "addSuccess": "Memory data added successfully!", - "addError": "Failed to add memory data", - "factDetailsError": "Failed to get memory details", - "metadataParseError": "Unable to parse metadata", - "relationNoMemoryData": "This relation has no associated memory data" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/auth.json b/legacy-dashboard/src/i18n/locales/en-US/features/auth.json deleted file mode 100644 index 842d0192..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/auth.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "login": "Login", - "username": "Username", - "password": "Password", - "defaultHint": "If this is your first login, check the logs for the default password.", - "totp": { - "code": "Verification code", - "verify": "Verify", - "trustDevice": "Trust this device for 30 days" - }, - "recovery": { - "title": "Recovery Code Login", - "subtitle": "Lost access to your authenticator app? Use a recovery code to log in.", - "code": "Recovery Code", - "submit": "Log in with Recovery Code", - "useRecoveryCode": "Can't use TOTP?", - "backToLogin": "Back to Login", - "savedWarning": "If lost, account access cannot be restored through normal means.", - "continue": "Continue", - "acknowledge": "I have saved my recovery codes", - "totpDisableWarning": "Using a recovery code will disable two-factor authentication." - }, - "setup": { - "title": "Set Up Account", - "subtitle": "Create the account used to manage AstrBot", - "username": "New username", - "password": "New password", - "confirmPassword": "Confirm new password", - "passwordHint": "Use at least 8 characters with uppercase, lowercase, and a number.", - "submit": "Complete Setup", - "totp": { - "code": "Verification code", - "qrAlt": "TOTP QR code", - "title": "Complete TOTP Setup", - "subtitle": "Scan the QR code with your authenticator app to enable two-factor authentication.", - "step2Hint": "Scan this QR code with your authenticator app (e.g. Google Authenticator, Authy) and enter the code below.", - "verify": "Verify & Complete", - "verifyError": "Unable to verify the code. Enter the latest code from your authenticator app.", - "disableError": "Unable to disable TOTP. Please try again.", - "back": "Back" - }, - "validation": { - "usernameRequired": "Enter a username", - "usernameMinLength": "Username must be at least 3 characters", - "passwordRequired": "Enter a password", - "passwordMinLength": "Password must be at least 8 characters", - "passwordUppercase": "Password must include at least one uppercase letter", - "passwordLowercase": "Password must include at least one lowercase letter", - "passwordDigit": "Password must include at least one number", - "confirmPasswordRequired": "Confirm the password", - "passwordMatch": "Passwords do not match" - } - }, - "logo": { - "title": "AstrBot Dashboard", - "totpTitle": "Two-Factor Verification", - "subtitle": "Welcome" - }, - "versions": { - "webui": "WebUI", - "astrbotRuntime": "AstrBot", - "astrbotCode": "AstrBot(Code)", - "mismatchTooltip": "Version mismatch", - "dialogTitle": "Version status needs attention", - "webuiMismatchTitle": "WebUI does not match the current AstrBot version", - "webuiMismatchMessage": "This WebUI may not have been built for the running AstrBot version, so some pages or APIs may behave unexpectedly. Restart AstrBot manually first. If the mismatch remains, delete data/dist in the runtime directory and restart again so AstrBot can download the matching WebUI.", - "runtimeMismatchTitle": "Running AstrBot does not match the code version", - "runtimeMismatchMessage": "The code has been updated, but the current process is still running an older version. Restart AstrBot manually to load the new code.", - "faq": "View FAQ", - "close": "Close" - }, - "theme": { - "light": "Light Mode", - "dark": "Dark Mode", - "system": "Follow System", - "switchToDark": "Switch to Dark Theme", - "switchToLight": "Switch to Light Theme", - "title": "Theme" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/chart.json b/legacy-dashboard/src/i18n/locales/en-US/features/chart.json deleted file mode 100644 index 919fb850..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/chart.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "messageCount": "Message Count", - "time": "Time" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/chat.json b/legacy-dashboard/src/i18n/locales/en-US/features/chat.json deleted file mode 100644 index 9f8401a1..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/chat.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "title": "Let's Chat!", - "subtitle": "Chat with AI Assistant", - "input": { - "placeholder": "Ask anything...", - "send": "Send", - "clear": "Clear", - "upload": "Upload File", - "voice": "Voice Input", - "recordingPrompt": "Recording, please speak...", - "chatPrompt": "Let's chat!", - "dropToUpload": "Drop files to upload", - "stopGenerating": "Stop generating" - }, - "message": { - "user": "User", - "assistant": "Assistant", - "system": "System", - "error": "Error Message", - "loading": "Thinking..." - }, - "voice": { - "start": "Start Recording", - "stop": "Stop Recording", - "recording": "New Recording", - "processing": "Processing...", - "error": "Recording Failed", - "listening": "Listening...", - "speaking": "Speaking", - "startRecording": "Start Voice Input", - "liveMode": "Live Mode" - }, - "welcome": { - "title": "Anything on your mind today?", - "subtitle": "Your Intelligent Chat Assistant", - "quickActions": "Quick Actions", - "examples": "Example Questions" - }, - "actions": { - "copy": "Copy", - "regenerate": "Regenerate", - "retry": "Retry", - "retryWithModel": "Use another model", - "noAvailableModels": "No available models", - "like": "Like", - "dislike": "Dislike", - "share": "Share", - "newChat": "New Chat", - "deleteChat": "Delete this conversation", - "editTitle": "Edit Title", - "fullscreen": "Fullscreen Mode", - "exitFullscreen": "Exit Fullscreen", - "reply": "Reply", - "providerConfig": "Model Configuration", - "toolsUsed": "Tool Used", - "toolCallUsed": "Used {name} tool", - "pythonCodeAnalysis": "Python Code Analysis Used" - }, - "ipython": { - "output": "Output" - }, - "toolStatus": { - "done": "Done", - "running": "Running" - }, - "thread": { - "title": "Thread", - "askInThread": "Ask in thread", - "count": "{count} threads", - "createFailed": "Failed to create thread", - "delete": "Delete thread", - "confirmDelete": "Delete this thread? This action cannot be undone.", - "placeholder": "Ask about this excerpt..." - }, - "conversation": { - "title": "Conversations", - "newConversation": "New Conversation", - "noHistory": "No conversation history", - "systemStatus": "System Status", - "llmService": "LLM Service", - "speechToText": "Speech to Text", - "editDisplayName": "Edit Session Name", - "displayName": "Session Name", - "displayNameUpdated": "Session name updated", - "displayNameUpdateFailed": "Failed to update session name", - "confirmDelete": "Are you sure you want to delete \"{name}\"? This action cannot be undone." - }, - "modes": { - "darkMode": "Switch to Dark Mode", - "lightMode": "Switch to Light Mode" - }, - "shortcuts": { - "help": "Get Help", - "voiceRecord": "Record Voice", - "pasteImage": "Paste Image", - "sendKey": { - "title": "Send Shortcut", - "enterToSend": "Enter to send", - "shiftEnterToSend": "Shift+Enter to send" - } - }, - "streaming": { - "enabled": "Streaming enabled", - "disabled": "Streaming disabled", - "on": "Stream", - "off": "Normal" - }, - "transport": { - "title": "Transport Mode", - "sse": "SSE", - "websocket": "WebSocket" - }, - "config": { - "title": "Config" - }, - "reasoning": { - "thinking": "Thinking Process", - "summarySeparator": ", ", - "thinkSummary": "Thought {count} times", - "toolSummary": "used {count} tools", - "think": "Thinking", - "toolUsed": "Using Tool" - }, - "reply": { - "replyTo": "Reply to", - "notFound": "Message not found" - }, - "project": { - "title": "Projects", - "create": "Create Project", - "edit": "Edit Project", - "name": "Project Name", - "emoji": "Icon (Emoji)", - "description": "Description (Optional)", - "workspace": { - "type": "Workspace", - "session": "Per-session workspace", - "project": "Shared project workspace", - "custom": "Custom path", - "path": "Workspace path" - }, - "loadingSessions": "Loading conversations...", - "noSessions": "No conversations in this project", - "noProjects": "No projects", - "confirmDelete": "Are you sure you want to delete project \"{title}\"? Conversations in this project will not be deleted." - }, - "time": { - "today": "Today", - "yesterday": "Yesterday" - }, - "stats": { - "tokens": "Tokens", - "inputTokens": "Input (other)", - "outputTokens": "Output Tokens", - "cachedTokens": "Input (cached)", - "duration": "Duration", - "ttft": "Time to First Token" - }, - "tokenUsage": { - "tooltip": "{used} / {limit} tokens ({percent}%)" - }, - "refs": { - "title": "References", - "sources": "Sources" - }, - "connection": { - "title": "Connection Status Notice", - "message": "The system detected that the chat connection needs to be re-established.", - "reasons": "This may be due to:", - "reasonWindowResize": "Switching chat window size (normal behavior)", - "reasonMultipleTabs": "Opening chat pages in other tabs", - "reasonNetworkIssue": "Temporary network interruption", - "notice": "Note: To ensure proper message delivery, the system only allows one active chat connection at a time. If you're using chat in multiple tabs, please keep only one page open.", - "understand": "Got it", - "status": { - "reconnecting": "Reconnecting...", - "reconnected": "Chat connection re-established", - "failed": "Connection failed, please refresh the page" - } - }, - "commandSuggestion": { - "navigate": "Navigate", - "select": "Select", - "close": "Close" - }, - "errors": { - "sendMessageFailed": "Failed to send message, please try again", - "createSessionFailed": "Failed to create session, please refresh the page" - }, - "batch": { - "selected": "{count} selected", - "confirmDelete": "Are you sure you want to delete {count} conversation(s)? This action cannot be undone.", - "selectAll": "Select All", - "deselectAll": "Deselect All", - "delete": "Delete", - "exit": "Exit", - "partialFailure": "{failed} of {total} conversations failed to delete", - "requestFailed": "Failed to delete conversations. Please try again." - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/command.json b/legacy-dashboard/src/i18n/locales/en-US/features/command.json deleted file mode 100644 index 95ecc989..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/command.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "title": "Command Management", - "summary": { - "total": "Displayed commands", - "disabled": "Disabled", - "conflicts": "Conflicts" - }, - "conflictAlert": { - "title": "Command Conflicts Detected", - "description": "There are {count} conflicting commands. Conflicting commands will trigger multiple plugins simultaneously, which may cause unexpected behavior.", - "hint": "Click the \"Rename\" button to rename conflicting commands and resolve conflicts." - }, - "table": { - "headers": { - "command": "Command", - "type": "Type", - "plugin": "Plugin", - "description": "Description", - "permission": "Permission", - "status": "Status", - "actions": "Actions" - } - }, - "type": { - "command": "Command", - "group": "Group", - "subCommand": "Sub-command" - }, - "status": { - "enabled": "Enabled", - "disabled": "Disabled", - "conflict": "Conflict" - }, - "permission": { - "everyone": "Everyone", - "admin": "Admin" - }, - "tooltips": { - "enable": "Enable command", - "disable": "Disable command", - "rename": "Rename command", - "viewDetails": "View details" - }, - "dialogs": { - "rename": { - "title": "Rename Command", - "newName": "New command name", - "aliases": "Manage aliases", - "addAlias": "Add alias", - "cancel": "Cancel", - "confirm": "Confirm" - }, - "details": { - "title": "Command Details", - "type": "Command Type", - "handler": "Handler", - "module": "Module Path", - "originalCommand": "Original Command", - "effectiveCommand": "Effective Command", - "parentGroup": "Parent Group", - "subCommands": "Sub-commands", - "aliases": "Aliases", - "permission": "Permission", - "conflictStatus": "Conflict Status" - } - }, - "messages": { - "toggleSuccess": "Command status updated", - "toggleFailed": "Failed to update command status", - "renameSuccess": "Command renamed", - "renameFailed": "Rename failed", - "loadFailed": "Failed to load commands", - "updateSuccess": "Updated successfully", - "updateFailed": "Update failed" - }, - "search": { - "placeholder": "Search commands..." - }, - "empty": { - "noCommands": "No Commands", - "noCommandsDesc": "No commands found" - }, - "filters": { - "all": "All", - "enabled": "Enabled", - "disabled": "Disabled", - "conflict": "Conflict", - "byPlugin": "Filter by plugin", - "byType": "Filter by type", - "byPermission": "Filter by permission", - "byStatus": "Filter by status", - "showSystemPlugins": "Show system plugins commands", - "systemPluginConflictHint": "System plugin conflicts detected. Resolve conflicts to hide." - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/config-metadata.json b/legacy-dashboard/src/i18n/locales/en-US/features/config-metadata.json deleted file mode 100644 index 31d1362b..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/config-metadata.json +++ /dev/null @@ -1,1768 +0,0 @@ -{ - "ai_group": { - "name": "AI", - "agent_runner": { - "description": "Agent Runner", - "hint": "Select the runner for AI conversations. Defaults to AstrBot's built-in Agent runner, which supports knowledge base, persona, and tool calling features. You don't need to modify this section unless you plan to integrate third-party Agent runners like Dify, Coze, or DeerFlow.", - "provider_settings": { - "enable": { - "description": "Enable", - "hint": "Master switch for AI conversations" - }, - "agent_runner_type": { - "description": "Runner", - "labels": [ - "Built-in Agent", - "Dify", - "Coze", - "Alibaba Cloud Bailian Application", - "DeerFlow" - ] - }, - "coze_agent_runner_provider_id": { - "description": "Coze Agent Runner Provider ID" - }, - "dify_agent_runner_provider_id": { - "description": "Dify Agent Runner Provider ID" - }, - "dashscope_agent_runner_provider_id": { - "description": "Alibaba Cloud Bailian Application Agent Runner Provider ID" - }, - "deerflow_agent_runner_provider_id": { - "description": "DeerFlow Agent Runner Provider ID" - } - } - }, - "ai": { - "description": "Model", - "hint": "When using non-built-in Agent runners, the default chat model and default image caption model may not take effect, but some plugins rely on these settings to invoke AI capabilities.", - "provider_settings": { - "default_provider_id": { - "description": "Default Chat Model", - "hint": "Uses the first model when left empty" - }, - "fallback_chat_models": { - "description": "Fallback chat model IDs", - "hint": "When the primary chat model request fails, fallback to these chat models in order." - }, - "request_max_retries": { - "description": "Request Max Retries", - "hint": "Maximum attempts for a single model request when retryable errors occur." - }, - "default_image_caption_provider_id": { - "description": "Default Image Caption Model", - "hint": "Leave empty to disable; useful for non-multimodal models" - }, - "image_caption_prompt": { - "description": "Image Caption Prompt" - } - }, - "provider_stt_settings": { - "enable": { - "description": "Enable Speech-to-Text", - "hint": "Master switch for STT" - }, - "provider_id": { - "description": "Default Speech-to-Text Model", - "hint": "Users can also select session-specific STT models using the /provider command." - } - }, - "provider_tts_settings": { - "enable": { - "description": "Enable Text-to-Speech", - "hint": "Master switch for TTS" - }, - "provider_id": { - "description": "Default Text-to-Speech Model" - }, - "trigger_probability": { - "description": "TTS Trigger Probability" - } - } - }, - "persona": { - "description": "Persona", - "hint": "Set the default persona for AI conversations. Personas can be managed in the Persona tab.", - "provider_settings": { - "default_personality": { - "description": "Default Persona" - } - } - }, - "knowledgebase": { - "description": "Knowledge Base", - "kb_names": { - "description": "Knowledge Base List", - "hint": "Supports multiple selections" - }, - "kb_fusion_top_k": { - "description": "Fusion Search Results Count", - "hint": "Number of results returned after fusing search results from multiple knowledge bases" - }, - "kb_final_top_k": { - "description": "Final Results Count", - "hint": "Number of results retrieved from the knowledge base. Higher values may provide more relevant information but could also introduce noise. Adjust based on actual needs" - }, - "kb_agentic_mode": { - "description": "Agentic Knowledge Base Retrieval", - "hint": "When enabled, knowledge base retrieval becomes an LLM Tool, allowing the model to autonomously decide when to query the knowledge base. Requires the model to support function calling." - } - }, - "websearch": { - "description": "Web Search", - "provider_settings": { - "web_search": { - "description": "Enable Web Search" - }, - "websearch_provider": { - "description": "Web Search Provider" - }, - "websearch_tavily_key": { - "description": "Tavily API Key", - "hint": "Multiple keys can be added for rotation." - }, - "websearch_bocha_key": { - "description": "BoCha API Key", - "hint": "Multiple keys can be added for rotation." - }, - "websearch_brave_key": { - "description": "Brave Search API Key", - "hint": "Multiple keys can be added for rotation." - }, - "websearch_firecrawl_key": { - "description": "Firecrawl API Key", - "hint": "Multiple keys can be added for rotation." - }, - "websearch_baidu_app_builder_key": { - "description": "Baidu Qianfan Smart Cloud APP Builder API Key", - "hint": "Reference: [https://console.bce.baidu.com/iam/#/iam/apikey/list](https://console.bce.baidu.com/iam/#/iam/apikey/list)" - }, - "web_search_link": { - "description": "Display Source Citations" - }, - "websearch_exa_key": { - "description": "Exa API Key", - "hint": "Multiple keys can be added for rotation. Get a key at https://dashboard.exa.ai" - } - } - }, - "file_extract": { - "description": "File Extract", - "provider_settings": { - "file_extract": { - "enable": { - "description": "Enable File Extract" - }, - "provider": { - "description": "File Extract Provider" - }, - "moonshotai_api_key": { - "description": "Moonshot AI API Key" - } - } - } - }, - "agent_computer_use": { - "description": "Agent Computer Use", - "hint": "Allows the AstrBot to access and use your computer or an sandbox environment to perform more complex tasks. See: [Computer Use](https://docs.astrbot.app/use/computer.html).", - "provider_settings": { - "computer_use_runtime": { - "description": "Computer Use Runtime", - "hint": "Environment allowed for Agent usage. `local` means the local machine environment, `sandbox` means the sandbox environment, and `none` means no environment is allowed." - }, - "computer_use_require_admin": { - "description": "Require AstrBot Admin Permission", - "hint": "When enabled, AstrBot admin permission is required to use computer capabilities. Admins can be added in Platform Config. Use the /sid command to view admin IDs." - }, - "sandbox": { - "booter": { - "description": "Sandbox Environment Driver" - }, - "shipyard_neo_endpoint": { - "description": "Shipyard Neo API Endpoint", - "hint": "Bay API address, default http://127.0.0.1:8114." - }, - "shipyard_neo_access_token": { - "description": "Shipyard Neo Access Token", - "hint": "Bay API Key (sk-bay-...). Leave empty for auto-discovery from credentials.json." - }, - "shipyard_neo_profile": { - "description": "Shipyard Neo Profile", - "hint": "Sandbox profile for Shipyard Neo, e.g. python-default. Leave empty to auto-select the most capable profile." - }, - "shipyard_neo_ttl": { - "description": "Shipyard Neo Sandbox TTL", - "hint": "Sandbox time-to-live in seconds." - }, - "cua_image": { - "description": "CUA Image", - "hint": "CUA sandbox image or OS type. Defaults to linux. Supported values depend on the installed CUA SDK." - }, - "cua_os_type": { - "description": "CUA OS Type", - "hint": "CUA sandbox operating system type. Defaults to linux." - }, - "cua_idle_timeout": { - "description": "CUA Idle Timeout", - "hint": "Idle timeout for CUA sandbox sessions in seconds. When greater than 0, AstrBot proactively shuts down an idle CUA sandbox after that amount of inactivity; 0 disables it." - }, - "cua_telemetry_enabled": { - "description": "CUA Telemetry", - "hint": "Allow the CUA SDK to send telemetry data. Disabled by default." - }, - "cua_local": { - "description": "CUA Local Sandbox", - "hint": "Prefer a local CUA sandbox. Enabled by default to avoid requiring CUA_API_KEY for cloud sandboxes. Disable this to use CUA cloud sandboxes." - }, - "cua_api_key": { - "description": "CUA API Key", - "hint": "CUA cloud sandbox API key. Required only when local sandbox is disabled. You can also provide it via the CUA_API_KEY environment variable." - }, - "shipyard_endpoint": { - "description": "Shipyard API Endpoint", - "hint": "API access address for Shipyard service." - }, - "shipyard_access_token": { - "description": "Shipyard Access Token", - "hint": "Access token for accessing Shipyard service." - }, - "shipyard_ttl": { - "description": "Shipyard Session TTL", - "hint": "Session time-to-live in seconds." - }, - "shipyard_max_sessions": { - "description": "Shipyard Max Sessions", - "hint": "Maximum number of Shipyard sessions an instance can handle." - } - } - } - }, - "proactive_capability": { - "description": "Proactive Agent", - "hint": "AstrBot will wake up, run your tasks, and deliver the results to you. See [Proactive Agent](https://docs.astrbot.app/en/use/proactive-agent.html)", - "provider_settings": { - "proactive_capability": { - "add_cron_tools": { - "description": "Enable", - "hint": "When enabled, related tools will be passed to the Agent to implement proactive Agent capabilities. You can tell AstrBot what to do at a future time, and it will be triggered on schedule to execute the task, and report the result back to you." - } - } - } - }, - "truncate_and_compress": { - "hint": "[Context Management](https://docs.astrbot.app/en/use/context-compress.html)", - "description": "Context Management Strategy", - "provider_settings": { - "max_context_length": { - "description": "Max Turns Before Compression", - "hint": "Persistent conversation history is truncated or LLM-compressed by the strategy below only after it exceeds this many turns. Request-time contexts are also constrained by this value before sending. -1 means no turn-based limit." - }, - "dequeue_context_length": { - "description": "Turns to Discard When Limit Exceeded", - "hint": "When history exceeds 'Max Turns Before Compression' and LLM compression is unavailable, discard this many oldest turns at once. Request-time truncation also reuses this value." - }, - "context_limit_reached_strategy": { - "description": "Handling for History Limits or Context Window Pressure", - "labels": [ - "Truncate by Turns", - "Compress by LLM" - ], - "hint": "Persistent conversation history uses this strategy only after exceeding 'Max Turns Before Compression'. Before each request, the same strategy may also protect the in-flight context when tokens approach the model window." - }, - "llm_compress_instruction": { - "description": "Context Compression Instruction", - "hint": "If empty, the default prompt will be used." - }, - "llm_compress_keep_recent_ratio": { - "description": "Recent Context Token Ratio to Keep", - "hint": "Keep recent exact context by current context token ratio, from 0-0.3. 0.15 means keeping 15%; values above 0 keep at least the latest round." - }, - "llm_compress_provider_id": { - "description": "Model Provider ID for Context Compression", - "hint": "When left empty, the current chat model will be used for compression. If the model is unavailable or compression fails, AstrBot falls back to the 'Truncate by Turns' strategy." - }, - "fallback_max_context_tokens": { - "description": "Fallback context window size", - "hint": "When max_context_tokens is 0 and the model is not in built-in metadata, use this value as the context window size. Default: 128000." - } - } - }, - "others": { - "description": "Other Settings", - "provider_settings": { - "display_reasoning_text": { - "description": "Display Reasoning Content" - }, - "llm_safety_mode": { - "description": "Healthy Mode", - "hint": "Add safety guardrails to model replies." - }, - "safety_mode_strategy": { - "description": "Healthy Mode Strategy", - "hint": "How to apply healthy mode." - }, - "identifier": { - "description": "User Identification", - "hint": "When enabled, user ID information will be included in the prompt." - }, - "group_name_display": { - "description": "Display Group Name", - "hint": "When enabled, group name information will be included in the prompt on supported platforms (OneBot v11)." - }, - "datetime_system_prompt": { - "description": "Real-world Time Awareness", - "hint": "When enabled, current time information will be appended to the system prompt." - }, - "show_tool_use_status": { - "description": "Output Function Call Status" - }, - "show_tool_call_result": { - "description": "Output Tool Call Results", - "hint": "Only takes effect when \"Output Function Call Status\" is enabled, and shows at most 70 characters." - }, - "buffer_intermediate_messages": { - "description": "Merge Agent Intermediate Messages", - "hint": "When enabled, intermediate text generated during multi-step tool calls in non-streaming mode will be buffered and sent as a single merged reply after the Agent finishes." - }, - "sanitize_context_by_modalities": { - "description": "Sanitize History by Modalities", - "hint": "When enabled, sanitizes contexts before each LLM request by removing image blocks and tool-call structures that the current provider's modalities do not support (this changes what the model sees)." - }, - "max_quoted_fallback_images": { - "description": "Forwarded Image Fetch Limit", - "hint": "Maximum number of images injected from forwarded-message parsing; extra images are truncated." - }, - "quoted_message_parser": { - "max_component_chain_depth": { - "description": "Forwarded Rich-Text Parse Depth", - "hint": "Maximum recursive depth when parsing rich-text component chains inside forwarded messages." - }, - "max_forward_node_depth": { - "description": "Forward Nesting Parse Depth", - "hint": "Maximum recursive depth when parsing nested forwarded nodes." - }, - "max_forward_fetch": { - "description": "Forward Recursive Fetch Limit", - "hint": "Maximum number of recursive get_forward_msg fetch operations." - }, - "warn_on_action_failure": { - "description": "Warn on Forward Parse Failure", - "hint": "When enabled, log warnings when all get_msg/get_forward_msg attempts fail." - } - }, - "max_agent_step": { - "description": "Maximum Tool Call Rounds" - }, - "tool_call_timeout": { - "description": "Tool Call Timeout (seconds)" - }, - "tool_schema_mode": { - "description": "Tool Schema Mode", - "hint": "Skills-like sends name/description first and re-queries for parameters; Full sends the complete schema in one step.", - "labels": [ - "Skills-like (two-stage)", - "Full schema" - ] - }, - "streaming_response": { - "description": "Streaming Output" - }, - "unsupported_streaming_strategy": { - "description": "Platforms Without Streaming Support", - "hint": "Select the handling method for platforms that don't support streaming responses. Real-time segmented reply sends content immediately when the system detects segment points like punctuation during streaming reception", - "labels": [ - "Real-time Segmented Reply", - "Disable Streaming Response" - ] - }, - "wake_prefix": { - "description": "Additional LLM Chat Wake Prefix", - "hint": "If the wake prefix is / and the additional chat wake prefix is chat, then /chat is required to trigger LLM requests" - }, - "prompt_prefix": { - "description": "User Prompt", - "hint": "You can use {{prompt}} as a placeholder for user input. If no placeholder is provided, it will be added before the user input." - }, - "image_compress_enabled": { - "description": "Enable image compression", - "hint": "When enabled, large local images are compressed before being sent to multimodal models." - }, - "image_compress_options": { - "description": "Image compression settings", - "hint": "Control image resize limits, JPEG quality, and the minimum size threshold for compression.", - "max_size": { - "description": "Maximum edge length", - "hint": "Longest edge of the compressed image in pixels. Images larger than this are resized proportionally." - }, - "quality": { - "description": "JPEG quality", - "hint": "JPEG output quality from 1 to 100. Higher values preserve more detail but produce larger files." - } - }, - "reachability_check": { - "description": "Provider Reachability Check", - "hint": "When running the /provider command, test provider connectivity in parallel. This actively pings models and may consume extra tokens." - } - }, - "provider_tts_settings": { - "dual_output": { - "description": "Output Both Voice and Text When TTS is Enabled" - } - } - } - }, - "platform_group": { - "name": "Platform", - "platform": { - "description": "Message Platform Adapters", - "active_send_mode": { - "description": "Use Proactive Send API" - }, - "appid": { - "description": "App ID", - "hint": "Required. App ID for the current messaging platform. See the platform integration docs for how to obtain it." - }, - "callback_server_host": { - "description": "Callback Server Host", - "hint": "Callback server host. Leave empty to disable the callback server." - }, - "card_template_id": { - "description": "Card Template ID", - "hint": "Optional. DingTalk interactive card template ID. When enabled, streaming replies will use interactive cards." - }, - "discord_activity_name": { - "description": "Discord Activity Name", - "hint": "Optional Discord activity name. Leave empty to disable." - }, - "discord_allow_bot_messages": { - "description": "Allow Bot Messages", - "hint": "When enabled, AstrBot will receive messages from other Discord bots. Useful for bot-to-bot communication scenarios (e.g., message forwarding). Disabled by default." - }, - "discord_command_register": { - "description": "Register Discord slash commands", - "hint": "When enabled, AstrBot will automatically register plugin commands as Discord slash commands" - }, - "discord_proxy": { - "description": "Discord Proxy URL", - "hint": "Optional proxy URL: http://ip:port" - }, - "discord_token": { - "description": "Discord Bot Token", - "hint": "Enter your Discord Bot Token here." - }, - "enable": { - "description": "Enable", - "hint": "Whether to enable this adapter. Disabled adapters will not receive messages." - }, - "enable_group_c2c": { - "description": "Enable Message List Private Chat", - "hint": "When enabled, the bot can receive private chats from QQ message list. You may need to add the bot as a friend by scanning a QR code in the QQ bot platform. See docs." - }, - "enable_guild_direct_message": { - "description": "Enable Guild Direct Messages", - "hint": "When enabled, the bot can receive guild direct messages." - }, - "id": { - "description": "Bot Name", - "hint": "Bot name" - }, - "is_sandbox": { - "description": "Sandbox Mode" - }, - "kf_name": { - "description": "WeChat Customer Service Account Name", - "hint": "Optional. Customer service account name (not ID). Get it at https://kf.weixin.qq.com/kf/frame#/accounts" - }, - "lark_connection_mode": { - "description": "Subscription Mode", - "labels": [ - "Long Connection Mode", - "Webhook Server Mode" - ] - }, - "lark_encrypt_key": { - "description": "Encrypt Key", - "hint": "Encryption key for decrypting Lark callback data." - }, - "lark_verification_token": { - "description": "Verification Token", - "hint": "Token for verifying Lark callback requests." - }, - "misskey_allow_insecure_downloads": { - "description": "Allow Insecure Downloads (Disable SSL Verification)", - "hint": "If remote servers have certificate issues, SSL verification will be disabled as a fallback. Use only when necessary due to security risks." - }, - "misskey_default_visibility": { - "description": "Default Post Visibility", - "hint": "Default visibility for bot posts. public: public, home: home timeline, followers: followers only." - }, - "misskey_download_chunk_size": { - "description": "Stream Download Chunk Size (bytes)", - "hint": "Bytes read per chunk during streaming download and MD5 calculation. Too small increases overhead; too large uses more memory." - }, - "misskey_download_timeout": { - "description": "Remote Download Timeout (seconds)", - "hint": "Timeout for downloading remote files (seconds), used when falling back to local upload." - }, - "misskey_enable_chat": { - "description": "Enable Chat Message Responses", - "hint": "When enabled, the bot listens and responds to private chat messages." - }, - "misskey_enable_file_upload": { - "description": "Enable File Upload to Misskey", - "hint": "When enabled, the adapter uploads files in message chains to Misskey. URL files try server-side upload first; if async upload fails, it falls back to local download and upload." - }, - "misskey_instance_url": { - "description": "Misskey Instance URL", - "hint": "e.g. https://misskey.example. The Misskey instance where the bot account lives." - }, - "misskey_local_only": { - "description": "Local Only (No Federation)", - "hint": "When enabled, bot posts are visible only on this instance and are not federated." - }, - "misskey_max_download_bytes": { - "description": "Max Download Size (bytes)", - "hint": "To limit download size to prevent OOM, set the maximum bytes; empty or null means no limit." - }, - "misskey_token": { - "description": "Misskey Access Token", - "hint": "API access token generated in the connection service settings." - }, - "misskey_upload_concurrency": { - "description": "Upload Concurrency Limit", - "hint": "Max number of concurrent upload tasks (integer, default 3)." - }, - "misskey_upload_folder": { - "description": "Target Drive Folder ID", - "hint": "Optional: ID of the target folder in Misskey drive. Leave empty to use the root folder." - }, - "port": { - "description": "Callback Server Port", - "hint": "Callback server port. Leave empty to disable the callback server." - }, - "satori_api_base_url": { - "description": "Satori API Endpoint", - "hint": "Base URL for the Satori API." - }, - "satori_auto_reconnect": { - "description": "Enable Auto Reconnect", - "hint": "Automatically reconnect the WebSocket when disconnected." - }, - "satori_endpoint": { - "description": "Satori WebSocket Endpoint", - "hint": "WebSocket endpoint for Satori events." - }, - "satori_heartbeat_interval": { - "description": "Satori Heartbeat Interval", - "hint": "Interval in seconds between heartbeat messages." - }, - "satori_reconnect_delay": { - "description": "Satori Reconnect Delay", - "hint": "Delay before attempting to reconnect (seconds)." - }, - "satori_token": { - "description": "Satori Token", - "hint": "Token for Satori API authentication." - }, - "secret": { - "description": "Secret", - "hint": "Required." - }, - "slack_connection_mode": { - "description": "Slack Connection Mode", - "hint": "The connection mode for Slack. `webhook` uses a webhook server, `socket` uses Slack's Socket Mode." - }, - "slack_webhook_host": { - "description": "Slack Webhook Host", - "hint": "Only valid when Slack connection mode is `webhook`." - }, - "slack_webhook_path": { - "description": "Slack Webhook Path", - "hint": "Only valid when Slack connection mode is `webhook`." - }, - "slack_webhook_port": { - "description": "Slack Webhook Port", - "hint": "Only valid when Slack connection mode is `webhook`." - }, - "telegram_command_auto_refresh": { - "description": "Telegram Command Auto Refresh", - "hint": "When enabled, AstrBot automatically refreshes Telegram commands at runtime. (Setting this alone has no effect)" - }, - "telegram_command_register": { - "description": "Telegram Command Registration", - "hint": "When enabled, AstrBot automatically registers Telegram commands." - }, - "telegram_command_register_interval": { - "description": "Telegram Command Auto Refresh Interval", - "hint": "Telegram command auto-refresh interval in seconds." - }, - "telegram_polling_restart_delay": { - "description": "Telegram Polling Restart Delay", - "hint": "Waiting time in seconds when the polling loop needs to restart after unexpected exits. Defaults to 5s." - }, - "telegram_token": { - "description": "Bot Token", - "hint": "If you are in mainland China, set a proxy or change api_base in Other Settings." - }, - "mattermost_url": { - "description": "Mattermost URL", - "hint": "Mattermost service URL, for example https://chat.example.com." - }, - "mattermost_bot_token": { - "description": "Mattermost Bot Token", - "hint": "The access token generated after creating a bot account in Mattermost." - }, - "mattermost_reconnect_delay": { - "description": "Mattermost Reconnect Delay", - "hint": "Delay in seconds before reconnecting after the WebSocket disconnects. Defaults to 5 seconds." - }, - "type": { - "description": "Adapter Type" - }, - "unified_webhook_mode": { - "description": "Unified Webhook Mode", - "hint": "When enabled, use AstrBot unified webhook entry without opening a separate port. Callback URL is /api/v1/webhooks/platforms/{webhook_uuid}." - }, - "webhook_uuid": { - "description": "Webhook UUID", - "hint": "Unique identifier for unified webhook mode; generated when creating the platform." - }, - "wecom_ai_bot_name": { - "description": "WeCom AI Bot Name", - "hint": "Must be correct; otherwise some commands won't work." - }, - "wecom_ai_bot_connection_mode": { - "description": "WeCom AI Bot Connection Mode", - "hint": "Webhook mode requires Token/EncodingAESKey; long_connection mode requires BotID/Secret." - }, - "wecomaibot_friend_message_welcome_text": { - "description": "WeCom AI Bot DM Welcome Message", - "hint": "When a user enters a DM session on that day, reply with a welcome message. Leave empty to disable." - }, - "wecomaibot_init_respond_text": { - "description": "WeCom AI Bot Initial Response Text", - "hint": "First reply when the bot receives a message. Leave empty to disable." - }, - "wecomaibot_token": { - "description": "WeCom AI Bot Token", - "hint": "Used for authentication in webhook callback mode." - }, - "wecomaibot_encoding_aes_key": { - "description": "WeCom AI Bot EncodingAESKey", - "hint": "Used for message encryption/decryption in webhook callback mode." - }, - "wecomaibot_ws_bot_id": { - "description": "Long Connection BotID", - "hint": "BotID credential for WeCom AI Bot long connection mode." - }, - "wecomaibot_ws_secret": { - "description": "Long Connection Secret", - "hint": "Secret credential for WeCom AI Bot long connection mode." - }, - "wecomaibot_ws_url": { - "description": "Long Connection WebSocket URL", - "hint": "Default is wss://openws.work.weixin.qq.com and usually does not need changes." - }, - "wecomaibot_heartbeat_interval": { - "description": "Long Connection Heartbeat Interval", - "hint": "Heartbeat interval (seconds) in long connection mode. 30 seconds is recommended." - }, - "wpp_active_message_poll": { - "description": "Enable Proactive Message Polling", - "hint": "Only enable if WeChat messages are not syncing to AstrBot on time. Disabled by default." - }, - "wpp_active_message_poll_interval": { - "description": "Proactive Message Poll Interval", - "hint": "Interval in seconds, default 3, should not exceed 60 or it may be considered old messages." - }, - "ws_reverse_host": { - "description": "Reverse WebSocket Host", - "hint": "AstrBot acts as the server." - }, - "ws_reverse_port": { - "description": "Reverse WebSocket Port" - }, - "ws_reverse_token": { - "description": "Reverse WebSocket Token", - "hint": "Reverse WebSocket token. If not set, token verification is disabled." - }, - "msg_push_webhook_url": { - "description": "WeCom Message Push Webhook URL", - "hint": "Used for proactive message push. It is strongly recommended to set this for a better message sending experience." - }, - "only_use_webhook_url_to_send": { - "description": "Send Replies via Webhook Only", - "hint": "When enabled, all WeCom AI Bot replies are sent through msg_push_webhook_url. The message push webhook supports more message types (such as images, files, etc.). If you do not need the typing effect, it is strongly recommended to use this option. " - }, - "weixin_oc_base_url": { - "description": "API Base URL", - "hint": "Default: https://ilinkai.weixin.qq.com" - }, - "weixin_oc_bot_type": { - "description": "bot_type (QR login parameter)", - "hint": "Default: 3" - }, - "weixin_oc_qr_poll_interval": { - "description": "QR status poll interval (seconds)", - "hint": "Polling interval in seconds for QR code status." - }, - "weixin_oc_long_poll_timeout_ms": { - "description": "getUpdates long-poll timeout (ms)", - "hint": "Timeout parameter for polling messages." - }, - "weixin_oc_api_timeout_ms": { - "description": "HTTP timeout (ms)", - "hint": "Generic API request timeout." - }, - "weixin_oc_token": { - "description": "Token after login (optional)", - "hint": "Automatically written after QR login; can be filled manually for advanced scenarios." - }, - "kook_bot_token": { - "description": "Bot Token", - "type": "string", - "hint": "Required. The Bot Token obtained from the KOOK Developer Platform." - }, - "kook_reconnect_delay": { - "description": "Reconnect Delay", - "type": "int", - "hint": "Delay time for reconnection (seconds), using an exponential backoff strategy." - }, - "kook_max_reconnect_delay": { - "description": "Max Reconnect Delay", - "type": "int", - "hint": "The maximum value for reconnection delay (seconds)." - }, - "kook_max_retry_delay": { - "description": "Max Retry Delay", - "type": "int", - "hint": "The maximum delay time for retries (seconds)." - }, - "kook_heartbeat_interval": { - "description": "Heartbeat Interval", - "type": "int", - "hint": "The interval time for heartbeat detection (seconds)." - }, - "kook_heartbeat_timeout": { - "description": "Heartbeat Timeout", - "type": "int", - "hint": "The timeout duration for heartbeat detection (seconds)." - }, - "kook_max_heartbeat_failures": { - "description": "Max Heartbeat Failures", - "type": "int", - "hint": "Maximum allowed heartbeat failures; the connection will be dropped if exceeded." - }, - "kook_max_consecutive_failures": { - "description": "Max Consecutive Failures", - "type": "int", - "hint": "Maximum allowed consecutive failures; retries will stop if exceeded." - } - }, - "general": { - "description": "General", - "admins_id": { - "description": "Administrator IDs" - }, - "platform_settings": { - "unique_session": { - "description": "Isolate Sessions", - "hint": "When enabled, group members have independent contexts." - }, - "friend_message_needs_wake_prefix": { - "description": "Private Messages Require Wake Word" - }, - "reply_prefix": { - "description": "Reply Text Prefix" - }, - "reply_with_mention": { - "description": "Mention Sender in Reply" - }, - "reply_with_quote": { - "description": "Quote Sender's Message in Reply" - }, - "forward_threshold": { - "description": "Forward Message Word Count Threshold" - }, - "empty_mention_waiting": { - "description": "Trigger Waiting on Mention-only Messages" - } - }, - "wake_prefix": { - "description": "Wake Word" - }, - "disable_builtin_commands": { - "description": "Disable Built-in Commands", - "hint": "Disable all built-in AstrBot commands such as help, sid, new, etc." - } - }, - "whitelist": { - "description": "Whitelist", - "platform_settings": { - "enable_id_white_list": { - "description": "Enable Whitelist", - "hint": "When enabled, only sessions in the whitelist will be responded to. If the whitelist is empty, the whitelist is disabled and all IDs are allowed." - }, - "id_whitelist": { - "description": "Whitelist ID List", - "hint": "Use /sid to get IDs. If the list is empty, it means whitelist is disabled (all IDs are in the whitelist)." - }, - "id_whitelist_log": { - "description": "Output Logs", - "hint": "When enabled, INFO level logs will be output when a message doesn't pass the whitelist." - }, - "wl_ignore_admin_on_group": { - "description": "Administrator Group Messages Bypass ID Whitelist" - }, - "wl_ignore_admin_on_friend": { - "description": "Administrator Private Messages Bypass ID Whitelist" - } - } - }, - "rate_limit": { - "description": "Rate Limiting", - "platform_settings": { - "rate_limit": { - "time": { - "description": "Message Rate Limit Time (seconds)" - }, - "count": { - "description": "Message Rate Limit Count" - }, - "strategy": { - "description": "Rate Limit Strategy" - } - } - } - }, - "content_safety": { - "description": "Content Safety", - "content_safety": { - "also_use_in_response": { - "description": "Also Check Model Response Content" - }, - "baidu_aip": { - "enable": { - "description": "Use Baidu Content Safety Moderation", - "hint": "You need to manually install the baidu-aip library." - }, - "app_id": { - "description": "App ID" - }, - "api_key": { - "description": "API Key" - }, - "secret_key": { - "description": "Secret Key" - } - }, - "internal_keywords": { - "enable": { - "description": "Keyword Check" - }, - "extra_keywords": { - "description": "Additional Keywords", - "hint": "Additional keyword blocklist, supports regular expressions." - } - } - } - }, - "t2i": { - "description": "Text-to-Image", - "t2i": { - "description": "Text-to-Image Output" - }, - "t2i_word_threshold": { - "description": "Text-to-Image Word Count Threshold" - } - }, - "others": { - "description": "Other Settings", - "platform_settings": { - "ignore_bot_self_message": { - "description": "Ignore Bot's Own Messages" - }, - "ignore_at_all": { - "description": "Ignore @All Events" - }, - "no_permission_reply": { - "description": "Reply When User Has Insufficient Permissions" - } - }, - "platform_specific": { - "lark": { - "pre_ack_emoji": { - "enable": { - "description": "[Lark] Enable Pre-acknowledgment Emoji" - }, - "emojis": { - "description": "Emoji List (Lark Emoji Enum Names)", - "hint": "Emoji enum names reference: [https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce](https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce)" - } - } - }, - "telegram": { - "pre_ack_emoji": { - "enable": { - "description": "[Telegram] Enable Pre-acknowledgment Emoji" - }, - "emojis": { - "description": "Emoji List (Unicode)", - "hint": "Telegram only supports a fixed reaction set, reference: [https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9)" - } - } - }, - "discord": { - "pre_ack_emoji": { - "enable": { - "description": "[Discord] Enable Pre-acknowledgment Emoji" - }, - "emojis": { - "description": "Emoji List (Unicode or Custom Emoji Name)", - "hint": "Enter Unicode emoji symbols, e.g., 👍, 🤔, ⏳" - } - } - } - } - } - }, - "plugin_group": { - "name": "Plugin", - "plugin": { - "description": "Plugins", - "plugin_set": { - "description": "Available Plugins", - "hint": "All non-disabled plugins are enabled by default. If a plugin is disabled on the plugins page, selections here will not take effect." - } - } - }, - "ext_group": { - "name": "Ext.", - "segmented_reply": { - "description": "Segmented Reply", - "platform_settings": { - "segmented_reply": { - "enable": { - "description": "Enable Segmented Reply" - }, - "only_llm_result": { - "description": "Segment Only LLM Results" - }, - "interval_method": { - "description": "Interval Method", - "hint": "random uses a random delay. log calculates delay by message length: $y=log_{log\\_base}(x)$, where x is word count and y is in seconds." - }, - "interval": { - "description": "Random Interval Time", - "hint": "Format: minimum,maximum (e.g., 1.5,3.5)" - }, - "log_base": { - "description": "Logarithm Base", - "hint": "Base for logarithmic intervals, defaults to 2.6. Value range: 1.0-10.0." - }, - "words_count_threshold": { - "description": "Segmented Reply Word Count Threshold", - "hint": "Segmented reply word count threshold. Only messages with less than this number of words will be segmented, and messages with more than this number of words will be sent directly (not segmented)." - }, - "split_mode": { - "description": "Split Mode", - "hint": "Used to segment a message. By default, it will be separated by punctuation marks like period, question mark, etc. For example, filling `[。?!]` will remove all periods, question marks, and exclamation marks. re.findall(r'', text)", - "labels": [ - "Regex", - "Words List" - ] - }, - "regex": { - "description": "Segmentation Regular Expression", - "hint": "Used to identify split points with a regular expression. Prefer patterns that match separators." - }, - "split_words": { - "description": "Split Word List", - "hint": "Split when any word in the list is detected" - }, - "content_cleanup_rule": { - "description": "Content Filtering Regular Expression", - "hint": "Remove specified content from segmented content. For example, `[。?!]` will remove all periods, question marks, and exclamation marks." - } - } - } - }, - "ltm": { - "description": "Group Chat Context Awareness (formerly Chat Memory Enhancement)", - "provider_ltm_settings": { - "group_icl_enable": { - "description": "Enable Group Chat Context Awareness" - }, - "group_message_max_cnt": { - "description": "Maximum Message Count" - }, - "image_caption": { - "description": "Auto-understand Images", - "hint": "Requires setting a group chat image caption model." - }, - "image_caption_provider_id": { - "description": "Group Chat Image Caption Model", - "hint": "Used for image understanding in group chat context awareness, configured separately from the default image caption model." - }, - "active_reply": { - "enable": { - "description": "Active Reply" - }, - "method": { - "description": "Active Reply Method" - }, - "possibility_reply": { - "description": "Reply Probability", - "hint": "Value between 0.0-1.0" - }, - "whitelist": { - "description": "Active Reply Whitelist", - "hint": "Whitelist filtering is disabled when empty. Use /sid to get IDs." - } - } - } - } - }, - "system_group": { - "name": "System", - "system": { - "description": "System Settings", - "t2i_strategy": { - "description": "Text-to-Image Strategy", - "hint": "Text-to-image strategy. `remote` uses a remote HTML-based rendering service, `local` uses PIL for local rendering. When using local, place a TTF font named 'font.ttf' in the data/ directory to customize the font." - }, - "t2i_endpoint": { - "description": "Text-to-Image Service API Endpoint", - "hint": "Uses AstrBot API service when empty" - }, - "t2i_template": { - "description": "Text-to-Image Custom Template", - "hint": "When enabled, you can customize HTML templates for text-to-image rendering." - }, - "t2i_active_template": { - "description": "Currently Active Text-to-Image Rendering Template", - "hint": "This value is maintained by the text-to-image template management page." - }, - "log_level": { - "description": "Console Log Level", - "hint": "Log level for console output." - }, - "log_file_enable": { - "description": "Enable File Logging", - "hint": "Write logs to a file in addition to the console." - }, - "log_file_path": { - "description": "Log File Path", - "hint": "Relative paths are resolved under the data directory, e.g. logs/astrbot.log; absolute paths are supported." - }, - "log_file_max_mb": { - "description": "Log File Max Size (MB)", - "hint": "Rotate when exceeding this size; default 20MB." - }, - "temp_dir_max_size": { - "description": "Temp Directory Size Limit (MB)", - "hint": "Limits total size of data/temp in MB. The system checks every 10 minutes, and when exceeded, deletes oldest files first to release about 30% of current size." - }, - "trace_log_enable": { - "description": "Enable Trace File Logging", - "hint": "Write trace events to a separate file (does not change console output)." - }, - "trace_log_path": { - "description": "Trace Log File Path", - "hint": "Relative paths are resolved under the data directory, e.g. logs/astrbot.trace.log; absolute paths are supported." - }, - "trace_log_max_mb": { - "description": "Trace Log Max Size (MB)", - "hint": "Rotate when exceeding this size; default 20MB." - }, - "pip_install_arg": { - "description": "Additional pip Installation Arguments", - "hint": "When installing plugin dependencies, Python's pip tool will be used. Additional arguments can be provided here, such as `--break-system-package`." - }, - "pypi_index_url": { - "description": "PyPI Repository URL", - "hint": "PyPI repository URL for installing Python dependencies. Defaults to [https://mirrors.aliyun.com/pypi/simple/](https://mirrors.aliyun.com/pypi/simple/)" - }, - "callback_api_base": { - "description": "Externally Accessible Callback API Address", - "hint": "External services may access AstrBot's backend through callback links generated by AstrBot (such as file download links). Since AstrBot cannot automatically determine the externally accessible host address in the deployment environment, this configuration item is needed to explicitly specify how external services should access AstrBot's address. Examples: [http://localhost:6185](http://localhost:6185), [https://example.com](https://example.com), etc." - }, - "disable_metrics": { - "description": "Disable Anonymous Usage Statistics", - "hint": "When disabled, AstrBot will not upload anonymous usage statistics." - }, - "dashboard": { - "trust_proxy_headers": { - "description": "Trust Proxy Headers for Client IP", - "hint": "When disabled, ignore X-Forwarded-For/X-Real-IP and use the connection address only." - }, - "auth_rate_limit": { - "enable": { - "description": "Enable Login Rate Limiting", - "hint": "When disabled, authentication endpoints (login, TOTP, etc.) will not be rate-limited." - }, - "average_interval": { - "description": "Endpoint Rate Limit Average Interval (seconds)", - "hint": "Minimum average interval between authentication requests. For example, 1.0 means at most 1 request per second." - }, - "max_burst": { - "description": "Endpoint Rate Limit Max Burst", - "hint": "Maximum number of consecutive burst requests allowed. For example, 3 allows up to 3 requests in a short burst." - } - }, - "ssl": { - "enable": { - "description": "Enable WebUI HTTPS", - "hint": "When enabled, WebUI serves directly over HTTPS." - }, - "cert_file": { - "description": "SSL Certificate File Path", - "hint": "Certificate file path (PEM). Supports absolute and relative paths (relative to current working directory)." - }, - "key_file": { - "description": "SSL Private Key File Path", - "hint": "Private key file path (PEM). Supports absolute and relative paths (relative to current working directory)." - }, - "ca_certs": { - "description": "SSL CA Certificate File Path", - "hint": "Optional. Path to CA certificate file." - } - }, - "totp": { - "enable": { - "description": "Enable WebUI TOTP", - "hint": "When enabled, a TOTP code is required during dashboard login." - }, - "manage": "Manage", - "configuration": "TOTP", - "statusPending": "Setup required", - "statusEnabled": "Enabled", - "setupRequiredHint": "TOTP is enabled but not yet configured. Click Manage to complete setup.", - "setupTitle": "Set up TOTP", - "setupSubtitle": "Scan this QR code in your authenticator app, then enter a verification code.", - "setupConfirm": "Verify and continue", - "activeSubtitle": "Use this QR code or secret to add another authenticator device.", - "rotateTitle": "Rotate TOTP Secret", - "rotateSubtitle": "Generate a new secret, then enter a code from your authenticator to confirm the replacement.", - "rotate": "Rotate", - "rotateRecovery": "Rotate Recovery Code", - "rotateConfirm": "Confirm Rotation", - "rotateCancel": "Cancel", - "rotateCode": "Verification Code", - "rotateCodeHint": "Enter the code from your authenticator app to confirm the new key.", - "rotateError": "Invalid code, please try again.", - "recoveryTitle": "Recovery Codes", - "recoverySubtitle": "This recovery code is shown once. Save it before continuing.", - "recoveryWarning": "If lost, account access cannot be restored through normal means.", - "recoveryAcknowledge": "I have saved my recovery codes", - "recoveryClose": "Done", - "disableTitle": "Disable TOTP", - "disableSubtitle": "Enter a verification code to disable two-factor authentication.", - "disableRecoverySubtitle": "Enter a recovery code to disable two-factor authentication.", - "disableCode": "Verification Code", - "disableRecoveryCode": "Recovery Code", - "disableConfirm": "Disable", - "disableCancel": "Cancel", - "disableError": "Verification failed. Please try again.", - "disableUseRecovery": "Can't use TOTP?", - "disableUseCode": "Use verification code", - "configSaveTitle": "Two-Factor Verification", - "configSaveSubtitle": "Enter a verification code to change protected configuration.", - "configSaveRotationHint": "When rotating TOTP keys, both the old and new verification codes are accepted (allowed once per rotation operation).", - "configSaveCode": "Verification Code", - "configSaveConfirm": "Continue", - "configSaveCancel": "Cancel", - "configSaveError": "Verification failed. Please try again." - } - }, - "timezone": { - "description": "Timezone", - "hint": "Timezone setting. Please enter an IANA timezone name, such as Asia/Shanghai. Uses system default timezone when empty. For all timezones, see: [https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab](https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab)" - }, - "http_proxy": { - "description": "HTTP Proxy", - "hint": "When enabled, proxy will be set by adding environment variables. Format: `http://ip:port`" - }, - "no_proxy": { - "description": "Direct Connection Address List" - } - } - }, - "provider_group": { - "provider": { - "genie_onnx_model_dir": { - "description": "ONNX Model Directory", - "hint": "The directory path containing the ONNX model files" - }, - "genie_language": { - "description": "Language" - }, - "xai_native_search": { - "description": "Enable native search", - "hint": "When enabled, uses xAI Chat Completions native Live Search for web queries (billed on demand). Only applies to xAI providers." - }, - "rerank_api_base": { - "description": "Rerank Model API Base URL", - "hint": "The full request URL is formed by combining the Base URL and a path suffix (defaults to /v1/rerank)." - }, - "rerank_api_suffix": { - "description": "API URL path suffix", - "hint": "Path appended to base_url, e.g. /v1/rerank. Leave empty to disable auto-append." - }, - "rerank_api_key": { - "description": "API Key", - "hint": "Leave empty if no API key is required." - }, - "rerank_model": { - "description": "Rerank model name" - }, - "return_documents": { - "description": "Return source documents in rerank results", - "hint": "Default is false to reduce network overhead." - }, - "instruct": { - "description": "Custom rerank task description", - "hint": "Only effective for qwen3-rerank models. Recommended to write in English." - }, - "nvidia_rerank_api_base": { - "description": "API Base URL" - }, - "nvidia_rerank_api_key": { - "description": "API Key" - }, - "nvidia_rerank_model": { - "description": "Rerank Model Name", - "hint": "Please refer to the NVIDIA Docs for the model name." - }, - "nvidia_rerank_model_endpoint": { - "description": "Custom Model Endpoint", - "hint": "Custom URL suffix endpoint, defaults to /reranking." - }, - "nvidia_rerank_truncate": { - "description": "Text Truncation Strategy", - "hint": "Whether to truncate the input to fit the model's maximum context length when the input text is too long." - }, - "launch_model_if_not_running": { - "description": "Auto-start model if not running", - "hint": "If the model is not running in Xinference, attempt to start it automatically. Recommended to disable in production." - }, - "modalities": { - "description": "Model capabilities", - "hint": "Modalities supported by the model. If the model does not support images, uncheck image.", - "labels": [ - "Text", - "Image", - "Audio", - "Tool use" - ] - }, - "custom_headers": { - "description": "Custom request headers", - "hint": "Key/value pairs added here are merged into the OpenAI SDK default_headers for custom HTTP headers. Values must be strings." - }, - "ollama_disable_thinking": { - "description": "Disable thinking mode", - "hint": "Close Ollama thinking mode." - }, - "custom_extra_body": { - "description": "Custom request body parameters", - "hint": "Add extra parameters to requests, such as temperature, top_p, max_tokens, etc.", - "template_schema": { - "temperature": { - "description": "Temperature", - "hint": "Controls randomness, typically 0-2. Higher is more random.", - "name": "Temperature" - }, - "top_p": { - "description": "Top-p sampling", - "hint": "Nucleus sampling parameter, usually 0-1. Controls probability mass considered.", - "name": "Top-p" - }, - "max_tokens": { - "description": "Max tokens", - "hint": "Maximum number of generated tokens.", - "name": "Max Tokens" - } - } - }, - "gpt_weights_path": { - "description": "GPT model file path", - "hint": "The .ckpt file. Use an absolute path without quotes. Leave empty to use the GPT_SoVITS built-in SoVITS model (recommended to change defaults in GPT_SoVITS)." - }, - "sovits_weights_path": { - "description": "SoVITS model file path", - "hint": "The .pth file. Use an absolute path without quotes. Leave empty to use the GPT_SoVITS built-in SoVITS model (recommended to change defaults in GPT_SoVITS)." - }, - "gsv_default_parms": { - "description": "GPT_SoVITS default parameters", - "hint": "Reference audio file path and text are required; other parameters are optional.", - "gsv_ref_audio_path": { - "description": "Reference audio file path", - "hint": "Required! Use an absolute path without quotes." - }, - "gsv_prompt_text": { - "description": "Reference audio text", - "hint": "Required! Provide the transcript of the reference audio." - }, - "gsv_prompt_lang": { - "description": "Reference audio text language", - "hint": "Language of the reference audio text; default is Chinese." - }, - "gsv_aux_ref_audio_paths": { - "description": "Auxiliary reference audio file paths", - "hint": "Auxiliary reference audio files; optional." - }, - "gsv_text_lang": { - "description": "Text language", - "hint": "Default is Chinese." - }, - "gsv_top_k": { - "description": "Speech diversity", - "hint": "" - }, - "gsv_top_p": { - "description": "Nucleus sampling threshold", - "hint": "" - }, - "gsv_temperature": { - "description": "Speech randomness", - "hint": "" - }, - "gsv_text_split_method": { - "description": "Text splitting method", - "hint": "Options: `cut0` no split, `cut1` split every 4 sentences, `cut2` split every 50 chars, `cut3` split by Chinese period, `cut4` split by English period, `cut5` split by punctuation." - }, - "gsv_batch_size": { - "description": "Batch size", - "hint": "" - }, - "gsv_batch_threshold": { - "description": "Batch threshold", - "hint": "" - }, - "gsv_split_bucket": { - "description": "Split text into buckets for parallel processing", - "hint": "" - }, - "gsv_speed_factor": { - "description": "Speech playback speed", - "hint": "1 is the original speed." - }, - "gsv_fragment_interval": { - "description": "Interval between speech segments", - "hint": "" - }, - "gsv_streaming_mode": { - "description": "Enable streaming mode", - "hint": "" - }, - "gsv_seed": { - "description": "Random seed", - "hint": "For reproducible results." - }, - "gsv_parallel_infer": { - "description": "Run inference in parallel", - "hint": "" - }, - "gsv_repetition_penalty": { - "description": "Repetition penalty", - "hint": "" - }, - "gsv_media_type": { - "description": "Output media type", - "hint": "Recommended: wav" - } - }, - "embedding_dimensions": { - "description": "Embedding dimensions", - "hint": "Embedding vector dimensions. May need adjustment per model; see model documentation. This must be correct or the vector database will not work." - }, - "embedding_model": { - "description": "Embedding model", - "hint": "Embedding model name." - }, - "embedding_api_key": { - "description": "API Key" - }, - "embedding_api_base": { - "description": "API Base URL" - }, - "openai_embedding": { - "hint": "If testing fails, try adding /v1 at the end for some OpenAI API versions." - }, - "gemini_embedding": { - "hint": "Gemini Embedding does not require manually adding /v1beta." - }, - "volcengine_cluster": { - "description": "Volcengine cluster", - "hint": "For voice cloning models, choose volcano_icl or volcano_icl_concurr; default is volcano_tts." - }, - "volcengine_voice_type": { - "description": "Volcengine voice", - "hint": "Enter voice id (Voice_type)." - }, - "volcengine_speed_ratio": { - "description": "Speech rate", - "hint": "Speech rate, range 0.2 to 3.0, default 1.0." - }, - "volcengine_volume_ratio": { - "description": "Volume", - "hint": "Volume, range 0.0 to 2.0, default 1.0." - }, - "azure_tts_voice": { - "description": "Voice style", - "hint": "API voice name" - }, - "azure_tts_style": { - "description": "Style", - "hint": "A voice-specific speaking style. Can express emotions like happy, sympathetic, and calm." - }, - "azure_tts_role": { - "description": "Role (optional)", - "hint": "Speaking role-play. The voice can emulate different ages and genders without changing the voice name. For example, a male voice can raise pitch to simulate a female voice, but the voice name does not change. If the role is missing or unsupported, this attribute is ignored." - }, - "azure_tts_rate": { - "description": "Speech rate", - "hint": "Controls speaking rate. You can apply the rate at word or sentence level. Rate should be 0.5x to 2x of original audio." - }, - "azure_tts_volume": { - "description": "Speech volume", - "hint": "Controls volume level. You can apply changes at sentence level. Use 0.0 to 100.0 (quiet to loud, e.g., 75). Default is 100.0." - }, - "azure_tts_region": { - "description": "API region", - "hint": "Region where Azure TTS processes data. See https://learn.microsoft.com/zh-cn/azure/ai-services/speech-service/regions" - }, - "azure_tts_subscription_key": { - "description": "Service subscription key", - "hint": "Azure TTS subscription key (not a token)." - }, - "dashscope_tts_voice": { - "description": "Voice" - }, - "gm_resp_image_modal": { - "description": "Enable image modality", - "hint": "When enabled, responses can include images. Requires model support or it will error. See the Google Gemini website for supported models. Tip: if you need image generation, disable the `Enable member recognition` setting for better results." - }, - "gm_native_search": { - "description": "Enable native search", - "hint": "When enabled, all function tools are disabled. Check official docs for free quota limits." - }, - "gm_native_coderunner": { - "description": "Enable native code runner", - "hint": "When enabled, all function tools are disabled." - }, - "gm_url_context": { - "description": "Enable URL context", - "hint": "When enabled, all function tools are disabled." - }, - "gm_safety_settings": { - "description": "Safety filters", - "hint": "Set the safety filtering level for model input. Levels: NONE (no blocking), HIGH (block high risk), MEDIUM_AND_ABOVE (block medium risk and above), LOW_AND_ABOVE (block low risk and above). See Gemini API docs.", - "harassment": { - "description": "Harassment", - "hint": "Negative or harmful comments" - }, - "hate_speech": { - "description": "Hate speech", - "hint": "Rude, disrespectful, or profane content" - }, - "sexually_explicit": { - "description": "Sexually explicit content", - "hint": "References to sexual acts or other obscene content" - }, - "dangerous_content": { - "description": "Dangerous content", - "hint": "Content that promotes, encourages, or assists harmful behavior" - } - }, - "gm_thinking_config": { - "description": "Thinking Config", - "budget": { - "description": "Thinking Budget", - "hint": "Guides the model on the specific number of thinking tokens to use for reasoning. See: https://ai.google.dev/gemini-api/docs/thinking#set-budget" - }, - "level": { - "description": "Thinking Level", - "hint": "Recommended for Gemini 3 models and onwards, lets you control reasoning behavior.See: https://ai.google.dev/gemini-api/docs/thinking#thinking-levels" - } - }, - "anth_thinking_config": { - "description": "Thinking Config", - "type": { - "description": "Thinking Type", - "hint": "Set 'adaptive' for Opus 4.6+ / Sonnet 4.6+ (recommended). Leave empty to use manual budget mode. See: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking" - }, - "budget": { - "description": "Thinking Budget", - "hint": "Anthropic thinking.budget_tokens param. Must >= 1024. Only used when type is empty. Deprecated on Opus 4.6 / Sonnet 4.6. See: https://platform.claude.com/docs/en/build-with-claude/extended-thinking" - }, - "effort": { - "description": "Effort Level", - "hint": "Controls thinking depth when type is 'adaptive'. 'high' is the default. 'max' is Opus 4.6 only. See: https://platform.claude.com/docs/en/build-with-claude/effort" - } - }, - "minimax-group-id": { - "description": "User group", - "hint": "Visible in Account Management -> Basic Info." - }, - "minimax-langboost": { - "description": "Target language/dialect", - "hint": "Enhances recognition for specified languages/dialects and improves speech performance in those scenarios." - }, - "minimax-voice-speed": { - "description": "Speech rate", - "hint": "Speech speed for synthesis, range [0.5, 2], default 1.0. Higher is faster." - }, - "minimax-voice-vol": { - "description": "Volume", - "hint": "Volume for synthesis, range (0, 10], default 1.0. Higher is louder." - }, - "minimax-voice-pitch": { - "description": "Pitch", - "hint": "Pitch for synthesis, range [-12, 12], default 0." - }, - "minimax-is-timber-weight": { - "description": "Enable mixed voices", - "hint": "Enable mixing up to four voices with custom weights. When enabled, single voice settings are ignored." - }, - "minimax-timber-weight": { - "description": "Mixed voices", - "hint": "Mixed voices and their weights. Up to four voices, integer weights in [1, 100]. Get presets and templates from the official API TTS debug console. Must be a JSON string; check the console to confirm parsing. See defaults and the official code preview for structure." - }, - "minimax-voice-id": { - "description": "Single voice", - "hint": "Single voice ID; see the official documentation." - }, - "minimax-voice-emotion": { - "description": "Emotion", - "hint": "Controls emotion of synthesized speech. When set to auto, it selects emotion based on text." - }, - "minimax-voice-latex": { - "description": "Read LaTeX formulas", - "hint": "Read LaTeX formulas, but ensure input text is formatted per the official requirements." - }, - "minimax-voice-english-normalization": { - "description": "English text normalization", - "hint": "Improves number-reading performance but slightly increases latency." - }, - "rag_options": { - "description": "RAG options", - "hint": "Knowledge base retrieval settings, optional. Only supported for Agent app types (agent apps, including RAG apps). For Bailian apps, enabling this disables multi-turn conversations.", - "pipeline_ids": { - "description": "Knowledge base ID list", - "hint": "Retrieve all documents in the specified knowledge bases. Go to https://bailian.console.aliyun.com/ Data Apps -> Knowledge Index to create and get IDs." - }, - "file_ids": { - "description": "Unstructured document IDs", - "hint": "Retrieve specified unstructured documents. Go to https://bailian.console.aliyun.com/ Data Management to create and get IDs." - }, - "output_reference": { - "description": "Output knowledge base/document references", - "hint": "Append reference sources to the end of each answer. Default is False." - } - }, - "sensevoice_hint": { - "description": "Deploy SenseVoice", - "hint": "Before enabling, install funasr, funasr_onnx, torchaudio, torch, modelscope, and jieba (CPU by default, about 1 GB download), and install ffmpeg. Otherwise STT will not work." - }, - "is_emotion": { - "description": "Emotion recognition", - "hint": "Enable emotion recognition. happy?sad?angry?neutral?fearful?disgusted?surprised?unknown" - }, - "stt_model": { - "description": "Model name", - "hint": "Model name on modelscope. Default: iic/SenseVoiceSmall." - }, - "variables": { - "description": "Workflow fixed input variables", - "hint": "Optional. Fixed workflow input variables are used as workflow inputs. You can also set variables dynamically with /set during a chat. If names conflict, dynamic settings take precedence." - }, - "dashscope_app_type": { - "description": "App type", - "hint": "Bailian app type." - }, - "timeout": { - "description": "Timeout", - "hint": "Timeout in seconds." - }, - "openai-tts-voice": { - "description": "voice", - "hint": "OpenAI TTS voice. OpenAI defaults: 'alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'." - }, - "elevenlabs-tts-voice-id": { - "description": "Voice ID", - "hint": "ElevenLabs voice ID. Browse and copy voice IDs at https://elevenlabs.io/app/voice-library. Default 'JBFqnCBsd6RMkjVDRZzb' (George)." - }, - "elevenlabs-tts-output-format": { - "description": "Output format", - "hint": "Audio output format, e.g. 'mp3_44100_128', 'mp3_22050_32', 'wav_44100', or 'opus_48000_128'. Raw PCM/u-law/a-law formats are not supported. Default 'mp3_44100_128'." - }, - "elevenlabs-tts-stability": { - "description": "Stability", - "hint": "Voice stability, range [0, 1]. Higher is more consistent, lower is more expressive. Leave empty to use the server default." - }, - "elevenlabs-tts-similarity-boost": { - "description": "Similarity boost", - "hint": "How closely the output matches the original voice, range [0, 1]. Leave empty to use the server default." - }, - "elevenlabs-tts-style": { - "description": "Style exaggeration", - "hint": "Style exaggeration of the voice, range [0, 1]. Higher values increase latency. Leave empty to use the server default." - }, - "elevenlabs-tts-use-speaker-boost": { - "description": "Speaker boost", - "hint": "Boost similarity to the original speaker. May slightly increase latency." - }, - "mimo-tts-voice": { - "description": "Voice", - "hint": "MiMo TTS voice name. Supported values include 'mimo_default', 'default_en', and 'default_zh'." - }, - "mimo-tts-format": { - "description": "Output format", - "hint": "Audio format generated by MiMo TTS. Supported values: 'wav', 'mp3', and 'pcm'." - }, - "mimo-tts-style-prompt": { - "description": "Style prompt", - "hint": "Prepended to the synthesis target text as a tag to control speed, emotion, character, or style, such as happy, faster, Sun Wukong, or whispering. Optional." - }, - "mimo-tts-dialect": { - "description": "Dialect", - "hint": "Combined with the style prompt inside the leading tag, for example Northeastern Mandarin, Sichuan dialect, Henan dialect, or Cantonese. Optional." - }, - "mimo-tts-seed-text": { - "description": "Seed text", - "hint": "Sent as an optional user message to help guide tone and speaking style. It is not appended to the synthesis target text." - }, - "fishaudio-tts-character": { - "description": "character", - "hint": "Fishaudio TTS character. Default is Klee. More roles: https://fish.audio/zh-CN/discovery" - }, - "fishaudio-tts-reference-id": { - "description": "reference_id", - "hint": "Fishaudio TTS reference model ID (optional). If set, the model ID is used directly instead of looking up by role name. Example: 626bb6d3f3364c9cbc3aa6a67300a664. More models: https://fish.audio/zh-CN/discovery; open a model detail page to copy the model ID." - }, - "whisper_hint": { - "description": "Notes for local Whisper deployment", - "hint": "Before enabling, install the openai-whisper library (NVIDIA users download ~2GB mainly for torch and cuda; CPU users download ~1GB), and install ffmpeg. Otherwise STT will not work." - }, - "whisper_device": { - "description": "Inference device", - "hint": "Whisper inference device. Apple Silicon can use mps; other environments should use cpu. If mps is selected but unavailable, AstrBot will fall back to cpu." - }, - "id": { - "description": "ID" - }, - "type": { - "description": "Provider category" - }, - "provider_type": { - "description": "Provider capability type" - }, - "enable": { - "description": "Enable" - }, - "key": { - "description": "API Key" - }, - "api_base": { - "description": "API Base URL" - }, - "proxy": { - "description": "Proxy address", - "hint": "HTTP/HTTPS proxy URL, e.g. http://127.0.0.1:7890. Applies only to this provider's API requests and does not affect Docker internal networking." - }, - "model": { - "description": "Model ID", - "hint": "Model name, e.g., gpt-4o-mini, deepseek-chat." - }, - "max_context_tokens": { - "description": "Model context window size", - "hint": "Maximum context tokens. If 0, it auto-fills from model metadata (if available); you can also edit manually." - }, - "dify_api_key": { - "description": "API Key", - "hint": "Dify API Key. This field is required." - }, - "dify_api_base": { - "description": "API Base URL", - "hint": "Dify API Base URL. Default: https://api.dify.ai/v1" - }, - "dify_api_type": { - "description": "Dify app type", - "hint": "Dify API type. According to Dify docs, supported types are chat, chatflow, agent, workflow." - }, - "dify_workflow_output_key": { - "description": "Dify workflow output variable name", - "hint": "Dify workflow output variable name. Only used when app type is workflow. Default: astrbot_wf_output." - }, - "dify_query_input_key": { - "description": "Prompt input variable name", - "hint": "Input variable name for the message text. Default: astrbot_text_query." - }, - "coze_api_key": { - "description": "Coze API Key", - "hint": "Coze API key for accessing Coze services." - }, - "bot_id": { - "description": "Bot ID", - "hint": "Coze bot ID, obtained after creating a bot on the Coze platform." - }, - "coze_api_base": { - "description": "API Base URL", - "hint": "Base URL for the Coze API. Default: https://api.coze.cn" - }, - "deerflow_api_base": { - "description": "API Base URL", - "hint": "DeerFlow API gateway URL. Default: http://127.0.0.1:2026" - }, - "deerflow_api_key": { - "description": "DeerFlow API Key", - "hint": "Optional. Fill this if your DeerFlow gateway is protected by Bearer auth." - }, - "deerflow_auth_header": { - "description": "Authorization Header", - "hint": "Optional. Custom Authorization header value; takes precedence over DeerFlow API Key." - }, - "deerflow_assistant_id": { - "description": "Assistant ID", - "hint": "LangGraph assistant_id, default is lead_agent." - }, - "deerflow_model_name": { - "description": "Model name override", - "hint": "Optional. Overrides DeerFlow default model (maps to runtime context model_name)." - }, - "deerflow_thinking_enabled": { - "description": "Enable thinking mode" - }, - "deerflow_plan_mode": { - "description": "Enable plan mode", - "hint": "Maps to DeerFlow is_plan_mode." - }, - "deerflow_subagent_enabled": { - "description": "Enable subagent", - "hint": "Maps to DeerFlow subagent_enabled." - }, - "deerflow_max_concurrent_subagents": { - "description": "Max concurrent subagents", - "hint": "Maps to DeerFlow max_concurrent_subagents. Effective only when subagent is enabled. Default: 3." - }, - "deerflow_recursion_limit": { - "description": "Recursion limit", - "hint": "Maps to LangGraph recursion_limit." - }, - "auto_save_history": { - "description": "Conversation history managed by Coze", - "hint": "When enabled, Coze manages conversation history. AstrBot's locally saved context will not take effect (read-only), and operations on AstrBot context will not apply. If disabled, AstrBot manages the context." - } - } - }, - "help": { - "documentation": "Official Documentation", - "support": "Join Support Group", - "helpText": "Don't understand the configuration? See {documentation} or {support}.", - "helpPrefix": "Don't understand the configuration? See", - "helpMiddle": "or", - "helpSuffix": "." - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/config.json b/legacy-dashboard/src/i18n/locales/en-US/features/config.json deleted file mode 100644 index 81052078..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/config.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "title": "Configuration", - "subtitle": "Manage system configuration and settings", - "editor": { - "visual": "Visual Editor", - "code": "Code Editor", - "revertCode": "Revert to Previous Code", - "applyConfig": "Apply This Configuration", - "applyTip": "`Apply This Configuration` will stage and apply the configuration to the visual editor. To save, you need to click the save button in the bottom right corner." - }, - "actions": { - "save": "Save Configuration", - "delete": "Delete This Item", - "add": "Add", - "reset": "Reset to Default", - "export": "Export Configuration", - "import": "Import Configuration", - "validate": "Validate Configuration" - }, - "help": { - "documentation": "Official Documentation", - "support": "Join Group for Help", - "helpText": "Don't understand the configuration? Please see {documentation} or {support}.", - "helpPrefix": "Don't understand the configuration? Please see", - "helpMiddle": "or", - "helpSuffix": "." - }, - "messages": { - "configApplied": "Configuration successfully applied. To save, you need to click the save button in the bottom right corner.", - "configApplyError": "Configuration not applied, JSON format error.", - "unsavedChangesNotice": "You have unsaved configuration changes. Click the save button in the bottom-right corner to apply them.", - "saveSuccess": "Configuration saved successfully", - "saveError": "Failed to save configuration", - "loadError": "Failed to load configuration", - "deleteSuccess": "Deleted successfully", - "deleteError": "Failed to delete", - "updateSuccess": "Updated successfully", - "updateError": "Failed to update" - }, - "sections": { - "general": "General Settings", - "advanced": "Advanced Settings", - "security": "Security Settings", - "appearance": "Appearance Settings", - "notification": "Notification Settings", - "moreConfig": "More Settings" - }, - "general": { - "botName": "Bot Name", - "language": "Interface Language", - "timezone": "Timezone", - "autoSave": "Auto Save", - "debugMode": "Debug Mode" - }, - "advanced": { - "logLevel": "Log Level", - "maxConnections": "Max Connections", - "timeout": "Timeout", - "retryAttempts": "Retry Attempts", - "cacheSize": "Cache Size" - }, - "security": { - "apiKey": "API Key", - "allowedHosts": "Allowed Hosts", - "rateLimit": "Rate Limit", - "encryption": "Encryption Settings" - }, - "configSelection": { - "selectConfig": "Select Configuration", - "normalConfig": "Basic", - "systemConfig": "System" - }, - "search": { - "placeholder": "Search config items (key/description/hint)", - "noResult": "No matching config items found" - }, - "configManagement": { - "title": "Configuration Management", - "description": "AstrBot supports separate configuration files for different bots. The `default` configuration is used by default.", - "newConfig": "New Configuration", - "copyConfig": "Copy Configuration", - "editConfig": "Edit Configuration", - "manageConfigs": "Manage Configurations...", - "configName": "Name", - "fillConfigName": "Enter configuration name", - "confirmDelete": "Are you sure you want to delete the configuration \"{name}\"? This action cannot be undone.", - "pleaseEnterName": "Please enter a configuration name", - "nameExists": "Configuration name already exists. Please use another name.", - "createFailed": "Failed to create new configuration", - "copyFailed": "Failed to copy configuration", - "deleteFailed": "Failed to delete configuration", - "updateFailed": "Failed to update configuration" - }, - "buttons": { - "cancel": "Cancel", - "create": "Create", - "update": "Update" - }, - "codeEditor": { - "title": "Edit Configuration File" - }, - "fileUpload": { - "button": "Manage Files", - "dialogTitle": "Uploaded Files", - "dropzone": "Upload new file", - "allowedTypes": "Allowed types: {types}", - "empty": "No files uploaded", - "statusMissing": "Missing file", - "statusUnconfigured": "Not in config", - "uploadSuccess": "Uploaded {count} files", - "uploadFailed": "Upload failed", - "loadFailed": "Failed to load file list", - "fileTooLarge": "File too large (max {max} MB): {name}", - "deleteSuccess": "Deleted file", - "deleteFailed": "Delete failed", - "addToConfig": "Added to config", - "fileCount": "Files: {count}", - "done": "Done" - }, - "unsavedChangesWarning": { - "dialogTitle": "Unsaved changes", - "leavePage": "You have unsaved changes. Do you want to save before leaving?", - "switchConfig": "Switching config will discard unsaved changes. Do you want to save first?", - "options": { - "save": "Save", - "saveAndSwitch": "Save and switch", - "discardAndSwitch": "Discard changes and switch", - "closeCard": "Close the pop-up window", - "confirm": "confirm", - "cancel": "cancel" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/console.json b/legacy-dashboard/src/i18n/locales/en-US/features/console.json deleted file mode 100644 index 812a9a4a..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/console.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "title": "Console", - "autoScroll": { - "enabled": "Auto-scroll enabled", - "disabled": "Auto-scroll disabled" - }, - "pipInstall": { - "button": "Install pip Package", - "dialogTitle": "Install Pip Package", - "packageLabel": "*Package name, e.g. llmtuner", - "mirrorLabel": "Force PyPI repository URL (optional)", - "mirrorHint": "Force PyPI repository URL > Config item `PyPI Repository Address`", - "installButton": "Install", - "installSuccess": "Installation successful.", - "installFailed": "Installation failed.", - "requestFailed": "Request failed." - }, - "debugHint": { - "text": "Debug logs can be enabled in \"Configuration File → System → Console Log Level\"" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/conversation.json b/legacy-dashboard/src/i18n/locales/en-US/features/conversation.json deleted file mode 100644 index d6f28706..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/conversation.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "title": "Conversation Management", - "subtitle": "Manage and view user conversation history", - "filters": { - "title": "Filter Conditions", - "platform": "Platform", - "type": "Type", - "search": "Search Keywords", - "reset": "Reset" - }, - "history": { - "title": "Conversation History", - "refresh": "Refresh" - }, - "batch": { - "deleteSelected": "Delete Selected ({count})", - "exportSelected": "Export Selected ({count})" - }, - "pagination": { - "itemsPerPage": "Items per page", - "showingItems": "Showing {start}-{end} of {total} items" - }, - "table": { - "headers": { - "title": "Conversation Title", - "platform": "Platform", - "type": "Type", - "cid": "Conversation ID", - "umo": "Unified Message Origin", - "sessionId": "Session ID", - "createdAt": "Created At", - "updatedAt": "Updated At", - "actions": "Actions" - }, - "umoDisplay": { - "parsed": "Parsed", - "raw": "UMO" - } - }, - "actions": { - "view": "View", - "edit": "Edit", - "delete": "Delete" - }, - "messageTypes": { - "group": "Group Chat", - "friend": "Private Chat", - "unknown": "Unknown" - }, - "status": { - "noTitle": "Untitled Conversation", - "unknown": "Unknown", - "noData": "No conversation records", - "emptyContent": "Conversation content is empty", - "audioNotSupported": "Your browser does not support audio playback." - }, - "dialogs": { - "view": { - "title": "Conversation Details", - "editMode": "Edit Conversation", - "previewMode": "Preview Mode", - "saveChanges": "Save Changes", - "close": "Close", - "confirmClose": "You have unsaved changes, are you sure you want to close?" - }, - "edit": { - "title": "Edit Conversation Information", - "titleLabel": "Conversation Title", - "titlePlaceholder": "Enter conversation title", - "cancel": "Cancel", - "save": "Save" - }, - "delete": { - "title": "Confirm Delete", - "message": "Are you sure you want to delete conversation {title}? This action cannot be undone.", - "cancel": "Cancel", - "confirm": "Delete" - }, - "batchDelete": { - "title": "Batch Delete Confirmation", - "message": "Are you sure you want to delete the selected {count} conversations? This action cannot be undone, please proceed with caution!", - "andMore": "and {count} more", - "cancel": "Cancel", - "confirm": "Batch Delete", - "warning": "Warning: This action cannot be undone!" - } - }, - "messages": { - "fetchError": "Failed to fetch conversation list", - "saveSuccess": "Save successful", - "saveError": "Save failed", - "deleteSuccess": "Delete successful", - "deleteError": "Delete failed", - "historyError": "Failed to fetch conversation history", - "historySaveSuccess": "Conversation history saved successfully", - "historySaveError": "Failed to save conversation history", - "invalidJson": "Invalid JSON format", - "noItemSelected": "Please select conversations to delete first", - "batchDeleteSuccess": "Successfully deleted {count} conversations", - "batchDeleteError": "Batch delete failed", - "batchDeletePartial": "Delete completed: {deleted} successful, {failed} failed", - "exportSuccess": "Export successful", - "exportError": "Export failed", - "noItemSelectedForExport": "Please select conversations to export first", - "copySuccess": "Copied", - "copyError": "Copy failed" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/cron.json b/legacy-dashboard/src/i18n/locales/en-US/features/cron.json deleted file mode 100644 index 5b081d69..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/cron.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "header": { - "eyebrow": "Automation", - "live": "Live Sync" - }, - "page": { - "title": "Future Task Management", - "beta": "Experimental", - "subtitle": "AstrBot wakes up at the scheduled time, completes the task, and sends the result back to the target conversation.", - "proactive": { - "link": "Supported platforms", - "supported": "Proactive delivery is available only on the configured platforms below", - "unsupported": "No proactive messaging platforms enabled. Turn them on in Platform settings." - } - }, - "actions": { - "create": "New Task", - "edit": "Edit", - "refresh": "Refresh", - "delete": "Delete", - "cancel": "Cancel", - "close": "Close", - "more": "More actions", - "runNow": "Run now", - "save": "Save", - "submit": "Create" - }, - "platformDialog": { - "title": "Supported Platforms", - "description": "Only the following platforms support proactive message delivery. AstrBot can send future task results back only to target conversations on these platforms." - }, - "card": { - "onceAt": "One-off · {time}", - "runAt": "Run at: {time}", - "nextRun": "Next run: {time}", - "dailyAt": "Daily at {time}", - "weeklyAt": "Every {day} at {time}", - "monthlyAt": "Monthly on day {day} at {time}", - "everyMinutes": "Every {count} minutes", - "everyHours": "Every {count} hours", - "everyDays": "Every {count} days", - "customCron": "Custom · {cron}", - "noDeliveryTarget": "No delivery target" - }, - "filters": { - "search": "Search title and content", - "umo": "Filter by delivery target", - "noUmos": "No UMOs", - "noDeliveryTarget": "No delivery target", - "noMatches": "No matching future tasks." - }, - "overview": { - "totalTasks": "Total Tasks", - "totalTasksNote": "Registered future tasks", - "enabledTasks": "Enabled Tasks", - "enabledTasksNote": "Tasks ready to wake and run", - "oneOffTasks": "One-off Tasks", - "recurringTasksNote": "{count} recurring tasks", - "proactivePlatforms": "Proactive Platforms", - "proactivePlatformsNote": "Platforms able to deliver results proactively" - }, - "section": { - "registered": { - "title": "Task List", - "subtitle": "Review the registered tasks, execution timing, and current state" - }, - "delivery": { - "title": "Delivery Status", - "subtitle": "After a task runs, results are proactively sent back through supported platforms", - "support": "Proactive delivery", - "available": "Available", - "unavailable": "Unavailable", - "enabledPlatforms": "Enabled platforms" - }, - "quickCreate": { - "title": "Quick Create", - "runMode": "Task mode", - "target": "Targeting" - }, - "platforms": { - "title": "Supported Platforms" - } - }, - "table": { - "title": "Registered Tasks", - "subtitle": "Track cron setup, session target, last run history, and enabled state", - "empty": "No tasks yet.", - "headers": { - "name": "Name", - "type": "Type", - "cron": "Cron", - "session": "Session ID", - "nextRun": "Next Run", - "lastRun": "Last Run", - "note": "Note", - "actions": "Actions" - }, - "type": { - "once": "One-off", - "recurring": "Recurring", - "activeAgent": "Active Agent", - "workflow": "Workflow", - "unknown": "{type}" - }, - "timezoneLocal": "local", - "notAvailable": "—" - }, - "form": { - "title": "New Task", - "editTitle": "Edit Task", - "chatHint": "You can ask AstrBot in chat to create future tasks instead of adding them here.", - "runOnce": "One-off task", - "name": "Task name *", - "note": "Task requirements *", - "scheduleMode": "Execution time *", - "scheduleModes": { - "once": "One-off", - "interval": "Interval", - "daily": "Daily", - "weekly": "Weekly", - "monthly": "Monthly", - "cron": "Custom Cron" - }, - "intervalEvery": "Every *", - "intervalUnit": "Unit *", - "intervalUnits": { - "minutes": "Minutes", - "hours": "Hours", - "days": "Days" - }, - "dailyTime": "Time *", - "weeklyDay": "Weekday *", - "weeklyTime": "Time *", - "monthlyDay": "Day *", - "monthlyTime": "Time *", - "weekdays": { - "sunday": "Sunday", - "monday": "Monday", - "tuesday": "Tuesday", - "wednesday": "Wednesday", - "thursday": "Thursday", - "friday": "Friday", - "saturday": "Saturday" - }, - "cron": "Cron expression *", - "cronPlaceholder": "0 9 * * *", - "runAt": "Run at *", - "session": "Deliver to", - "noUmos": "No sessions available", - "timezone": "Timezone (optional, e.g. Asia/Shanghai)", - "enabled": "Enabled" - }, - "messages": { - "loadFailed": "Failed to load tasks", - "updateSuccess": "Updated successfully", - "updateFailed": "Failed to update", - "deleteSuccess": "Deleted", - "deleteFailed": "Failed to delete", - "nameRequired": "Task name is required", - "sessionRequired": "Delivery target is required", - "noteRequired": "Task requirements are required", - "cronRequired": "Cron expression is required", - "runAtRequired": "Please select run time", - "intervalRequired": "Please enter a valid interval", - "dailyTimeRequired": "Please select the daily run time", - "weeklyTimeRequired": "Please select the weekly day and time", - "monthlyTimeRequired": "Please select the monthly day and time", - "createSuccess": "Created successfully", - "createFailed": "Failed to create", - "runStarted": "Started", - "runFailed": "Failed to run" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/dashboard.json b/legacy-dashboard/src/i18n/locales/en-US/features/dashboard.json deleted file mode 100644 index ad0dd039..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/dashboard.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "title": "Dashboard", - "subtitle": "Real-time monitoring and statistics", - "lastUpdate": "Last updated", - "status": { - "loading": "Loading...", - "dataError": "Failed to fetch data", - "noticeError": "Failed to fetch notice", - "online": "Online", - "uptime": "Uptime", - "memoryUsage": "Memory Usage" - }, - "stats": { - "totalMessage": { - "title": "Total Messages", - "subtitle": "Total messages sent from all platforms" - }, - "onlinePlatform": { - "title": "Platforms", - "subtitle": "Number of connected platforms" - }, - "runningTime": { - "title": "Uptime", - "subtitle": "System uptime duration", - "format": "{hours}h {minutes}m {seconds}s" - }, - "memoryUsage": { - "title": "Memory Usage", - "subtitle": "System memory usage status", - "cpuLoad": "CPU Load", - "status": { - "good": "Good", - "normal": "Normal", - "high": "High" - } - } - }, - "charts": { - "messageTrend": { - "title": "Message Trend Analysis", - "subtitle": "Track message count changes over time", - "totalMessages": "Total Messages", - "dailyAverage": "Daily Average", - "growthRate": "Growth Rate", - "timeLabel": "Time", - "messageCount": "Message Count", - "timeRanges": { - "1day": "Past 1 Day", - "3days": "Past 3 Days", - "1week": "Past 1 Week", - "1month": "Past 1 Month" - } - }, - "platformStat": { - "title": "Platform Message Statistics", - "subtitle": "Message count distribution by platform", - "total": "Total", - "noData": "No platform data available", - "messageUnit": "msgs", - "platformCount": "Platforms", - "mostActive": "Most Active", - "totalPercentage": "Total Percentage" - } - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/extension.json b/legacy-dashboard/src/i18n/locales/en-US/features/extension.json deleted file mode 100644 index 75313fdb..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/extension.json +++ /dev/null @@ -1,456 +0,0 @@ -{ - "title": "Extension Management", - "subtitle": "Manage and configure system extensions", - "tabs": { - "installedPlugins": "AstrBot Plugins", - "installedMcpServers": "MCP", - "skills": "Skills", - "handlersOperation": "Manage Handlers", - "market": "AstrBot Plugin Market" - }, - "titles": { - "installedAstrBotPlugins": "Installed AstrBot Plugins" - }, - "failedPlugins": { - "title": "Failed to Load Plugins ({count})", - "hint": "These plugins failed to load. You can try reload or uninstall them directly.", - "columns": { - "plugin": "Plugin", - "error": "Error" - } - }, - "search": { - "placeholder": "Search extensions...", - "marketPlaceholder": "Search market extensions..." - }, - "filters": { - "all": "All" - }, - "views": { - "card": "Card View", - "list": "List View" - }, - "buttons": { - "showSystemPlugins": "Show System Extensions", - "hideSystemPlugins": "Hide System Extensions", - "install": "Install", - "uninstall": "Uninstall", - "update": "Update", - "reload": "Reload", - "enable": "Enable", - "disable": "Disable", - "configure": "Configure", - "viewInfo": "Handlers", - "viewDocs": "Documentation", - "viewRepo": "Repository", - "openPages": "Pages", - "openPage": "Open", - "openWebui": "Open Plugin UI", - "close": "Close", - "save": "Save", - "saveAndClose": "Save and Close", - "cancel": "Cancel", - "next": "Next", - "actions": "Actions", - "back": "Back", - "selectFile": "Select File", - "refresh": "Refresh", - "updateAll": "Update All", - "pin": "Pin", - "unpin": "Unpin", - "deleteSource": "Delete Source", - "reshuffle": "Shuffle Again" - }, - "status": { - "enabled": "Enabled", - "disabled": "Disabled", - "system": "System", - "loading": "Loading...", - "installed": "Installed", - "unknown": "Unknown", - "unsupported": "Unsupported" - }, - "tooltips": { - "enable": "Click to Enable", - "disable": "Click to Disable", - "reload": "Reload", - "configure": "Configure", - "viewInfo": "Handlers", - "viewDocs": "Documentation", - "update": "Update", - "uninstall": "Uninstall" - }, - "table": { - "headers": { - "name": "Name", - "description": "Description", - "version": "Version", - "author": "Author", - "status": "Status", - "actions": "Actions", - "stars": "Stars", - "lastUpdate": "Last Update", - "tags": "Tags", - "eventType": "Event Type", - "specificType": "Specific Type", - "trigger": "Trigger" - } - }, - "empty": { - "noPlugins": "No Extensions", - "noPluginsDesc": "Try installing extensions or adjusting the search" - }, - "detail": { - "contents": "Plugin Handlers", - "noContents": "No handlers", - "notFound": "Plugin not found", - "docsTitle": "Documentation", - "docsEmpty": "No documentation", - "changelogTitle": "Changelog", - "changelogEmpty": "No changelog", - "subCommandsCount": "{count} sub-commands", - "handlerGroups": { - "page": "Pages", - "skill": "Skills", - "command": "Commands / Command Groups", - "llm_tool": "LLM Tools", - "listener": "Event Listeners", - "hook": "Hooks", - "commands": "Commands / Command Groups", - "hooks": "Hooks", - "functionTools": "Function Tools", - "eventListeners": "Event Listeners" - }, - "info": { - "title": "Info", - "version": "Version", - "author": "Author", - "category": "Category", - "stars": "Stars", - "tags": "Tags", - "astrbotVersion": "AstrBot Version Requirement", - "supportPlatforms": "Supported Platforms", - "authorWebsite": "Author Website", - "repository": "Repository" - } - }, - "market": { - "recommended": "🥳 Recommended", - "allPlugins": "📦 All Extensions", - "category": "Category", - "categories": { - "all": "All", - "ai_tools": "AI Tools", - "entertainment": "Entertainment", - "productivity": "Productivity", - "integrations": "Integrations", - "utilities": "Utilities", - "other": "Other" - }, - "showFullName": "Full Name", - "devDocs": "Extension Development Docs", - "submitRepo": "Submit Extension Repository", - "customSource": "Custom Extension Source", - "source": "Source", - "availableSources": "Available Sources", - "sourceManagement": "Source Management", - "addSource": "Add Source", - "sourceName": "Source Name", - "sourceUrl": "Source URL", - "defaultSource": "Default Source", - "removeSource": "Remove Source", - "confirmRemoveSource": "Are you sure you want to remove this source?", - "sourceAdded": "Source added successfully", - "sourceRemoved": "Source removed successfully", - "sourceError": "Operation failed", - "selectSource": "Select Source", - "currentSource": "Current Source", - "editSource": "Edit Source", - "sourceUpdated": "Source updated successfully", - "defaultOfficialSource": "Default Official Source", - "sourceExists": "This source already exists", - "sourceResolved": "Source resolved successfully", - "installPlugin": "Install Plugin", - "randomPlugins": "🎲 Random Plugins", - "sourceSafetyWarning": "Even with the default source, plugin stability and security cannot be fully guaranteed. Please verify carefully before use." - }, - "sort": { - "by": "Sort by", - "default": "Default", - "installTime": "Last Modified", - "name": "Name", - "stars": "Stars", - "author": "Author", - "updated": "Last Updated", - "updateStatus": "Update Status", - "ascending": "Ascending", - "descending": "Descending" - }, - "tags": { - "danger": "Danger" - }, - "dialogs": { - "error": { - "title": "Error Information", - "checkConsole": "Please check console for details" - }, - "config": { - "title": "Extension Configuration", - "noConfig": "This extension has no configuration" - }, - "loading": { - "title": "Loading...", - "logs": "Logs" - }, - "uninstall": { - "title": "Confirm Deletion", - "message": "Are you sure you want to delete this extension?", - "deleteConfig": "Also delete plugin configuration file", - "deleteData": "Also delete plugin persistent data", - "configHint": "Configuration file located in data/config directory", - "dataHint": "Deletes data in data/plugin_data and data/plugins_data, as well as KV preference data in the database" - }, - "install": { - "title": "Install Extension", - "fromFile": "Install from File", - "fromUrl": "Install from URL", - "supportPlatformsCount": "Supports {count} Platforms", - "sectionTitle": "Install", - "downloadSource": "The plugin package will be installed from:", - "githubSecurityWarning": "AstrBot cannot guarantee the safety of plugins downloaded from GitHub. Install only if you trust the plugin source." - }, - "update": { - "title": "Update Extension", - "sectionTitle": "Update", - "downloadSource": "The update package will be installed from:", - "confirm": "Confirm Update" - }, - "danger_warning": { - "title": "Dangerous Plugin Warning", - "message": "This plugin has been flagged as containing security risks, including unsafe code or functionalities that may cause system malfunctions or data loss. Do you wish to proceed with the installation?", - "confirm": "Continue", - "cancel": "Cancel" - }, - "versionSupport": { - "title": "Version Support Warning", - "message": "This plugin declares an AstrBot version range that does not match your current version. You can ignore this warning and continue installation, but it may not work correctly.", - "confirm": "Ignore Warning and Install", - "cancel": "Cancel Installation" - }, - "forceUpdate": { - "title": "No New Version Detected", - "message": "No new version detected for this plugin. Do you want to force reinstall? This will pull the latest code from the remote repository.", - "confirm": "Force Update" - }, - "updateAllConfirm": { - "title": "Confirm Update All Plugins", - "message": "Are you sure you want to update all {count} plugins? This operation may take some time.", - "confirm": "Confirm Update" - }, - "sourceBinding": { - "title": "Change Plugin Source", - "selectTitle": "Select Plugin Install Source", - "noCandidates": "No configured plugin source contains a matching record, and this plugin has no usable repository URL.", - "repoOption": "Use Repository URL Directly", - "repoOptionId": "Reinstall via repo", - "installDestination": "Install destination", - "confirm": "Use This Source", - "confirmAndContinue": "Use and Continue" - } - }, - "messages": { - "uninstalling": "Uninstalling", - "refreshing": "Refreshing extension list...", - "refreshSuccess": "Extension list refreshed!", - "refreshFailed": "Error occurred while refreshing extension list", - "operationFailed": "Operation failed", - "reloadSuccess": "Reload successful", - "reloadFailed": "Reload failed", - "updateSuccess": "Update successful!", - "addSuccess": "Add successful!", - "saveSuccess": "Save successful!", - "deleteSuccess": "Delete successful!", - "installing": "Installing extension from file", - "installingFromUrl": "Installing extension from URL...", - "installFailed": "Extension installation failed:", - "getMarketDataFailed": "Failed to get extension market data:", - "hasUpdate": "New version available:", - "confirmDelete": "Are you sure you want to delete this extension?", - "fillUrlOrFile": "Please fill in extension URL or upload extension file", - "dontFillBoth": "Please don't fill in both extension URL and upload file", - "supportedFormats": "Supports .zip extension files", - "updateAllSuccess": "All upgradable extensions have been updated!", - "updateAllFailed": "{failed} of {total} extensions failed to update:", - "noUpdatesAvailable": "No extensions have updates available", - "updateDisabled": "This plugin has no usable install source or repository URL, so it cannot be updated or reinstalled", - "changeSourceDisabled": "This plugin has no usable repository URL, so its source cannot be changed", - "fillSourceUrl": "Please enter the source URL", - "fillSourceName": "Please enter the source name", - "resolveSourceFirst": "Resolve and validate the source URL first", - "sourceResolveFailed": "Failed to resolve source", - "invalidUrl": "Please enter a valid URL", - "invalidGithubRepo": "Please enter a valid GitHub repository URL", - "enterJsonUrl": "Please enter a URL that returns plugin list JSON data", - "pluginNotFound": "Plugin not found", - "pluginDisabled": "This plugin is disabled. Enable it before opening its page.", - "pluginPageNotFound": "Plugin page not found", - "pluginPageLoadFailed": "Failed to load the plugin page", - "missingMarketPluginId": "This source record is missing legal plugin identifiers, so it cannot be installed from the market", - "sourceBindSuccess": "Plugin source updated", - "validatingPlugin": "Validating plugin...", - "pluginValidateSuccess": "Plugin validation passed", - "pluginValidateFailed": "Plugin validation failed" - }, - "upload": { - "fromFile": "Install from File", - "fromUrl": "Install from URL", - "selectFile": "Select File", - "enterUrl": "Enter extension repository URL" - }, - "skills": { - "modeLocal": "Local Skills", - "modeNeo": "Neo Skills", - "actions": "Actions", - "upload": "Upload Skills", - "refresh": "Refresh", - "empty": "No Skills found", - "emptyHint": "Upload a Skills zip to get started", - "uploadDialogTitle": "Upload Skills", - "uploadHint": "Upload multiple zip skill packages or drag them in. The system validates the structure automatically and shows a result for each file.", - "structureRequirement": "The archive supports multiple skills folders.", - "abilityMultiple": "Upload multiple zip files at once", - "abilityValidate": "Validate `SKILL.md` automatically", - "abilitySkip": "Automatically skip duplicate files.", - "selectFile": "Select file", - "selectFiles": "Select files (multiple allowed)", - "dropzoneTitle": "Drag multiple zip files here", - "dropzoneAction": "or click to pick multiple files from a folder", - "dropzoneHint": "Batch upload is supported and the structure will be validated automatically", - "fileListTitle": "Files in queue", - "fileListEmpty": "Selected files will appear here with validation feedback and upload status", - "uploading": "Uploading...", - "batchResultTitle": "Batch Upload Results", - "batchResultSummary": "{success} of {total} files uploaded successfully", - "batchSuccessList": "Successfully uploaded", - "batchFailedList": "Failed to upload", - "confirm": "OK", - "confirmUpload": "Start Upload", - "cancel": "Cancel", - "statusWaiting": "Waiting", - "statusUploading": "Uploading", - "statusSuccess": "Uploaded", - "statusError": "Failed", - "statusSkipped": "Skipped", - "summaryTotal": "{count} file(s)", - "summaryReady": "Pending {count}", - "summarySuccess": "Success {count}", - "summaryFailed": "Failed {count}", - "summarySkipped": "Skipped {count}", - "validationReady": "Ready to upload. The archive structure will be checked during upload.", - "validationZipOnly": "Only zip skill packages are supported", - "validationDuplicate": "A file with the same name is already in the queue and has been skipped", - "validationUploading": "Validating and uploading...", - "validationUploadFailed": "Upload failed. Please try again.", - "validationUploadedAs": "Installed as {name}", - "validationNoResult": "No validation result was returned. Check the platform logs.", - "noDescription": "No description", - "path": "Path", - "uploadSuccess": "Upload succeeded", - "uploadFailed": "Upload failed", - "download": "Download", - "downloadSuccess": "Download succeeded", - "downloadFailed": "Download failed", - "loadFailed": "Failed to load Skills", - "updateSuccess": "Updated successfully", - "updateFailed": "Update failed", - "deleteTitle": "Delete confirmation", - "deleteMessage": "Are you sure you want to delete this Skill?", - "deleteSuccess": "Deleted successfully", - "deleteFailed": "Delete failed", - "neoSkillKey": "Filter by skill_key", - "neoStatus": "Candidate Status", - "neoStage": "Release Stage", - "neoFilterHint": "Filter candidates and release records", - "neoAll": "All", - "neoCandidates": "Neo Candidates", - "neoReleases": "Neo Releases", - "neoLoadFailed": "Failed to load Neo skills data", - "neoPass": "Pass", - "neoReject": "Reject", - "neoEvaluateSuccess": "Evaluation updated", - "neoEvaluateFailed": "Failed to update evaluation", - "neoPromoteSuccess": "Promoted successfully", - "neoPromoteFailed": "Failed to promote", - "neoRollback": "Rollback", - "neoRollbackSuccess": "Rollback succeeded", - "neoRollbackFailed": "Rollback failed", - "neoDeactivate": "Deactivate", - "neoDeactivateSuccess": "Deactivated successfully", - "neoDeactivateFailed": "Failed to deactivate", - "neoSync": "Sync", - "neoSyncSuccess": "Sync succeeded", - "neoSyncFailed": "Sync failed", - "neoDelete": "Delete", - "neoDeleteSuccess": "Deleted successfully", - "neoDeleteFailed": "Failed to delete", - "neoPayloadTitle": "Neo Payload", - "neoPayloadFailed": "Failed to load payload", - "runtimeNoneWarning": "Computer Use runtime is set to None; Skills may not run correctly because no runtime is enabled.", - "runtimeHint": "Set the Computer Use runtime to Local or Sandbox in settings so AstrBot can use your Skills. Workspace Skills are not shown on this page yet.", - "neoRuntimeRequired": "Neo Skills are available only when runtime is sandbox and sandbox booter is shipyard_neo.", - "sourceLocalOnly": "Local Skill", - "sourceSandboxOnly": "Sandbox Preset Skill", - "sourceBoth": "Local + Sandbox", - "sourcePlugin": "Plugin: {plugin}", - "sandboxDiscoveryPending": "Sandbox preset skills have not been discovered yet. Start at least one sandbox session to populate this list.", - "sandboxPresetReadonly": "Sandbox preset skills are read-only here. You cannot delete or enable/disable them from Local Skills.", - "pluginReadonly": "Plugin-provided skills are managed by their plugin. They cannot be deleted or downloaded from Local Skills.", - "openEditor": "View/Edit", - "editorTitle": "Edit Skill", - "editorLoadFailed": "Failed to load Skill file", - "editorSaveFailed": "Failed to save Skill file", - "editorSaveSuccess": "Saved successfully", - "saveFile": "Save file", - "readonly": "Read-only", - "unsaved": "Unsaved", - "noFileSelected": "No file selected", - "discardChanges": "This file has unsaved changes. Discard them?" - }, - "card": { - "actions": { - "pluginConfig": "Extension Config", - "uninstallPlugin": "Uninstall Extension", - "reloadPlugin": "Reload Extension", - "togglePlugin": "Extension", - "viewHandlers": "View Handlers", - "updateTo": "Update to", - "reinstall": "Reinstall", - "changeSource": "Change Plugin Source" - }, - "status": { - "hasUpdate": "New version available", - "disabled": "This extension is disabled", - "handlersCount": " handlers", - "supportPlatform": "Supported Platform", - "supportPlatformsCount": "Supports {count} Platforms", - "astrbotVersion": "AstrBot Version Requirement" - }, - "alt": { - "logo": "logo", - "extensionIcon": "extension icon" - }, - "errors": { - "confirmNotRegistered": "$confirm not properly registered" - } - }, - "conflicts": { - "title": "Command Conflicts Detected", - "message": "This will cause some commands to work abnormally. It is recommended to go to the [Command Management] panel to handle it.", - "pairs": "command conflicts", - "goToManage": "Go to Manage", - "later": "Later" - }, - "pluginChangelog": { - "menuTitle": "View Changelog" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/detail.json b/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/detail.json deleted file mode 100644 index 78a00669..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/detail.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "title": "Knowledge Base Details", - "backToList": "Back to List", - "breadcrumb": { - "list": "Knowledge Bases" - }, - "tabs": { - "overview": "Overview", - "documents": "Documents", - "retrieval": "Retrieval", - "sessions": "Sessions", - "settings": "Settings" - }, - "overview": { - "title": "Basic Information", - "name": "Name", - "description": "Description", - "emoji": "Icon", - "createdAt": "Created At", - "updatedAt": "Updated At", - "stats": "Statistics", - "docCount": "Documents", - "chunkCount": "Chunks", - "embeddingModel": "Embedding Model", - "rerankModel": "Rerank Model", - "notSet": "Not Set" - }, - "documents": { - "title": "Documents", - "upload": "Upload Document", - "empty": "No documents", - "name": "Name", - "type": "Type", - "size": "Size", - "chunks": "Chunks", - "createdAt": "Uploaded At", - "actions": "Actions", - "view": "View", - "delete": "Delete", - "deleteConfirm": "Are you sure you want to delete document '{name}'?", - "deleteWarning": "This will delete the document and all its chunks. This action cannot be undone.", - "uploading": "Uploading...", - "uploadSuccess": "Document uploaded successfully", - "uploadFailed": "Failed to upload document", - "deleteSuccess": "Document deleted successfully", - "deleteFailed": "Failed to delete document" - }, - "upload": { - "title": "Upload Document", - "selectFile": "Select File", - "dropzone": "Drop files here or click to select", - "supportedFormats": "Supported formats: .txt, .md, .markdown, .rst, .adoc, .pdf, .docx, .epub, .xls, .xlsx", - "maxSize": "Max file size: 128MB", - "chunkSettings": "Chunk Settings", - "batchSettings": "Batch Settings", - "cleaningSettings": "Cleaning Settings", - "enableCleaning": "Enable Content Cleaning", - "cleaningProvider": "Cleaning Service Provider", - "cleaningProviderHint": "Select an LLM provider to clean and summarize the extracted web page content", - "chunkSize": "Chunk Size", - "chunkSizeHint": "Number of characters per chunk (default: 512)", - "chunkOverlap": "Chunk Overlap", - "chunkOverlapHint": "Overlapping characters between chunks (default: 50)", - "batchSize": "Batch Size", - "batchSizeHint": "Number of chunks to process in each batch (default: 32)", - "tasksLimit": "Concurrent Tasks Limit", - "tasksLimitHint": "Maximum number of concurrent upload tasks (default: 3)", - "maxRetries": "Max Retries", - "maxRetriesHint": "Number of times to retry a failed upload task (default: 3)", - "cancel": "Cancel", - "submit": "Upload", - "fileRequired": "Please select a file to upload", - "fileUpload": "File Upload", - "fromUrl": "From URL", - "urlPlaceholder": "Enter the URL of the web page to extract content from", - "urlRequired": "Please enter a URL", - "urlHint": "The main content will be automatically extracted from the target URL as a document. Currently supports {supported} pages. Before use, please ensure that the target web page allows crawler access.", - "beta": "Beta" - }, - "retrieval": { - "title": "Retrieval", - "subtitle": "Test the knowledge base using dense and sparse retrieval methods", - "query": "Query", - "queryPlaceholder": "Enter a query...", - "search": "Search", - "searching": "Searching...", - "results": "Results", - "noResults": "No results found", - "tryDifferentQuery": "Try a different query", - "settings": "Retrieval Settings", - "topK": "Number of Results", - "topKHint": "Maximum number of results to return", - "enableRerank": "Enable Rerank", - "enableRerankHint": "Use a rerank model to improve retrieval quality", - "score": "Relevance Score", - "document": "Document", - "chunk": "Chunk #{index}", - "content": "Content", - "charCount": "{count} characters", - "searchSuccess": "Search completed, found {count} results", - "searchFailed": "Search failed", - "queryRequired": "Please enter a query" - }, - "settings": { - "title": "Knowledge Base Settings", - "basic": "Basic Settings", - "retrieval": "Retrieval Settings", - "chunkSize": "Chunk Size", - "chunkOverlap": "Chunk Overlap", - "topKDense": "Dense Retrieval Count", - "topKSparse": "Sparse Retrieval Count", - "topMFinal": "Final Result Count", - "enableRerank": "Enable Rerank", - "embeddingProvider": "Embedding Provider", - "rerankProvider": "Rerank Provider", - "save": "Save Settings", - "saveSuccess": "Settings saved successfully", - "saveFailed": "Failed to save settings", - "tips": "Tip: Modifying retrieval settings will affect subsequent knowledge base queries." - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/document.json b/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/document.json deleted file mode 100644 index d3a3b65c..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/document.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "title": "Document Details", - "backToKB": "Back to Knowledge Base", - "info": { - "title": "Document Information", - "name": "Document Name", - "type": "File Type", - "size": "File Size", - "chunkCount": "Chunk Count", - "createdAt": "Uploaded At" - }, - "chunks": { - "title": "Chunks", - "empty": "No chunks", - "index": "Index", - "content": "Content", - "charCount": "Characters", - "actions": "Actions", - "view": "View", - "edit": "Edit", - "delete": "Delete", - "preview": "Preview", - "search": "Search Chunks", - "searchPlaceholder": "Enter keywords to search chunks...", - "showing": "Showing", - "deleteConfirm": "Are you sure you want to delete this chunk?", - "deleteSuccess": "Chunk deleted successfully", - "deleteFailed": "Failed to delete chunk" - }, - "edit": { - "title": "Edit Chunk", - "content": "Chunk Content", - "cancel": "Cancel", - "save": "Save", - "saveSuccess": "Chunk saved successfully", - "saveFailed": "Failed to save chunk" - }, - "delete": { - "title": "Delete Chunk", - "confirmText": "Are you sure you want to delete this chunk?", - "warning": "This action cannot be undone and may affect knowledge base retrieval performance.", - "cancel": "Cancel", - "confirm": "Delete", - "deleteSuccess": "Chunk deleted successfully", - "deleteFailed": "Failed to delete chunk" - }, - "view": { - "title": "Chunk Details", - "index": "Index", - "content": "Content", - "charCount": "Characters", - "vecDocId": "Vector ID", - "close": "Close" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/index.json b/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/index.json deleted file mode 100644 index 67bb4d57..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/knowledge-base/index.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "title": "Knowledge Base Management", - "subtitle": "Manage and query knowledge base contents", - "list": { - "title": "Knowledge Bases", - "subtitle": "Manage all your knowledge base collections", - "create": "Create Knowledge Base", - "refresh": "Refresh List", - "empty": "No knowledge bases", - "loading": "Loading...", - "documents": "Documents", - "chunks": "Chunks", - "sessionConfig": "Session Config", - "initError": "Initialization Failed" - }, - "card": { - "edit": "Edit", - "delete": "Delete", - "open": "Open", - "docCount": "{count} Documents", - "chunkCount": "{count} Chunks" - }, - "create": { - "title": "Create Knowledge Base", - "nameLabel": "Name", - "namePlaceholder": "Enter knowledge base name", - "descriptionLabel": "Description", - "descriptionPlaceholder": "Describe the purpose of this knowledge base...", - "emojiLabel": "Icon", - "embeddingModelLabel": "Embedding Model", - "rerankModelLabel": "Rerank Model (Optional)", - "providerInfo": "Provider: {id} | Dimensions: {dimensions}", - "rerankProviderInfo": "Provider: {id}", - "cancel": "Cancel", - "submit": "Create", - "nameRequired": "Please enter knowledge base name" - }, - "edit": { - "title": "Edit Knowledge Base", - "submit": "Save" - }, - "delete": { - "title": "Delete Knowledge Base", - "confirmText": "Are you sure you want to delete knowledge base '{name}'?", - "warning": "This action is irreversible. All documents, chunks, and associated configurations will be permanently deleted.", - "cancel": "Cancel", - "confirm": "Delete" - }, - "emoji": { - "title": "Select Icon", - "close": "Close", - "categories": { - "books": "Books & Documents", - "emotions": "Emotions & Faces", - "objects": "Objects & Tools", - "symbols": "Symbols & Signs" - } - }, - "messages": { - "createSuccess": "Knowledge base created successfully", - "createFailed": "Failed to create", - "updateSuccess": "Knowledge base updated successfully", - "updateFailed": "Failed to update", - "deleteSuccess": "Knowledge base deleted successfully", - "deleteFailed": "Failed to delete", - "loadError": "Failed to load knowledge base list" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/persona.json b/legacy-dashboard/src/i18n/locales/en-US/features/persona.json deleted file mode 100644 index 3dc865c0..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/persona.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "page": { - "description": "Manage and configure chat bot personality settings" - }, - "buttons": { - "create": "Create Persona", - "createFirst": "Create First Persona", - "edit": "Edit", - "delete": "Delete", - "cancel": "Cancel", - "save": "Save", - "move": "Move", - "addDialogPair": "Add Dialog Pair" - }, - "labels": { - "presetDialogs": "Preset Dialogs ({count} pairs)", - "createdAt": "Created At", - "updatedAt": "Updated At" - }, - "form": { - "personaId": "Persona ID", - "systemPrompt": "System Prompt", - "customErrorMessage": "Custom Error Reply Message (Optional)", - "customErrorMessageHelp": "When this persona's LLM request fails (for example, connection failures), this error reply is sent first. Leave empty to use the default error message.", - "presetDialogs": "Preset Dialogs", - "presetDialogsHelp": "Add some preset dialogs to help the bot better understand the role settings. The number of dialogs must be even (users and assistants take turns).", - "userMessage": "User Message", - "assistantMessage": "Assistant Message", - "tools": "Tool Selection", - "toolsHelp": "Select available tools for this persona. Tools allow the bot to perform specific functions such as searching, calculating, getting information, etc.", - "toolsSelection": "Tool Selection Actions", - "selectAllTools": "Select All Tools", - "clearAllTools": "Clear Selection", - "allSelected": "All Selected", - "mcpServersQuickSelect": "MCP Servers Quick Select", - "searchTools": "Search Tools", - "selectedTools": "Selected Tools", - "builtinToolDisabledHint": "Builtin tools cannot be enabled or disabled here yet. Please enable or disable the corresponding config items in the config file.", - "noToolsAvailable": "No tools available", - "noToolsFound": "No matching tools found", - "loadingTools": "Loading tools...", - "allToolsAvailable": "Use all available tools", - "noToolsSelected": "No tools selected", - "skills": "Skills Selection", - "skillsHelp": "Select available Skills for this persona. Skills provide reusable workflows and guidance.", - "skillsAllAvailable": "Use all available Skills", - "skillsSelectSpecific": "Select specific Skills", - "searchSkills": "Search Skills", - "selectedSkills": "Selected Skills", - "noSkillsAvailable": "No skills available", - "noSkillsFound": "No matching skills found", - "loadingSkills": "Loading skills...", - "allSkillsAvailable": "Use all available Skills", - "noSkillsSelected": "No skills selected", - "skillsRuntimeNoneWarning": "Computer Use runtime is set to None; Skills may not run correctly because no runtime is enabled.", - "createInFolder": "Will be created in \"{folder}\"", - "rootFolder": "All Personas" - }, - "dialog": { - "create": { - "title": "Create New Persona" - }, - "edit": { - "title": "Edit Persona" - } - }, - "empty": { - "title": "No Persona Configured", - "description": "Create your first persona to start using personalized chatbots", - "folderEmpty": "This folder is empty", - "folderEmptyDescription": "Create a new persona or folder to get started" - }, - "validation": { - "required": "This field is required", - "minLength": "Minimum {min} characters required", - "alphanumeric": "Only letters, numbers, underscores and hyphens are allowed", - "dialogRequired": "{type} cannot be empty", - "personaIdExists": "This persona name already exists" - }, - "messages": { - "loadError": "Failed to load persona list", - "saveSuccess": "Saved successfully", - "saveError": "Save failed", - "deleteConfirm": "Are you sure you want to delete persona \"{id}\"? This action cannot be undone.", - "deleteSuccess": "Deleted successfully", - "deleteError": "Delete failed" - }, - "persona": { - "personasTitle": "Personas", - "toolsCount": "tools", - "skillsCount": "skills", - "contextMenu": { - "moveTo": "Move to..." - }, - "messages": { - "moveSuccess": "Persona moved successfully", - "moveError": "Failed to move persona" - } - }, - "folder": { - "sidebarTitle": "Folders", - "rootFolder": "Root", - "foldersTitle": "Folders", - "noFolders": "No folders yet", - "createButton": "New Folder", - "searchPlaceholder": "Search folders...", - "form": { - "name": "Folder Name", - "description": "Description (optional)" - }, - "validation": { - "nameRequired": "Folder name is required" - }, - "contextMenu": { - "open": "Open", - "rename": "Rename", - "moveTo": "Move to...", - "delete": "Delete" - }, - "createDialog": { - "title": "Create New Folder", - "createButton": "Create" - }, - "renameDialog": { - "title": "Rename Folder" - }, - "deleteDialog": { - "title": "Delete Folder", - "message": "Are you sure you want to delete folder \"{name}\"?", - "warning": "All personas inside will be moved to root folder." - }, - "messages": { - "createSuccess": "Folder created successfully", - "createError": "Failed to create folder", - "renameSuccess": "Folder renamed successfully", - "renameError": "Failed to rename folder", - "deleteSuccess": "Folder deleted successfully", - "deleteError": "Failed to delete folder" - } - }, - "moveDialog": { - "title": "Move to Folder", - "description": "Select a destination folder for \"{name}\"", - "success": "Moved successfully", - "error": "Failed to move" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/platform.json b/legacy-dashboard/src/i18n/locales/en-US/features/platform.json deleted file mode 100644 index 4932a0b1..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/platform.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "title": "Platform Adapter Management", - "subtitle": "Manage bot platform adapters to connect to different chat platforms", - "adapters": "Platform Adapters", - "addAdapter": "Add Adapter", - "emptyText": "No platform adapters yet, click Add Adapter to create one", - "viewWebhook": "View Webhook URL", - "webhookCopied": "Webhook URL copied to clipboard", - "webhookCopyFailed": "Copy failed, please copy manually", - "webhookDialog": { - "title": "Webhook Callback URL", - "description": "The callback address is as follows, please ensure that the network environment can be publicly accessible. You can also view the callback address information in the logs. It is recommended to fill in the \"Externally Accessible Callback API Address\" in Settings -> General.", - "close": "Close" - }, - "details": { - "adapterType": "Adapter Type", - "token": "Token", - "description": "Description" - }, - "logs": { - "title": "Platform Logs", - "expand": "Expand", - "collapse": "Collapse" - }, - "dialog": { - "add": "Add", - "edit": "Edit", - "adapter": "Platform Adapter", - "refresh": "Refresh", - "cancel": "Cancel", - "save": "Save", - "addPlatform": "Add Platform Adapter", - "connectTitle": "Connect {name}", - "viewTutorial": "View Tutorial", - "noTemplates": "No platform templates available", - "idConflict": { - "title": "ID Conflict Warning", - "message": "Detected duplicate ID \"{id}\". Please use a new ID.", - "confirm": "OK" - }, - "securityWarning": { - "title": "Security Warning", - "aiocqhttpTokenMissing": "To enhance connection security, it is strongly recommended to set ws_reverse_token. Not setting a token may lead to security risks.", - "learnMore": "Learn More" - }, - "invalidPlatformId": "Platform ID cannot contain ':' or '!'." - }, - "createDialog": { - "step1Title": "Choose Platform Category", - "step1Hint": "Where do you want to connect the bot? e.g. QQ, WeCom, Feishu, Discord, Telegram.", - "platformTypeLabel": "Platform Category", - "configFileTitle": "Config File", - "optional": "Optional", - "configHint": "How do you want to configure the bot? The config file includes model, persona, knowledge base, plugins, and more.", - "configDefaultHint": "Uses the default config file \"default\" by default. You can configure it later.", - "useExistingConfig": "Use existing config file", - "selectConfigLabel": "Select config file", - "createNewConfig": "Create new config file", - "newConfigNameLabel": "New config name", - "newConfigTitle": "Use new config file", - "newConfigLoadFailed": "Failed to load default config template", - "addRouteRule": "Add route rule", - "viewMode": "View", - "editMode": "Edit", - "noRouteRules": "No route rules for this platform. The default config file will be used.", - "sessionIdPlaceholder": "Session ID or *", - "routeSource": { - "selectPlaceholder": "Select session source", - "noData": "No unified message source for this bot is available yet. It will appear here after this bot receives at least one message from the corresponding messaging platform.", - "switchToKnown": "Switch to existing source selection", - "switchToManual": "Switch to manual input" - }, - "allSessions": "All sessions", - "configMissing": "Config file not found", - "routeHint": "When delivering messages, the first matching config file from top to bottom is used based on session source. Use * to match all. Use /sid to get the session ID. If none match, the default config file is used.", - "warningContinue": "Ignore warning and continue", - "warningEditAgain": "Edit again", - "configDrawerTitle": "Config File Management", - "configDrawerIdLabel": "ID", - "configTableHeaders": { - "configId": "Config ID linked to this instance", - "scope": "Scope in this instance" - }, - "routeTableHeaders": { - "source": "Message Source (Type: Session ID)", - "config": "Config File", - "actions": "Actions" - }, - "messageTypeOptions": { - "all": "All messages", - "group": "Group messages (GroupMessage)", - "friend": "Direct messages (FriendMessage)" - }, - "messageTypeLabels": { - "all": "All messages", - "group": "Group messages", - "friend": "Direct messages" - } - }, - "messages": { - "updateSuccess": "Update successful!", - "addSuccess": "Add successful!", - "deleteSuccess": "Delete successful!", - "statusUpdateSuccess": "Status update successful!", - "deleteConfirm": "Are you sure you want to delete platform adapter", - "configNotFoundOpenConfig": "Target config file not found. Opened the config page for review.", - "updateMissingPlatformId": "Update failed: missing platform ID.", - "platformUpdateFailed": "Platform update failed.", - "addSuccessWithConfig": "Platform added. Config file updated.", - "configIdMissing": "Unable to get config file ID.", - "routingUpdateFailed": "Failed to update routing table: {message}", - "createConfigFailed": "Failed to create config file: {message}", - "platformIdMissing": "Unable to get platform ID.", - "routingSaveFailed": "Failed to save routing table: {message}" - }, - "status": { - "enabled": "Enabled", - "disabled": "Disabled", - "connecting": "Connecting", - "connected": "Connected", - "disconnected": "Disconnected", - "error": "Error" - }, - "runtimeStatus": { - "running": "Running", - "error": "Error", - "pending": "Pending", - "stopped": "Stopped", - "unknown": "Unknown", - "errors": "error(s)" - }, - "platformQr": { - "title": "QR Login", - "show": "Show QR", - "status": "QR Status", - "waiting": "Waiting for QR", - "close": "Close" - }, - "registrationAction": { - "lark": { - "title": "One-click QR Setup", - "show": "One-click QR Setup", - "scanTitle": "Scan with Feishu on your phone", - "feishu": "Feishu China", - "lark": "Lark Global" - }, - "weixinOc": { - "title": "Personal WeChat QR Login", - "scanTitle": "Scan with WeChat on your phone", - "created": "Login Complete", - "status": { - "idle": "Not Started", - "starting": "Getting QR Code", - "pending": "Waiting for Scan", - "created": "Login Complete - remember to click the save button!", - "denied": "Canceled", - "expired": "Expired", - "error": "Login Failed" - } - }, - "dingtalk": { - "title": "DingTalk QR Setup", - "scanTitle": "Scan with DingTalk on your phone", - "created": "Setup Complete" - }, - "qqOfficial": { - "title": "QQ Official Bot QR Binding", - "scanTitle": "Scan with QQ on your phone", - "created": "Binding Complete", - "status": { - "idle": "Not Started", - "starting": "Getting QR Code", - "pending": "Waiting for Scan", - "created": "Binding Complete - remember to click the save button!", - "denied": "Canceled", - "expired": "Expired", - "error": "Binding Failed" - } - }, - "start": "Start Setup", - "created": "Setup Complete", - "startFailed": "Failed to start QR setup", - "pollFailed": "Failed to poll QR setup status", - "platformIdLabel": "Bot ID", - "platformIdRequired": "Bot ID is required", - "platformIdInvalid": "Bot ID cannot contain spaces, ':' or '!'", - "mode": { - "title": "Choose setup method", - "scan": "One-click QR setup", - "manual": "Manual setup", - "larkManual": "Manual setup (supports self-hosted Feishu)" - }, - "scanTitle": "Scan with Feishu on your phone", - "status": { - "idle": "Not Started", - "starting": "Starting", - "pending": "Waiting for Scan", - "slow_down": "Polling Slowed", - "created": "Setup Complete - remember to click the save button!", - "denied": "Canceled", - "expired": "Expired", - "error": "Setup Failed" - } - }, - "errorDialog": { - "title": "Error Details", - "platformId": "Platform ID", - "errorCount": "Error Count", - "lastError": "Last Error", - "occurredAt": "Occurred At", - "traceback": "Traceback", - "close": "Close" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/provider.json b/legacy-dashboard/src/i18n/locales/en-US/features/provider.json deleted file mode 100644 index 4dd1a1fe..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/provider.json +++ /dev/null @@ -1,165 +0,0 @@ -{ - "title": "Providers", - "subtitle": "Can configure chat models in \"Chat Completion\". Additionally, \"Agent Runner\" includes integrations with third-party services like Dify, Coze, and Alibaba Bailian(DashScope).", - "providers": { - "title": "Service Providers", - "settings": "Settings", - "addProvider": "Add Provider", - "providerType": "Provider Type", - "tabs": { - "all": "All", - "chatCompletion": "Chat Completion", - "agentRunner": "Agent Runner", - "speechToText": "Speech to Text", - "textToSpeech": "Text to Speech", - "embedding": "Embedding", - "rerank": "Rerank" - }, - "empty": { - "all": "No service providers available, click Add Provider to add one", - "typed": "No {type} type service providers available, click Add Provider to add one" - }, - "description": { - "openai": "Supports all OpenAI API-style providers.", - "kimi_code": "Dedicated Kimi CodingPlan / Kimi Code integration using a custom Anthropic API adapter.", - "default": "" - } - }, - "availability": { - "title": "Provider Availability", - "subtitle": "Determined by testing model conversation availability, may incur API costs", - "refresh": "Refresh Status", - "noData": "Click \"Refresh Status\" button to get service provider availability", - "available": "Available", - "unavailable": "Unavailable", - "pending": "Pending...", - "errorMessage": "Error Message", - "test": "Test" - }, - "logs": { - "title": "Service Logs", - "expand": "Expand", - "collapse": "Collapse" - }, - "dialogs": { - "addProvider": { - "title": "Service Provider", - "tabs": { - "basic": "Basic", - "agentRunner": "Agent Runner", - "speechToText": "Speech to Text", - "textToSpeech": "Text to Speech", - "embedding": "Embedding", - "rerank": "Rerank" - }, - "noTemplates": "No this type provider templates available" - }, - "config": { - "addTitle": "Add", - "editTitle": "Edit", - "provider": "Service Provider", - "cancel": "Cancel", - "save": "Save" - }, - "settings": { - "title": "Service Provider Settings", - "sessionSeparation": { - "title": "Enable Provider Session Isolation", - "description": "Different sessions can independently select text generation, TTS, STT and other service providers." - }, - "close": "Close" - } - }, - "messages": { - "success": { - "update": "Updated successfully!", - "add": "Added successfully!", - "delete": "Deleted successfully!", - "statusUpdate": "Status updated successfully!", - "sessionSeparation": "Session isolation settings updated" - }, - "error": { - "sessionSeparation": "Failed to get session isolation configuration", - "fetchStatus": "Failed to get service provider status", - "testError": "Test failed for {id}: {error}" - }, - "confirm": { - "delete": "Are you sure you want to delete service provider {id}?" - } - }, - "providerTypes": { - "title": "Provider Types" - }, - "providerSources": { - "title": "Provider Sources", - "add": "Add", - "delete": "Delete provider source", - "empty": "No provider sources", - "selectHint": "Please select a provider source", - "selectCreated": "Select created provider source", - "save": "Save Configuration", - "saveAndFetchModels": "Save and Fetch Models", - "fetchModels": "Fetch Model List", - "saveSuccess": "Provider source saved successfully", - "saveError": "Failed to save provider source", - "deleteConfirm": "Are you sure you want to delete provider source {id}? This will also delete all associated model configurations.", - "deleteSuccess": "Provider source deleted successfully", - "deleteError": "Failed to delete provider source", - "enabled": "Enabled", - "disabled": "Disabled", - "advancedConfig": "Advanced Configuration...", - "fields": { - "name": "Name", - "apiKey": "API Key", - "baseUrl": "Base URL" - }, - "hints": { - "id": "Provider source ID (not provider ID)", - "key": "API key for authentication", - "apiBase": "Custom API endpoint URL", - "proxy": "HTTP/HTTPS proxy address, e.g. http://127.0.0.1:7890. Only affects this provider's API requests, doesn't interfere with Docker internal networking." - }, - "labels": { - "proxy": "Proxy" - } - }, - "models": { - "title": "Models", - "available": "Available Models", - "configured": "Configured Models", - "empty": "No configured models yet. Click \"Fetch Models\" above to add.", - "noModelsFound": "No available models found", - "fetchError": "Failed to fetch models", - "addSuccess": "Model {model} added successfully", - "deleteConfirm": "Are you sure you want to delete model {id}?", - "deleteSuccess": "Model deleted successfully", - "deleteError": "Failed to delete model", - "testSuccess": "Model {id} test passed", - "testSuccessWithLatency": "Model {id} test passed, latency {latency} ms", - "testError": "Model test failed", - "searchPlaceholder": "Search models or ID", - "manualAddButton": "Custom Model", - "manualDialogTitle": "Add Custom Model", - "manualDialogModelLabel": "Model ID (e.g. gpt-4.1-mini)", - "manualDialogPreviewLabel": "Display ID (auto generated)", - "manualDialogPreviewHint": "Generated as sourceId/modelId", - "manualModelRequired": "Please enter a model ID", - "manualModelExists": "Model already exists", - "configure": "Configure", - "testButton": "Test model", - "metadata": { - "image": "Image input", - "audio": "Audio input", - "toolUse": "Tool use", - "reasoning": "Reasoning", - "context": "{tokens} context window", - "available": "Model metadata supports {capability}", - "enabled": "{capability} enabled", - "supportedDisabled": "Model metadata supports {capability}, but it is not enabled in this provider's modalities." - }, - "tooltips": { - "providerId": "Provider ID", - "modelId": "Model ID" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/session-management.json b/legacy-dashboard/src/i18n/locales/en-US/features/session-management.json deleted file mode 100644 index 839dc248..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/session-management.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "title": "Custom Rules", - "subtitle": "Set custom rules for specific sessions, which take priority over global settings", - "buttons": { - "refresh": "Refresh", - "edit": "Edit", - "editRule": "Edit Rules", - "deleteAllRules": "Delete All Rules", - "addRule": "Add Rule", - "save": "Save", - "cancel": "Cancel", - "delete": "Delete", - "clear": "Clear", - "next": "Next", - "editCustomName": "Edit Note", - "batchDelete": "Batch Delete" - }, - "customRules": { - "title": "Custom Rules", - "rulesCount": "rules", - "hasRules": "Configured", - "noRules": "No Custom Rules", - "noRulesDesc": "Click 'Add Rule' to configure custom rules for specific sessions", - "serviceConfig": "Service Config", - "pluginConfig": "Plugin Config", - "kbConfig": "Knowledge Base", - "providerConfig": "Provider Config", - "configured": "Configured", - "noCustomName": "No note set" - }, - "quickEditName": { - "title": "Edit Note" - }, - "search": { - "placeholder": "Search sessions..." - }, - "table": { - "headers": { - "umoInfo": "Unified Message Origin", - "rulesOverview": "Rules Overview", - "actions": "Actions" - } - }, - "persona": { - "none": "Follow Config" - }, - "provider": { - "followConfig": "Follow Config" - }, - "addRule": { - "title": "Add Custom Rule", - "description": "Select a session (UMO) to configure custom rules. Custom rules take priority over global settings.", - "selectUmo": "Select Session", - "noUmos": "No sessions available" - }, - "ruleEditor": { - "title": "Edit Custom Rules", - "description": "Configure custom rules for this session. These rules take priority over global settings.", - "serviceConfig": { - "title": "Service Configuration", - "sessionEnabled": "Enable Session", - "llmEnabled": "Enable LLM", - "ttsEnabled": "Enable TTS", - "customName": "Custom Name" - }, - "providerConfig": { - "title": "Provider Configuration", - "chatProvider": "Chat Provider", - "sttProvider": "STT Provider", - "ttsProvider": "TTS Provider" - }, - "personaConfig": { - "title": "Persona Configuration", - "selectPersona": "Select Persona", - "hint": "Persona settings affect the conversation style and behavior of the LLM" - }, - "pluginConfig": { - "title": "Plugin Configuration", - "disabledPlugins": "Disabled Plugins", - "hint": "Select plugins to disable for this session. Unselected plugins will remain enabled." - }, - "kbConfig": { - "title": "Knowledge Base Configuration", - "selectKbs": "Select Knowledge Bases", - "topK": "Top K Results", - "enableRerank": "Enable Reranking" - } - }, - "deleteConfirm": { - "title": "Confirm Delete", - "message": "Are you sure you want to delete all custom rules for this session? Global settings will be used after deletion." - }, - "batchDeleteConfirm": { - "title": "Confirm Batch Delete", - "message": "Are you sure you want to delete {count} selected rules? Global settings will be used after deletion." - }, - "batchOperations": { - "title": "Batch Operations", - "hint": "Quick batch modify session settings", - "scope": "Apply to", - "scopeSelected": "Selected sessions", - "scopeAll": "All sessions", - "scopeGroup": "All groups", - "scopePrivate": "All private chats", - "llmStatus": "LLM Status", - "ttsStatus": "TTS Status", - "chatProvider": "Chat Model", - "ttsProvider": "TTS Model", - "apply": "Apply Changes" - }, - "groups": { - "title": "Group Management", - "count": "{count} groups", - "addToGroup": "Add to Group", - "create": "Create Group", - "edit": "Edit Group", - "name": "Group Name", - "sessionsCount": "{count} sessions", - "empty": "No groups yet. Click 'Create Group' to create one.", - "availableSessions": "Available Sessions ({count})", - "selectedSessions": "Selected Sessions ({count})", - "searchPlaceholder": "Search...", - "noMatch": "No matches", - "noMembers": "No members", - "customGroupDivider": "── Custom Groups ──", - "customGroupOption": "📁 {name} ({count})", - "groupOption": "{name} ({count} sessions)", - "deleteConfirm": "Are you sure you want to delete group \"{name}\"?" - }, - "status": { - "enabled": "Enabled", - "disabled": "Disabled" - }, - "messages": { - "refreshSuccess": "Data refreshed", - "loadError": "Failed to load data", - "saveSuccess": "Saved successfully", - "saveError": "Failed to save", - "clearSuccess": "Cleared successfully", - "clearError": "Failed to clear", - "deleteSuccess": "Deleted successfully", - "deleteError": "Failed to delete", - "noChanges": "No changes to save", - "batchDeleteSuccess": "Batch delete successful", - "batchDeleteError": "Batch delete failed", - "selectSessionsFirst": "Please select sessions first", - "selectAtLeastOneConfig": "Please select at least one setting to modify", - "batchUpdateSuccess": "Batch update successful", - "partialUpdateFailed": "Some updates failed", - "batchUpdateError": "Batch update failed", - "groupNameRequired": "Group name cannot be empty", - "saveGroupError": "Failed to save group", - "deleteGroupError": "Failed to delete group", - "selectSessionsToAddFirst": "Please select sessions to add first", - "addToGroupSuccess": "Added {count} sessions to the group", - "addToGroupError": "Failed to add to group" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/settings.json b/legacy-dashboard/src/i18n/locales/en-US/features/settings.json deleted file mode 100644 index e7427f1e..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/settings.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "page": { - "title": "Settings" - }, - "sections": { - "general": { - "title": "General" - }, - "appearance": { - "title": "Appearance" - }, - "network": { - "title": "Network" - }, - "security": { - "title": "Security" - }, - "maintenance": { - "title": "Maintenance" - }, - "openapi": { - "title": "OpenAPI" - } - }, - "systemConfig": { - "restartRequired": "System config saved. Some changes only take effect after restarting AstrBot.", - "groups": { - "runtime": { - "title": "Runtime Basics", - "subtitle": "Timezone and externally reachable callback URL." - }, - "network": { - "title": "Proxy & Dependency Sources", - "subtitle": "Runtime proxy, Python dependency install options, and PyPI source." - }, - "webuiSecurity": { - "title": "WebUI Security", - "subtitle": "HTTPS, trusted proxy headers, login rate limits, and TOTP." - }, - "logs": { - "title": "Logs", - "subtitle": "Console logs, file logs, and Trace file logging." - }, - "tempStorage": { - "title": "Cache", - "subtitle": "Automatic cleanup threshold for the temporary directory." - }, - "t2iRendering": { - "title": "Text-to-Image", - "subtitle": "Rendering strategy, endpoint, and custom templates for text-to-image." - } - }, - "messages": { - "loadFailed": "Failed to load system config", - "saveSuccess": "System config saved", - "saveFailed": "Failed to save system config" - } - }, - "network": { - "title": "Network", - "githubProxy": { - "title": "GitHub Proxy Address", - "subtitle": "Set the GitHub proxy address used when downloading plugins or updating AstrBot. This is effective in mainland China's network environment. Can be customized, input takes effect in real time. All addresses do not guarantee stability. If errors occur when updating plugins/projects, please first check if the proxy address is working properly.", - "label": "Select GitHub Proxy Address" - }, - "proxySelector": { - "title": "GitHub Proxy", - "noProxy": "Don't use GitHub Proxy", - "useProxy": "Use GitHub Proxy", - "testConnection": "Test Connection", - "available": "Available", - "unavailable": "Unavailable", - "custom": "Custom" - } - }, - "theme": { - "title": "Theme", - "subtitle": "Customize theme primary and secondary colors. Changes apply immediately and are stored locally in your browser.", - "customize": { - "title": "Theme Colors", - "primary": "Primary Color", - "secondary": "Secondary Color", - "reset": "Reset to Default" - } - }, - "system": { - "title": "System", - "restart": { - "title": "Restart", - "subtitle": "Restart AstrBot", - "confirm": "Restart AstrBot now? The WebUI may be temporarily unavailable during restart.", - "button": "Restart" - }, - "backup": { - "title": "Backup & Restore", - "subtitle": "Export or import all AstrBot data for easy migration to a new server", - "button": "Backup Manager" - }, - "cleanup": { - "title": "Log & Cache Cleanup", - "subtitle": "Review disk usage for logs and caches, then clean them from the UI without shell commands.", - "refresh": "Refresh Usage", - "clean": "Clean", - "cleanAll": "Clean All", - "panel": { - "title": "Cleanup Details", - "subtitle": "Current usage: {size}" - }, - "fileCount": "{count} files", - "confirm": "Clean {target} now?", - "targetNames": { - "cache": "cache", - "logs": "logs", - "all": "logs and cache" - }, - "targets": { - "cache": { - "title": "Cache", - "subtitle": "Remove temporary files, plugin market cache, and skill cache.", - "button": "Clean Cache" - }, - "logs": { - "title": "Logs", - "subtitle": "Remove rotated logs and truncate the current active log files.", - "button": "Clean Logs" - } - }, - "messages": { - "statusFailed": "Failed to load storage usage", - "cleanupSuccess": "Cleared {count} files and freed {size}", - "cleanupFailed": "Cleanup failed" - } - } - }, - "sidebar": { - "title": "Sidebar", - "customize": { - "title": "Customize Sidebar", - "subtitle": "Drag to reorder modules, or move modules in/out of the \"More Features\" group. Settings are saved locally in your browser.", - "reset": "Reset to Default", - "mainItems": "Main Modules", - "moreItems": "More Features" - } - }, - "backup": { - "dialog": { - "title": "Backup Manager" - }, - "tabs": { - "export": "Export Backup", - "import": "Import Backup", - "list": "Backup List" - }, - "export": { - "title": "Create Backup", - "description": "Export all data as a ZIP backup file, including database, knowledge base, config, attachments and skills.", - "includes": "Backup includes: Main database, Knowledge bases (metadata + vector index + documents), Config files, Attachment files, Skills", - "button": "Start Export", - "processing": "Exporting...", - "wait": "Please wait, packaging data...", - "completed": "Export Completed!", - "download": "Download Backup", - "another": "Create New Backup", - "failed": "Export Failed", - "retry": "Retry" - }, - "import": { - "title": "Import Backup", - "warning": "⚠️ Import will clear and overwrite existing data! Please make sure you have backed up your current data.", - "selectFile": "Select backup file (.zip)", - "uploadAndCheck": "Upload & Check", - "uploading": "Uploading...", - "uploadWait": "Please wait, uploading backup file...", - "uploadInit": "Initializing upload...", - "uploadingChunks": "Uploading chunks...", - "uploadComplete": "Upload complete, merging file...", - "checking": "Checking backup file...", - "invalidBackup": "Invalid backup file", - "backupContents": "Backup Contents", - "tables": "tables", - "knowledgeBases": "Knowledge Bases", - "configFiles": "Config Files", - "confirmImport": "Confirm Import", - "button": "Start Import", - "processing": "Importing...", - "wait": "Please wait, restoring data...", - "completed": "Import Completed!", - "restartRequired": "Data has been successfully imported. It is recommended to restart AstrBot immediately for all changes to take effect.", - "restartNow": "Restart Now", - "failed": "Import Failed", - "retry": "Retry", - "version": { - "backupVersion": "Backup Version", - "currentVersion": "Current Version", - "backupTime": "Backup Time", - "matchTitle": "✅ Version Match", - "matchMessage": "Import will clear and overwrite all existing data, including:\n• Main database (conversations, settings, etc.)\n• Knowledge bases\n• Plugins and plugin data\n• Configuration files\n• Skills\n\nThis action cannot be undone! Do you want to continue?", - "minorDiffTitle": "⚠️ Version Difference Warning", - "minorDiffMessage": "Minor version differences can usually be imported, but there may be some data structure changes.\nImport will clear and overwrite all existing data!\n\nDo you want to continue?", - "majorDiffTitle": "⛔ Cannot Import", - "majorDiffMessage": "Major version numbers are different. Cross-major-version import may cause data corruption.\nPlease use the same major version of AstrBot for import." - } - }, - "list": { - "empty": "No backup files", - "refresh": "Refresh List", - "confirmDelete": "Are you sure you want to delete this backup file? This action cannot be undone.", - "uploaded": "Uploaded", - "restore": "Restore this backup", - "rename": "Rename", - "renameTitle": "Rename Backup File", - "newName": "New Filename", - "renameHint": "Filename can only contain letters, numbers, underscores, hyphens and dots", - "renameRequired": "Please enter a filename", - "renameInvalidChars": "Filename contains invalid characters", - "renameFailed": "Rename failed", - "ftpHint": "For large backup files, you can also upload directly to the data/backups directory via FTP/SFTP" - } - }, - "apiKey": { - "title": "API Keys", - "manageTitle": "Developer Access Keys", - "subtitle": "Create API keys for external developers to call open HTTP APIs.", - "name": "Key Name", - "expiresInDays": "Expiration", - "expiryOptions": { - "day1": "1 day", - "day7": "7 days", - "day30": "30 days", - "day90": "90 days", - "permanent": "Permanent" - }, - "permanentWarning": "Permanent API keys are high risk. Store them securely and use only when necessary.", - "scopes": "Scopes", - "create": "Create API Key", - "revoke": "Revoke", - "delete": "Delete", - "copy": "Copy", - "docsLink": "Open docs", - "plaintextHint": "Save this key now. The plaintext will not be shown again.", - "empty": "No API keys", - "status": { - "active": "Active", - "inactive": "Inactive" - }, - "table": { - "name": "Name", - "prefix": "Prefix", - "scopes": "Scopes", - "status": "Status", - "lastUsed": "Last Used", - "createdAt": "Created At", - "actions": "Actions" - }, - "messages": { - "loadFailed": "Failed to load API keys", - "scopeRequired": "Please select at least one scope", - "createSuccess": "API key created", - "createFailed": "Failed to create API key", - "revokeSuccess": "API key revoked", - "revokeFailed": "Failed to revoke API key", - "deleteSuccess": "API key deleted", - "deleteFailed": "Failed to delete API key", - "copySuccess": "API key copied", - "copyFailed": "Failed to copy API key" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/stats.json b/legacy-dashboard/src/i18n/locales/en-US/features/stats.json deleted file mode 100644 index 18c2e281..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/stats.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "header": { - "eyebrow": "Dashboard", - "title": "System Statistics", - "subtitle": "A unified view of platforms, messages, and model calls.", - "notUpdated": "Not updated yet" - }, - "ranges": { - "oneDay": "1 Day", - "threeDays": "3 Days", - "oneWeek": "1 Week" - }, - "rangeLabels": { - "oneDay": "Last 1 day", - "threeDays": "Last 3 days", - "oneWeek": "Last 1 week" - }, - "overviewCards": { - "platformCount": { - "label": "Platform Instances", - "note": "Configured and currently running platform instances" - }, - "messageCount": { - "label": "Messages", - "note": "Total accumulated messages" - }, - "todayModelCalls": { - "label": "Today's Model Calls", - "note": "Tokens" - }, - "cpu": { - "label": "CPU", - "note": "AstrBot CPU usage" - }, - "memory": { - "label": "Process Memory", - "note": "System memory {systemMemory}" - }, - "uptime": { - "label": "Uptime", - "note": "Started at {startTime}" - } - }, - "messageOverview": { - "title": "Message Overview", - "subtitle": "Robot message statistics" - }, - "messageTrend": { - "title": "Message Trend", - "subtitle": "{range}, aggregated by hour", - "totalMessages": "Total Messages" - }, - "platformRanking": { - "title": "Platform Message Ranking", - "subtitle": "{range}, sorted by aggregated message volume" - }, - "modelCalls": { - "title": "Model Calls", - "subtitle": "Model call statistics" - }, - "modelTrend": { - "title": "Model Call Trend", - "subtitle": "Token trend for model calls" - }, - "modelTotal": { - "title": "{range} total model calls", - "callCount": "{count} calls", - "avgTtft": "Average TTFT", - "avgDuration": "Average Response Time", - "avgTpm": "Average Output TPM", - "successRate": "Success Rate" - }, - "modelRanking": { - "title": "{range} model call ranking", - "subtitle": "Ranked by model call tokens" - }, - "sessionRanking": { - "title": "{range} session token top 10", - "subtitle": "Sessions ranked by token usage" - }, - "empty": { - "platformStats": "No platform message statistics available.", - "modelCalls": "No model call data for {range}.", - "sessionCalls": "No session call data for {range}." - }, - "chart": { - "messages": "Messages", - "others": "Others" - }, - "units": { - "tokens": "Tokens", - "tpm": "TPM", - "gb": "GB", - "mb": "MB", - "ms": "ms", - "hoursShort": "h", - "minutesShort": "m", - "secondsShort": "s" - }, - "errors": { - "loadFailed": "Failed to load statistics. Please try again later.", - "rangeFailed": "Failed to switch the statistics range. Please try again later." - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/subagent.json b/legacy-dashboard/src/i18n/locales/en-US/features/subagent.json deleted file mode 100644 index e9ea127f..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/subagent.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "header": { - "eyebrow": "Orchestration" - }, - "page": { - "title": "SubAgent Orchestration", - "beta": "Experimental", - "subtitle": "The main LLM can use its own tools directly and delegate tasks to SubAgents via handoff." - }, - "actions": { - "refresh": "Refresh", - "save": "Save", - "add": "Add SubAgent", - "expand": "Expand", - "collapse": "Collapse", - "delete": "Delete", - "close": "Close" - }, - "overview": { - "totalAgents": "Total SubAgents", - "totalAgentsNote": "Configured delegate agents", - "enabledAgents": "Enabled Agents", - "enabledAgentsNote": "Agents available for handoff", - "mainOrchestration": "Main Orchestration", - "boundPersonas": "Bound Personas", - "boundPersonasNote": "Agents with an attached persona" - }, - "switches": { - "enable": "Enable SubAgent orchestration", - "enableHint": "Enable sub-agent functionality", - "dedupe": "Deduplicate main LLM tools (hide tools duplicated by SubAgents)", - "dedupeHint": "Remove duplicate tools from main agent" - }, - "description": { - "disabled": "When off: SubAgent is disabled; the main LLM mounts tools via persona rules (all by default) and calls them directly.", - "enabled": "When on: the main LLM keeps its own tools and mounts transfer_to_* delegate tools. With deduplication, tools overlapping with SubAgents are removed from the main tool set." - }, - "section": { - "title": "SubAgents", - "subtitle": "Configure delegate agents, personas, and descriptions for the main LLM", - "globalSettings": "Global Settings", - "agentSetup": "Agent Setup" - }, - "cards": { - "statusEnabled": "Enabled", - "statusDisabled": "Disabled", - "unnamed": "Untitled SubAgent", - "transferPrefix": "transfer_to_{name}", - "switchLabel": "Enable", - "previewTitle": "Preview: handoff tool shown to the main LLM", - "personaChip": "Persona: {id}", - "personaPreview": "Persona Preview", - "noDescription": "No description yet", - "previewHint": "Review the currently selected persona to verify the handoff target." - }, - "form": { - "nameLabel": "Agent name (used for transfer_to_{name})", - "nameHint": "Use lowercase letters + underscores; must be globally unique.", - "providerLabel": "Chat Provider (optional)", - "providerHint": "Leave empty to follow the global default provider.", - "personaLabel": "Choose Persona", - "personaHint": "The SubAgent inherits the selected Persona's system settings and tools.", - "descriptionLabel": "Description for the main LLM (used to decide handoff)", - "descriptionHint": "Shown to the main LLM as the transfer_to_* tool description—keep it short and clear." - }, - "messages": { - "loadConfigFailed": "Failed to load config", - "loadPersonaFailed": "Failed to load persona list", - "unsavedChangesNotice": "You have unsaved changes on this page. Save before leaving.", - "unsavedChangesLeaveConfirm": "You have unsaved changes. Leaving will discard them. Continue?", - "unsavedChangesReloadConfirm": "You have unsaved changes. Reloading will discard them. Continue?", - "nameMissing": "A SubAgent is missing a name", - "nameInvalid": "Invalid SubAgent name: only lowercase letters/numbers/underscores, starting with a letter", - "nameDuplicate": "Duplicate SubAgent name: {name}", - "personaMissing": "SubAgent {name} has no persona selected", - "saveSuccess": "Saved successfully", - "saveFailed": "Failed to save", - "nameRequired": "Name is required", - "namePattern": "Lowercase letters, numbers, underscore only" - }, - "empty": { - "title": "No Agents Configured", - "subtitle": "Add a new sub-agent to get started", - "action": "Create First Agent" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/tool-use.json b/legacy-dashboard/src/i18n/locales/en-US/features/tool-use.json deleted file mode 100644 index 52c27bbf..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/tool-use.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "title": "Function Tool Management", - "subtitle": "Manage MCP servers and view available function tools", - "tooltip": { - "info": "What are Function Calling and MCP?", - "marketplace": "Browse and install MCP servers from the community", - "serverConfig": "MCP server (stdio) configuration supports the following fields:\ncommand: Command name (e.g. python or uv)\nargs: Command arguments array (e.g. [\"run\", \"server.py\"])\nenv: Environment variables object (e.g. {\"api_key\": \"abc\"})\ncwd: Working directory path (e.g. /path/to/server)\nencoding: Output encoding (default utf-8)\nencoding_error_handler: The text encoding error handler. Defaults to strict.\nOther fields please refer to MCP documentation\n⚠️ If you deploy AstrBot using Docker, make sure to install MCP servers in the data directory mounted by AstrBot" - }, - "tabs": { - "local": "Local Servers", - "marketplace": "MCP Marketplace" - }, - "mcpServers": { - "title": "MCP Servers", - "description": "Manage MCP servers", - "buttons": { - "refresh": "Refresh", - "add": "Add Server", - "useTemplateStdio": "Stdio Template", - "useTemplateStreamableHttp": "Streamable HTTP Template", - "useTemplateSse": "SSE Template", - "sync": "Sync MCP Servers" - }, - "empty": "No MCP servers available, click Add Server to add one", - "status": { - "noTools": "No available tools", - "availableTools": "Available tools", - "configSummary": "Config: {keys}", - "noConfig": "No configuration set" - } - }, - "functionTools": { - "title": "Function Tools", - "buttons": { - "view": "View Tools" - }, - "search": "Search function tools", - "empty": "No function tools available", - "description": "Function Description", - "parameters": "Parameter List", - "noParameters": "This tool has no parameters", - "filter": { - "showBuiltin": "Show builtin tools" - }, - "table": { - "paramName": "Parameter Name", - "type": "Type", - "description": "Description", - "required": "Required", - "origin": "Origin", - "originName": "Origin Name", - "readonly": "Read-only", - "permission": "Permission", - "permissionAdmin": "Admin", - "permissionMember": "All Users", - "permissionEveryone": "All Users", - "permissionBuiltin": "System Builtin", - "actions": "Actions" - }, - "configTags": { - "tooltipTitle": "This tool is enabled in config file {config} because:", - "conditions": { - "truthy": "{key} is enabled", - "equals": "{key} = {expected}", - "in": "{key} matched {expected}", - "fallback": "{key} is currently {actual}" - } - }, - "summary": { - "total": "Total", - "active": "Active", - "inactive": "Inactive" - } - }, - "marketplace": { - "title": "MCP Server Marketplace", - "search": "Search servers", - "buttons": { - "refresh": "Refresh", - "detail": "Details", - "import": "Import" - }, - "loading": "Loading MCP server marketplace...", - "empty": "No MCP servers available", - "status": { - "availableTools": "Available tools ({count})", - "noToolsInfo": "No tool information available" - } - }, - "dialogs": { - "addServer": { - "title": "Add MCP Server", - "editTitle": "Edit MCP Server", - "fields": { - "name": "Server Name", - "nameRequired": "Name is required", - "enable": "Enable Server", - "config": "Server Configuration" - }, - "errors": { - "configEmpty": "Configuration cannot be empty", - "jsonFormat": "JSON format error: {error}", - "jsonParse": "JSON parse error: {error}" - }, - "buttons": { - "cancel": "Cancel", - "save": "Save", - "testConnection": "Test Connection", - "sync": "Sync" - }, - "tips": { - "timeoutConfig": "Please configure tool call timeout separately in the configuration page", - "transportRecommendation": "Prefer Streamable HTTP or SSE mode. Stdio mode starts a local process on the AstrBot host and should only be used for trusted MCP servers." - } - }, - "serverDetail": { - "title": "Server Details", - "installConfig": "Installation Configuration", - "availableTools": "Available Tools", - "buttons": { - "close": "Close", - "importConfig": "Import Configuration" - } - }, - "confirmDelete": "Are you sure you want to delete server {name}?" - }, - "syncProvider": { - "title": "Sync MCP Servers", - "subtitle": "Sync MCP server configurations from providers to local", - "steps": { - "selectProvider": "Step 1: Select Provider", - "configureAuth": "Step 2: Configure Authentication", - "syncServers": "Step 3: Sync Servers" - }, - "providers": { - "modelscope": "ModelScope", - "description": "ModelScope is an open model community providing MCP servers for various machine learning and AI services" - }, - "fields": { - "provider": "Select Provider", - "accessToken": "Access Token", - "tokenRequired": "Access token is required", - "tokenHint": "Please enter your ModelScope access token" - }, - "buttons": { - "cancel": "Cancel", - "previous": "Previous", - "next": "Next", - "sync": "Start Sync", - "getToken": "Get Token" - }, - "status": { - "selectProvider": "Please select an MCP server provider", - "enterToken": "Please enter the access token to continue", - "readyToSync": "Ready to sync server configurations" - }, - "messages": { - "syncSuccess": "MCP servers synced successfully!", - "syncError": "Sync failed: {error}", - "tokenHelp": "How to get a ModelScope access token? Click the button on the right for instructions" - } - }, - "messages": { - "getServersError": "Failed to get MCP server list: {error}", - "getToolsError": "Failed to get function tools list: {error}", - "saveSuccess": "Save successful!", - "saveError": "Save failed: {error}", - "deleteSuccess": "Delete successful!", - "deleteError": "Delete failed: {error}", - "updateSuccess": "Update successful!", - "updateError": "Update failed: {error}", - "getMarketError": "Failed to get MCP marketplace server list: {error}", - "importError": { - "noConfig": "This server has no available configuration", - "invalidFormat": "Server configuration format is incorrect", - "failed": "Import configuration failed: {error}" - }, - "configParseError": "Configuration parse error: {error}", - "noAvailableConfig": "No available configuration", - "toggleToolSuccess": "Tool status toggled successfully!", - "toggleToolReadonly": "Builtin tools are read-only and cannot be enabled or disabled.", - "toggleToolError": "Failed to toggle tool status: {error}", - "updateToolPermissionSuccess": "Permission for {name} updated", - "updateToolPermissionFailed": "Failed to update tool permission", - "updateToolPermissionBuiltin": "Builtin tools do not support per-tool permission configuration.", - "testError": "Test connection failed: {error}" - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/trace.json b/legacy-dashboard/src/i18n/locales/en-US/features/trace.json deleted file mode 100644 index 00b8ab44..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/trace.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Trace", - "autoScroll": { - "enabled": "Auto-scroll: On", - "disabled": "Auto-scroll: Off" - }, - "hint": "Currently only recording partial model call paths from AstrBot main Agent. More coverage will be added.", - "recording": "Recording", - "paused": "Paused" -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/features/welcome.json b/legacy-dashboard/src/i18n/locales/en-US/features/welcome.json deleted file mode 100644 index e9b933c2..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/features/welcome.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "greeting": { - "morning": "Good morning, welcome to AstrBot", - "afternoon": "Good afternoon, welcome to AstrBot", - "evening": "Good evening, welcome to AstrBot", - "newYear": "Happy New Year!" - }, - "subtitle": "You can complete the basic onboarding first. Platform and chat provider setup can both be skipped.", - "announcement": { - "title": "Announcement" - }, - "onboard": { - "title": "Quick Onboarding", - "subtitle": "Complete initialization directly on the welcome page.", - "step1Title": "Configure AI Model", - "step1Desc": "Configure AI models for AstrBot.", - "step2Title": "Configure Platform Bot", - "step2Desc": "Connect AstrBot to IM platforms like QQ, Lark, Slack, Telegram, etc.", - "step3Title": "Allow Agent to Use the Computer", - "step3Desc": "Set whether the Agent can access and use this computer.", - "step3HelpTitle": "Access Details", - "step3HelpItem1": "The Agent can access and use the workspace directory, which is isolated for each user, as well as Skills to handle more complex tasks.", - "step3HelpItem2": "For AstrBot administrators, the Agent is additionally allowed to execute shell commands, run Python code, and access all local directories on this machine.", - "step3HelpClose": "Close", - "step3SelectLabel": "Computer Access", - "step3Allow": "Allow", - "step3Deny": "Disallow", - "configure": "Configure", - "save": "Save", - "skip": "Skip", - "pending": "Pending", - "completed": "Completed", - "skipped": "Skipped", - "platformLoadFailed": "Failed to load platform configuration", - "providerLoadFailed": "Failed to load provider configuration", - "providerUpdateFailed": "Failed to update default chat provider in config file \"default\"", - "providerDefaultUpdated": "Default chat provider in config file \"default\" has been set to {id}", - "computerAccessUpdateFailed": "Failed to update Agent computer access", - "computerAccessAllowed": "Agent is now allowed to access and use the computer", - "computerAccessDenied": "Agent is no longer allowed to access and use the computer", - "step3HelpItem3": "For more detailed control, configure the Computer Access options under Settings -> General -> Computer Access." - }, - "resources": { - "title": "Resources", - "githubDesc": "Give us a Star!", - "docsTitle": "Documentation", - "docsDesc": "Read the official AstrBot documentation.", - "afdianTitle": "Afdian", - "afdianDesc": "Support the AstrBot team on Afdian." - } -} diff --git a/legacy-dashboard/src/i18n/locales/en-US/messages/errors.json b/legacy-dashboard/src/i18n/locales/en-US/messages/errors.json deleted file mode 100644 index 7572c447..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/messages/errors.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "network": { - "timeout": "Network request timeout, please try again later", - "connection": "Network connection failed, please check your network", - "server": "Server error, please contact technical support", - "unavailable": "Service temporarily unavailable", - "forbidden": "Access denied" - }, - "validation": { - "required": "This field is required", - "invalid": "Invalid input format", - "tooLong": "Input is too long", - "tooShort": "Input is too short", - "email": "Please enter a valid email address", - "url": "Please enter a valid URL", - "number": "Please enter a valid number" - }, - "auth": { - "unauthorized": "Unauthorized access, please login again", - "forbidden": "Insufficient permissions to perform this operation", - "tokenExpired": "Login expired, please login again", - "invalidCredentials": "Invalid username or password" - }, - "file": { - "uploadFailed": "File upload failed", - "invalidFormat": "Unsupported file format", - "tooLarge": "File size exceeds limit", - "notFound": "File not found" - }, - "operation": { - "failed": "Operation failed", - "cancelled": "Operation cancelled", - "notSupported": "Operation not supported", - "conflict": "Operation conflict, please try again later" - }, - "browser": { - "audioNotSupported": "Your browser does not support audio playback." - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/messages/success.json b/legacy-dashboard/src/i18n/locales/en-US/messages/success.json deleted file mode 100644 index a55d5005..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/messages/success.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "operation": { - "saved": "Save Successful", - "created": "Create Successful", - "updated": "Update Successful", - "deleted": "Delete Successful", - "uploaded": "Upload Successful", - "downloaded": "Download Successful", - "imported": "Import Successful", - "exported": "Export Successful", - "copied": "Copy Successful", - "sent": "Send Successful" - }, - "connection": { - "connected": "Connection Successful", - "authenticated": "Login Successful", - "synchronized": "Synchronization Successful" - }, - "validation": { - "valid": "Validation Passed", - "completed": "Operation Completed" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/en-US/messages/validation.json b/legacy-dashboard/src/i18n/locales/en-US/messages/validation.json deleted file mode 100644 index c9e9a172..00000000 --- a/legacy-dashboard/src/i18n/locales/en-US/messages/validation.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "required": "This field is required", - "email": "Please enter a valid email address", - "url": "Please enter a valid URL", - "number": "Please enter a valid number", - "min": "Minimum value is {min}", - "max": "Maximum value is {max}", - "minLength": "Minimum length is {length} characters", - "maxLength": "Maximum length is {length} characters", - "pattern": "Invalid format", - "unique": "This value already exists", - "confirm": "The two entries do not match", - "fileSize": "File size cannot exceed {size}MB", - "fileType": "Unsupported file type", - "required_field": "Please fill in the required field", - "invalid_format": "Invalid format", - "password_too_short": "Password must be at least 8 characters", - "password_too_weak": "Password is too weak", - "invalid_phone": "Please enter a valid phone number", - "invalid_date": "Please enter a valid date", - "date_range": "Invalid date range", - "upload_failed": "File upload failed", - "network_error": "Network connection error, please try again", - "operation_cannot_be_undone": "⚠️ This operation cannot be undone, please choose carefully!" -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/core/actions.json b/legacy-dashboard/src/i18n/locales/ru-RU/core/actions.json deleted file mode 100644 index 9e91e114..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/core/actions.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "create": "Создать", - "read": "Чтение", - "update": "Обновить", - "delete": "Удалить", - "search": "Поиск", - "filter": "Фильтр", - "sort": "Сортировка", - "export": "Экспорт", - "import": "Импорт", - "backup": "Резервное копирование", - "restore": "Восстановление", - "copy": "Копировать", - "paste": "Вставить", - "cut": "Вырезать", - "undo": "Отменить", - "redo": "Повторить", - "refresh": "Обновить", - "submit": "Отправить", - "reset": "Сбросить", - "clear": "Очистить", - "save": "Сохранить", - "close": "Закрыть" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/core/common.json b/legacy-dashboard/src/i18n/locales/ru-RU/core/common.json deleted file mode 100644 index 2816f9fc..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/core/common.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "save": "Сохранить", - "cancel": "Отмена", - "close": "Закрыть", - "copy": "Копировать", - "copied": "Скопировано", - "copyFailed": "Ошибка копирования", - "delete": "Удалить", - "edit": "Редактировать", - "add": "Добавить", - "confirm": "Подтвердить", - "loading": "Загрузка...", - "success": "Успешно", - "error": "Ошибка", - "warning": "Внимание", - "info": "Информация", - "name": "Имя", - "description": "Описание", - "author": "Автор", - "status": "Статус", - "actions": "Действия", - "enable": "Включить", - "disable": "Выключить", - "enabled": "Включено", - "disabled": "Выключено", - "reload": "Перезагрузить", - "configure": "Настроить", - "install": "Установить", - "uninstall": "Удалить", - "update": "Обновить", - "language": "Язык", - "settings": "Настройки", - "locale": "JSON", - "type": "Тип", - "press": "Нажмите", - "longPress": "Долгое нажатие", - "yes": "Да", - "no": "Нет", - "imagePreview": "Предпросмотр изображения", - "autoDetect": "Автоопределение", - "dialog": { - "confirmTitle": "Подтверждение", - "confirmMessage": "Вы уверены, что хотите выполнить это действие?", - "confirmButton": "ОК", - "cancelButton": "Отмена" - }, - "restart": { - "waiting": "Ожидание перезагрузки AstrBot...", - "maxRetriesReached": "Превышено количество попыток проверки статуса. Пожалуйста, проверьте вручную." - }, - "upgradeRecovery": { - "title": "Требуется завершить обновление", - "description": "WebUI обновлен до {dashboardVersion}, но AstrBot Core все еще {coreVersion}. Обычно это означает, что процесс перезапуска был прерван обновлением страницы во время обновления. Если перезапуск не решит проблему, попробуйте удалить папку data/dist в runtime-директории AstrBot, затем перезапустите AstrBot.", - "hint": "Перезапустите backend, чтобы завершить обновление. Страница автоматически обновится после восстановления AstrBot.", - "restartButton": "Перезапустить backend", - "laterButton": "Позже", - "restarting": "Запрашивается перезапуск backend...", - "waiting": "Перезапуск запрошен. Ожидание восстановления backend...", - "failed": "Не удалось отправить запрос на перезапуск. Перезапустите AstrBot вручную." - }, - "readme": { - "title": "Документация плагина", - "buttons": { - "viewOnGithub": "Открыть репозиторий на GitHub", - "refresh": "Обновить" - }, - "loading": "Загрузка README...", - "errors": { - "fetchFailed": "Не удалось загрузить README", - "fetchError": "Произошла ошибка при загрузке README" - }, - "empty": { - "title": "У этого плагина нет ссылки на документацию или репозиторий GitHub.", - "subtitle": "Пожалуйста, посетите магазин плагинов или свяжитесь с автором для получения дополнительной информации." - } - }, - "changelog": { - "title": "Журнал изменений", - "loading": "Загрузка журнала изменений...", - "empty": { - "title": "У этого плагина нет журнала изменений", - "subtitle": "Разработчики могут добавить файл CHANGELOG.md в директорию плагина" - } - }, - "editor": { - "fullscreen": "На весь экран", - "editingTitle": "Редактирование содержимого" - }, - "templateList": { - "addEntry": "Добавить запись", - "empty": "Записей нет, выберите шаблон для добавления", - "missingTemplate": "Шаблон не найден, пожалуйста, удалите и добавьте заново.", - "unknownTemplate": "Неизвестный шаблон" - }, - "list": { - "addItemPlaceholder": "Добавьте новый элемент и нажмите Enter", - "addButton": "Добавить", - "addMore": "Добавить еще", - "batchImport": "Массовый импорт", - "batchImportTitle": "Массовый импорт", - "batchImportLabel": "Один элемент на строку", - "batchImportPlaceholder": "Например:\nЭлемент 1\nЭлемент 2\nЭлемент 3", - "batchImportHint": "Каждая строка будет считаться отдельным элементом. Пустые строки игнорируются.", - "batchImportButton": "Импортировать {count} эл.", - "noItems": "Список пуст", - "noItemsHint": "Элементов нет. Напишите что-нибудь выше и нажмите Enter.", - "inputPlaceholder": "Введите текст и нажмите Enter", - "editTitle": "Изменить элемент", - "modifyButton": "Изменить" - }, - "itemCard": { - "enabled": "Включено", - "disabled": "Выключено", - "delete": "Удалить", - "edit": "Изменить", - "copy": "Копировать", - "noData": "Нет данных" - }, - "objectEditor": { - "dialogTitle": "Изменение пар ключ-значение", - "noItems": "Нет элементов", - "noParams": "Нет параметров", - "presets": "Пресеты", - "newKeyLabel": "Имя ключа", - "valueTypeLabel": "Тип значения", - "keyExists": "Ключ уже существует", - "invalidJson": "Некорректный формат JSON", - "placeholders": { - "keyName": "Ключ", - "stringValue": "Строка", - "numberValue": "Число", - "jsonValue": "JSON" - } - }, - "firstNotice": { - "title": "Первичная информация", - "loading": "Загрузка информации...", - "empty": { - "title": "Нет информации для отображения", - "subtitle": "Файл FIRST_NOTICE.md не найден или пуст." - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/core/header.json b/legacy-dashboard/src/i18n/locales/ru-RU/core/header.json deleted file mode 100644 index a43b3140..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/core/header.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "logoTitle": "Панель управления AstrBot", - "version": { - "hasNewVersion": "Доступна новая версия AstrBot!", - "dashboardHasNewVersion": "Доступна новая версия WebUI!" - }, - "buttons": { - "update": "Обновить", - "account": "Аккаунт", - "theme": { - "title": "Тема", - "light": "Светлая тема", - "dark": "Темная тема", - "system": "Как в системе" - } - }, - "updateDialog": { - "title": "Обновить AstrBot", - "currentVersion": "Текущая версия", - "status": { - "checking": "Проверка обновлений...", - "switching": "Переключение версии...", - "updating": "Обновление..." - }, - "tabs": { - "release": "😊 Релиз" - }, - "advancedSettings": "Расширенные настройки", - "releases": "Релизы", - "updateToLatest": "Обновить до последней версии", - "showPreReleases": "Показывать предварительные версии", - "preRelease": "Предварительная версия", - "preReleaseWarning": { - "title": "Внимание: предварительная версия", - "description": "Версии с меткой Pre-release могут содержать неизвестные ошибки. Не рекомендуется использовать в рабочих средах. Если вы обнаружили ошибку, пожалуйста, сообщите о ней в ", - "issueLink": "GitHub Issues" - }, - "tip": "💡 ПОДСКАЗКА: ", - "tipContinue": "По умолчанию при переключении версии загружаются соответствующие файлы WebUI. Код WebUI находится в директории dashboard, вы можете собрать его самостоятельно с помощью npm.", - "dockerTip": "При переключении версии будет предпринята попытка обновить как основной процесс бота, так и панель управления. Если вы используете Docker, вы также можете обновить образ или использовать", - "dockerTipLink": "watchtower", - "dockerTipContinue": "для автоматического мониторинга и обновления.", - "table": { - "tag": "Тег", - "publishDate": "Дата публикации", - "content": "Содержание", - "sourceUrl": "Исходный код", - "actions": "Действия", - "view": "Просмотр", - "switch": "Переключить" - }, - "releaseNotes": { - "title": "Журнал изменений", - "latestLabel": "Последняя версия" - }, - "progress": { - "target": "Целевая версия:", - "preparing": "Подготовка обновления...", - "dashboard": "Загрузка WebUI", - "core": "Загрузка кода проекта", - "dependencies": "Обновление зависимостей...", - "restart": "Подготовка перезапуска...", - "restarting": "Перезапуск AstrBot", - "completed": "Обновление завершено", - "successReady": "Обновление успешно", - "autoReloadIn": "Автоматическое обновление через {seconds} с", - "reloadNow": "Обновить сейчас", - "failed": "Ошибка обновления" - }, - "redirectConfirm": { - "title": "Переход по ссылке", - "message": "Вы будете перенаправлены на страницу GitHub Releases. Продолжить?", - "latestLabel": "Последняя версия", - "targetVersion": "Целевая версия:", - "currentVersion": "Текущая версия:", - "guideTitle": "Рекомендации после перехода:", - "guideStep1": "Загрузите пакет, соответствующий архитектуре вашей системы.", - "guideStep2": "После завершения установки перезапустите AstrBot.", - "guideStep3": "Если вы используете Docker, отдайте приоритет обновлению через образ." - }, - "desktopApp": { - "title": "Обновить десктопное приложение", - "message": "Проверка и обновление десктопной версии AstrBot.", - "currentVersion": "Текущая версия:", - "latestVersion": "Последняя версия:", - "checking": "Проверка обновлений десктопного приложения...", - "hasNewVersion": "Найдена новая версия. Нажмите для подтверждения обновления.", - "isLatest": "Установлена последняя версия", - "installing": "Загрузка и установка обновления... Приложение будет перезапущено автоматически.", - "checkFailed": "Ошибка проверки обновлений. Попробуйте позже.", - "installFailed": "Ошибка обновления. Попробуйте позже." - }, - "dashboardUpdate": { - "title": "Обновить только панель управления", - "currentVersion": "Текущая версия", - "hasNewVersion": "Доступна новая версия!", - "isLatest": "Установлена последняя версия.", - "fallback": "Используйте это для повторной загрузки WebUI, если полное обновление завершилось ошибкой.", - "downloadAndUpdate": "Скачать и обновить" - } - }, - "accountDialog": { - "title": "Изменить аккаунт", - "securityWarning": "Безопасность: Пожалуйста, смените пароль для защиты аккаунта", - "securityWarningMd5": "Новая версия AstrBot улучшила безопасность. Пожалуйста, измените пароль.", - "securityWarningUpgrade": "Обнаружено устаревшее хранение пароля. Измените пароль, чтобы завершить обновление безопасности.", - "form": { - "currentPassword": "Текущий пароль", - "newPassword": "Новый пароль", - "confirmPassword": "Подтвердите новый пароль", - "newUsername": "Новое имя пользователя (опционально)", - "passwordHint": "Не менее 8 символов, включая заглавные и строчные буквы, а также цифры", - "confirmPasswordHint": "Введите новый пароль еще раз", - "usernameHint": "Оставьте пустым, если не хотите менять имя пользователя", - "defaultCredentials": "Имя пользователя по умолчанию — astrbot. Начальный пароль генерируется при первом запуске и отображается в консоли." - }, - "validation": { - "passwordRequired": "Введите пароль", - "passwordMinLength": "Пароль должен быть не менее 8 символов", - "passwordUppercase": "Пароль должен содержать хотя бы одну заглавную букву", - "passwordLowercase": "Пароль должен содержать хотя бы одну строчную букву", - "passwordDigit": "Пароль должен содержать хотя бы одну цифру", - "passwordMatch": "Паролы не совпадают", - "usernameMinLength": "Имя пользователя должно быть не менее 3 символов" - }, - "actions": { - "save": "Сохранить изменения", - "cancel": "Отмена" - }, - "messages": { - "updateFailed": "Ошибка обновления, попробуйте еще раз" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/core/navigation.json b/legacy-dashboard/src/i18n/locales/ru-RU/core/navigation.json deleted file mode 100644 index 315e386e..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/core/navigation.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "welcome": "Добро пожаловать", - "dashboard": "Статистика", - "platforms": "Боты", - "providers": "Провайдеры моделей", - "commands": "Команды", - "persona": "Персонажи", - "subagent": "Субагенты", - "toolUse": "Инструменты MCP", - "extension": "Плагины", - "extensionTabs": { - "installed": "Плагины AstrBot", - "market": "Магазин плагинов", - "mcp": "Серверы MCP", - "skills": "Навыки", - "components": "Управление поведением" - }, - "config": "Конфигурация", - "chat": "Чат", - "cron": "Запланированные задачи", - "conversation": "Данные диалогов", - "sessionManagement": "Пользовательские правила", - "console": "Логи платформы", - "trace": "Трассировка", - "alkaid": "Alkaid Lab", - "knowledgeBase": "База знаний", - "about": "О программе", - "settings": "Настройки", - "changelog": "Журнал изменений", - "documentation": "Документация", - "faq": "FAQ", - "github": "GitHub", - "drag": "Перетащить", - "groups": { - "more": "Дополнительно" - }, - "changelogDialog": { - "title": "Журнал изменений", - "loading": "Загрузка...", - "error": "Ошибка загрузки", - "notFound": "Журнал изменений для этой версии не найден", - "selectVersion": "Выберите версию", - "current": "Текущая" - }, - "configTabs": { - "normal": "Обычная конфигурация", - "system": "Системная конфигурация" - }, - "pluginWebui": "Страницы плагинов" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/core/shared.json b/legacy-dashboard/src/i18n/locales/ru-RU/core/shared.json deleted file mode 100644 index f5445d25..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/core/shared.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "knowledgeBaseSelector": { - "notSelected": "Не выбрано", - "buttonText": "Выбрать базу знаний...", - "dialogTitle": "Выбор базы знаний", - "loading": "Загрузка...", - "noKnowledgeBases": "Базы знаний не найдены", - "createKnowledgeBase": "Создать базу знаний", - "selectedCount": "Выбрано баз знаний: {count}", - "confirmSelection": "ОК", - "cancelSelection": "Отмена", - "noDescription": "Нет описания", - "documentCount": "Документов: {count}", - "chunkCount": "Фрагментов: {count}" - }, - "pluginSetSelector": { - "notSelected": "Плагины не включены", - "allPlugins": "Включить все плагины (*)", - "selectedCount": "Выбрано плагинов: {count}", - "buttonText": "Выбрать набор плагинов...", - "dialogTitle": "Выбор набора плагинов", - "loading": "Загрузка...", - "enableAll": "Включить все", - "enableNone": "Ничего не включать", - "customSelect": "Настроить выбор", - "noPlugins": "Доступных плагинов нет", - "confirmSelection": "ОК", - "cancelSelection": "Отмена", - "noDescription": "Нет описания", - "notActivated": "Не активирован", - "note": "*Системные и уже выключенные в настройках плагины не отображаются.", - "selectedPluginsLabel": "Выбранные плагины:", - "allPluginsLabel": "Все плагины" - }, - "providerSelector": { - "notSelected": "Не выбрано", - "buttonText": "Выбрать провайдера...", - "dialogTitle": "Выбор провайдера", - "loading": "Загрузка...", - "noProviders": "Доступных провайдеров нет", - "confirmSelection": "ОК", - "cancelSelection": "Отмена", - "clearSelection": "Сбросить выбор", - "clearSelectionSubtitle": "Очистить текущий выбор", - "unknownType": "Неизвестный тип", - "createProvider": "Создать провайдера", - "manageProviders": "Управление провайдерами", - "selectProviderPool": "Выбрать пул провайдеров...", - "selectedCount": "Выбрано провайдеров: {count}" - }, - "personaSelector": { - "notSelected": "Не выбрано", - "defaultPersona": "Персонаж по умолчанию", - "buttonText": "Выбрать персонажа...", - "editPersona": "Изменить текущего персонажа", - "dialogTitle": "Выбор персонажа", - "noDescription": "Нет описания", - "noPersonas": "Доступных персонажей нет", - "createPersona": "Создать персонажа", - "cancelSelection": "Отмена", - "confirmSelection": "ОК", - "selectPersonaPool": "Выбрать пул персонажей...", - "rootFolder": "Все персонажи", - "emptyFolder": "Папка пуста" - }, - "personaQuickPreview": { - "title": "Быстрый просмотр", - "loading": "Загрузка...", - "noPersonaSelected": "Персонаж не выбран", - "personaNotFound": "Информация о персонаже не найдена", - "systemPromptLabel": "Системный промпт", - "toolsLabel": "Инструменты", - "skillsLabel": "Навыки (Skills)", - "originLabel": "Источник", - "originNameLabel": "Имя источника", - "toolInactive": "Выключено", - "toolInactiveTooltip": "Этот инструмент выключен. Включите его в Плагины -> Управление поведением -> Функции.", - "allTools": "Доступны все инструменты", - "allToolsWithCount": "Доступны все инструменты ({count})", - "noTools": "Инструменты не настроены", - "allSkills": "Доступны все навыки (Skills)", - "allSkillsWithCount": "Доступны все навыки ({count})", - "noSkills": "Навыки (Skills) не настроены" - }, - "t2iTemplateEditor": { - "buttonText": "Настроить T2I шаблон", - "dialogTitle": "Настройка HTML шаблона Text-to-Image", - "newTemplateNameLabel": "Введите имя нового шаблона", - "nameRequired": "Имя обязательно для заполнения", - "selectTemplateLabel": "Выбрать шаблон", - "applied": "Применено", - "apply": "Применить", - "templateEditor": "Редактор шаблона", - "new": "Создать", - "resetBase": "Сбросить 'base'", - "delete": "Удалить", - "save": "Сохранить", - "livePreview": "Предпросмотр (может отличаться)", - "refreshPreview": "Обновить", - "previewText": "Это пример текста для предпросмотра результата шаблона.\n\nОн может содержать несколько строк и различные форматы.", - "syntaxHint": "Поддерживается синтаксис jinja2. Переменные: text | safe (текст для рендеринга), version (версия AstrBot). Если тестовый текст предпросмотра не загружается или выглядит неверно, нажмите «Сбросить 'base'», чтобы восстановить шаблон по умолчанию.", - "saveAndApply": "Сохранить и применить текущий шаблон", - "confirmReset": "Подтверждение сброса", - "confirmResetMessage": "Вы уверены, что хотите сбросить шаблон 'base' до значений по умолчанию? Все несохраненные изменения будут потеряны. Это действие необратимо.", - "confirmResetButton": "Сбросить", - "confirmDelete": "Подтверждение удаления", - "confirmDeleteMessage": "Вы уверены, что хотите удалить шаблон '{name}'? Это действие необратимо.", - "confirmDeleteButton": "Удалить", - "confirmAction": "Подтверждение действия", - "confirmApplyMessage": "Вы уверены, что хотите сохранить изменения в '{name}' и сделать его активным шаблоном?" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/core/status.json b/legacy-dashboard/src/i18n/locales/ru-RU/core/status.json deleted file mode 100644 index a016dae1..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/core/status.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "loading": "Загрузка", - "success": "Успешно", - "error": "Ошибка", - "warning": "Внимание", - "info": "Информация", - "pending": "В ожидании", - "processing": "В процессе", - "completed": "Завершено", - "failed": "Ошибка", - "cancelled": "Отменено", - "timeout": "Тайм-аут", - "connecting": "Подключение", - "connected": "Подключено", - "disconnected": "Отключено", - "online": "В сети", - "offline": "Не в сети", - "active": "Активен", - "inactive": "Неактивен", - "ready": "Готов", - "busy": "Занят" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/about.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/about.json deleted file mode 100644 index d9ef90b4..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/about.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "hero": { - "title": "AstrBot", - "subtitle": "Проект, рожденный из интереса и любви ❤️", - "starButton": "Star этот проект! 🌟", - "issueButton": "Сообщить об ошибке" - }, - "contributors": { - "title": "Контрибьюторы", - "description": "Этот проект поддерживается участниками open-source сообщества. Спасибо каждому за вклад!", - "viewLink": "Посмотреть всех участников" - }, - "stats": { - "title": "Глобальное развертывание", - "license": "AstrBot распространяется по лицензии AGPL v3" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/index.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/index.json deleted file mode 100644 index ece76056..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/index.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "title": "Лаборатория Alkaid", - "subtitle": "Исследуйте передовые возможности AI", - "comingSoon": "Этот мир еще впереди, заходите позже!", - "page": { - "title": "Проект Alkaid.", - "subtitle": "AstrBot Alpha Project", - "navigation": { - "knowledgeBase": "База знаний (Плагин)", - "longTermMemory": "Долгосрочная память", - "other": "..." - } - }, - "features": { - "knowledgeBase": "База знаний", - "longTermMemory": "Долгосрочная память", - "advancedChat": "Продвинутый чат", - "multiModal": "Мультимодальность" - }, - "status": { - "experimental": "Экспериментально", - "beta": "Бета", - "stable": "Стабильно", - "deprecated": "Устарело" - }, - "sigma": { - "subtitle": "Экспериментальный проект AstrBot", - "visualization": "Визуализация", - "filterUserId": "Фильтр по User ID", - "filter": "Фильтр", - "resetFilter": "Сброс", - "refreshGraph": "Обновить граф", - "nodeDetails": "Детали узла", - "id": "ID", - "type": "Тип", - "name": "Имя", - "userId": "ID пользователя", - "timestamp": "Метка времени", - "graphStats": "Статистика графа", - "nodeCount": "Узлов", - "edgeCount": "Связей", - "inDevelopment": "В разработке" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/knowledge-base.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/knowledge-base.json deleted file mode 100644 index 3d52f261..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/knowledge-base.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "title": "База знаний", - "subtitle": "Управление контентом базы знаний и поиск", - "documents": { - "title": "Список документов", - "name": "Имя файла", - "size": "Размер", - "uploadTime": "Дата загрузки", - "status": "Статус", - "actions": "Действия" - }, - "management": { - "delete": "Удалить", - "preview": "Предпросмотр", - "download": "Скачать", - "reindex": "Переиндексировать" - }, - "notInstalled": { - "title": "Плагин базы знаний не установлен", - "install": "Установить сейчас" - }, - "empty": { - "title": "База знаний пуста. Создайте свою первую базу! 🙂", - "create": "Создать базу знаний" - }, - "list": { - "title": "Список баз знаний", - "create": "Создать базу знаний", - "config": "Настройка", - "checkUpdate": "Проверить обновления плагина", - "updatePlugin": "Обновить плагин до версии {version}", - "knowledgeCount": "записей", - "tips": "Совет: используйте команду /kb в чате, чтобы узнать, как пользоваться базой!" - }, - "createDialog": { - "title": "Создание базы знаний", - "nameLabel": "Название", - "descriptionLabel": "Описание", - "descriptionPlaceholder": "Краткое описание...", - "embeddingModelLabel": "Embedding модель", - "rerankModelLabel": "Rerank модель", - "providerInfo": "Провайдер: {id} | Размерность: {dimensions}", - "rerankProviderInfo": "Провайдер: {id}", - "tips": "Совет: после выбора Embedding модели не рекомендуется менять провайдера или размерность векторов, так как это сделает текущий индекс нечитаемым.", - "cancel": "Отмена", - "create": "Создать" - }, - "emojiPicker": { - "title": "Выберите иконку", - "close": "Закрыть", - "categories": { - "emotions": "Смайлы", - "animals": "Животные и природа", - "food": "Еда и напитки", - "activities": "Занятия и вещи", - "travel": "Места и путешествия", - "symbols": "Символы и флаги" - } - }, - "contentDialog": { - "title": "Управление базой знаний", - "embeddingModel": "Embedding модель", - "vectorDimension": "Размерность", - "usage": "Использование: введите «/kb use {name}» в чате", - "tabs": { - "upload": "Загрузка файлов", - "search": "Поиск", - "fromURL": "Импорт из URL" - } - }, - "upload": { - "title": "Загрузка файлов", - "subtitle": "Поддерживаются форматы txt, pdf, word, excel и др.", - "dropzone": "Перетащите файлы сюда или нажмите для выбора", - "chunkSettings": { - "title": "Настройка фрагментации (Chunking)", - "tooltip": "Размер фрагмента определяет объем текста в одном блоке. Перекрытие позволяет сохранить контекст между соседними блоками.\nМаленькие фрагменты точнее, но увеличивают объем базы.", - "chunkSizeLabel": "Размер фрагмента", - "chunkSizeHint": "Длина текста в одном блоке (пусто = по умолчанию)", - "overlapLabel": "Перекрытие", - "overlapHint": "Нахлест между соседними блоками (пусто = по умолчанию)" - }, - "upload": "Начать загрузку", - "uploading": "Загрузка..." - }, - "search": { - "queryLabel": "Поиск по базе знаний", - "queryPlaceholder": "Введите ключевые слова...", - "resultCountLabel": "Количество результатов", - "searching": "Поиск...", - "resultsTitle": "Результаты поиска", - "relevance": "Релевантность", - "noResults": "Совпадений не найдено" - }, - "deleteDialog": { - "title": "Подтверждение удаления", - "confirmText": "Вы уверены, что хотите удалить базу знаний «{name}»?", - "warning": "Это действие необратимо. Весь контент базы знаний будет навсегда удален.", - "cancel": "Отмена", - "delete": "Удалить" - }, - "messages": { - "pluginNotAvailable": "Плагин не установлен или недоступен", - "pluginNotActivated": "Плагин astrbot_plugin_knowledge_base не включен. Пожалуйста, активируйте его в разделе плагинов и перезапустите AstrBot.", - "checkPluginFailed": "Не удалось проверить плагин", - "installFailed": "Ошибка установки", - "installPluginFailed": "Не удалось установить плагин", - "getKnowledgeBaseListFailed": "Ошибка получения списка баз знаний", - "knowledgeBaseCreated": "База знаний создана", - "createFailed": "Ошибка создания", - "createKnowledgeBaseFailed": "Не удалось создать базу знаний", - "pleaseEnterKnowledgeBaseName": "Укажите название базы знаний", - "pleaseSelectFile": "Пожалуйста, сначала выберите файл", - "operationSuccess": "Успешно: {message}", - "uploadFailed": "Ошибка загрузки", - "fileUploadFailed": "Не удалось загрузить файл", - "pleaseEnterSearchContent": "Введите текст для поиска", - "noMatchingContent": "Ничего не найдено", - "searchFailed": "Ошибка поиска", - "searchKnowledgeBaseFailed": "Не удалось выполнить поиск", - "deleteTargetNotExists": "Объект для удаления не найден", - "knowledgeBaseDeleted": "База знаний удалена", - "deleteFailed": "Ошибка удаления", - "deleteKnowledgeBaseFailed": "Не удалось удалить базу знаний", - "getEmbeddingModelListFailed": "Не удалось загрузить список Embedding моделей", - "updateAvailable": "Доступна новая версия: {current} -> {latest}", - "pluginUpToDate": "У вас последняя версия плагина", - "pluginNotFoundInMarket": "Плагин не найден в магазине", - "checkUpdateFailed": "Ошибка проверки обновлений", - "updateSuccess": "Плагин успешно обновлен", - "updateFailed": "Ошибка обновления", - "updatePluginFailed": "Не удалось обновить плагин" - }, - "importFromUrl": { - "title": "Импорт из URL", - "urlLabel": "Адрес страницы", - "urlPlaceholder": "Введите URL для извлечения знаний", - "optionsTitle": "Настройки импорта", - "tooltip": "Эти параметры управляют извлечением текста из URL.\nЕсли оставить пустыми, будут использованы настройки по умолчанию.\nТекстовая очистка через LLM может занять время.", - "useLlmRepairLabel": "Исправление текста через LLM", - "useClusteringSummaryLabel": "Кластеризация и суммаризация", - "repairLlmProviderIdLabel": "Модель для очистки", - "summarizeLlmProviderIdLabel": "Модель для суммаризации", - "embeddingProviderIdLabel": "Embedding модель", - "chunkSizeLabel": "Размер фрагмента", - "chunkOverlapLabel": "Перекрытие", - "startImport": "Начать импорт", - "importing": "Импорт...", - "importSuccess": "Импортировано успешно", - "importFailed": "Ошибка импорта", - "uploadingChunks": "Текст извлечен, загрузка фрагментов...", - "preRequisite": "Примечание: сначала установите плагин astrbot_plugin_url_2_knowledge_base и выполните установку playwright согласно документации.", - "allChunksUploaded": "Все фрагменты успешно загружены" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/memory.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/memory.json deleted file mode 100644 index 507c6ae1..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/alkaid/memory.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "title": "Долгосрочная память", - "subtitle": "Управление памятью вашего AI-помощника", - "memories": { - "title": "Список воспоминаний", - "content": "Содержание", - "importance": "Важность", - "createTime": "Дата создания", - "lastAccess": "Последнее обращение", - "category": "Категория" - }, - "categories": { - "personal": "Личное", - "preferences": "Предпочтения", - "conversations": "История диалогов", - "facts": "Факты", - "skills": "Навыки" - }, - "importance": { - "high": "Высокая", - "medium": "Средняя", - "low": "Низкая" - }, - "actions": { - "view": "Детали", - "edit": "Изменить", - "delete": "Удалить", - "pin": "Закрепить", - "unpin": "Открепить" - }, - "filters": { - "all": "Все", - "category": "По категории", - "importance": "По важности", - "dateRange": "По периоду", - "title": "Фильтр", - "userIdLabel": "Фильтр по User ID", - "filterButton": "Применить", - "resetButton": "Сбросить", - "refreshButton": "Обновить граф" - }, - "search": { - "title": "Поиск по памяти", - "userIdLabel": "ID пользователя", - "queryLabel": "Ключевое слово", - "searchButton": "Поиск", - "resultsTitle": "Результаты поиска", - "noResults": "Ничего не найдено", - "similarity": "Сходство", - "noTextContent": "Нет текста" - }, - "addMemory": { - "title": "Добавить данные в память", - "textLabel": "Текст воспоминания", - "userIdLabel": "ID пользователя", - "summarizeLabel": "Нужна суммаризация", - "addButton": "Добавить" - }, - "nodeDetails": { - "title": "Детали узла", - "id": "ID", - "type": "Тип", - "name": "Имя", - "userId": "ID пользователя", - "timestamp": "Метка времени" - }, - "graphStats": { - "title": "Статистика графа", - "nodeCount": "Узлов", - "edgeCount": "Связей" - }, - "factDialog": { - "title": "Факт из памяти", - "id": "ID", - "docId": "ID документа", - "createdAt": "Создано", - "updatedAt": "Обновлено", - "metadata": "Метаданные", - "metadataKey": "Ключ", - "metadataValue": "Значение", - "loading": "Загрузка...", - "close": "Закрыть", - "noValue": "нет", - "unknown": "неизвестно" - }, - "messages": { - "searchQueryRequired": "Пожалуйста, введите запрос", - "searchSuccess": "Найдено записей: {count}", - "searchNoResults": "В памяти ничего не найдено", - "searchError": "Ошибка поиска", - "addSuccess": "Данные успешно добавлены в память!", - "addError": "Не удалось добавить данные", - "factDetailsError": "Ошибка загрузки деталей", - "metadataParseError": "Не удалось разобрать метаданные", - "relationNoMemoryData": "У этой связи нет ассоциированных данных" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/auth.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/auth.json deleted file mode 100644 index 43666480..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/auth.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "login": "Вход", - "username": "Имя пользователя", - "password": "Пароль", - "defaultHint": "Если это первый вход, проверьте пароль по умолчанию в логах.", - "totp": { - "code": "Код подтверждения", - "verify": "Проверить", - "trustDevice": "Доверять этому устройству 30 дней" - }, - "recovery": { - "title": "Вход по коду восстановления", - "subtitle": "Потеряли доступ к приложению-аутентификатору? Используйте код восстановления для входа.", - "code": "Код восстановления", - "submit": "Войти по коду восстановления", - "useRecoveryCode": "Не можете использовать TOTP?", - "backToLogin": "Назад к входу", - "savedWarning": "При утере этого кода восстановить доступ к учётной записи обычными средствами будет невозможно.", - "continue": "Продолжить", - "acknowledge": "Я сохранил(а) коды восстановления", - "totpDisableWarning": "Использование кода восстановления отключит двухфакторную аутентификацию." - }, - "setup": { - "title": "Настройка аккаунта", - "subtitle": "Создайте аккаунт для управления AstrBot", - "username": "Новое имя пользователя", - "password": "Новый пароль", - "confirmPassword": "Подтвердите новый пароль", - "passwordHint": "Минимум 8 символов, включая заглавную букву, строчную букву и цифру.", - "submit": "Завершить настройку", - "totp": { - "code": "Код подтверждения", - "qrAlt": "QR-код TOTP", - "title": "Завершить настройку TOTP", - "subtitle": "Отсканируйте QR-код в приложении-аутентификаторе, чтобы включить двухфакторную аутентификацию.", - "step2Hint": "Отсканируйте этот QR-код в приложении-аутентификаторе (например, Google Authenticator, Authy) и введите код ниже.", - "verify": "Проверить и завершить", - "verifyError": "Не удалось проверить код. Введите последний код из приложения-аутентификатора.", - "disableError": "Не удалось отключить TOTP. Пожалуйста, попробуйте снова.", - "back": "Назад" - }, - "validation": { - "usernameRequired": "Введите имя пользователя", - "usernameMinLength": "Имя пользователя должно содержать минимум 3 символа", - "passwordRequired": "Введите пароль", - "passwordMinLength": "Пароль должен содержать минимум 8 символов", - "passwordUppercase": "Пароль должен содержать хотя бы одну заглавную букву", - "passwordLowercase": "Пароль должен содержать хотя бы одну строчную букву", - "passwordDigit": "Пароль должен содержать хотя бы одну цифру", - "confirmPasswordRequired": "Подтвердите пароль", - "passwordMatch": "Пароли не совпадают" - } - }, - "logo": { - "title": "Панель управления AstrBot", - "totpTitle": "Двухфакторная аутентификация", - "subtitle": "Добро пожаловать" - }, - "versions": { - "webui": "WebUI", - "astrbotRuntime": "AstrBot", - "astrbotCode": "AstrBot(Code)", - "mismatchTooltip": "Несовпадение версий", - "dialogTitle": "Проверьте состояние версий", - "webuiMismatchTitle": "WebUI не соответствует текущей версии AstrBot", - "webuiMismatchMessage": "Этот WebUI мог быть собран не для запущенной версии AstrBot, поэтому некоторые страницы или API могут работать нестабильно. Сначала перезапустите AstrBot вручную. Если несовпадение останется, удалите data/dist в рабочем каталоге и перезапустите снова, чтобы AstrBot скачал подходящий WebUI.", - "runtimeMismatchTitle": "Запущенный AstrBot не соответствует версии кода", - "runtimeMismatchMessage": "Код уже обновлен, но текущий процесс все еще работает на старой версии. Перезапустите AstrBot вручную, чтобы загрузить новый код.", - "faq": "Открыть FAQ", - "close": "Закрыть" - }, - "theme": { - "light": "Светлая тема", - "dark": "Темная тема", - "system": "Как в системе", - "switchToDark": "Перейти на темную тему", - "switchToLight": "Перейти на светлую тему", - "title": "Тема" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/chart.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/chart.json deleted file mode 100644 index d5d20474..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/chart.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "messageCount": "Количество сообщений", - "time": "Время" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/chat.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/chat.json deleted file mode 100644 index 7031cd48..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/chat.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "title": "Давай пообщаемся!", - "subtitle": "Общение с AI-помощником", - "input": { - "placeholder": "Спросите что угодно...", - "send": "Отправить", - "clear": "Очистить", - "upload": "Загрузить файл", - "voice": "Голосовой ввод", - "recordingPrompt": "Запись... говорите", - "chatPrompt": "Давай пообщаемся!", - "dropToUpload": "Отпустите, чтобы загрузить файл", - "stopGenerating": "Остановить генерацию" - }, - "message": { - "user": "Вы", - "assistant": "Ассистент", - "system": "Система", - "error": "Ошибка в сообщении", - "loading": "Думаю..." - }, - "voice": { - "start": "Начать запись", - "stop": "Стоп", - "recording": "Запись", - "processing": "Обработка...", - "error": "Ошибка записи", - "listening": "Слушаю...", - "speaking": "Говорю", - "startRecording": "Начать голосовой ввод", - "liveMode": "Общение в реальном времени" - }, - "welcome": { - "title": "О чем поговорим сегодня?", - "subtitle": "Ваш умный помощник", - "quickActions": "Быстрые действия", - "examples": "Примеры вопросов" - }, - "actions": { - "copy": "Копировать", - "regenerate": "Перегенерировать", - "retry": "Повторить", - "retryWithModel": "Использовать другую модель", - "noAvailableModels": "Нет доступных моделей", - "like": "Нравится", - "dislike": "Не нравится", - "share": "Поделиться", - "newChat": "Новый чат", - "deleteChat": "Удалить чат", - "editTitle": "Изменить заголовок", - "fullscreen": "На весь экран", - "exitFullscreen": "Выход из полноэкранного режима", - "reply": "Ответить", - "providerConfig": "Настройки AI", - "toolsUsed": "Использованные инструменты", - "toolCallUsed": "Использован инструмент {name}", - "pythonCodeAnalysis": "Использован анализ кода Python" - }, - "ipython": { - "output": "Вывод" - }, - "toolStatus": { - "done": "Готово", - "running": "Выполняется" - }, - "thread": { - "title": "Thread", - "askInThread": "Спросить в ветке", - "count": "Веток: {count}", - "createFailed": "Не удалось создать ветку", - "delete": "Удалить ветку", - "confirmDelete": "Удалить эту ветку? Это действие необратимо.", - "placeholder": "Спросить об этом фрагменте..." - }, - "conversation": { - "title": "Диалоги", - "newConversation": "Новый чат", - "noHistory": "История диалогов пуста", - "systemStatus": "Статус системы", - "llmService": "Сервис LLM", - "speechToText": "Преобразование речи", - "editDisplayName": "Изменить имя чата", - "displayName": "Имя чата", - "displayNameUpdated": "Имя чата обновлено", - "displayNameUpdateFailed": "Не удалось обновить имя чата", - "confirmDelete": "Вы уверены, что хотите удалить «{name}»? Это действие необратимо." - }, - "modes": { - "darkMode": "Темная тема", - "lightMode": "Светлая тема" - }, - "shortcuts": { - "help": "Справка", - "voiceRecord": "Запись голоса", - "pasteImage": "Вставить изображение", - "sendKey": { - "title": "Клавиша отправки", - "enterToSend": "Enter для отправки", - "shiftEnterToSend": "Shift+Enter для отправки" - } - }, - "streaming": { - "enabled": "Потоковый ответ включен", - "disabled": "Потоковый ответ выключен", - "on": "Поток", - "off": "Обычный" - }, - "transport": { - "title": "Протокол передачи", - "sse": "SSE", - "websocket": "WebSocket" - }, - "config": { - "title": "Конфигурация" - }, - "reasoning": { - "thinking": "Рассуждение", - "summarySeparator": ", ", - "thinkSummary": "Размышлений: {count}", - "toolSummary": "использований инструментов: {count}", - "think": "Размышление", - "toolUsed": "Использование инструмента" - }, - "reply": { - "replyTo": "В ответ на", - "notFound": "Сообщение не найдено" - }, - "project": { - "title": "Проект", - "create": "Создать проект", - "edit": "Изменить проект", - "name": "Имя проекта", - "emoji": "Иконка (Emoji)", - "description": "Описание проекта (опционально)", - "workspace": { - "type": "Рабочая область", - "session": "Отдельно для каждого чата", - "project": "Общая область проекта", - "custom": "Пользовательский путь", - "path": "Путь рабочей области" - }, - "loadingSessions": "Загрузка диалогов...", - "noSessions": "В этом проекте пока нет диалогов", - "noProjects": "Проектов пока нет", - "confirmDelete": "Вы уверены, что хотите удалить проект «{title}»? Диалоги внутри проекта не будут удалены." - }, - "time": { - "today": "Сегодня", - "yesterday": "Вчера" - }, - "stats": { - "tokens": "Токены", - "inputTokens": "Входящие (прочие)", - "outputTokens": "Исходящие", - "cachedTokens": "Входящие (кэш)", - "duration": "Время", - "ttft": "Время до первого токена" - }, - "tokenUsage": { - "tooltip": "{used} / {limit} токенов ({percent}%)" - }, - "refs": { - "title": "Ссылки", - "sources": "Источники" - }, - "connection": { - "title": "Статус подключения", - "message": "Системе необходимо переустановить соединение с чатом.", - "reasons": "Это может быть вызвано следующими причинами:", - "reasonWindowResize": "Изменение размера окна (нормально)", - "reasonMultipleTabs": "Страница чата открыта в другой вкладке", - "reasonNetworkIssue": "Временная проблема с сетью", - "notice": "Примечание: для стабильной работы допускается только одно активное соединение. Если вы используете чат в нескольких вкладках, рекомендуем оставить только одну.", - "understand": "Понятно", - "status": { - "reconnecting": "Переподключение...", - "reconnected": "Соединение восстановлено", - "failed": "Ошибка подключения, обновите страницу" - } - }, - "commandSuggestion": { - "navigate": "Выбор", - "select": "Подтвердить", - "close": "Закрыть" - }, - "errors": { - "sendMessageFailed": "Ошибка отправки сообщения, попробуйте еще раз", - "createSessionFailed": "Ошибка создания сессии, обновите страницу" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/command.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/command.json deleted file mode 100644 index 7d887c8e..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/command.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "title": "Управление командами", - "summary": { - "total": "Всего команд", - "disabled": "Отключено", - "conflicts": "Конфликты" - }, - "conflictAlert": { - "title": "Обнаружены конфликты команд", - "description": "Сейчас конфликтуют {count} пары команд. Это может привести к одновременному срабатыванию нескольких плагинов и непредсказуемому поведению.", - "hint": "Нажмите «Переименовать», чтобы изменить название конфликтующей команды." - }, - "table": { - "headers": { - "command": "Команда", - "type": "Тип", - "plugin": "Плагин", - "description": "Описание", - "permission": "Доступ", - "status": "Статус", - "actions": "Действия" - } - }, - "type": { - "command": "Команда", - "group": "Группа команд", - "subCommand": "Под-команда" - }, - "status": { - "enabled": "Активна", - "disabled": "Отключена", - "conflict": "Конфликт" - }, - "permission": { - "everyone": "Все", - "admin": "Админ" - }, - "tooltips": { - "enable": "Включить", - "disable": "Выключить", - "rename": "Переименовать", - "viewDetails": "Подробности" - }, - "dialogs": { - "rename": { - "title": "Переименование команды", - "newName": "Новое название", - "aliases": "Управление алиасами", - "addAlias": "Добавить алиас", - "cancel": "Отмена", - "confirm": "Подтвердить" - }, - "details": { - "title": "Детали команды", - "type": "Тип команды", - "handler": "Обработчик (Handler)", - "module": "Путь к модулю", - "originalCommand": "Исходная команда", - "effectiveCommand": "Действующая команда", - "parentGroup": "Родительская группа", - "subCommands": "Под-команды", - "aliases": "Алиасы (Синонимы)", - "permission": "Требования прав", - "conflictStatus": "Статус конфликта" - } - }, - "messages": { - "toggleSuccess": "Статус команды обновлен", - "toggleFailed": "Не удалось изменить статус команды", - "renameSuccess": "Команда переименована", - "renameFailed": "Ошибка переименования", - "loadFailed": "Ошибка загрузки списка команд", - "updateSuccess": "Обновлено успешно", - "updateFailed": "Ошибка обновления" - }, - "search": { - "placeholder": "Поиск команд..." - }, - "empty": { - "noCommands": "Команд не найдено", - "noCommandsDesc": "По вашему запросу не найдено ни одной команды" - }, - "filters": { - "all": "Все", - "enabled": "Активные", - "disabled": "Отключенные", - "conflict": "Конфликтующие", - "byPlugin": "По плагину", - "byType": "По типу", - "byPermission": "По правам", - "byStatus": "По статусу", - "showSystemPlugins": "Показывать системные плагины", - "systemPluginConflictHint": "Конфликт затрагивает системный плагин, его нельзя скрыть до разрешения конфликта" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/config-metadata.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/config-metadata.json deleted file mode 100644 index f0a1294d..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/config-metadata.json +++ /dev/null @@ -1,1765 +0,0 @@ -{ - "ai_group": { - "name": "AI", - "agent_runner": { - "description": "Запуск агентов (Agent Runner)", - "hint": "Выберите среду для работы AI-диалогов. По умолчанию используется встроенный агент AstrBot, поддерживающий базу знаний, персонализацию и вызов технических инструментов. Не изменяйте этот раздел, если не планируете использовать сторонние платформы, такие как Dify, Coze или DeerFlow.", - "provider_settings": { - "enable": { - "description": "Включить", - "hint": "Главный переключатель для AI-диалогов" - }, - "agent_runner_type": { - "description": "Запуск (Runner)", - "labels": [ - "Встроенный агент", - "Dify", - "Coze", - "Приложение Alibaba Cloud Bailian", - "DeerFlow" - ] - }, - "coze_agent_runner_provider_id": { - "description": "ID провайдера Coze Agent Runner" - }, - "dify_agent_runner_provider_id": { - "description": "ID провайдера Dify Agent Runner" - }, - "dashscope_agent_runner_provider_id": { - "description": "ID провайдера Alibaba Cloud Bailian" - }, - "deerflow_agent_runner_provider_id": { - "description": "ID провайдера DeerFlow Agent Runner" - } - } - }, - "ai": { - "description": "Модель", - "hint": "При использовании сторонних сред запуска модель чата и модель описания изображений могут не работать напрямую, но некоторые плагины всё равно зависят от этих настроек.", - "provider_settings": { - "default_provider_id": { - "description": "Модель чата по умолчанию", - "hint": "Если пусто, используется первая доступная модель" - }, - "fallback_chat_models": { - "description": "Резервные модели чата (ID)", - "hint": "Если текущая модель недоступна, запрос будет перенаправлен на эти модели по порядку." - }, - "request_max_retries": { - "description": "Максимум повторов запроса", - "hint": "Максимальное число попыток для одного запроса модели при повторяемых ошибках." - }, - "default_image_caption_provider_id": { - "description": "Модель описания изображений", - "hint": "Оставьте пустым для отключения; полезно для моделей без поддержки мультимодальности" - }, - "image_caption_prompt": { - "description": "Промпт для описания изображений" - } - }, - "provider_stt_settings": { - "enable": { - "description": "Включить преобразование речи в текст (STT)", - "hint": "Главный переключатель для STT" - }, - "provider_id": { - "description": "Модель STT по умолчанию", - "hint": "Пользователи могут выбирать другие модели STT через команду /provider." - } - }, - "provider_tts_settings": { - "enable": { - "description": "Включить преобразование текста в речь (TTS)", - "hint": "Главный переключатель для TTS" - }, - "provider_id": { - "description": "Модель TTS по умолчанию" - }, - "trigger_probability": { - "description": "Вероятность срабатывания TTS" - } - } - }, - "persona": { - "description": "Персонаж", - "hint": "Установите персонажа по умолчанию для AI-диалогов. Управлять персонажами можно на вкладке «Персонажи».", - "provider_settings": { - "default_personality": { - "description": "Персонаж по умолчанию" - } - } - }, - "knowledgebase": { - "description": "База знаний", - "kb_names": { - "description": "Список баз знаний", - "hint": "Поддерживается выбор нескольких баз" - }, - "kb_fusion_top_k": { - "description": "Кол-во результатов (Fusion Search)", - "hint": "Количество результатов после объединения данных из нескольких баз знаний" - }, - "kb_final_top_k": { - "description": "Итоговое кол-во результатов", - "hint": "Количество результатов, извлекаемых из базы знаний. Высокие значения дают больше данных, но могут добавить шума. Настройте по необходимости." - }, - "kb_agentic_mode": { - "description": "Агентский режим извлечения (Agentic Retrieval)", - "hint": "Если включено, извлечение из базы знаний становится инструментом (Tool) для LLM, позволяя модели самой решать, когда обращаться к базе. Требует поддержки вызова функций (function calling) в модели." - } - }, - "websearch": { - "description": "Поиск в сети", - "provider_settings": { - "web_search": { - "description": "Включить поиск в сети" - }, - "websearch_provider": { - "description": "Провайдер поиска" - }, - "websearch_tavily_key": { - "description": "API-ключ Tavily", - "hint": "Можно добавить несколько ключей для ротации." - }, - "websearch_bocha_key": { - "description": "API-ключ BoCha", - "hint": "Можно добавить несколько ключей для ротации." - }, - "websearch_brave_key": { - "description": "API-ключ Brave Search", - "hint": "Можно добавить несколько ключей для ротации." - }, - "websearch_firecrawl_key": { - "description": "API-ключ Firecrawl", - "hint": "Можно добавить несколько ключей для ротации." - }, - "websearch_baidu_app_builder_key": { - "description": "API-ключ Baidu Qianfan APP Builder", - "hint": "Ссылка: [https://console.bce.baidu.com/iam/#/iam/apikey/list](https://console.bce.baidu.com/iam/#/iam/apikey/list)" - }, - "web_search_link": { - "description": "Показывать ссылки на источники" - }, - "websearch_exa_key": { - "description": "API-ключ Exa", - "hint": "Можно добавить несколько ключей для ротации. Получить ключ: https://dashboard.exa.ai" - } - } - }, - "file_extract": { - "description": "Извлечение из файлов", - "provider_settings": { - "file_extract": { - "enable": { - "description": "Включить извлечение из файлов" - }, - "provider": { - "description": "Провайдер извлечения" - }, - "moonshotai_api_key": { - "description": "API-ключ Moonshot AI" - } - } - } - }, - "agent_computer_use": { - "description": "Использование компьютера (Agent Computer Use)", - "hint": "Позволяет AstrBot получать доступ к вашему компьютеру или песочнице для выполнения сложных задач. Подробнее: [Использование компьютера](https://docs.astrbot.app/use/computer.html).", - "provider_settings": { - "computer_use_runtime": { - "description": "Среда выполнения (Runtime)", - "hint": "Среда, к которой разрешён доступ Agent: `local` — локальная среда компьютера, `sandbox` — изолированная песочница, `none` — запретить доступ к любым средам." - }, - "computer_use_require_admin": { - "description": "Требовать права администратора AstrBot", - "hint": "Если включено, только администраторы смогут использовать возможности управления компьютером. Добавить администраторов можно в конфиге платформы." - }, - "sandbox": { - "booter": { - "description": "Драйвер среды песочницы" - }, - "shipyard_neo_endpoint": { - "description": "Эндпоинт Shipyard Neo API", - "hint": "Адрес Bay API, по умолчанию http://127.0.0.1:8114." - }, - "shipyard_neo_access_token": { - "description": "Токен доступа Shipyard Neo", - "hint": "Ключ Bay API (sk-bay-...). Оставьте пустым для автопоиска в credentials.json." - }, - "shipyard_neo_profile": { - "description": "Профиль Shipyard Neo", - "hint": "Профиль песочницы, например, python-default. Оставьте пустым для автоматического выбора самого функционального профиля." - }, - "shipyard_neo_ttl": { - "description": "TTL песочницы Shipyard Neo", - "hint": "Время жизни песочницы в секундах." - }, - "cua_image": { - "description": "Образ CUA", - "hint": "Образ или тип ОС песочницы CUA. По умолчанию linux. Поддерживаемые значения зависят от установленного CUA SDK." - }, - "cua_os_type": { - "description": "Тип ОС CUA", - "hint": "Тип операционной системы песочницы CUA. По умолчанию linux." - }, - "cua_idle_timeout": { - "description": "Таймаут простоя CUA", - "hint": "Таймаут простоя сессии песочницы CUA в секундах. Если значение больше 0, AstrBot автоматически завершит неактивную песочницу CUA после указанного времени бездействия; 0 отключает автоочистку." - }, - "cua_telemetry_enabled": { - "description": "Телеметрия CUA", - "hint": "Разрешить CUA SDK отправлять телеметрию. По умолчанию выключено." - }, - "cua_local": { - "description": "Локальная песочница CUA", - "hint": "Предпочитать локальную песочницу CUA. Включено по умолчанию, чтобы не требовать CUA_API_KEY для облачных песочниц. Отключите для использования облачных песочниц CUA." - }, - "cua_api_key": { - "description": "CUA API Key", - "hint": "API key для облачной песочницы CUA. Требуется только если локальная песочница отключена. Также можно передать через переменную окружения CUA_API_KEY." - }, - "shipyard_endpoint": { - "description": "Эндпоинт Shipyard API", - "hint": "Адрес API для доступа к сервису Shipyard." - }, - "shipyard_access_token": { - "description": "Токен доступа Shipyard", - "hint": "Токен доступа для работы с сервисом Shipyard." - }, - "shipyard_ttl": { - "description": "TTL сессии Shipyard", - "hint": "Время жизни сессии в секундах." - }, - "shipyard_max_sessions": { - "description": "Макс. количество сессий Shipyard", - "hint": "Максимальное количество сессий Shipyard, которое может поддерживать экземпляр." - } - } - } - }, - "proactive_capability": { - "description": "Проактивный агент", - "hint": "AstrBot будет просыпаться, выполнять ваши задачи и сообщать о результатах. См. [Проактивный агент](https://docs.astrbot.app/en/use/proactive-agent.html)", - "provider_settings": { - "proactive_capability": { - "add_cron_tools": { - "description": "Включить", - "hint": "Если включено, агенту будут переданы инструменты для проактивной работы. Вы сможете поручать задачи на будущее, и они будут выполнены по расписанию." - } - } - } - }, - "truncate_and_compress": { - "hint": "[Управление контекстом](https://docs.astrbot.app/en/use/context-compress.html)", - "description": "Стратегия управления контекстом", - "provider_settings": { - "max_context_length": { - "description": "Макс. раундов перед сжатием", - "hint": "Постоянная история диалога обрезается или сжимается LLM по стратегии ниже только после превышения этого числа раундов. Контекст перед запросом также ограничивается этим значением. -1 означает без ограничений по раундам." - }, - "dequeue_context_length": { - "description": "Раундов для удаления при превышении лимита", - "hint": "Когда история превышает лимит раундов и LLM-сжатие недоступно, за один раз удаляется это число самых старых раундов. Обрезка перед запросом также использует это значение." - }, - "context_limit_reached_strategy": { - "description": "Действие при лимите истории или давлении окна контекста", - "labels": [ - "Обрезать по раундам", - "Сжать с помощью LLM" - ], - "hint": "Постоянная история диалога использует эту стратегию только после превышения лимита раундов. Перед каждым запросом та же стратегия может защищать текущий контекст, когда токены приближаются к окну модели." - }, - "llm_compress_instruction": { - "description": "Инструкция для сжатия контекста", - "hint": "Если пусто, используется промпт по умолчанию." - }, - "llm_compress_keep_recent_ratio": { - "description": "Доля последних токенов контекста при сжатии", - "hint": "Сохраняет последние сообщения по доле текущих токенов контекста, от 0 до 0.3. 0.15 означает 15%; значение выше 0 сохраняет как минимум последний раунд." - }, - "llm_compress_provider_id": { - "description": "Модель для сжатия контекста", - "hint": "Если не выбрано, для сжатия используется текущая модель чата. Если модель недоступна или сжатие завершается ошибкой, AstrBot откатывается к обрезке по раундам." - }, - "fallback_max_context_tokens": { - "description": "Запасной размер окна контекста", - "hint": "Если max_context_tokens равен 0 и модель отсутствует во встроенных метаданных, используется это значение. По умолчанию: 128000." - } - } - }, - "others": { - "description": "Прочие настройки", - "provider_settings": { - "display_reasoning_text": { - "description": "Отображать процесс рассуждения (Reasoning)" - }, - "llm_safety_mode": { - "description": "Безопасный режим", - "hint": "Добавляет защитные фильтры к ответам модели." - }, - "safety_mode_strategy": { - "description": "Стратегия безопасного режима", - "hint": "Как применять защитные фильтры." - }, - "identifier": { - "description": "Идентификация пользователя", - "hint": "Если включено, информация об ID пользователя будет включена в промпт." - }, - "group_name_display": { - "description": "Отображать название группы", - "hint": "Если включено, название группы будет включено в промпт на поддерживаемых платформах (OneBot v11)." - }, - "datetime_system_prompt": { - "description": "Осведомленность о реальном времени", - "hint": "Если включено, информация о текущем времени будет добавлена в системный промпт." - }, - "show_tool_use_status": { - "description": "Выводить статус вызова функций" - }, - "show_tool_call_result": { - "description": "Выводить результаты работы инструментов", - "hint": "Работает только при включенном статусе вызова функций. Показывает макс. 70 символов." - }, - "buffer_intermediate_messages": { - "description": "Объединять промежуточные сообщения Agent", - "hint": "Если включено, промежуточный текст, созданный во время многошаговых вызовов инструментов в непотоковом режиме, будет буферизован и отправлен одним объединенным ответом после завершения Agent." - }, - "sanitize_context_by_modalities": { - "description": "Очистка истории по модальностям", - "hint": "Если включено, очищает контекст перед запросом, удаляя блоки (например, изображения), которые не поддерживаются выбранным провайдером." - }, - "max_quoted_fallback_images": { - "description": "Лимит загрузки изображений из пересланных сообщений", - "hint": "Максимальное количество изображений при парсинге цитируемых сообщений." - }, - "quoted_message_parser": { - "max_component_chain_depth": { - "description": "Глубина парсинга богатого текста", - "hint": "Максимальная глубина рекурсии при парсинге сложных компонентов в пересланных сообщениях." - }, - "max_forward_node_depth": { - "description": "Глубина вложенности пересылок", - "hint": "Максимальная глубина парсинга вложенных пересланных узлов." - }, - "max_forward_fetch": { - "description": "Лимит рекурсивного получения пересылок", - "hint": "Максимальное количество операций get_forward_msg." - }, - "warn_on_action_failure": { - "description": "Предупреждать при ошибке парсинга пересылок", - "hint": "Если включено, логирует предупреждения при неудачных попытках получения сообщений." - } - }, - "max_agent_step": { - "description": "Макс. количество раундов вызова инструментов" - }, - "tool_call_timeout": { - "description": "Таймаут вызова инструмента (сек)" - }, - "tool_schema_mode": { - "description": "Режим схемы инструментов", - "hint": "Skills-like сначала отправляет имя/описание и дозапрашивает параметры; Full отправляет полную схему сразу.", - "labels": [ - "Skills-like (двухэтапный)", - "Полная схема (Full)" - ] - }, - "streaming_response": { - "description": "Потоковый вывод (Streaming)" - }, - "unsupported_streaming_strategy": { - "description": "Для платформ без поддержки стриминга", - "hint": "Выберите метод обработки, если платформа не поддерживает потоковые ответы. 'Сегментированный ответ' отправляет части текста по мере появления знаков препинания.", - "labels": [ - "Сегментированный ответ в реальном времени", - "Отключить стриминг" - ] - }, - "wake_prefix": { - "description": "Дополнительный префикс пробуждения LLM", - "hint": "Если префикс пробуждения '/', а дополнительные — 'chat', то потребуется '/chat' для активации LLM." - }, - "prompt_prefix": { - "description": "Промпт пользователя", - "hint": "Вы можете использовать {{prompt}} как заполнитель для ввода. Если заполнитель не указан, он будет добавлен перед текстом пользователя." - }, - "image_compress_enabled": { - "description": "Включить сжатие изображений", - "hint": "Когда включено, большие локальные изображения сжимаются перед отправкой в мультимодальные модели." - }, - "image_compress_options": { - "description": "Настройки сжатия изображений", - "hint": "Управляет ограничением размера, качеством JPEG и минимальным порогом размера для сжатия.", - "max_size": { - "description": "Максимальная длина стороны", - "hint": "Максимальная длина стороны сжатого изображения в пикселях. Более крупные изображения пропорционально уменьшаются." - }, - "quality": { - "description": "Качество JPEG", - "hint": "Качество JPEG от 1 до 100. Более высокие значения сохраняют больше деталей, но увеличивают размер файла." - } - }, - "reachability_check": { - "description": "Проверка доступности провайдеров", - "hint": "При выполнении команды /provider проверяет связь со всеми моделями. Это может расходовать токены." - } - }, - "provider_tts_settings": { - "dual_output": { - "description": "Выводить и голос, и текст при включенном TTS" - } - } - } - }, - "platform_group": { - "name": "Платформы", - "platform": { - "description": "Адаптеры сообщений", - "active_send_mode": { - "description": "Использовать API активной отправки" - }, - "appid": { - "description": "ID приложения", - "hint": "Обязательно. App ID текущей платформы сообщений. См. документацию по интеграции платформы." - }, - "callback_server_host": { - "description": "Хост callback-сервера", - "hint": "Оставьте пустым для отключения." - }, - "card_template_id": { - "description": "ID шаблона карточки", - "hint": "Необязательно. Для интерактивных карточек DingTalk." - }, - "discord_activity_name": { - "description": "Название активности Discord", - "hint": "Статус бота в Discord. Оставьте пустым для отключения." - }, - "discord_allow_bot_messages": { - "description": "Разрешить сообщения от ботов", - "hint": "Если включено, AstrBot будет получать сообщения от других Discord-ботов. Полезно для взаимодействия между ботами (например, пересылка сообщений). По умолчанию отключено." - }, - "discord_command_register": { - "description": "Регистрировать слэш-команды Discord", - "hint": "Если включено, команды плагинов будут зарегистрированы как /команды Discord." - }, - "discord_proxy": { - "description": "Proxy URL для Discord", - "hint": "Формат: http://ip:port" - }, - "discord_token": { - "description": "Токен бота Discord", - "hint": "Введите сюда токен вашего Discord-бота." - }, - "enable": { - "description": "Включить", - "hint": "Включить этот адаптер. Отключенные адаптеры не будут получать сообщения." - }, - "enable_group_c2c": { - "description": "Включить личные сообщения из списка", - "hint": "Позволяет боту получать личные сообщения из списка QQ (может потребоваться добавление в друзья)." - }, - "enable_guild_direct_message": { - "description": "Включить ЛС в гильдиях", - "hint": "Позволяет боту получать прямые сообщения в рамках гильдий." - }, - "id": { - "description": "Имя бота", - "hint": "Отображаемое имя бота" - }, - "is_sandbox": { - "description": "Режим песочницы" - }, - "kf_name": { - "description": "Имя аккаунта службы поддержки WeChat", - "hint": "Необязательно. См. https://kf.weixin.qq.com/kf/frame#/accounts" - }, - "lark_connection_mode": { - "description": "Режим подписки", - "labels": [ - "Режим длинного соединения", - "Режим Webhook" - ] - }, - "lark_encrypt_key": { - "description": "Ключ шифрования", - "hint": "Для расшифровки данных обратного вызова Lark." - }, - "lark_verification_token": { - "description": "Токен верификации", - "hint": "Для проверки запросов обратного вызова Lark." - }, - "misskey_allow_insecure_downloads": { - "description": "Разрешить небезопасные загрузки (без SSL)", - "hint": "Отключает проверку SSL если у удаленного сервера проблемы с сертификатами. Используйте с осторожностью." - }, - "misskey_default_visibility": { - "description": "Видимость постов по умолчанию", - "hint": "public — всем, home — в ленте, followers — только подписчикам." - }, - "misskey_download_chunk_size": { - "description": "Размер чанка загрузки (байт)", - "hint": "Влияет на потребление памяти при загрузке файлов." - }, - "misskey_download_timeout": { - "description": "Таймаут загрузки (сек)", - "hint": "Максимльное время на загрузку файлов из сети." - }, - "misskey_enable_chat": { - "description": "Включить ответы в чатах", - "hint": "Бот будет отвечать на личные сообщения в Misskey." - }, - "misskey_enable_file_upload": { - "description": "Включить загрузку файлов в Misskey", - "hint": "Бот будет загружать файлы из сообщений в хранилище Misskey." - }, - "misskey_instance_url": { - "description": "URL инстанса Misskey", - "hint": "Например, https://misskey.example" - }, - "misskey_local_only": { - "description": "Только локально (без федерации)", - "hint": "Посты бота будут видны только на текущем инстансе." - }, - "misskey_max_download_bytes": { - "description": "Макс. размер загрузки (байт)", - "hint": "Лимит на размер загружаемых файлов для предотвращения нехватки памяти." - }, - "misskey_token": { - "description": "Токен доступа Misskey", - "hint": "Токен доступа к API, созданный в настройках сервиса подключения." - }, - "misskey_upload_concurrency": { - "description": "Лимит одновременных загрузок", - "hint": "По умолчанию 3." - }, - "misskey_upload_folder": { - "description": "ID папки в хранилище", - "hint": "Необязательно. Если пусто — в корень." - }, - "port": { - "description": "Порт callback-сервера", - "hint": "Оставьте пустым для отключения." - }, - "satori_api_base_url": { - "description": "Эндпоинт Satori API", - "hint": "Базовый URL для Satori API." - }, - "satori_auto_reconnect": { - "description": "Автоматическое переподключение", - "hint": "Переподключать WebSocket при разрыве." - }, - "satori_endpoint": { - "description": "WebSocket эндпоинт Satori", - "hint": "WebSocket-эндпоинт для событий Satori." - }, - "satori_heartbeat_interval": { - "description": "Интервал сердцебиения Satori", - "hint": "Интервал в секундах между отправкой heartbeat-сообщений." - }, - "satori_reconnect_delay": { - "description": "Задержка переподключения Satori", - "hint": "Задержка перед попыткой переподключения (в секундах)." - }, - "satori_token": { - "description": "Токен Satori", - "hint": "Токен для аутентификации в Satori API." - }, - "secret": { - "description": "Секрет (Secret)", - "hint": "Обязательно." - }, - "slack_connection_mode": { - "description": "Режим соединения Slack", - "hint": "webhook — через вебхуки, socket — через Socket Mode." - }, - "slack_webhook_host": { - "description": "Хост вебхука Slack", - "hint": "Действительно только если режим подключения Slack установлен как `webhook`." - }, - "slack_webhook_path": { - "description": "Путь вебхука Slack", - "hint": "Действительно только если режим подключения Slack установлен как `webhook`." - }, - "slack_webhook_port": { - "description": "Порт вебхука Slack", - "hint": "Действительно только если режим подключения Slack установлен как `webhook`." - }, - "telegram_command_auto_refresh": { - "description": "Автообновление команд Telegram", - "hint": "Если включено, AstrBot автоматически обновляет команды Telegram во время выполнения. (Одна эта настройка не имеет эффекта)" - }, - "telegram_command_register": { - "description": "Регистрация команд Telegram", - "hint": "Если включено, AstrBot автоматически регистрирует команды Telegram." - }, - "telegram_command_register_interval": { - "description": "Интервал автообновления команд Telegram", - "hint": "Интервал автоматического обновления команд Telegram в секундах." - }, - "telegram_token": { - "description": "Токен бота", - "hint": "Если вы находитесь в материковом Китае, установите прокси или измените api_base в разделе «Другие настройки»." - }, - "mattermost_url": { - "description": "URL Mattermost", - "hint": "Адрес сервиса Mattermost, например https://chat.example.com." - }, - "mattermost_bot_token": { - "description": "Bot Token Mattermost", - "hint": "Токен доступа, созданный после добавления bot-аккаунта в Mattermost." - }, - "mattermost_reconnect_delay": { - "description": "Задержка переподключения Mattermost", - "hint": "Сколько секунд ждать перед переподключением после разрыва WebSocket. По умолчанию 5 секунд." - }, - "type": { - "description": "Тип адаптера" - }, - "unified_webhook_mode": { - "description": "Единый режим Webhook", - "hint": "Использовать общий вход вебхуков AstrBot без открытия новых портов. URL: /api/v1/webhooks/platforms/{uuid}." - }, - "webhook_uuid": { - "description": "UUID вебхука", - "hint": "Создается автоматически при добавлении платформы." - }, - "wecom_ai_bot_name": { - "description": "Имя бота WeCom AI", - "hint": "Должно быть указано верно, иначе некоторые команды не будут работать." - }, - "wecom_ai_bot_connection_mode": { - "description": "Режим соединения WeCom AI", - "hint": "webhook требует Token/AESKey; long_connection требует BotID/Secret." - }, - "wecomaibot_friend_message_welcome_text": { - "description": "Приветствие в ЛС WeCom AI", - "hint": "Отправляется при первом входе пользователя в чат за день." - }, - "wecomaibot_init_respond_text": { - "description": "Начальный текст ответа WeCom AI", - "hint": "Первое сообщение, которое отправляет бот при получении запроса." - }, - "wecomaibot_token": { - "description": "Токен WeCom AI", - "hint": "Используется для аутентификации в режиме обратного вызова webhook." - }, - "wecomaibot_encoding_aes_key": { - "description": "EncodingAESKey для ИИ-бота WeCom", - "hint": "Используется для шифрования/дешифрования сообщений в режиме обратного вызова webhook." - }, - "wecomaibot_ws_bot_id": { - "description": "BotID для длинного соединения", - "hint": "Учетные данные BotID для режима длительного соединения ИИ-бота WeCom." - }, - "wecomaibot_ws_secret": { - "description": "Secret для длинного соединения", - "hint": "Учетные данные Secret для режима длительного соединения ИИ-бота WeCom." - }, - "wecomaibot_ws_url": { - "description": "WebSocket URL для длинного соединения", - "hint": "По умолчанию используется wss://openws.work.weixin.qq.com, обычно не требует изменений." - }, - "wecomaibot_heartbeat_interval": { - "description": "Интервал сердцебиения соединения", - "hint": "Интервал пульса (в секундах) в режиме длительного соединения. Рекомендуется 30 секунд." - }, - "wpp_active_message_poll": { - "description": "Включить активный опрос сообщений", - "hint": "Включите, только если сообщения WeChat приходят с задержкой." - }, - "wpp_active_message_poll_interval": { - "description": "Интервал опроса сообщений", - "hint": "По умолчанию 3 сек." - }, - "ws_reverse_host": { - "description": "Хост реверсивного WebSocket", - "hint": "AstrBot выступает в роли сервера." - }, - "ws_reverse_port": { - "description": "Порт реверсивного WebSocket" - }, - "ws_reverse_token": { - "description": "Токен реверсивного WebSocket", - "hint": "Токен обратного WebSocket (Reverse WebSocket). Если не задан, проверка токена будет отключена." - }, - "msg_push_webhook_url": { - "description": "URL вебхука для пуш-сообщений WeCom", - "hint": "Рекомендуется для корректной отправки всех типов сообщений." - }, - "only_use_webhook_url_to_send": { - "description": "Отправлять ответы только через Webhook", - "hint": "Все ответы WeCom AI Bot будут идти через вебхук пуш-сообщений. Поддерживает больше типов контента." - }, - "weixin_oc_base_url": { - "description": "URL API iLink", - "hint": "Значение по умолчанию: https://ilinkai.weixin.qq.com" - }, - "weixin_oc_bot_type": { - "description": "bot_type (параметр входа по QR)", - "hint": "Значение по умолчанию: 3" - }, - "weixin_oc_qr_poll_interval": { - "description": "Интервал опроса статуса QR (сек)", - "hint": "Пауза между запросами статуса QR-кода." - }, - "weixin_oc_long_poll_timeout_ms": { - "description": "Таймаут long-poll getUpdates (мс)", - "hint": "Параметр таймаута запроса получения сообщений." - }, - "weixin_oc_api_timeout_ms": { - "description": "Таймаут HTTP запроса (мс)", - "hint": "Общий таймаут для HTTP запросов." - }, - "weixin_oc_token": { - "description": "Token после входа (опционально)", - "hint": "Автоматически сохраняется после QR логина; для сложных сценариев можно ввести вручную." - }, - "kook_bot_token": { - "description": "Токен бота", - "type": "string", - "hint": "Обязательно. Токен бота, полученный на платформе разработчиков KOOK." - }, - "kook_bot_nickname": { - "description": "Никнейм бота", - "type": "string", - "hint": "Сообщения от пользователя с таким никнеймом будут игнорироваться." - }, - "kook_reconnect_delay": { - "description": "Задержка переподключения", - "type": "int", - "hint": "Задержка перед повторным подключением (в секундах), используется стратегия экспоненциальной задержки (exponential backoff)." - }, - "kook_max_reconnect_delay": { - "description": "Макс. задержка переподключения", - "type": "int", - "hint": "Максимальное значение задержки для повторного подключения (в секундах)." - }, - "kook_max_retry_delay": { - "description": "Макс. задержка повтора", - "type": "int", - "hint": "Максимальное время задержки для повторных попыток (в секундах)." - }, - "kook_heartbeat_interval": { - "description": "Интервал сердцебиения", - "type": "int", - "hint": "Интервал времени для проверки пульса (в секундах)." - }, - "kook_heartbeat_timeout": { - "description": "Таймаут сердцебиения", - "type": "int", - "hint": "Длительность ожидания (таймаут) для проверки пульса (в секундах)." - }, - "kook_max_heartbeat_failures": { - "description": "Макс. количество сбоев сердцебиения", - "type": "int", - "hint": "Максимально допустимое количество сбоев пульса; если лимит превышен, соединение будет разорвано." - }, - "kook_max_consecutive_failures": { - "description": "Макс. количество последовательных сбоев", - "type": "int", - "hint": "Максимально допустимое количество последовательных сбоев; если лимит превышен, повторные попытки будут остановлены." - } - }, - "general": { - "description": "Общие", - "admins_id": { - "description": "ID администраторов" - }, - "platform_settings": { - "unique_session": { - "description": "Изолировать сессии", - "hint": "У каждого участника группы будет свой независимый контекст." - }, - "friend_message_needs_wake_prefix": { - "description": "Личные сообщения требуют префикс пробуждения" - }, - "reply_prefix": { - "description": "Префикс текста ответа" - }, - "reply_with_mention": { - "description": "Упоминать отправителя в ответе" - }, - "reply_with_quote": { - "description": "Цитировать сообщение отправителя в ответе" - }, - "forward_threshold": { - "description": "Порог количества слов для пересылки" - }, - "empty_mention_waiting": { - "description": "Реагировать на пустое упоминание (@бота)" - } - }, - "wake_prefix": { - "description": "Префикс пробуждения" - }, - "disable_builtin_commands": { - "description": "Отключить встроенные команды", - "hint": "Отключает help, sid, new и другие базовые команды." - } - }, - "whitelist": { - "description": "Белый список", - "platform_settings": { - "enable_id_white_list": { - "description": "Включить белый список", - "hint": "Бот будет отвечать только в разрешенных сессиях." - }, - "id_whitelist": { - "description": "Список разрешенных ID", - "hint": "Используйте /sid для получения ID." - }, - "id_whitelist_log": { - "description": "Выводить логи", - "hint": "Логировать попытки доступа от пользователей не из белого списка." - }, - "wl_ignore_admin_on_group": { - "description": "Администраторы в группах обходят белый список" - }, - "wl_ignore_admin_on_friend": { - "description": "Администраторы в ЛС обходят белый список" - } - } - }, - "rate_limit": { - "description": "Лимит частоты", - "platform_settings": { - "rate_limit": { - "time": { - "description": "Время лимита сообщений (сек)" - }, - "count": { - "description": "Количество сообщений для лимита" - }, - "strategy": { - "description": "Стратегия лимитирования" - } - } - } - }, - "content_safety": { - "description": "Безопасность контента", - "content_safety": { - "also_use_in_response": { - "description": "Проверять также ответы модели" - }, - "baidu_aip": { - "enable": { - "description": "Использовать Baidu Content Safety", - "hint": "Требуется установка библиотеки baidu-aip." - }, - "app_id": { - "description": "App ID" - }, - "api_key": { - "description": "API Key" - }, - "secret_key": { - "description": "Secret Key" - } - }, - "internal_keywords": { - "enable": { - "description": "Проверка по ключевым словам" - }, - "extra_keywords": { - "description": "Дополнительные слова", - "hint": "Список запрещенных слов, поддерживает регулярные выражения." - } - } - } - }, - "t2i": { - "description": "Текст в изображение", - "t2i": { - "description": "Вывод текста картинкой" - }, - "t2i_word_threshold": { - "description": "Порог количества слов для перевода в картинку" - } - }, - "others": { - "description": "Прочие настройки", - "platform_settings": { - "ignore_bot_self_message": { - "description": "Игнорировать собственные сообщения бота" - }, - "ignore_at_all": { - "description": "Игнорировать события @all" - }, - "no_permission_reply": { - "description": "Ответ при отсутствии прав у пользователя" - } - }, - "platform_specific": { - "lark": { - "pre_ack_emoji": { - "enable": { - "description": "[Lark] Пре-эмодзи подтверждения" - }, - "emojis": { - "description": "Список эмодзи (Lark Enum Names)", - "hint": "Справка по именам эмодзи Lark." - } - } - }, - "telegram": { - "pre_ack_emoji": { - "enable": { - "description": "[Telegram] Пре-эмодзи подтверждения" - }, - "emojis": { - "description": "Список эмодзи (Unicode)", - "hint": "Telegram поддерживает ограниченный набор реакций." - } - } - }, - "discord": { - "pre_ack_emoji": { - "enable": { - "description": "[Discord] Пре-эмодзи подтверждения" - }, - "emojis": { - "description": "Список эмодзи (Unicode или имя эмодзи)", - "hint": "Введите Unicode эмодзи, напр. 👍, 🤔, ⏳" - } - } - } - } - } - }, - "plugin_group": { - "name": "Плагины", - "plugin": { - "description": "Управление плагинами", - "plugin_set": { - "description": "Доступные плагины", - "hint": "Все невыключенные плагины включены по умолчанию. Если плагин отключен на странице плагинов, выбор здесь не будет иметь силы." - } - } - }, - "ext_group": { - "name": "Расширения (Extensions)", - "segmented_reply": { - "description": "Сегментированный ответ", - "platform_settings": { - "segmented_reply": { - "enable": { - "description": "Включить сегментированные ответы" - }, - "only_llm_result": { - "description": "Сегментировать только ответы LLM" - }, - "interval_method": { - "description": "Метод интервала", - "hint": "random — случайное время, log — расчет на основе длины сообщения: $y=log_{log\\_base}(x)$, где x — количество знаков, y — секунды." - }, - "interval": { - "description": "Случайный интервал времени", - "hint": "Формат: минимум,максимум (напр., 1.5,3.5)" - }, - "log_base": { - "description": "Основание логарифма", - "hint": "Основание для логарифмических интервалов, по умолчанию 2.6. Диапазон: 1.0-10.0." - }, - "words_count_threshold": { - "description": "Порог сегментации (количество знаков)", - "hint": "Порог количества знаков для сегментированного ответа. Сообщения короче этого лимита будут сегментированы, длиннее — отправлены целиком." - }, - "split_mode": { - "description": "Режим разделения", - "hint": "Используется для сегментации сообщения. По умолчанию разделяется знаками препинания (точка, вопрос и т.д.). Например, `[。?!]` удалит все точки, вопросительные и восклицательные знаки. re.findall(r'', text)", - "labels": [ - "Регулярное выражение", - "Список слов" - ] - }, - "regex": { - "description": "Регулярное выражение для сегментации", - "hint": "Используется для поиска точек разделения с помощью регулярного выражения. Рекомендуется использовать паттерны, соответствующие разделителям." - }, - "split_words": { - "description": "Список слов-разделителей", - "hint": "Разделять при обнаружении любого слова из списка" - }, - "content_cleanup_rule": { - "description": "Регулярное выражение для фильтрации контента", - "hint": "Удаляет указанный контент из сегментированных частей. Например, `[。?!]` удалит точки, вопросы и восклицательные знаки." - } - } - } - }, - "ltm": { - "description": "Контекстная осведомленность в группах (ранее — Улучшение памяти чата)", - "provider_ltm_settings": { - "group_icl_enable": { - "description": "Включить осведомленность о контексте группы" - }, - "group_message_max_cnt": { - "description": "Максимальное количество сообщений" - }, - "image_caption": { - "description": "Автоматическое понимание изображений", - "hint": "Требуется настройка модели описания изображений для группового чата." - }, - "image_caption_provider_id": { - "description": "Модель описания изображений для групп", - "hint": "Используется для понимания изображений в контексте группового чата, настраивается отдельно от основной модели." - }, - "active_reply": { - "enable": { - "description": "Активный ответ" - }, - "method": { - "description": "Метод активного ответа" - }, - "possibility_reply": { - "description": "Вероятность ответа", - "hint": "Значение от 0.0 до 1.0" - }, - "whitelist": { - "description": "Белый список для активных ответов", - "hint": "Фильтрация отключена, если список пуст. Используйте /sid для получения ID." - } - } - } - } - }, - "system_group": { - "name": "Система", - "system": { - "description": "Системные настройки", - "t2i_strategy": { - "description": "Стратегия текст-в-изображение", - "hint": "Стратегия текст-в-изображение. `remote` — удаленный рендеринг, `local` — библиотека PIL. Для локального режима положите font.ttf в data/." - }, - "t2i_endpoint": { - "description": "Эндпоинт API сервиса текст-в-изображение", - "hint": "Использовать API AstrBot, если пусто" - }, - "t2i_template": { - "description": "Пользовательский шаблон текст-в-изображение", - "hint": "Если включено, можно использовать свои HTML-шаблоны." - }, - "t2i_active_template": { - "description": "Текущий активный шаблон рендеринга", - "hint": "Значение управляется на странице шаблонов." - }, - "log_level": { - "description": "Уровень логирования консоли", - "hint": "Уровень логирования в консоли." - }, - "log_file_enable": { - "description": "Включить логирование в файл", - "hint": "Записывать логи в файл." - }, - "log_file_path": { - "description": "Путь к файлу логов", - "hint": "Пути относительно data/, напр. logs/astrbot.log." - }, - "log_file_max_mb": { - "description": "Макс. размер файла логов (МБ)", - "hint": "Ротация при достижении размера. По умолчанию 20МБ." - }, - "temp_dir_max_size": { - "description": "Лимит размера временной директории (МБ)", - "hint": "Лимит временной папки (МБ). Система проверяет каждые 10 минут и удаляет старое при переполнении." - }, - "trace_log_enable": { - "description": "Включить логирование трассировки", - "hint": "Записывать трассировку в отдельный файл." - }, - "trace_log_path": { - "description": "Путь к файлу логов трассировки", - "hint": "Относительные пути определяются относительно директории данных, например, logs/astrbot.trace.log; абсолютные пути также поддерживаются." - }, - "trace_log_max_mb": { - "description": "Макс. размер файла логов трассировки (МБ)", - "hint": "Ротация при достижении размера. По умолчанию 20МБ." - }, - "pip_install_arg": { - "description": "Дополнительные аргументы установки pip", - "hint": "При установке зависимостей можно указать аргументы pip, напр. --break-system-package." - }, - "pypi_index_url": { - "description": "URL репозитория PyPI", - "hint": "URL репозитория PyPI. По умолчанию: [https://mirrors.aliyun.com/pypi/simple/](https://mirrors.aliyun.com/pypi/simple/)" - }, - "callback_api_base": { - "description": "Внешний адрес для Callback API", - "hint": "Используется для сервисов, требующих обратных выливов (например, в песочнице)." - }, - "disable_metrics": { - "description": "Отключить анонимную статистику", - "hint": "После отключения AstrBot не будет отправлять анонимные данные об использовании." - }, - "dashboard": { - "trust_proxy_headers": { - "description": "Доверять прокси-заголовкам для IP клиента", - "hint": "Если выключено, X-Forwarded-For/X-Real-IP игнорируются и используется только адрес соединения." - }, - "auth_rate_limit": { - "enable": { - "description": "Включить ограничение скорости входа", - "hint": "Если выключено, конечные точки аутентификации (вход, TOTP и т.д.) не будут ограничены по скорости." - }, - "average_interval": { - "description": "Средний интервал ограничения скорости конечных точек (сек)", - "hint": "Минимальный средний интервал между запросами аутентификации. Например, 1.0 означает не более 1 запроса в секунду." - }, - "max_burst": { - "description": "Максимальный всплеск ограничения скорости конечных точек", - "hint": "Максимальное количество последовательных всплесков запросов. Например, 3 допускает до 3 запросов за короткий всплеск." - } - }, - "ssl": { - "enable": { - "description": "Включить HTTPS для WebUI", - "hint": "Если включено, панель управления работает по HTTPS." - }, - "cert_file": { - "description": "Путь к файлу сертификата SSL", - "hint": "Путь к файлу сертификата (PEM)." - }, - "key_file": { - "description": "Путь к ключу SSL", - "hint": "Путь к приватному ключу (PEM)." - }, - "ca_certs": { - "description": "Путь к сертификату CA SSL", - "hint": "Опционально. Путь к сертификату CA." - } - }, - "totp": { - "enable": { - "description": "Включить TOTP для WebUI", - "hint": "Когда включено, TOTP-код требуется для входа в панель управления." - }, - "manage": "Управление", - "configuration": "TOTP", - "statusPending": "Требуется настройка", - "statusEnabled": "Включено", - "setupRequiredHint": "TOTP включен, но ещё не настроен. Откройте «Управление», чтобы завершить настройку.", - "setupTitle": "Настройка TOTP", - "setupSubtitle": "Отсканируйте QR-код в приложении-аутентификаторе и введите код подтверждения.", - "setupConfirm": "Подтвердить и продолжить", - "activeSubtitle": "Используйте этот QR-код или секрет для добавления нового устройства-аутентификатора.", - "rotateTitle": "Смена секрета TOTP", - "rotateSubtitle": "Сгенерируйте новый секрет и подтвердите его перед заменой текущего.", - "rotate": "Сменить", - "rotateRecovery": "Сменить код восстановления", - "rotateConfirm": "Подтвердить смену", - "rotateCancel": "Отмена", - "rotateCode": "Код подтверждения", - "rotateCodeHint": "Введите код из приложения-аутентификатора для подтверждения нового ключа.", - "rotateError": "Неверный код, попробуйте снова.", - "recoveryTitle": "Коды восстановления", - "recoverySubtitle": "Этот код показывается один раз. Сохраните его перед продолжением.", - "recoveryWarning": "При утере этого кода восстановить доступ к учётной записи обычными средствами будет невозможно.", - "recoveryAcknowledge": "Я сохранил(а) коды восстановления", - "recoveryClose": "Готово", - "disableTitle": "Отключить TOTP", - "disableSubtitle": "Введите код подтверждения для отключения двухфакторной аутентификации.", - "disableRecoverySubtitle": "Введите код восстановления для отключения двухфакторной аутентификации.", - "disableCode": "Код подтверждения", - "disableRecoveryCode": "Код восстановления", - "disableConfirm": "Отключить", - "disableCancel": "Отмена", - "disableError": "Ошибка проверки. Попробуйте снова.", - "disableUseRecovery": "Не можете использовать TOTP?", - "disableUseCode": "Использовать код подтверждения", - "configSaveTitle": "Двухфакторная проверка", - "configSaveSubtitle": "Введите код подтверждения для изменения защищённой конфигурации.", - "configSaveRotationHint": "При смене TOTP-ключа принимаются как старый, так и новый коды подтверждения (только однократно при смене).", - "configSaveCode": "Код подтверждения", - "configSaveConfirm": "Продолжить", - "configSaveCancel": "Отмена", - "configSaveError": "Ошибка проверки. Попробуйте снова." - } - }, - "timezone": { - "description": "Часовой пояс", - "hint": "Например, Europe/Moscow." - }, - "http_proxy": { - "description": "HTTP прокси", - "hint": "Формат: http://user:pass@ip:port" - }, - "no_proxy": { - "description": "Список исключений прокси" - } - } - }, - "provider_group": { - "provider": { - "genie_onnx_model_dir": { - "description": "Директория моделей ONNX", - "hint": "Путь к директории с файлами моделей ONNX" - }, - "genie_language": { - "description": "Язык" - }, - "xai_native_search": { - "description": "Включить нативный поиск", - "hint": "Если включено, использует Live Search от xAI для веб-запросов (тарифицируется отдельно). Применимо только к провайдерам xAI." - }, - "rerank_api_base": { - "description": "Base URL API модели Rerank", - "hint": "Полный URL запроса формируется путём добавления суффикса к Base URL (по умолчанию /v1/rerank)." - }, - "rerank_api_suffix": { - "description": "Суффикс пути API", - "hint": "Суффикс пути, добавляемый к base_url, например /v1/rerank. Оставьте пустым, чтобы не добавлять." - }, - "rerank_api_key": { - "description": "API Key", - "hint": "Оставьте пустым, если API key не требуется." - }, - "rerank_model": { - "description": "Имя модели Rerank" - }, - "return_documents": { - "description": "Возвращать исходные документы в результатах Rerank", - "hint": "По умолчанию false для снижения сетевой нагрузки." - }, - "instruct": { - "description": "Описание задачи для Rerank", - "hint": "Эффективно только для моделей qwen3-rerank. Рекомендуется писать на английском." - }, - "nvidia_rerank_api_base": { - "description": "Базовый URL API" - }, - "nvidia_rerank_api_key": { - "description": "API-ключ" - }, - "nvidia_rerank_model": { - "description": "Название модели Rerank", - "hint": "Укажите название модели в соответствии с документацией NVIDIA." - }, - "nvidia_rerank_model_endpoint": { - "description": "Пользовательский endpoint модели", - "hint": "Пользовательский суффикс URL endpoint, по умолчанию /reranking." - }, - "nvidia_rerank_truncate": { - "description": "Стратегия усечения текста", - "hint": "Определяет, следует ли усекать входной текст, если он слишком длинный и не помещается в максимальную длину контекста модели." - }, - "launch_model_if_not_running": { - "description": "Автозапуск модели", - "hint": "Если модель не запущена в Xinference, попытаться запустить её автоматически. Рекомендуется отключать в продакшене." - }, - "modalities": { - "description": "Возможности модели", - "hint": "Поддерживаемые модальности. Если модель не поддерживает изображения, снимите галочку с 'Image'.", - "labels": [ - "Текст", - "Изображение", - "Аудио", - "Инструменты" - ] - }, - "custom_headers": { - "description": "Заголовки запроса", - "hint": "Пары ключ/значение будут добавлены в заголовки запроса (default_headers). Значения должны быть строками." - }, - "custom_extra_body": { - "description": "Параметры тела запроса", - "hint": "Добавление дополнительных параметров в запрос (temperature, top_p и др.).", - "template_schema": { - "temperature": { - "description": "Температура", - "hint": "Контролирует случайность, обычно от 0 до 2. Чем выше, тем более случайные ответы.", - "name": "Температура" - }, - "top_p": { - "description": "Top-p семплирование", - "hint": "Параметр ядерного семплирования (Nucleus sampling), обычно 0-1.", - "name": "Top-p" - }, - "max_tokens": { - "description": "Макс. токенов", - "hint": "Максимальное количество генерируемых токенов.", - "name": "Макс. токены" - } - } - }, - "gpt_weights_path": { - "description": "Путь к файлу модели GPT", - "hint": "Файл .ckpt. Используйте абсолютный путь без кавычек. Оставьте пустым для использования встроенной модели GPT_SoVITS." - }, - "sovits_weights_path": { - "description": "Путь к файлу модели SoVITS", - "hint": "Файл .pth. Используйте абсолютный путь без кавычек. Оставьте пустым для использования встроенной модели GPT_SoVITS." - }, - "gsv_default_parms": { - "description": "Параметры GPT_SoVITS по умолчанию", - "hint": "Путь к эталонному аудио и текст обязательны; остальные параметры опциональны.", - "gsv_ref_audio_path": { - "description": "Путь к эталонному аудио", - "hint": "Обязательно! Используйте абсолютный путь без кавычек." - }, - "gsv_prompt_text": { - "description": "Текст эталонного аудио", - "hint": "Обязательно! Укажите содержание эталонного аудио." - }, - "gsv_prompt_lang": { - "description": "Язык текста эталонного аудио", - "hint": "Язык текста эталонного аудио; по умолчанию китайский." - }, - "gsv_aux_ref_audio_paths": { - "description": "Пути к вспомогательным эталонным аудио", - "hint": "Вспомогательные файлы эталонного аудио; опционально." - }, - "gsv_text_lang": { - "description": "Язык текста", - "hint": "По умолчанию китайский." - }, - "gsv_top_k": { - "description": "Разнообразие речи", - "hint": "" - }, - "gsv_top_p": { - "description": "Порог ядерного семплирования", - "hint": "" - }, - "gsv_temperature": { - "description": "Случайность речи", - "hint": "" - }, - "gsv_text_split_method": { - "description": "Метод разделения текста", - "hint": "Варианты: `cut0` без разделения, `cut1` каждые 4 предложения, `cut2` каждые 50 знаков, `cut3` по китайской точке, `cut4` по английской точке, `cut5` по знакам препинания." - }, - "gsv_batch_size": { - "description": "Размер пакета (Batch size)", - "hint": "" - }, - "gsv_batch_threshold": { - "description": "Порог пакета (Batch threshold)", - "hint": "API Key" - }, - "gsv_split_bucket": { - "description": "Разделять текст на корзины для параллельной обработки", - "hint": "API Base URL" - }, - "gsv_speed_factor": { - "description": "Скорость воспроизведения речи", - "hint": "1 — исходная скорость." - }, - "gsv_fragment_interval": { - "description": "Интервал между сегментами речи", - "hint": "Скорость речи для синтеза, диапазон [0.5, 2], по умолчанию 1.0. Большее значение — быстрее." - }, - "gsv_streaming_mode": { - "description": "Включить потоковый режим", - "hint": "голос" - }, - "gsv_seed": { - "description": "Случайное число (Seed)", - "hint": "Для воспроизводимости результатов." - }, - "gsv_parallel_infer": { - "description": "Параллельный вывод (Inference)", - "hint": "reference_id" - }, - "gsv_repetition_penalty": { - "description": "Штраф за повторение", - "hint": "API Key" - }, - "gsv_media_type": { - "description": "Тип медиафайла", - "hint": "Рекомендуется: wav" - } - }, - "embedding_dimensions": { - "description": "Размерность эмбеддингов", - "hint": "Размерность векторов эмбеддингов. Зависит от модели. Должно быть указано верно для работы векторной базы данных." - }, - "embedding_model": { - "description": "Модель эмбеддингов", - "hint": "Имя модели эмбеддингов." - }, - "embedding_api_key": { - "description": "API Base URL" - }, - "embedding_api_base": { - "description": "Адрес прокси-сервера" - }, - "openai_embedding": { - "hint": "Если тест не проходит, попробуйте добавить /v1 в конец embedding_api_base для совместимости с некоторыми версиями OpenAI API." - }, - "gemini_embedding": { - "hint": "Gemini Embedding не требует ручного добавления /v1beta." - }, - "volcengine_cluster": { - "description": "Кластер Volcengine", - "hint": "Для моделей клонирования голоса выберите volcano_icl или volcano_icl_concurr; по умолчанию volcano_tts." - }, - "volcengine_voice_type": { - "description": "Голос Volcengine", - "hint": "Введите ID голоса (Voice_type)." - }, - "volcengine_speed_ratio": { - "description": "Скорость речи", - "hint": "Скорость речи, от 0.2 до 3.0, по умолчанию 1.0." - }, - "volcengine_volume_ratio": { - "description": "Громкость", - "hint": "Громкость, от 0.0 до 2.0, по умолчанию 1.0." - }, - "azure_tts_voice": { - "description": "Стиль голоса", - "hint": "Системное имя голоса" - }, - "azure_tts_style": { - "description": "Стиль", - "hint": "Стиль речи. Может выражать эмоции (счастье, сочувствие, спокойствие)." - }, - "azure_tts_role": { - "description": "Роль (опционально)", - "hint": "Ролевая модель. Голос может имитировать разные возрасты и пол без смены имени голоса. Если роль не поддерживается, атрибут игнорируется." - }, - "azure_tts_rate": { - "description": "Скорость речи", - "hint": "Контролирует скорость речи. От 0.5x до 2x от оригинала." - }, - "azure_tts_volume": { - "description": "Громкость речи", - "hint": "Контролирует громкость. От 0.0 до 100.0. По умолчанию 100.0." - }, - "azure_tts_region": { - "description": "Регион API", - "hint": "Регион обработки данных Azure TTS." - }, - "azure_tts_subscription_key": { - "description": "Ключ подписки (Subscription Key)", - "hint": "Ключ подписки Azure TTS (не токен)." - }, - "dashscope_tts_voice": { - "description": "Голос" - }, - "gm_resp_image_modal": { - "description": "Включить визуальную модальность", - "hint": "Если включено, ответы могут содержать изображения. Требует поддержки моделью. Совет: для генерации изображений отключите 'Распознавание участников'." - }, - "gm_native_search": { - "description": "Включить нативный поиск", - "hint": "Если включено, инструменты функций отключаются. Проверьте лимиты бесплатной квоты." - }, - "gm_native_coderunner": { - "description": "Включить нативный исполнитель кода", - "hint": "Если включено, инструменты функций отключаются." - }, - "gm_url_context": { - "description": "Включить контекст URL", - "hint": "Если включено, инструменты функций отключаются." - }, - "gm_safety_settings": { - "description": "Фильтры безопасности", - "hint": "Настройка уровня фильтрации контента. См. документацию Gemini API.", - "harassment": { - "description": "Харрасмент", - "hint": "Негативные или вредные комментарии" - }, - "hate_speech": { - "description": "Язык вражды", - "hint": "Грубый, неуважительный или нецензурный контент" - }, - "sexually_explicit": { - "description": "Сексуально откровенный контент", - "hint": "Упоминания сексуальных актов или другой непристойный контент" - }, - "dangerous_content": { - "description": "Опасный контент", - "hint": "Контент, поощряющий или способствующий вредному поведению" - } - }, - "gm_thinking_config": { - "description": "Настройки рассуждения (Thinking)", - "budget": { - "description": "Бюджет рассуждения", - "hint": "Указывает модели количество токенов рассуждения. См. документацию Google." - }, - "level": { - "description": "Уровень рассуждения", - "hint": "Рекомендуется для моделей Gemini 3+. Позволяет контролировать процесс рассуждения." - } - }, - "anth_thinking_config": { - "description": "Настройки рассуждения (Thinking)", - "type": { - "description": "Тип рассуждения", - "hint": "Установите 'adaptive' для Opus 4.6+ / Sonnet 4.6+ (рекомендуется)." - }, - "budget": { - "description": "Бюджет рассуждения", - "hint": "Параметр budget_tokens (минимум 1024). Устарело для моделей 4.6+." - }, - "effort": { - "description": "Уровень усилий", - "hint": "Контролирует глубину рассуждений в режиме 'adaptive'." - } - }, - "minimax-group-id": { - "description": "Группа пользователей", - "hint": "Доступно в Управлении аккаунтом -> Основная информация." - }, - "minimax-langboost": { - "description": "Целевой язык/диалект", - "hint": "Улучшает распознавание и качество речи для определенных языков/диалектов." - }, - "minimax-voice-speed": { - "description": "Скорость речи", - "hint": "API Key" - }, - "minimax-voice-vol": { - "description": "Громкость", - "hint": "Громкость синтеза от 0 до 10. Чем выше, тем громче." - }, - "minimax-voice-pitch": { - "description": "Высота голоса", - "hint": "Высота голоса от -12 до 12." - }, - "minimax-is-timber-weight": { - "description": "Включить смешанные голоса", - "hint": "Смешивание до четырех голосов с весами. Если включено, настройки одиночного голоса игнорируются." - }, - "minimax-timber-weight": { - "description": "Смешанные голоса", - "hint": "Список голосов и их весов (JSON-строка). См. документацию API." - }, - "minimax-voice-id": { - "description": "Одиночный голос", - "hint": "ID одиночного голоса; см. официальную документацию." - }, - "minimax-voice-emotion": { - "description": "Эмоция", - "hint": "Эмоция речи. 'auto' выбирает эмоцию на основе текста." - }, - "minimax-voice-latex": { - "description": "Читать формулы LaTeX", - "hint": "Чтение формул LaTeX (требуется правильное форматирование)." - }, - "minimax-voice-english-normalization": { - "description": "Нормализация английского текста", - "hint": "Улучшает чтение чисел, но немного увеличивает задержку." - }, - "rag_options": { - "description": "Опции RAG", - "hint": "Настройки извлечения из базы знаний. В приложениях Bailian отключает многоэтапные диалоги.", - "pipeline_ids": { - "description": "Список ID баз знаний", - "hint": "Извлечение из указанных баз данных Bailian." - }, - "file_ids": { - "description": "ID неструктурированных документов", - "hint": "Извлечение из указанных документов Bailian." - }, - "output_reference": { - "description": "Выводить ссылки на источники", - "hint": "Добавлять источники в конец ответа. По умолчанию False." - } - }, - "sensevoice_hint": { - "description": "Развертывание SenseVoice", - "hint": "Перед включением установите необходимые библиотеки: funasr, torch и др. Также требуется ffmpeg." - }, - "is_emotion": { - "description": "Распознавание эмоций", - "hint": "Включить распознавание эмоций (радость, грусть, гнев и т.д.)." - }, - "stt_model": { - "description": "Имя модели", - "hint": "Имя модели на ModelScope." - }, - "variables": { - "description": "Фиксированные переменные воркфлоу", - "hint": "Входные переменные для воркфлоу. Можно также задавать через /set в чате." - }, - "dashscope_app_type": { - "description": "Тип приложения", - "hint": "Тип приложения Bailian." - }, - "timeout": { - "description": "Таймаут (сек)", - "hint": "Максимальное время ожидания ответа." - }, - "openai-tts-voice": { - "description": "API Base URL", - "hint": "Голоса OpenAI TTS: alloy, echo и др." - }, - "elevenlabs-tts-voice-id": { - "description": "ID голоса", - "hint": "ID голоса ElevenLabs. Просмотрите и скопируйте ID на https://elevenlabs.io/app/voice-library. По умолчанию 'JBFqnCBsd6RMkjVDRZzb' (George)." - }, - "elevenlabs-tts-output-format": { - "description": "Формат вывода", - "hint": "Формат аудио, например 'mp3_44100_128', 'mp3_22050_32', 'wav_44100' или 'opus_48000_128'. Raw PCM/u-law/a-law форматы не поддерживаются. По умолчанию 'mp3_44100_128'." - }, - "elevenlabs-tts-stability": { - "description": "Стабильность", - "hint": "Стабильность голоса, диапазон [0, 1]. Оставьте пустым для значения по умолчанию." - }, - "elevenlabs-tts-similarity-boost": { - "description": "Усиление сходства", - "hint": "Насколько вывод соответствует исходному голосу, диапазон [0, 1]. Оставьте пустым для значения по умолчанию." - }, - "elevenlabs-tts-style": { - "description": "Выразительность стиля", - "hint": "Выразительность стиля голоса, диапазон [0, 1]. Высокие значения увеличивают задержку. Оставьте пустым для значения по умолчанию." - }, - "elevenlabs-tts-use-speaker-boost": { - "description": "Усиление диктора", - "hint": "Усиливает сходство с исходным диктором. Может немного увеличить задержку." - }, - "mimo-tts-voice": { - "description": "Голос", - "hint": "Имя голоса MiMo TTS. Поддерживаются значения 'mimo_default', 'default_en' и 'default_zh'." - }, - "mimo-tts-format": { - "description": "Формат вывода", - "hint": "Формат аудио, создаваемого MiMo TTS. Поддерживаются 'wav', 'mp3' и 'pcm'." - }, - "mimo-tts-style-prompt": { - "description": "Подсказка стиля", - "hint": "Добавляется в начало синтезируемого текста в виде тега и управляет скоростью, эмоцией, ролью или манерой речи. Необязательно." - }, - "mimo-tts-dialect": { - "description": "Диалект", - "hint": "Объединяется с подсказкой стиля внутри начального тега , например северо-восточный, сычуаньский, хэнаньский или кантонский вариант речи. Необязательно." - }, - "mimo-tts-seed-text": { - "description": "Начальный текст", - "hint": "Отправляется как необязательное user-сообщение для настройки тона и манеры речи. Не добавляется к самому тексту синтеза." - }, - "fishaudio-tts-character": { - "description": "Персонаж", - "hint": "Персонаж Fishaudio. По умолчанию Klee." - }, - "fishaudio-tts-reference-id": { - "description": "Coze API Key", - "hint": "ID модели Fishaudio; используется вместо имени роли." - }, - "whisper_hint": { - "description": "Заметки по локальному развертыванию Whisper", - "hint": "Перед включением установите openai-whisper и ffmpeg." - }, - "whisper_device": { - "description": "Устройство инференса", - "hint": "Устройство для инференса Whisper. На Apple Silicon можно выбрать mps; в остальных средах рекомендуется cpu. Если выбран mps, но он недоступен, AstrBot автоматически переключится на cpu." - }, - "id": { - "description": "ID провайдера" - }, - "type": { - "description": "Тип провайдера" - }, - "provider_type": { - "description": "Тип возможностей провайдера" - }, - "enable": { - "description": "Включить" - }, - "key": { - "description": "Ключ Coze API для доступа к сервисам Coze." - }, - "api_base": { - "description": "Bot ID" - }, - "proxy": { - "description": "API Base URL", - "hint": "Индивидуальный прокси для этого провайдера." - }, - "model": { - "description": "Имя модели", - "hint": "Имя модели, напр. gpt-4o-mini." - }, - "max_context_tokens": { - "description": "Размер окна контекста модели", - "hint": "Максимальное количество токенов контекста. Если 0 — автозаполнение." - }, - "dify_api_key": { - "description": "Базовый URL для Coze API. По умолчанию: https://api.coze.cn", - "hint": "API-ключ Dify (обязательно)." - }, - "dify_api_base": { - "description": "API Base URL", - "hint": "Base URL API Dify. По умолчанию: https://api.dify.ai/v1" - }, - "dify_api_type": { - "description": "Тип приложения Dify", - "hint": "Тип API Dify (chat, agent, workflow и т.д.)." - }, - "dify_workflow_output_key": { - "description": "Имя выходной переменной воркфлоу Dify", - "hint": "Имя выходной переменной для типа 'workflow'." - }, - "dify_query_input_key": { - "description": "Имя переменной ввода промпта", - "hint": "Имя переменной для текста сообщения." - }, - "coze_api_key": { - "description": "Coze API Key", - "hint": "Coze API key for accessing Coze services." - }, - "bot_id": { - "description": "Bot ID", - "hint": "Bot ID Coze, полученный на платформе." - }, - "coze_api_base": { - "description": "API Base URL", - "hint": "Base URL for the Coze API. Default: https://api.coze.cn" - }, - "deerflow_api_base": { - "description": "API Base URL", - "hint": "URL шлюза DeerFlow. По умолчанию: http://127.0.0.1:2026" - }, - "deerflow_api_key": { - "description": "API-ключ DeerFlow", - "hint": "Опционально. Заполните, если шлюз защищен Bearer-авторизацией." - }, - "deerflow_auth_header": { - "description": "Заголовок Authorization", - "hint": "Опционально. Свой заголовок Authorization; приоритетнее ключа." - }, - "deerflow_assistant_id": { - "description": "ID ассистента", - "hint": "Assistant ID для LangGraph. По умолчанию lead_agent." - }, - "deerflow_model_name": { - "description": "Переопределение имени модели", - "hint": "Опционально. Переопределяет модель DeerFlow." - }, - "deerflow_thinking_enabled": { - "description": "Включить режим рассуждения (Thinking)" - }, - "deerflow_plan_mode": { - "description": "Включить режим планирования (Plan)", - "hint": "Управляет is_plan_mode в DeerFlow." - }, - "deerflow_subagent_enabled": { - "description": "Включить субагента", - "hint": "Управляет subagent_enabled в DeerFlow." - }, - "deerflow_max_concurrent_subagents": { - "description": "Макс. параллельных субагентов", - "hint": "Максимум параллельных субагентов. По умолчанию 3." - }, - "deerflow_recursion_limit": { - "description": "Лимит рекурсии", - "hint": "Лимит рекурсии для LangGraph." - }, - "auto_save_history": { - "description": "История диалогов управляется Coze", - "hint": "Если включено, Coze управляет историей. Локальный контекст AstrBot будет работать в режиме чтения." - } - } - }, - "help": { - "documentation": "Официальная документация", - "support": "Группа поддержки", - "helpText": "Непонятно, как настроить? См. {documentation} или {support}.", - "helpPrefix": "Непонятно, как настроить? См.", - "helpMiddle": "или", - "helpSuffix": "." - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/config.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/config.json deleted file mode 100644 index 151bbb17..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/config.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "title": "Конфигурация", - "subtitle": "Управление системными настройками", - "editor": { - "visual": "Визуальный редактор", - "code": "Редактор кода", - "revertCode": "Отменить изменения", - "applyConfig": "Применить", - "applyTip": "Кнопка «Применить» временно фиксирует изменения в визуальном редакторе. Чтобы сохранить их на постоянной основе, нажмите кнопку «Сохранить» в правом нижнем углу." - }, - "actions": { - "save": "Сохранить", - "delete": "Удалить", - "add": "Добавить", - "reset": "Сбросить настройки", - "export": "Экспорт", - "import": "Импорт", - "validate": "Проверить" - }, - "help": { - "documentation": "Документация", - "support": "Поддержка", - "helpText": "Нужна помощь? См. {documentation} или обратитесь в {support}.", - "helpPrefix": "Нужна помощь? См.", - "helpMiddle": "или обратитесь в", - "helpSuffix": "." - }, - "messages": { - "configApplied": "Настройки применены образно. Нажмите «Сохранить» для окончательной записи.", - "configApplyError": "Ошибка применения: некорректный формат JSON.", - "unsavedChangesNotice": "Есть несохраненные изменения. Пожалуйста, нажмите «Сохранить», чтобы они вступили в силу.", - "saveSuccess": "Настройки успешно сохранены", - "saveError": "Ошибка при сохранении", - "loadError": "Ошибка при загрузке настроек", - "deleteSuccess": "Удалено", - "deleteError": "Ошибка удаления", - "updateSuccess": "Обновлено", - "updateError": "Ошибка обновления" - }, - "sections": { - "general": "Основные", - "advanced": "Расширенные", - "security": "Безопасность", - "appearance": "Внешний вид", - "notification": "Уведомления", - "moreConfig": "Дополнительные настройки" - }, - "general": { - "botName": "Имя бота", - "language": "Язык интерфейса", - "timezone": "Часовой пояс", - "autoSave": "Автосохранение", - "debugMode": "Режим отладки" - }, - "advanced": { - "logLevel": "Уровень логирования", - "maxConnections": "Макс. соединений", - "timeout": "Тайм-аут", - "retryAttempts": "Попытки повтора", - "cacheSize": "Размер кэша" - }, - "security": { - "apiKey": "Ключ API", - "allowedHosts": "Разрешенные хосты", - "rateLimit": "Лимит запросов", - "encryption": "Шифрование" - }, - "configSelection": { - "selectConfig": "Выбор конфигурации", - "normalConfig": "Обычная", - "systemConfig": "Системная" - }, - "search": { - "placeholder": "Поиск по настройкам (поле/описание/подсказка)", - "noResult": "Совпадений не найдено" - }, - "configManagement": { - "title": "Управление конфигурациями", - "description": "AstrBot поддерживает несколько конфигураций для разных ботов. По умолчанию используется «default».", - "newConfig": "Новая конфигурация", - "copyConfig": "Копировать конфигурацию", - "editConfig": "Изменить конфигурацию", - "manageConfigs": "Управление файлами...", - "configName": "Имя", - "fillConfigName": "Введите имя конфигурации", - "confirmDelete": "Вы уверены, что хотите удалить конфигурацию «{name}»? Это действие необратимо.", - "pleaseEnterName": "Пожалуйста, введите имя", - "nameExists": "Имя конфигурации уже существует. Используйте другое имя.", - "createFailed": "Ошибка создания конфигурации", - "copyFailed": "Ошибка копирования конфигурации", - "deleteFailed": "Ошибка удаления", - "updateFailed": "Ошибка обновления" - }, - "buttons": { - "cancel": "Отмена", - "create": "Создать", - "update": "Обновить" - }, - "codeEditor": { - "title": "Редактирование файла" - }, - "fileUpload": { - "button": "Файлы", - "dialogTitle": "Загруженные файлы", - "dropzone": "Загрузить файлы", - "allowedTypes": "Разрешенные типы: {types}", - "empty": "Файлов нет", - "statusMissing": "Файл отсутствует", - "statusUnconfigured": "Не в конфиге", - "uploadSuccess": "Загружено файлов: {count}", - "uploadFailed": "Ошибка загрузки", - "loadFailed": "Ошибка получения списка файлов", - "fileTooLarge": "Файл слишком велик (макс. {max} МБ): {name}", - "deleteSuccess": "Файл удален", - "deleteFailed": "Ошибка удаления", - "addToConfig": "Добавлено в конфигурацию", - "fileCount": "Файлов: {count}", - "done": "Готово" - }, - "unsavedChangesWarning": { - "dialogTitle": "Несохраненные изменения", - "leavePage": "У вас есть несохраненные изменения. Сохранить перед уходом?", - "switchConfig": "Переключение конфигурации приведет к потере несохраненных изменений. Сохранить?", - "options": { - "save": "Сохранить", - "saveAndSwitch": "Сохранить и переключить", - "discardAndSwitch": "Сбросить и переключить", - "closeCard": "Закрыть", - "confirm": "ОК", - "cancel": "Отмена" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/console.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/console.json deleted file mode 100644 index d77505d0..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/console.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "title": "Логи платформы", - "autoScroll": { - "enabled": "Автопрокрутка включена", - "disabled": "Автопрокрутка выключена" - }, - "pipInstall": { - "button": "Установить pip-пакет", - "dialogTitle": "Установка Pip-пакета", - "packageLabel": "*Имя пакета, например: llmtuner", - "mirrorLabel": "Использовать зеркало PyPI (опционально)", - "mirrorHint": "Приоритет зеркала PyPI > настройки «Зеркало репозитория PyPI»", - "installButton": "Установить", - "installSuccess": "Установка выполнена успешно.", - "installFailed": "Ошибка установки.", - "requestFailed": "Ошибка запроса." - }, - "debugHint": { - "text": "Для отображения Debug-логов необходимо установить соответствующий уровень в «Конфигурация → Система → Уровень логирования»" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/conversation.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/conversation.json deleted file mode 100644 index ea29fe1b..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/conversation.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "title": "Управление диалогами", - "subtitle": "Просмотр и управление историей сообщений", - "filters": { - "title": "Фильтры", - "platform": "ID бота", - "type": "Тип", - "search": "Поиск по ключевым словам", - "reset": "Сбросить" - }, - "history": { - "title": "История", - "refresh": "Обновить" - }, - "batch": { - "deleteSelected": "Удалить выбранные ({count})", - "exportSelected": "Экспорт выбранных ({count})" - }, - "pagination": { - "itemsPerPage": "на странице", - "showingItems": "Показано {start}-{end} из {total}" - }, - "table": { - "headers": { - "title": "Заголовок диалога", - "platform": "ID бота", - "type": "Тип сообщения", - "cid": "ID диалога", - "umo": "Источник сообщения", - "sessionId": "ID сессии", - "createdAt": "Создан", - "updatedAt": "Обновлен", - "actions": "Действия" - }, - "umoDisplay": { - "parsed": "Разобрано", - "raw": "UMO" - } - }, - "actions": { - "view": "Просмотр", - "edit": "Редактировать", - "delete": "Удалить" - }, - "messageTypes": { - "group": "Группа", - "friend": "ЛС", - "unknown": "Неизвестно" - }, - "status": { - "noTitle": "Без заголовка", - "unknown": "Неизвестно", - "noData": "История диалогов пуста", - "emptyContent": "Содержимое диалога пусто", - "audioNotSupported": "Ваш браузер не поддерживает воспроизведение аудио." - }, - "dialogs": { - "view": { - "title": "Детали диалога", - "editMode": "Режим редактирования", - "previewMode": "Режим просмотра", - "saveChanges": "Сохранить изменения", - "close": "Закрыть", - "confirmClose": "У вас есть несохраненные изменения. Вы уверены, что хотите закрыть?" - }, - "edit": { - "title": "Изменить информацию", - "titleLabel": "Заголовок диалога", - "titlePlaceholder": "Введите заголовок", - "cancel": "Отмена", - "save": "Сохранить" - }, - "delete": { - "title": "Подтверждение удаления", - "message": "Вы уверены, что хотите удалить диалог «{title}»? Это действие необратимо.", - "cancel": "Отмена", - "confirm": "Удалить" - }, - "batchDelete": { - "title": "Массовое удаление", - "message": "Вы уверены, что хотите удалить {count} выбранных диалогов? Это действие необратимо!", - "andMore": "и еще {count}", - "cancel": "Отмена", - "confirm": "Удалить всё", - "warning": "Внимание: удаление нельзя будет отменить!" - } - }, - "messages": { - "fetchError": "Не удалось загрузить список диалогов", - "saveSuccess": "Сохранено", - "saveError": "Ошибка сохранения", - "deleteSuccess": "Удалено", - "deleteError": "Ошибка удаления", - "historyError": "Не удалось загрузить историю диалога", - "historySaveSuccess": "История сохранена", - "historySaveError": "Ошибка сохранения истории", - "invalidJson": "Некорректный формат JSON", - "noItemSelected": "Сначала выберите диалоги для удаления", - "batchDeleteSuccess": "Успешно удалено {count} диалогов", - "batchDeleteError": "Ошибка массового удаления", - "batchDeletePartial": "Удаление завершено: успешно {deleted}, ошибок {failed}", - "exportSuccess": "Экспорт завершен", - "exportError": "Ошибка экспорта", - "noItemSelectedForExport": "Сначала выберите диалоги для экспорта", - "copySuccess": "Скопировано", - "copyError": "Не удалось скопировать" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/cron.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/cron.json deleted file mode 100644 index 10422895..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/cron.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "header": { - "eyebrow": "Automation", - "live": "Живая синхронизация" - }, - "page": { - "title": "Запланированные задачи", - "beta": "Экспериментальные функции", - "subtitle": "AstrBot проснется в заданное время, выполнит задачу и отправит результат в целевую беседу.", - "proactive": { - "link": "Поддерживаемые платформы", - "supported": "Отправка результатов поддерживается только на указанных ниже настроенных платформах", - "unsupported": "Нет платформ, поддерживающих проактивные сообщения. Включите их в настройках платформ." - } - }, - "actions": { - "create": "Новая задача", - "edit": "Изменить", - "refresh": "Обновить", - "delete": "Удалить", - "cancel": "Отмена", - "close": "Закрыть", - "more": "Другие действия", - "runNow": "Запустить сейчас", - "save": "Сохранить", - "submit": "Создать" - }, - "platformDialog": { - "title": "Поддерживаемые платформы", - "description": "Только следующие платформы поддерживают проактивную отправку сообщений. AstrBot сможет отправить результат будущей задачи только в целевые беседы на этих платформах." - }, - "card": { - "onceAt": "Один раз · {time}", - "runAt": "Время запуска: {time}", - "nextRun": "Следующий запуск: {time}", - "dailyAt": "Каждый день в {time}", - "weeklyAt": "Каждый {day} в {time}", - "monthlyAt": "Каждый месяц, день {day}, {time}", - "everyMinutes": "Каждые {count} мин.", - "everyHours": "Каждые {count} ч.", - "everyDays": "Каждые {count} дн.", - "customCron": "Вручную · {cron}", - "noDeliveryTarget": "Цель доставки не задана" - }, - "filters": { - "search": "Поиск по названию и содержанию", - "umo": "Фильтр по цели доставки", - "noUmos": "Нет UMO", - "noDeliveryTarget": "Цель доставки не задана", - "noMatches": "Нет подходящих будущих задач." - }, - "overview": { - "totalTasks": "Всего задач", - "totalTasksNote": "Все зарегистрированные будущие задачи", - "enabledTasks": "Активные задачи", - "enabledTasksNote": "Задачи, которые будут автоматически выполнены", - "oneOffTasks": "Разовые задачи", - "recurringTasksNote": "Повторяющихся задач: {count}", - "proactivePlatforms": "Проактивные платформы", - "proactivePlatformsNote": "Платформы, способные отправлять результат сами" - }, - "section": { - "registered": { - "title": "Список задач", - "subtitle": "Просматривайте зарегистрированные задачи, время запуска и состояние" - }, - "delivery": { - "title": "Статус доставки", - "subtitle": "После выполнения задачи результат будет отправлен обратно через поддерживаемые платформы", - "support": "Проактивная доставка", - "available": "Доступна", - "unavailable": "Недоступна", - "enabledPlatforms": "Включенные платформы" - }, - "quickCreate": { - "title": "Быстрое создание", - "runMode": "Режим задачи", - "target": "Целевой контекст" - }, - "platforms": { - "title": "Поддерживаемые платформы" - } - }, - "table": { - "title": "Список задач", - "subtitle": "Отслеживайте cron, целевую сессию, историю запусков и состояние", - "empty": "Задач пока нет.", - "headers": { - "name": "Имя", - "type": "Тип", - "cron": "Cron", - "session": "ID сессии", - "nextRun": "Следующий запуск", - "lastRun": "Последний запуск", - "note": "Описание", - "actions": "Действия" - }, - "type": { - "once": "Разовая", - "recurring": "Повторяющаяся", - "activeAgent": "Активный агент", - "workflow": "Рабочий процесс", - "unknown": "{type}" - }, - "timezoneLocal": "Местное время", - "notAvailable": "—" - }, - "form": { - "title": "Создать задачу", - "editTitle": "Редактировать задачу", - "chatHint": "Вы можете ставить задачи прямо в чате, AstrBot создаст их автоматически без заполнения этой формы.", - "runOnce": "Разовая задача", - "name": "Имя задачи *", - "note": "Требования задачи *", - "scheduleMode": "Время выполнения *", - "scheduleModes": { - "once": "Один раз", - "interval": "Интервал", - "daily": "Каждый день", - "weekly": "Каждую неделю", - "monthly": "Каждый месяц", - "cron": "Cron вручную" - }, - "intervalEvery": "Каждые *", - "intervalUnit": "Единица *", - "intervalUnits": { - "minutes": "Минуты", - "hours": "Часы", - "days": "Дни" - }, - "dailyTime": "Время *", - "weeklyDay": "День недели *", - "weeklyTime": "Время *", - "monthlyDay": "День *", - "monthlyTime": "Время *", - "weekdays": { - "sunday": "Воскресенье", - "monday": "Понедельник", - "tuesday": "Вторник", - "wednesday": "Среда", - "thursday": "Четверг", - "friday": "Пятница", - "saturday": "Суббота" - }, - "cron": "Cron-выражения *", - "cronPlaceholder": "0 9 * * *", - "runAt": "Время запуска *", - "session": "Доставить в", - "noUmos": "Нет доступных сессий", - "timezone": "Часовой пояс (опционально, напр. Europe/Moscow)", - "enabled": "Включено" - }, - "messages": { - "loadFailed": "Ошибка загрузки задач", - "updateSuccess": "Задача обновлена", - "updateFailed": "Ошибка обновления", - "deleteSuccess": "Удалено", - "deleteFailed": "Ошибка удаления", - "nameRequired": "Укажите имя задачи", - "sessionRequired": "Укажите цель доставки", - "noteRequired": "Заполните требования задачи", - "cronRequired": "Укажите Cron-выражение", - "runAtRequired": "Выберите время запуска", - "intervalRequired": "Укажите корректный интервал", - "dailyTimeRequired": "Выберите ежедневное время запуска", - "weeklyTimeRequired": "Выберите день недели и время запуска", - "monthlyTimeRequired": "Выберите день месяца и время запуска", - "createSuccess": "Задача создана", - "createFailed": "Ошибка создания", - "runStarted": "Запущено", - "runFailed": "Ошибка запуска" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/dashboard.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/dashboard.json deleted file mode 100644 index b4ecd50a..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/dashboard.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "title": "Логи платформы", - "subtitle": "Мониторинг и статистика в реальном времени", - "lastUpdate": "Последнее обновление", - "status": { - "loading": "Загрузка...", - "dataError": "Ошибка получения данных", - "noticeError": "Ошибка получения объявлений", - "online": "В сети", - "uptime": "Время работы", - "memoryUsage": "Память" - }, - "stats": { - "totalMessage": { - "title": "Всего сообщений", - "subtitle": "Все сообщения со всех платформ" - }, - "onlinePlatform": { - "title": "Платформы", - "subtitle": "Количество подключенных платформ" - }, - "runningTime": { - "title": "Время работы", - "subtitle": "Общее время работы системы", - "format": "{hours} ч. {minutes} мин. {seconds} сек." - }, - "memoryUsage": { - "title": "Память", - "subtitle": "Использование оперативной памяти", - "cpuLoad": "Загрузка CPU", - "status": { - "good": "Отлично", - "normal": "Нормально", - "high": "Высокая" - } - } - }, - "charts": { - "messageTrend": { - "title": "Тренды сообщений", - "subtitle": "Изменение количества сообщений во времени", - "totalMessages": "Всего сообщений", - "dailyAverage": "В среднем за день", - "growthRate": "Скорость роста", - "timeLabel": "Время", - "messageCount": "Кол-во сообщений", - "timeRanges": { - "1day": "За 1 день", - "3days": "За 3 дня", - "1week": "За 7 дней", - "1month": "За 30 дней" - } - }, - "platformStat": { - "title": "Статистика по платформам", - "subtitle": "Распределение сообщений по платформам", - "total": "Всего", - "noData": "Нет данных по платформам", - "messageUnit": "шт.", - "platformCount": "Кол-во платформ", - "mostActive": "Самый активный", - "totalPercentage": "Доля от общего числа" - } - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/extension.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/extension.json deleted file mode 100644 index d876f08e..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/extension.json +++ /dev/null @@ -1,451 +0,0 @@ -{ - "title": "Плагины", - "subtitle": "Управление и настройка расширений системы", - "tabs": { - "installedPlugins": "Плагины AstrBot", - "market": "Магазин плагинов", - "installedMcpServers": "MCP", - "skills": "Навыки", - "handlersOperation": "Управление поведением" - }, - "titles": { - "installedAstrBotPlugins": "Установленные плагины AstrBot" - }, - "failedPlugins": { - "title": "Ошибка загрузки ({count})", - "hint": "Эти плагины не удалось загрузить. Вы можете попробовать перезагрузить их или удалить.", - "columns": { - "plugin": "Плагин", - "error": "Ошибка" - } - }, - "search": { - "placeholder": "Поиск плагинов...", - "marketPlaceholder": "Поиск в магазине..." - }, - "filters": { - "all": "Все" - }, - "views": { - "card": "Плитка", - "list": "Список" - }, - "buttons": { - "showSystemPlugins": "Показать системные", - "hideSystemPlugins": "Скрыть системные", - "install": "Установить", - "uninstall": "Удалить", - "update": "Обновить", - "reload": "Перезагрузить", - "enable": "Включить", - "disable": "Выключить", - "configure": "Настроить", - "viewInfo": "Детали", - "viewDocs": "Документация", - "viewRepo": "Репозиторий", - "openPages": "Pages", - "openPage": "Открыть", - "openWebui": "Открыть UI плагина", - "close": "Закрыть", - "save": "Сохранить", - "saveAndClose": "Сохранить и закрыть", - "cancel": "Отмена", - "next": "Далее", - "actions": "Действия", - "back": "Назад", - "selectFile": "Выбрать файл", - "refresh": "Обновить", - "updateAll": "Обновить все", - "pin": "Закрепить", - "unpin": "Открепить", - "deleteSource": "Удалить источник", - "reshuffle": "Мне повезет!" - }, - "status": { - "enabled": "Включен", - "disabled": "Выключен", - "system": "Системный", - "loading": "Загрузка...", - "installed": "Установлен", - "unknown": "Неизвестно", - "unsupported": "Не поддерживается" - }, - "tooltips": { - "enable": "Включить", - "disable": "Выключить", - "reload": "Перезагрузить", - "configure": "Настроить", - "viewInfo": "Просмотр поведения", - "viewDocs": "Документация", - "update": "Обновить", - "uninstall": "Удалить" - }, - "table": { - "headers": { - "name": "Имя", - "description": "Описание", - "version": "Версия", - "author": "Автор", - "status": "Статус", - "actions": "Действия", - "stars": "Звезды", - "lastUpdate": "Обновлен", - "tags": "Теги", - "eventType": "Тип события", - "specificType": "Тип", - "trigger": "Триггер" - } - }, - "empty": { - "noPlugins": "Плагины не найдены", - "noPluginsDesc": "Попробуйте установить новые плагины или изменить поиск." - }, - "detail": { - "contents": "Обработчики плагина", - "noContents": "Нет обработчиков", - "notFound": "Плагин не найден", - "docsTitle": "Документация", - "docsEmpty": "Документация отсутствует", - "changelogTitle": "Журнал изменений", - "changelogEmpty": "Журнал изменений отсутствует", - "subCommandsCount": "{count} подкоманд", - "handlerGroups": { - "page": "Pages", - "skill": "Skills", - "command": "Команды / группы команд", - "llm_tool": "LLM-инструменты", - "listener": "Слушатели событий", - "hook": "Хуки", - "commands": "Команды / группы команд", - "hooks": "Хуки", - "functionTools": "Функциональные инструменты", - "eventListeners": "Слушатели событий" - }, - "info": { - "title": "Информация", - "version": "Версия", - "author": "Автор", - "category": "Категория", - "stars": "Звезды", - "tags": "Теги", - "astrbotVersion": "Требуемая версия AstrBot", - "supportPlatforms": "Поддерживаемые платформы", - "authorWebsite": "Сайт автора", - "repository": "Репозиторий" - } - }, - "market": { - "recommended": "🥳 Рекомендуем", - "allPlugins": "📦 Все плагины", - "categories": { - "all": "Все", - "ai_tools": "AI Tools", - "entertainment": "Развлечения", - "productivity": "Продуктивность", - "integrations": "Интеграции", - "utilities": "Утилиты", - "other": "Другое" - }, - "showFullName": "Полное имя", - "devDocs": "Документация для разработчиков", - "submitRepo": "Добавить репозиторий", - "customSource": "Свои источники", - "source": "Источник", - "availableSources": "Доступные источники", - "sourceManagement": "Управление источниками", - "addSource": "Добавить источник", - "sourceName": "Имя", - "sourceUrl": "Исходный URL", - "defaultSource": "Источник по умолчанию", - "removeSource": "Удалить источник", - "confirmRemoveSource": "Вы уверены, что хотите удалить этот источник плагинов?", - "sourceAdded": "Источник успешно добавлен", - "sourceRemoved": "Источник удален", - "sourceError": "Ошибка операции", - "selectSource": "Выбрать источник", - "currentSource": "Текущий источник", - "editSource": "Изменить источник", - "sourceUpdated": "Источник обновлен", - "defaultOfficialSource": "Официальный источник", - "sourceExists": "Этот источник уже есть в списке", - "sourceResolved": "Источник успешно проверен", - "installPlugin": "Установить плагин", - "randomPlugins": "🎲 Случайные плагины", - "sourceSafetyWarning": "Даже при использовании источников по умолчанию мы не можем гарантировать 100% безопасность и стабильность сторонних плагинов. Пожалуйста, будьте внимательны." - }, - "sort": { - "by": "Сортировать по", - "default": "По умолчанию", - "installTime": "Дате установки", - "name": "Имени", - "stars": "Звездам", - "author": "Автору", - "updated": "Дате обновления", - "updateStatus": "Статусу обновления", - "ascending": "По возрастанию", - "descending": "По убыванию" - }, - "tags": { - "danger": "Опасно" - }, - "dialogs": { - "error": { - "title": "Ошибка", - "checkConsole": "Подробности смотрите в логах платформы" - }, - "config": { - "title": "Настройка плагина", - "noConfig": "У этого плагина нет настраиваемых параметров" - }, - "loading": { - "title": "Загрузка...", - "logs": "Логи" - }, - "uninstall": { - "title": "Подтверждение удаления", - "message": "Вы уверены, что хотите удалить этот плагин?", - "deleteConfig": "Удалить файл конфигурации плагина", - "deleteData": "Удалить сохраненные данные плагина", - "configHint": "Конфиг находится в data/config", - "dataHint": "Данные находятся в data/plugin_data и data/plugins_data, а также KV-данные в базе данных" - }, - "install": { - "title": "Установка плагина", - "fromFile": "Из файла", - "fromUrl": "По ссылке", - "supportPlatformsCount": "Поддерживает платформ: {count}", - "sectionTitle": "Установка", - "downloadSource": "Пакет плагина будет установлен из:", - "githubSecurityWarning": "AstrBot не может гарантировать безопасность плагинов, загруженных с GitHub. Устанавливайте только если доверяете источнику плагина." - }, - "update": { - "title": "Обновление плагина", - "sectionTitle": "Обновление", - "downloadSource": "Пакет обновления будет установлен из:", - "confirm": "Подтвердить обновление" - }, - "danger_warning": { - "title": "Внимание!", - "message": "Этот плагин может содержать небезопасный код или функции, которые могут привести к нестабильности системы или потере данных. Вы уверены, что хотите продолжить установку?", - "confirm": "Продолжить", - "cancel": "Отмена" - }, - "versionSupport": { - "title": "Предупреждение о версии", - "message": "Требуемая плагином версия AstrBot не совпадает с вашей текущей версией. Вы можете продолжить установку на свой страх и риск.", - "confirm": "Игнорировать и установить", - "cancel": "Отмена" - }, - "forceUpdate": { - "title": "Новых версий не найдено", - "message": "Новых версий не обнаружено. Выполнить принудительную переустановку из удаленного репозитория?", - "confirm": "Принудительно" - }, - "updateAllConfirm": { - "title": "Обновить всё", - "message": "Обновить все плагины ({count} шт.)? Это может занять некоторое время.", - "confirm": "Подтвердить" - }, - "sourceBinding": { - "title": "Сменить источник плагина", - "selectTitle": "Выбрать источник установки", - "noCandidates": "В настроенных источниках не найдена подходящая запись, и у плагина нет пригодного URL репозитория.", - "repoOption": "Использовать URL репозитория", - "repoOptionId": "Переустановить из repo", - "installDestination": "Место установки", - "confirm": "Использовать источник", - "confirmAndContinue": "Использовать и продолжить" - } - }, - "messages": { - "uninstalling": "Удаление", - "refreshing": "Обновление списка плагинов...", - "refreshSuccess": "Список плагинов обновлен", - "refreshFailed": "Ошибка при обновлении списка", - "operationFailed": "Ошибка операции", - "reloadSuccess": "Перезагрузка завершена", - "reloadFailed": "Ошибка перезагрузки", - "updateSuccess": "Обновление завершено", - "addSuccess": "Успешно добавлено", - "saveSuccess": "Сохранено", - "deleteSuccess": "Удалено", - "installing": "Установка из файла...", - "installingFromUrl": "Установка по ссылке...", - "installFailed": "Ошибка установки:", - "getMarketDataFailed": "Ошибка получения данных магазина:", - "hasUpdate": "Доступно обновление:", - "confirmDelete": "Вы уверены, что хотите удалить плагин?", - "fillUrlOrFile": "Укажите ссылку или выберите файл", - "dontFillBoth": "Пожалуйста, используйте либо ссылку, либо файл, но не оба сразу", - "supportedFormats": "Поддерживаются файлы плагинов в формате .zip", - "updateAllSuccess": "Все плагины успешно обновлены", - "updateAllFailed": "Ошибок при обновлении: {failed} из {total}:", - "noUpdatesAvailable": "Нет плагинов с доступными обновлениями", - "updateDisabled": "У плагина нет пригодного источника установки или URL репозитория, поэтому его нельзя обновить или переустановить", - "changeSourceDisabled": "У плагина нет пригодного URL репозитория, поэтому источник нельзя изменить", - "fillSourceUrl": "Введите URL источника", - "fillSourceName": "Введите имя источника", - "resolveSourceFirst": "Сначала проверьте URL источника", - "sourceResolveFailed": "Не удалось проверить источник", - "invalidUrl": "Введите корректный URL", - "invalidGithubRepo": "Введите корректный URL репозитория GitHub", - "enterJsonUrl": "Введите URL, возвращающий список плагинов в формате JSON", - "missingMarketPluginId": "Эта запись источника не содержит идентификаторов плагина, поэтому его нельзя установить из магазина", - "sourceBindSuccess": "Источник плагина обновлен", - "validatingPlugin": "Проверка плагина...", - "pluginValidateSuccess": "Плагин успешно проверен", - "pluginValidateFailed": "Не удалось проверить плагин" - }, - "upload": { - "fromFile": "Загрузить файл", - "fromUrl": "Указать ссылку", - "selectFile": "Выбрать файл", - "enterUrl": "Ссылка на репозиторий" - }, - "skills": { - "modeLocal": "Локальные навыки", - "modeNeo": "Навыки Neo", - "actions": "Действия", - "upload": "Загрузить навыки", - "refresh": "Обновить", - "empty": "Навыки не найдены", - "emptyHint": "Пожалуйста, загрузите архив с навыками", - "uploadDialogTitle": "Загрузка навыков", - "uploadHint": "Поддерживается массовая загрузка zip-архивов. Вы также можете перетащить файлы в это окно. Система автоматически проверит структуру каждого архива.", - "structureRequirement": "Поддерживаются архивы с несколькими папками skills.", - "abilityMultiple": "Поддержка массовой загрузки", - "abilityValidate": "Автопроверка `SKILL.md`", - "abilitySkip": "Пропуск дубликатов", - "selectFile": "Выбрать файл", - "selectFiles": "Выбрать файлы", - "dropzoneTitle": "Перетащите zip-файлы сюда", - "dropzoneAction": "или нажмите, чтобы выбрать файлы на компьютере", - "dropzoneHint": "Система проверит структуру архивов перед загрузкой", - "fileListTitle": "Очередь загрузки", - "fileListEmpty": "Здесь будет отображаться статус проверки и загрузки файлов", - "uploading": "Загрузка...", - "batchResultTitle": "Результаты загрузки", - "batchResultSummary": "Всего: {total}, успешно: {success}", - "batchSuccessList": "Успешно загружено", - "batchFailedList": "Ошибка загрузки", - "confirm": "ОК", - "confirmUpload": "Начать загрузку", - "cancel": "Отмена", - "statusWaiting": "В очереди", - "statusUploading": "Загрузка...", - "statusSuccess": "Готово", - "statusError": "Ошибка структуры", - "statusSkipped": "Пропущено", - "summaryTotal": "Всего: {count}", - "summaryReady": "Готовы: {count}", - "summarySuccess": "Успешно: {count}", - "summaryFailed": "Ошибок: {count}", - "summarySkipped": "Дубликатов: {count}", - "validationReady": "Ожидает загрузки (проверка структуры будет выполнена автоматически)", - "validationZipOnly": "Допускаются только zip-архивы", - "validationDuplicate": "Файл уже есть в списке, пропуск", - "validationUploading": "Проверка и загрузка...", - "validationUploadFailed": "Ошибка загрузки, попробуйте еще раз", - "validationUploadedAs": "Установлено как {name}", - "validationNoResult": "Результат не получен, проверьте логи платформы", - "noDescription": "Нет описания", - "path": "Путь", - "uploadSuccess": "Успешно загружено", - "uploadFailed": "Ошибка загрузки", - "download": "Скачать", - "downloadSuccess": "Скачивание начато", - "downloadFailed": "Ошибка скачивания", - "loadFailed": "Не удалось загрузить навыки", - "updateSuccess": "Обновлено", - "updateFailed": "Ошибка обновления", - "deleteTitle": "Подтверждение удаления", - "deleteMessage": "Вы уверены, что хотите удалить этот навык?", - "deleteSuccess": "Удалено", - "deleteFailed": "Ошибка удаления", - "neoSkillKey": "Фильтр по ключу", - "neoStatus": "Статус кандидата", - "neoStage": "Этап публикации", - "neoFilterHint": "Фильтрация записей о публикации", - "neoAll": "Все", - "neoCandidates": "Кандидаты Neo", - "neoReleases": "Релизы Neo", - "neoLoadFailed": "Ошибка загрузки данных Neo Skills", - "neoPass": "Одобрить", - "neoReject": "Отклонить", - "neoEvaluateSuccess": "Оценка обновлена", - "neoEvaluateFailed": "Ошибка обновления оценки", - "neoPromoteSuccess": "Опубликовано", - "neoPromoteFailed": "Ошибка публикации", - "neoRollback": "Откат", - "neoRollbackSuccess": "Откат выполнен", - "neoRollbackFailed": "Ошибка отката", - "neoDeactivate": "Деактивация", - "neoDeactivateSuccess": "Деактивировано", - "neoDeactivateFailed": "Ошибка деактивации", - "neoSync": "Синхронизация", - "neoSyncSuccess": "Синхронизировано", - "neoSyncFailed": "Ошибка синхронизации", - "neoDelete": "Удалить", - "neoDeleteSuccess": "Удалено", - "neoDeleteFailed": "Ошибка удаления", - "neoPayloadTitle": "Детали Neo Payload", - "neoPayloadFailed": "Ошибка чтения Payload", - "runtimeNoneWarning": "Среда выполнения Computer Use не задана. Навыки могут не работать, так как нет активного окружения.", - "runtimeHint": "Установите среду выполнения в «local» или «sandbox» в настройках способностей использования компьютера. Навыки из рабочей области пока не отображаются на этой странице.", - "neoRuntimeRequired": "Neo Skills доступны только в среде sandbox с драйвером shipyard_neo.", - "sourceLocalOnly": "Локальный навык", - "sourceSandboxOnly": "Предустановленный Sandbox навык", - "sourceBoth": "Локальный + Sandbox", - "sourcePlugin": "Плагин: {plugin}", - "sandboxDiscoveryPending": "Предустановленные Sandbox навыки не найдены. Запустите сессию Sandbox хотя бы один раз.", - "sandboxPresetReadonly": "Предустановленные навыки Sandbox доступны только для чтения и не могут быть удалены здесь.", - "pluginReadonly": "Навыки из плагинов управляются плагином и не могут быть удалены или скачаны здесь.", - "openEditor": "Просмотр/правка", - "editorTitle": "Редактировать навык", - "editorLoadFailed": "Не удалось открыть файл навыка", - "editorSaveFailed": "Не удалось сохранить файл навыка", - "editorSaveSuccess": "Сохранено", - "saveFile": "Сохранить файл", - "readonly": "Только чтение", - "unsaved": "Не сохранено", - "noFileSelected": "Файл не выбран", - "discardChanges": "В файле есть несохраненные изменения. Отменить их?" - }, - "card": { - "actions": { - "pluginConfig": "Настройки", - "uninstallPlugin": "Удалить", - "reloadPlugin": "Перезагрузить", - "togglePlugin": "Плагин", - "viewHandlers": "Действия", - "updateTo": "Обновить до", - "reinstall": "Переустановить", - "changeSource": "Сменить источник" - }, - "status": { - "hasUpdate": "Доступно обновление", - "disabled": "Плагин выключен", - "handlersCount": "действий", - "supportPlatform": "Платформы", - "supportPlatformsCount": "Платформ: {count}", - "astrbotVersion": "Требуемая версия AstrBot" - }, - "alt": { - "logo": "логотип", - "extensionIcon": "иконка расширения" - }, - "errors": { - "confirmNotRegistered": "$confirm не зарегистрирован" - } - }, - "conflicts": { - "title": "Конфликт команд", - "message": "Обнаружены конфликтующие команды. Это может привести к некорректной работе. Рекомендуется разрешить конфликты в панели «Управление командами».", - "pairs": "конфликтующих пар", - "goToManage": "Управление", - "later": "Позже" - }, - "pluginChangelog": { - "menuTitle": "Журнал изменений" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/detail.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/detail.json deleted file mode 100644 index 5145d5c2..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/detail.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "title": "Детали базы знаний", - "backToList": "К списку", - "breadcrumb": { - "list": "Базы знаний" - }, - "tabs": { - "overview": "Обзор", - "documents": "Документы", - "retrieval": "Поиск", - "sessions": "Сессии", - "settings": "Настройки" - }, - "overview": { - "title": "Информация", - "name": "Название", - "description": "Описание", - "emoji": "Иконка", - "createdAt": "Создана", - "updatedAt": "Обновлена", - "stats": "Статистика", - "docCount": "Количество документов", - "chunkCount": "Количество фрагментов", - "embeddingModel": "Embedding модель", - "rerankModel": "Rerank модель", - "notSet": "не выбрано" - }, - "documents": { - "title": "Список документов", - "upload": "Загрузить", - "empty": "Документов нет", - "name": "Имя файла", - "type": "Тип", - "size": "Размер", - "chunks": "Фрагменты", - "createdAt": "Дата загрузки", - "actions": "Действия", - "view": "Смотреть", - "delete": "Удалить", - "deleteConfirm": "Вы уверены, что хотите удалить «{name}»?", - "deleteWarning": "Это удалит файл и все его фрагменты из индекса.", - "uploading": "Загрузка...", - "uploadSuccess": "Файл успешно загружен", - "uploadFailed": "Ошибка загрузки", - "deleteSuccess": "Файл удален", - "deleteFailed": "Ошибка удаления" - }, - "upload": { - "title": "Добавление контента", - "selectFile": "Файл", - "dropzone": "Нажмите или перетащите файл сюда", - "supportedFormats": "Форматы: .txt, .md, .markdown, .rst, .adoc, .pdf, .docx, .epub, .xls, .xlsx", - "maxSize": "Максимум: 128MB", - "chunkSettings": "Фрагментация", - "batchSettings": "Пакетная обработка", - "cleaningSettings": "Очистка данных", - "enableCleaning": "Включить очистку контента", - "cleaningProvider": "Сервис для очистки", - "cleaningProviderHint": "LLM провайдер для суммаризации и извлечения смыслов из веб-страниц", - "chunkSize": "Размер чанка", - "chunkSizeHint": "Символов в блоке (по умолчанию: 512)", - "chunkOverlap": "Перекрытие", - "chunkOverlapHint": "Перекрытие между блоками (по умолчанию: 50)", - "batchSize": "Размер пакета", - "batchSizeHint": "Блоков за один запрос (по умолчанию: 32)", - "tasksLimit": "Лимит задач", - "tasksLimitHint": "Макс. параллельных потоков (по умолчанию: 3)", - "maxRetries": "Попытки", - "maxRetriesHint": "Повторов при сбое (по умолчанию: 3)", - "cancel": "Отмена", - "submit": "Загрузить", - "fileRequired": "Пожалуйста, выберите файл", - "fileUpload": "Загрузка файла", - "fromUrl": "Из URL", - "urlPlaceholder": "Ссылка на веб-страницу", - "urlRequired": "Введите URL", - "urlHint": "Контент будет автоматически извлечен со страницы. Убедитесь, что сайт разрешает доступ роботам.", - "beta": "Бета-версия" - }, - "retrieval": { - "title": "Поиск и проверка", - "subtitle": "Проверьте качество поиска (Dense & Sparse) по вашей базе знаний", - "query": "Тестовый запрос", - "queryPlaceholder": "Что вы хотите найти?", - "search": "Найти", - "searching": "Ищем...", - "results": "Результаты поиска", - "noResults": "Релевантный контент не найден", - "tryDifferentQuery": "Попробуйте изменить формулировку запроса", - "settings": "Параметры поиска", - "topK": "Количество результатов", - "topKHint": "Сколько фрагментов возвращать", - "enableRerank": "Включить Rerank", - "enableRerankHint": "Применить переранжирование для повышения точности", - "score": "Вес (Score)", - "document": "Документ", - "chunk": "Фрагмент #{index}", - "content": "Текст", - "charCount": "{count} симв.", - "searchSuccess": "Поиск завершен, найдено: {count}", - "searchFailed": "Ошибка выполнения поиска", - "queryRequired": "Введите поисковый запрос" - }, - "settings": { - "title": "Общие настройки базы", - "basic": "Основные", - "retrieval": "Поиск", - "chunkSize": "Размер чанка", - "chunkOverlap": "Перекрытие", - "topKDense": "Вернуть (Dense)", - "topKSparse": "Вернуть (Sparse)", - "topMFinal": "Итоговый результат", - "enableRerank": "Включить Rerank", - "embeddingProvider": "Провайдер Embedding", - "rerankProvider": "Провайдер Rerank", - "save": "Сохранить", - "saveSuccess": "Настройки сохранены", - "saveFailed": "Ошибка сохранения", - "tips": "Внимание! Изменение этих параметров повлияет на будущую выдачу базы знаний." - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/document.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/document.json deleted file mode 100644 index 7fcb30ee..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/document.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "title": "Просмотр документа", - "backToKB": "К базе знаний", - "info": { - "title": "Информация о документе", - "name": "Имя файла", - "type": "Формат", - "size": "Размер", - "chunkCount": "Количество фрагментов", - "createdAt": "Загружен" - }, - "chunks": { - "title": "Фрагменты текста", - "empty": "Фрагменты не найдены", - "index": "Индекс", - "content": "Текст", - "charCount": "Символов", - "actions": "Действия", - "view": "Детали", - "edit": "Изменить", - "delete": "Удалить", - "preview": "Обзор", - "search": "Поиск по документу", - "searchPlaceholder": "Найти во фрагментах...", - "showing": "Показано", - "deleteConfirm": "Удалить этот фрагмент?", - "deleteSuccess": "Фрагмент удален", - "deleteFailed": "Ошибка удаления" - }, - "edit": { - "title": "Редактирование фрагмента", - "content": "Текст", - "cancel": "Отмена", - "save": "Сохранить", - "saveSuccess": "Фрагмент обновлен", - "saveFailed": "Ошибка сохранения" - }, - "delete": { - "title": "Удаление", - "confirmText": "Вы уверены?", - "warning": "Удаление фрагмента может ухудшить качество ответов AI по этой теме.", - "cancel": "Отмена", - "confirm": "Удалить", - "deleteSuccess": "Удаление выполнено", - "deleteFailed": "Ошибка удаления" - }, - "view": { - "title": "Детальный просмотр", - "index": "Индекс", - "content": "Текст", - "charCount": "Символов", - "vecDocId": "ID вектора", - "close": "Закрыть" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/index.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/index.json deleted file mode 100644 index 4eb99d5f..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/knowledge-base/index.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "title": "Управление базами знаний", - "subtitle": "Централизованное управление всеми знаниями AstrBot", - "list": { - "title": "Базы знаний", - "subtitle": "Все доступные коллекции знаний", - "create": "Создать базу", - "refresh": "Обновить", - "empty": "Баз знаний пока нет", - "loading": "Загрузка...", - "documents": "док.", - "chunks": "фрагм.", - "sessionConfig": "Профиль", - "initError": "Ошибка инициализации" - }, - "card": { - "edit": "Изменить", - "delete": "Удалить", - "open": "Открыть", - "docCount": "Документов: {count}", - "chunkCount": "Фрагментов: {count}" - }, - "create": { - "title": "Создание базы знаний", - "nameLabel": "Название", - "namePlaceholder": "Придумайте имя для базы", - "descriptionLabel": "Описание", - "descriptionPlaceholder": "Для чего нужна эта база?", - "emojiLabel": "Иконка", - "embeddingModelLabel": "Embedding модель", - "rerankModelLabel": "Rerank модель (опционально)", - "providerInfo": "Провайдер: {id} | Размерность: {dimensions}", - "rerankProviderInfo": "Провайдер: {id}", - "cancel": "Отмена", - "submit": "Создать", - "nameRequired": "Введите название базы знаний" - }, - "edit": { - "title": "Редактирование", - "submit": "Сохранить" - }, - "delete": { - "title": "Удаление", - "confirmText": "Вы уверены, что хотите удалить базу знаний «{name}»?", - "warning": "Это действие необратимо. Все документы, фрагменты и настройки будут навсегда удалены.", - "cancel": "Отмена", - "confirm": "Удалить" - }, - "emoji": { - "title": "Выберите иконку", - "close": "Закрыть", - "categories": { - "books": "Книги и документы", - "emotions": "Эмоции", - "objects": "Вещи", - "symbols": "Символы" - } - }, - "messages": { - "createSuccess": "База знаний создана", - "createFailed": "Ошибка создания", - "updateSuccess": "Обновлено успешно", - "updateFailed": "Ошибка обновления", - "deleteSuccess": "Удалено успешно", - "deleteFailed": "Ошибка удаления", - "loadError": "Не удалось загрузить список" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/persona.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/persona.json deleted file mode 100644 index f7e0aff2..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/persona.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "page": { - "description": "Управление настройками и поведением персонажей" - }, - "buttons": { - "create": "Создать персонажа", - "createFirst": "Создать первого персонажа", - "edit": "Изменить", - "delete": "Удалить", - "cancel": "Отмена", - "save": "Сохранить", - "move": "Переместить", - "addDialogPair": "Добавить пример диалога" - }, - "labels": { - "presetDialogs": "Примеры диалогов ({count})", - "createdAt": "Создан", - "updatedAt": "Обновлен" - }, - "form": { - "personaId": "ID персонажа", - "systemPrompt": "Системный промпт", - "customErrorMessage": "Свое сообщение об ошибке (опционально)", - "customErrorMessageHelp": "Это сообщение будет отправлено пользователю при сбое запроса к LLM. Если оставить пустым, будет использовано системное сообщение по умолчанию.", - "presetDialogs": "Примеры диалогов", - "presetDialogsHelp": "Добавьте примеры взаимодействия, чтобы помочь AI лучше понять свою роль и стиль общения.", - "userMessage": "Сообщение пользователя", - "assistantMessage": "Ответ AI", - "tools": "Инструменты / MCP серверы", - "toolsHelp": "Выберите инструменты, доступные этому персонажу. Инструменты позволяют AI взаимодействовать с внешним миром: искать в интернете, выполнять расчеты и т.д.", - "toolsSelection": "Выбор инструментов", - "selectAllTools": "Выбрать все", - "clearAllTools": "Очистить всё", - "allSelected": "Выбрано всё", - "mcpServersQuickSelect": "Быстрый выбор MCP серверов", - "searchTools": "Поиск инструментов", - "selectedTools": "Выбранные инструменты", - "builtinToolDisabledHint": "Встроенные инструменты пока нельзя включать или выключать здесь. Измените соответствующие параметры в файле конфигурации.", - "noToolsAvailable": "Нет доступных инструментов", - "noToolsFound": "Инструменты не найдены", - "loadingTools": "Загрузка инструментов...", - "allToolsAvailable": "Использовать все доступные инструменты", - "noToolsSelected": "Инструменты не выбраны", - "skills": "Навыки (Skills)", - "skillsHelp": "Выберите навыки, доступные этому персонажу. Навыки предоставляют AI готовые сценарии и правила работы.", - "skillsAllAvailable": "По умолчанию использовать все навыки", - "skillsSelectSpecific": "Выбрать определенные навыки", - "searchSkills": "Поиск навыков", - "selectedSkills": "Выбранные навыки", - "noSkillsAvailable": "Нет доступных навыков", - "noSkillsFound": "Навыки не найдены", - "loadingSkills": "Загрузка навыков...", - "allSkillsAvailable": "Использовать все доступные навыки", - "noSkillsSelected": "Навыки не выбраны", - "skillsRuntimeNoneWarning": "Среда выполнения Computer Use не задана. Навыки могут не работать, так как нет активного окружения.", - "createInFolder": "Будет создан в папке «{folder}»", - "rootFolder": "Все персонажи" - }, - "dialog": { - "create": { - "title": "Создание персонажа" - }, - "edit": { - "title": "Редактирование персонажа" - } - }, - "empty": { - "title": "Персонажи не настроены", - "description": "Самое время создать одного!", - "folderEmpty": "Папка пуста", - "folderEmptyDescription": "Создайте нового персонажа или папку, чтобы начать" - }, - "validation": { - "required": "Это поле обязательно для заполнения", - "minLength": "Минимум {min} символов", - "alphanumeric": "Разрешены только латинские буквы, цифры, подчёркивания и дефисы", - "dialogRequired": "{type} не может быть пустым", - "personaIdExists": "Персонаж с таким ID уже существует" - }, - "messages": { - "loadError": "Не удалось загрузить список персонажей", - "saveSuccess": "Сохранено", - "saveError": "Ошибка сохранения", - "deleteConfirm": "Вы уверены, что хотите удалить персонажа «{id}»? Это действие необратимо.", - "deleteSuccess": "Удалено", - "deleteError": "Ошибка удаления" - }, - "persona": { - "personasTitle": "Персонаж", - "toolsCount": "инстр.", - "skillsCount": "навыков", - "contextMenu": { - "moveTo": "Переместить в..." - }, - "messages": { - "moveSuccess": "Персонаж перемещен", - "moveError": "Не удалось переместить персонажа" - } - }, - "folder": { - "sidebarTitle": "Папки", - "rootFolder": "Корень", - "foldersTitle": "Папки", - "noFolders": "Папок нет", - "createButton": "Новая папка", - "searchPlaceholder": "Поиск папок...", - "form": { - "name": "Имя папки", - "description": "Описание (опционально)" - }, - "validation": { - "nameRequired": "Имя папки не может быть пустым" - }, - "contextMenu": { - "open": "Открыть", - "rename": "Переименовать", - "moveTo": "Переместить в...", - "delete": "Удалить" - }, - "createDialog": { - "title": "Создать папку", - "createButton": "Создать" - }, - "renameDialog": { - "title": "Переименовать папку" - }, - "deleteDialog": { - "title": "Удаление папки", - "message": "Вы уверены, что хотите удалить папку «{name}»?", - "warning": "Все персонажи из этой папки будут перемещены в корневой каталог." - }, - "messages": { - "createSuccess": "Папка создана", - "createError": "Ошибка создания папки", - "renameSuccess": "Папка переименована", - "renameError": "Ошибка переименования папки", - "deleteSuccess": "Папка удалена", - "deleteError": "Ошибка удаления папки" - } - }, - "moveDialog": { - "title": "Перемещение", - "description": "Выберите папку для «{name}»", - "success": "Объект перемещен", - "error": "Ошибка перемещения" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/platform.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/platform.json deleted file mode 100644 index 25ec3e11..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/platform.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "title": "Боты", - "subtitle": "Управление адаптерами платформ для подключения к мессенджерам", - "adapters": "Адаптеры платформ", - "addAdapter": "Создать бота", - "emptyText": "Боты не настроены. Нажмите «Создать бота», чтобы начать.", - "viewWebhook": "Показать Webhook", - "webhookCopied": "URL скопирован в буфер обмена", - "webhookCopyFailed": "Не удалось скопировать, сделайте это вручную", - "webhookDialog": { - "title": "Адрес Webhook", - "description": "Используйте этот адрес для обратных вызовов. Убедитесь, что ваш AstrBot доступен из интернета. Рекомендуется указать «Внешний URL для Webhook» в Настройки -> Общие.", - "close": "Закрыть" - }, - "details": { - "adapterType": "Тип адаптера", - "token": "Токен", - "description": "Описание" - }, - "logs": { - "title": "Логи платформы", - "expand": "Развернуть", - "collapse": "Свернуть" - }, - "dialog": { - "add": "Добавить", - "edit": "Изменить", - "adapter": "Бот", - "refresh": "Обновить", - "cancel": "Отмена", - "save": "Сохранить", - "addPlatform": "Создать бота", - "connectTitle": "Подключение к {name}", - "viewTutorial": "Открыть руководство", - "noTemplates": "Шаблоны не найдены", - "idConflict": { - "title": "Конфликт ID", - "message": "Бот с ID «{id}» уже существует. Пожалуйста, используйте уникальный ID.", - "confirm": "Понятно" - }, - "securityWarning": { - "title": "Безопасность", - "aiocqhttpTokenMissing": "Для защиты соединения крайне рекомендуется установить ws_reverse_token. Работа без токена небезопасна.", - "learnMore": "Подробнее" - }, - "invalidPlatformId": "ID платформы не может содержать символы ':' или '!'." - }, - "createDialog": { - "step1Title": "Выберите мессенджер", - "step1Hint": "Куда вы хотите подключить бота? (QQ, Telegram, Discord, WeChat и др.)", - "platformTypeLabel": "Платформа", - "configFileTitle": "Файл конфигурации", - "optional": "опционально", - "configHint": "Как настроить бота? Конфиг содержит модель, персонажа, базу знаний и набор плагинов.", - "configDefaultHint": "По умолчанию используется профиль «default». Вы сможете изменить его позже.", - "useExistingConfig": "Использовать существующий конфиг", - "selectConfigLabel": "Выберите профиль", - "createNewConfig": "Создать новый профиль", - "newConfigNameLabel": "Имя нового профиля", - "newConfigTitle": "Создание нового профиля", - "newConfigLoadFailed": "Не удалось загрузить шаблон конфигурации", - "addRouteRule": "Добавить правило маршрутизации", - "viewMode": "Просмотр", - "editMode": "Редактирование", - "noRouteRules": "Правила маршрутизации не заданы, будет использоваться профиль по умолчанию", - "sessionIdPlaceholder": "ID сессии или *", - "routeSource": { - "selectPlaceholder": "Выберите источник сессии", - "noData": "Для этого бота пока нет унифицированных источников сообщений. Они появятся здесь после первого сообщения боту на соответствующей платформе.", - "switchToKnown": "Переключиться на выбор источника", - "switchToManual": "Переключиться на ручной ввод" - }, - "allSessions": "Все сессии", - "configMissing": "Файл конфигурации не найден", - "routeHint": "* При получении сообщения AstrBot ищет первое совпадение в списке сверху вниз. Используйте слэш-команду /sid, чтобы узнать ID текущей сессии. Если совпадений нет, используется профиль по умолчанию.", - "warningContinue": "Игнорировать и создать", - "warningEditAgain": "Вернуться к редактированию", - "configDrawerTitle": "Управление профилями", - "configDrawerIdLabel": "ID", - "configTableHeaders": { - "configId": "ID связанного профиля", - "scope": "Область применения" - }, - "routeTableHeaders": { - "source": "Источник (тип:ID)", - "config": "Файл конфига", - "actions": "Действия" - }, - "messageTypeOptions": { - "all": "Все сообщения", - "group": "Групповые (GroupMessage)", - "friend": "Личные (FriendMessage)" - }, - "messageTypeLabels": { - "all": "Все", - "group": "Группа", - "friend": "ЛС" - } - }, - "messages": { - "updateSuccess": "Обновлено!", - "addSuccess": "Добавлено!", - "deleteSuccess": "Удалено!", - "statusUpdateSuccess": "Статус обновлен!", - "deleteConfirm": "Вы уверены, что хотите удалить этого бота?", - "configNotFoundOpenConfig": "Целевой конфиг не найден. Открыта страница настроек для проверки.", - "updateMissingPlatformId": "Ошибка обновления: отсутствует ID платформы.", - "platformUpdateFailed": "Не удалось обновить платформу.", - "addSuccessWithConfig": "Бот успешно добавлен, профиль обновлен", - "configIdMissing": "Не удалось получить ID конфигурации.", - "routingUpdateFailed": "Ошибка обновления маршрутов: {message}", - "createConfigFailed": "Ошибка создания профиля: {message}", - "platformIdMissing": "Не удалось получить ID платформы.", - "routingSaveFailed": "Ошибка сохранения маршрутов: {message}" - }, - "status": { - "enabled": "Включен", - "disabled": "Выключен", - "connecting": "Подключение", - "connected": "Подключен", - "disconnected": "Отключен", - "error": "Ошибка" - }, - "runtimeStatus": { - "running": "Работает", - "error": "Ошибка", - "pending": "Ожидание", - "stopped": "Остановлен", - "unknown": "Неизвестно", - "errors": "ошибок" - }, - "platformQr": { - "title": "QR вход", - "show": "Показать QR", - "status": "Статус QR", - "waiting": "Ожидание QR", - "close": "Закрыть" - }, - "registrationAction": { - "lark": { - "title": "Настройка по QR", - "show": "Настройка по QR", - "scanTitle": "Отсканируйте в мобильном Feishu", - "feishu": "Feishu China", - "lark": "Lark Global" - }, - "weixinOc": { - "title": "QR вход в WeChat", - "scanTitle": "Отсканируйте в мобильном WeChat", - "created": "Вход выполнен", - "status": { - "idle": "Не начато", - "starting": "Получение QR", - "pending": "Ожидание сканирования", - "created": "Вход выполнен - не забудьте нажать кнопку сохранения!", - "denied": "Отменено", - "expired": "Истекло", - "error": "Ошибка входа" - } - }, - "dingtalk": { - "title": "Создание DingTalk по QR", - "scanTitle": "Отсканируйте в мобильном DingTalk", - "created": "Настройка завершена" - }, - "qqOfficial": { - "title": "Привязка QQ Official Bot по QR", - "scanTitle": "Отсканируйте в мобильном QQ", - "created": "Привязка завершена", - "status": { - "idle": "Не начато", - "starting": "Получение QR", - "pending": "Ожидание сканирования", - "created": "Привязка завершена - не забудьте нажать кнопку сохранения!", - "denied": "Отменено", - "expired": "Истекло", - "error": "Ошибка привязки" - } - }, - "start": "Начать настройку", - "created": "Настройка завершена", - "startFailed": "Не удалось начать QR настройку", - "pollFailed": "Не удалось проверить статус QR настройки", - "platformIdLabel": "ID бота", - "platformIdRequired": "ID бота обязателен", - "platformIdInvalid": "ID бота не может содержать пробелы, ':' или '!'", - "mode": { - "title": "Выберите способ создания", - "scan": "Создать через QR", - "manual": "Создать вручную", - "larkManual": "Создать вручную (поддерживает self-hosted Feishu)" - }, - "scanTitle": "Отсканируйте в мобильном Feishu", - "status": { - "idle": "Не начато", - "starting": "Запуск", - "pending": "Ожидание сканирования", - "slow_down": "Опрос замедлен", - "created": "Настройка завершена - не забудьте нажать кнопку сохранения!", - "denied": "Отменено", - "expired": "Истекло", - "error": "Ошибка настройки" - } - }, - "errorDialog": { - "title": "Детали ошибки", - "platformId": "ID платформы", - "errorCount": "Кол-во ошибок", - "lastError": "Последняя ошибка", - "occurredAt": "Время", - "traceback": "Стек вызовов", - "close": "Закрыть" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/provider.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/provider.json deleted file mode 100644 index 7c597df4..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/provider.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "title": "Провайдеры моделей", - "subtitle": "Настройка AI моделей для диалогов. Также поддерживает Dify, Coze, а также внешние Agent-сервисы.", - "providers": { - "title": "Сервис-провайдеры", - "settings": "Настройки", - "addProvider": "Добавить провайдера", - "providerType": "Тип провайдера", - "tabs": { - "all": "Все", - "chatCompletion": "Диалоги", - "agentRunner": "Агенты", - "speechToText": "STT (Речь -> Текст)", - "textToSpeech": "TTS (Текст -> Речь)", - "embedding": "Эмбеддинги", - "rerank": "Rerank (Ранжирование)" - }, - "empty": { - "all": "Провайдеры не добавлены. Нажмите «Добавить провайдера», чтобы начать.", - "typed": "Провайдеры типа «{type}» не найдены." - }, - "description": { - "openai": "Поддерживаются все провайдеры, совместимые с OpenAI API.", - "kimi_code": "Специальная интеграция Kimi CodingPlan / Kimi Code с отдельной совместимой адаптацией Anthropic API.", - "vllm_rerank": "Также поддерживает Jina AI, Cohere, PPIO и другие.", - "default": "Преобразование речи в текст" - } - }, - "availability": { - "title": "Доступность провайдеров", - "subtitle": "Статус определяется путем выполнения тестового запроса. Может взиматься плата согласно тарифу API.", - "refresh": "Проверить статус", - "noData": "Нажмите «Проверить статус», чтобы узнать доступность моделей", - "available": "Доступен", - "unavailable": "Недоступен", - "pending": "Проверка...", - "errorMessage": "Ошибка", - "test": "Тест" - }, - "logs": { - "title": "Логи сервиса", - "expand": "Развернуть", - "collapse": "Свернуть" - }, - "dialogs": { - "addProvider": { - "title": "Новый провайдер", - "tabs": { - "basic": "Диалоги", - "agentRunner": "Агенты", - "speechToText": "Преобразование текста в речь", - "textToSpeech": "Переранжирование", - "embedding": "Эмбеддинги", - "rerank": "API Key" - }, - "noTemplates": "Шаблоны для этого типа не найдены" - }, - "config": { - "addTitle": "Добавить", - "editTitle": "Изменить", - "provider": "Провайдер", - "cancel": "Отмена", - "save": "Сохранить" - }, - "settings": { - "title": "Общие настройки провайдеров", - "sessionSeparation": { - "title": "Изоляция провайдеров по сессиям", - "description": "Позволяет выбирать независимых провайдеров для генерации текста, TTS и STT в каждой конкретной сессии." - }, - "close": "Закрыть" - } - }, - "messages": { - "success": { - "update": "Обновлено!", - "add": "Добавлено!", - "delete": "Удалено!", - "statusUpdate": "Статус обновлен!", - "sessionSeparation": "Настройки изоляции сохранены" - }, - "error": { - "sessionSeparation": "Не удалось загрузить настройки изоляции", - "fetchStatus": "Не удалось получить статус провайдеров", - "testError": "Тест {id} провален: {error}" - }, - "confirm": { - "delete": "Вы уверены, что хотите удалить провайдера «{id}»?" - } - }, - "providerTypes": { - "title": "Тип провайдера" - }, - "providerSources": { - "title": "Источник провайдера", - "add": "Добавить", - "delete": "Удалить источник провайдера", - "empty": "Источники не найдены", - "selectHint": "Пожалуйста, выберите источник провайдера", - "selectCreated": "Выбрать существующий источник", - "save": "Сохранить конфиг", - "saveAndFetchModels": "Сохранить и загрузить модели", - "fetchModels": "Загрузить список моделей", - "saveSuccess": "Источник успешно сохранен", - "saveError": "Ошибка сохранения источника", - "deleteConfirm": "Вы уверены, что хотите удалить источник «{id}»? Все связанные конфигурации моделей будут удалены.", - "deleteSuccess": "Источник удален", - "deleteError": "Ошибка удаления", - "enabled": "Включен", - "disabled": "Выключен", - "advancedConfig": "Расширенные настройки...", - "fields": { - "name": "Имя", - "apiKey": "Base URL", - "baseUrl": "Base URL" - }, - "hints": { - "id": "Уникальный ID источника", - "key": "Ваш серетный API-ключ", - "apiBase": "Адрес API точки входа (Endpoint URL)", - "proxy": "Прокси сервер (HTTP/HTTPS), напр. http://127.0.0.1:7890. Используется только для запросов к этому провайдеру." - }, - "labels": { - "proxy": "Прокси" - } - }, - "models": { - "title": "Модели", - "available": "Доступные модели", - "configured": "Настроенные модели", - "empty": "Модели не настроены. Нажмите «Загрузить список моделей» выше.", - "noModelsFound": "Модели не найдены", - "fetchError": "Не удалось получить список моделей", - "addSuccess": "Модель {model} успешно добавлена", - "deleteConfirm": "Вы уверены, что хотите удалить модель «{id}»?", - "deleteSuccess": "Модель удалена", - "deleteError": "Ошибка удаления модели", - "testSuccess": "Тест модели «{id}» пройден успешно", - "testSuccessWithLatency": "Тест модели «{id}» пройден успешно, задержка {latency} мс", - "testError": "Тест модели провален", - "searchPlaceholder": "Поиск по имени или ID", - "manualAddButton": "Добавить вручную", - "manualDialogTitle": "Произвольная модель", - "manualDialogModelLabel": "Код модели (напр. gpt-4o-mini)", - "manualDialogPreviewLabel": "Отображаемый ID (авто)", - "manualDialogPreviewHint": "Будет выглядеть как: SourceID/ModelID", - "manualModelRequired": "Укажите ID модели", - "manualModelExists": "Эта модель уже добавлена", - "configure": "Настроить", - "testButton": "Тестировать модель", - "metadata": { - "image": "Ввод изображений", - "audio": "Ввод аудио", - "toolUse": "Использование инструментов", - "reasoning": "Рассуждение", - "context": "Контекстное окно {tokens}", - "available": "Metadata модели поддерживает {capability}", - "enabled": "{capability} включено", - "supportedDisabled": "Metadata модели поддерживает {capability}, но эта возможность не включена в modalities текущего провайдера." - }, - "tooltips": { - "providerId": "ID провайдера", - "modelId": "ID модели" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/session-management.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/session-management.json deleted file mode 100644 index f0ffa31b..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/session-management.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "title": "Управление сессиями", - "subtitle": "Настройка индивидуальных правил для конкретных диалогов. Эти правила имеют приоритет над глобальной конфигурацией.", - "buttons": { - "refresh": "Обновить", - "edit": "Изменить", - "editRule": "Редактировать правило", - "deleteAllRules": "Удалить все правила", - "addRule": "Добавить правило", - "save": "Сохранить", - "cancel": "Отмена", - "delete": "Удалить", - "clear": "Очистить", - "next": "Далее", - "editCustomName": "Изменить заметку", - "batchDelete": "Массовое удаление" - }, - "customRules": { - "title": "Пользовательские правила", - "rulesCount": "правил", - "hasRules": "Настроено", - "noRules": "Индивидуальных правил нет", - "noRulesDesc": "Нажмите «Добавить правило», чтобы задать настройки для конкретного диалога", - "serviceConfig": "Сервис", - "pluginConfig": "Плагины", - "kbConfig": "База знаний", - "providerConfig": "Модель", - "configured": "Настроено", - "noCustomName": "Без заметки" - }, - "quickEditName": { - "title": "Редактирование заметки" - }, - "search": { - "placeholder": "Поиск сессии..." - }, - "table": { - "headers": { - "umoInfo": "Источник (UMO)", - "rulesOverview": "Обзор правил", - "actions": "Действия" - } - }, - "persona": { - "none": "Из конфигурации" - }, - "provider": { - "followConfig": "Из конфигурации" - }, - "addRule": { - "title": "Добавление правила", - "description": "Выберите источник сообщения (UMO) для настройки. Индивидуальные правила приоритетнее глобальных. Используйте команду /sid в чате, чтобы узнать информацию об источнике.", - "selectUmo": "Выберите сессию", - "noUmos": "Нет доступных сессий" - }, - "ruleEditor": { - "title": "Редактор правил", - "description": "Настройте поведение для этой сессии. Настройки ниже перекроют глобальный конфиг.", - "serviceConfig": { - "title": "Сервисные настройки", - "sessionEnabled": "Обрабатывать сообщения", - "llmEnabled": "Использовать LLM", - "ttsEnabled": "Использовать TTS", - "customName": "Заметка для сессии" - }, - "providerConfig": { - "title": "Выбор моделей", - "chatProvider": "Чат-модель", - "sttProvider": "STT (Распознавание)", - "ttsProvider": "TTS (Озвучка)" - }, - "personaConfig": { - "title": "Персона", - "selectPersona": "Выберите Persona", - "hint": "При выборе Persona все диалоги из этого источника будут использовать именно её." - }, - "pluginConfig": { - "title": "Плагины", - "disabledPlugins": "Отключенные плагины", - "hint": "Выберите плагины, которые нужно ОТКЛЮЧИТЬ в этой сессии. Остальные останутся активными." - }, - "kbConfig": { - "title": "База знаний", - "selectKbs": "Выбор баз знаний", - "topK": "Количество результатов (Top K)", - "enableRerank": "Использовать Rerank" - } - }, - "deleteConfirm": { - "title": "Подтверждение", - "message": "Удалить все настройки для этой сессии? Будут применены глобальные настройки." - }, - "batchDeleteConfirm": { - "title": "Массовое удаление", - "message": "Удалить {count} выбранных правил? Будут применены глобальные настройки." - }, - "batchOperations": { - "title": "Массовые операции", - "hint": "Быстрое изменение настроек для группы сессий", - "scope": "Область применения", - "scopeSelected": "Выбранные", - "scopeAll": "Все сессии", - "scopeGroup": "Все группы", - "scopePrivate": "Личные диалоги", - "llmStatus": "Статус LLM", - "ttsStatus": "Статус TTS", - "chatProvider": "Чат-модель", - "ttsProvider": "TTS-модель", - "apply": "Применить" - }, - "groups": { - "title": "Управление группами", - "count": "групп: {count}", - "addToGroup": "Добавить в группу", - "create": "Создать группу", - "edit": "Изменить группу", - "name": "Имя группы", - "sessionsCount": "сессий: {count}", - "empty": "Пока нет групп. Нажмите «Создать группу», чтобы добавить.", - "availableSessions": "Доступные сессии ({count})", - "selectedSessions": "Выбранные сессии ({count})", - "searchPlaceholder": "Поиск...", - "noMatch": "Нет совпадений", - "noMembers": "Нет участников", - "customGroupDivider": "── Пользовательские группы ──", - "customGroupOption": "📁 {name} ({count})", - "groupOption": "{name} (сессий: {count})", - "deleteConfirm": "Вы уверены, что хотите удалить группу \"{name}\"?" - }, - "status": { - "enabled": "Включено", - "disabled": "Выключено" - }, - "messages": { - "refreshSuccess": "Данные обновлены", - "loadError": "Ошибка загрузки", - "saveSuccess": "Настройки сохранены", - "saveError": "Ошибка сохранения", - "clearSuccess": "Очищено", - "clearError": "Ошибка очистки", - "deleteSuccess": "Удалено", - "deleteError": "Ошибка удаления", - "noChanges": "Изменений не обнаружено", - "batchDeleteSuccess": "Массовое удаление выполнено", - "batchDeleteError": "Ошибка массового удаления", - "selectSessionsFirst": "Пожалуйста, сначала выберите сессии", - "selectAtLeastOneConfig": "Пожалуйста, выберите хотя бы одну настройку для изменения", - "batchUpdateSuccess": "Пакетное обновление успешно выполнено", - "partialUpdateFailed": "Некоторые обновления не выполнены", - "batchUpdateError": "Ошибка пакетного обновления", - "groupNameRequired": "Имя группы не может быть пустым", - "saveGroupError": "Ошибка сохранения группы", - "deleteGroupError": "Ошибка удаления группы", - "selectSessionsToAddFirst": "Пожалуйста, сначала выберите сессии для добавления", - "addToGroupSuccess": "Добавлено сессий в группу: {count}", - "addToGroupError": "Ошибка добавления в группу" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/settings.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/settings.json deleted file mode 100644 index 58cdda79..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/settings.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "page": { - "title": "Настройки" - }, - "sections": { - "general": { - "title": "Общие" - }, - "appearance": { - "title": "Внешний вид" - }, - "network": { - "title": "Сеть" - }, - "security": { - "title": "Безопасность" - }, - "maintenance": { - "title": "Обслуживание" - }, - "openapi": { - "title": "OpenAPI" - } - }, - "systemConfig": { - "restartRequired": "Системная конфигурация сохранена. Часть изменений вступит в силу после перезапуска AstrBot.", - "groups": { - "runtime": { - "title": "Базовый запуск", - "subtitle": "Часовой пояс и внешний адрес callback." - }, - "network": { - "title": "Прокси и источники зависимостей", - "subtitle": "Runtime-прокси, параметры установки Python-зависимостей и источник PyPI." - }, - "webuiSecurity": { - "title": "Безопасность WebUI", - "subtitle": "HTTPS, proxy-заголовки, ограничения входа и TOTP." - }, - "logs": { - "title": "Логи", - "subtitle": "Консольные логи, файловые логи и Trace." - }, - "tempStorage": { - "title": "Кэш", - "subtitle": "Порог автоматической очистки временной папки." - }, - "t2iRendering": { - "title": "Текст в изображение", - "subtitle": "Стратегия, endpoint и пользовательские шаблоны для рендеринга." - } - }, - "messages": { - "loadFailed": "Не удалось загрузить системную конфигурацию", - "saveSuccess": "Системная конфигурация сохранена", - "saveFailed": "Не удалось сохранить системную конфигурацию" - } - }, - "network": { - "title": "Сеть", - "githubProxy": { - "title": "Зеркало GitHub", - "subtitle": "Адрес для ускорения загрузки плагинов и обновлений AstrBot. Особенно актуально для пользователей из Китая. Все адреса предоставляются как есть, если обновление не удается — проверьте доступность выбранного зеркала.", - "label": "Выбрать ускоритель GitHub" - }, - "proxySelector": { - "title": "Ускорение GitHub", - "noProxy": "Не использовать", - "useProxy": "Включить", - "testConnection": "Проверить соединение", - "available": "Доступен", - "unavailable": "Недоступен", - "custom": "Свой вариант" - } - }, - "theme": { - "title": "Тема оформления", - "subtitle": "Настройка основных и дополнительных цветов. Изменения вступают в силу немедленно и сохраняются в браузере.", - "customize": { - "title": "Цвета темы", - "primary": "Основной", - "secondary": "Дополнительный", - "reset": "Сбросить" - } - }, - "system": { - "title": "Система", - "restart": { - "title": "Перезапуск", - "subtitle": "Выполнить мягкий перезапуск AstrBot", - "confirm": "Перезапустить AstrBot сейчас? Во время перезапуска WebUI может быть временно недоступен.", - "button": "Перезагрузить" - }, - "backup": { - "title": "Резервное копирование", - "subtitle": "Важнейший инструмент для безопасного переноса данных между серверами.", - "button": "Управление бэкапами" - }, - "cleanup": { - "title": "Очистка логов и кэша", - "subtitle": "Показывает текущий размер логов и кэша и позволяет очистить их прямо из WebUI.", - "refresh": "Обновить", - "clean": "Очистить", - "cleanAll": "Очистить все", - "panel": { - "title": "Детали очистки", - "subtitle": "Текущий размер: {size}" - }, - "fileCount": "{count} файлов", - "confirm": "Очистить {target}?", - "targetNames": { - "cache": "кэш", - "logs": "логи", - "all": "логи и кэш" - }, - "targets": { - "cache": { - "title": "Кэш", - "subtitle": "Удаляет временные файлы, кэш каталога плагинов и кэш навыков.", - "button": "Очистить кэш" - }, - "logs": { - "title": "Логи", - "subtitle": "Удаляет старые файлы логов и очищает текущие активные логи.", - "button": "Очистить логи" - } - }, - "messages": { - "statusFailed": "Не удалось получить размер хранилища", - "cleanupSuccess": "Очищено {count} файлов, освобождено {size}", - "cleanupFailed": "Ошибка очистки" - } - } - }, - "sidebar": { - "title": "Боковая панель", - "customize": { - "title": "Настройка меню", - "subtitle": "Перетаскивайте элементы, чтобы изменить их порядок или скрыть в группе «Дополнительно». Настройки сохраняются локально в браузере.", - "reset": "Сбросить порядок", - "mainItems": "Основные разделы", - "moreItems": "Дополнительно" - } - }, - "backup": { - "dialog": { - "title": "Резервное копирование" - }, - "tabs": { - "export": "Экспорт", - "import": "Импорт", - "list": "Список копий" - }, - "export": { - "title": "Создать резервную копию", - "description": "Экспорт всех данных в ZIP-архив, включая базы данных, базу знаний, конфигурации, вложения и Skills.", - "includes": "Включает: основную БД, векторные индексы знаний, файлы конфигурации, медиа-вложения, Skills.", - "button": "Начать экспорт", - "processing": "Экспорт...", - "wait": "Пожалуйста, подождите, мы упаковываем данные...", - "completed": "Готово!", - "download": "Скачать архив", - "another": "Создать новый", - "failed": "Ошибка экспорта", - "retry": "Повторить" - }, - "import": { - "title": "Восстановление из копии", - "warning": "⚠️ Внимание! Импорт полностью удалит и перезапишет текущие данные! Убедитесь, что у вас есть копия текущего состояния.", - "selectFile": "Выберите ZIP-архив", - "uploadAndCheck": "Загрузить и проверить", - "uploading": "Загрузка...", - "uploadWait": "Файл передается на сервер...", - "uploadInit": "Инициализация...", - "uploadingChunks": "Передача фрагментов...", - "uploadComplete": "Загружено, идет сборка...", - "checking": "Проверка структуры...", - "invalidBackup": "Некорректный файл резервной копии", - "backupContents": "Состав архива", - "tables": "таблиц БД", - "knowledgeBases": "баз знаний", - "configFiles": "конфигов", - "confirmImport": "Подтвердите импорт", - "button": "Начать восстановление", - "processing": "Восстановление...", - "wait": "Идет процесс развертывания данных...", - "completed": "Восстановление успешно завершено!", - "restartRequired": "Данные восстановлены. Необходимо немедленно перезапустить AstrBot для вступления изменений в силу.", - "restartNow": "Перезапустить сейчас", - "failed": "Ошибка импорта", - "retry": "Повторить", - "version": { - "backupVersion": "Версия бэкапа", - "currentVersion": "Текущая версия", - "backupTime": "Дата создания", - "matchTitle": "✅ Версии совпадают", - "matchMessage": "Импорт перезапишет все текущие данные, включая:\n• Основную БД (чаты, настройки)\n• Базы знаний\n• Плагины и их данные\n• Файлы конфигурации\n• Skills\n\nЭто действие необратимо! Продолжить?", - "minorDiffTitle": "⚠️ Разница в минорной версии", - "minorDiffMessage": "Разница в минорных версиях обычно допустима, но структура данных могла немного измениться. Все текущие данные будут удалены!\n\nПродолжить импорт?", - "majorDiffTitle": "⛔ Импорт невозможен", - "majorDiffMessage": "Версии основного выпуска различаются. Импорт между мажорными версиями может привести к фатальному повреждению данных.\nИспользуйте AstrBot той же основной версии." - } - }, - "list": { - "empty": "Резервные копии не найдены", - "refresh": "Обновить список", - "confirmDelete": "Вы уверены, что хотите безвозвратно удалить эту копию?", - "uploaded": "Загружено", - "restore": "Восстановить из этого файла", - "rename": "Переименовать", - "renameTitle": "Переименование файла", - "newName": "Новое имя", - "renameHint": "Разрешены буквы, цифры, точки, дефисы и подчеркивания", - "renameRequired": "Введите имя файла", - "renameInvalidChars": "Имя содержит недопустимые символы", - "renameFailed": "Ошибка переименования", - "ftpHint": "Для больших архивов вы можете загружать их напрямую в папку data/backups через FTP/SFTP." - } - }, - "apiKey": { - "title": "API Keys", - "manageTitle": "Ключи доступа разработчика", - "subtitle": "Управление токенами для доступа к открытому HTTP API AstrBot.", - "name": "Имя ключа", - "expiresInDays": "Срок действия", - "expiryOptions": { - "day1": "1 день", - "day7": "7 дней", - "day30": "30 дней", - "day90": "90 дней", - "permanent": "Бессрочно" - }, - "permanentWarning": "Бессрочные ключи менее безопасны. Пожалуйста, храните их в надежном месте.", - "scopes": "Область доступа (Scopes)", - "create": "Создать API Key", - "revoke": "Отозвать", - "delete": "Удалить", - "copy": "Копировать", - "docsLink": "Документация API", - "plaintextHint": "Обязательно сохраните ключ сейчас. После закрытия окна вы больше не сможете увидеть его значение.", - "empty": "Ключи не созданы", - "status": { - "active": "Активен", - "inactive": "Неактивен" - }, - "table": { - "name": "Имя", - "prefix": "Префикс", - "scopes": "Права", - "status": "Статус", - "lastUsed": "Использован", - "createdAt": "Создан", - "actions": "Действия" - }, - "messages": { - "loadFailed": "Не удалось загрузить ключи", - "scopeRequired": "Выберите хотя бы одну область доступа", - "createSuccess": "API Key создан", - "createFailed": "Ошибка создания ключа", - "revokeSuccess": "Ключ отозван", - "revokeFailed": "Ошибка отзыва ключа", - "deleteSuccess": "Ключ удален", - "deleteFailed": "Ошибка удаления ключа", - "copySuccess": "Ключ скопирован", - "copyFailed": "Ошибка копирования" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/stats.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/stats.json deleted file mode 100644 index 572ac358..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/stats.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "header": { - "eyebrow": "Dashboard", - "title": "Статистика системы", - "subtitle": "Единый обзор платформ, сообщений и вызовов моделей.", - "notUpdated": "Еще не обновлялось" - }, - "ranges": { - "oneDay": "1 день", - "threeDays": "3 дня", - "oneWeek": "1 неделя" - }, - "rangeLabels": { - "oneDay": "За 1 день", - "threeDays": "За 3 дня", - "oneWeek": "За 1 неделю" - }, - "overviewCards": { - "platformCount": { - "label": "Платформы", - "note": "Настроенные и запущенные экземпляры платформ" - }, - "messageCount": { - "label": "Сообщения", - "note": "Общее число сообщений" - }, - "todayModelCalls": { - "label": "Вызовы моделей сегодня", - "note": "Tokens" - }, - "cpu": { - "label": "CPU", - "note": "Использование CPU AstrBot" - }, - "memory": { - "label": "Память процесса", - "note": "Системная память {systemMemory}" - }, - "uptime": { - "label": "Время работы", - "note": "Запущен в {startTime}" - } - }, - "messageOverview": { - "title": "Обзор сообщений", - "subtitle": "Статистика сообщений бота" - }, - "messageTrend": { - "title": "Динамика сообщений", - "subtitle": "{range}, агрегация по часам", - "totalMessages": "Всего сообщений" - }, - "platformRanking": { - "title": "Рейтинг платформ по сообщениям", - "subtitle": "{range}, сортировка по общему числу сообщений" - }, - "modelCalls": { - "title": "Вызовы моделей", - "subtitle": "Статистика вызовов моделей" - }, - "modelTrend": { - "title": "Динамика вызовов моделей", - "subtitle": "Динамика токенов вызовов моделей" - }, - "modelTotal": { - "title": "{range} всего вызовов моделей", - "callCount": "{count} вызовов", - "avgTtft": "Средний TTFT", - "avgDuration": "Среднее время ответа", - "avgTpm": "Средний Output TPM", - "successRate": "Доля успешных вызовов" - }, - "modelRanking": { - "title": "{range} рейтинг вызовов моделей", - "subtitle": "Рейтинг по токенам вызовов моделей" - }, - "sessionRanking": { - "title": "{range} топ-10 сессий по токенам", - "subtitle": "Сессии с наибольшим расходом токенов" - }, - "empty": { - "platformStats": "Статистика сообщений по платформам отсутствует.", - "modelCalls": "Нет данных по вызовам моделей за период {range}.", - "sessionCalls": "Нет данных по сессиям за период {range}." - }, - "chart": { - "messages": "Сообщения", - "others": "Другое" - }, - "units": { - "tokens": "Tokens", - "tpm": "TPM", - "gb": "ГБ", - "mb": "МБ", - "ms": "мс", - "hoursShort": "ч", - "minutesShort": "м", - "secondsShort": "с" - }, - "errors": { - "loadFailed": "Не удалось загрузить статистику. Повторите попытку позже.", - "rangeFailed": "Не удалось переключить диапазон статистики. Повторите попытку позже." - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/subagent.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/subagent.json deleted file mode 100644 index 4f6b298b..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/subagent.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "header": { - "eyebrow": "Orchestration" - }, - "page": { - "title": "Оркестрация SubAgent", - "beta": "Экспериментально", - "subtitle": "Основной LLM может напрямую использовать свои инструменты или делегировать задачи SubAgent через handoff." - }, - "actions": { - "refresh": "Обновить", - "save": "Сохранить", - "add": "Добавить SubAgent", - "expand": "Развернуть", - "collapse": "Свернуть", - "delete": "Удалить", - "close": "Закрыть" - }, - "overview": { - "totalAgents": "Всего SubAgent", - "totalAgentsNote": "Количество настроенных под-агентов", - "enabledAgents": "Активные агенты", - "enabledAgentsNote": "Под-агенты, доступные для handoff", - "mainOrchestration": "Основная оркестрация", - "boundPersonas": "Привязанные персонажи", - "boundPersonasNote": "Под-агенты с выбранной персоной" - }, - "switches": { - "enable": "Включить оркестрацию SubAgent", - "enableHint": "Включить функциональность под-агентов", - "dedupe": "Дедупликация инструментов основного LLM (скрывать инструменты, дублируемые SubAgent)", - "dedupeHint": "Удалить дублирующиеся инструменты из основного агента" - }, - "description": { - "disabled": "Выключено: SubAgent отключен; основной LLM подключает инструменты согласно правилам персонажа (все по умолчанию) и вызывает их напрямую.", - "enabled": "Включено: основной LLM сохраняет свои инструменты и подключает инструменты делегирования transfer_to_*. При дедупликации инструменты, пересекающиеся с SubAgent, удаляются из основного набора." - }, - "section": { - "title": "Субагенты", - "subtitle": "Настройте делегируемых агентов, персонажей и описания для основного LLM", - "globalSettings": "Глобальные настройки", - "agentSetup": "Настройка агента" - }, - "cards": { - "statusEnabled": "Включено", - "statusDisabled": "Отключено", - "unnamed": "Безымянный SubAgent", - "transferPrefix": "передать_{name}", - "switchLabel": "Включить", - "previewTitle": "Предпросмотр: инструмент handoff, видимый основному LLM", - "personaChip": "Персонаж: {id}", - "personaPreview": "Предпросмотр персонажа", - "noDescription": "Описание пока не задано", - "previewHint": "Просмотрите выбранную персону, чтобы проверить цель handoff." - }, - "form": { - "nameLabel": "Имя агента (используется для transfer_to_{name})", - "nameHint": "Используйте строчные латинские буквы и подчеркивания; имя должно быть глобально уникальным.", - "providerLabel": "Chat Provider (опционально)", - "providerHint": "Оставьте пустым, чтобы использовать глобальный провайдер по умолчанию.", - "personaLabel": "Выберите персонажа", - "personaHint": "SubAgent наследует системные настройки и инструменты выбранного персонажа.", - "descriptionLabel": "Описание для основного LLM (используется для принятия решения о handoff)", - "descriptionHint": "Отображается как описание инструмента transfer_to_* — будьте кратки и ясны." - }, - "messages": { - "loadConfigFailed": "Не удалось загрузить конфигурацию", - "loadPersonaFailed": "Не удалось загрузить список персонажей", - "unsavedChangesNotice": "На странице есть несохраненные изменения. Сохраните их перед уходом.", - "unsavedChangesLeaveConfirm": "Есть несохраненные изменения. При уходе со страницы они будут потеряны. Продолжить?", - "unsavedChangesReloadConfirm": "Есть несохраненные изменения. Обновление конфигурации их сбросит. Продолжить?", - "nameMissing": "У SubAgent отсутствует имя", - "nameInvalid": "Недопустимое имя SubAgent: только строчные латинские буквы/цифры/подчеркивания, должно начинаться с буквы", - "nameDuplicate": "Дублирующееся имя SubAgent: {name}", - "personaMissing": "У SubAgent {name} не выбран персонаж", - "saveSuccess": "Успешно сохранено", - "saveFailed": "Ошибка сохранения", - "nameRequired": "Имя обязательно", - "namePattern": "Только строчные буквы, цифры и подчеркивание" - }, - "empty": { - "title": "Агенты не настроены", - "subtitle": "Добавьте первого под-агента, чтобы начать", - "action": "Создать первого агента" - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/tool-use.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/tool-use.json deleted file mode 100644 index a4e2a583..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/tool-use.json +++ /dev/null @@ -1,224 +0,0 @@ -{ - "title": "Инструменты и функции", - "subtitle": "Управление MCP-серверами и доступными функциями", - "tooltip": { - "info": "Что такое Function Calling и MCP?", - "marketplace": "Обзор и установка MCP-серверов от сообщества", - "serverConfig": "Конфигурация MCP-серверов (stdio) поддерживает следующие поля:\ncommand: имя команды (например, python или uv)\nargs: массив аргументов (например, [\"run\", \"server.py\"])\nenv: объект переменных окружения (например, {\"api_key\": \"abc\"})\ncwd: рабочий каталог (например, /path/to/server)\nencoding: кодировка вывода (по умолчанию utf-8)\nПодробности см. в документации MCP.\n⚠️ Если вы используете Docker, устанавливайте сервера в смонтированную директорию data." - }, - "tabs": { - "local": "Локальные сервера", - "marketplace": "Магазин MCP" - }, - "mcpServers": { - "title": "MCP Сервера", - "description": "Управление MCP-серверами", - "buttons": { - "refresh": "Обновить", - "add": "Добавить сервер", - "useTemplateStdio": "Шаблон Stdio", - "useTemplateStreamableHttp": "Шаблон Streamable HTTP", - "useTemplateSse": "Шаблон SSE", - "sync": "Синхронизировать" - }, - "empty": "MCP-сервера не найдены. Нажмите «Добавить сервер».", - "status": { - "noTools": "Нет доступных инструментов", - "availableTools": "Доступные инструменты", - "configSummary": "Конфигурация: {keys}", - "noConfig": "Конфигурация не задана" - } - }, - "functionTools": { - "title": "Функции (Tools)", - "buttons": { - "view": "Показать инструменты" - }, - "search": "Поиск по функциям", - "empty": "Доступные инструменты не найдены", - "description": "Описание функции", - "parameters": "Параметры", - "noParameters": "У этого инструмента нет параметров", - "filter": { - "showBuiltin": "Показать встроенные" - }, - "table": { - "paramName": "Параметр", - "type": "Тип", - "description": "Описание", - "required": "Обяз.", - "origin": "Источник", - "originName": "Имя источника", - "readonly": "Только чтение", - "permission": "Доступ", - "permissionAdmin": "Админ", - "permissionMember": "Все", - "permissionEveryone": "Все", - "permissionBuiltin": "Встроенный", - "actions": "Действия" - }, - "configTags": { - "tooltipTitle": "Этот инструмент включен в файле конфигурации {config}, потому что:", - "conditions": { - "truthy": "{key} включен", - "equals": "{key} = {expected}", - "in": "{key} соответствует {expected}", - "fallback": "Текущее значение {key}: {actual}" - } - }, - "summary": { - "total": "Всего", - "active": "Активно", - "inactive": "Неактивно" - } - }, - "marketplace": { - "title": "Магазин MCP-серверов", - "search": "Поиск по магазину", - "buttons": { - "refresh": "Обновить", - "detail": "Инфо", - "import": "Импорт" - }, - "loading": "Загрузка списка серверов...", - "empty": "Доступных MCP-серверов не найдено", - "status": { - "availableTools": "Инструментов: {count}", - "noToolsInfo": "Нет данных об инструментах" - } - }, - "dialogs": { - "addServer": { - "title": "Добавление MCP-сервера", - "editTitle": "Редактирование MCP-сервера", - "fields": { - "name": "Название сервера", - "nameRequired": "Название обязательно", - "enable": "Включить сервер", - "config": "Конфигурация сервера" - }, - "errors": { - "configEmpty": "Конфигурация не может быть пустой", - "jsonFormat": "Ошибка формата JSON: {error}", - "jsonParse": "Ошибка разбора JSON: {error}" - }, - "buttons": { - "cancel": "Отмена", - "save": "Сохранить", - "testConnection": "Тест связи", - "sync": "Синхронизировать" - }, - "tips": { - "timeoutConfig": "Тайм-аут вызова инструментов настраивается отдельно на странице конфигурации", - "transportRecommendation": "Рекомендуется сначала использовать режим Streamable HTTP или SSE. Режим stdio запускает локальный процесс на хосте AstrBot и подходит только для доверенных MCP-серверов." - } - }, - "serverDetail": { - "title": "Детали сервера", - "installConfig": "Конфигурация установки", - "availableTools": "Список инструментов", - "buttons": { - "close": "Закрыть", - "importConfig": "Импортировать конфиг" - } - }, - "confirmDelete": "Вы уверены, что хотите удалить сервер «{name}»?", - "syncProvider": { - "title": "Синхронизация MCP", - "subtitle": "Загрузка конфигурации MCP-серверов от провайдера", - "steps": { - "selectProvider": "Шаг 1: Провайдер", - "configureAuth": "Шаг 2: Авторизация", - "syncServers": "Шаг 3: Синхронизация" - }, - "providers": { - "modelscope": "ModelScope", - "description": "ModelScope — это сообщество моделей с открытым исходным кодом, предоставляющее различные MCP-сервера для AI-сервисов" - }, - "fields": { - "provider": "Выберите провайдера", - "accessToken": "Токен доступа", - "tokenRequired": "Токен обязателен", - "tokenHint": "Введите ваш токен доступа ModelScope" - }, - "buttons": { - "cancel": "Отмена", - "previous": "Назад", - "next": "Далее", - "sync": "Начать", - "getToken": "Получить токен" - }, - "status": { - "selectProvider": "Пожалуйста, выберите провайдера MCP-серверов", - "enterToken": "Введите токен для продолжения", - "readyToSync": "Готов к синхронизации" - }, - "messages": { - "syncSuccess": "MCP-сервера успешно синхронизированы!", - "syncError": "Ошибка синхронизации: {error}", - "tokenHelp": "Как получить токен ModelScope? Нажмите кнопку справа для инструкции" - } - } - }, - "messages": { - "getServersError": "Ошибка получения списка серверов: {error}", - "getToolsError": "Ошибка получения списка инструментов: {error}", - "saveSuccess": "Настройки сохранены!", - "saveError": "Ошибка сохранения: {error}", - "deleteSuccess": "Сервер удален успешно!", - "deleteError": "Ошибка удаления: {error}", - "updateSuccess": "Обновлено успешно!", - "updateError": "Ошибка обновления: {error}", - "getMarketError": "Не удалось загрузить магазин MCP: {error}", - "importError": { - "noConfig": "У этого сервера нет доступной конфигурации", - "invalidFormat": "Неверный формат конфигурации", - "failed": "Импорт не удался: {error}" - }, - "configParseError": "Ошибка разбора конфигурации: {error}", - "noAvailableConfig": "Конфигурация отсутствует", - "toggleToolSuccess": "Статус инструмента изменен!", - "toggleToolReadonly": "Встроенные инструменты доступны только для чтения и не могут быть включены или выключены.", - "toggleToolError": "Не удалось изменить статус: {error}", - "updateToolPermissionSuccess": "Доступ к {name} обновлён", - "updateToolPermissionFailed": "Не удалось обновить доступ к инструменту", - "updateToolPermissionBuiltin": "Встроенные инструменты не поддерживают настройку доступа.", - "testError": "Ошибка теста связи: {error}" - }, - "syncProvider": { - "title": "Синхронизация серверов MCP", - "subtitle": "Синхронизировать конфигурации серверов MCP от провайдеров с локальными", - "steps": { - "selectProvider": "Шаг 1: Выберите провайдер", - "configureAuth": "Шаг 2: Настройте аутентификацию", - "syncServers": "Шаг 3: Синхронизируйте серверы" - }, - "providers": { - "modelscope": "ModelScope", - "description": "ModelScope — это сообщество открытых моделей, предоставляющее серверы MCP для различных сервисов машинного обучения и ИИ" - }, - "fields": { - "provider": "Выберите провайдер", - "accessToken": "Токен доступа", - "tokenRequired": "Требуется токен доступа", - "tokenHint": "Введите ваш токен доступа ModelScope" - }, - "buttons": { - "cancel": "Отмена", - "previous": "Назад", - "next": "Далее", - "sync": "Начать синхронизацию", - "getToken": "Получить токен" - }, - "status": { - "selectProvider": "Пожалуйста, выберите провайдер сервера MCP", - "enterToken": "Введите токен доступа для продолжения", - "readyToSync": "Готово к синхронизации конфигураций серверов" - }, - "messages": { - "syncSuccess": "Серверы MCP успешно синхронизированы!", - "syncError": "Ошибка синхронизации: {error}", - "tokenHelp": "Как получить токен доступа ModelScope? Нажмите кнопку справа для получения инструкций" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/trace.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/trace.json deleted file mode 100644 index 484985ba..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/trace.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "Трассировка (Trace)", - "autoScroll": { - "enabled": "Автопрокрутка: ВКЛ", - "disabled": "Автопрокрутка: ВЫКЛ" - }, - "hint": "В данный момент записываются только вызовы моделей основного агента AstrBot. Система будет совершенствоваться.", - "recording": "Запись...", - "paused": "Пауза" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/features/welcome.json b/legacy-dashboard/src/i18n/locales/ru-RU/features/welcome.json deleted file mode 100644 index f1b08513..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/features/welcome.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "greeting": { - "morning": "Доброе утро, добро пожаловать в AstrBot", - "afternoon": "Добрый день, добро пожаловать в AstrBot", - "evening": "Добрый вечер, добро пожаловать в AstrBot", - "newYear": "С Новым Годом!" - }, - "subtitle": "Сначала пройдите базовое руководство. Настройку платформ и провайдеров моделей можно завершить позже.", - "announcement": { - "title": "Объявление" - }, - "onboard": { - "title": "Быстрый старт", - "subtitle": "Вы можете выполнить первичную настройку прямо здесь.", - "step1Title": "Настройка AI моделей", - "step1Desc": "Выберите и настройте AI провайдеров для AstrBot.", - "step2Title": "Настройка платформ", - "step2Desc": "Подключите AstrBot к QQ, Lark, WeChat, Telegram и другим мессенджерам.", - "step3Title": "Разрешить Agent использовать компьютер", - "step3Desc": "Укажите, может ли Agent получать доступ к этому компьютеру и использовать его.", - "step3HelpTitle": "Сведения о доступе", - "step3HelpItem1": "Agent сможет получать доступ к каталогу рабочего пространства и использовать его, при этом рабочие каталоги разных пользователей изолированы друг от друга, а также использовать Skills для выполнения более сложных задач.", - "step3HelpItem2": "Для администраторов AstrBot Agent дополнительно сможет выполнять shell-команды, запускать Python-код и получать доступ ко всем локальным каталогам на этом компьютере.", - "step3HelpClose": "Закрыть", - "step3SelectLabel": "Доступ к компьютеру", - "step3Allow": "Разрешить", - "step3Deny": "Запретить", - "configure": "Настроить", - "save": "Сохранить", - "skip": "Пропустить", - "pending": "Ожидает", - "completed": "Готово", - "skipped": "Пропущено", - "platformLoadFailed": "Ошибка загрузки конфигурации платформ", - "providerLoadFailed": "Ошибка загрузки конфигурации провайдеров", - "providerUpdateFailed": "Ошибка обновления провайдера по умолчанию в файле default", - "providerDefaultUpdated": "Провайдер {id} установлен по умолчанию в файле default", - "computerAccessUpdateFailed": "Не удалось обновить доступ Agent к компьютеру", - "computerAccessAllowed": "Agent теперь может получать доступ к компьютеру и использовать его", - "computerAccessDenied": "Agent больше не может получать доступ к компьютеру и использовать его", - "step3HelpItem3": "Для более тонкой настройки перейдите в «Конфигурация → Основные настройки → Доступ к компьютеру»." - }, - "resources": { - "title": "Ресурсы", - "githubDesc": "Поставьте нам звезду на GitHub!", - "docsTitle": "Документация", - "docsDesc": "Официальная документация AstrBot.", - "afdianTitle": "Afdian", - "afdianDesc": "Поддержите команду AstrBot через Afdian." - } -} diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/messages/errors.json b/legacy-dashboard/src/i18n/locales/ru-RU/messages/errors.json deleted file mode 100644 index 90ea43c8..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/messages/errors.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "network": { - "timeout": "Время ожидания запроса истекло, попробуйте позже", - "connection": "Ошибка сетевого соединения. Проверьте интернет", - "server": "Внутренняя ошибка сервера. Обратитесь в поддержку", - "unavailable": "Сервис временно недоступен", - "forbidden": "Доступ запрещен" - }, - "validation": { - "required": "Это поле обязательно для заполнения", - "invalid": "Неверный формат ввода", - "tooLong": "Введено слишком много символов", - "tooShort": "Введено слишком мало символов", - "email": "Укажите корректный email", - "url": "Укажите корректный URL", - "number": "Введите числовое значение" - }, - "auth": { - "unauthorized": "Авторизация не выполнена, войдите снова", - "forbidden": "Недостаточно прав для выполнения операции", - "tokenExpired": "Сессия истекла, пожалуйста, войдите заново", - "invalidCredentials": "Неверное имя пользователя или пароль" - }, - "file": { - "uploadFailed": "Загрузка файла не удалась", - "invalidFormat": "Неподдерживаемый формат файла", - "tooLarge": "Файл слишком большой", - "notFound": "Файл не найден" - }, - "operation": { - "failed": "Операция не удалась", - "cancelled": "Операция отменена", - "notSupported": "Действие не поддерживается", - "conflict": "Конфликт операций, попробуйте позже" - }, - "browser": { - "audioNotSupported": "Ваш браузер не поддерживает воспроизведение аудио." - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/messages/success.json b/legacy-dashboard/src/i18n/locales/ru-RU/messages/success.json deleted file mode 100644 index 1501d5cb..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/messages/success.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "operation": { - "saved": "Сохранено", - "created": "Создано", - "updated": "Обновлено успешно", - "deleted": "Удалено", - "uploaded": "Загружено", - "downloaded": "Скачано", - "imported": "Импорт завершен", - "exported": "Экспорт завершен", - "copied": "Скопировано в буфер", - "sent": "Отправлено" - }, - "connection": { - "connected": "Подключено", - "authenticated": "Вход выполнен", - "synchronized": "Синхронизация завершена" - }, - "validation": { - "valid": "Проверка пройдена", - "completed": "Готово" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/ru-RU/messages/validation.json b/legacy-dashboard/src/i18n/locales/ru-RU/messages/validation.json deleted file mode 100644 index 41f1de81..00000000 --- a/legacy-dashboard/src/i18n/locales/ru-RU/messages/validation.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "required": "Это поле обязательно", - "email": "Введите корректный email", - "url": "Введите корректный URL", - "number": "Введите число", - "min": "Минимальное значение: {min}", - "max": "Максимальное значение: {max}", - "minLength": "Минимум {length} симв.", - "maxLength": "Максимум {length} симв.", - "pattern": "Неверный формат", - "unique": "Такое значение уже существует", - "confirm": "Значения не совпадают", - "fileSize": "Размер файла не должен превышать {size}MB", - "fileType": "Неподдерживаемый тип файла", - "required_field": "Заполните обязательные поля", - "invalid_format": "Некорректный формат", - "password_too_short": "Пароль должен быть не менее 8 символов", - "password_too_weak": "Пароль слишком слабый", - "invalid_phone": "Некорректный номер телефона", - "invalid_date": "Некорректная дата", - "date_range": "Неверный диапазон дат", - "upload_failed": "Загрузка не удалась", - "network_error": "Ошибка сети, попробуйте снова", - "operation_cannot_be_undone": "⚠️ Это действие нельзя отменить, будьте осторожны!" -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/core/actions.json b/legacy-dashboard/src/i18n/locales/zh-CN/core/actions.json deleted file mode 100644 index a0a8db1b..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/core/actions.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "create": "创建", - "read": "读取", - "update": "更新", - "delete": "删除", - "search": "搜索", - "filter": "筛选", - "sort": "排序", - "export": "导出", - "import": "导入", - "backup": "备份", - "restore": "恢复", - "copy": "复制", - "paste": "粘贴", - "cut": "剪切", - "undo": "撤销", - "redo": "重做", - "refresh": "刷新", - "submit": "提交", - "reset": "重置", - "clear": "清空", - "save": "保存", - "close": "关闭" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/core/common.json b/legacy-dashboard/src/i18n/locales/zh-CN/core/common.json deleted file mode 100644 index b3e7dc52..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/core/common.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "save": "保存", - "cancel": "取消", - "close": "关闭", - "copy": "复制", - "copied": "已复制", - "copyFailed": "复制失败", - "delete": "删除", - "edit": "编辑", - "add": "添加", - "confirm": "确认", - "loading": "加载中...", - "success": "成功", - "error": "错误", - "warning": "警告", - "info": "信息", - "name": "名称", - "description": "描述", - "author": "作者", - "status": "状态", - "actions": "操作", - "enable": "启用", - "disable": "禁用", - "enabled": "已启用", - "disabled": "已禁用", - "reload": "重载", - "configure": "配置", - "install": "安装", - "uninstall": "卸载", - "update": "更新", - "language": "语言", - "settings": "设置", - "locale": "zh-CN", - "type": "输入", - "press": "按", - "longPress": "长按", - "yes": "是", - "no": "否", - "imagePreview": "图片预览", - "autoDetect": "自动检测", - "dialog": { - "confirmTitle": "确认操作", - "confirmMessage": "你确定要执行此操作吗?", - "confirmButton": "确定", - "cancelButton": "取消" - }, - "restart": { - "waiting": "正在等待 AstrBot 重启...", - "maxRetriesReached": "拉取状态达到最大次数,请手动检查。" - }, - "upgradeRecovery": { - "title": "检测到升级未完成", - "description": "当前 WebUI 已更新到 {dashboardVersion},但 AstrBot Core 仍是 {coreVersion}。这通常是升级过程中刷新页面导致重启流程被打断。如果重启后仍未解决,请尝试删除 AstrBot 运行时目录下的 data/dist 文件夹后重启 AstrBot。", - "hint": "请重启后端以完成升级,重启完成后页面会自动刷新。", - "restartButton": "立即重启后端", - "laterButton": "稍后处理", - "restarting": "正在请求后端重启...", - "waiting": "已发起重启,正在等待后端恢复...", - "failed": "重启请求失败,请手动重启 AstrBot。" - }, - "readme": { - "title": "插件说明文档", - "buttons": { - "viewOnGithub": "在GitHub中查看仓库", - "refresh": "刷新文档" - }, - "loading": "正在加载README文档...", - "errors": { - "fetchFailed": "获取README失败", - "fetchError": "获取README时发生错误" - }, - "empty": { - "title": "该插件未提供文档链接或GitHub仓库地址。", - "subtitle": "请查看插件市场或联系插件作者获取更多信息。" - } - }, - "changelog": { - "title": "更新日志", - "loading": "正在加载更新日志...", - "empty": { - "title": "该插件未提供更新日志", - "subtitle": "开发者可在插件目录下添加 CHANGELOG.md 文件来提供更新日志" - } - }, - "editor": { - "fullscreen": "全屏编辑", - "editingTitle": "编辑内容" - }, - "templateList": { - "addEntry": "添加条目", - "empty": "暂无条目,请选择模板添加", - "missingTemplate": "找不到对应模板,请删除后重新添加。", - "unknownTemplate": "未指定模板" - }, - "list": { - "addItemPlaceholder": "添加新项,按回车确认添加", - "addButton": "添加", - "addMore": "添加更多", - "batchImport": "批量导入", - "batchImportTitle": "批量导入", - "batchImportLabel": "每行一个项目", - "batchImportPlaceholder": "例如:\n项目1\n项目2\n项目3\n项目4", - "batchImportHint": "每行将作为一个单独的项目,空行会被自动忽略", - "batchImportButton": "导入 {count} 项", - "noItems": "暂无项目", - "noItemsHint": "暂无项目,在上方输入框输入后按回车添加", - "inputPlaceholder": "输入后按回车添加", - "editTitle": "修改列表项", - "modifyButton": "修改" - }, - "itemCard": { - "enabled": "已启用", - "disabled": "已禁用", - "delete": "删除", - "edit": "编辑", - "copy": "复制", - "noData": "暂无数据" - }, - "objectEditor": { - "dialogTitle": "修改键值对", - "noItems": "暂无项目", - "noParams": "暂无参数", - "presets": "预设", - "newKeyLabel": "新键名", - "valueTypeLabel": "值类型", - "keyExists": "键名已存在", - "invalidJson": "JSON 格式错误", - "placeholders": { - "keyName": "键名", - "stringValue": "字符串值", - "numberValue": "数值", - "jsonValue": "JSON" - } - }, - "firstNotice": { - "title": "首次提示", - "loading": "正在加载首次提示...", - "empty": { - "title": "暂无首次提示内容", - "subtitle": "未找到 FIRST_NOTICE.md 或文件为空。" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/core/header.json b/legacy-dashboard/src/i18n/locales/zh-CN/core/header.json deleted file mode 100644 index 96c81ab6..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/core/header.json +++ /dev/null @@ -1,134 +0,0 @@ -{ - "logoTitle": "AstrBot WebUI", - "version": { - "hasNewVersion": "AstrBot 有新版本!", - "dashboardHasNewVersion": "WebUI 有新版本!" - }, - "buttons": { - "update": "更新", - "account": "账户", - "theme": { - "title": "主题", - "light": "浅色模式", - "dark": "深色模式", - "system": "跟随系统" - } - }, - "updateDialog": { - "title": "更新 AstrBot", - "currentVersion": "当前版本", - "status": { - "checking": "正在检查更新...", - "switching": "正在切换版本...", - "updating": "正在更新..." - }, - "tabs": { - "release": "😊 正式版" - }, - "advancedSettings": "高级设置", - "releases": "版本列表", - "updateToLatest": "更新到最新版本", - "showPreReleases": "显示预发布版本", - "preRelease": "预发布", - "preReleaseWarning": { - "title": "预发布版本提醒", - "description": "标有预发布标签的版本可能存在未知问题或 Bug,不建议在生产环境使用。如发现问题,请提交至 ", - "issueLink": "GitHub Issues" - }, - "tip": "💡 TIP: ", - "tipContinue": "默认在切换版本时会下载对应版本的 WebUI 文件。WebUI 代码位于项目的 dashboard 目录,您可使用 npm 自行构建。", - "dockerTip": "切换版本时,会同时尝试更新机器人主程序和管理面板。如果您正在使用 Docker 部署,也可以重新拉取镜像或者使用", - "dockerTipLink": "watchtower", - "dockerTipContinue": "来自动监控拉取。", - "table": { - "tag": "标签", - "publishDate": "发布时间", - "content": "内容", - "sourceUrl": "源码地址", - "actions": "操作", - "view": "查看", - "switch": "切换" - }, - "releaseNotes": { - "title": "更新日志", - "latestLabel": "最新版本" - }, - "progress": { - "target": "目标版本:", - "preparing": "正在准备更新...", - "dashboard": "下载 WebUI", - "core": "下载项目代码", - "dependencies": "正在更新依赖...", - "restart": "正在准备重启...", - "restarting": "正在重启 AstrBot", - "completed": "更新完成", - "successReady": "更新成功", - "autoReloadIn": "{seconds} 秒后自动刷新", - "reloadNow": "立即刷新", - "failed": "更新失败" - }, - "redirectConfirm": { - "title": "即将跳转", - "message": "将跳转到 GitHub Releases 页面,是否继续?", - "latestLabel": "最新版本", - "targetVersion": "目标版本:", - "currentVersion": "当前版本:", - "guideTitle": "跳转后建议:", - "guideStep1": "根据你的系统架构下载对应安装包。", - "guideStep2": "完成安装后重启 AstrBot。", - "guideStep3": "如果你使用 Docker,请优先使用镜像更新方式。" - }, - "desktopApp": { - "title": "更新桌面应用", - "message": "将检查并升级 AstrBot 桌面端程序。", - "currentVersion": "当前版本:", - "latestVersion": "最新版本:", - "checking": "正在检查桌面应用更新...", - "hasNewVersion": "发现新版本,可点击确认升级。", - "isLatest": "已经是最新版本", - "installing": "正在下载并安装更新,完成后将自动重启应用...", - "checkFailed": "检查更新失败,请稍后重试。", - "installFailed": "升级失败,请稍后重试。" - }, - "dashboardUpdate": { - "title": "单独更新管理面板到最新版本", - "currentVersion": "当前版本", - "hasNewVersion": "有新版本!", - "isLatest": "已经是最新版本了。", - "fallback": "完整更新异常时,可仅重新下载 WebUI 作为兜底。", - "downloadAndUpdate": "下载并更新" - } - }, - "accountDialog": { - "title": "修改账户", - "securityWarning": "安全提醒: 请修改密码以确保账户安全", - "securityWarningMd5": "新的 AstrBot 版本强化了安全策略,请重新修改密码", - "securityWarningUpgrade": "检测到旧版本密码存储方式,请修改密码以完成安全升级", - "form": { - "currentPassword": "当前密码", - "newPassword": "新密码", - "confirmPassword": "确认新密码", - "newUsername": "新用户名 (可选)", - "passwordHint": "长度至少 8 位,且包含大写字母、小写字母和数字", - "confirmPasswordHint": "请再次输入新密码以确认", - "usernameHint": "留空表示不修改用户名", - "defaultCredentials": "默认用户名为 astrbot,初始密码会在首次启动时自动生成并显示在控制台中。" - }, - "validation": { - "passwordRequired": "请输入密码", - "passwordMinLength": "密码长度至少 8 位", - "passwordUppercase": "密码必须包含至少一个大写字母", - "passwordLowercase": "密码必须包含至少一个小写字母", - "passwordDigit": "密码必须包含至少一个数字", - "passwordMatch": "两次输入的密码不一致", - "usernameMinLength": "用户名长度至少3位" - }, - "actions": { - "save": "保存修改", - "cancel": "取消" - }, - "messages": { - "updateFailed": "修改失败,请重试" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/core/navigation.json b/legacy-dashboard/src/i18n/locales/zh-CN/core/navigation.json deleted file mode 100644 index 163ab351..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/core/navigation.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "welcome": "欢迎", - "dashboard": "数据统计", - "platforms": "机器人", - "providers": "模型提供商", - "commands": "指令管理", - "persona": "人格设定", - "subagent": "子代理编排", - "toolUse": "MCP", - "extension": "插件", - "extensionTabs": { - "installed": "AstrBot 插件", - "market": "插件市场", - "mcp": "MCP", - "skills": "Skills", - "components": "管理行为" - }, - "config": "配置文件", - "chat": "聊天", - "cron": "未来任务", - "conversation": "对话数据", - "sessionManagement": "自定义规则", - "console": "平台日志", - "trace": "追踪", - "alkaid": "Alkaid", - "knowledgeBase": "知识库", - "about": "关于", - "settings": "设置", - "changelog": "更新日志", - "documentation": "官方文档", - "faq": "FAQ", - "github": "GitHub", - "drag": "拖拽", - "groups": { - "more": "更多功能" - }, - "changelogDialog": { - "title": "更新日志", - "loading": "加载中...", - "error": "加载失败", - "notFound": "未找到该版本的更新日志", - "selectVersion": "选择版本", - "current": "当前" - }, - "configTabs": { - "normal": "普通配置", - "system": "系统配置" - }, - "pluginWebui": "插件页面" -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/core/shared.json b/legacy-dashboard/src/i18n/locales/zh-CN/core/shared.json deleted file mode 100644 index dfc3a8d4..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/core/shared.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "knowledgeBaseSelector": { - "notSelected": "未选择", - "buttonText": "选择知识库...", - "dialogTitle": "选择知识库", - "loading": "加载中...", - "noKnowledgeBases": "暂无知识库", - "createKnowledgeBase": "创建知识库", - "selectedCount": "已选择 {count} 个知识库", - "confirmSelection": "确认选择", - "cancelSelection": "取消", - "noDescription": "无描述", - "documentCount": "{count} 个文档", - "chunkCount": "{count} 个块" - }, - "pluginSetSelector": { - "notSelected": "未启用任何插件", - "allPlugins": "启用所有插件 (*)", - "selectedCount": "已选择 {count} 个插件", - "buttonText": "选择插件集合...", - "dialogTitle": "选择插件集合", - "loading": "加载中...", - "enableAll": "启用所有插件", - "enableNone": "不启用任何插件", - "customSelect": "自定义选择", - "noPlugins": "暂无可用的插件", - "confirmSelection": "确认选择", - "cancelSelection": "取消", - "noDescription": "无描述", - "notActivated": "未激活", - "note": "*不显示系统插件和已经在插件页禁用的插件。", - "selectedPluginsLabel": "已选择的插件:", - "allPluginsLabel": "所有插件" - }, - "providerSelector": { - "notSelected": "未选择", - "buttonText": "选择提供商...", - "dialogTitle": "选择提供商", - "loading": "加载中...", - "noProviders": "暂无可用的提供商", - "confirmSelection": "确认选择", - "cancelSelection": "取消", - "clearSelection": "不选择", - "clearSelectionSubtitle": "清除当前选择", - "unknownType": "未知类型", - "createProvider": "创建提供商", - "manageProviders": "提供商管理", - "selectProviderPool": "选择提供商池...", - "selectedCount": "已选择 {count} 个提供商" - }, - "personaSelector": { - "notSelected": "未选择", - "defaultPersona": "默认人格", - "buttonText": "选择人格...", - "dialogTitle": "选择人格", - "noDescription": "无描述", - "noPersonas": "暂无可用的人格", - "createPersona": "创建新人格", - "cancelSelection": "取消", - "confirmSelection": "确认选择", - "selectPersonaPool": "选择人格池...", - "rootFolder": "全部人格", - "emptyFolder": "此文件夹为空" - }, - "personaQuickPreview": { - "title": "快速预览", - "loading": "加载中...", - "noPersonaSelected": "未选择人格", - "personaNotFound": "未找到该人格的详情", - "systemPromptLabel": "系统提示词", - "toolsLabel": "工具", - "skillsLabel": "技能(Skills)", - "originLabel": "来源", - "originNameLabel": "来源名称", - "toolInactive": "已禁用", - "toolInactiveTooltip": "该工具已被禁用。在插件->管理行为->函数工具中重新启用。", - "allTools": "全部工具可用", - "allToolsWithCount": "全部工具可用({count})", - "noTools": "未配置工具", - "allSkills": "全部 Skills 可用", - "allSkillsWithCount": "全部 Skills 可用({count})", - "noSkills": "未配置 Skills" - }, - "t2iTemplateEditor": { - "buttonText": "自定义 T2I 模板", - "dialogTitle": "自定义文转图 HTML 模板", - "newTemplateNameLabel": "输入新模板名称", - "nameRequired": "名称不能为空", - "selectTemplateLabel": "选择模板", - "applied": "已应用", - "apply": "应用", - "templateEditor": "模板编辑器", - "new": "新建", - "resetBase": "重置Base", - "delete": "删除", - "save": "保存", - "livePreview": "实时预览(可能有差异)", - "refreshPreview": "刷新预览", - "previewText": "这是一个示例文本,用于预览模板效果。\n\n这里可以包含多行文本,支持换行和各种格式。", - "syntaxHint": "支持 jinja2 语法。可用变量:text | safe(要渲染的文本), version(AstrBot 版本)。如果预览测试内容未加载或显示异常,请点击“重置Base”恢复默认模板。", - "saveAndApply": "保存应用当前编辑模板", - "confirmReset": "确认重置", - "confirmResetMessage": "确定要将 'base' 模板恢复为默认内容吗?当前编辑器中的任何未保存更改将丢失。此操作无法撤销。", - "confirmResetButton": "确认重置", - "confirmDelete": "确认删除", - "confirmDeleteMessage": "确定要删除模板 '{name}' 吗?此操作无法撤销。", - "confirmDeleteButton": "确认删除", - "confirmAction": "确认操作", - "confirmApplyMessage": "确定要保存对 '{name}' 的修改,并将其设为新的活动模板吗?" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/core/status.json b/legacy-dashboard/src/i18n/locales/zh-CN/core/status.json deleted file mode 100644 index 233d8f12..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/core/status.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "loading": "加载中", - "success": "成功", - "error": "错误", - "warning": "警告", - "info": "信息", - "pending": "等待中", - "processing": "处理中", - "completed": "已完成", - "failed": "失败", - "cancelled": "已取消", - "timeout": "超时", - "connecting": "连接中", - "connected": "已连接", - "disconnected": "已断开", - "online": "在线", - "offline": "离线", - "active": "活跃", - "inactive": "非活跃", - "ready": "就绪", - "busy": "忙碌" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/about.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/about.json deleted file mode 100644 index 85f7956a..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/about.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "hero": { - "title": "AstrBot", - "subtitle": "A project out of interests and loves ❤️", - "starButton": "Star 这个项目! 🌟", - "issueButton": "提交 Issue" - }, - "contributors": { - "title": "贡献者", - "description": "本项目由众多开源社区成员共同维护。感谢每一位贡献者的付出!", - "viewLink": "查看 AstrBot 贡献者" - }, - "stats": { - "title": "全球部署", - "license": "AstrBot 采用 AGPL v3 协议开源" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/index.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/index.json deleted file mode 100644 index 98e1a606..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/index.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "title": "Alkaid实验室", - "subtitle": "探索前沿AI功能", - "comingSoon": "前面的世界,以后再来探索吧!", - "page": { - "title": "The Alkaid Project.", - "subtitle": "AstrBot Alpha 项目", - "navigation": { - "knowledgeBase": "知识库 (插件)", - "longTermMemory": "长期记忆层", - "other": "..." - } - }, - "features": { - "knowledgeBase": "知识库", - "longTermMemory": "长期记忆", - "advancedChat": "高级对话", - "multiModal": "多模态交互" - }, - "status": { - "experimental": "实验性", - "beta": "测试版", - "stable": "稳定版", - "deprecated": "已弃用" - }, - "sigma": { - "subtitle": "AstrBot 实验性项目", - "visualization": "可视化", - "filterUserId": "筛选用户 ID", - "filter": "筛选", - "resetFilter": "重置筛选", - "refreshGraph": "刷新图形", - "nodeDetails": "节点详情", - "id": "ID", - "type": "类型", - "name": "名称", - "userId": "用户ID", - "timestamp": "时间戳", - "graphStats": "图形统计", - "nodeCount": "节点数", - "edgeCount": "边数", - "inDevelopment": "功能开发中" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/knowledge-base.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/knowledge-base.json deleted file mode 100644 index d4c7ea83..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/knowledge-base.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "title": "知识库", - "subtitle": "管理和查询知识库内容", - "documents": { - "title": "文档列表", - "name": "文档名称", - "size": "大小", - "uploadTime": "上传时间", - "status": "状态", - "actions": "操作" - }, - "management": { - "delete": "删除", - "preview": "预览", - "download": "下载", - "reindex": "重新索引" - }, - "notInstalled": { - "title": "还没有安装知识库插件", - "install": "立即安装" - }, - "empty": { - "title": "还没有知识库,快创建一个吧!🙂", - "create": "创建知识库" - }, - "list": { - "title": "知识库列表", - "create": "创建知识库", - "config": "配置", - "checkUpdate": "检查插件更新", - "updatePlugin": "更新插件到 {version}", - "knowledgeCount": "条知识", - "tips": "Tips: 在聊天页面通过 /kb 指令了解如何使用!" - }, - "createDialog": { - "title": "创建新知识库", - "nameLabel": "知识库名称", - "descriptionLabel": "描述", - "descriptionPlaceholder": "知识库的简短描述...", - "embeddingModelLabel": "嵌入模型(Embedding Model)", - "rerankModelLabel": "重排序模型(Rerank Model)", - "providerInfo": "提供商 ID: {id} | 嵌入模型维度: {dimensions}", - "rerankProviderInfo": "提供商 ID: {id}", - "tips": "Tips: 一旦选择了一个知识库的嵌入模型,请不要再修改该提供商的模型或者向量维度信息,否则将严重影响该知识库的召回率甚至报错。", - "cancel": "取消", - "create": "创建" - }, - "emojiPicker": { - "title": "选择表情", - "close": "关闭", - "categories": { - "emotions": "笑脸和情感", - "animals": "动物和自然", - "food": "食物和饮料", - "activities": "活动和物品", - "travel": "旅行和地点", - "symbols": "符号和旗帜" - } - }, - "contentDialog": { - "title": "知识库管理", - "embeddingModel": "嵌入模型", - "vectorDimension": "向量维度", - "usage": "使用方式: 在聊天页中输入 \"/kb use {name}\"", - "tabs": { - "upload": "上传文件", - "search": "搜索内容", - "fromURL": "从URL导入" - } - }, - "upload": { - "title": "上传文件到知识库", - "subtitle": "支持 txt、pdf、epub、word、excel 等多种格式", - "dropzone": "拖放文件到这里或点击上传", - "chunkSettings": { - "title": "分片设置", - "tooltip": "分片长度决定每块文本的大小,重叠长度决定相邻文本块之间的重叠程度。\n较小的分片更精确但会增加数量,适当的重叠可提高检索准确性。", - "chunkSizeLabel": "分片长度", - "chunkSizeHint": "控制每个文本块大小,留空使用默认值", - "overlapLabel": "重叠长度", - "overlapHint": "控制相邻文本块重叠度,留空使用默认值" - }, - "upload": "上传文件", - "uploading": "正在上传..." - }, - "search": { - "queryLabel": "搜索知识库内容", - "queryPlaceholder": "输入关键词搜索知识库内容...", - "resultCountLabel": "结果数量", - "searching": "正在搜索...", - "resultsTitle": "搜索结果", - "relevance": "相关度", - "noResults": "没有找到匹配的内容" - }, - "deleteDialog": { - "title": "确认删除", - "confirmText": "您确定要删除知识库 {name} 吗?", - "warning": "此操作不可逆,所有知识库内容将被永久删除。", - "cancel": "取消", - "delete": "删除" - }, - "messages": { - "pluginNotAvailable": "插件未安装或不可用", - "pluginNotActivated": "astrbot_plugin_knowledge_base 插件未启用,请前往插件管理页面启用,然后重启 AstrBot。", - "checkPluginFailed": "检查插件失败", - "installFailed": "安装失败", - "installPluginFailed": "安装插件失败", - "getKnowledgeBaseListFailed": "获取知识库列表失败", - "knowledgeBaseCreated": "知识库创建成功", - "createFailed": "创建失败", - "createKnowledgeBaseFailed": "创建知识库失败", - "pleaseEnterKnowledgeBaseName": "请输入知识库名称", - "pleaseSelectFile": "请先选择文件", - "operationSuccess": "操作成功: {message}", - "uploadFailed": "上传失败", - "fileUploadFailed": "文件上传失败", - "pleaseEnterSearchContent": "请输入搜索内容", - "noMatchingContent": "没有找到匹配的内容", - "searchFailed": "搜索失败", - "searchKnowledgeBaseFailed": "搜索知识库失败", - "deleteTargetNotExists": "删除目标不存在", - "knowledgeBaseDeleted": "知识库删除成功", - "deleteFailed": "删除失败", - "deleteKnowledgeBaseFailed": "删除知识库失败", - "getEmbeddingModelListFailed": "获取嵌入模型列表失败", - "updateAvailable": "发现新版本: {current} -> {latest}", - "pluginUpToDate": "插件已是最新版本", - "pluginNotFoundInMarket": "在插件市场中未找到该插件", - "checkUpdateFailed": "检查更新失败", - "updateSuccess": "插件更新成功", - "updateFailed": "更新失败", - "updatePluginFailed": "更新插件失败" - }, - "importFromUrl": { - "title": "从 URL 导入", - "urlLabel": "网页 URL", - "urlPlaceholder": "请输入要提取知识的网页地址", - "optionsTitle": "导入选项", - "tooltip": "这些选项控制如何从URL内容中提取和处理文本。\n留空将使用插件的默认设置。\n启用LLM文本修复和摘要后可能花费时间较长。", - "useLlmRepairLabel": "启用LLM文本修复", - "useClusteringSummaryLabel": "启用聚类摘要", - "repairLlmProviderIdLabel": "文本修复模型", - "summarizeLlmProviderIdLabel": "摘要模型", - "embeddingProviderIdLabel": "嵌入模型", - "chunkSizeLabel": "分片长度", - "chunkOverlapLabel": "重叠长度", - "startImport": "开始导入", - "importing": "正在导入...", - "importSuccess": "导入成功", - "importFailed": "导入失败", - "uploadingChunks": "内容提取成功,正在上传分片...", - "preRequisite": "提示:请先前往插件市场安装 astrbot_plugin_url_2_knowledge_base 并根据插件文档内的指示完成 playwright 安装后才可使用本功能", - "allChunksUploaded": "所有分片上传成功" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/memory.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/memory.json deleted file mode 100644 index 0520d9da..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/alkaid/memory.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "title": "长期记忆", - "subtitle": "AI助手的长期记忆管理", - "memories": { - "title": "记忆列表", - "content": "记忆内容", - "importance": "重要程度", - "createTime": "创建时间", - "lastAccess": "最后访问", - "category": "分类" - }, - "categories": { - "personal": "个人信息", - "preferences": "偏好设置", - "conversations": "对话历史", - "facts": "事实信息", - "skills": "技能知识" - }, - "importance": { - "high": "高", - "medium": "中", - "low": "低" - }, - "actions": { - "view": "查看详情", - "edit": "编辑", - "delete": "删除", - "pin": "置顶", - "unpin": "取消置顶" - }, - "filters": { - "all": "全部", - "category": "按分类", - "importance": "按重要程度", - "dateRange": "按时间范围", - "title": "筛选", - "userIdLabel": "筛选用户 ID", - "filterButton": "筛选", - "resetButton": "重置筛选", - "refreshButton": "刷新图形" - }, - "search": { - "title": "搜索记忆", - "userIdLabel": "用户 ID", - "queryLabel": "输入关键词", - "searchButton": "搜索", - "resultsTitle": "搜索结果", - "noResults": "未找到相关记忆内容", - "similarity": "相关度", - "noTextContent": "无文本内容" - }, - "addMemory": { - "title": "添加记忆数据", - "textLabel": "输入文本内容", - "userIdLabel": "用户 ID", - "summarizeLabel": "需要摘要", - "addButton": "添加数据" - }, - "nodeDetails": { - "title": "节点详情", - "id": "ID", - "type": "类型", - "name": "名称", - "userId": "用户ID", - "timestamp": "时间戳" - }, - "graphStats": { - "title": "图形统计", - "nodeCount": "节点数", - "edgeCount": "边数" - }, - "factDialog": { - "title": "记忆事实", - "id": "ID", - "docId": "文档ID", - "createdAt": "创建时间", - "updatedAt": "更新时间", - "metadata": "元数据", - "metadataKey": "键", - "metadataValue": "值", - "loading": "加载中...", - "close": "关闭", - "noValue": "无", - "unknown": "未知" - }, - "messages": { - "searchQueryRequired": "请输入搜索关键词", - "searchSuccess": "找到 {count} 条相关记忆", - "searchNoResults": "未找到相关记忆内容", - "searchError": "搜索失败", - "addSuccess": "记忆数据添加成功!", - "addError": "添加记忆数据失败", - "factDetailsError": "获取记忆详情失败", - "metadataParseError": "无法解析元数据", - "relationNoMemoryData": "该关系没有关联的记忆数据" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/auth.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/auth.json deleted file mode 100644 index 7b683a16..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/auth.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "login": "登录", - "username": "用户名", - "password": "密码", - "defaultHint": "如果这是首次登录,请在日志中查看默认密码。", - "totp": { - "code": "验证码", - "verify": "验证", - "trustDevice": "信任此设备 30 天" - }, - "recovery": { - "title": "恢复码登录", - "subtitle": "无法使用认证器应用时,可通过恢复码登录。", - "code": "恢复码", - "submit": "使用恢复码登录", - "useRecoveryCode": "无法使用 TOTP?", - "backToLogin": "返回登录", - "savedWarning": "若恢复码丢失将无法通过常规途径恢复账户访问权限。", - "continue": "继续", - "acknowledge": "我已保存恢复码", - "totpDisableWarning": "使用恢复码登录将禁用双因素认证。" - }, - "setup": { - "title": "设置账户", - "subtitle": "创建用于管理 AstrBot 的账户", - "username": "新用户名", - "password": "新密码", - "confirmPassword": "确认新密码", - "passwordHint": "长度至少 8 位,并包含大写字母、小写字母和数字。", - "submit": "完成设置", - "totp": { - "code": "验证码", - "qrAlt": "TOTP 二维码", - "title": "完成 TOTP 配置", - "subtitle": "使用认证器应用扫描二维码,以完成双因素认证配置。", - "step2Hint": "使用认证器应用(如 Google Authenticator、Authy)扫描此二维码,然后输入验证码。", - "verify": "验证并完成", - "verifyError": "验证失败,请输入认证器应用中的最新验证码。", - "disableError": "无法关闭 TOTP,请重试。", - "back": "返回" - }, - "validation": { - "usernameRequired": "请输入用户名", - "usernameMinLength": "用户名长度至少 3 位", - "passwordRequired": "请输入密码", - "passwordMinLength": "密码长度至少 8 位", - "passwordUppercase": "密码必须包含至少一个大写字母", - "passwordLowercase": "密码必须包含至少一个小写字母", - "passwordDigit": "密码必须包含至少一个数字", - "confirmPasswordRequired": "请确认密码", - "passwordMatch": "两次输入的密码不一致" - } - }, - "logo": { - "title": "AstrBot WebUI", - "totpTitle": "两步验证", - "subtitle": "欢迎使用" - }, - "versions": { - "webui": "WebUI", - "astrbotRuntime": "AstrBot", - "astrbotCode": "AstrBot(Code)", - "mismatchTooltip": "版本不一致", - "dialogTitle": "版本状态需要确认", - "webuiMismatchTitle": "WebUI 与当前 AstrBot 版本不匹配", - "webuiMismatchMessage": "当前 WebUI 可能不是为此 AstrBot 版本构建,部分页面或接口可能出现异常。请先手动重启 AstrBot;若仍不一致,删除运行目录下的 data/dist 后再次重启,AstrBot 会重新下载匹配的 WebUI。", - "runtimeMismatchTitle": "运行中的 AstrBot 与代码版本不一致", - "runtimeMismatchMessage": "代码已经更新,但当前进程仍在运行旧版本。手动重启 AstrBot 后,将加载新的代码版本。", - "faq": "查看 FAQ", - "close": "关闭" - }, - "theme": { - "light": "浅色模式", - "dark": "深色模式", - "system": "跟随系统", - "switchToDark": "切换到深色主题", - "switchToLight": "切换到浅色主题", - "title": "主题" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/chart.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/chart.json deleted file mode 100644 index eae4ee54..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/chart.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "messageCount": "消息条数", - "time": "时间" -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/chat.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/chat.json deleted file mode 100644 index 58a9264b..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/chat.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "title": "聊天吧!", - "subtitle": "与AI助手进行对话", - "input": { - "placeholder": "想问什么都可以", - "send": "发送", - "clear": "清空", - "upload": "上传文件", - "voice": "语音输入", - "recordingPrompt": "录音中,请说话...", - "chatPrompt": "聊天吧!", - "dropToUpload": "松开鼠标上传文件", - "stopGenerating": "停止生成" - }, - "message": { - "user": "用户", - "assistant": "助手", - "system": "系统", - "error": "错误消息", - "loading": "思考中..." - }, - "voice": { - "start": "开始录音", - "stop": "停止录音", - "recording": "新录音", - "processing": "处理中...", - "error": "录音失败", - "listening": "等待语音...", - "speaking": "正在说话", - "startRecording": "开始语音输入", - "liveMode": "实时对话" - }, - "welcome": { - "title": "今天想聊点什么?", - "subtitle": "您的智能对话助手", - "quickActions": "快速操作", - "examples": "示例问题" - }, - "actions": { - "copy": "复制", - "regenerate": "重新生成", - "retry": "重试", - "retryWithModel": "使用其他模型", - "noAvailableModels": "暂无可用模型", - "like": "点赞", - "dislike": "踩", - "share": "分享", - "newChat": "创建对话", - "deleteChat": "删除此对话", - "editTitle": "编辑标题", - "fullscreen": "全屏模式", - "exitFullscreen": "退出全屏", - "reply": "引用回复", - "providerConfig": "模型配置", - "toolsUsed": "已使用工具", - "toolCallUsed": "已使用 {name} 工具", - "pythonCodeAnalysis": "已使用 Python 代码分析" - }, - "ipython": { - "output": "输出" - }, - "toolStatus": { - "done": "已完成", - "running": "运行中" - }, - "thread": { - "title": "分支", - "askInThread": "在新分支中询问", - "count": "{count} 个分支", - "createFailed": "创建分支失败", - "delete": "删除分支", - "confirmDelete": "确定要删除这个分支吗?此操作无法撤销。", - "placeholder": "继续追问这段内容..." - }, - "conversation": { - "title": "对话", - "newConversation": "新的聊天", - "noHistory": "暂无对话历史", - "systemStatus": "系统状态", - "llmService": "LLM 服务", - "speechToText": "语音转文本", - "editDisplayName": "编辑会话名称", - "displayName": "会话名称", - "displayNameUpdated": "会话名称已更新", - "displayNameUpdateFailed": "更新会话名称失败", - "confirmDelete": "确定要删除“{name}”吗?此操作无法撤销。" - }, - "modes": { - "darkMode": "切换到夜间模式", - "lightMode": "切换到日间模式" - }, - "shortcuts": { - "help": "获取帮助", - "voiceRecord": "录制语音", - "pasteImage": "粘贴图片", - "sendKey": { - "title": "发送快捷键", - "enterToSend": "Enter 发送", - "shiftEnterToSend": "Shift+Enter 发送" - } - }, - "streaming": { - "enabled": "流式响应已开启", - "disabled": "流式响应已关闭", - "on": "流式", - "off": "普通" - }, - "transport": { - "title": "通信传输模式", - "sse": "SSE", - "websocket": "WebSocket" - }, - "config": { - "title": "配置文件" - }, - "reasoning": { - "thinking": "思考过程", - "summarySeparator": ",", - "thinkSummary": "思考了 {count} 次", - "toolSummary": "使用了 {count} 次工具", - "think": "思考", - "toolUsed": "使用工具" - }, - "reply": { - "replyTo": "引用", - "notFound": "无法定位消息" - }, - "project": { - "title": "项目", - "create": "创建项目", - "edit": "编辑项目", - "name": "项目名称", - "emoji": "图标 (Emoji)", - "description": "项目描述(可选)", - "workspace": { - "type": "工作区", - "session": "按会话分开", - "project": "项目共享工作区", - "custom": "自定义路径", - "path": "工作区路径" - }, - "loadingSessions": "加载对话中...", - "noSessions": "该项目暂无对话", - "noProjects": "暂无项目", - "confirmDelete": "确定要删除项目 \"{title}\" 吗?项目中的对话不会被删除。" - }, - "time": { - "today": "今天", - "yesterday": "昨天" - }, - "stats": { - "tokens": "Token", - "inputTokens": "输入(其他)", - "outputTokens": "输出 Token", - "cachedTokens": "输入(缓存)", - "duration": "耗时", - "ttft": "首字时间" - }, - "tokenUsage": { - "tooltip": "{used} / {limit} tokens({percent}%)" - }, - "refs": { - "title": "引用", - "sources": "来源" - }, - "connection": { - "title": "连接状态提醒", - "message": "系统检测到聊天连接需要重新建立。", - "reasons": "这可能是因为:", - "reasonWindowResize": "切换了聊天窗口大小(正常现象)", - "reasonMultipleTabs": "在其他标签页中打开了聊天页面", - "reasonNetworkIssue": "网络连接临时中断", - "notice": "注意:为了确保消息正确接收,系统只允许同时保持一个聊天连接。如果您在多个标签页中使用聊天功能,建议只保留一个页面。", - "understand": "我知道了", - "status": { - "reconnecting": "正在重新连接...", - "reconnected": "聊天连接已重新建立", - "failed": "连接失败,请刷新页面重试" - } - }, - "commandSuggestion": { - "navigate": "选择", - "select": "确认", - "close": "关闭" - }, - "errors": { - "sendMessageFailed": "发送消息失败,请重试", - "createSessionFailed": "创建会话失败,请刷新页面重试" - }, - "batch": { - "selected": "已选择 {count} 个", - "confirmDelete": "确定要删除 {count} 个对话吗?此操作无法撤销。", - "selectAll": "全选", - "deselectAll": "取消全选", - "delete": "删除", - "exit": "退出", - "partialFailure": "{total} 个对话中有 {failed} 个删除失败", - "requestFailed": "删除对话失败,请重试。" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/command.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/command.json deleted file mode 100644 index ccaf3434..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/command.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "title": "指令管理", - "summary": { - "total": "展示的指令数", - "disabled": "已禁用", - "conflicts": "有冲突" - }, - "conflictAlert": { - "title": "检测到指令冲突", - "description": "当前有 {count} 对指令存在冲突,冲突的指令会同时触发多个插件响应,可能导致意外行为。", - "hint": "请点击「重命名」按钮修改冲突指令的名称以解决冲突。" - }, - "table": { - "headers": { - "command": "指令", - "type": "类型", - "plugin": "所属插件", - "description": "描述", - "permission": "权限", - "status": "状态", - "actions": "操作" - } - }, - "type": { - "command": "指令", - "group": "指令组", - "subCommand": "子指令" - }, - "status": { - "enabled": "已启用", - "disabled": "已禁用", - "conflict": "有冲突" - }, - "permission": { - "everyone": "所有人", - "admin": "管理员" - }, - "tooltips": { - "enable": "启用指令", - "disable": "禁用指令", - "rename": "重命名指令", - "viewDetails": "查看详情" - }, - "dialogs": { - "rename": { - "title": "重命名指令", - "newName": "新指令名", - "aliases": "管理别名", - "addAlias": "添加别名", - "cancel": "取消", - "confirm": "确认" - }, - "details": { - "title": "指令详情", - "type": "指令类型", - "handler": "处理函数", - "module": "模块路径", - "originalCommand": "原始指令", - "effectiveCommand": "生效指令", - "parentGroup": "所属指令组", - "subCommands": "子指令列表", - "aliases": "别名", - "permission": "权限要求", - "conflictStatus": "冲突状态" - } - }, - "messages": { - "toggleSuccess": "指令状态已更新", - "toggleFailed": "更新指令状态失败", - "renameSuccess": "指令已重命名", - "renameFailed": "重命名失败", - "loadFailed": "加载指令列表失败", - "updateSuccess": "更新成功", - "updateFailed": "更新失败" - }, - "search": { - "placeholder": "搜索指令..." - }, - "empty": { - "noCommands": "暂无指令", - "noCommandsDesc": "当前筛选条件下没有找到任何指令" - }, - "filters": { - "all": "全部", - "enabled": "已启用", - "disabled": "已禁用", - "conflict": "有冲突", - "byPlugin": "按插件筛选", - "byType": "按类型筛选", - "byPermission": "按权限筛选", - "byStatus": "按状态筛选", - "showSystemPlugins": "显示系统插件指令", - "systemPluginConflictHint": "存在涉及系统插件的冲突,需解决冲突后才能隐藏" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/config-metadata.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/config-metadata.json deleted file mode 100644 index 6ae98838..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/config-metadata.json +++ /dev/null @@ -1,1770 +0,0 @@ -{ - "ai_group": { - "name": "AI 配置", - "agent_runner": { - "description": "Agent 执行方式", - "hint": "选择 AI 对话的执行器,默认为 AstrBot 内置 Agent 执行器,可使用 AstrBot 内的知识库、人格、工具调用功能。如果不打算接入 Dify、Coze、DeerFlow 等第三方 Agent 执行器,不需要修改此节。", - "provider_settings": { - "enable": { - "description": "启用", - "hint": "AI 对话总开关" - }, - "agent_runner_type": { - "description": "执行器", - "labels": [ - "内置 Agent", - "Dify", - "Coze", - "阿里云百炼应用", - "DeerFlow" - ] - }, - "coze_agent_runner_provider_id": { - "description": "Coze Agent 执行器提供商 ID" - }, - "dify_agent_runner_provider_id": { - "description": "Dify Agent 执行器提供商 ID" - }, - "dashscope_agent_runner_provider_id": { - "description": "阿里云百炼应用 Agent 执行器提供商 ID" - }, - "deerflow_agent_runner_provider_id": { - "description": "DeerFlow Agent 执行器提供商 ID" - } - } - }, - "ai": { - "description": "模型", - "hint": "当使用非内置 Agent 执行器时,默认对话模型和默认图片转述模型可能会无效,但某些插件会依赖此配置项来调用 AI 能力。", - "provider_settings": { - "default_provider_id": { - "description": "默认对话模型", - "hint": "留空时使用第一个模型" - }, - "fallback_chat_models": { - "description": "回退对话模型列表", - "hint": "主对话模型请求失败时,按顺序切换到这些对话模型。" - }, - "request_max_retries": { - "description": "请求最大重试次数", - "hint": "单次模型请求遇到可重试错误时的最大尝试次数。" - }, - "default_image_caption_provider_id": { - "description": "默认图片转述模型", - "hint": "留空代表不使用,可用于非多模态模型" - }, - "image_caption_prompt": { - "description": "图片转述提示词" - } - }, - "provider_stt_settings": { - "enable": { - "description": "启用语音转文本", - "hint": "STT 总开关" - }, - "provider_id": { - "description": "默认语音转文本模型", - "hint": "用户也可使用 /provider 指令单独选择会话的 STT 模型。" - } - }, - "provider_tts_settings": { - "enable": { - "description": "启用文本转语音", - "hint": "TTS 总开关" - }, - "provider_id": { - "description": "默认文本转语音模型" - }, - "trigger_probability": { - "description": "TTS 触发概率" - } - } - }, - "persona": { - "description": "人格", - "hint": "赋予 AstrBot 人格。", - "provider_settings": { - "default_personality": { - "description": "默认采用的人格" - } - } - }, - "knowledgebase": { - "description": "知识库", - "hint": "AstrBot 的 “外置大脑”。", - "kb_names": { - "description": "知识库列表", - "hint": "支持多选" - }, - "kb_fusion_top_k": { - "description": "融合检索结果数", - "hint": "多个知识库检索结果融合后的返回结果数量" - }, - "kb_final_top_k": { - "description": "最终返回结果数", - "hint": "从知识库中检索到的结果数量,越大可能获得越多相关信息,但也可能引入噪音。建议根据实际需求调整" - }, - "kb_agentic_mode": { - "description": "Agentic 知识库检索", - "hint": "启用后,知识库检索将作为 LLM Tool,由模型自主决定何时调用知识库进行查询。需要模型支持函数调用能力。" - } - }, - "websearch": { - "description": "网页搜索", - "hint": "让 AstrBot 能够访问互联网,获悉时讯。", - "provider_settings": { - "web_search": { - "description": "启用网页搜索" - }, - "websearch_provider": { - "description": "网页搜索提供商" - }, - "websearch_tavily_key": { - "description": "Tavily API Key", - "hint": "可添加多个 Key 进行轮询。" - }, - "websearch_bocha_key": { - "description": "BoCha API Key", - "hint": "可添加多个 Key 进行轮询。" - }, - "websearch_brave_key": { - "description": "Brave Search API Key", - "hint": "可添加多个 Key 进行轮询。" - }, - "websearch_firecrawl_key": { - "description": "Firecrawl API Key", - "hint": "可添加多个 Key 进行轮询。" - }, - "websearch_baidu_app_builder_key": { - "description": "百度千帆智能云 APP Builder API Key", - "hint": "参考:[https://console.bce.baidu.com/iam/#/iam/apikey/list](https://console.bce.baidu.com/iam/#/iam/apikey/list)" - }, - "web_search_link": { - "description": "显示来源引用" - }, - "websearch_exa_key": { - "description": "Exa API Key", - "hint": "可添加多个 Key 进行轮询。获取 Key: https://dashboard.exa.ai" - } - } - }, - "file_extract": { - "description": "文档解析能力", - "provider_settings": { - "file_extract": { - "enable": { - "description": "启用文档解析能力" - }, - "provider": { - "description": "文档解析提供商" - }, - "moonshotai_api_key": { - "description": "Moonshot AI API Key" - } - } - } - }, - "agent_computer_use": { - "description": "使用电脑能力", - "hint": "让 AstrBot 访问和使用本机环境或者隔离的沙盒环境,以执行更复杂的任务。详见: [电脑使用](https://docs.astrbot.app/use/computer.html)。", - "provider_settings": { - "computer_use_runtime": { - "description": "运行环境", - "hint": "允许 Agent 访问的环境。local 为本机环境,sandbox 为沙箱环境,none 为不允许任何环境。" - }, - "computer_use_require_admin": { - "description": "需要 AstrBot 管理员权限", - "hint": "开启后,需要 AstrBot 管理员权限才能调用使用电脑能力。在平台配置->管理员中可添加管理员。使用 /sid 指令查看管理员 ID。" - }, - "sandbox": { - "booter": { - "description": "沙箱环境驱动器" - }, - "shipyard_neo_endpoint": { - "description": "Shipyard Neo API Endpoint", - "hint": "Shipyard Neo(Bay) 服务的 API 地址,默认 http://127.0.0.1:8114。" - }, - "shipyard_neo_access_token": { - "description": "Shipyard Neo 访问令牌", - "hint": "Bay 的 API Key(sk-bay-...)。留空时自动从 credentials.json 发现。" - }, - "shipyard_neo_profile": { - "description": "Shipyard Neo Profile", - "hint": "Shipyard Neo 沙箱 profile,例如 python-default。留空时自动选择能力更完整的 profile。" - }, - "shipyard_neo_ttl": { - "description": "Shipyard Neo Sandbox 存活时间(秒)", - "hint": "Shipyard Neo 沙箱的生存时间(秒)。" - }, - "cua_image": { - "description": "CUA 镜像", - "hint": "CUA 沙箱镜像/系统类型,默认 linux。可填写 linux、macos、windows、android,具体取决于 CUA SDK 支持。" - }, - "cua_os_type": { - "description": "CUA 操作系统类型", - "hint": "CUA 沙箱操作系统类型,默认 linux。" - }, - "cua_idle_timeout": { - "description": "CUA 空闲超时(秒)", - "hint": "CUA 沙箱空闲超时时间(秒)。大于 0 时,AstrBot 会在会话空闲达到该时长后主动关闭 CUA 沙箱;0 表示禁用。" - }, - "cua_telemetry_enabled": { - "description": "CUA 遥测", - "hint": "是否允许 CUA SDK 发送遥测数据。默认关闭。" - }, - "cua_local": { - "description": "CUA 本地沙箱", - "hint": "是否优先使用 CUA 本地沙箱。默认开启,避免云端沙箱要求 CUA_API_KEY。关闭后可使用 CUA 云端沙箱。" - }, - "cua_api_key": { - "description": "CUA API Key", - "hint": "CUA 云端沙箱 API Key。仅在关闭本地沙箱时需要。也可以通过 CUA_API_KEY 环境变量提供。" - }, - "shipyard_endpoint": { - "description": "Shipyard API Endpoint", - "hint": "Shipyard 服务的 API 访问地址。" - }, - "shipyard_access_token": { - "description": "Shipyard 访问令牌", - "hint": "用于访问 Shipyard 服务的访问令牌。" - }, - "shipyard_ttl": { - "description": "Shipyard Ship 存活时间(秒)", - "hint": "Shipyard 会话的生存时间(秒)。" - }, - "shipyard_max_sessions": { - "description": "Shipyard Ship 会话复用上限", - "hint": "决定了一个实例承载的最大会话数量。" - } - } - } - }, - "proactive_capability": { - "description": "主动型能力", - "hint": "让 AstrBot 能够在某一时刻自动唤醒,帮你完成任务。详见: [主动型 Agent](https://docs.astrbot.app/use/proactive-agent.html)。", - "provider_settings": { - "proactive_capability": { - "add_cron_tools": { - "description": "启用", - "hint": "启用后,将会传递给 Agent 相关工具来实现主动型 Agent。你可以告诉 AstrBot 未来某个时间要做的事情,它将被定时触发然后执行任务,然后将结果发送给你。" - } - } - } - }, - "truncate_and_compress": { - "hint": "AstrBot 如何管理工作记忆。详见: [上下文管理策略](https://docs.astrbot.app/use/context-compress.html)。", - "description": "上下文管理策略", - "provider_settings": { - "max_context_length": { - "description": "压缩前最多保留对话轮数", - "hint": "普通会话历史超过该轮数后,才会按下方策略进行持久化截断或 LLM 压缩;请求发送前也会先按该值约束上下文。-1 表示不按轮数限制。" - }, - "dequeue_context_length": { - "description": "轮次超限时一次丢弃轮数", - "hint": "当超过\"压缩前最多保留对话轮数\"且无法使用 LLM 压缩时,一次丢弃多少轮旧对话;请求期截断也会复用该值。" - }, - "context_limit_reached_strategy": { - "description": "历史超限或上下文接近上限时的处理方式", - "labels": [ - "按对话轮数截断", - "由 LLM 压缩上下文" - ], - "hint": "普通会话历史仅在超过\"压缩前最多保留对话轮数\"后执行该策略;请求发送前也会在上下文 token 接近模型窗口时使用同一策略保护本次请求。" - }, - "llm_compress_instruction": { - "description": "上下文压缩提示词", - "hint": "如果为空则使用默认提示词。" - }, - "llm_compress_keep_recent_ratio": { - "description": "压缩时保留最近上下文比例", - "hint": "按当前上下文 token 数保留最近内容,范围 0-0.3。0.15 表示保留 15%;比例大于 0 时至少保留最后一轮。" - }, - "llm_compress_provider_id": { - "description": "用于上下文压缩的模型提供商 ID", - "hint": "留空时使用当前聊天模型进行压缩;如果模型不可用或压缩失败,将回退为\"按对话轮数截断\"的策略。" - }, - "fallback_max_context_tokens": { - "description": "上下文窗口兜底值", - "hint": "当 max_context_tokens 为 0 且模型不在内置元数据中时,使用此值作为上下文窗口大小。默认 128000。" - } - } - }, - "others": { - "description": "其他配置", - "provider_settings": { - "display_reasoning_text": { - "description": "显示思考内容" - }, - "llm_safety_mode": { - "description": "健康模式", - "hint": "引导模型输出健康、安全、积极的内容,避免有害或敏感话题。" - }, - "safety_mode_strategy": { - "description": "健康模式策略", - "hint": "选择健康模式的实现方式。" - }, - "identifier": { - "description": "用户识别", - "hint": "启用后,会在提示词前包含用户 ID 信息。" - }, - "group_name_display": { - "description": "显示群名称", - "hint": "启用后,在支持的平台(OneBot v11)上会在提示词前包含群名称信息。" - }, - "datetime_system_prompt": { - "description": "现实世界时间感知", - "hint": "启用后,会在系统提示词中附带当前时间信息。" - }, - "show_tool_use_status": { - "description": "输出函数调用状态" - }, - "show_tool_call_result": { - "description": "输出函数调用返回结果", - "hint": "仅在启用“输出函数调用状态”时生效,且最多展示 70 个字符。" - }, - "buffer_intermediate_messages": { - "description": "合并 Agent 中间消息", - "hint": "开启后,非流式模式下多步工具调用过程中产生的中间文本将缓冲,待 Agent 完成后合并为一条回复发送。" - }, - "sanitize_context_by_modalities": { - "description": "按模型能力清理历史上下文", - "hint": "开启后,在每次请求 LLM 前会按当前模型提供商中所选择的模型能力删除对话中不支持的图片/工具调用结构(会改变模型看到的历史)" - }, - "max_quoted_fallback_images": { - "description": "转发消息中图片获取上限", - "hint": "转发消息解析到的图片最多注入数量,超出部分会截断。" - }, - "quoted_message_parser": { - "max_component_chain_depth": { - "description": "转发消息富文本解析深度", - "hint": "解析转发消息中的富文本组件链时允许的最大递归深度。" - }, - "max_forward_node_depth": { - "description": "转发消息嵌套解析深度", - "hint": "解析嵌套转发节点时允许的最大递归深度。" - }, - "max_forward_fetch": { - "description": "转发消息递归拉取上限", - "hint": "递归调用 get_forward_msg 拉取转发内容的最大次数。" - }, - "warn_on_action_failure": { - "description": "转发消息解析失败告警", - "hint": "开启后,get_msg/get_forward_msg 全部尝试失败时输出 warning 日志。" - } - }, - "max_agent_step": { - "description": "工具调用轮数上限" - }, - "tool_call_timeout": { - "description": "工具调用超时时间(秒)" - }, - "tool_schema_mode": { - "description": "工具调用模式", - "hint": "skills-like 先下发工具名称与描述,再下发参数;full 一次性下发完整参数。", - "labels": [ - "Skills-like(两阶段)", - "Full(完整参数)" - ] - }, - "streaming_response": { - "description": "流式输出" - }, - "unsupported_streaming_strategy": { - "description": "不支持流式回复的平台", - "hint": "选择在不支持流式回复的平台上的处理方式。实时分段回复会在系统接收流式响应检测到诸如标点符号等分段点时,立即发送当前已接收的内容", - "labels": [ - "实时分段回复", - "关闭流式回复" - ] - }, - "wake_prefix": { - "description": "LLM 聊天额外唤醒前缀", - "hint": "如果唤醒前缀为 /, 额外聊天唤醒前缀为 chat,则需要 /chat 才会触发 LLM 请求" - }, - "prompt_prefix": { - "description": "用户提示词", - "hint": "可使用 {{prompt}} 作为用户输入的占位符。如果不输入占位符则代表添加在用户输入的前面。" - }, - "image_compress_enabled": { - "description": "启用图片压缩", - "hint": "启用后,发送给多模态模型前会先压缩本地大图片。" - }, - "image_compress_options": { - "description": "图片压缩配置", - "hint": "用于控制图片压缩的尺寸、质量和触发阈值。", - "max_size": { - "description": "最大边长", - "hint": "压缩后图片的最长边,单位为像素。超过该尺寸时会按比例缩放。" - }, - "quality": { - "description": "JPEG 质量", - "hint": "JPEG 输出质量,范围为 1-100。值越高,画质越好,文件也越大。" - } - }, - "reachability_check": { - "description": "提供商可达性检测", - "hint": "/provider 命令列出模型时并发检测连通性。开启后会主动调用模型测试连通性,可能产生额外 token 消耗。" - } - }, - "provider_tts_settings": { - "dual_output": { - "description": "开启 TTS 时同时输出语音和文字内容" - } - } - } - }, - "platform_group": { - "name": "平台配置", - "platform": { - "description": "消息平台适配器", - "active_send_mode": { - "description": "是否换用主动发送接口" - }, - "appid": { - "description": "appid", - "hint": "必填项。当前消息平台的 AppID。如何获取请参考对应平台接入文档。" - }, - "callback_server_host": { - "description": "回调服务器主机", - "hint": "回调服务器主机。留空则不启用回调服务器。" - }, - "card_template_id": { - "description": "卡片模板 ID", - "hint": "可选。钉钉互动卡片模板 ID。启用后将使用互动卡片进行流式回复。" - }, - "discord_activity_name": { - "description": "Discord 活动名称", - "hint": "可选的 Discord 活动名称。留空则不设置活动。" - }, - "discord_allow_bot_messages": { - "description": "允许接收机器人消息", - "hint": "启用后,AstrBot 将接收来自其他 Discord 机器人的消息。适用于机器人间通信场景(如消息转发)。默认关闭。" - }, - "discord_command_register": { - "description": "注册 Discord 指令", - "hint": "启用后,自动将插件指令注册为 Discord 斜杠指令" - }, - "discord_proxy": { - "description": "Discord 代理地址", - "hint": "可选的代理地址:http://ip:port" - }, - "discord_token": { - "description": "Discord Bot Token", - "hint": "在此处填入你的 Discord Bot Token" - }, - "enable": { - "description": "启用", - "hint": "是否启用该适配器。未启用的适配器对应的消息平台将不会接收到消息。" - }, - "enable_group_c2c": { - "description": "启用消息列表单聊", - "hint": "启用后,机器人可以接收到 QQ 消息列表中的私聊消息。你可能需要在 QQ 机器人平台上通过扫描二维码的方式添加机器人为你的好友。详见文档。" - }, - "enable_guild_direct_message": { - "description": "启用频道私聊", - "hint": "启用后,机器人可以接收到频道的私聊消息。" - }, - "id": { - "description": "机器人名称", - "hint": "机器人名称" - }, - "is_sandbox": { - "description": "沙箱模式" - }, - "kf_name": { - "description": "微信客服账号名", - "hint": "如果填写此项,即代表你将使用企业微信客服,而不是企业微信应用。可在 https://kf.weixin.qq.com/kf/frame#/accounts 获取。" - }, - "lark_connection_mode": { - "description": "订阅方式", - "labels": [ - "长连接模式", - "推送至服务器模式" - ] - }, - "lark_encrypt_key": { - "description": "Encrypt Key", - "hint": "用于解密飞书回调数据的加密密钥" - }, - "lark_verification_token": { - "description": "Verification Token", - "hint": "用于验证飞书回调请求的令牌" - }, - "misskey_allow_insecure_downloads": { - "description": "允许不安全下载(禁用 SSL 验证)", - "hint": "当远端服务器存在证书问题导致无法正常下载时,自动禁用 SSL 验证作为回退方案。适用于某些图床的证书配置问题。启用有安全风险,仅在必要时使用。" - }, - "misskey_default_visibility": { - "description": "默认帖子可见性", - "hint": "机器人发帖时的默认可见性设置。public:公开,home:主页时间线,followers:仅关注者。" - }, - "misskey_download_chunk_size": { - "description": "流式下载分块大小(字节)", - "hint": "流式下载和计算 MD5 时使用的每次读取字节数,过小会增加开销,过大会占用内存。" - }, - "misskey_download_timeout": { - "description": "远端下载超时时间(秒)", - "hint": "下载远程文件时的超时时间(秒),用于异步上传回退到本地上传的场景。" - }, - "misskey_enable_chat": { - "description": "启用聊天消息响应", - "hint": "启用后,机器人将会监听和响应私信聊天消息" - }, - "misskey_enable_file_upload": { - "description": "启用文件上传到 Misskey", - "hint": "启用后,适配器会尝试将消息链中的文件上传到 Misskey。URL 文件会先尝试服务器端上传,异步上传失败时会回退到下载后本地上传。" - }, - "misskey_instance_url": { - "description": "Misskey 实例 URL", - "hint": "例如 https://misskey.example,填写 Bot 账号所在的 Misskey 实例地址" - }, - "misskey_local_only": { - "description": "仅限本站(不参与联合)", - "hint": "启用后,机器人发出的帖子将仅在本实例可见,不会联合到其他实例" - }, - "misskey_max_download_bytes": { - "description": "最大允许下载字节数(超出则中止)", - "hint": "如果希望限制下载文件的最大大小以防止 OOM,请填写最大字节数;留空或 null 表示不限制。" - }, - "misskey_token": { - "description": "Misskey Access Token", - "hint": "连接服务设置生成的 API 鉴权访问令牌(Access token)" - }, - "misskey_upload_concurrency": { - "description": "并发上传限制", - "hint": "同时进行的文件上传任务上限(整数,默认 3)。" - }, - "misskey_upload_folder": { - "description": "上传到网盘的目标文件夹 ID", - "hint": "可选:填写 Misskey 网盘中目标文件夹的 ID,上传的文件将放置到该文件夹内。留空则使用账号网盘根目录。" - }, - "port": { - "description": "回调服务器端口", - "hint": "回调服务器端口。留空则不启用回调服务器。" - }, - "satori_api_base_url": { - "description": "Satori API 终结点", - "hint": "Satori API 的基础地址。" - }, - "satori_auto_reconnect": { - "description": "启用自动重连", - "hint": "断开连接时是否自动重新连接 WebSocket。" - }, - "satori_endpoint": { - "description": "Satori WebSocket 终结点", - "hint": "Satori 事件的 WebSocket 端点。" - }, - "satori_heartbeat_interval": { - "description": "Satori 心跳间隔", - "hint": "发送心跳消息的间隔(秒)。" - }, - "satori_reconnect_delay": { - "description": "Satori 重连延迟", - "hint": "尝试重新连接前的延迟时间(秒)。" - }, - "satori_token": { - "description": "Satori 令牌", - "hint": "用于 Satori API 身份验证的令牌。" - }, - "secret": { - "description": "secret", - "hint": "必填项。" - }, - "slack_connection_mode": { - "description": "Slack Connection Mode", - "hint": "The connection mode for Slack. `webhook` uses a webhook server, `socket` uses Slack's Socket Mode." - }, - "slack_webhook_host": { - "description": "Slack Webhook Host", - "hint": "Only valid when Slack connection mode is `webhook`." - }, - "slack_webhook_path": { - "description": "Slack Webhook Path", - "hint": "Only valid when Slack connection mode is `webhook`." - }, - "slack_webhook_port": { - "description": "Slack Webhook Port", - "hint": "Only valid when Slack connection mode is `webhook`." - }, - "telegram_command_auto_refresh": { - "description": "Telegram 命令自动刷新", - "hint": "启用后,AstrBot 将会在运行时自动刷新 Telegram 命令。(单独设置此项无效)" - }, - "telegram_command_register": { - "description": "Telegram 命令注册", - "hint": "启用后,AstrBot 将会自动注册 Telegram 命令。" - }, - "telegram_command_register_interval": { - "description": "Telegram 命令自动刷新间隔", - "hint": "Telegram 命令自动刷新间隔,单位为秒。" - }, - "telegram_polling_restart_delay": { - "description": "Telegram 轮询重启延迟", - "hint": "当轮询意外结束尝试自动重启时的延迟时间,单位为秒。默认为 5s。" - }, - "telegram_token": { - "description": "Bot Token", - "hint": "如果你的网络环境为中国大陆,请在 `其他配置` 处设置代理或更改 api_base。" - }, - "mattermost_url": { - "description": "Mattermost URL", - "hint": "Mattermost 服务地址,例如 https://chat.example.com。" - }, - "mattermost_bot_token": { - "description": "Mattermost Bot Token", - "hint": "在 Mattermost 中创建 Bot 账户后生成的访问令牌。" - }, - "mattermost_reconnect_delay": { - "description": "Mattermost 重连延迟", - "hint": "WebSocket 断开后的重连等待时间,单位为秒。默认 5 秒。" - }, - "type": { - "description": "适配器类型" - }, - "unified_webhook_mode": { - "description": "统一 Webhook 模式", - "hint": "Webhook 模式下使用 AstrBot 统一 Webhook 入口,无需单独开启端口。回调地址为 /api/v1/webhooks/platforms/{webhook_uuid}。" - }, - "webhook_uuid": { - "description": "Webhook UUID", - "hint": "统一 Webhook 模式下的唯一标识符,创建平台时自动生成。" - }, - "wecom_ai_bot_name": { - "description": "企业微信智能机器人的名字", - "hint": "请务必填写正确,否则无法使用一些指令。" - }, - "wecom_ai_bot_connection_mode": { - "description": "企业微信智能机器人连接模式", - "hint": "Webhook 回调模式需要配置 Token/EncodingAESKey;长连接模式需要配置 BotID/Secret。" - }, - "wecomaibot_friend_message_welcome_text": { - "description": "企业微信智能机器人私聊欢迎语", - "hint": "可选。当用户当天进入智能机器人单聊会话,回复欢迎语,如 “💭 思考中...”。留空则不回复。" - }, - "wecomaibot_init_respond_text": { - "description": "企业微信智能机器人初始响应文本", - "hint": "可选。当机器人收到消息时,首先回复的文本内容。留空则不设置。" - }, - "wecomaibot_token": { - "description": "企业微信智能机器人 Token", - "hint": "用于 Webhook 回调模式的身份验证。" - }, - "wecomaibot_encoding_aes_key": { - "description": "企业微信智能机器人 EncodingAESKey", - "hint": "用于 Webhook 回调模式的消息加密解密。" - }, - "wecomaibot_ws_bot_id": { - "description": "长连接 BotID", - "hint": "企业微信智能机器人长连接模式凭证 BotID。" - }, - "wecomaibot_ws_secret": { - "description": "长连接 Secret", - "hint": "企业微信智能机器人长连接模式凭证 Secret。" - }, - "wecomaibot_ws_url": { - "description": "长连接 WebSocket 地址", - "hint": "默认值为 wss://openws.work.weixin.qq.com,一般无需修改。" - }, - "wecomaibot_heartbeat_interval": { - "description": "长连接心跳间隔", - "hint": "长连接模式心跳间隔(秒),建议 30 秒。" - }, - "wpp_active_message_poll": { - "description": "是否启用主动消息轮询", - "hint": "只有当你发现微信消息没有按时同步到 AstrBot 时,才需要启用这个功能,默认不启用。" - }, - "wpp_active_message_poll_interval": { - "description": "主动消息轮询间隔", - "hint": "主动消息轮询间隔,单位为秒,默认 3 秒,最大不要超过 60 秒,否则可能被认为是旧消息。" - }, - "ws_reverse_host": { - "description": "反向 Websocket 主机", - "hint": "AstrBot 将作为服务器端。" - }, - "ws_reverse_port": { - "description": "反向 Websocket 端口" - }, - "ws_reverse_token": { - "description": "反向 Websocket Token", - "hint": "反向 Websocket Token。未设置则不启用 Token 验证。" - }, - "msg_push_webhook_url": { - "description": "企业微信消息推送 Webhook URL", - "hint": "可选。用于主动消息推送,请在企微群->消息推送得到 URL。建议设置此项以带来更好的消息发送体验。" - }, - "only_use_webhook_url_to_send": { - "description": "仅使用 Webhook 发送消息", - "hint": "可选。启用后,企业微信智能机器人的所有回复都改为通过消息推送 Webhook 发送。消息推送 Webhook 支持更多的消息类型(如图片、文件等)。如果不需要打字机效果,强烈建议使用此选项。" - }, - "weixin_oc_base_url": { - "description": "Base URL 地址", - "hint": "默认值: https://ilinkai.weixin.qq.com" - }, - "weixin_oc_bot_type": { - "description": "扫码参数 bot_type", - "hint": "默认值: 3" - }, - "weixin_oc_qr_poll_interval": { - "description": "二维码状态轮询间隔(秒)", - "hint": "每隔多少秒轮询一次二维码状态。" - }, - "weixin_oc_long_poll_timeout_ms": { - "description": "getUpdates 长轮询超时时间(毫秒)", - "hint": "会话消息拉取接口超时参数。" - }, - "weixin_oc_api_timeout_ms": { - "description": "HTTP 请求超时(毫秒)", - "hint": "通用 API 请求超时参数。" - }, - "weixin_oc_token": { - "description": "登录后 token(可留空)", - "hint": "扫码登录成功后会自动写入;高级场景可手动填写。" - }, - "kook_bot_token": { - "description": "机器人 Token", - "type": "string", - "hint": "必填项。从 KOOK 开发者平台获取的机器人 Token" - }, - "kook_reconnect_delay": { - "description": "重连延迟", - "type": "int", - "hint": "重连延迟时间(秒),使用指数退避策略" - }, - "kook_max_reconnect_delay": { - "description": "最大重连延迟", - "type": "int", - "hint": "重连延迟的最大值(秒)" - }, - "kook_max_retry_delay": { - "description": "最大重试延迟", - "type": "int", - "hint": "重试的最大延迟时间(秒)" - }, - "kook_heartbeat_interval": { - "description": "心跳间隔", - "type": "int", - "hint": "心跳检测间隔时间(秒)" - }, - "kook_heartbeat_timeout": { - "description": "心跳超时时间", - "type": "int", - "hint": "心跳检测超时时间(秒)" - }, - "kook_max_heartbeat_failures": { - "description": "最大心跳失败次数", - "type": "int", - "hint": "允许的最大心跳失败次数,超过后断开连接" - }, - "kook_max_consecutive_failures": { - "description": "最大连续失败次数", - "type": "int", - "hint": "允许的最大连续失败次数,超过后停止重试" - } - }, - "general": { - "description": "基本", - "admins_id": { - "description": "管理员 ID" - }, - "platform_settings": { - "unique_session": { - "description": "隔离会话", - "hint": "启用后,群成员的上下文独立。" - }, - "friend_message_needs_wake_prefix": { - "description": "私聊消息需要唤醒词" - }, - "reply_prefix": { - "description": "回复时的文本前缀" - }, - "reply_with_mention": { - "description": "回复时 @ 发送人" - }, - "reply_with_quote": { - "description": "回复时引用发送人消息" - }, - "forward_threshold": { - "description": "转发消息的字数阈值" - }, - "empty_mention_waiting": { - "description": "只 @ 机器人是否触发等待" - } - }, - "wake_prefix": { - "description": "唤醒词" - }, - "disable_builtin_commands": { - "description": "禁用自带指令", - "hint": "禁用所有 AstrBot 自带指令,如 help, sid, new 等" - } - }, - "whitelist": { - "description": "白名单", - "platform_settings": { - "enable_id_white_list": { - "description": "启用白名单", - "hint": "启用后,只有在白名单内的会话会被响应,白名单列表为空时代表不启用白名单(所有 ID 都会被放行)。" - }, - "id_whitelist": { - "description": "白名单 ID 列表", - "hint": "使用 /sid 获取 ID。列表为空时表示该白名单不启用(即所有 ID 都在白名单内)。" - }, - "id_whitelist_log": { - "description": "输出日志", - "hint": "启用后,当一条消息没通过白名单时,会输出 INFO 级别的日志。" - }, - "wl_ignore_admin_on_group": { - "description": "管理员群组消息无视 ID 白名单" - }, - "wl_ignore_admin_on_friend": { - "description": "管理员私聊消息无视 ID 白名单" - } - } - }, - "rate_limit": { - "description": "速率限制", - "platform_settings": { - "rate_limit": { - "time": { - "description": "消息速率限制时间(秒)" - }, - "count": { - "description": "消息速率限制计数" - }, - "strategy": { - "description": "速率限制策略" - } - } - } - }, - "content_safety": { - "description": "内容安全", - "content_safety": { - "also_use_in_response": { - "description": "同时检查模型的响应内容" - }, - "baidu_aip": { - "enable": { - "description": "使用百度内容安全审核", - "hint": "您需要手动安装 baidu-aip 库。" - }, - "app_id": { - "description": "App ID" - }, - "api_key": { - "description": "API Key" - }, - "secret_key": { - "description": "Secret Key" - } - }, - "internal_keywords": { - "enable": { - "description": "关键词检查" - }, - "extra_keywords": { - "description": "额外关键词", - "hint": "额外的屏蔽关键词列表,支持正则表达式。" - } - } - } - }, - "t2i": { - "description": "文本转图像", - "t2i": { - "description": "文本转图像输出" - }, - "t2i_word_threshold": { - "description": "文本转图像字数阈值" - } - }, - "others": { - "description": "其他配置", - "platform_settings": { - "ignore_bot_self_message": { - "description": "是否忽略机器人自身的消息" - }, - "ignore_at_all": { - "description": "是否忽略 @ 全体成员事件" - }, - "no_permission_reply": { - "description": "用户权限不足时是否回复" - } - }, - "platform_specific": { - "lark": { - "pre_ack_emoji": { - "enable": { - "description": "[飞书] 启用预回应表情" - }, - "emojis": { - "description": "表情列表(飞书表情枚举名)", - "hint": "表情枚举名参考:[https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce](https://open.feishu.cn/document/server-docs/im-v1/message-reaction/emojis-introduce)" - } - } - }, - "telegram": { - "pre_ack_emoji": { - "enable": { - "description": "[Telegram] 启用预回应表情" - }, - "emojis": { - "description": "表情列表(Unicode)", - "hint": "Telegram 仅支持固定反应集合,参考:[https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9](https://gist.github.com/Soulter/3f22c8e5f9c7e152e967e8bc28c97fc9)" - } - } - }, - "discord": { - "pre_ack_emoji": { - "enable": { - "description": "[Discord] 启用预回应表情" - }, - "emojis": { - "description": "表情列表(Unicode 或自定义表情名)", - "hint": "填写 Unicode 表情符号,例如:👍、🤔、⏳" - } - } - } - } - } - }, - "plugin_group": { - "name": "插件配置", - "plugin": { - "description": "插件", - "plugin_set": { - "description": "可用插件", - "hint": "默认启用全部未被禁用的插件。若插件在插件页面被禁用,则此处的选择不会生效。" - } - } - }, - "ext_group": { - "name": "扩展功能", - "segmented_reply": { - "description": "分段回复", - "platform_settings": { - "segmented_reply": { - "enable": { - "description": "启用分段回复" - }, - "only_llm_result": { - "description": "仅对 LLM 结果分段" - }, - "interval_method": { - "description": "间隔方法", - "hint": "random 为随机时间,log 为根据消息长度计算,$y=log_(x)$,x为字数,y的单位为秒。" - }, - "interval": { - "description": "随机间隔时间", - "hint": "格式:最小值,最大值(如:1.5,3.5)" - }, - "log_base": { - "description": "对数底数", - "hint": "对数间隔的底数,默认为 2.6。取值范围为 1.0-10.0。" - }, - "words_count_threshold": { - "description": "分段回复字数阈值", - "hint": "分段回复的字数上限。只有字数小于此值的消息才会被分段,超过此值的长消息将直接发送(不分段),默认为 150。" - }, - "split_mode": { - "description": "分段模式", - "hint": "用于分隔一段消息。默认情况下会根据句号、问号等标点符号分隔。如填写 `[。?!]` 将移除所有的句号、问号、感叹号。re.findall(r'', text)", - "labels": [ - "正则表达式", - "分段词列表" - ] - }, - "regex": { - "description": "分段正则表达式", - "hint": "用于按正则规则识别分段点。建议使用能匹配分隔符的表达式。" - }, - "split_words": { - "description": "分段词列表", - "hint": "检测到列表中的任意词时进行分段" - }, - "content_cleanup_rule": { - "description": "内容过滤正则表达式", - "hint": "移除分段后内容中的指定内容。如填写 `[。?!]` 将移除所有的句号、问号、感叹号。" - } - } - } - }, - "ltm": { - "description": "群聊上下文感知(原聊天记忆增强)", - "provider_ltm_settings": { - "group_icl_enable": { - "description": "启用群聊上下文感知" - }, - "group_message_max_cnt": { - "description": "最大消息数量" - }, - "image_caption": { - "description": "自动理解图片", - "hint": "需要设置群聊图片转述模型。" - }, - "image_caption_provider_id": { - "description": "群聊图片转述模型", - "hint": "用于群聊上下文感知的图片理解,与默认图片转述模型分开配置。" - }, - "active_reply": { - "enable": { - "description": "主动回复" - }, - "method": { - "description": "主动回复方法" - }, - "possibility_reply": { - "description": "回复概率", - "hint": "0.0-1.0 之间的数值" - }, - "whitelist": { - "description": "主动回复白名单", - "hint": "为空时不启用白名单过滤。使用 /sid 获取 ID。" - } - } - } - } - }, - "system_group": { - "name": "系统配置", - "system": { - "description": "系统配置", - "t2i_strategy": { - "description": "文本转图像策略", - "hint": "文本转图像策略。`remote` 为使用远程基于 HTML 的渲染服务,`local` 为使用 PIL 本地渲染。当使用 local 时,将 ttf 字体命名为 'font.ttf' 放在 data/ 目录下可自定义字体。" - }, - "t2i_endpoint": { - "description": "文本转图像服务 API 地址", - "hint": "为空时使用 AstrBot API 服务" - }, - "t2i_template": { - "description": "文本转图像自定义模版", - "hint": "启用后可自定义 HTML 模板用于文转图渲染。" - }, - "t2i_active_template": { - "description": "当前应用的文转图渲染模板", - "hint": "此处的值由文转图模板管理页面进行维护。" - }, - "log_level": { - "description": "控制台日志级别", - "hint": "控制台输出日志的级别。" - }, - "log_file_enable": { - "description": "启用文件日志", - "hint": "在控制台输出的同时,将日志写入文件。" - }, - "log_file_path": { - "description": "日志文件路径", - "hint": "相对路径以 data 目录为基准,例如 logs/astrbot.log;支持绝对路径。" - }, - "log_file_max_mb": { - "description": "日志文件大小上限 (MB)", - "hint": "超过大小后自动轮转,默认 20MB。" - }, - "temp_dir_max_size": { - "description": "临时目录大小上限 (MB)", - "hint": "用于限制 data/temp 目录总大小,单位为 MB。系统每 10 分钟检查一次,超限时按文件修改时间从旧到新删除,释放约 30% 当前体积。" - }, - "trace_log_enable": { - "description": "启用 Trace 文件日志", - "hint": "将 Trace 事件写入独立文件(不影响控制台输出)。" - }, - "trace_log_path": { - "description": "Trace 日志文件路径", - "hint": "相对路径以 data 目录为基准,例如 logs/astrbot.trace.log;支持绝对路径。" - }, - "trace_log_max_mb": { - "description": "Trace 日志大小上限 (MB)", - "hint": "超过大小后自动轮转,默认 20MB。" - }, - "pip_install_arg": { - "description": "pip 安装额外参数", - "hint": "安装插件依赖时,会使用 Python 的 pip 工具。这里可以填写额外的参数,如 `--break-system-package` 等。" - }, - "pypi_index_url": { - "description": "PyPI 软件仓库地址", - "hint": "安装 Python 依赖时请求的 PyPI 软件仓库地址。默认为 [https://mirrors.aliyun.com/pypi/simple/](https://mirrors.aliyun.com/pypi/simple/)" - }, - "callback_api_base": { - "description": "对外可达的回调接口地址", - "hint": "外部服务可能会通过 AstrBot 生成的回调链接(如文件下载链接)访问 AstrBot 后端。由于 AstrBot 无法自动判断部署环境中对外可达的主机地址(host),因此需要通过此配置项显式指定外部服务如何访问 AstrBot 的地址。如 [http://localhost:6185](http://localhost:6185),[https://example.com](https://example.com) 等。" - }, - "disable_metrics": { - "description": "禁用匿名使用统计", - "hint": "禁用后,AstrBot 将不再上传匿名使用统计数据。" - }, - "dashboard": { - "trust_proxy_headers": { - "description": "信任代理请求头获取客户端 IP", - "hint": "关闭时忽略 X-Forwarded-For/X-Real-IP,仅使用连接地址。" - }, - "auth_rate_limit": { - "enable": { - "description": "启用登录验证速率限制", - "hint": "关闭后将不对登录、TOTP 等身份验证接口进行速率限制。" - }, - "average_interval": { - "description": "验证端点速率限制平均间隔(秒)", - "hint": "两次身份验证请求之间的最小平均间隔时间。例如设置为 1.0 表示每秒最多处理 1 个请求。" - }, - "max_burst": { - "description": "验证端点速率限制最大突发数", - "hint": "允许的瞬时最大突发请求数。例如设置为 3 表示在短时间内最多连续处理 3 个请求。" - } - }, - "ssl": { - "enable": { - "description": "启用 WebUI HTTPS", - "hint": "启用后,WebUI 将直接使用 HTTPS 提供服务。" - }, - "cert_file": { - "description": "SSL 证书文件路径", - "hint": "证书文件路径(PEM)。支持绝对路径和相对路径(相对于当前工作目录)。" - }, - "key_file": { - "description": "SSL 私钥文件路径", - "hint": "私钥文件路径(PEM)。支持绝对路径和相对路径(相对于当前工作目录)。" - }, - "ca_certs": { - "description": "SSL CA 证书文件路径", - "hint": "可选。用于指定 CA 证书文件路径。" - } - }, - "totp": { - "enable": { - "description": "启用 WebUI TOTP 双因素认证", - "hint": "启用后,登录 WebUI 需要额外输入验证码。" - }, - "manage": "管理", - "configuration": "TOTP", - "statusPending": "需完成设置", - "statusEnabled": "已启用", - "setupRequiredHint": "TOTP 已开启但尚未完成配置,请点击“管理”完成初始化。", - "setupTitle": "设置 TOTP", - "setupSubtitle": "请使用认证器应用扫描二维码,然后输入验证码。", - "setupConfirm": "验证并继续", - "activeSubtitle": "可使用此二维码和密钥添加新的认证器设备。", - "rotateTitle": "更换 TOTP 密钥", - "rotateSubtitle": "生成新密钥并完成验证后,将替换当前密钥。", - "rotate": "更换密钥", - "rotateRecovery": "更换恢复码", - "rotateConfirm": "确认更换", - "rotateCancel": "取消", - "rotateCode": "验证码", - "rotateCodeHint": "输入认证器应用中的验证码以确认新密钥。", - "rotateError": "验证码无效,请重试。", - "recoveryTitle": "恢复码", - "recoverySubtitle": "恢复码仅展示一次,请在继续前妥善保存。", - "recoveryWarning": "若恢复码丢失将无法通过常规途径恢复账户访问权限。", - "recoveryAcknowledge": "我已保存恢复码", - "recoveryClose": "完成", - "disableTitle": "关闭 TOTP", - "disableSubtitle": "输入验证码以确认关闭双因素认证。", - "disableRecoverySubtitle": "输入恢复码以确认关闭双因素认证。", - "disableCode": "验证码", - "disableRecoveryCode": "恢复码", - "disableConfirm": "确认关闭", - "disableCancel": "取消", - "disableError": "验证失败,请重试。", - "disableUseRecovery": "无法使用TOTP?", - "disableUseCode": "使用验证码", - "configSaveTitle": "两步验证", - "configSaveSubtitle": "输入验证码以更改受保护的配置。", - "configSaveRotationHint": "轮换 TOTP 密钥时,轮换前和轮换后的验证码均可用(仅轮换操作中单次允许)。", - "configSaveCode": "验证码", - "configSaveConfirm": "继续", - "configSaveCancel": "取消", - "configSaveError": "验证失败,请重试。" - } - }, - "timezone": { - "description": "时区", - "hint": "时区设置。请填写 IANA 时区名称, 如 Asia/Shanghai, 为空时使用系统默认时区。所有时区请查看: [https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab](https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab)" - }, - "http_proxy": { - "description": "HTTP 代理", - "hint": "启用后,会以添加环境变量的方式设置代理。格式为 `http://ip:port`" - }, - "no_proxy": { - "description": "直连地址列表" - } - } - }, - "provider_group": { - "provider": { - "genie_onnx_model_dir": { - "description": "ONNX Model Directory", - "hint": "The directory path containing the ONNX model files" - }, - "genie_language": { - "description": "Language" - }, - "xai_native_search": { - "description": "启用原生搜索功能", - "hint": "启用后,将通过 xAI 的 Chat Completions 原生 Live Search 进行联网检索(按需计费)。仅对 xAI 提供商生效。" - }, - "rerank_api_base": { - "description": "重排序模型 API Base URL", - "hint": "最终请求路径由 Base URL 和路径后缀拼接而成(默认为 /v1/rerank)。" - }, - "rerank_api_suffix": { - "description": "API URL 路径后缀", - "hint": "追加到 base_url 后的路径后缀,如 /v1/rerank。留空则不追加。" - }, - "rerank_api_key": { - "description": "API Key", - "hint": "如果不需要 API Key, 请留空。" - }, - "rerank_model": { - "description": "重排序模型名称" - }, - "return_documents": { - "description": "是否在排序结果中返回文档原文", - "hint": "默认值false,以减少网络传输开销。" - }, - "instruct": { - "description": "自定义排序任务类型说明", - "hint": "仅在使用 qwen3-rerank 模型时生效。建议使用英文撰写。" - }, - "nvidia_rerank_api_base": { - "description": "API Base URL" - }, - "nvidia_rerank_api_key": { - "description": "API Key" - }, - "nvidia_rerank_model": { - "description": "重排序模型名称", - "hint": "请参照NVIDIA Docs中模型名称填写。" - }, - "nvidia_rerank_model_endpoint": { - "description": "自定义模型端点", - "hint": "自定义URL末尾端点,默认为 /reranking" - }, - "nvidia_rerank_truncate": { - "description": "文本截断策略", - "hint": "当输入文本过长时,是否截断输入以适应模型的最大上下文长度。" - }, - "launch_model_if_not_running": { - "description": "模型未运行时自动启动", - "hint": "如果模型当前未在 Xinference 服务中运行,是否尝试自动启动它。在生产环境中建议关闭。" - }, - "modalities": { - "description": "模型能力", - "hint": "模型支持的模态及能力。", - "labels": [ - "文本", - "图像", - "音频", - "工具使用" - ] - }, - "custom_headers": { - "description": "自定义请求头", - "hint": "此处添加的键值对将被合并到 OpenAI SDK 的 default_headers 中,用于自定义 HTTP 请求头。值必须为字符串。" - }, - "ollama_disable_thinking": { - "description": "关闭思考模式", - "hint": "关闭 Ollama 思考模式。" - }, - "custom_extra_body": { - "description": "自定义请求体参数", - "hint": "用于在请求时添加额外的参数,如 temperature, top_p, max_tokens, reasoning_effort 等。", - "template_schema": { - "temperature": { - "description": "温度参数", - "hint": "控制输出的随机性,范围通常为 0-2。值越高越随机。", - "name": "Temperature" - }, - "top_p": { - "description": "Top-p 采样", - "hint": "核采样参数,范围通常为 0-1。控制模型考虑的概率质量。", - "name": "Top-p" - }, - "max_tokens": { - "description": "最大词元(Tokens)数", - "hint": "生成的最大词元(Tokens)数。", - "name": "Max Tokens" - } - } - }, - "gpt_weights_path": { - "description": "GPT模型文件路径", - "hint": "即“.ckpt”后缀的文件,请使用绝对路径,路径两端不要带双引号,不填则默认用GPT_SoVITS内置的SoVITS模型(建议直接在GPT_SoVITS中改默认模型)" - }, - "sovits_weights_path": { - "description": "SoVITS模型文件路径", - "hint": "即“.pth”后缀的文件,请使用绝对路径,路径两端不要带双引号,不填则默认用GPT_SoVITS内置的SoVITS模型(建议直接在GPT_SoVITS中改默认模型)" - }, - "gsv_default_parms": { - "description": "GPT_SoVITS默认参数", - "hint": "参考音频文件路径、参考音频文本必填,其他参数根据个人爱好自行填写", - "gsv_ref_audio_path": { - "description": "参考音频文件路径", - "hint": "必填!请使用绝对路径!路径两端不要带双引号!" - }, - "gsv_prompt_text": { - "description": "参考音频文本", - "hint": "必填!请填写参考音频讲述的文本" - }, - "gsv_prompt_lang": { - "description": "参考音频文本语言", - "hint": "请填写参考音频讲述的文本的语言,默认为中文" - }, - "gsv_aux_ref_audio_paths": { - "description": "辅助参考音频文件路径", - "hint": "辅助参考音频文件,可不填" - }, - "gsv_text_lang": { - "description": "文本语言", - "hint": "默认为中文" - }, - "gsv_top_k": { - "description": "生成语音的多样性", - "hint": "" - }, - "gsv_top_p": { - "description": "核采样的阈值", - "hint": "" - }, - "gsv_temperature": { - "description": "生成语音的随机性", - "hint": "" - }, - "gsv_text_split_method": { - "description": "切分文本的方法", - "hint": "可选值: `cut0`:不切分 `cut1`:四句一切 `cut2`:50字一切 `cut3`:按中文句号切 `cut4`:按英文句号切 `cut5`:按标点符号切" - }, - "gsv_batch_size": { - "description": "批处理大小", - "hint": "" - }, - "gsv_batch_threshold": { - "description": "批处理阈值", - "hint": "" - }, - "gsv_split_bucket": { - "description": "将文本分割成桶以便并行处理", - "hint": "" - }, - "gsv_speed_factor": { - "description": "语音播放速度", - "hint": "1为原始语速" - }, - "gsv_fragment_interval": { - "description": "语音片段之间的间隔时间", - "hint": "" - }, - "gsv_streaming_mode": { - "description": "启用流模式", - "hint": "" - }, - "gsv_seed": { - "description": "随机种子", - "hint": "用于结果的可重复性" - }, - "gsv_parallel_infer": { - "description": "并行执行推理", - "hint": "" - }, - "gsv_repetition_penalty": { - "description": "重复惩罚因子", - "hint": "" - }, - "gsv_media_type": { - "description": "输出媒体的类型", - "hint": "建议用wav" - } - }, - "embedding_dimensions": { - "description": "嵌入维度", - "hint": "嵌入向量的维度。根据模型不同,可能需要调整,请参考具体模型的文档。此配置项请务必填写正确,否则将导致向量数据库无法正常工作。" - }, - "embedding_model": { - "description": "嵌入模型", - "hint": "嵌入模型名称。" - }, - "embedding_api_key": { - "description": "API Key" - }, - "embedding_api_base": { - "description": "API Base URL" - }, - "openai_embedding": { - "hint": "如果测试不通过,可以尝试添加 /v1 在末尾以兼容部分 OpenAI API 版本。" - }, - "gemini_embedding": { - "hint": "Gemini Embedding 无需手动添加 /v1beta。" - }, - "volcengine_cluster": { - "description": "火山引擎集群", - "hint": "若使用语音复刻大模型,可选volcano_icl或volcano_icl_concurr,默认使用volcano_tts" - }, - "volcengine_voice_type": { - "description": "火山引擎音色", - "hint": "输入声音id(Voice_type)" - }, - "volcengine_speed_ratio": { - "description": "语速设置", - "hint": "语速设置,范围为 0.2 到 3.0,默认值为 1.0" - }, - "volcengine_volume_ratio": { - "description": "音量设置", - "hint": "音量设置,范围为 0.0 到 2.0,默认值为 1.0" - }, - "azure_tts_voice": { - "description": "音色设置", - "hint": "API 音色" - }, - "azure_tts_style": { - "description": "风格设置", - "hint": "声音特定的讲话风格。 可以表达快乐、同情和平静等情绪。" - }, - "azure_tts_role": { - "description": "模仿设置(可选)", - "hint": "讲话角色扮演。 声音可以模仿不同的年龄和性别,但声音名称不会更改。 例如,男性语音可以提高音调和改变语调来模拟女性语音,但语音名称不会更改。 如果角色缺失或不受声音的支持,则会忽略此属性。" - }, - "azure_tts_rate": { - "description": "语速设置", - "hint": "指示文本的讲出速率。可在字词或句子层面应用语速。 速率变化应为原始音频的 0.5 到 2 倍。" - }, - "azure_tts_volume": { - "description": "语音音量设置", - "hint": "指示语音的音量级别。 可在句子层面应用音量的变化。以从 0.0 到 100.0(从最安静到最大声,例如 75)的数字表示。 默认值为 100.0。" - }, - "azure_tts_region": { - "description": "API 地区", - "hint": "Azure_TTS 处理数据所在区域,具体参考 https://learn.microsoft.com/zh-cn/azure/ai-services/speech-service/regions" - }, - "azure_tts_subscription_key": { - "description": "服务订阅密钥", - "hint": "Azure_TTS 服务的订阅密钥(注意不是令牌)" - }, - "dashscope_tts_voice": { - "description": "音色" - }, - "gm_resp_image_modal": { - "description": "启用图片模态", - "hint": "启用后,将支持返回图片内容。需要模型支持,否则会报错。具体支持模型请查看 Google Gemini 官方网站。温馨提示,如果您需要生成图片,请关闭 `启用群员识别` 配置获得更好的效果。" - }, - "gm_native_search": { - "description": "启用原生搜索功能", - "hint": "启用后所有函数工具将全部失效,免费次数限制请查阅官方文档" - }, - "gm_native_coderunner": { - "description": "启用原生代码执行器", - "hint": "启用后所有函数工具将全部失效" - }, - "gm_url_context": { - "description": "启用URL上下文功能", - "hint": "启用后所有函数工具将全部失效" - }, - "gm_safety_settings": { - "description": "安全过滤器", - "hint": "设置模型输入的内容安全过滤级别。过滤级别分类为NONE(不屏蔽)、HIGH(高风险时屏蔽)、MEDIUM_AND_ABOVE(中等风险及以上屏蔽)、LOW_AND_ABOVE(低风险及以上时屏蔽),具体参见Gemini API文档。", - "harassment": { - "description": "骚扰内容", - "hint": "负面或有害评论" - }, - "hate_speech": { - "description": "仇恨言论", - "hint": "粗鲁、无礼或亵渎性质内容" - }, - "sexually_explicit": { - "description": "露骨色情内容", - "hint": "包含性行为或其他淫秽内容的引用" - }, - "dangerous_content": { - "description": "危险内容", - "hint": "宣扬、助长或鼓励有害行为的信息" - } - }, - "gm_thinking_config": { - "description": "思考配置", - "budget": { - "description": "思考预算", - "hint": "用于指定模型推理时使用的思考 token 数量上限。参见: https://ai.google.dev/gemini-api/docs/thinking#set-budget" - }, - "level": { - "description": "思考级别", - "hint": "推荐用于 Gemini 3 及以上模型,可控制推理行为。参见: https://ai.google.dev/gemini-api/docs/thinking#thinking-levels" - } - }, - "anth_thinking_config": { - "description": "思考配置", - "type": { - "description": "思考类型", - "hint": "设为 'adaptive' 以使用自适应思考(推荐 Opus 4.6+ / Sonnet 4.6+)。留空则使用手动预算模式。参见: https://platform.claude.com/docs/en/build-with-claude/adaptive-thinking" - }, - "budget": { - "description": "思考预算", - "hint": "Anthropic thinking.budget_tokens 参数。必须 >= 1024。仅在思考类型为空时生效。Opus 4.6 / Sonnet 4.6 已弃用。参见: https://platform.claude.com/docs/en/build-with-claude/extended-thinking" - }, - "effort": { - "description": "思考深度", - "hint": "当思考类型为 'adaptive' 时控制思考深度。'high' 为默认值。'max' 仅限 Opus 4.6。参见: https://platform.claude.com/docs/en/build-with-claude/effort" - } - }, - "minimax-group-id": { - "description": "用户组", - "hint": "于账户管理->基本信息中可见" - }, - "minimax-langboost": { - "description": "指定语言/方言", - "hint": "增强对指定的小语种和方言的识别能力,设置后可以提升在指定小语种/方言场景下的语音表现" - }, - "minimax-voice-speed": { - "description": "语速", - "hint": "生成声音的语速, 取值[0.5, 2], 默认为1.0, 取值越大,语速越快" - }, - "minimax-voice-vol": { - "description": "音量", - "hint": "生成声音的音量, 取值(0, 10], 默认为1.0, 取值越大,音量越高" - }, - "minimax-voice-pitch": { - "description": "语调", - "hint": "生成声音的语调, 取值[-12, 12], 默认为0" - }, - "minimax-is-timber-weight": { - "description": "启用混合音色", - "hint": "启用混合音色, 支持以自定义权重混合最多四种音色, 启用后自动忽略单一音色设置" - }, - "minimax-timber-weight": { - "description": "混合音色", - "hint": "混合音色及其权重, 最多支持四种音色, 权重为整数, 取值[1, 100]. 可在官网API语音调试台预览代码获得预设以及编写模板, 需要严格按照json字符串格式编写, 可以查看控制台判断是否解析成功. 具体结构可参照默认值以及官网代码预览." - }, - "minimax-voice-id": { - "description": "单一音色", - "hint": "单一音色编号, 详见官网文档" - }, - "minimax-voice-emotion": { - "description": "情绪", - "hint": "控制合成语音的情绪。当为 auto 时,将根据文本内容自动选择情绪。" - }, - "minimax-voice-latex": { - "description": "支持朗读latex公式", - "hint": "朗读latex公式, 但是需要确保输入文本按官网要求格式化" - }, - "minimax-voice-english-normalization": { - "description": "支持英语文本规范化", - "hint": "可提升数字阅读场景的性能,但会略微增加延迟" - }, - "rag_options": { - "description": "RAG 选项", - "hint": "检索知识库设置, 非必填。仅 Agent 应用类型支持(智能体应用, 包括 RAG 应用)。阿里云百炼应用开启此功能后将无法多轮对话。", - "pipeline_ids": { - "description": "知识库 ID 列表", - "hint": "对指定知识库内所有文档进行检索, 前往 https://bailian.console.aliyun.com/ 数据应用->知识索引创建和获取 ID。" - }, - "file_ids": { - "description": "非结构化文档 ID, 传入该参数将对指定非结构化文档进行检索。", - "hint": "对指定非结构化文档进行检索。前往 https://bailian.console.aliyun.com/ 数据管理创建和获取 ID。" - }, - "output_reference": { - "description": "是否输出知识库/文档的引用", - "hint": "在每次回答尾部加上引用源。默认为 False。" - } - }, - "sensevoice_hint": { - "description": "部署SenseVoice", - "hint": "启用前请 pip 安装 funasr、funasr_onnx、torchaudio、torch、modelscope、jieba 库(默认使用CPU,大约下载 1 GB),并且安装 ffmpeg。否则将无法正常转文字。" - }, - "is_emotion": { - "description": "情绪识别", - "hint": "是否开启情绪识别。happy|sad|angry|neutral|fearful|disgusted|surprised|unknown" - }, - "stt_model": { - "description": "模型名称", - "hint": "modelscope 上的模型名称。默认:iic/SenseVoiceSmall。" - }, - "variables": { - "description": "工作流固定输入变量", - "hint": "可选。工作流固定输入变量,将会作为工作流的输入。也可以在对话时使用 /set 指令动态设置变量。如果变量名冲突,优先使用动态设置的变量。" - }, - "dashscope_app_type": { - "description": "应用类型", - "hint": "百炼应用的应用类型。" - }, - "timeout": { - "description": "超时时间", - "hint": "超时时间,单位为秒。" - }, - "openai-tts-voice": { - "description": "voice", - "hint": "OpenAI TTS 的声音。OpenAI 默认支持:'alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'" - }, - "elevenlabs-tts-voice-id": { - "description": "音色 ID", - "hint": "ElevenLabs 音色 ID。可在 https://elevenlabs.io/app/voice-library 浏览并复制音色 ID。默认 'JBFqnCBsd6RMkjVDRZzb'(George)。" - }, - "elevenlabs-tts-output-format": { - "description": "输出格式", - "hint": "音频输出格式,例如 'mp3_44100_128'、'mp3_22050_32'、'wav_44100'、'opus_48000_128'。不支持裸 PCM/u-law/a-law 格式。默认 'mp3_44100_128'。" - }, - "elevenlabs-tts-stability": { - "description": "稳定性", - "hint": "音色稳定性,范围 [0, 1]。值越高越稳定,越低越富有表现力。留空则使用服务端默认值。" - }, - "elevenlabs-tts-similarity-boost": { - "description": "相似度增强", - "hint": "输出与原始音色的接近程度,范围 [0, 1]。留空则使用服务端默认值。" - }, - "elevenlabs-tts-style": { - "description": "风格夸张度", - "hint": "音色风格的夸张程度,范围 [0, 1]。值越高延迟越大。留空则使用服务端默认值。" - }, - "elevenlabs-tts-use-speaker-boost": { - "description": "说话人增强", - "hint": "增强与原始说话人的相似度,可能略微增加延迟。" - }, - "mimo-tts-voice": { - "description": "音色", - "hint": "MiMo TTS 的音色名称。可选值包括 'mimo_default'、'default_en'、'default_zh'。" - }, - "mimo-tts-format": { - "description": "输出格式", - "hint": "MiMo TTS 生成音频的格式。支持 'wav'、'mp3'、'pcm'。" - }, - "mimo-tts-style-prompt": { - "description": "风格提示词", - "hint": "会以 标签形式添加到待合成文本开头,用于控制语速、情绪、角色或风格,例如 开心、变快、孙悟空、悄悄话。可留空。" - }, - "mimo-tts-dialect": { - "description": "方言", - "hint": "会与风格提示词一起写入开头的 标签中,例如 东北话、四川话、河南话、粤语。可留空。" - }, - "mimo-tts-seed-text": { - "description": "种子文本", - "hint": "作为可选的 user 消息发送,用于辅助调节语气和风格,不会拼接到待合成文本中。" - }, - "fishaudio-tts-character": { - "description": "character", - "hint": "fishaudio TTS 的角色。默认为可莉。更多角色请访问:https://fish.audio/zh-CN/discovery" - }, - "fishaudio-tts-reference-id": { - "description": "reference_id", - "hint": "fishaudio TTS 的参考模型ID(可选)。如果填入此字段,将直接使用模型ID而不通过角色名称查询。例如:626bb6d3f3364c9cbc3aa6a67300a664。更多模型请访问:https://fish.audio/zh-CN/discovery,进入模型详情界面后可复制模型ID" - }, - "whisper_hint": { - "description": "本地部署 Whisper 模型须知", - "hint": "启用前请 pip 安装 openai-whisper 库(N卡用户大约下载 2GB,主要是 torch 和 cuda,CPU 用户大约下载 1 GB),并且安装 ffmpeg。否则将无法正常转文字。" - }, - "whisper_device": { - "description": "推理设备", - "hint": "Whisper 推理设备。Apple Silicon 可选 mps;其他环境建议使用 cpu。若指定 mps 但当前环境不可用,将自动回退到 cpu。" - }, - "id": { - "description": "ID" - }, - "type": { - "description": "模型提供商种类" - }, - "provider_type": { - "description": "模型提供商能力种类" - }, - "enable": { - "description": "启用" - }, - "key": { - "description": "API Key" - }, - "api_base": { - "description": "API Base URL" - }, - "proxy": { - "description": "代理地址", - "hint": "HTTP/HTTPS 代理地址,格式如 http://127.0.0.1:7890。仅对该提供商的 API 请求生效,不影响 Docker 内网通信。" - }, - "model": { - "description": "模型 ID", - "hint": "模型名称,如 gpt-4o-mini, deepseek-chat。" - }, - "max_context_tokens": { - "description": "模型上下文窗口大小", - "hint": "模型最大上下文 Token 大小。如果为 0,则会自动从模型元数据填充(如有)。" - }, - "dify_api_key": { - "description": "API Key", - "hint": "Dify API Key。此项必填。" - }, - "dify_api_base": { - "description": "API Base URL", - "hint": "Dify API Base URL。默认为 https://api.dify.ai/v1" - }, - "dify_api_type": { - "description": "Dify 应用类型", - "hint": "Dify API 类型。根据 Dify 官网,目前支持 chat, chatflow, agent, workflow 三种应用类型。" - }, - "dify_workflow_output_key": { - "description": "Dify Workflow 输出变量名", - "hint": "Dify Workflow 输出变量名。当应用类型为 workflow 时才使用。默认为 astrbot_wf_output。" - }, - "dify_query_input_key": { - "description": "Prompt 输入变量名", - "hint": "发送的消息文本内容对应的输入变量名。默认为 astrbot_text_query。" - }, - "coze_api_key": { - "description": "Coze API Key", - "hint": "Coze API 密钥,用于访问 Coze 服务。" - }, - "bot_id": { - "description": "Bot ID", - "hint": "Coze 机器人的 ID,在 Coze 平台上创建机器人后获得。" - }, - "coze_api_base": { - "description": "API Base URL", - "hint": "Coze API 的基础 URL 地址,默认为 https://api.coze.cn" - }, - "deerflow_api_base": { - "description": "API Base URL", - "hint": "DeerFlow API 网关地址,默认为 http://127.0.0.1:2026" - }, - "deerflow_api_key": { - "description": "DeerFlow API Key", - "hint": "可选。若 DeerFlow 网关配置了 Bearer 鉴权,则在此填写。" - }, - "deerflow_auth_header": { - "description": "Authorization Header", - "hint": "可选。自定义 Authorization 请求头,优先级高于 DeerFlow API Key。" - }, - "deerflow_assistant_id": { - "description": "Assistant ID", - "hint": "DeerFlow 2.0 LangGraph assistant_id,默认为 lead_agent。" - }, - "deerflow_model_name": { - "description": "模型名称覆盖", - "hint": "可选。覆盖 DeerFlow 默认模型(对应运行时 configurable 的 model_name)。" - }, - "deerflow_thinking_enabled": { - "description": "启用思考模式" - }, - "deerflow_plan_mode": { - "description": "启用计划模式", - "hint": "对应 DeerFlow 2.0 运行时 configurable 的 is_plan_mode。" - }, - "deerflow_subagent_enabled": { - "description": "启用子智能体", - "hint": "对应 DeerFlow 2.0 运行时 configurable 的 subagent_enabled。" - }, - "deerflow_max_concurrent_subagents": { - "description": "子智能体最大并发数", - "hint": "对应 DeerFlow 2.0 运行时 configurable 的 max_concurrent_subagents。仅在启用子智能体时生效,默认 3。" - }, - "deerflow_recursion_limit": { - "description": "递归深度上限", - "hint": "对应 LangGraph recursion_limit。" - }, - "auto_save_history": { - "description": "由 Coze 管理对话记录", - "hint": "启用后,将由 Coze 进行对话历史记录管理, 此时 AstrBot 本地保存的上下文不会生效(仅供浏览), 对 AstrBot 的上下文进行的操作也不会生效。如果为禁用, 则使用 AstrBot 管理上下文。" - } - } - }, - "help": { - "documentation": "官方文档", - "support": "加群询问", - "helpText": "不了解配置?请见 {documentation} 或 {support}。", - "helpPrefix": "不了解配置?请见", - "helpMiddle": "或", - "helpSuffix": "。" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/config.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/config.json deleted file mode 100644 index 46db3fb9..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/config.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "title": "配置文件", - "subtitle": "管理系统配置和设置", - "editor": { - "visual": "可视化编辑", - "code": "代码编辑", - "revertCode": "回到更改前的代码", - "applyConfig": "应用此配置", - "applyTip": "`应用此配置` 将配置暂存并应用到可视化。如要保存,需再点击右下角保存按钮。" - }, - "actions": { - "save": "保存配置", - "delete": "删除这项", - "add": "添加", - "reset": "重置为默认", - "export": "导出配置", - "import": "导入配置", - "validate": "验证配置" - }, - "help": { - "documentation": "官方文档", - "support": "加群询问", - "helpText": "不了解配置?请见 {documentation} 或 {support}。", - "helpPrefix": "不了解配置?请见", - "helpMiddle": "或", - "helpSuffix": "。" - }, - "messages": { - "configApplied": "配置成功应用。如要保存,需再点击右下角保存按钮。", - "configApplyError": "配置未应用,Json 格式错误。", - "unsavedChangesNotice": "当前配置有未保存修改。请点击右下角保存按钮以生效。", - "saveSuccess": "配置保存成功", - "saveError": "配置保存失败", - "loadError": "配置加载失败", - "deleteSuccess": "删除成功", - "deleteError": "删除失败", - "updateSuccess": "更新成功", - "updateError": "更新失败" - }, - "sections": { - "general": "常规设置", - "advanced": "高级设置", - "security": "安全设置", - "appearance": "外观设置", - "notification": "通知设置", - "moreConfig": "更多配置" - }, - "general": { - "botName": "机器人名称", - "language": "界面语言", - "timezone": "时区", - "autoSave": "自动保存", - "debugMode": "调试模式" - }, - "advanced": { - "logLevel": "日志级别", - "maxConnections": "最大连接数", - "timeout": "超时时间", - "retryAttempts": "重试次数", - "cacheSize": "缓存大小" - }, - "security": { - "apiKey": "API密钥", - "allowedHosts": "允许的主机", - "rateLimit": "频率限制", - "encryption": "加密设置" - }, - "configSelection": { - "selectConfig": "选择配置文件", - "normalConfig": "普通", - "systemConfig": "系统" - }, - "search": { - "placeholder": "搜索配置项(字段名/描述/提示)", - "noResult": "未找到匹配的配置项" - }, - "configManagement": { - "title": "配置文件管理", - "description": "AstrBot 支持针对不同机器人分别设置配置文件。默认会使用 `default` 配置。", - "newConfig": "新建配置文件", - "copyConfig": "复制配置文件", - "editConfig": "编辑配置文件", - "manageConfigs": "管理配置文件...", - "configName": "名称", - "fillConfigName": "填写配置文件名称", - "confirmDelete": "确定要删除配置文件 \"{name}\" 吗?此操作不可恢复。", - "pleaseEnterName": "请填写配置名称", - "nameExists": "配置文件名称已存在,请使用其他名称", - "createFailed": "新配置文件创建失败", - "copyFailed": "复制配置文件失败", - "deleteFailed": "删除配置文件失败", - "updateFailed": "更新配置文件失败" - }, - "buttons": { - "cancel": "取消", - "create": "创建", - "update": "更新" - }, - "codeEditor": { - "title": "编辑配置文件" - }, - "fileUpload": { - "button": "管理文件", - "dialogTitle": "已上传文件", - "dropzone": "上传新文件", - "allowedTypes": "允许类型:{types}", - "empty": "暂无已上传文件", - "statusMissing": "文件缺失", - "statusUnconfigured": "未加入配置", - "uploadSuccess": "已上传 {count} 个文件", - "uploadFailed": "上传失败", - "loadFailed": "获取文件列表失败", - "fileTooLarge": "文件过大(上限 {max} MB):{name}", - "deleteSuccess": "已删除文件", - "deleteFailed": "删除失败", - "addToConfig": "已加入配置", - "fileCount": "文件:{count}", - "done": "完成" - }, - "unsavedChangesWarning": { - "dialogTitle": "未保存的更改", - "leavePage": "当前配置有未保存的更改,切换前是否保存?", - "switchConfig": "切换配置文件会丢失当前未保存的更改,是否先保存?", - "options": { - "save": "保存", - "saveAndSwitch": "保存并切换", - "discardAndSwitch": "放弃更改并切换", - "closeCard": "关闭弹窗", - "confirm": "确定", - "cancel": "取消" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/console.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/console.json deleted file mode 100644 index f24f80ad..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/console.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "title": "平台日志", - "autoScroll": { - "enabled": "自动滚动已开启", - "disabled": "自动滚动已关闭" - }, - "pipInstall": { - "button": "安装 pip 库", - "dialogTitle": "安装 Pip 库", - "packageLabel": "*库名,如 llmtuner", - "mirrorLabel": "强制 PyPI 软件仓库链接(可选)", - "mirrorHint": "强制 PyPI 软件仓库链接 > 配置项 `PyPI 软件仓库地址`", - "installButton": "安装", - "installSuccess": "安装成功。", - "installFailed": "安装失败。", - "requestFailed": "请求失败。" - }, - "debugHint": { - "text": "Debug 日志需要在「配置文件 → 系统 → 控制台日志级别」中开启" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/conversation.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/conversation.json deleted file mode 100644 index 583c6f44..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/conversation.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "title": "对话管理", - "subtitle": "管理和查看用户对话历史记录", - "filters": { - "title": "筛选条件", - "platform": "机器人 ID", - "type": "类型", - "search": "搜索关键词", - "reset": "重置" - }, - "history": { - "title": "对话历史", - "refresh": "刷新" - }, - "batch": { - "deleteSelected": "删除选中 ({count})", - "exportSelected": "导出选中 ({count})" - }, - "pagination": { - "itemsPerPage": "每页", - "showingItems": "显示 {start}-{end} 项,共 {total} 项" - }, - "table": { - "headers": { - "title": "标题", - "platform": "机器人 ID", - "type": "消息类型", - "cid": "对话 ID", - "umo": "消息会话来源", - "sessionId": "会话 ID", - "createdAt": "创建时间", - "updatedAt": "更新时间", - "actions": "操作" - }, - "umoDisplay": { - "parsed": "解析后", - "raw": "UMO" - } - }, - "actions": { - "view": "查看", - "edit": "编辑", - "delete": "删除" - }, - "messageTypes": { - "group": "群聊", - "friend": "私聊", - "unknown": "未知" - }, - "status": { - "noTitle": "无标题对话", - "unknown": "未知", - "noData": "暂无对话记录", - "emptyContent": "对话内容为空", - "audioNotSupported": "您的浏览器不支持音频播放。" - }, - "dialogs": { - "view": { - "title": "对话详情", - "editMode": "编辑对话", - "previewMode": "预览模式", - "saveChanges": "保存修改", - "close": "关闭", - "confirmClose": "您有未保存的更改,确定要关闭吗?" - }, - "edit": { - "title": "编辑对话信息", - "titleLabel": "对话标题", - "titlePlaceholder": "输入对话标题", - "cancel": "取消", - "save": "保存" - }, - "delete": { - "title": "确认删除", - "message": "确定要删除对话 {title} 吗?此操作不可恢复。", - "cancel": "取消", - "confirm": "删除" - }, - "batchDelete": { - "title": "批量删除确认", - "message": "确定要删除选中的 {count} 个对话吗?此操作不可恢复,请谨慎操作!", - "andMore": "等 {count} 个", - "cancel": "取消", - "confirm": "批量删除", - "warning": "警告:此操作不可撤销!" - } - }, - "messages": { - "fetchError": "获取对话列表失败", - "saveSuccess": "保存成功", - "saveError": "保存失败", - "deleteSuccess": "删除成功", - "deleteError": "删除失败", - "historyError": "获取对话历史失败", - "historySaveSuccess": "对话历史保存成功", - "historySaveError": "对话历史保存失败", - "invalidJson": "JSON格式无效", - "noItemSelected": "请先选择要删除的对话", - "batchDeleteSuccess": "成功删除 {count} 个对话", - "batchDeleteError": "批量删除失败", - "batchDeletePartial": "删除完成:成功 {deleted} 个,失败 {failed} 个", - "exportSuccess": "导出成功", - "exportError": "导出失败", - "noItemSelectedForExport": "请先选择要导出的对话", - "copySuccess": "复制成功", - "copyError": "复制失败" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/cron.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/cron.json deleted file mode 100644 index 750af66b..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/cron.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "header": { - "eyebrow": "Automation", - "live": "实时同步" - }, - "page": { - "title": "未来任务", - "beta": "实验性", - "subtitle": "AstrBot 会在设定时间自动唤醒,完成任务后把结果投递回目标会话。", - "proactive": { - "link": "支持的消息平台", - "supported": "主动发送结果仅支持以下您已配置的平台", - "unsupported": "暂无支持主动消息的平台,请在平台设置中开启。" - } - }, - "actions": { - "create": "新建", - "edit": "编辑", - "refresh": "刷新", - "delete": "删除", - "cancel": "取消", - "close": "关闭", - "more": "更多操作", - "runNow": "立即执行", - "save": "保存", - "submit": "创建" - }, - "platformDialog": { - "title": "支持的消息平台", - "description": "只有以下平台支持主动推送消息。未来任务完成后,AstrBot 才能把结果投递回这些平台的目标会话。" - }, - "card": { - "onceAt": "一次性 · {time}", - "runAt": "执行时间:{time}", - "nextRun": "下次执行:{time}", - "dailyAt": "每天 {time}", - "weeklyAt": "每周{day} {time}", - "monthlyAt": "每月 {day} 日 {time}", - "everyMinutes": "每隔 {count} 分钟", - "everyHours": "每隔 {count} 小时", - "everyDays": "每隔 {count} 天", - "customCron": "自定义 · {cron}", - "noDeliveryTarget": "未设置投递地" - }, - "filters": { - "search": "搜索任务名称和内容", - "umo": "按投递地筛选", - "noUmos": "暂无 UMO", - "noDeliveryTarget": "未设置投递地", - "noMatches": "没有匹配的未来任务。" - }, - "overview": { - "totalTasks": "任务总数", - "totalTasksNote": "当前已注册的未来任务", - "enabledTasks": "启用任务", - "enabledTasksNote": "执行器会按计划唤醒并运行", - "oneOffTasks": "一次性任务", - "recurringTasksNote": "循环任务 {count} 个", - "proactivePlatforms": "主动平台", - "proactivePlatformsNote": "已开启主动消息的平台数量" - }, - "section": { - "registered": { - "title": "任务列表", - "subtitle": "集中查看当前注册任务、执行时间与启用状态" - }, - "delivery": { - "title": "投递状态", - "subtitle": "未来任务执行后的结果,会通过支持主动消息的平台回传", - "support": "主动投递", - "available": "可用", - "unavailable": "不可用", - "enabledPlatforms": "已启用平台" - }, - "quickCreate": { - "title": "快速创建", - "runMode": "任务模式", - "target": "目标定位" - }, - "platforms": { - "title": "适用平台" - } - }, - "table": { - "title": "已注册任务", - "subtitle": "支持查看 cron、会话、最近执行记录与启用状态", - "empty": "暂无任务。", - "headers": { - "name": "名称", - "type": "类型", - "cron": "Cron", - "session": "会话 ID", - "nextRun": "下一次执行", - "lastRun": "最近执行", - "note": "说明", - "actions": "操作" - }, - "type": { - "once": "一次性", - "recurring": "循环", - "activeAgent": "Active Agent", - "workflow": "Workflow", - "unknown": "{type}" - }, - "timezoneLocal": "本地时区", - "notAvailable": "—" - }, - "form": { - "title": "新建任务", - "editTitle": "编辑任务", - "chatHint": "你可以直接通过聊天的方式来让 AstrBot 创建未来任务,而不必在此添加。", - "runOnce": "一次性任务", - "name": "任务名称 *", - "note": "任务需求 *", - "scheduleMode": "执行时间 *", - "scheduleModes": { - "once": "一次性", - "interval": "间隔", - "daily": "每天", - "weekly": "每周", - "monthly": "每个月", - "cron": "自定义" - }, - "intervalEvery": "每隔 *", - "intervalUnit": "单位 *", - "intervalUnits": { - "minutes": "分钟", - "hours": "小时", - "days": "天" - }, - "dailyTime": "时间 *", - "weeklyDay": "星期 *", - "weeklyTime": "时间 *", - "monthlyDay": "日期 *", - "monthlyTime": "时间 *", - "weekdays": { - "sunday": "周日", - "monday": "周一", - "tuesday": "周二", - "wednesday": "周三", - "thursday": "周四", - "friday": "周五", - "saturday": "周六" - }, - "cron": "Cron 表达式 *", - "cronPlaceholder": "0 9 * * *", - "runAt": "执行时间 *", - "session": "投递到(可选)", - "noUmos": "暂无可用会话", - "timezone": "时区(可选,如 Asia/Shanghai)", - "enabled": "启用" - }, - "messages": { - "loadFailed": "获取任务失败", - "updateSuccess": "更新成功", - "updateFailed": "更新失败", - "deleteSuccess": "已删除", - "deleteFailed": "删除失败", - "nameRequired": "请填写任务名称", - "sessionRequired": "请选择投递目标", - "noteRequired": "请填写任务需求", - "cronRequired": "请填写 Cron 表达式", - "runAtRequired": "请选择执行时间", - "intervalRequired": "请填写有效的间隔时间", - "dailyTimeRequired": "请选择每天执行的时间", - "weeklyTimeRequired": "请选择每周执行的星期和时间", - "monthlyTimeRequired": "请选择每月执行的日期和时间", - "createSuccess": "创建成功", - "createFailed": "创建失败", - "runStarted": "已开始执行", - "runFailed": "执行失败" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/dashboard.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/dashboard.json deleted file mode 100644 index 42227932..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/dashboard.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "title": "平台日志", - "subtitle": "实时监控和统计数据", - "lastUpdate": "最后更新", - "status": { - "loading": "加载中...", - "dataError": "获取数据失败", - "noticeError": "获取公告失败", - "online": "在线", - "uptime": "运行时间", - "memoryUsage": "内存占用" - }, - "stats": { - "totalMessage": { - "title": "消息总数", - "subtitle": "所有平台发送的消息总计" - }, - "onlinePlatform": { - "title": "消息平台", - "subtitle": "已连接的消息平台数量" - }, - "runningTime": { - "title": "运行时间", - "subtitle": "系统已运行时长", - "format": "{hours}小时{minutes}分{seconds}秒" - }, - "memoryUsage": { - "title": "内存占用", - "subtitle": "系统内存使用情况", - "cpuLoad": "CPU 负载", - "status": { - "good": "良好", - "normal": "正常", - "high": "偏高" - } - } - }, - "charts": { - "messageTrend": { - "title": "消息趋势分析", - "subtitle": "跟踪消息数量随时间的变化", - "totalMessages": "总消息数", - "dailyAverage": "平均每天", - "growthRate": "增长率", - "timeLabel": "时间", - "messageCount": "消息条数", - "timeRanges": { - "1day": "过去 1 天", - "3days": "过去 3 天", - "1week": "过去 7 天", - "1month": "过去 30 天" - } - }, - "platformStat": { - "title": "平台消息统计", - "subtitle": "各平台消息数量分布", - "total": "总计", - "noData": "暂无平台数据", - "messageUnit": "条", - "platformCount": "平台数", - "mostActive": "最活跃", - "totalPercentage": "总消息占比" - } - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/extension.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/extension.json deleted file mode 100644 index d8ba9869..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/extension.json +++ /dev/null @@ -1,456 +0,0 @@ -{ - "title": "插件管理", - "subtitle": "管理和配置系统插件", - "tabs": { - "installedPlugins": "AstrBot 插件", - "market": "AstrBot 插件市场", - "installedMcpServers": "MCP", - "skills": "Skills", - "handlersOperation": "管理行为" - }, - "titles": { - "installedAstrBotPlugins": "已安装的 AstrBot 插件" - }, - "failedPlugins": { - "title": "加载失败插件({count})", - "hint": "这些插件加载失败,仍可尝试重载或直接卸载。", - "columns": { - "plugin": "插件", - "error": "错误" - } - }, - "search": { - "placeholder": "搜索插件...", - "marketPlaceholder": "搜索市场插件..." - }, - "filters": { - "all": "全部" - }, - "views": { - "card": "卡片视图", - "list": "列表视图" - }, - "buttons": { - "showSystemPlugins": "显示系统插件", - "hideSystemPlugins": "隐藏系统插件", - "install": "安装", - "uninstall": "卸载", - "update": "更新", - "reload": "重载", - "enable": "启用", - "disable": "禁用", - "configure": "配置", - "viewInfo": "行为", - "viewDocs": "文档", - "viewRepo": "仓库", - "openPages": "Pages", - "openPage": "打开", - "openWebui": "打开插件UI界面", - "close": "关闭", - "save": "保存", - "saveAndClose": "保存并关闭", - "cancel": "取消", - "next": "下一步", - "actions": "操作", - "back": "返回", - "selectFile": "选择文件", - "refresh": "刷新", - "updateAll": "更新全部插件", - "pin": "置顶", - "unpin": "取消置顶", - "deleteSource": "删除源", - "reshuffle": "随机一发" - }, - "status": { - "enabled": "启用", - "disabled": "禁用", - "system": "系统", - "loading": "加载中...", - "installed": "已安装", - "unknown": "未知", - "unsupported": "不支持" - }, - "tooltips": { - "enable": "点击启用", - "disable": "点击禁用", - "reload": "重载", - "configure": "配置", - "viewInfo": "行为", - "viewDocs": "文档", - "update": "更新", - "uninstall": "卸载" - }, - "table": { - "headers": { - "name": "名称", - "description": "描述", - "version": "版本", - "author": "作者", - "status": "状态", - "actions": "操作", - "stars": "Star数", - "lastUpdate": "最近更新", - "tags": "标签", - "eventType": "行为类型", - "specificType": "具体类型", - "trigger": "触发方式" - } - }, - "empty": { - "noPlugins": "暂无插件", - "noPluginsDesc": "尝试安装插件或调整搜索条件" - }, - "detail": { - "contents": "插件行为", - "noContents": "暂无行为", - "notFound": "未找到该插件", - "docsTitle": "文档", - "docsEmpty": "暂无文档", - "changelogTitle": "更新日志", - "changelogEmpty": "暂无更新日志", - "subCommandsCount": "{count} 个子指令", - "handlerGroups": { - "page": "页面", - "skill": "技能", - "command": "指令/指令组", - "llm_tool": "LLM 工具", - "listener": "事件监听器", - "hook": "钩子", - "commands": "指令/指令组", - "hooks": "钩子", - "functionTools": "函数工具", - "eventListeners": "事件监听器" - }, - "info": { - "title": "信息", - "version": "版本", - "author": "作者", - "category": "类别", - "stars": "Star数", - "tags": "标签", - "astrbotVersion": "AstrBot 版本要求", - "supportPlatforms": "支持平台", - "authorWebsite": "作者网站", - "repository": "仓库" - } - }, - "market": { - "recommended": "🥳 推荐", - "allPlugins": "📦 全部插件", - "category": "分类", - "categories": { - "all": "全部", - "ai_tools": "AI 增强", - "entertainment": "娱乐", - "productivity": "效率", - "integrations": "外部集成", - "utilities": "生活实用", - "other": "其他" - }, - "showFullName": "完整名称", - "devDocs": "插件开发文档", - "submitRepo": "提交插件仓库", - "customSource": "自定义插件源", - "source": "插件源", - "availableSources": "可用源", - "sourceManagement": "插件源管理", - "addSource": "添加插件源", - "sourceName": "源名称", - "sourceUrl": "源地址", - "defaultSource": "默认插件源", - "removeSource": "删除插件源", - "confirmRemoveSource": "确定要删除此插件源吗?", - "sourceAdded": "插件源添加成功", - "sourceRemoved": "插件源删除成功", - "sourceError": "操作失败", - "selectSource": "选择插件源", - "currentSource": "当前插件源", - "editSource": "编辑插件源", - "sourceUpdated": "插件源更新成功", - "defaultOfficialSource": "默认官方源", - "sourceExists": "该插件源已存在", - "sourceResolved": "插件源可用", - "installPlugin": "安装插件", - "randomPlugins": "🎲 随机插件", - "sourceSafetyWarning": "即使是默认插件源,我们也不能完全保证插件的稳定性和安全性,使用前请谨慎核查。" - }, - "sort": { - "by": "排序方式", - "default": "默认排序", - "installTime": "最后修改时间", - "name": "名称", - "stars": "Star数", - "author": "作者名", - "updated": "更新时间", - "updateStatus": "更新状态", - "ascending": "升序", - "descending": "降序" - }, - "tags": { - "danger": "危险" - }, - "dialogs": { - "error": { - "title": "错误信息", - "checkConsole": "详情请检查平台日志" - }, - "config": { - "title": "插件配置", - "noConfig": "这个插件没有配置" - }, - "loading": { - "title": "加载中...", - "logs": "日志" - }, - "uninstall": { - "title": "删除确认", - "message": "你确定要删除当前插件吗?", - "deleteConfig": "同时删除插件配置文件", - "deleteData": "同时删除插件持久化数据", - "configHint": "配置文件位于 data/config 目录", - "dataHint": "删除 data/plugin_data、plugins_data 目录下的数据,以及数据库中插件的 KV 数据" - }, - "install": { - "title": "安装插件", - "fromFile": "从文件安装", - "fromUrl": "从链接安装", - "supportPlatformsCount": "支持 {count} 个平台", - "sectionTitle": "安装", - "downloadSource": "将从以下位置安装插件包体:", - "githubSecurityWarning": "AstrBot 不能保证从 GitHub 上下载的插件的安全性。请确认你信任该插件来源后再安装。" - }, - "update": { - "title": "更新插件", - "sectionTitle": "更新", - "downloadSource": "将从以下位置安装更新包体:", - "confirm": "确认更新" - }, - "danger_warning": { - "title": "警告", - "message": "该插件可能包含不安全的代码或功能,可能导致系统异常或数据损失等。请确认是否继续安装?", - "confirm": "继续", - "cancel": "取消" - }, - "versionSupport": { - "title": "版本兼容性警告", - "message": "该插件声明的 AstrBot 版本范围与当前版本不匹配。你可以无视警告继续安装,但可能无法正常运行。", - "confirm": "无视警告,继续安装", - "cancel": "取消安装" - }, - "forceUpdate": { - "title": "未检测到新版本", - "message": "当前插件未检测到新版本,是否强制重新安装?这将从远程仓库拉取最新代码。", - "confirm": "强制更新" - }, - "updateAllConfirm": { - "title": "确认更新全部插件", - "message": "确定要更新全部 {count} 个插件吗?此操作可能需要一些时间。", - "confirm": "确认更新" - }, - "sourceBinding": { - "title": "更换插件源", - "selectTitle": "选择插件安装源", - "noCandidates": "未在已配置的插件源中找到匹配记录,且当前插件缺少可用仓库地址。", - "repoOption": "直接使用仓库地址", - "repoOptionId": "通过 repo 重新安装", - "installDestination": "安装目的地", - "confirm": "使用该插件源", - "confirmAndContinue": "使用并继续" - } - }, - "messages": { - "uninstalling": "正在卸载", - "refreshing": "正在刷新插件列表...", - "refreshSuccess": "插件列表已刷新!", - "refreshFailed": "刷新插件列表时发生错误", - "operationFailed": "操作失败", - "reloadSuccess": "重载成功", - "reloadFailed": "重载失败", - "updateSuccess": "更新成功!", - "addSuccess": "添加成功!", - "saveSuccess": "保存成功!", - "deleteSuccess": "删除成功!", - "installing": "正在从文件安装插件", - "installingFromUrl": "正在从链接安装插件...", - "installFailed": "安装插件失败:", - "getMarketDataFailed": "获取插件市场数据失败:", - "hasUpdate": "有新版本:", - "confirmDelete": "确定要删除插件吗?", - "fillUrlOrFile": "请填写插件链接或上传插件文件", - "dontFillBoth": "请不要同时填写插件链接和上传文件", - "supportedFormats": "支持 .zip 格式的插件文件", - "updateAllSuccess": "所有可更新的插件都已更新!", - "updateAllFailed": "有 {failed}/{total} 个插件更新失败:", - "noUpdatesAvailable": "当前没有可更新的插件", - "updateDisabled": "该插件缺少可用的安装源或仓库地址,无法更新或重新安装", - "changeSourceDisabled": "当前插件缺少可用仓库地址,无法更换插件源", - "fillSourceUrl": "请填写插件源地址", - "fillSourceName": "请填写插件源名称", - "resolveSourceFirst": "请先解析并校验插件源地址", - "sourceResolveFailed": "插件源解析失败", - "invalidUrl": "请输入有效的URL地址", - "invalidGithubRepo": "请输入有效的 GitHub 仓库链接", - "enterJsonUrl": "请输入返回插件列表JSON数据的URL地址", - "pluginNotFound": "未找到该插件", - "pluginDisabled": "该插件当前已禁用,请先启用后再访问页面", - "pluginPageNotFound": "未找到该插件页面", - "pluginPageLoadFailed": "加载插件页面失败", - "missingMarketPluginId": "缺少合法的插件标识,无法从该插件市场源安装", - "sourceBindSuccess": "插件源已更新", - "validatingPlugin": "正在校验插件...", - "pluginValidateSuccess": "插件校验通过", - "pluginValidateFailed": "插件校验失败" - }, - "upload": { - "fromFile": "从文件安装", - "fromUrl": "从链接安装", - "selectFile": "选择文件", - "enterUrl": "输入插件仓库链接" - }, - "skills": { - "modeLocal": "本地 Skills", - "modeNeo": "Neo Skills", - "actions": "操作", - "upload": "上传 Skills", - "refresh": "刷新", - "empty": "暂无 Skills", - "emptyHint": "请上传 Skills 压缩包", - "uploadDialogTitle": "上传 Skills", - "uploadHint": "支持批量上传 zip 技能包,也支持拖拽批量上传 zip 技能包。系统会自动校验目录结构,并给出逐个文件的结果。", - "structureRequirement": "支持压缩包内含多个 skills 文件夹。", - "abilityMultiple": "支持一次上传多个zip文件", - "abilityValidate": "自动校验 `SKILL.md`", - "abilitySkip": "自动跳过重复文件", - "selectFile": "选择文件", - "selectFiles": "选择文件(可多选)", - "dropzoneTitle": "拖拽多个 zip 文件到这里", - "dropzoneAction": "或者点击之后在文件夹中选择多个文件", - "dropzoneHint": "支持批量上传,系统会自动校验目录结构", - "fileListTitle": "待处理文件", - "fileListEmpty": "选择文件后会在这里显示校验结果与上传状态", - "uploading": "正在上传...", - "batchResultTitle": "批量上传结果", - "batchResultSummary": "共 {total} 个文件,成功 {success} 个", - "batchSuccessList": "上传成功", - "batchFailedList": "上传失败", - "confirm": "确定", - "confirmUpload": "开始上传", - "cancel": "取消", - "statusWaiting": "待上传", - "statusUploading": "上传中", - "statusSuccess": "已上传", - "statusError": "校验失败", - "statusSkipped": "已跳过", - "summaryTotal": "共 {count} 个文件", - "summaryReady": "待处理 {count}", - "summarySuccess": "成功 {count}", - "summaryFailed": "失败 {count}", - "summarySkipped": "跳过 {count}", - "validationReady": "等待上传,上传时会自动校验目录结构", - "validationZipOnly": "仅支持 zip 技能包", - "validationDuplicate": "同名文件已在列表中,已跳过", - "validationUploading": "正在校验并上传...", - "validationUploadFailed": "上传失败,请重试", - "validationUploadedAs": "已安装为 {name}", - "validationNoResult": "未收到校验结果,请检查平台日志", - "noDescription": "无描述", - "path": "路径", - "uploadSuccess": "上传成功", - "uploadFailed": "上传失败", - "download": "下载", - "downloadSuccess": "下载成功", - "downloadFailed": "下载失败", - "loadFailed": "加载 Skills 失败", - "updateSuccess": "更新成功", - "updateFailed": "更新失败", - "deleteTitle": "删除确认", - "deleteMessage": "确定要删除该 Skill 吗?", - "deleteSuccess": "删除成功", - "deleteFailed": "删除失败", - "neoSkillKey": "skill_key 过滤", - "neoStatus": "候选状态", - "neoStage": "发布阶段", - "neoFilterHint": "筛选候选与发布记录", - "neoAll": "全部", - "neoCandidates": "Neo Candidates", - "neoReleases": "Neo Releases", - "neoLoadFailed": "加载 Neo Skills 数据失败", - "neoPass": "通过", - "neoReject": "拒绝", - "neoEvaluateSuccess": "评测更新成功", - "neoEvaluateFailed": "评测更新失败", - "neoPromoteSuccess": "发布成功", - "neoPromoteFailed": "发布失败", - "neoRollback": "回滚", - "neoRollbackSuccess": "回滚成功", - "neoRollbackFailed": "回滚失败", - "neoDeactivate": "失活", - "neoDeactivateSuccess": "失活成功", - "neoDeactivateFailed": "失活失败", - "neoSync": "同步", - "neoSyncSuccess": "同步成功", - "neoSyncFailed": "同步失败", - "neoDelete": "删除", - "neoDeleteSuccess": "删除成功", - "neoDeleteFailed": "删除失败", - "neoPayloadTitle": "Neo Payload 详情", - "neoPayloadFailed": "读取 Payload 失败", - "runtimeNoneWarning": "Computer Use 运行环境为无,Skills 可能无法正确被 Agent 运行,因为没有启用运行环境。", - "runtimeHint": "需要在配置的 “使用电脑能力” 中将运行环境设置为 “local” 或 “sandbox” 才能让 AstrBot 正常使用你提供的 Skills。工作区的 Skills 暂不在此页面显示。", - "neoRuntimeRequired": "Neo Skills 仅在运行环境为 sandbox 且沙箱驱动为 shipyard_neo 时可用。", - "sourceLocalOnly": "本地 Skill", - "sourceSandboxOnly": "Sandbox 预置 Skill", - "sourceBoth": "本地 + Sandbox", - "sourcePlugin": "插件:{plugin}", - "sandboxDiscoveryPending": "尚未发现 Sandbox 预置 Skill。请至少启动一次 Sandbox 会话后再查看。", - "sandboxPresetReadonly": "Sandbox 预置 Skill 在此处为只读,无法在本地 Skills 页面删除或启用/禁用。", - "pluginReadonly": "插件提供的 Skill 由插件管理,无法在本地 Skills 页面删除或下载。", - "openEditor": "查看/编辑", - "editorTitle": "编辑 Skill", - "editorLoadFailed": "读取 Skill 文件失败", - "editorSaveFailed": "保存 Skill 文件失败", - "editorSaveSuccess": "保存成功", - "saveFile": "保存文件", - "readonly": "只读", - "unsaved": "未保存", - "noFileSelected": "未选择文件", - "discardChanges": "当前文件有未保存修改,确定要丢弃吗?" - }, - "card": { - "actions": { - "pluginConfig": "插件配置", - "uninstallPlugin": "卸载插件", - "reloadPlugin": "重载插件", - "togglePlugin": "插件", - "viewHandlers": "查看行为", - "updateTo": "更新到", - "reinstall": "重新安装", - "changeSource": "更换插件源" - }, - "status": { - "hasUpdate": "有新版本可用", - "disabled": "该插件已经被禁用", - "handlersCount": "个行为", - "supportPlatform": "支持平台", - "supportPlatformsCount": "支持 {count} 个平台", - "astrbotVersion": "AstrBot 版本要求" - }, - "alt": { - "logo": "logo", - "extensionIcon": "扩展图标" - }, - "errors": { - "confirmNotRegistered": "$confirm 未正确注册" - } - }, - "conflicts": { - "title": "检测到指令冲突", - "message": "这会导致部分指令工作异常,建议前往【指令管理】面板进行处理。", - "pairs": "对指令冲突", - "goToManage": "前往处理", - "later": "稍后处理" - }, - "pluginChangelog": { - "menuTitle": "查看更新日志" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/detail.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/detail.json deleted file mode 100644 index 54bc60b7..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/detail.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "title": "知识库详情", - "backToList": "返回列表", - "breadcrumb": { - "list": "知识库" - }, - "tabs": { - "overview": "概览", - "documents": "文档管理", - "retrieval": "知识库检索", - "sessions": "使用会话", - "settings": "设置" - }, - "overview": { - "title": "基本信息", - "name": "名称", - "description": "描述", - "emoji": "图标", - "createdAt": "创建时间", - "updatedAt": "更新时间", - "stats": "统计信息", - "docCount": "文档数量", - "chunkCount": "分块数量", - "embeddingModel": "嵌入模型", - "rerankModel": "重排序模型", - "notSet": "未设置" - }, - "documents": { - "title": "文档列表", - "upload": "上传文档", - "empty": "暂无文档", - "name": "文档名称", - "type": "类型", - "size": "大小", - "chunks": "分块数", - "createdAt": "上传时间", - "actions": "操作", - "view": "查看", - "delete": "删除", - "deleteConfirm": "确定要删除文档「{name}」吗?", - "deleteWarning": "此操作将删除文档及其所有分块,不可恢复。", - "uploading": "正在上传...", - "uploadSuccess": "文档上传成功", - "uploadFailed": "文档上传失败", - "deleteSuccess": "文档删除成功", - "deleteFailed": "文档删除失败" - }, - "upload": { - "title": "上传文档", - "selectFile": "选择文件", - "dropzone": "拖放文件到这里或点击选择", - "supportedFormats": "支持的格式: .txt, .md, .markdown, .rst, .adoc, .pdf, .docx, .epub, .xls, .xlsx", - "maxSize": "最大文件大小: 128MB", - "chunkSettings": "分块设置", - "batchSettings": "批处理设置", - "cleaningSettings": "清洗设置", - "enableCleaning": "启用内容清洗", - "cleaningProvider": "清洗服务提供商", - "cleaningProviderHint": "选择一个 LLM 服务商来对提取的网页内容进行清洗和总结", - "chunkSize": "分块大小", - "chunkSizeHint": "每个文本块的字符数 (默认: 512)", - "chunkOverlap": "分块重叠", - "chunkOverlapHint": "相邻文本块之间的重叠字符数 (默认: 50)", - "batchSize": "批处理大小", - "batchSizeHint": "每批处理的文本块数量 (默认: 32)", - "tasksLimit": "并发任务限制", - "tasksLimitHint": "最大并发上传任务数 (默认: 3)", - "maxRetries": "最大重试次数", - "maxRetriesHint": "上传失败任务的重试次数 (默认: 3)", - "cancel": "取消", - "submit": "上传", - "fileRequired": "请选择要上传的文件", - "fileUpload": "文件上传", - "fromUrl": "从 URL", - "urlPlaceholder": "请输入要提取内容的网页 URL", - "urlRequired": "请输入 URL", - "urlHint": "将自动从目标 URL 提取主要内容作为文档。目前支持 {supported} 页面,请确保目标网页允许爬虫访问。", - "beta": "测试版" - }, - "retrieval": { - "title": "知识库检索", - "subtitle": "使用稠密检索和稀疏检索测试知识库内容", - "query": "检索查询", - "queryPlaceholder": "输入要检索的内容...", - "search": "检索", - "searching": "检索中...", - "results": "检索结果", - "noResults": "没有找到相关内容", - "tryDifferentQuery": "尝试使用不同的查询词", - "settings": "检索设置", - "topK": "返回结果数量", - "topKHint": "最多返回多少条检索结果", - "enableRerank": "启用重排序", - "enableRerankHint": "使用重排序模型提高检索质量", - "score": "相关度分数", - "document": "所属文档", - "chunk": "文本块 #{index}", - "content": "内容", - "charCount": "{count} 字符", - "searchSuccess": "检索完成,找到 {count} 条结果", - "searchFailed": "检索失败", - "queryRequired": "请输入检索查询" - }, - "settings": { - "title": "知识库设置", - "basic": "基本设置", - "retrieval": "检索设置", - "chunkSize": "分块大小", - "chunkOverlap": "分块重叠", - "topKDense": "稠密检索数量", - "topKSparse": "稀疏检索数量", - "topMFinal": "最终返回数量", - "enableRerank": "启用重排序", - "embeddingProvider": "嵌入模型提供商", - "rerankProvider": "重排序模型提供商", - "save": "保存设置", - "saveSuccess": "设置保存成功", - "saveFailed": "设置保存失败", - "tips": "提示: 修改检索设置后,将影响后续的知识库查询效果。" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/document.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/document.json deleted file mode 100644 index c90c29cc..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/document.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "title": "文档详情", - "backToKB": "返回知识库", - "info": { - "title": "文档信息", - "name": "文档名称", - "type": "文件类型", - "size": "文件大小", - "chunkCount": "分块数量", - "createdAt": "上传时间" - }, - "chunks": { - "title": "分块列表", - "empty": "暂无分块", - "index": "序号", - "content": "内容", - "charCount": "字符数", - "actions": "操作", - "view": "查看", - "edit": "编辑", - "delete": "删除", - "preview": "预览", - "search": "搜索分块", - "searchPlaceholder": "输入关键词搜索分块内容...", - "showing": "显示", - "deleteConfirm": "确定要删除该文本块吗?", - "deleteSuccess": "文本块删除成功", - "deleteFailed": "文本块删除失败" - }, - "edit": { - "title": "编辑分块", - "content": "分块内容", - "cancel": "取消", - "save": "保存", - "saveSuccess": "分块保存成功", - "saveFailed": "分块保存失败" - }, - "delete": { - "title": "删除分块", - "confirmText": "确定要删除此分块吗?", - "warning": "删除后将无法恢复,可能影响知识库检索效果。", - "cancel": "取消", - "confirm": "删除", - "deleteSuccess": "分块删除成功", - "deleteFailed": "分块删除失败" - }, - "view": { - "title": "分块详情", - "index": "序号", - "content": "内容", - "charCount": "字符数", - "vecDocId": "向量ID", - "close": "关闭" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/index.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/index.json deleted file mode 100644 index cac88bac..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/knowledge-base/index.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "title": "知识库管理", - "subtitle": "统一管理和查询知识库内容", - "list": { - "title": "知识库", - "subtitle": "管理您的所有知识库集合", - "create": "创建知识库", - "refresh": "刷新列表", - "empty": "暂无知识库", - "loading": "正在加载...", - "documents": "文档", - "chunks": "分块", - "sessionConfig": "会话配置", - "initError": "初始化失败" - }, - "card": { - "edit": "编辑", - "delete": "删除", - "open": "打开", - "docCount": "{count} 个文档", - "chunkCount": "{count} 个分块" - }, - "create": { - "title": "创建知识库", - "nameLabel": "知识库名称", - "namePlaceholder": "为知识库起个名字", - "descriptionLabel": "描述", - "descriptionPlaceholder": "简单描述这个知识库的用途...", - "emojiLabel": "图标", - "embeddingModelLabel": "嵌入模型 (Embedding Model)", - "rerankModelLabel": "重排序模型 (Rerank Model, 可选)", - "providerInfo": "提供商: {id} | 维度: {dimensions}", - "rerankProviderInfo": "提供商: {id}", - "cancel": "取消", - "submit": "创建", - "nameRequired": "请输入知识库名称" - }, - "edit": { - "title": "编辑知识库", - "submit": "保存" - }, - "delete": { - "title": "删除知识库", - "confirmText": "确定要删除知识库「{name}」吗?", - "warning": "此操作不可逆,所有文档、分块和关联配置都将被永久删除。", - "cancel": "取消", - "confirm": "删除" - }, - "emoji": { - "title": "选择图标", - "close": "关闭", - "categories": { - "books": "书籍与文档", - "emotions": "表情与情感", - "objects": "物品与工具", - "symbols": "符号与标志" - } - }, - "messages": { - "createSuccess": "知识库创建成功", - "createFailed": "创建失败", - "updateSuccess": "知识库更新成功", - "updateFailed": "更新失败", - "deleteSuccess": "知识库删除成功", - "deleteFailed": "删除失败", - "loadError": "加载知识库列表失败" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/persona.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/persona.json deleted file mode 100644 index e707a5d6..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/persona.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "page": { - "description": "管理人格角色设定" - }, - "buttons": { - "create": "创建人格", - "createFirst": "创建第一个人格", - "edit": "编辑", - "delete": "删除", - "cancel": "取消", - "save": "保存", - "move": "移动", - "addDialogPair": "添加对话对" - }, - "labels": { - "presetDialogs": "预设对话 ({count} 对)", - "createdAt": "创建时间", - "updatedAt": "更新时间" - }, - "form": { - "personaId": "人格 ID", - "systemPrompt": "系统提示词", - "customErrorMessage": "自定义报错回复信息(可选)", - "customErrorMessageHelp": "当该人格的 LLM 请求失败(例如连接失败)时,优先发送这条报错回复;留空则发送默认报错信息。", - "presetDialogs": "预设对话", - "presetDialogsHelp": "添加一些预设的对话来帮助机器人更好地理解角色设定。", - "userMessage": "用户消息", - "assistantMessage": "AI 回答", - "tools": "工具 / MCP 工具选择", - "toolsHelp": "为这个人格选择可用的外部工具。外部工具给了 AI 接触外部环境的能力,如搜索、计算、获取信息等。", - "toolsSelection": "工具选择操作", - "selectAllTools": "选择所有工具", - "clearAllTools": "清空选择", - "allSelected": "全选", - "mcpServersQuickSelect": "MCP 服务器快速选择", - "searchTools": "搜索工具", - "selectedTools": "已选择的工具", - "builtinToolDisabledHint": "暂不支持在这里启用和停用内置工具,请在配置文件中启用和停用工具对应的配置项。", - "noToolsAvailable": "暂无可用工具", - "noToolsFound": "未找到匹配的工具", - "loadingTools": "正在加载工具...", - "allToolsAvailable": "使用所有可用工具", - "noToolsSelected": "未选择任何工具", - "skills": "Skills 选择", - "skillsHelp": "为这个人格选择可用的 Skills。Skills 会给 AI 提供可复用的流程与规范。", - "skillsAllAvailable": "默认使用全部 Skills", - "skillsSelectSpecific": "选择指定 Skills", - "searchSkills": "搜索 Skills", - "selectedSkills": "已选择的 Skills", - "noSkillsAvailable": "暂无可用 Skills", - "noSkillsFound": "未找到匹配的 Skills", - "loadingSkills": "正在加载 Skills...", - "allSkillsAvailable": "使用所有可用 Skills", - "noSkillsSelected": "未选择任何 Skills", - "skillsRuntimeNoneWarning": "Computer Use 运行环境为无,Skills 可能无法正确被 Agent 运行,因为没有启用运行环境。", - "createInFolder": "将在「{folder}」中创建", - "rootFolder": "全部人格" - }, - "dialog": { - "create": { - "title": "创建新人格" - }, - "edit": { - "title": "编辑人格" - } - }, - "empty": { - "title": "暂无人格配置", - "description": "来创建一个吧!", - "folderEmpty": "此文件夹为空", - "folderEmptyDescription": "创建新的人格或文件夹开始使用" - }, - "validation": { - "required": "此字段为必填项", - "minLength": "最少需要 {min} 个字符", - "alphanumeric": "只能包含字母、数字、下划线和连字符", - "dialogRequired": "{type}不能为空", - "personaIdExists": "该人格名称已存在" - }, - "messages": { - "loadError": "加载人格列表失败", - "saveSuccess": "保存成功", - "saveError": "保存失败", - "deleteConfirm": "确定要删除人格 \"{id}\" 吗?此操作不可撤销。", - "deleteSuccess": "删除成功", - "deleteError": "删除失败" - }, - "persona": { - "personasTitle": "人格", - "toolsCount": "个工具", - "skillsCount": "个 Skills", - "contextMenu": { - "moveTo": "移动到..." - }, - "messages": { - "moveSuccess": "人格移动成功", - "moveError": "移动人格失败" - } - }, - "folder": { - "sidebarTitle": "文件夹", - "rootFolder": "根目录", - "foldersTitle": "文件夹", - "noFolders": "暂无文件夹", - "createButton": "新建文件夹", - "searchPlaceholder": "搜索文件夹...", - "form": { - "name": "文件夹名称", - "description": "描述(可选)" - }, - "validation": { - "nameRequired": "文件夹名称不能为空" - }, - "contextMenu": { - "open": "打开", - "rename": "重命名", - "moveTo": "移动到...", - "delete": "删除" - }, - "createDialog": { - "title": "创建新文件夹", - "createButton": "创建" - }, - "renameDialog": { - "title": "重命名文件夹" - }, - "deleteDialog": { - "title": "删除文件夹", - "message": "确定要删除文件夹 \"{name}\" 吗?", - "warning": "文件夹内的所有人格将被移动到根目录。" - }, - "messages": { - "createSuccess": "文件夹创建成功", - "createError": "创建文件夹失败", - "renameSuccess": "文件夹重命名成功", - "renameError": "重命名文件夹失败", - "deleteSuccess": "文件夹删除成功", - "deleteError": "删除文件夹失败" - } - }, - "moveDialog": { - "title": "移动到文件夹", - "description": "为 \"{name}\" 选择目标文件夹", - "success": "移动成功", - "error": "移动失败" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/platform.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/platform.json deleted file mode 100644 index 2b64b440..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/platform.json +++ /dev/null @@ -1,214 +0,0 @@ -{ - "title": "机器人", - "subtitle": "管理平台适配器实例,连接到不同的消息平台", - "adapters": "平台适配器", - "addAdapter": "创建机器人", - "emptyText": "暂无平台适配器,点击 创建机器人 添加", - "viewWebhook": "查看 Webhook 链接", - "webhookCopied": "Webhook URL 已复制到剪贴板", - "webhookCopyFailed": "复制失败,请手动复制", - "webhookDialog": { - "title": "Webhook 回调地址", - "description": "回调地址如下,请确保网络环境可以公网访问。也可以在日志中查看回调地址信息。建议填写 设置 -> 常规 中的「对外可达的回调接口地址」配置项。", - "close": "关闭" - }, - "details": { - "adapterType": "适配器类型", - "token": "Token", - "description": "描述" - }, - "logs": { - "title": "平台日志", - "expand": "展开", - "collapse": "收起" - }, - "dialog": { - "add": "新增", - "edit": "编辑", - "adapter": "机器人", - "refresh": "刷新", - "cancel": "取消", - "save": "保存", - "addPlatform": "创建机器人", - "connectTitle": "接入 {name}", - "viewTutorial": "查看接入教程", - "noTemplates": "暂无平台模板", - "idConflict": { - "title": "ID 冲突警告", - "message": "检测到 ID \"{id}\" 重复。请使用一个新的 ID。", - "confirm": "好的" - }, - "securityWarning": { - "title": "安全提醒", - "aiocqhttpTokenMissing": "为了增强连接安全性,强烈建议您设置 ws_reverse_token。未设置 Token 可能导致安全风险。", - "learnMore": "了解更多" - }, - "invalidPlatformId": "平台 ID 不能包含 ':' 或 '!'。" - }, - "createDialog": { - "step1Title": "选择消息平台类别", - "step1Hint": "想把机器人接入到哪里?如 QQ、企业微信、飞书、Discord、Telegram 等。", - "platformTypeLabel": "消息平台类别", - "configFileTitle": "配置文件", - "optional": "可选", - "configHint": "想如何配置机器人?配置文件包含了聊天模型、人格、知识库、插件范围等丰富的机器人配置项。", - "configDefaultHint": "默认使用默认配置文件 “default”。您也可以稍后配置。", - "useExistingConfig": "使用现有配置文件", - "selectConfigLabel": "选择配置文件", - "createNewConfig": "创建新配置文件", - "newConfigNameLabel": "新配置文件名称", - "newConfigTitle": "使用新的配置文件", - "newConfigLoadFailed": "无法加载默认配置模板", - "addRouteRule": "添加路由规则", - "viewMode": "查看", - "editMode": "编辑", - "noRouteRules": "该平台暂无路由规则,将使用默认配置文件", - "sessionIdPlaceholder": "会话ID或*", - "routeSource": { - "selectPlaceholder": "选择会话来源", - "noData": "暂无与该机器人的统一会话来源,在对应的消息平台与该机器人产生一次对话后才会显示在这里。", - "switchToKnown": "切换为从已有来源选择", - "switchToManual": "切换为手动输入" - }, - "allSessions": "全部会话", - "configMissing": "配置文件不存在", - "routeHint": "*消息下发时,根据会话来源按顺序从上到下匹配首个符合条件的配置文件。使用 * 表示匹配所有。使用 /sid 指令获取会话 ID。全部不匹配时将使用默认配置文件。", - "warningContinue": "无视警告并继续创建", - "warningEditAgain": "重新修改", - "configDrawerTitle": "配置文件管理", - "configDrawerIdLabel": "ID", - "configTableHeaders": { - "configId": "与此实例关联的配置文件 ID", - "scope": "在此实例下的应用范围" - }, - "routeTableHeaders": { - "source": "消息会话来源(消息类型:会话 ID)", - "config": "使用配置文件", - "actions": "操作" - }, - "messageTypeOptions": { - "all": "全部消息", - "group": "群组消息(GroupMessage)", - "friend": "私聊消息(FriendMessage)" - }, - "messageTypeLabels": { - "all": "全部消息", - "group": "群组消息", - "friend": "私聊消息" - } - }, - "messages": { - "updateSuccess": "更新成功!", - "addSuccess": "添加成功!", - "deleteSuccess": "删除成功!", - "statusUpdateSuccess": "状态更新成功!", - "deleteConfirm": "确定要删除平台适配器", - "configNotFoundOpenConfig": "目标配置文件不存在,已打开配置页面以便检查。", - "updateMissingPlatformId": "更新失败,缺少平台 ID。", - "platformUpdateFailed": "平台更新失败。", - "addSuccessWithConfig": "平台添加成功,配置文件已更新", - "configIdMissing": "无法获取配置文件ID。", - "routingUpdateFailed": "更新路由表失败: {message}", - "createConfigFailed": "创建新配置文件失败: {message}", - "platformIdMissing": "无法获取平台 ID。", - "routingSaveFailed": "保存路由表失败: {message}" - }, - "status": { - "enabled": "已启用", - "disabled": "已禁用", - "connecting": "连接中", - "connected": "已连接", - "disconnected": "已断开", - "error": "错误" - }, - "runtimeStatus": { - "running": "运行中", - "error": "发生错误", - "pending": "等待启动", - "stopped": "已停止", - "unknown": "未知", - "errors": "个错误" - }, - "platformQr": { - "title": "二维码登录", - "show": "查看二维码", - "status": "二维码状态", - "waiting": "等待二维码", - "close": "关闭" - }, - "registrationAction": { - "lark": { - "title": "一键扫码创建", - "show": "一键扫码创建", - "scanTitle": "请使用手机飞书扫码", - "feishu": "飞书国内版", - "lark": "Lark 海外版" - }, - "weixinOc": { - "title": "个人微信扫码登录", - "scanTitle": "请使用手机微信扫码", - "created": "登录成功", - "status": { - "idle": "未开始", - "starting": "正在获取二维码", - "pending": "等待扫码确认", - "created": "登录成功,记得点击下方保存按钮!", - "denied": "用户取消", - "expired": "已过期", - "error": "登录失败" - } - }, - "dingtalk": { - "title": "钉钉扫码创建", - "scanTitle": "请使用手机钉钉扫码", - "created": "创建成功" - }, - "qqOfficial": { - "title": "QQ 官方机器人扫码绑定", - "scanTitle": "请使用手机 QQ 扫码", - "created": "绑定成功", - "status": { - "idle": "未开始", - "starting": "正在获取二维码", - "pending": "等待扫码确认", - "created": "绑定成功,记得点击下方保存按钮!", - "denied": "用户取消", - "expired": "已过期", - "error": "绑定失败" - } - }, - "start": "开始创建", - "created": "创建成功", - "startFailed": "发起扫码创建失败", - "pollFailed": "获取扫码创建状态失败", - "platformIdLabel": "机器人 ID", - "platformIdRequired": "机器人 ID 不能为空", - "platformIdInvalid": "机器人 ID 不能包含空格、':' 或 '!'", - "mode": { - "title": "选择创建方式", - "scan": "扫码一键创建", - "manual": "手动创建", - "larkManual": "手动创建(支持企业自部署飞书平台)" - }, - "scanTitle": "请使用手机飞书扫码", - "status": { - "idle": "未开始", - "starting": "正在发起", - "pending": "等待扫码确认", - "slow_down": "轮询降速", - "created": "创建成功,记得点击下方保存按钮!", - "denied": "用户取消", - "expired": "已过期", - "error": "创建失败" - } - }, - "errorDialog": { - "title": "错误详情", - "platformId": "平台 ID", - "errorCount": "错误数量", - "lastError": "最近错误", - "occurredAt": "发生时间", - "traceback": "错误堆栈", - "close": "关闭" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/provider.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/provider.json deleted file mode 100644 index 63809fbc..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/provider.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "title": "模型提供商", - "subtitle": "可以在“对话”中配置对话模型。此外,“Agent 执行器”包含了 Dify、Coze、阿里云百炼应用等第三方服务的集成。", - "providers": { - "title": "模型提供商", - "settings": "设置", - "addProvider": "新增模型提供商", - "providerType": "提供商类型", - "tabs": { - "all": "全部", - "chatCompletion": "对话", - "agentRunner": "Agent 执行器", - "speechToText": "语音转文字", - "textToSpeech": "文字转语音", - "embedding": "嵌入(Embedding)", - "rerank": "重排序(Rerank)" - }, - "empty": { - "all": "暂无模型提供商,点击 新增模型提供商 添加", - "typed": "暂无{type}类型的模型提供商,点击 新增模型提供商 添加" - }, - "description": { - "openai": "也支持所有兼容 OpenAI API 的模型提供商。", - "kimi_code": "Kimi 的 CodingPlan / Kimi Code 专用接入,使用特殊的 Anthropic API 兼容适配。", - "vllm_rerank": "也支持 Jina AI, Cohere, PPIO 等提供商。", - "default": "" - } - }, - "availability": { - "title": "模型提供商可用性", - "subtitle": "通过测试模型对话可用性判断,可能产生API费用", - "refresh": "刷新状态", - "noData": "点击\"刷新状态\"按钮获取模型提供商可用性", - "available": "可用", - "unavailable": "不可用", - "pending": "检查中...", - "errorMessage": "错误信息", - "test": "测试" - }, - "logs": { - "title": "服务日志", - "expand": "展开", - "collapse": "收起" - }, - "dialogs": { - "addProvider": { - "title": "模型提供商", - "tabs": { - "basic": "对话", - "agentRunner": "Agent 执行器", - "speechToText": "语音转文字", - "textToSpeech": "文字转语音", - "embedding": "嵌入(Embedding)", - "rerank": "重排序(Rerank)" - }, - "noTemplates": "暂无该类型的提供商模板" - }, - "config": { - "addTitle": "新增", - "editTitle": "编辑", - "provider": "模型提供商", - "cancel": "取消", - "save": "保存" - }, - "settings": { - "title": "模型提供商设置", - "sessionSeparation": { - "title": "启用提供商会话隔离", - "description": "不同会话将可独立选择文本生成、TTS、STT 等模型提供商。" - }, - "close": "关闭" - } - }, - "messages": { - "success": { - "update": "更新成功!", - "add": "添加成功!", - "delete": "删除成功!", - "statusUpdate": "状态更新成功!", - "sessionSeparation": "会话隔离设置已更新" - }, - "error": { - "sessionSeparation": "获取会话隔离配置失败", - "fetchStatus": "获取模型提供商状态失败", - "testError": "测试 {id} 失败: {error}" - }, - "confirm": { - "delete": "确定要删除模型提供商 {id} 吗?" - } - }, - "providerTypes": { - "title": "提供商类型" - }, - "providerSources": { - "title": "提供商源", - "add": "新增", - "delete": "删除提供商源", - "empty": "暂无提供商源", - "selectHint": "请选择一个提供商源", - "selectCreated": "选择已创建的提供商源", - "save": "保存配置", - "saveAndFetchModels": "保存并获取模型", - "fetchModels": "获取模型列表", - "saveSuccess": "提供商源保存成功", - "saveError": "提供商源保存失败", - "deleteConfirm": "确定要删除提供商源 {id} 吗?这将同时删除关联的所有模型配置。", - "deleteSuccess": "提供商源删除成功", - "deleteError": "提供商源删除失败", - "enabled": "已启用", - "disabled": "已禁用", - "advancedConfig": "高级配置...", - "fields": { - "name": "名称", - "apiKey": "API Key", - "baseUrl": "Base URL" - }, - "hints": { - "id": "提供商源唯一 ID(不是提供商 ID)", - "key": "API 密钥", - "apiBase": "自定义 API 端点 URL", - "proxy": "HTTP/HTTPS 代理地址,格式如 http://127.0.0.1:7890。仅对该提供商的 API 请求生效,不影响 Docker 内网通信。" - }, - "labels": { - "proxy": "代理地址" - } - }, - "models": { - "title": "模型", - "available": "可用模型", - "configured": "已配置的模型", - "empty": "暂无已配置的模型,点击上方的\"获取模型列表\"添加", - "noModelsFound": "未找到可用模型", - "fetchError": "获取模型列表失败", - "addSuccess": "模型 {model} 添加成功", - "deleteConfirm": "确定要删除模型 {id} 吗?", - "deleteSuccess": "模型删除成功", - "deleteError": "模型删除失败", - "testSuccess": "模型 {id} 测试通过", - "testSuccessWithLatency": "模型 {id} 测试通过,延迟 {latency} ms", - "testError": "模型测试失败", - "searchPlaceholder": "搜索模型或 ID", - "manualAddButton": "自定义模型", - "manualDialogTitle": "添加自定义模型", - "manualDialogModelLabel": "模型 ID(如 gpt-4.1-mini)", - "manualDialogPreviewLabel": "显示 ID(自动生成)", - "manualDialogPreviewHint": "生成规则:源ID/模型ID", - "manualModelRequired": "请输入模型 ID", - "manualModelExists": "该模型已存在", - "configure": "配置", - "testButton": "测试模型", - "metadata": { - "image": "图像输入", - "audio": "音频输入", - "toolUse": "工具使用", - "reasoning": "推理", - "context": "{tokens} 上下文窗口", - "available": "模型 metadata 支持{capability}", - "enabled": "已启用{capability}", - "supportedDisabled": "模型 metadata 支持{capability},但当前提供商的模型能力未启用。" - }, - "tooltips": { - "providerId": "提供商 ID", - "modelId": "模型 ID" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/session-management.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/session-management.json deleted file mode 100644 index 7fb1d0eb..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/session-management.json +++ /dev/null @@ -1,158 +0,0 @@ -{ - "title": "自定义规则", - "subtitle": "为特定会话设置自定义规则,优先级高于全局配置", - "buttons": { - "refresh": "刷新", - "edit": "编辑", - "editRule": "编辑规则", - "deleteAllRules": "删除所有规则", - "addRule": "添加规则", - "save": "保存", - "cancel": "取消", - "delete": "删除", - "clear": "清除", - "next": "下一步", - "editCustomName": "编辑备注", - "batchDelete": "批量删除" - }, - "customRules": { - "title": "自定义规则", - "rulesCount": "条规则", - "hasRules": "已配置", - "noRules": "暂无自定义规则", - "noRulesDesc": "点击「添加规则」为特定会话配置自定义规则", - "serviceConfig": "服务配置", - "pluginConfig": "插件配置", - "kbConfig": "知识库配置", - "providerConfig": "模型配置", - "configured": "已配置", - "noCustomName": "未设置备注" - }, - "quickEditName": { - "title": "编辑备注名" - }, - "search": { - "placeholder": "搜索会话..." - }, - "table": { - "headers": { - "umoInfo": "消息会话来源", - "rulesOverview": "规则概览", - "actions": "操作" - } - }, - "persona": { - "none": "跟随配置文件" - }, - "provider": { - "followConfig": "跟随配置文件" - }, - "addRule": { - "title": "添加自定义规则", - "description": "选择一个消息会话来源 (UMO) 来配置自定义规则。自定义规则的优先级高于该来源所属的配置文件中的全局规则。可以使用 /sid 指令获取该来源的 UMO 信息。", - "selectUmo": "选择会话", - "noUmos": "暂无可用会话" - }, - "ruleEditor": { - "title": "编辑自定义规则", - "description": "为此会话配置自定义规则,这些规则将优先于全局配置生效。", - "serviceConfig": { - "title": "服务配置", - "sessionEnabled": "启用该消息会话来源的消息处理", - "llmEnabled": "启用 LLM", - "ttsEnabled": "启用 TTS", - "customName": "消息会话来源备注名称" - }, - "providerConfig": { - "title": "模型配置", - "chatProvider": "聊天模型", - "sttProvider": "语音识别模型", - "ttsProvider": "语音合成模型" - }, - "personaConfig": { - "title": "人格配置", - "selectPersona": "选择人格", - "hint": "应用人格配置后,将会强制该来源的所有对话使用该人格。" - }, - "pluginConfig": { - "title": "插件配置", - "disabledPlugins": "禁用的插件", - "hint": "选择要在此会话中禁用的插件。未选择的插件将保持启用状态。" - }, - "kbConfig": { - "title": "知识库配置", - "selectKbs": "选择知识库", - "topK": "返回结果数量 (Top K)", - "enableRerank": "启用重排序" - } - }, - "deleteConfirm": { - "title": "确认删除", - "message": "确定要删除此会话的所有自定义规则吗?删除后将恢复使用全局配置。" - }, - "batchDeleteConfirm": { - "title": "确认批量删除", - "message": "确定要删除选中的 {count} 条规则吗?删除后将恢复使用全局配置。" - }, - "batchOperations": { - "title": "批量操作", - "hint": "快速批量修改会话配置", - "scope": "应用范围", - "scopeSelected": "选中的会话", - "scopeAll": "所有会话", - "scopeGroup": "所有群聊", - "scopePrivate": "所有私聊", - "llmStatus": "LLM 状态", - "ttsStatus": "TTS 状态", - "chatProvider": "聊天模型", - "ttsProvider": "TTS 模型", - "apply": "应用更改" - }, - "groups": { - "title": "分组管理", - "count": "{count} 个分组", - "addToGroup": "添加到分组", - "create": "新建分组", - "edit": "编辑分组", - "name": "分组名称", - "sessionsCount": "{count} 个会话", - "empty": "暂无分组,点击「新建分组」创建", - "availableSessions": "可选会话 ({count})", - "selectedSessions": "已选会话 ({count})", - "searchPlaceholder": "搜索...", - "noMatch": "无匹配项", - "noMembers": "暂无成员", - "customGroupDivider": "── 自定义分组 ──", - "customGroupOption": "📁 {name} ({count})", - "groupOption": "{name} ({count} 个会话)", - "deleteConfirm": "确定要删除分组 \"{name}\" 吗?" - }, - "status": { - "enabled": "启用", - "disabled": "禁用" - }, - "messages": { - "refreshSuccess": "数据已刷新", - "loadError": "加载数据失败", - "saveSuccess": "保存成功", - "saveError": "保存失败", - "clearSuccess": "已清除", - "clearError": "清除失败", - "deleteSuccess": "删除成功", - "deleteError": "删除失败", - "noChanges": "没有需要保存的更改", - "batchDeleteSuccess": "批量删除成功", - "batchDeleteError": "批量删除失败", - "selectSessionsFirst": "请先选择要操作的会话", - "selectAtLeastOneConfig": "请至少选择一项要修改的配置", - "batchUpdateSuccess": "批量更新成功", - "partialUpdateFailed": "部分更新失败", - "batchUpdateError": "批量更新失败", - "groupNameRequired": "分组名称不能为空", - "saveGroupError": "保存分组失败", - "deleteGroupError": "删除分组失败", - "selectSessionsToAddFirst": "请先选择要添加的会话", - "addToGroupSuccess": "已添加 {count} 个会话到分组", - "addToGroupError": "添加失败" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/settings.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/settings.json deleted file mode 100644 index 9784aa97..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/settings.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "page": { - "title": "设置" - }, - "sections": { - "general": { - "title": "常规" - }, - "appearance": { - "title": "外观" - }, - "network": { - "title": "网络" - }, - "security": { - "title": "安全" - }, - "maintenance": { - "title": "维护" - }, - "openapi": { - "title": "OpenAPI" - } - }, - "systemConfig": { - "restartRequired": "系统配置已保存。部分配置需要重启 AstrBot 后才会生效。", - "groups": { - "runtime": { - "title": "基础运行", - "subtitle": "时区与外部回调地址。" - }, - "network": { - "title": "代理与依赖源", - "subtitle": "运行时代理、Python 依赖安装参数和 PyPI 源。" - }, - "webuiSecurity": { - "title": "WebUI 安全", - "subtitle": "HTTPS、代理头、登录限速与 TOTP。" - }, - "logs": { - "title": "日志", - "subtitle": "控制台日志、文件日志和 Trace 文件日志。" - }, - "tempStorage": { - "title": "缓存", - "subtitle": "临时目录自动清理阈值。" - }, - "t2iRendering": { - "title": "文本转图像", - "subtitle": "文本转图像渲染策略、端点和自定义模板。" - } - }, - "messages": { - "loadFailed": "加载系统配置失败", - "saveSuccess": "系统配置已保存", - "saveFailed": "保存系统配置失败" - } - }, - "network": { - "title": "网络", - "githubProxy": { - "title": "GitHub 加速地址", - "subtitle": "设置下载插件或者更新 AstrBot 时所用的 GitHub 加速地址。这在中国大陆的网络环境有效。可以自定义,输入结果实时生效。所有地址均不保证稳定性,如果在更新插件/项目时出现报错,请首先检查加速地址是否能正常使用。", - "label": "选择 GitHub 加速地址" - }, - "proxySelector": { - "title": "GitHub 加速", - "noProxy": "不使用 GitHub 加速", - "useProxy": "使用 GitHub 加速", - "testConnection": "测试代理连通性", - "available": "可用", - "unavailable": "不可用", - "custom": "自定义" - } - }, - "theme": { - "title": "主题", - "subtitle": "自定义主题主色与辅助色,数据保存在浏览器本地。", - "customize": { - "title": "主题颜色", - "primary": "主色", - "secondary": "辅助色", - "reset": "恢复默认" - } - }, - "system": { - "title": "系统", - "restart": { - "title": "重启", - "subtitle": "重启 AstrBot", - "confirm": "确定要重启 AstrBot 吗?重启期间 WebUI 会短暂不可用。", - "button": "重启" - }, - "backup": { - "title": "数据备份与恢复", - "subtitle": "导出或导入 AstrBot 的所有数据,方便迁移到新服务器", - "button": "备份管理" - }, - "cleanup": { - "title": "日志与缓存清理", - "subtitle": "查看当前日志和缓存占用,并一键清理。", - "refresh": "刷新占用", - "clean": "清理", - "cleanAll": "全部清理", - "panel": { - "title": "清理详情", - "subtitle": "当前占用 {size}" - }, - "fileCount": "{count} 个文件", - "confirm": "确定要清理 {target} 吗?", - "targetNames": { - "cache": "缓存", - "logs": "日志", - "all": "日志和缓存" - }, - "targets": { - "cache": { - "title": "缓存", - "subtitle": "清理临时文件、插件市场缓存和技能缓存。", - "button": "清理缓存" - }, - "logs": { - "title": "日志", - "subtitle": "清理历史日志,并清空当前正在写入的日志文件内容。", - "button": "清理日志" - } - }, - "messages": { - "statusFailed": "加载存储占用失败", - "cleanupSuccess": "已清理 {count} 个文件,释放 {size}", - "cleanupFailed": "清理失败" - } - } - }, - "sidebar": { - "title": "侧边栏", - "customize": { - "title": "自定义侧边栏", - "subtitle": "拖拽以调整模块顺序,或者将模块移入/移出\"更多功能\"分组。设置仅保存在浏览器本地。", - "reset": "恢复默认", - "mainItems": "主要模块", - "moreItems": "更多功能" - } - }, - "backup": { - "dialog": { - "title": "备份管理" - }, - "tabs": { - "export": "导出备份", - "import": "导入备份", - "list": "备份列表" - }, - "export": { - "title": "创建备份", - "description": "将所有数据导出为 ZIP 备份文件,包括数据库、知识库、配置、附件和 Skills。", - "includes": "备份包含:主数据库、知识库(元数据+向量索引+文档)、配置文件、附件文件、Skills", - "button": "开始导出", - "processing": "正在导出...", - "wait": "请稍候,正在打包数据...", - "completed": "导出完成!", - "download": "下载备份", - "another": "创建新备份", - "failed": "导出失败", - "retry": "重试" - }, - "import": { - "title": "导入备份", - "warning": "⚠️ 导入将会清空并覆盖现有数据!请确保已备份当前数据。", - "selectFile": "选择备份文件 (.zip)", - "uploadAndCheck": "上传并检查", - "uploading": "正在上传...", - "uploadWait": "请稍候,正在上传备份文件...", - "uploadInit": "正在初始化上传...", - "uploadingChunks": "正在上传分片...", - "uploadComplete": "上传完成,正在合并文件...", - "checking": "正在检查备份文件...", - "invalidBackup": "无效的备份文件", - "backupContents": "备份内容", - "tables": "个数据表", - "knowledgeBases": "知识库", - "configFiles": "配置文件", - "confirmImport": "确认导入", - "button": "开始导入", - "processing": "正在导入...", - "wait": "请稍候,正在恢复数据...", - "completed": "导入完成!", - "restartRequired": "数据已成功导入。建议立即重启 AstrBot 以使所有更改生效。", - "restartNow": "立即重启", - "failed": "导入失败", - "retry": "重试", - "version": { - "backupVersion": "备份版本", - "currentVersion": "当前版本", - "backupTime": "备份时间", - "matchTitle": "✅ 版本匹配", - "matchMessage": "导入将会清空并覆盖现有的所有数据,包括:\n• 主数据库(对话记录、配置等)\n• 知识库数据\n• 插件及插件数据\n• 配置文件\n• Skills\n\n此操作不可撤销!是否确认继续?", - "minorDiffTitle": "⚠️ 版本差异警告", - "minorDiffMessage": "小版本差异通常是兼容的,但可能存在少量数据结构变化。\n导入将会清空并覆盖现有的所有数据!\n\n是否确认继续导入?", - "majorDiffTitle": "⛔ 无法导入", - "majorDiffMessage": "主版本号不同,跨主版本导入可能导致数据损坏。\n请使用相同主版本的 AstrBot 进行导入。" - } - }, - "list": { - "empty": "暂无备份文件", - "refresh": "刷新列表", - "confirmDelete": "确定要删除这个备份文件吗?此操作不可撤销。", - "uploaded": "已上传", - "restore": "恢复此备份", - "rename": "重命名", - "renameTitle": "重命名备份文件", - "newName": "新文件名", - "renameHint": "文件名只能包含字母、数字、下划线、连字符和点", - "renameRequired": "请输入文件名", - "renameInvalidChars": "文件名包含非法字符", - "renameFailed": "重命名失败", - "ftpHint": "对于较大的备份文件,也可以通过 FTP/SFTP 等方式直接上传到 data/backups 目录" - } - }, - "apiKey": { - "title": "API Key", - "manageTitle": "开发者访问密钥", - "subtitle": "为外部开发者创建 API Key,用于调用开放 HTTP API。", - "name": "Key 名称", - "expiresInDays": "有效期", - "expiryOptions": { - "day1": "1 天", - "day7": "7 天", - "day30": "30 天", - "day90": "90 天", - "permanent": "永久" - }, - "permanentWarning": "永久有效的 API Key 风险较高,请妥善保存并建议仅在必要场景使用。", - "scopes": "权限范围", - "create": "创建 API Key", - "revoke": "吊销", - "delete": "删除", - "copy": "复制", - "docsLink": "查看文档", - "plaintextHint": "请立即保存该 Key,关闭后将无法再次查看明文。", - "empty": "暂无 API Key", - "status": { - "active": "有效", - "inactive": "无效" - }, - "table": { - "name": "名称", - "prefix": "前缀", - "scopes": "权限", - "status": "状态", - "lastUsed": "最近使用", - "createdAt": "创建时间", - "actions": "操作" - }, - "messages": { - "loadFailed": "加载 API Key 失败", - "scopeRequired": "请至少选择一个权限", - "createSuccess": "API Key 创建成功", - "createFailed": "创建 API Key 失败", - "revokeSuccess": "API Key 已吊销", - "revokeFailed": "吊销 API Key 失败", - "deleteSuccess": "API Key 已删除", - "deleteFailed": "删除 API Key 失败", - "copySuccess": "已复制 API Key", - "copyFailed": "复制 API Key 失败" - } - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/stats.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/stats.json deleted file mode 100644 index 6dc71751..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/stats.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "header": { - "eyebrow": "Dashboard", - "title": "系统统计", - "subtitle": "平台、消息与模型调用的统一视图。", - "notUpdated": "尚未更新" - }, - "ranges": { - "oneDay": "1 天", - "threeDays": "3 天", - "oneWeek": "1 周" - }, - "rangeLabels": { - "oneDay": "最近 1 天", - "threeDays": "最近 3 天", - "oneWeek": "最近 1 周" - }, - "overviewCards": { - "platformCount": { - "label": "平台实例数", - "note": "当前配置并已运行的平台实例" - }, - "messageCount": { - "label": "消息总数", - "note": "累计消息量" - }, - "todayModelCalls": { - "label": "今日模型调用", - "note": "词元(Tokens)" - }, - "cpu": { - "label": "CPU", - "note": "程序 CPU 占用" - }, - "memory": { - "label": "进程内存", - "note": "系统内存 {systemMemory}" - }, - "uptime": { - "label": "运行时间", - "note": "启动于 {startTime}" - } - }, - "messageOverview": { - "title": "消息概览", - "subtitle": "机器人消息数据统计" - }, - "messageTrend": { - "title": "消息趋势", - "subtitle": "{range}按小时聚合", - "totalMessages": "总消息数" - }, - "platformRanking": { - "title": "平台消息排名", - "subtitle": "{range}按聚合消息总量排序" - }, - "modelCalls": { - "title": "模型调用", - "subtitle": "模型调用数据统计" - }, - "modelTrend": { - "title": "模型调用趋势", - "subtitle": "模型调用词元(Tokens)趋势统计" - }, - "modelTotal": { - "title": "{range}模型调用总量", - "callCount": "共 {count} 次调用", - "avgTtft": "平均首字延迟(TTFT)", - "avgDuration": "平均响应时间", - "avgTpm": "平均每分钟输出(TPM)", - "successRate": "调用成功率" - }, - "modelRanking": { - "title": "{range}模型调用排名", - "subtitle": "模型调用词元(Tokens)排名" - }, - "sessionRanking": { - "title": "{range}会话词元(Tokens)Top 10", - "subtitle": "词元(Tokens)用量最大的会话排名" - }, - "empty": { - "platformStats": "当前没有平台消息统计数据。", - "modelCalls": "{range}还没有模型调用数据。", - "sessionCalls": "{range}还没有会话调用数据。" - }, - "chart": { - "messages": "消息", - "others": "其他" - }, - "units": { - "tokens": "词元(Tokens)", - "tpm": "TPM", - "gb": "GB", - "mb": "MB", - "ms": "ms", - "hoursShort": "h", - "minutesShort": "m", - "secondsShort": "s" - }, - "errors": { - "loadFailed": "统计数据加载失败,请稍后重试。", - "rangeFailed": "统计范围切换失败,请稍后重试。" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/subagent.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/subagent.json deleted file mode 100644 index cd49ae43..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/subagent.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "header": { - "eyebrow": "Subagent Orchestration" - }, - "page": { - "title": "子代理编排", - "beta": "实验性", - "subtitle": "主代理可直接使用自身工具,也可将部分任务委派给各个子代理,完成更复杂的任务,避免主代理上下文过长。" - }, - "actions": { - "refresh": "刷新", - "save": "保存", - "add": "新增子代理", - "expand": "展开设置", - "collapse": "收起设置", - "delete": "删除", - "close": "关闭" - }, - "overview": { - "totalAgents": "子代理总数", - "totalAgentsNote": "当前已配置的子代理数量", - "enabledAgents": "启用中的 Agent", - "enabledAgentsNote": "会参与 handoff 编排的子代理", - "mainOrchestration": "主编排状态", - "boundPersonas": "已绑定 Persona", - "boundPersonasNote": "完成角色绑定的子代理数量" - }, - "switches": { - "enable": "启用子代理编排", - "enableHint": "启用子代理功能", - "dedupe": "主 LLM 去重重复工具(与子代理重叠的工具将被隐藏)", - "dedupeHint": "从主代理中移除与子代理重复的工具" - }, - "description": { - "disabled": "未启动子代理编排功能。", - "enabled": "子代理将作为工具放在主代理的工具集中,主代理会在适当的时机调用子代理完成任务。" - }, - "section": { - "title": "子代理配置", - "subtitle": "为主代理配置可委派的子代理、人格与描述信息", - "globalSettings": "全局设置", - "agentSetup": "Agent 设置" - }, - "cards": { - "statusEnabled": "启用", - "statusDisabled": "停用", - "unnamed": "未命名子代理", - "transferPrefix": "transfer_to_{name}", - "switchLabel": "启用", - "previewTitle": "预览:主 LLM 将看到的 handoff 工具", - "personaChip": "Persona: {id}", - "noDescription": "暂无描述", - "personaPreview": "人格预览", - "previewHint": "这里展示当前所选 Persona 的预览,便于确认 handoff 目标。" - }, - "form": { - "nameLabel": "Agent 名称(用于 transfer_to_{name})", - "nameHint": "建议使用英文小写+下划线,且全局唯一", - "providerLabel": "Chat Provider(可选)", - "providerHint": "留空表示跟随全局默认 provider。", - "personaLabel": "选择人格设定", - "personaHint": "子代理 将直接继承所选 Persona 的系统设定与工具。在人格设定页管理和新建人格。", - "personaPreview": "人格预览", - "descriptionLabel": "对主 LLM 的描述(用于决定是否 handoff)", - "descriptionHint": "这段会作为 transfer_to_* 工具的描述给主 LLM 看,建议简短明确。" - }, - "messages": { - "loadConfigFailed": "获取配置失败", - "loadPersonaFailed": "获取 Persona 列表失败", - "unsavedChangesNotice": "当前页面有未保存修改,请在离开前保存。", - "unsavedChangesLeaveConfirm": "当前页面有未保存修改,离开后将丢失这些更改。是否继续离开?", - "unsavedChangesReloadConfirm": "当前页面有未保存修改,刷新配置会丢失这些更改。是否继续刷新?", - "nameMissing": "存在未填写名称的子代理", - "nameInvalid": "子代理名称不合法:仅允许英文小写字母/数字/下划线,且需以字母开头", - "nameDuplicate": "子代理名称重复:{name}", - "personaMissing": "子代理 {name} 未选择 Persona", - "saveSuccess": "保存成功", - "saveFailed": "保存失败", - "nameRequired": "名称必填", - "namePattern": "仅支持小写字母、数字和下划线" - }, - "empty": { - "title": "未配置子代理", - "subtitle": "添加一个新的子代理以开始", - "action": "创建第一个 Agent" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/tool-use.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/tool-use.json deleted file mode 100644 index a2f14026..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/tool-use.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "title": "函数工具管理", - "subtitle": "管理 MCP 服务器和查看可用的函数工具", - "tooltip": { - "info": "函数调用和 MCP 是什么?", - "marketplace": "浏览和安装来自社区的 MCP 服务器", - "serverConfig": "MCP 服务器(stdio)配置支持以下字段:\ncommand: 命令名称 (例如 python 或 uv)\nargs: 命令参数数组 (例如 [\"run\", \"server.py\"])\nenv: 环境变量对象 (例如 {\"api_key\": \"abc\"})\ncwd: 工作目录路径 (例如 /path/to/server)\nencoding: 输出编码 (默认 utf-8)\nencoding_error_handler: The text encoding error handler. Defaults to strict.\n其他字段请参考 MCP 文档\n⚠️ 如果您使用 Docker 部署 AstrBot, 请务必将 MCP 服务器装在 AstrBot 挂载好的 data 目录下" - }, - "tabs": { - "local": "本地服务器", - "marketplace": "MCP 市场" - }, - "mcpServers": { - "title": "MCP 服务器", - "description": "管理 MCP 服务器", - "buttons": { - "refresh": "刷新", - "add": "新增服务器", - "useTemplateStdio": "Stdio 模板", - "useTemplateStreamableHttp": "Streamable HTTP 模板", - "useTemplateSse": "SSE 模板", - "sync": "同步服务器" - }, - "empty": "暂无 MCP 服务器,点击 新增服务器 添加", - "status": { - "noTools": "无可用工具", - "availableTools": "可用工具", - "configSummary": "配置: {keys}", - "noConfig": "未设置配置" - } - }, - "functionTools": { - "title": "函数工具", - "buttons": { - "view": "查看工具" - }, - "search": "搜索函数工具", - "empty": "没有可用的函数工具", - "description": "功能描述", - "parameters": "参数列表", - "noParameters": "此工具没有参数", - "filter": { - "showBuiltin": "显示内置工具" - }, - "table": { - "paramName": "参数名", - "type": "类型", - "description": "描述", - "required": "必填", - "origin": "来源", - "originName": "来源名称", - "readonly": "只读", - "permission": "权限", - "permissionAdmin": "管理员", - "permissionMember": "全部用户", - "permissionEveryone": "全部用户", - "permissionBuiltin": "系统内置", - "actions": "操作" - }, - "configTags": { - "tooltipTitle": "该工具在配置文件 {config} 中启用,因为:", - "conditions": { - "truthy": "启用了 {key}", - "equals": "{key} = {expected}", - "in": "{key} 命中了 {expected}", - "fallback": "{key} 当前值为 {actual}" - } - }, - "summary": { - "total": "总数", - "active": "已启用", - "inactive": "未启用" - } - }, - "marketplace": { - "title": "MCP 服务器市场", - "search": "搜索服务器", - "buttons": { - "refresh": "刷新", - "detail": "详情", - "import": "导入" - }, - "loading": "正在加载 MCP 服务器市场...", - "empty": "暂无可用的 MCP 服务器", - "status": { - "availableTools": "可用工具 ({count})", - "noToolsInfo": "无可用工具信息" - } - }, - "dialogs": { - "addServer": { - "title": "新增 MCP 服务器", - "editTitle": "编辑 MCP 服务器", - "fields": { - "name": "服务器名称", - "nameRequired": "名称是必填项", - "enable": "启用服务器", - "config": "服务器配置" - }, - "errors": { - "configEmpty": "配置不能为空", - "jsonFormat": "JSON 格式错误: {error}", - "jsonParse": "JSON 解析错误: {error}" - }, - "buttons": { - "cancel": "取消", - "save": "保存", - "testConnection": "测试连接", - "sync": "同步" - }, - "tips": { - "timeoutConfig": "工具调用的超时时间请前往配置页面单独配置", - "transportRecommendation": "建议优先使用 Streamable HTTP 或 SSE 模式。stdio 模式会在 AstrBot 主机上启动本地进程,仅适合可信 MCP 服务器。" - } - }, - "serverDetail": { - "title": "服务器详情", - "installConfig": "安装配置", - "availableTools": "可用工具", - "buttons": { - "close": "关闭", - "importConfig": "导入配置" - } - }, - "confirmDelete": "确定要删除服务器 {name} 吗?", - "syncProvider": { - "title": "同步 MCP 服务器", - "subtitle": "从提供商同步 MCP 服务器配置到本地", - "steps": { - "selectProvider": "步骤 1: 选择提供商", - "configureAuth": "步骤 2: 配置认证", - "syncServers": "步骤 3: 同步服务器" - }, - "providers": { - "modelscope": "ModelScope", - "description": "ModelScope 是一个开源的模型社区,提供各种机器学习和AI服务的MCP服务器" - }, - "fields": { - "provider": "选择提供商", - "accessToken": "访问令牌", - "tokenRequired": "访问令牌是必填项", - "tokenHint": "请输入您的 ModelScope 访问令牌" - }, - "buttons": { - "cancel": "取消", - "previous": "上一步", - "next": "下一步", - "sync": "开始同步", - "getToken": "获取令牌" - }, - "status": { - "selectProvider": "请选择一个 MCP 服务器提供商", - "enterToken": "请输入访问令牌以继续", - "readyToSync": "准备同步服务器配置" - }, - "messages": { - "syncSuccess": "MCP 服务器同步成功!", - "syncError": "同步失败: {error}", - "tokenHelp": "如何获取 ModelScope 访问令牌?点击右侧按钮查看说明" - } - } - }, - "messages": { - "getServersError": "获取 MCP 服务器列表失败: {error}", - "getToolsError": "获取函数工具列表失败: {error}", - "saveSuccess": "保存成功!", - "saveError": "保存失败: {error}", - "deleteSuccess": "删除成功!", - "deleteError": "删除失败: {error}", - "updateSuccess": "更新成功!", - "updateError": "更新失败: {error}", - "getMarketError": "获取 MCP 市场服务器列表失败: {error}", - "importError": { - "noConfig": "此服务器没有可用配置", - "invalidFormat": "服务器配置格式不正确", - "failed": "导入配置失败: {error}" - }, - "configParseError": "配置解析错误: {error}", - "noAvailableConfig": "无可用配置", - "toggleToolSuccess": "工具状态切换成功!", - "toggleToolReadonly": "内置工具为只读,无法进行启用或停用操作。", - "toggleToolError": "工具状态切换失败: {error}", - "updateToolPermissionSuccess": "工具 {name} 权限已更新", - "updateToolPermissionFailed": "工具权限更新失败", - "updateToolPermissionBuiltin": "内置工具不支持单独配置权限", - "testError": "测试连接失败: {error}" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/trace.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/trace.json deleted file mode 100644 index a8923d3b..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/trace.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "追踪", - "autoScroll": { - "enabled": "自动滚动:开", - "disabled": "自动滚动:关" - }, - "hint": "当前仅记录部分 AstrBot 主 Agent 的模型调用路径,后续会不断完善。", - "recording": "记录中", - "paused": "已暂停" -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/features/welcome.json b/legacy-dashboard/src/i18n/locales/zh-CN/features/welcome.json deleted file mode 100644 index 0b58306e..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/features/welcome.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "greeting": { - "morning": "上午好,欢迎使用 AstrBot", - "afternoon": "下午好,欢迎使用 AstrBot", - "evening": "晚上好,欢迎使用 AstrBot", - "newYear": "新年快乐!" - }, - "subtitle": "可以先完成基础引导,平台和对话提供商都支持稍后再配置。", - "announcement": { - "title": "公告" - }, - "onboard": { - "title": "快速引导", - "subtitle": "欢迎页可直接完成初始化。", - "step1Title": "配置 AI 模型", - "step1Desc": "为 AstrBot 配置 AI 模型。", - "step2Title": "配置平台机器人", - "step2Desc": "将 AstrBot 连接到 QQ、飞书、企业微信、Telegram 等 IM 平台。", - "step3Title": "允许 Agent 使用电脑", - "step3Desc": "设置 Agent 是否可以访问和使用当前电脑。", - "step3HelpTitle": "允许 Agent 使用电脑", - "step3HelpItem1": "Agent 将可以访问和使用工作区目录(每个用户的工作区目录相互独立)以及 Skills 来执行更多复杂任务。", - "step3HelpItem2": "对于 AstrBot 管理员,会额外允许 Agent 执行 Shell 命令、Python 代码,以及访问本机的所有目录。", - "step3HelpClose": "关闭", - "step3SelectLabel": "电脑访问权限", - "step3Allow": "允许", - "step3Deny": "不允许", - "configure": "去配置", - "save": "保存", - "skip": "跳过", - "pending": "待处理", - "completed": "已完成", - "skipped": "已跳过", - "platformLoadFailed": "加载平台配置失败", - "providerLoadFailed": "加载提供商配置失败", - "providerUpdateFailed": "更新 default 配置文件默认对话提供商失败", - "providerDefaultUpdated": "已将 default 配置文件的默认对话提供商设置为 {id}", - "computerAccessUpdateFailed": "更新 Agent 电脑访问权限失败", - "computerAccessAllowed": "已允许 Agent 访问和使用电脑", - "computerAccessDenied": "已禁止 Agent 访问和使用电脑", - "step3HelpItem3": "如需更多细化权限与能力配置,可前往 配置文件 -> 普通配置 -> 使用电脑能力 继续设置。" - }, - "resources": { - "title": "相关资源", - "githubDesc": "给 AstrBot 点个 Star 吧!", - "docsTitle": "文档", - "docsDesc": "查阅 AstrBot 的官方文档。", - "afdianTitle": "爱发电", - "afdianDesc": "通过爱发电支持 AstrBot 团队。" - } -} diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/messages/errors.json b/legacy-dashboard/src/i18n/locales/zh-CN/messages/errors.json deleted file mode 100644 index 65588431..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/messages/errors.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "network": { - "timeout": "网络请求超时,请稍后重试", - "connection": "网络连接失败,请检查网络状态", - "server": "服务器错误,请联系技术支持", - "unavailable": "服务暂不可用", - "forbidden": "访问被拒绝" - }, - "validation": { - "required": "此字段为必填项", - "invalid": "输入格式不正确", - "tooLong": "输入内容过长", - "tooShort": "输入内容过短", - "email": "请输入有效的邮箱地址", - "url": "请输入有效的URL地址", - "number": "请输入有效的数字" - }, - "auth": { - "unauthorized": "未授权访问,请重新登录", - "forbidden": "权限不足,无法执行此操作", - "tokenExpired": "登录已过期,请重新登录", - "invalidCredentials": "用户名或密码错误" - }, - "file": { - "uploadFailed": "文件上传失败", - "invalidFormat": "不支持的文件格式", - "tooLarge": "文件大小超出限制", - "notFound": "文件未找到" - }, - "operation": { - "failed": "操作失败", - "cancelled": "操作已取消", - "notSupported": "不支持此操作", - "conflict": "操作冲突,请稍后重试" - }, - "browser": { - "audioNotSupported": "您的浏览器不支持音频播放。" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/messages/success.json b/legacy-dashboard/src/i18n/locales/zh-CN/messages/success.json deleted file mode 100644 index 0f7ecfc3..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/messages/success.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "operation": { - "saved": "保存成功", - "created": "创建成功", - "updated": "更新成功", - "deleted": "删除成功", - "uploaded": "上传成功", - "downloaded": "下载成功", - "imported": "导入成功", - "exported": "导出成功", - "copied": "复制成功", - "sent": "发送成功" - }, - "connection": { - "connected": "连接成功", - "authenticated": "登录成功", - "synchronized": "同步成功" - }, - "validation": { - "valid": "验证通过", - "completed": "操作完成" - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/locales/zh-CN/messages/validation.json b/legacy-dashboard/src/i18n/locales/zh-CN/messages/validation.json deleted file mode 100644 index 7810fe65..00000000 --- a/legacy-dashboard/src/i18n/locales/zh-CN/messages/validation.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "required": "此字段为必填项", - "email": "请输入有效的邮箱地址", - "url": "请输入有效的URL地址", - "number": "请输入有效的数字", - "min": "最小值为 {min}", - "max": "最大值为 {max}", - "minLength": "至少需要 {length} 个字符", - "maxLength": "最多允许 {length} 个字符", - "pattern": "格式不正确", - "unique": "该值已存在", - "confirm": "两次输入不一致", - "fileSize": "文件大小不能超过 {size}MB", - "fileType": "不支持的文件类型", - "required_field": "请填写必填字段", - "invalid_format": "格式无效", - "password_too_short": "密码至少需要8个字符", - "password_too_weak": "密码强度太弱", - "invalid_phone": "请输入有效的手机号码", - "invalid_date": "请输入有效的日期", - "date_range": "日期范围无效", - "upload_failed": "文件上传失败", - "network_error": "网络连接错误,请重试", - "operation_cannot_be_undone": "⚠️ 此操作无法撤销,请谨慎选择!" -} diff --git a/legacy-dashboard/src/i18n/tools/index.ts b/legacy-dashboard/src/i18n/tools/index.ts deleted file mode 100644 index f62267ce..00000000 --- a/legacy-dashboard/src/i18n/tools/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -// 导出核心组件 -export { I18nValidator } from '../validator'; -export { I18nLoader } from '../loader'; -export type * from '../types'; - -// 实用工具函数 -export function generateMissingKeys( - sourceTranslations: Record, - targetTranslations: Record -): string[] { - const missing: string[] = []; - - function traverse(source: any, target: any, path: string = '') { - for (const key in source) { - const currentPath = path ? `${path}.${key}` : key; - - if (typeof source[key] === 'object' && source[key] !== null) { - if (!target[key]) { - missing.push(currentPath); - } else { - traverse(source[key], target[key], currentPath); - } - } else { - if (!(key in target)) { - missing.push(currentPath); - } - } - } - } - - traverse(sourceTranslations, targetTranslations); - return missing; -} \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/translations.ts b/legacy-dashboard/src/i18n/translations.ts deleted file mode 100644 index e2a12c2d..00000000 --- a/legacy-dashboard/src/i18n/translations.ts +++ /dev/null @@ -1,278 +0,0 @@ -// 静态导入所有翻译文件 -// 这种方式确保构建时所有翻译都会被正确打包 - -// 中文翻译 -import zhCNCommon from './locales/zh-CN/core/common.json'; -import zhCNActions from './locales/zh-CN/core/actions.json'; -import zhCNStatus from './locales/zh-CN/core/status.json'; -import zhCNNavigation from './locales/zh-CN/core/navigation.json'; -import zhCNHeader from './locales/zh-CN/core/header.json'; -import zhCNShared from './locales/zh-CN/core/shared.json'; - -import zhCNChat from './locales/zh-CN/features/chat.json'; -import zhCNExtension from './locales/zh-CN/features/extension.json'; -import zhCNConversation from './locales/zh-CN/features/conversation.json'; -import zhCNSessionManagement from './locales/zh-CN/features/session-management.json'; -import zhCNToolUse from './locales/zh-CN/features/tool-use.json'; -import zhCNProvider from './locales/zh-CN/features/provider.json'; -import zhCNPlatform from './locales/zh-CN/features/platform.json'; -import zhCNConfig from './locales/zh-CN/features/config.json'; -import zhCNConfigMetadata from './locales/zh-CN/features/config-metadata.json'; -import zhCNConsole from './locales/zh-CN/features/console.json'; -import zhCNTrace from './locales/zh-CN/features/trace.json'; -import zhCNAbout from './locales/zh-CN/features/about.json'; -import zhCNSettings from './locales/zh-CN/features/settings.json'; -import zhCNAuth from './locales/zh-CN/features/auth.json'; -import zhCNChart from './locales/zh-CN/features/chart.json'; -import zhCNDashboard from './locales/zh-CN/features/dashboard.json'; -import zhCNCron from './locales/zh-CN/features/cron.json'; -import zhCNStats from './locales/zh-CN/features/stats.json'; -import zhCNAlkaidIndex from './locales/zh-CN/features/alkaid/index.json'; -import zhCNAlkaidKnowledgeBase from './locales/zh-CN/features/alkaid/knowledge-base.json'; -import zhCNAlkaidMemory from './locales/zh-CN/features/alkaid/memory.json'; -import zhCNKnowledgeBaseIndex from './locales/zh-CN/features/knowledge-base/index.json'; -import zhCNKnowledgeBaseDetail from './locales/zh-CN/features/knowledge-base/detail.json'; -import zhCNKnowledgeBaseDocument from './locales/zh-CN/features/knowledge-base/document.json'; -import zhCNPersona from './locales/zh-CN/features/persona.json'; -import zhCNCommand from './locales/zh-CN/features/command.json'; -import zhCNSubagent from './locales/zh-CN/features/subagent.json'; -import zhCNWelcome from './locales/zh-CN/features/welcome.json'; - -import zhCNErrors from './locales/zh-CN/messages/errors.json'; -import zhCNSuccess from './locales/zh-CN/messages/success.json'; -import zhCNValidation from './locales/zh-CN/messages/validation.json'; - -// English translation -import enUSCommon from './locales/en-US/core/common.json'; -import enUSActions from './locales/en-US/core/actions.json'; -import enUSStatus from './locales/en-US/core/status.json'; -import enUSNavigation from './locales/en-US/core/navigation.json'; -import enUSHeader from './locales/en-US/core/header.json'; -import enUSShared from './locales/en-US/core/shared.json'; - -import enUSChat from './locales/en-US/features/chat.json'; -import enUSExtension from './locales/en-US/features/extension.json'; -import enUSConversation from './locales/en-US/features/conversation.json'; -import enUSSessionManagement from './locales/en-US/features/session-management.json'; -import enUSToolUse from './locales/en-US/features/tool-use.json'; -import enUSProvider from './locales/en-US/features/provider.json'; -import enUSPlatform from './locales/en-US/features/platform.json'; -import enUSConfig from './locales/en-US/features/config.json'; -import enUSConfigMetadata from './locales/en-US/features/config-metadata.json'; -import enUSConsole from './locales/en-US/features/console.json'; -import enUSTrace from './locales/en-US/features/trace.json'; -import enUSAbout from './locales/en-US/features/about.json'; -import enUSSettings from './locales/en-US/features/settings.json'; -import enUSAuth from './locales/en-US/features/auth.json'; -import enUSChart from './locales/en-US/features/chart.json'; -import enUSDashboard from './locales/en-US/features/dashboard.json'; -import enUSCron from './locales/en-US/features/cron.json'; -import enUSStats from './locales/en-US/features/stats.json'; -import enUSAlkaidIndex from './locales/en-US/features/alkaid/index.json'; -import enUSAlkaidKnowledgeBase from './locales/en-US/features/alkaid/knowledge-base.json'; -import enUSAlkaidMemory from './locales/en-US/features/alkaid/memory.json'; -import enUSKnowledgeBaseIndex from './locales/en-US/features/knowledge-base/index.json'; -import enUSKnowledgeBaseDetail from './locales/en-US/features/knowledge-base/detail.json'; -import enUSKnowledgeBaseDocument from './locales/en-US/features/knowledge-base/document.json'; -import enUSPersona from './locales/en-US/features/persona.json'; -import enUSCommand from './locales/en-US/features/command.json'; -import enUSSubagent from './locales/en-US/features/subagent.json'; -import enUSWelcome from './locales/en-US/features/welcome.json'; - -import enUSErrors from './locales/en-US/messages/errors.json'; -import enUSSuccess from './locales/en-US/messages/success.json'; -import enUSValidation from './locales/en-US/messages/validation.json'; - -// Russian translation -import ruRUCommon from './locales/ru-RU/core/common.json'; -import ruRUActions from './locales/ru-RU/core/actions.json'; -import ruRUStatus from './locales/ru-RU/core/status.json'; -import ruRUNavigation from './locales/ru-RU/core/navigation.json'; -import ruRUHeader from './locales/ru-RU/core/header.json'; -import ruRUShared from './locales/ru-RU/core/shared.json'; - -import ruRUChat from './locales/ru-RU/features/chat.json'; -import ruRUExtension from './locales/ru-RU/features/extension.json'; -import ruRUConversation from './locales/ru-RU/features/conversation.json'; -import ruRUSessionManagement from './locales/ru-RU/features/session-management.json'; -import ruRUToolUse from './locales/ru-RU/features/tool-use.json'; -import ruRUProvider from './locales/ru-RU/features/provider.json'; -import ruRUPlatform from './locales/ru-RU/features/platform.json'; -import ruRUConfig from './locales/ru-RU/features/config.json'; -import ruRUConfigMetadata from './locales/ru-RU/features/config-metadata.json'; -import ruRUConsole from './locales/ru-RU/features/console.json'; -import ruRUTrace from './locales/ru-RU/features/trace.json'; -import ruRUAbout from './locales/ru-RU/features/about.json'; -import ruRUSettings from './locales/ru-RU/features/settings.json'; -import ruRUAuth from './locales/ru-RU/features/auth.json'; -import ruRUChart from './locales/ru-RU/features/chart.json'; -import ruRUDashboard from './locales/ru-RU/features/dashboard.json'; -import ruRUCron from './locales/ru-RU/features/cron.json'; -import ruRUStats from './locales/ru-RU/features/stats.json'; -import ruRUAlkaidIndex from './locales/ru-RU/features/alkaid/index.json'; -import ruRUAlkaidKnowledgeBase from './locales/ru-RU/features/alkaid/knowledge-base.json'; -import ruRUAlkaidMemory from './locales/ru-RU/features/alkaid/memory.json'; -import ruRUKnowledgeBaseIndex from './locales/ru-RU/features/knowledge-base/index.json'; -import ruRUKnowledgeBaseDetail from './locales/ru-RU/features/knowledge-base/detail.json'; -import ruRUKnowledgeBaseDocument from './locales/ru-RU/features/knowledge-base/document.json'; -import ruRUPersona from './locales/ru-RU/features/persona.json'; -import ruRUCommand from './locales/ru-RU/features/command.json'; -import ruRUSubagent from './locales/ru-RU/features/subagent.json'; -import ruRUWelcome from './locales/ru-RU/features/welcome.json'; - -import ruRUErrors from './locales/ru-RU/messages/errors.json'; -import ruRUSuccess from './locales/ru-RU/messages/success.json'; -import ruRUValidation from './locales/ru-RU/messages/validation.json'; - -// 组装翻译对象 -export const translations = { - 'zh-CN': { - core: { - common: zhCNCommon, - actions: zhCNActions, - status: zhCNStatus, - navigation: zhCNNavigation, - header: zhCNHeader, - shared: zhCNShared - }, - features: { - chat: zhCNChat, - extension: zhCNExtension, - conversation: zhCNConversation, - 'session-management': zhCNSessionManagement, - tooluse: zhCNToolUse, - provider: zhCNProvider, - platform: zhCNPlatform, - config: zhCNConfig, - 'config-metadata': zhCNConfigMetadata, - console: zhCNConsole, - trace: zhCNTrace, - about: zhCNAbout, - settings: zhCNSettings, - auth: zhCNAuth, - chart: zhCNChart, - dashboard: zhCNDashboard, - cron: zhCNCron, - stats: zhCNStats, - alkaid: { - index: zhCNAlkaidIndex, - 'knowledge-base': zhCNAlkaidKnowledgeBase, - memory: zhCNAlkaidMemory - }, - 'knowledge-base': { - index: zhCNKnowledgeBaseIndex, - detail: zhCNKnowledgeBaseDetail, - document: zhCNKnowledgeBaseDocument - }, - persona: zhCNPersona, - command: zhCNCommand, - subagent: zhCNSubagent, - welcome: zhCNWelcome - }, - messages: { - errors: zhCNErrors, - success: zhCNSuccess, - validation: zhCNValidation - } - }, - 'en-US': { - core: { - common: enUSCommon, - actions: enUSActions, - status: enUSStatus, - navigation: enUSNavigation, - header: enUSHeader, - shared: enUSShared - }, - features: { - chat: enUSChat, - extension: enUSExtension, - conversation: enUSConversation, - 'session-management': enUSSessionManagement, - tooluse: enUSToolUse, - provider: enUSProvider, - platform: enUSPlatform, - config: enUSConfig, - 'config-metadata': enUSConfigMetadata, - console: enUSConsole, - trace: enUSTrace, - about: enUSAbout, - settings: enUSSettings, - auth: enUSAuth, - chart: enUSChart, - dashboard: enUSDashboard, - cron: enUSCron, - stats: enUSStats, - alkaid: { - index: enUSAlkaidIndex, - 'knowledge-base': enUSAlkaidKnowledgeBase, - memory: enUSAlkaidMemory - }, - 'knowledge-base': { - index: enUSKnowledgeBaseIndex, - detail: enUSKnowledgeBaseDetail, - document: enUSKnowledgeBaseDocument - }, - persona: enUSPersona, - command: enUSCommand, - subagent: enUSSubagent, - welcome: enUSWelcome - }, - messages: { - errors: enUSErrors, - success: enUSSuccess, - validation: enUSValidation - } - }, - 'ru-RU': { - core: { - common: ruRUCommon, - actions: ruRUActions, - status: ruRUStatus, - navigation: ruRUNavigation, - header: ruRUHeader, - shared: ruRUShared - }, - features: { - chat: ruRUChat, - extension: ruRUExtension, - conversation: ruRUConversation, - 'session-management': ruRUSessionManagement, - tooluse: ruRUToolUse, - provider: ruRUProvider, - platform: ruRUPlatform, - config: ruRUConfig, - 'config-metadata': ruRUConfigMetadata, - console: ruRUConsole, - trace: ruRUTrace, - about: ruRUAbout, - settings: ruRUSettings, - auth: ruRUAuth, - chart: ruRUChart, - dashboard: ruRUDashboard, - cron: ruRUCron, - stats: ruRUStats, - alkaid: { - index: ruRUAlkaidIndex, - 'knowledge-base': ruRUAlkaidKnowledgeBase, - memory: ruRUAlkaidMemory - }, - 'knowledge-base': { - index: ruRUKnowledgeBaseIndex, - detail: ruRUKnowledgeBaseDetail, - document: ruRUKnowledgeBaseDocument - }, - persona: ruRUPersona, - command: ruRUCommand, - subagent: ruRUSubagent, - welcome: ruRUWelcome - }, - messages: { - errors: ruRUErrors, - success: ruRUSuccess, - validation: ruRUValidation - } - } -}; - -export type TranslationData = typeof translations; diff --git a/legacy-dashboard/src/i18n/types.ts b/legacy-dashboard/src/i18n/types.ts deleted file mode 100644 index 4cd55371..00000000 --- a/legacy-dashboard/src/i18n/types.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * I18n TypeScript Type Definitions - Auto-generated from JSON - * 国际化类型定义,从JSON文件自动推断,确保类型安全且自动同步 - */ - -// 直接导入已经组织好的翻译数据 -import { translations } from './translations'; - -// 导出翻译数据常量,供类型推断使用 -export const translationData = translations; - -// 从实际的翻译数据推断完整的翻译结构类型 -export type TranslationSchema = typeof translations[keyof typeof translations]; - -// TypeScript 助手:递归提取嵌套键路径 -type NestedKeyOf = T extends object - ? { - [K in keyof T & string]: T[K] extends object - ? `${K}` | `${K}.${NestedKeyOf}` - : `${K}` - }[keyof T & string] - : never; - -// 自动推断的翻译键联合类型 - 包含所有有效的点分隔键路径 -export type TranslationKey = NestedKeyOf; - -// 语言环境类型 - 从实际的翻译数据键推断 -export type Locale = keyof typeof translations; - -// 翻译函数类型 -export type TranslationFunction = { - (key: TranslationKey): string; - (key: TranslationKey, params: Record): string; -}; - -// 以下是保留的工具类型定义,这些不依赖具体的翻译结构 - -// 模块加载状态 -export interface ModuleLoadingState { - core: boolean; - features: boolean; - messages: boolean; -} - -// 翻译配置 -export interface I18nConfig { - locale: Locale; - fallbackLocale: Locale; - lazy: boolean; - preload: string[]; - caching: boolean; - devMode: boolean; -} - -// 验证结果 -export interface ValidationResult { - isValid: boolean; - missingKeys: string[]; - extraKeys: string[]; - errors: ValidationError[]; -} - -export interface ValidationError { - type: 'missing' | 'extra' | 'type_mismatch' | 'empty_value'; - key: string; - message: string; - severity: 'error' | 'warning'; -} - -// 使用情况报告 -export interface UsageReport { - unusedKeys: string[]; - undefinedKeys: string[]; - coverage: number; - totalKeys: number; - usedKeys: number; -} - -// 翻译统计信息 -export interface TranslationStats { - modules: { - [moduleName: string]: { - keys: number; - coverage: number; - lastUpdated: string; - }; - }; - locales: { - [locale: string]: { - totalKeys: number; - translatedKeys: number; - coverage: number; - }; - }; - overall: { - totalKeys: number; - averageCoverage: number; - lastSync: string; - }; -} - -// 开发工具类型 -export interface DevToolsData { - currentLocale: Locale; - loadedModules: string[]; - cacheStats: { - size: number; - hits: number; - misses: number; - }; - performance: { - loadTime: number; - renderTime: number; - }; -} - -// Vue I18n 模块增强 - 为了避免编译时的模块查找问题,暂时注释掉 -// 这些类型定义在运行时仍然有效,但不会在编译时产生错误 -/* -declare module '@vue/runtime-core' { - interface ComponentCustomProperties { - $t: (key: TranslationKey, params?: Record) => string; - } -} - -declare module 'vue-i18n' { - export interface DefineLocaleMessage extends TranslationSchema {} -} -*/ \ No newline at end of file diff --git a/legacy-dashboard/src/i18n/validator.ts b/legacy-dashboard/src/i18n/validator.ts deleted file mode 100644 index 462be1ec..00000000 --- a/legacy-dashboard/src/i18n/validator.ts +++ /dev/null @@ -1,441 +0,0 @@ -/** - * I18n Validator - * 国际化验证器,用于检查翻译完整性、使用情况分析和错误检测 - */ - -import type { ValidationResult, ValidationError, UsageReport, TranslationStats } from './types'; - -export class I18nValidator { - private baseLocale: string = 'zh-CN'; - private supportedLocales: string[] = ['zh-CN', 'en-US']; - - /** - * 验证翻译完整性 - */ - validateCompleteness(localeData: Record): ValidationResult { - const errors: ValidationError[] = []; - const missingKeys: string[] = []; - const extraKeys: string[] = []; - - // 获取基准语言数据 - const baseData = localeData[this.baseLocale]; - if (!baseData) { - errors.push({ - type: 'missing', - key: this.baseLocale, - message: `基准语言 ${this.baseLocale} 数据缺失`, - severity: 'error' - }); - return { isValid: false, missingKeys, extraKeys, errors }; - } - - // 获取所有键 - const baseKeys = this.getAllKeys(baseData); - - // 验证每种语言 - for (const locale of this.supportedLocales) { - if (locale === this.baseLocale) continue; - - const targetData = localeData[locale]; - if (!targetData) { - errors.push({ - type: 'missing', - key: locale, - message: `语言 ${locale} 数据缺失`, - severity: 'error' - }); - continue; - } - - const targetKeys = this.getAllKeys(targetData); - - // 检查缺失的键 - const missing = baseKeys.filter(key => !targetKeys.includes(key)); - missingKeys.push(...missing.map(key => `${locale}.${key}`)); - - // 检查多余的键 - const extra = targetKeys.filter(key => !baseKeys.includes(key)); - extraKeys.push(...extra.map(key => `${locale}.${key}`)); - - // 添加详细错误信息 - missing.forEach(key => { - errors.push({ - type: 'missing', - key: `${locale}.${key}`, - message: `${locale} 中缺失键: ${key}`, - severity: 'error' - }); - }); - - extra.forEach(key => { - errors.push({ - type: 'extra', - key: `${locale}.${key}`, - message: `${locale} 中存在多余键: ${key}`, - severity: 'warning' - }); - }); - } - - return { - isValid: errors.filter(e => e.severity === 'error').length === 0, - missingKeys, - extraKeys, - errors - }; - } - - /** - * 验证翻译值的有效性 - */ - validateValues(localeData: Record): ValidationError[] { - const errors: ValidationError[] = []; - - for (const [locale, data] of Object.entries(localeData)) { - this.validateNestedValues(data, locale, '', errors); - } - - return errors; - } - - /** - * 递归验证嵌套值 - */ - private validateNestedValues( - obj: any, - locale: string, - parentKey: string, - errors: ValidationError[] - ): void { - for (const [key, value] of Object.entries(obj)) { - const fullKey = parentKey ? `${parentKey}.${key}` : key; - - if (typeof value === 'object' && value !== null) { - this.validateNestedValues(value, locale, fullKey, errors); - } else if (typeof value === 'string') { - // 检查空值 - if (!value.trim()) { - errors.push({ - type: 'empty_value', - key: `${locale}.${fullKey}`, - message: `空翻译值: ${locale}.${fullKey}`, - severity: 'warning' - }); - } - - // 检查插值占位符 - const placeholders = value.match(/\{[^}]+\}/g) || []; - for (const placeholder of placeholders) { - if (!/^{[a-zA-Z_][a-zA-Z0-9_]*}$/.test(placeholder)) { - errors.push({ - type: 'type_mismatch', - key: `${locale}.${fullKey}`, - message: `无效的插值占位符: ${placeholder} in ${locale}.${fullKey}`, - severity: 'warning' - }); - } - } - } else { - errors.push({ - type: 'type_mismatch', - key: `${locale}.${fullKey}`, - message: `翻译值应为字符串,实际为: ${typeof value}`, - severity: 'error' - }); - } - } - } - - /** - * 分析翻译使用情况 - */ - validateUsage(translationKeys: string[], usedKeys: string[]): UsageReport { - const unusedKeys = translationKeys.filter(key => !usedKeys.includes(key)); - const undefinedKeys = usedKeys.filter(key => !translationKeys.includes(key)); - - return { - unusedKeys, - undefinedKeys, - coverage: (usedKeys.length / translationKeys.length) * 100, - totalKeys: translationKeys.length, - usedKeys: usedKeys.length - }; - } - - /** - * 生成翻译统计信息 - */ - generateStats(localeData: Record): TranslationStats { - const stats: TranslationStats = { - modules: {}, - locales: {}, - overall: { - totalKeys: 0, - averageCoverage: 0, - lastSync: new Date().toISOString() - } - }; - - // 分析每种语言 - for (const [locale, data] of Object.entries(localeData)) { - const keys = this.getAllKeys(data); - const translatedKeys = keys.filter(key => { - const value = this.getValueByKey(data, key); - return typeof value === 'string' && value.trim() !== ''; - }); - - stats.locales[locale] = { - totalKeys: keys.length, - translatedKeys: translatedKeys.length, - coverage: (translatedKeys.length / keys.length) * 100 - }; - - // 分析模块 - this.analyzeModules(data, locale, stats.modules); - } - - // 计算总体统计 - const locales = Object.values(stats.locales); - stats.overall.totalKeys = Math.max(...locales.map(l => l.totalKeys)); - stats.overall.averageCoverage = locales.reduce((sum, l) => sum + l.coverage, 0) / locales.length; - - return stats; - } - - /** - * 分析模块统计 - */ - private analyzeModules(data: any, locale: string, modules: TranslationStats['modules']): void { - for (const [moduleName, moduleData] of Object.entries(data)) { - if (typeof moduleData === 'object' && moduleData !== null) { - const moduleKey = `${locale}.${moduleName}`; - const keys = this.getAllKeys(moduleData); - const translatedKeys = keys.filter(key => { - const value = this.getValueByKey(moduleData, key); - return typeof value === 'string' && value.trim() !== ''; - }); - - if (!modules[moduleKey]) { - modules[moduleKey] = { - keys: 0, - coverage: 0, - lastUpdated: new Date().toISOString() - }; - } - - modules[moduleKey].keys = keys.length; - modules[moduleKey].coverage = (translatedKeys.length / keys.length) * 100; - } - } - } - - /** - * 获取对象的所有键路径 - */ - private getAllKeys(obj: any, prefix: string = ''): string[] { - const keys: string[] = []; - - for (const [key, value] of Object.entries(obj)) { - const fullKey = prefix ? `${prefix}.${key}` : key; - - if (typeof value === 'object' && value !== null) { - keys.push(...this.getAllKeys(value, fullKey)); - } else { - keys.push(fullKey); - } - } - - return keys; - } - - /** - * 根据键路径获取值 - */ - private getValueByKey(obj: any, keyPath: string): any { - return keyPath.split('.').reduce((current, key) => { - return current && current[key]; - }, obj); - } - - /** - * 检查插值一致性 - */ - validateInterpolation(localeData: Record): ValidationError[] { - const errors: ValidationError[] = []; - const baseData = localeData[this.baseLocale]; - - if (!baseData) return errors; - - const baseKeys = this.getAllKeys(baseData); - - for (const key of baseKeys) { - const baseValue = this.getValueByKey(baseData, key); - if (typeof baseValue !== 'string') continue; - - const basePlaceholders = (baseValue.match(/\{[^}]+\}/g) || []).sort(); - - for (const locale of this.supportedLocales) { - if (locale === this.baseLocale) continue; - - const targetData = localeData[locale]; - if (!targetData) continue; - - const targetValue = this.getValueByKey(targetData, key); - if (typeof targetValue !== 'string') continue; - - const targetPlaceholders = (targetValue.match(/\{[^}]+\}/g) || []).sort(); - - if (JSON.stringify(basePlaceholders) !== JSON.stringify(targetPlaceholders)) { - errors.push({ - type: 'type_mismatch', - key: `${locale}.${key}`, - message: `插值占位符不匹配: ${locale}.${key},期望 ${basePlaceholders.join(', ')},实际 ${targetPlaceholders.join(', ')}`, - severity: 'error' - }); - } - } - } - - return errors; - } - - /** - * 验证键命名规范 - */ - validateKeyNaming(localeData: Record): ValidationError[] { - const errors: ValidationError[] = []; - const keyNamingPattern = /^[a-z][a-zA-Z0-9]*$/; - - for (const [locale, data] of Object.entries(localeData)) { - this.validateKeyNamingRecursive(data, locale, '', keyNamingPattern, errors); - } - - return errors; - } - - /** - * 递归验证键命名 - */ - private validateKeyNamingRecursive( - obj: any, - locale: string, - parentKey: string, - pattern: RegExp, - errors: ValidationError[] - ): void { - for (const key of Object.keys(obj)) { - const fullKey = parentKey ? `${parentKey}.${key}` : key; - - if (!pattern.test(key)) { - errors.push({ - type: 'type_mismatch', - key: `${locale}.${fullKey}`, - message: `键名不符合命名规范: ${key},应使用小驼峰命名`, - severity: 'warning' - }); - } - - if (typeof obj[key] === 'object' && obj[key] !== null) { - this.validateKeyNamingRecursive(obj[key], locale, fullKey, pattern, errors); - } - } - } - - /** - * 验证多个语言包 - */ - async validateLocales(locales: string[]): Promise<{ - summary: { - totalLocales: number; - totalKeys: number; - missingKeys: number; - emptyValues: number; - invalidInterpolations: number; - completeness: number; - }; - details: ValidationResult[]; - recommendations: string[]; - }> { - const results: ValidationResult[] = []; - - for (const locale of locales) { - try { - // 这里应该从实际的翻译文件中加载,暂时创建基本结构 - const localeData = { [locale]: {} }; - const result = this.validateCompleteness(localeData); - results.push(result); - } catch (error) { - console.error(`验证语言包 ${locale} 时出错:`, error); - // 创建错误结果 - const errorResult: ValidationResult = { - isValid: false, - missingKeys: [], - extraKeys: [], - errors: [ - { - type: 'missing', - key: locale, - message: error instanceof Error ? error.message : '未知错误', - severity: 'error' - } - ] - }; - results.push(errorResult); - } - } - - // 生成汇总报告 - const totalKeys = results.length * 100; // 估算的总键数 - const missingKeys = results.reduce((sum, r) => sum + r.missingKeys.length, 0); - - return { - summary: { - totalLocales: results.length, - totalKeys, - missingKeys, - emptyValues: 0, // 暂时设为0 - invalidInterpolations: 0, // 暂时设为0 - completeness: totalKeys > 0 ? ((totalKeys - missingKeys) / totalKeys) * 100 : 100 - }, - details: results, - recommendations: [ - '建议优先翻译核心模块的缺失键', - '检查所有空值并提供适当的翻译', - '确保插值占位符在所有语言中保持一致' - ] - }; - } - - /** - * 生成验证报告 - */ - generateReport(localeData: Record, usedKeys: string[] = []): { - completeness: ValidationResult; - values: ValidationError[]; - interpolation: ValidationError[]; - naming: ValidationError[]; - usage: UsageReport | null; - stats: TranslationStats; - } { - const completeness = this.validateCompleteness(localeData); - const values = this.validateValues(localeData); - const interpolation = this.validateInterpolation(localeData); - const naming = this.validateKeyNaming(localeData); - const stats = this.generateStats(localeData); - - let usage: UsageReport | null = null; - if (usedKeys.length > 0) { - const allKeys = this.getAllKeys(localeData[this.baseLocale] || {}); - usage = this.validateUsage(allKeys, usedKeys); - } - - return { - completeness, - values, - interpolation, - naming, - usage, - stats - }; - } -} \ No newline at end of file diff --git a/legacy-dashboard/src/layouts/blank/BlankLayout.vue b/legacy-dashboard/src/layouts/blank/BlankLayout.vue deleted file mode 100644 index 09d66466..00000000 --- a/legacy-dashboard/src/layouts/blank/BlankLayout.vue +++ /dev/null @@ -1,8 +0,0 @@ - - diff --git a/legacy-dashboard/src/layouts/full/FullLayout.vue b/legacy-dashboard/src/layouts/full/FullLayout.vue deleted file mode 100644 index 19f0da45..00000000 --- a/legacy-dashboard/src/layouts/full/FullLayout.vue +++ /dev/null @@ -1,167 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/layouts/full/vertical-header/VerticalHeader.vue b/legacy-dashboard/src/layouts/full/vertical-header/VerticalHeader.vue deleted file mode 100644 index 76b1cdfb..00000000 --- a/legacy-dashboard/src/layouts/full/vertical-header/VerticalHeader.vue +++ /dev/null @@ -1,2496 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/layouts/full/vertical-sidebar/NavItem.vue b/legacy-dashboard/src/layouts/full/vertical-sidebar/NavItem.vue deleted file mode 100644 index 6c546caa..00000000 --- a/legacy-dashboard/src/layouts/full/vertical-sidebar/NavItem.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue b/legacy-dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue deleted file mode 100644 index 0c78f2ea..00000000 --- a/legacy-dashboard/src/layouts/full/vertical-sidebar/VerticalSidebar.vue +++ /dev/null @@ -1,458 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts b/legacy-dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts deleted file mode 100644 index b5725726..00000000 --- a/legacy-dashboard/src/layouts/full/vertical-sidebar/sidebarItem.ts +++ /dev/null @@ -1,134 +0,0 @@ -export interface menu { - header?: string; - title?: string; - icon?: string; - to?: string; - divider?: boolean; - chip?: string; - chipColor?: string; - chipVariant?: string; - chipIcon?: string; - children?: menu[]; - disabled?: boolean; - type?: string; - subCaption?: string; - isRawTitle?: boolean; -} - -export const MORE_GROUP_KEY = 'core.navigation.groups.more'; - -// 注意:这个文件现在包含i18n键值而不是直接的文本 -// 在组件中使用时需要通过t()函数进行翻译 -// 所有键名都使用 core.navigation.* 格式 -const sidebarItem: menu[] = [ - { - title: 'core.navigation.welcome', - icon: 'mdi-hand-wave-outline', - to: '/welcome', - }, - { - title: 'core.navigation.platforms', - icon: 'mdi-robot', - to: '/platforms', - }, - { - title: 'core.navigation.providers', - icon: 'mdi-creation', - to: '/providers', - }, - { - title: 'core.navigation.config', - icon: 'mdi-cog', - to: '/config', - }, - { - title: 'core.navigation.extension', - icon: 'mdi-puzzle', - to: '/extension#installed', - children: [ - { - title: 'core.navigation.extensionTabs.installed', - icon: 'mdi-puzzle', - to: '/extension#installed' - }, - { - title: 'core.navigation.extensionTabs.market', - icon: 'mdi-store', - to: '/extension#market' - }, - { - title: 'core.navigation.extensionTabs.mcp', - icon: 'mdi-server-network', - to: '/extension#mcp' - }, - { - title: 'core.navigation.extensionTabs.skills', - icon: 'mdi-lightning-bolt', - to: '/extension#skills' - }, - { - title: 'core.navigation.extensionTabs.components', - icon: 'mdi-wrench', - to: '/extension#components' - } - ] - }, - { - title: 'core.navigation.knowledgeBase', - icon: 'mdi-book-open-variant', - to: '/knowledge-base', - }, - { - title: 'core.navigation.persona', - icon: 'mdi-heart', - to: '/persona' - }, - { - title: 'core.navigation.groups.more', - icon: 'mdi-dots-horizontal', - children: [ - { - title: 'core.navigation.conversation', - icon: 'mdi-database', - to: '/conversation' - }, - { - title: 'core.navigation.sessionManagement', - icon: 'mdi-pencil-ruler', - to: '/session-management' - }, - { - title: 'core.navigation.cron', - icon: 'mdi-clock-outline', - to: '/cron' - }, - { - title: 'core.navigation.subagent', - icon: 'mdi-vector-link', - to: '/subagent' - }, - { - title: 'core.navigation.dashboard', - icon: 'mdi-view-dashboard', - to: '/dashboard/default' - }, - { - title: 'core.navigation.console', - icon: 'mdi-console', - to: '/console' - }, - { - title: 'core.navigation.trace', - icon: 'mdi-timeline-text-outline', - to: '/trace' - }, - ] - } - // { - // title: 'Project ATRI', - // icon: 'mdi-grain', - // to: '/project-atri' - // }, -]; - -export default sidebarItem; diff --git a/legacy-dashboard/src/main.ts b/legacy-dashboard/src/main.ts deleted file mode 100644 index f8cadb92..00000000 --- a/legacy-dashboard/src/main.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { createApp } from 'vue'; -import { createPinia } from 'pinia'; -import App from './App.vue'; -import { router } from './router'; -import vuetify from './plugins/vuetify'; -import confirmPlugin from './plugins/confirmPlugin'; -import { setupI18n } from './i18n/composables'; -import '@/scss/style.scss'; -import VueApexCharts from 'vue3-apexcharts'; - -import print from 'vue3-print-nb'; -import { loader } from '@guolao/vue-monaco-editor' -import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; -import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution'; -import 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution'; -import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'; -import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution'; -import 'monaco-editor/esm/vs/basic-languages/powershell/powershell.contribution'; -import 'monaco-editor/esm/vs/basic-languages/python/python.contribution'; -import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution'; -import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'; -import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution'; -import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution'; -import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution'; -import 'monaco-editor/esm/vs/language/css/monaco.contribution'; -import 'monaco-editor/esm/vs/language/html/monaco.contribution'; -import 'monaco-editor/esm/vs/language/json/monaco.contribution'; -import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'; -import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'; -import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'; -import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'; -import { setupHttpClient } from './api/http'; -import { waitForRouterReadyInBackground } from './utils/routerReadiness.mjs'; -import { waitForDesktopBackendReady } from './utils/desktopBackendReadiness.mjs'; - -setupHttpClient(); - -(self as any).MonacoEnvironment = { - getWorker(_: string, label: string) { - if (label === 'json') { - return new jsonWorker(); - } - if (label === 'css' || label === 'scss' || label === 'less') { - return new cssWorker(); - } - if (label === 'html' || label === 'handlebars' || label === 'razor') { - return new htmlWorker(); - } - return new editorWorker(); - }, -}; - -/** - * 挂载后初始化主题并注册全局系统主题监听器。 - * 职责: - * - 同步 Vuetify theme 名称与 store 中的 uiTheme - * - 当 themeMode === 'system' 时,监听系统色彩模式变化,实时更新两者 - * - 应用自定义 primary/secondary 色 - * 注意:VerticalHeader.vue / ThemeSwitcher.vue 不再自行注册 matchMedia 监听器, - * 避免与此处产生竞态。 - */ -function setupThemeSync(pinia: ReturnType) { - import('./stores/customizer').then(({ useCustomizerStore }) => { - const customizer = useCustomizerStore(pinia); - - // 1. 若当前是 system 模式,重新用 matchMedia 计算,防止 SSR / 构建时偏差 - if (customizer.themeMode === 'system') { - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - const uiTheme = prefersDark ? 'PurpleThemeDark' : 'PurpleTheme'; - customizer.uiTheme = uiTheme; - localStorage.setItem('uiTheme', uiTheme); - } - - // 2. 将 Vuetify 主题对齐到 store - vuetify.theme.global.name.value = customizer.uiTheme; - - // 3. 应用用户自定义色 - const storedPrimary = localStorage.getItem('themePrimary'); - const storedSecondary = localStorage.getItem('themeSecondary'); - if (storedPrimary || storedSecondary) { - const themes = vuetify.theme.themes.value; - ['PurpleTheme', 'PurpleThemeDark'].forEach((name) => { - const theme = themes[name]; - if (!theme?.colors) return; - if (storedPrimary) theme.colors.primary = storedPrimary; - if (storedSecondary) theme.colors.secondary = storedSecondary; - if (storedPrimary && theme.colors.darkprimary) theme.colors.darkprimary = storedPrimary; - if (storedSecondary && theme.colors.darksecondary) theme.colors.darksecondary = storedSecondary; - }); - } - - // 4. 全局唯一 matchMedia 监听器:仅在 system 模式下响应系统切换 - const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); - mediaQuery.addEventListener('change', (e) => { - if (customizer.themeMode !== 'system') return; - const uiTheme = e.matches ? 'PurpleThemeDark' : 'PurpleTheme'; - customizer.uiTheme = uiTheme; - localStorage.setItem('uiTheme', uiTheme); - vuetify.theme.global.name.value = uiTheme; - }); - }); -} - -// 初始化新的i18n系统,等待完成后再挂载应用 -waitForDesktopBackendReady().then(() => setupI18n()).then(async () => { - console.log('🌍 新i18n系统初始化完成'); - - const app = createApp(App); - const pinia = createPinia(); - app.use(pinia); - app.use(router); - app.use(print); - app.use(VueApexCharts); - app.use(vuetify); - app.use(confirmPlugin); - await router.isReady(); - app.mount('#app'); - - setupThemeSync(pinia); -}).catch(error => { - console.error('❌ 新i18n系统初始化失败:', error); - - // 即使i18n初始化失败,也要挂载应用(使用回退机制) - const app = createApp(App); - const pinia = createPinia(); - app.use(pinia); - app.use(router); - app.use(print); - app.use(VueApexCharts); - app.use(vuetify); - app.use(confirmPlugin); - app.mount('#app'); - waitForRouterReadyInBackground(router); - - setupThemeSync(pinia); -}); - - -loader.config({ monaco }) diff --git a/legacy-dashboard/src/plugins/confirmPlugin.ts b/legacy-dashboard/src/plugins/confirmPlugin.ts deleted file mode 100644 index 94e126e9..00000000 --- a/legacy-dashboard/src/plugins/confirmPlugin.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { App } from "vue"; -import { h, render } from "vue"; -import ConfirmDialog from "@/components/ConfirmDialog.vue"; - -export default { - install(app: App) { - const mountNode = document.createElement("div"); - document.body.appendChild(mountNode); - - const vNode = h(ConfirmDialog); - vNode.appContext = app._context; - render(vNode, mountNode); - - const confirm = (options: { title?: string; message?: string }) => { - return new Promise((resolve) => { - vNode.component?.exposed?.open(options).then(resolve); // ✅ 确保返回 `Promise` - }); - }; - - app.config.globalProperties.$confirm = confirm; - app.provide("$confirm", confirm); - }, -}; \ No newline at end of file diff --git a/legacy-dashboard/src/plugins/vuetify.ts b/legacy-dashboard/src/plugins/vuetify.ts deleted file mode 100644 index 37b56f3d..00000000 --- a/legacy-dashboard/src/plugins/vuetify.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { createVuetify } from 'vuetify'; -import '@/assets/mdi-subset/materialdesignicons-subset.css'; -import * as components from 'vuetify/components'; -import * as directives from 'vuetify/directives'; -import { PurpleTheme } from '@/theme/LightTheme'; -import { PurpleThemeDark } from "@/theme/DarkTheme"; - -export default createVuetify({ - components, - directives, - - theme: { - defaultTheme: 'PurpleTheme', - themes: { - PurpleTheme, - PurpleThemeDark - } - }, - defaults: { - VBtn: {}, - VCard: { - rounded: 'lg' - }, - VSnackbar: { - elevation: 6, - rounded: 'lg' - }, - VTextField: { - rounded: 'lg' - }, - VTooltip: { - // set v-tooltip default location to top - location: 'top' - } - } -}); diff --git a/legacy-dashboard/src/router/AuthRoutes.ts b/legacy-dashboard/src/router/AuthRoutes.ts deleted file mode 100644 index 5781646e..00000000 --- a/legacy-dashboard/src/router/AuthRoutes.ts +++ /dev/null @@ -1,21 +0,0 @@ -const AuthRoutes = { - path: '/auth', - component: () => import('@/layouts/blank/BlankLayout.vue'), - meta: { - requiresAuth: false - }, - children: [ - { - name: 'Login', - path: '/auth/login', - component: () => import('@/views/authentication/auth/LoginPage.vue') - }, - { - name: 'Setup', - path: '/auth/setup', - component: () => import('@/views/authentication/auth/SetupPage.vue') - } - ] -}; - -export default AuthRoutes; diff --git a/legacy-dashboard/src/router/ChatBoxRoutes.ts b/legacy-dashboard/src/router/ChatBoxRoutes.ts deleted file mode 100644 index 393ad8f6..00000000 --- a/legacy-dashboard/src/router/ChatBoxRoutes.ts +++ /dev/null @@ -1,21 +0,0 @@ -const ChatBoxRoutes = { - path: '/chatbox', - component: () => import('@/layouts/blank/BlankLayout.vue'), - children: [ - { - name: 'ChatBox', - path: '/chatbox', - component: () => import('@/views/ChatBoxPage.vue'), - children: [ - { - path: ':conversationId', - name: 'ChatBoxDetail', - component: () => import('@/views/ChatBoxPage.vue'), - props: true - } - ] - } - ] -}; - -export default ChatBoxRoutes; diff --git a/legacy-dashboard/src/router/MainRoutes.ts b/legacy-dashboard/src/router/MainRoutes.ts deleted file mode 100644 index 4b399b5b..00000000 --- a/legacy-dashboard/src/router/MainRoutes.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { - EXTENSION_DETAILS_ROUTE_NAME, - EXTENSION_ROUTE_NAME -} from './routeConstants.mjs'; - -const MainRoutes = { - path: '/main', - meta: { - requiresAuth: true - }, - redirect: '/welcome', - component: () => import('@/layouts/full/FullLayout.vue'), - children: [ - { - name: 'MainPage', - path: '/', - component: () => import('@/views/WelcomePage.vue') - }, - { - name: 'Welcome', - path: '/welcome', - component: () => import('@/views/WelcomePage.vue') - }, - { - name: EXTENSION_ROUTE_NAME, - path: '/extension', - component: () => import('@/views/ExtensionPage.vue') - }, - { - name: 'PluginPage', - path: '/plugin-page/:pluginName/:pageName', - component: () => import('@/views/PluginPagePage.vue') - }, - { - name: EXTENSION_DETAILS_ROUTE_NAME, - path: '/extension/:pluginId', - component: () => import('@/views/ExtensionPage.vue') - }, - { - name: 'ExtensionMarketplace', - path: '/extension-marketplace', - component: () => import('@/views/ExtensionPage.vue') - }, - { - name: 'Platforms', - path: '/platforms', - component: () => import('@/views/PlatformPage.vue') - }, - { - name: 'Providers', - path: '/providers', - component: () => import('@/views/ProviderPage.vue') - }, - { - name: 'Configs', - path: '/config', - component: () => import('@/views/ConfigPage.vue') - }, - { - path: '/normal', - redirect: '/config' - }, - { - path: '/system', - redirect: '/settings#system-config' - }, - { - name: 'Stats', - path: '/dashboard/default', - component: () => import('@/views/stats/StatsPage.vue') - }, - { - name: 'Conversation', - path: '/conversation', - component: () => import('@/views/ConversationPage.vue') - }, - { - name: 'SessionManagement', - path: '/session-management', - component: () => import('@/views/SessionManagementPage.vue') - }, - { - name: 'Persona', - path: '/persona', - component: () => import('@/views/PersonaPage.vue') - }, - { - name: 'SubAgent', - path: '/subagent', - component: () => import('@/views/SubAgentPage.vue') - }, - { - name: 'CronJobs', - path: '/cron', - component: () => import('@/views/CronJobPage.vue') - }, - { - name: 'Console', - path: '/console', - component: () => import('@/views/ConsolePage.vue') - }, - { - name: 'Trace', - path: '/trace', - component: () => import('@/views/TracePage.vue') - }, - { - name: 'NativeKnowledgeBase', - path: '/knowledge-base', - component: () => import('@/views/knowledge-base/index.vue'), - children: [ - { - path: '', - name: 'NativeKBList', - component: () => import('@/views/knowledge-base/KBList.vue') - }, - { - path: ':kbId', - name: 'NativeKBDetail', - component: () => import('@/views/knowledge-base/KBDetail.vue'), - props: true - }, - { - path: ':kbId/document/:docId', - name: 'NativeDocumentDetail', - component: () => import('@/views/knowledge-base/DocumentDetail.vue'), - props: true - } - ] - }, - - // 旧版本的知识库路由 - { - name: 'KnowledgeBase', - path: '/alkaid/knowledge-base', - component: () => import('@/views/alkaid/KnowledgeBase.vue'), - }, - // { - // name: 'Alkaid', - // path: '/alkaid', - // component: () => import('@/views/AlkaidPage.vue'), - // children: [ - // { - // path: 'knowledge-base', - // name: 'KnowledgeBase', - // component: () => import('@/views/alkaid/KnowledgeBase.vue') - // }, - // { - // path: 'long-term-memory', - // name: 'LongTermMemory', - // component: () => import('@/views/alkaid/LongTermMemory.vue') - // }, - // { - // path: 'other', - // name: 'OtherFeatures', - // component: () => import('@/views/alkaid/Other.vue') - // } - // ] - // }, - { - name: 'Chat', - path: '/chat', - component: () => import('@/views/ChatPage.vue'), - children: [ - { - path: ':conversationId', - name: 'ChatDetail', - component: () => import('@/views/ChatPage.vue'), - props: true - } - ] - }, - { - name: 'Settings', - path: '/settings', - component: () => import('@/views/Settings.vue') - }, - { - name: 'About', - path: '/about', - component: () => import('@/views/AboutPage.vue') - } - ] -}; - -export default MainRoutes; diff --git a/legacy-dashboard/src/router/index.ts b/legacy-dashboard/src/router/index.ts deleted file mode 100644 index 2eab4669..00000000 --- a/legacy-dashboard/src/router/index.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { createRouter, createWebHashHistory } from 'vue-router'; -import MainRoutes from './MainRoutes'; -import AuthRoutes from './AuthRoutes'; -import ChatBoxRoutes from './ChatBoxRoutes'; -import { useAuthStore } from '@/stores/auth'; -import { useRouterLoadingStore } from '@/stores/routerLoading'; - -export const router = createRouter({ - history: createWebHashHistory(import.meta.env.BASE_URL), - routes: [ - MainRoutes, - AuthRoutes, - ChatBoxRoutes - ] -}); - -interface AuthStore { - username: string; - returnUrl: string | null; - login( - username: string, - password: string, - code?: string, - trustDeviceToken?: boolean, - ): Promise; - logout(): void; - has_token(): boolean; -} - -router.beforeEach(async (to, from, next) => { - if (from.name && from.path !== to.path) { - const loadingStore = useRouterLoadingStore(); - loadingStore.start(); - } - - const publicPages = ['/auth/login', '/auth/setup']; - const authRequired = !publicPages.includes(to.path); - const auth: AuthStore = useAuthStore(); - - // 如果用户已登录且试图访问登录页面,则重定向到首页 - if (to.path === '/auth/login' && auth.has_token()) { - return next('/welcome'); - } - - if (to.matched.some((record) => record.meta.requiresAuth)) { - if (authRequired && !auth.has_token()) { - auth.returnUrl = to.fullPath; - return next('/auth/login'); - } - return next(); - } else { - next(); - } -}); - -router.afterEach(() => { - const loadingStore = useRouterLoadingStore(); - loadingStore.finish(); -}); diff --git a/legacy-dashboard/src/router/routeConstants.mjs b/legacy-dashboard/src/router/routeConstants.mjs deleted file mode 100644 index cabf9026..00000000 --- a/legacy-dashboard/src/router/routeConstants.mjs +++ /dev/null @@ -1,2 +0,0 @@ -export const EXTENSION_ROUTE_NAME = 'Extensions'; -export const EXTENSION_DETAILS_ROUTE_NAME = 'ExtensionDetails'; diff --git a/legacy-dashboard/src/scss/_override.scss b/legacy-dashboard/src/scss/_override.scss deleted file mode 100644 index 79204dce..00000000 --- a/legacy-dashboard/src/scss/_override.scss +++ /dev/null @@ -1,132 +0,0 @@ -html { - .bg-success { - color: white !important; - } -} - -.v-row + .v-row { - margin-top: 0px; -} - -.v-divider { - opacity: 1; - border-color: rgba(var(--v-theme-borderLight), 0.36); -} - -.v-selection-control { - flex: unset; -} - -.v-overlay.v-snackbar { - --v-layout-left: 0px !important; - --v-layout-right: 0px !important; - - .v-snackbar__wrapper { - min-height: 52px; - border-radius: 8px !important; - box-shadow: 0 10px 24px rgba(16, 24, 40, 0.16) !important; - } - - .v-snackbar__content { - padding: 14px 18px; - line-height: 1.35; - font-weight: 600; - } - - .v-snackbar__actions { - margin-inline-end: 6px; - } - - .v-snackbar__actions .v-btn { - color: inherit; - } - - .v-snackbar__wrapper.bg-success { - color: #ffffff !important; - background-color: #457c3c !important; - } -} - -.app-snackbar-message { - display: flex; - align-items: center; - gap: 14px; - - .v-icon { - flex: 0 0 auto; - opacity: 0.95; - } -} - -.customizer-btn .icon { - animation: progress-circular-rotate 1.4s linear infinite; - transform-origin: center center; - transition: all 0.2s ease-in-out; -} - -.no-spacer { - .v-list-item__spacer { - display: none !important; - } -} - -@keyframes progress-circular-rotate { - 100% { - transform: rotate(270deg); - } -} - -pre, code, .markdown pre, .markdown code, .release-notes pre, .release-notes code { - font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, "Roboto Mono", "Helvetica Neue", monospace; - color: var(--astrbot-code-color); -} - -:root { - --astrbot-menu-shadow: 0 12px 28px rgba(0, 0, 0, 0.08); -} - -.v-overlay.v-menu { - .v-overlay__content > .v-card, - .v-overlay__content > .v-list { - border: 0 !important; - box-shadow: var(--astrbot-menu-shadow) !important; - } - - .v-overlay__content .v-card, - .v-overlay__content .v-list { - border: 0 !important; - } -} - -// Dark mode global overrides -.v-theme--PurpleThemeDark { - // Override code text color for dark mode - --astrbot-code-color: rgb(var(--v-theme-on-surface-variant)); - - // Soften flat primary buttons for dark mode - .v-btn--variant-flat.bg-primary { - background-color: rgba(var(--v-theme-primary), 0.85) !important; - } - - // Timeline dots: use a muted version - .v-timeline-item .v-timeline-divider__dot .v-timeline-divider__inner-dot { - opacity: 0.85; - } - - // Use a darker scrim for overlays and dialogs in dark mode. - .v-overlay__scrim { - background-color: rgba(var(--v-theme-background), 0.72) !important; - opacity: 1 !important; - } - - // Ensure dialog/card surfaces use the dark surface color - .v-overlay__content > .v-card { - background-color: rgb(var(--v-theme-surface)) !important; - } - - // Links inside markdown - slightly muted - .markdown-content a, - .markstream-vue a { - color: rgb(var(--v-theme-primary)); - } -} diff --git a/legacy-dashboard/src/scss/_variables.scss b/legacy-dashboard/src/scss/_variables.scss deleted file mode 100644 index 8fb01767..00000000 --- a/legacy-dashboard/src/scss/_variables.scss +++ /dev/null @@ -1,134 +0,0 @@ -@use 'sass:math'; -@use 'sass:map'; -@use 'sass:meta'; -@use 'vuetify/lib/styles/tools/functions' as *; - -// This will false all colors which is not necessory for theme -$color-pack: false; - -// Global font size and border radius -$font-size-root: 1rem; -$border-radius-root: 8px; -$cjk-sans-fallback: 'PingFang SC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Microsoft YaHei', 'Noto Sans SC' !default; -$cjk-mono-fallback: 'PingFang SC', 'PingFang TC', 'Hiragino Sans GB', 'Noto Sans CJK SC', 'Microsoft YaHei' !default; -$code-text-color: #111827 !default; - -:root { - --astrbot-font-cjk-sans: #{$cjk-sans-fallback}; - --astrbot-font-cjk-mono: #{$cjk-mono-fallback}; - --astrbot-code-color: #{$code-text-color}; -} - -$body-font-family: 'Outfit', 'Noto Sans', $cjk-sans-fallback, sans-serif !default; -$heading-font-family: $body-font-family !default; -$btn-font-weight: 400 !default; -$btn-letter-spacing: 0 !default; - -// Global Radius as per breakeven point -$rounded: () !default; -$rounded: map-deep-merge( - ( - 0: 0, - 'sm': $border-radius-root * 0.5, - null: $border-radius-root, - 'md': $border-radius-root * 1, - 'lg': $border-radius-root * 2, - 'xl': $border-radius-root * 6, - 'pill': 9999px, - 'circle': 50%, - 'shaped': $border-radius-root * 6 0 - ), - $rounded -); -// Global Typography -$typography: () !default; -$typography: map-deep-merge( - ( - 'h1': ( - 'size': 2.125rem, - 'weight': 700, - 'line-height': 3.5rem, - 'font-family': inherit - ), - 'h2': ( - 'size': 1.5rem, - 'weight': 700, - 'line-height': 2.5rem, - 'font-family': inherit - ), - 'h3': ( - 'size': 1.25rem, - 'weight': 600, - 'line-height': 2rem, - 'font-family': inherit - ), - 'h4': ( - 'size': 1rem, - 'weight': 600, - 'line-height': 1.5rem, - 'font-family': inherit - ), - 'h5': ( - 'size': 0.875rem, - 'weight': 500, - 'line-height': 1.2rem, - 'font-family': inherit - ), - 'h6': ( - 'size': 0.75rem, - 'weight': 500, - 'font-family': inherit - ), - 'subtitle-1': ( - 'size': 0.875rem, - 'weight': 500, - 'line-height': 1rem, - 'font-family': inherit - ), - 'subtitle-2': ( - 'size': 0.75rem, - 'weight': 400, - 'line-height': 1rem, - 'font-family': inherit - ), - 'body-1': ( - 'size': 0.875rem, - 'weight': 400, - 'font-family': inherit - ), - 'body-2': ( - 'size': 0.75rem, - 'weight': 400, - 'font-family': inherit - ), - 'button': ( - 'size': 0.875rem, - 'weight': 500, - 'font-family': inherit, - 'text-transform': uppercase - ), - 'caption': ( - 'size': 0.75rem, - 'weight': 400, - 'font-family': inherit - ), - 'overline': ( - 'size': 0.75rem, - 'weight': 500, - 'font-family': inherit, - 'text-transform': uppercase - ) - ), - $typography -); - -// Custom Variables -// colors -$white: #fff !default; - -// cards -$card-item-spacer-xy: 20px 24px !default; -$card-text-spacer: 24px !default; -$card-title-size: 18px !default; -// Global Shadow -$box-shadow: 1px 0 20px rgb(0 0 0 / 8%); diff --git a/legacy-dashboard/src/scss/components/_CodeBlockDark.scss b/legacy-dashboard/src/scss/components/_CodeBlockDark.scss deleted file mode 100644 index afc4f7ab..00000000 --- a/legacy-dashboard/src/scss/components/_CodeBlockDark.scss +++ /dev/null @@ -1,19 +0,0 @@ -.v-theme--PurpleThemeDark { - .shiki.shiki-themes, - .shiki.shiki-themes span { - color: var(--shiki-dark) !important; - } - - .shiki.shiki-themes { - background-color: var(--shiki-dark-bg) !important; - } - - .markstream-vue { - --border: 217.2 32.6% 17.5%; - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - --secondary: 217.2 32.6% 17.5%; - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - } -} diff --git a/legacy-dashboard/src/scss/components/_VButtons.scss b/legacy-dashboard/src/scss/components/_VButtons.scss deleted file mode 100644 index a7d095e9..00000000 --- a/legacy-dashboard/src/scss/components/_VButtons.scss +++ /dev/null @@ -1,23 +0,0 @@ -// -// Light Buttons -// - -.v-btn { - &.bg-lightsecondary { - &:hover, - &:active, - &:focus { - background-color: rgb(var(--v-theme-secondary)) !important; - color: $white !important; - } - } -} - -.v-btn { - text-transform: capitalize; - letter-spacing: $btn-letter-spacing; -} -.v-btn--icon.v-btn--density-default { - width: calc(var(--v-btn-height) + 6px); - height: calc(var(--v-btn-height) + 6px); -} diff --git a/legacy-dashboard/src/scss/components/_VCard.scss b/legacy-dashboard/src/scss/components/_VCard.scss deleted file mode 100644 index 10dcaaf8..00000000 --- a/legacy-dashboard/src/scss/components/_VCard.scss +++ /dev/null @@ -1,26 +0,0 @@ -// Outline Card -.v-card--variant-outlined { - border-color: rgba(var(--v-theme-borderLight), 0.36); - .v-divider { - border-color: rgba(var(--v-theme-borderLight), 0.36); - } -} - -.v-card-text { - padding: $card-text-spacer; -} - -.v-card { - width: 100%; - overflow: visible; - &.withbg { - background-color: rgb(var(--v-theme-background)); - } - &.overflow-hidden { - overflow: hidden; - } -} - -.v-card-item { - padding: $card-item-spacer-xy; -} diff --git a/legacy-dashboard/src/scss/components/_VField.scss b/legacy-dashboard/src/scss/components/_VField.scss deleted file mode 100644 index 97352acf..00000000 --- a/legacy-dashboard/src/scss/components/_VField.scss +++ /dev/null @@ -1,9 +0,0 @@ -.v-field--variant-outlined .v-field__outline__start.v-locale--is-ltr, -.v-locale--is-ltr .v-field--variant-outlined .v-field__outline__start { - border-radius: $border-radius-root 0 0 $border-radius-root; -} - -.v-field--variant-outlined .v-field__outline__end.v-locale--is-ltr, -.v-locale--is-ltr .v-field--variant-outlined .v-field__outline__end { - border-radius: 0 $border-radius-root $border-radius-root 0; -} diff --git a/legacy-dashboard/src/scss/components/_VInput.scss b/legacy-dashboard/src/scss/components/_VInput.scss deleted file mode 100644 index 2ecc46a6..00000000 --- a/legacy-dashboard/src/scss/components/_VInput.scss +++ /dev/null @@ -1,7 +0,0 @@ -.v-label { - font-size: 0.975rem; -} -.v-switch .v-label, -.v-checkbox .v-label { - opacity: 1; -} diff --git a/legacy-dashboard/src/scss/components/_VNavigationDrawer.scss b/legacy-dashboard/src/scss/components/_VNavigationDrawer.scss deleted file mode 100644 index 9994ae91..00000000 --- a/legacy-dashboard/src/scss/components/_VNavigationDrawer.scss +++ /dev/null @@ -1,3 +0,0 @@ -.v-navigation-drawer__scrim.fade-transition-leave-to { - display: none; -} diff --git a/legacy-dashboard/src/scss/components/_VScrollbar.scss b/legacy-dashboard/src/scss/components/_VScrollbar.scss deleted file mode 100644 index 629afdf4..00000000 --- a/legacy-dashboard/src/scss/components/_VScrollbar.scss +++ /dev/null @@ -1,104 +0,0 @@ -/* 自定义滚动条样式 - 跟随主题 */ - -:root { - --astrbot-scrollbar-track: rgba(var(--v-theme-primary), 0.08); - --astrbot-scrollbar-thumb: rgba(var(--v-theme-primary), 0.72); - --astrbot-scrollbar-thumb-hover: rgba(var(--v-theme-primary), 0.84); - --astrbot-scrollbar-thumb-active: rgba(var(--v-theme-primary), 0.94); - --astrbot-scrollbar-thumb-border: rgba(var(--v-theme-surface), 0.5); - --astrbot-scrollbar-thumb-shadow: rgba(var(--v-theme-primary), 0.32); -} - -/* 全局滚动条样式 */ -::-webkit-scrollbar { - width: 10px; - height: 10px; -} - -::-webkit-scrollbar-track { - background: var(--astrbot-scrollbar-track); - border-radius: 5px; -} - -::-webkit-scrollbar-thumb { - background: var(--astrbot-scrollbar-thumb); - border-radius: 5px; - transition: all 0.3s ease; - border: 1px solid var(--astrbot-scrollbar-thumb-border); -} - -::-webkit-scrollbar-thumb:hover { - background: var(--astrbot-scrollbar-thumb-hover); - transform: scale(1.05); - box-shadow: 0 2px 8px var(--astrbot-scrollbar-thumb-shadow); -} - -::-webkit-scrollbar-thumb:active { - background: var(--astrbot-scrollbar-thumb-active); -} - -::-webkit-scrollbar-corner { - background: transparent; -} - -/* 细滚动条变体 */ -.thin-scrollbar { - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-thumb { - background: var(--astrbot-scrollbar-thumb); - border: none; - } -} - -/* 聊天区域滚动条 */ -.chat-scrollbar { - ::-webkit-scrollbar { - width: 8px; - } - - ::-webkit-scrollbar-track { - background: var(--astrbot-scrollbar-track); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb { - background: var(--astrbot-scrollbar-thumb); - border-radius: 4px; - border: 1px solid var(--astrbot-scrollbar-thumb-border); - } - - ::-webkit-scrollbar-thumb:hover { - background: var(--astrbot-scrollbar-thumb-hover); - } -} - -/* 隐藏滚动条变体 */ -.hidden-scrollbar { - ::-webkit-scrollbar { - width: 0px; - height: 0px; - } - - scrollbar-width: none; - -ms-overflow-style: none; -} - -/* Firefox 兼容性 */ -* { - scrollbar-width: thin; - scrollbar-color: var(--astrbot-scrollbar-thumb) var(--astrbot-scrollbar-track); -} - -/* 平滑滚动 */ -html { - scroll-behavior: smooth; -} - -/* 移动端触摸滚动优化 */ -* { - -webkit-overflow-scrolling: touch; -} \ No newline at end of file diff --git a/legacy-dashboard/src/scss/components/_VShadow.scss b/legacy-dashboard/src/scss/components/_VShadow.scss deleted file mode 100644 index aebe0e8e..00000000 --- a/legacy-dashboard/src/scss/components/_VShadow.scss +++ /dev/null @@ -1,3 +0,0 @@ -.elevation-10 { - box-shadow: $box-shadow !important; -} diff --git a/legacy-dashboard/src/scss/components/_VTabs.scss b/legacy-dashboard/src/scss/components/_VTabs.scss deleted file mode 100644 index afb08fd4..00000000 --- a/legacy-dashboard/src/scss/components/_VTabs.scss +++ /dev/null @@ -1,11 +0,0 @@ -.theme-tab { - &.v-tabs { - .v-tab { - border-radius: $border-radius-root !important; - min-width: auto !important; - &.v-slide-group-item--active { - background: rgb(var(--v-theme-primary)); - } - } - } -} diff --git a/legacy-dashboard/src/scss/components/_VTextField.scss b/legacy-dashboard/src/scss/components/_VTextField.scss deleted file mode 100644 index 3f7c5ff9..00000000 --- a/legacy-dashboard/src/scss/components/_VTextField.scss +++ /dev/null @@ -1,3 +0,0 @@ -.v-text-field input { - font-size: 0.8rem; -} diff --git a/legacy-dashboard/src/scss/layout/_container.scss b/legacy-dashboard/src/scss/layout/_container.scss deleted file mode 100644 index c2e71b84..00000000 --- a/legacy-dashboard/src/scss/layout/_container.scss +++ /dev/null @@ -1,119 +0,0 @@ -html { - overflow-y: auto; -} -.v-main { - margin: 0; -} - -.top-header { - border-bottom: 1px solid rgba(var(--v-theme-borderLight), 0.5); -} -.spacer { - padding: 100px 0; -} -@media (max-width: 800px) { - .spacer { - padding: 40px 0; - } -} - -.page-wrapper { - min-height: calc(100vh - 100px); - padding: 8px; - // border-radius: $border-radius-root; - background: rgb(var(--v-theme-containerBg)); -} -$sizes: ( - 'display-1': 44px, - 'display-2': 40px, - 'display-3': 30px, - 'h1': 36px, - 'h2': 30px, - 'h3': 21px, - 'h4': 18px, - 'h5': 16px, - 'h6': 14px, - 'text-8': 8px, - 'text-10': 10px, - 'text-13': 13px, - 'text-18': 18px, - 'text-20': 20px, - 'text-24': 24px, - 'body-text-1': 10px -); - -@each $pixel, $size in $sizes { - .#{$pixel} { - font-size: $size; - line-height: $size + 10; - } -} - -.customizer-btn { - position: fixed; - top: 25%; - right: 10px; - border-radius: 50% 50% 4px; - .icon { - animation: progress-circular-rotate 1.4s linear infinite; - transform-origin: center center; - transition: all 0.2s ease-in-out; - } -} -.w-100 { - width: 100%; -} - -.h-100vh { - height: 100vh; -} - -.gap-3 { - gap: 16px; -} - -.text-white { - color: rgb(255, 255, 255) !important; -} - -// font family - -body { - .Outfit { - font-family: $body-font-family !important; - } -} - -@keyframes blink { - 50% { - opacity: 0; - } - 100% { - opacity: 1; - } -} -@keyframes bounce { - 0%, - 20%, - 53%, - to { - animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - transform: translateZ(0); - } - 40%, - 43% { - animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); - transform: translate3d(0, -5px, 0); - } - 70% { - animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); - transform: translate3d(0, -7px, 0); - } - 80% { - transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); - transform: translateZ(0); - } - 90% { - transform: translate3d(0, -2px, 0); - } -} diff --git a/legacy-dashboard/src/scss/layout/_sidebar.scss b/legacy-dashboard/src/scss/layout/_sidebar.scss deleted file mode 100644 index 52f9e404..00000000 --- a/legacy-dashboard/src/scss/layout/_sidebar.scss +++ /dev/null @@ -1,124 +0,0 @@ -/*This is for the logo*/ -.leftSidebar { - border: 0px; - border-right: 1px solid rgba(var(--v-theme-borderLight), 0.45); - box-shadow: none !important; -} -.listitem { - overflow-y: auto; - .v-list { - color: rgb(var(--v-theme-secondaryText)); - } - .v-list-group__items .v-list-item, - .v-list-item { - border-radius: $border-radius-root; - padding-inline-start: calc(12px + var(--indent-padding) / 2) !important; - &:hover { - color: rgb(var(--v-theme-secondary)); - } - } - .v-list-item--density-default.v-list-item--one-line { - min-height: 40px; - } -} - -// 深色主题下的侧边栏悬停和选中样式 -.v-theme--PurpleThemeDark .leftSidebar { - .listitem { - .v-list-group__items .v-list-item, - .v-list-item { - &:hover { - color: rgb(var(--v-theme-primary)) !important; - - .v-list-item-title { - color: rgb(var(--v-theme-primary)) !important; - } - - .v-icon { - color: rgb(var(--v-theme-primary)) !important; - } - } - - // 选中状态的样式 - &.v-list-item--active { - color: rgb(var(--v-theme-primary)) !important; - - .v-list-item-title { - color: rgb(var(--v-theme-primary)) !important; - } - - .v-icon { - color: rgb(var(--v-theme-primary)) !important; - } - } - } - } - .v-list-item--density-default.v-list-item--one-line { - min-height: 40px; - } -} -.v-navigation-drawer--rail { - .scrollnavbar .v-list .v-list-group__items, - .hide-menu { - opacity: 1; - } - .leftPadding { - margin-left: 0px; - } -} -@media only screen and (min-width: 1170px) { - .mini-sidebar { - .logo { - width: 90px; - overflow: hidden; - } - .leftSidebar:hover { - box-shadow: $box-shadow !important; - } - .v-navigation-drawer--expand-on-hover:hover { - .logo { - width: 100%; - } - .v-list .v-list-group__items, - .hide-menu { - opacity: 1; - } - } - } -} - -// 新的flex布局样式 -.leftSidebar { - .sidebar-container { - display: flex; - flex-direction: column; - height: 100%; - - .flex-grow-1 { - flex: 1 1 auto; - overflow-y: auto; - } - - .sidebar-footer { - padding: 16px; - background: inherit; - display: flex; - flex-direction: column; - align-items: center; - font-size: 13px; - text-align: center; - flex-shrink: 0; - - .v-btn { - width: 100%; - max-width: 180px; - margin-bottom: 8px !important; - } - - .sidebar-footer-btn { - justify-content: flex-start; - text-align: left; - } - } - } -} diff --git a/legacy-dashboard/src/scss/pages/_dashboards.scss b/legacy-dashboard/src/scss/pages/_dashboards.scss deleted file mode 100644 index aeb4c9f5..00000000 --- a/legacy-dashboard/src/scss/pages/_dashboards.scss +++ /dev/null @@ -1,53 +0,0 @@ -.bubble-shape { - position: relative; - &:before { - content: ''; - position: absolute; - width: 210px; - height: 210px; - border-radius: 50%; - top: -125px; - right: -15px; - opacity: 0.5; - } - &:after { - content: ''; - position: absolute; - width: 210px; - height: 210px; - border-radius: 50%; - top: -85px; - right: -95px; - } -} - -.z-1 { - z-index: 1; - position: relative; -} -.bubble-shape-sm { - position: relative; - &::before { - content: ''; - position: absolute; - width: 210px; - height: 210px; - border-radius: 50%; - top: -160px; - right: -130px; - } - &::after { - content: ''; - position: absolute; - width: 210px; - height: 210px; - border-radius: 50%; - top: -30px; - right: -180px; - } -} - -.rounded-square { - width: 20px; - height: 20px; -} diff --git a/legacy-dashboard/src/scss/style.scss b/legacy-dashboard/src/scss/style.scss deleted file mode 100644 index 6e02010b..00000000 --- a/legacy-dashboard/src/scss/style.scss +++ /dev/null @@ -1,22 +0,0 @@ -@import './variables'; -@import 'vuetify/styles/main.sass'; -@import './override'; -@import './layout/container'; -@import './layout/sidebar'; - -@import './components/VButtons'; -@import './components/VCard'; -@import './components/VField'; -@import './components/VInput'; -@import './components/VNavigationDrawer'; -@import './components/VShadow'; -@import './components/VTextField'; -@import './components/VTabs'; -@import './components/VScrollbar'; -@import './components/CodeBlockDark'; - -@import './pages/dashboards'; - -html, body { - overscroll-behavior-y: none; -} diff --git a/legacy-dashboard/src/stores/auth.ts b/legacy-dashboard/src/stores/auth.ts deleted file mode 100644 index 95a6a160..00000000 --- a/legacy-dashboard/src/stores/auth.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { defineStore } from 'pinia'; -import { router } from '@/router'; -import { - authApi, - providerApi, - systemConfigApi, - UPGRADE_RECOVERY_EVENT, - UPGRADE_RECOVERY_TOKEN_KEY, - type ApiEnvelope, - type VersionData, -} from '@/api/v1'; -import { httpClient } from '@/api/http'; - -export const useAuthStore = defineStore("auth", { - state: () => ({ - // @ts-ignore - username: '', - returnUrl: null, - }), - actions: { - async finishAuthenticatedSession(data: any): Promise { - this.username = data.username; - localStorage.setItem('user', this.username); - localStorage.setItem('token', data.token); - const passwordUpgradeRequired = !!data?.password_upgrade_required; - const md5PwdHint = !!data?.md5_pwd_hint; - const passwordWarning = - !!data?.change_pwd_hint || - (md5PwdHint && !passwordUpgradeRequired); - if (passwordWarning) { - localStorage.setItem('change_pwd_hint', 'true'); - if (md5PwdHint && !passwordUpgradeRequired) { - localStorage.setItem('md5_pwd_hint', 'true'); - } else { - localStorage.removeItem('md5_pwd_hint'); - } - } else { - localStorage.removeItem('change_pwd_hint'); - localStorage.removeItem('md5_pwd_hint'); - } - if (passwordUpgradeRequired) { - localStorage.setItem('password_upgrade_required', 'true'); - } else { - localStorage.removeItem('password_upgrade_required'); - } - - const onboardingCompleted = await this.checkOnboardingCompleted(); - this.returnUrl = null; - if (passwordWarning) { - router.push('/auth/setup'); - return; - } - if (onboardingCompleted) { - router.push('/dashboard/default'); - } else { - router.push('/welcome'); - } - }, - async login( - username: string, - password: string, - code?: string, - trustDeviceToken = false, - ): Promise<'totp_required' | 'upgrade_recovery_required' | void> { - try { - const res = await authApi.login({ - username, - password, - code, - trust_device_flag: trustDeviceToken, - }); - - if (res.data.status === 'error') { - return Promise.reject(res.data.message); - } - - const legacyToken = String(res.data.data?.token || ''); - if (res.legacyFallback && legacyToken) { - const versionRes = await httpClient.get>( - '/api/stat/version', - { - headers: { - Authorization: `Bearer ${legacyToken}`, - }, - validateStatus: () => true, - }, - ); - const versionData = versionRes.data?.data || {}; - const coreVersion = String(versionData.version || '') - .trim() - .replace(/^v/i, ''); - const dashboardVersion = String(versionData.dashboard_version || '') - .trim() - .replace(/^v/i, ''); - if ( - versionRes.status < 400 && - coreVersion && - dashboardVersion && - coreVersion !== dashboardVersion - ) { - sessionStorage.setItem(UPGRADE_RECOVERY_TOKEN_KEY, legacyToken); - window.dispatchEvent( - new CustomEvent(UPGRADE_RECOVERY_EVENT, { - detail: { - version: versionData.version, - dashboard_version: versionData.dashboard_version, - blocking: true, - }, - }), - ); - return 'upgrade_recovery_required'; - } - } - - await this.finishAuthenticatedSession(res.data.data); - } catch (error: any) { - if (error?.response?.status === 401 && error.response?.data?.data?.totp_required) { - return 'totp_required'; - } - return Promise.reject(error?.response?.data?.message || error); - } - }, - async setup( - username: string, - password: string, - confirmPassword: string, - ): Promise { - try { - const res = await authApi.setup({ - username, - password, - confirm_password: confirmPassword, - }); - - if (res.data.status === 'error') { - return Promise.reject(res.data.message); - } - - await this.finishAuthenticatedSession(res.data.data); - } catch (error) { - return Promise.reject(error); - } - }, - async checkOnboardingCompleted(): Promise { - try { - // 1. 检查平台配置 - const platformRes = await systemConfigApi.get(); - const systemConfig = (platformRes.data.data as any).config || {}; - const hasPlatform = (systemConfig.platform || []).length > 0; - if (!hasPlatform) return false; - - // 2. 检查提供者配置 - const providerRes = await providerApi.schema(); - const providers = providerRes.data.data?.providers || []; - const sources = providerRes.data.data?.provider_sources || []; - const sourceMap = new Map(); - sources.forEach((s: any) => sourceMap.set(s.id, s.provider_type)); - - const hasProvider = providers.some((provider: any) => { - if (provider.provider_type) return provider.provider_type === 'chat_completion'; - if (provider.provider_source_id) { - const type = sourceMap.get(provider.provider_source_id); - if (type === 'chat_completion') return true; - } - return String(provider.type || '').includes('chat_completion'); - }); - - return hasProvider; - } catch (e) { - console.error('Failed to check onboarding status:', e); - return false; - } - }, - logout() { - this.username = ''; - localStorage.removeItem('user'); - localStorage.removeItem('token'); - localStorage.removeItem('change_pwd_hint'); - localStorage.removeItem('md5_pwd_hint'); - localStorage.removeItem('password_upgrade_required'); - void authApi.logout().catch(() => undefined); - router.push('/auth/login'); - }, - has_token(): boolean { - return !!localStorage.getItem('token'); - } - } -}); diff --git a/legacy-dashboard/src/stores/chatHeader.ts b/legacy-dashboard/src/stores/chatHeader.ts deleted file mode 100644 index f3e0afbd..00000000 --- a/legacy-dashboard/src/stores/chatHeader.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { defineStore } from "pinia"; - -export const useChatHeaderStore = defineStore("chatHeader", { - state: () => ({ - title: "", - subtitle: "", - }), - - actions: { - SET_CONTEXT(payload: { title?: string; subtitle?: string }) { - this.title = payload.title || ""; - this.subtitle = payload.subtitle || ""; - }, - CLEAR_CONTEXT() { - this.title = ""; - this.subtitle = ""; - }, - }, -}); diff --git a/legacy-dashboard/src/stores/common.js b/legacy-dashboard/src/stores/common.js deleted file mode 100644 index cfcdb2ee..00000000 --- a/legacy-dashboard/src/stores/common.js +++ /dev/null @@ -1,286 +0,0 @@ -import { defineStore } from "pinia"; -import { logApi, pluginApi, statsApi } from "@/api/v1"; -import { fetchWithAuth } from "@/api/http"; - -export const useCommonStore = defineStore("common", { - state: () => ({ - // @ts-ignore - eventSource: null, - log_cache: [], - sse_connected: false, - - log_cache_max_len: 1000, - startTime: -1, - astrbotVersion: "", - dashboardVersion: "", - - pluginMarketData: [], - pluginMarketDataBySource: {}, - }), - actions: { - async createEventSource() { - if (this.eventSource) { - return; - } - const controller = new AbortController(); - const { signal } = controller; - - // 注意:这里如果之前改过 Polyfill 的话,可能需要保持原样 - // 如果是用 fetch 的话,这里是支持 Authorization Header 的 - const headers = { - "Content-Type": "multipart/form-data", - Authorization: "Bearer " + localStorage.getItem("token"), - }; - - fetchWithAuth(logApi.liveUrl(), { - method: "GET", - headers, - signal, - cache: "no-cache", - }) - .then((response) => { - if (!response.ok) { - throw new Error(`SSE connection failed: ${response.status}`); - } - console.log("SSE stream opened"); - this.sse_connected = true; - - const reader = response.body.getReader(); - const decoder = new TextDecoder(); - let bufferedText = ""; - - const processStream = ({ done, value }) => { - if (done) { - console.log("SSE stream closed"); - setTimeout(() => { - this.eventSource = null; - this.createEventSource(); - }, 2000); - return; - } - - // Accumulate partial chunks; SSE data may split JSON across reads. - const text = decoder.decode(value, { stream: true }); - bufferedText += text; - - // Split completed events; keep the trailing partial in buffer. - const segments = bufferedText.split("\n\n"); - bufferedText = segments.pop() || ""; - - segments.forEach((segment) => { - const line = segment.trim(); - if (!line.startsWith("data: ")) { - return; - } - - const logLine = line.replace("data: ", "").trim(); - if (!logLine) { - return; - } - - try { - const logObject = JSON.parse(logLine); - - // 修复:兼容 HTTP 环境的 UUID 生成 - if (!logObject.uuid) { - if ( - typeof crypto !== "undefined" && - typeof crypto.randomUUID === "function" - ) { - logObject.uuid = crypto.randomUUID(); - } else { - // 手动生成 UUID v4 - logObject.uuid = - "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace( - /[xy]/g, - function (c) { - var r = (Math.random() * 16) | 0, - v = c == "x" ? r : (r & 0x3) | 0x8; - return v.toString(16); - }, - ); - } - } - - this.log_cache.push(logObject); - // Limit log cache size - if (this.log_cache.length > this.log_cache_max_len) { - this.log_cache.splice( - 0, - this.log_cache.length - this.log_cache_max_len, - ); - } - } catch (err) { - console.warn( - "Failed to parse SSE log line, skipping:", - err, - logLine, - ); - } - }); - - return reader.read().then(processStream); - }; - - reader.read().then(processStream); - }) - .catch((error) => { - console.error("SSE error:", error); - // Attempt to reconnect after a delay - this.log_cache.push({ - type: "log", - level: "ERROR", - time: Date.now() / 1000, - data: "SSE Connection failed, retrying in 5 seconds...", - uuid: "error-" + Date.now(), - }); - setTimeout(() => { - this.eventSource = null; - this.createEventSource(); - }, 1000); - }); - - // Store controller to allow closing the connection - this.eventSource = controller; - }, - closeEventSourcet() { - if (this.eventSource) { - this.eventSource.abort(); - this.eventSource = null; - } - }, - getLogCache() { - return this.log_cache; - }, - async fetchStartTime() { - const res = await statsApi.startTime(); - this.startTime = res.data.data.start_time; - return this.startTime; - }, - setAstrBotVersion(version, dashboardVersion = "") { - this.astrbotVersion = String(version || "").replace(/^v/i, ""); - this.dashboardVersion = String(dashboardVersion || ""); - }, - async fetchAstrBotVersion(force = false) { - if (!force && this.astrbotVersion) { - return this.astrbotVersion; - } - const res = await statsApi.version(); - const data = res.data?.data || {}; - this.setAstrBotVersion(data.version, data.dashboard_version); - return this.astrbotVersion; - }, - getStartTime() { - if (this.startTime !== -1) { - return this.startTime; - } - this.fetchStartTime().catch(() => {}); - return this.startTime; - }, - async getPluginCollections(force = false, customSource = null) { - // 获取插件市场数据 - const sourceKey = String(customSource || "") - .trim() - .replace(/\/+$/, ""); - if (!force) { - if (!sourceKey && this.pluginMarketData.length > 0) { - return Promise.resolve(this.pluginMarketData); - } - if ( - sourceKey && - Array.isArray(this.pluginMarketDataBySource[sourceKey]) - ) { - return Promise.resolve(this.pluginMarketDataBySource[sourceKey]); - } - } - - return pluginApi - .market({ - force_refresh: force || undefined, - custom_registry: sourceKey || undefined, - }) - .then((res) => { - let data = []; - if (res.data.data && typeof res.data.data === "object") { - for (let key in res.data.data) { - if (key === "$meta") { - continue; - } - - const pluginData = res.data.data[key]; - const fallbackPluginName = String(key || "").includes("/") - ? "" - : String(key || "").trim(); - const pluginAuthor = String(pluginData?.author || "").trim(); - const pluginName = - String(pluginData?.name || "").trim() || fallbackPluginName; - const displayPluginName = pluginName || key; - const marketPluginId = - String(pluginData?.market_plugin_id || "").trim() || - (pluginAuthor && pluginName - ? `${pluginAuthor}/${pluginName}` - : ""); - const parsedDownloadCount = Number(pluginData?.download_count); - const downloadCount = - pluginData?.download_count === undefined || - pluginData?.download_count === null || - pluginData?.download_count === "" || - !Number.isFinite(parsedDownloadCount) - ? undefined - : Math.max(0, Math.trunc(parsedDownloadCount)); - - data.push({ - ...pluginData, - name: displayPluginName, // 优先使用插件数据中的name字段,否则使用键名 - market_plugin_id: marketPluginId, - desc: pluginData.desc, - short_desc: pluginData?.short_desc ? pluginData.short_desc : "", - author: pluginData.author, - repo: pluginData.repo, - installed: false, - version: pluginData?.version ? pluginData.version : "未知", - social_link: pluginData?.social_link, - tags: pluginData?.tags ? pluginData.tags : [], - logo: pluginData?.logo ? pluginData.logo : "", - pinned: pluginData?.pinned ? pluginData.pinned : false, - stars: pluginData?.stars ? pluginData.stars : 0, - download_count: downloadCount, - updated_at: pluginData?.updated_at ? pluginData.updated_at : "", - download_url: pluginData?.download_url - ? pluginData.download_url - : "", - display_name: pluginData?.display_name - ? pluginData.display_name - : "", - i18n: - pluginData?.i18n && typeof pluginData.i18n === "object" - ? pluginData.i18n - : {}, - astrbot_version: pluginData?.astrbot_version - ? pluginData.astrbot_version - : "", - category: pluginData?.category ? pluginData.category : "", - support_platforms: Array.isArray(pluginData?.support_platforms) - ? pluginData.support_platforms - : Array.isArray(pluginData?.support_platform) - ? pluginData.support_platform - : Array.isArray(pluginData?.platform) - ? pluginData.platform - : [], - }); - } - } - - if (sourceKey) { - this.pluginMarketDataBySource[sourceKey] = data; - } else { - this.pluginMarketData = data; - } - return data; - }) - .catch((err) => { - return Promise.reject(err); - }); - }, - }, -}); diff --git a/legacy-dashboard/src/stores/customizer.ts b/legacy-dashboard/src/stores/customizer.ts deleted file mode 100644 index e68822a0..00000000 --- a/legacy-dashboard/src/stores/customizer.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { defineStore } from 'pinia'; -import config, { type ThemeMode, resolveUiTheme } from '@/config'; - -const DARK_THEMES: ReadonlySet = new Set(['PurpleThemeDark']); - - -export const useCustomizerStore = defineStore('customizer', { - state: () => ({ - Sidebar_drawer: config.Sidebar_drawer, - Customizer_drawer: config.Customizer_drawer, - mini_sidebar: config.mini_sidebar, - fontTheme: 'Noto Sans SC', - uiTheme: config.uiTheme, - themeMode: config.themeMode as ThemeMode, - inputBg: config.inputBg, - chatSidebarOpen: false, // chat mode mobile sidebar state - chatSidebarCollapsed: false, // chat mode desktop sidebar state - }), - - getters: { - isDark: (state) => state.uiTheme ? DARK_THEMES.has(state.uiTheme) : false, - }, - - actions: { - SET_SIDEBAR_DRAWER() { - this.Sidebar_drawer = !this.Sidebar_drawer; - }, - SET_MINI_SIDEBAR(payload: boolean) { - this.mini_sidebar = payload; - }, - SET_FONT(payload: string) { - this.fontTheme = payload; - }, - - SET_UI_THEME(payload: string) { - this.uiTheme = payload; - localStorage.setItem('uiTheme', payload); - const mode: ThemeMode = payload === 'PurpleThemeDark' ? 'dark' : 'light'; - this.themeMode = mode; - localStorage.setItem('themeMode', mode); - }, - - SET_THEME_MODE(mode: ThemeMode) { - this.themeMode = mode; - localStorage.setItem('themeMode', mode); - const uiTheme = resolveUiTheme(mode); - this.uiTheme = uiTheme; - localStorage.setItem('uiTheme', uiTheme); - }, - - TOGGLE_CHAT_SIDEBAR() { - this.chatSidebarOpen = !this.chatSidebarOpen; - }, - SET_CHAT_SIDEBAR(payload: boolean) { - this.chatSidebarOpen = payload; - }, - SET_CHAT_SIDEBAR_COLLAPSED(payload: boolean) { - this.chatSidebarCollapsed = payload; - }, - }, -}); diff --git a/legacy-dashboard/src/stores/personaStore.ts b/legacy-dashboard/src/stores/personaStore.ts deleted file mode 100644 index d1d976f6..00000000 --- a/legacy-dashboard/src/stores/personaStore.ts +++ /dev/null @@ -1,322 +0,0 @@ -/** - * Persona 文件夹管理 Store - */ -import { defineStore } from 'pinia'; -import { personaApi } from '@/api/v1'; - -// 类型定义 -export interface PersonaFolder { - folder_id: string; - name: string; - parent_id: string | null; - description: string | null; - sort_order: number; - created_at: string; - updated_at: string; -} - -export interface Persona { - persona_id: string; - system_prompt: string; - custom_error_message: string | null; - begin_dialogs: string[]; - tools: string[] | null; - skills: string[] | null; - folder_id: string | null; - sort_order: number; - created_at: string; - updated_at: string; -} - -export interface FolderTreeNode { - folder_id: string; - name: string; - parent_id: string | null; - description: string | null; - sort_order: number; - children: FolderTreeNode[]; -} - -export interface ReorderItem { - id: string; - type: 'persona' | 'folder'; - sort_order: number; -} - -export const usePersonaStore = defineStore("persona", { - state: () => ({ - folderTree: [] as FolderTreeNode[], - currentFolderId: null as string | null, - currentFolders: [] as PersonaFolder[], - currentPersonas: [] as Persona[], - breadcrumbPath: [] as FolderTreeNode[], - expandedFolderIds: [] as string[], // Store expanded folder IDs - loading: false, - treeLoading: false, - }), - - getters: { - // 当前文件夹名称 - currentFolderName(): string { - if (this.breadcrumbPath.length === 0) { - return '根目录'; - } - return this.breadcrumbPath[this.breadcrumbPath.length - 1]?.name || '根目录'; - }, - }, - - actions: { - /** - * Toggle folder expansion state - */ - toggleFolderExpansion(folderId: string) { - const index = this.expandedFolderIds.indexOf(folderId); - if (index === -1) { - this.expandedFolderIds.push(folderId); - } else { - this.expandedFolderIds.splice(index, 1); - } - }, - - /** - * Set folder expansion state - */ - setFolderExpansion(folderId: string, expanded: boolean) { - const index = this.expandedFolderIds.indexOf(folderId); - if (expanded && index === -1) { - this.expandedFolderIds.push(folderId); - } else if (!expanded && index !== -1) { - this.expandedFolderIds.splice(index, 1); - } - }, - - /** - * 加载文件夹树形结构 - */ - async loadFolderTree(): Promise { - this.treeLoading = true; - try { - const response = await personaApi.tree(); - if (response.data.status === 'ok') { - this.folderTree = response.data.data || []; - } else { - throw new Error(response.data.message || '获取文件夹树失败'); - } - } finally { - this.treeLoading = false; - } - }, - - /** - * 导航到指定文件夹 - */ - async navigateToFolder(folderId: string | null): Promise { - this.loading = true; - try { - this.currentFolderId = folderId; - - // 并行加载子文件夹和 Persona - const [foldersRes, personasRes] = await Promise.all([ - personaApi.folders(folderId), - personaApi.list(folderId), - ]); - - if (foldersRes.data.status === 'ok') { - this.currentFolders = foldersRes.data.data || []; - } - - if (personasRes.data.status === 'ok') { - this.currentPersonas = personasRes.data.data || []; - } - - // 更新面包屑 - this.updateBreadcrumb(folderId); - } finally { - this.loading = false; - } - }, - - /** - * 更新面包屑路径 - */ - updateBreadcrumb(folderId: string | null): void { - if (folderId === null) { - this.breadcrumbPath = []; - return; - } - - // 从树中查找路径 - const path: FolderTreeNode[] = []; - const findPath = (nodes: FolderTreeNode[], targetId: string): boolean => { - for (const node of nodes) { - if (node.folder_id === targetId) { - path.push(node); - return true; - } - if (node.children.length > 0 && findPath(node.children, targetId)) { - path.unshift(node); - return true; - } - } - return false; - }; - - findPath(this.folderTree, folderId); - this.breadcrumbPath = path; - }, - - /** - * 刷新当前文件夹内容 - */ - async refreshCurrentFolder(): Promise { - await this.navigateToFolder(this.currentFolderId); - }, - - /** - * 移动 Persona 到文件夹 - */ - async movePersonaToFolder(personaId: string, targetFolderId: string | null): Promise { - const response = await personaApi.move(personaId, targetFolderId); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '移动人格失败'); - } - - // 刷新当前文件夹内容和文件夹树 - await Promise.all([ - this.refreshCurrentFolder(), - this.loadFolderTree(), - ]); - }, - - /** - * 移动文件夹到另一个文件夹 - */ - async moveFolderToFolder(folderId: string, targetParentId: string | null): Promise { - const response = await personaApi.updateFolder(folderId, { - parent_id: targetParentId, - }); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '移动文件夹失败'); - } - - // 刷新当前文件夹内容和文件夹树 - await Promise.all([ - this.refreshCurrentFolder(), - this.loadFolderTree(), - ]); - }, - - /** - * 创建文件夹 - */ - async createFolder(data: { - name: string; - parent_id?: string | null; - description?: string; - }): Promise { - const response = await personaApi.createFolder({ - ...data, - parent_id: data.parent_id ?? this.currentFolderId, - }); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '创建文件夹失败'); - } - - // 刷新当前文件夹内容和文件夹树 - await Promise.all([ - this.refreshCurrentFolder(), - this.loadFolderTree(), - ]); - - return response.data.data.folder as PersonaFolder; - }, - - /** - * 更新文件夹 - */ - async updateFolder(data: { - folder_id: string; - name?: string; - description?: string; - }): Promise { - const response = await personaApi.updateFolder(data.folder_id, data); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '更新文件夹失败'); - } - - // 刷新当前文件夹内容和文件夹树 - await Promise.all([ - this.refreshCurrentFolder(), - this.loadFolderTree(), - ]); - }, - - /** - * 删除文件夹 - */ - async deleteFolder(folderId: string): Promise { - const response = await personaApi.deleteFolder(folderId); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '删除文件夹失败'); - } - - // 刷新当前文件夹内容和文件夹树 - await Promise.all([ - this.refreshCurrentFolder(), - this.loadFolderTree(), - ]); - }, - - /** - * 删除 Persona - */ - async deletePersona(personaId: string): Promise { - const response = await personaApi.delete(personaId); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '删除人格失败'); - } - - // 刷新当前文件夹内容 - await this.refreshCurrentFolder(); - }, - - /** - * 批量更新排序 - */ - async reorderItems(items: ReorderItem[]): Promise { - const response = await personaApi.reorder(items); - - if (response.data.status !== 'ok') { - throw new Error(response.data.message || '更新排序失败'); - } - - // 刷新当前文件夹内容 - await this.refreshCurrentFolder(); - }, - - /** - * 根据文件夹 ID 查找树节点 - */ - findFolderInTree(folderId: string): FolderTreeNode | null { - const findNode = (nodes: FolderTreeNode[]): FolderTreeNode | null => { - for (const node of nodes) { - if (node.folder_id === folderId) { - return node; - } - if (node.children.length > 0) { - const found = findNode(node.children); - if (found) return found; - } - } - return null; - }; - return findNode(this.folderTree); - }, - } -}); diff --git a/legacy-dashboard/src/stores/routerLoading.ts b/legacy-dashboard/src/stores/routerLoading.ts deleted file mode 100644 index 747d2810..00000000 --- a/legacy-dashboard/src/stores/routerLoading.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { defineStore } from 'pinia'; -import { ref } from 'vue'; - -export const useRouterLoadingStore = defineStore('routerLoading', () => { - const isLoading = ref(false); - const progress = ref(0); - let progressInterval: ReturnType | null = null; - - function start() { - isLoading.value = true; - progress.value = 0; - - if (progressInterval) { - clearInterval(progressInterval); - } - - let currentProgress = 0; - progressInterval = setInterval(() => { - if (currentProgress < 80) { - // 快速阶段:0-80% - currentProgress += Math.random() * 20 + 10; - if (currentProgress > 80) { - currentProgress = 80; - } - } else if (currentProgress < 90) { - // 缓慢阶段:80-90% - currentProgress += Math.random() * 3 + 1; - if (currentProgress > 90) { - currentProgress = 90; - } - } - progress.value = Math.min(currentProgress, 90); - }, 50); - } - - function finish() { - // 清理interval - if (progressInterval) { - clearInterval(progressInterval); - progressInterval = null; - } - - // 快速完成到100% - progress.value = 100; - - // 延迟隐藏,让用户看到100% - setTimeout(() => { - isLoading.value = false; - progress.value = 0; - }, 300); - } - - return { - isLoading, - progress, - start, - finish - }; -}); - diff --git a/legacy-dashboard/src/stores/toast.js b/legacy-dashboard/src/stores/toast.js deleted file mode 100644 index 87ae80bc..00000000 --- a/legacy-dashboard/src/stores/toast.js +++ /dev/null @@ -1,31 +0,0 @@ -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' - -export const useToastStore = defineStore('toast', () => { - const queue = ref([]) - const current = computed(() => queue.value[0]) - - function add({ - message, - color = 'info', // Vuetify 颜色 - timeout = 3000, - closable = true, - multiLine = false, - location = 'top center' - }) { - queue.value.push({ - message, - color, - timeout, - closable, - multiLine, - location - }) - } - - function shift() { - queue.value.shift() - } - - return { current, add, shift } -}) diff --git a/legacy-dashboard/src/styles/dashboard-shell.css b/legacy-dashboard/src/styles/dashboard-shell.css deleted file mode 100644 index 7398d3b1..00000000 --- a/legacy-dashboard/src/styles/dashboard-shell.css +++ /dev/null @@ -1,283 +0,0 @@ -.dashboard-page { - --dashboard-bg: rgb(var(--v-theme-background)); - --dashboard-surface: rgb(var(--v-theme-surface)); - --dashboard-text: rgb(var(--v-theme-on-surface)); - --dashboard-muted: rgba(var(--v-theme-on-surface), 0.68); - --dashboard-subtle: rgba(var(--v-theme-on-surface), 0.56); - --dashboard-border: rgba(var(--v-theme-on-surface), 0.1); - --dashboard-border-strong: rgba(var(--v-theme-on-surface), 0.14); - --dashboard-soft: rgba(var(--v-theme-primary), 0.08); - --dashboard-soft-strong: rgba(var(--v-theme-primary), 0.14); - min-height: 100%; - background: var(--dashboard-bg); -} - -.dashboard-page.is-dark { - --dashboard-border: rgba(var(--v-theme-on-surface), 0.14); - --dashboard-border-strong: rgba(var(--v-theme-on-surface), 0.18); - --dashboard-soft: rgba(var(--v-theme-primary), 0.12); - --dashboard-soft-strong: rgba(var(--v-theme-primary), 0.18); -} - -.dashboard-shell { - max-width: 1560px; - margin: 0 auto; - color: var(--dashboard-text); - font-family: "SF Pro Display", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -} - -.dashboard-header { - display: flex; - justify-content: space-between; - align-items: flex-end; - gap: 24px; - margin-bottom: 24px; -} - -.dashboard-header-main { - min-width: 0; -} - -.dashboard-title { - margin: 0; - font-size: 1.5rem; - line-height: 1.2; - font-weight: 700; - letter-spacing: 0; -} - -.dashboard-subtitle { - margin: 4px 0 0; - color: var(--dashboard-muted); - font-size: 0.875rem; - line-height: 1.6; - max-width: 860px; -} - -.dashboard-header-meta, -.dashboard-header-actions { - display: flex; - align-items: center; - gap: 10px; - flex-wrap: wrap; -} - -.dashboard-pill { - display: inline-flex; - align-items: center; - gap: 8px; - padding: 10px 14px; - border: 1px solid var(--dashboard-border); - border-radius: 999px; - background: var(--dashboard-surface); - color: var(--dashboard-muted); - font-size: 13px; -} - -.dashboard-overview-grid { - display: grid; - grid-template-columns: repeat(4, minmax(0, 1fr)); - gap: 16px; - margin-bottom: 20px; -} - -.dashboard-card { - border: 1px solid var(--dashboard-border); - border-radius: 16px; - background: var(--dashboard-surface); -} - -.dashboard-card--padded { - padding: 22px; -} - -.dashboard-overview-card { - padding: 20px 20px 18px; -} - -.dashboard-card-icon { - display: inline-flex; - align-items: center; - justify-content: center; - width: 34px; - height: 34px; - border-radius: 12px; - background: var(--dashboard-soft); - color: rgb(var(--v-theme-primary)); -} - -.dashboard-card-label { - margin-top: 12px; - color: var(--dashboard-muted); - font-size: 13px; - font-weight: 500; -} - -.dashboard-card-value { - margin-top: 8px; - font-size: clamp(24px, 2vw, 34px); - line-height: 1.1; - font-weight: 700; - letter-spacing: -0.03em; - overflow-wrap: anywhere; -} - -.dashboard-card-note { - margin-top: 8px; - color: var(--dashboard-subtle); - font-size: 12px; - line-height: 1.5; - overflow-wrap: anywhere; -} - -.dashboard-section-head { - display: flex; - justify-content: space-between; - align-items: flex-end; - gap: 16px; - margin-bottom: 16px; -} - -.dashboard-section-title { - font-size: 19px; - font-weight: 650; - letter-spacing: -0.02em; - line-height: 1.3; - overflow-wrap: anywhere; -} - -.dashboard-section-subtitle { - margin-top: 6px; - color: var(--dashboard-muted); - font-size: 13px; - line-height: 1.6; -} - -.dashboard-section-actions { - display: flex; - align-items: center; - justify-content: flex-end; - gap: 10px; - flex-wrap: wrap; -} - -.dashboard-split-grid { - display: grid; - grid-template-columns: 1.6fr 0.9fr; - gap: 20px; - align-items: stretch; -} - -.dashboard-split-grid > * { - min-width: 0; - width: 100%; -} - -.dashboard-side-column { - display: grid; - gap: 20px; - align-content: start; - min-width: 0; -} - -.dashboard-meta-list { - display: grid; - gap: 10px; - margin-top: 18px; - padding-top: 14px; - border-top: 1px solid var(--dashboard-border); -} - -.dashboard-meta-row, -.dashboard-list-row { - display: flex; - justify-content: space-between; - align-items: center; - gap: 12px; - min-width: 0; -} - -.dashboard-meta-row { - color: var(--dashboard-muted); - font-size: 14px; -} - -.dashboard-list { - display: grid; - gap: 12px; -} - -.dashboard-list-row { - padding: 12px 0; - border-bottom: 1px solid var(--dashboard-border); - font-size: 14px; -} - -.dashboard-list-row:last-child { - border-bottom: 0; -} - -.dashboard-list-label { - color: var(--dashboard-text); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.dashboard-empty { - color: var(--dashboard-muted); - font-size: 14px; -} - -.dashboard-scrollable { - max-height: 360px; - overflow-y: auto; - padding-right: 6px; -} - -.dashboard-form-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 16px; -} - -.dashboard-form-grid--single { - grid-template-columns: 1fr; -} - -.dashboard-dialog-card { - border: 1px solid var(--dashboard-border); - border-radius: 18px; -} - -@media (max-width: 1280px) { - .dashboard-overview-grid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } -} - -@media (max-width: 1080px) { - .dashboard-split-grid, - .dashboard-form-grid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 900px) { - .dashboard-header, - .dashboard-section-head { - flex-direction: column; - align-items: flex-start; - } -} - -@media (max-width: 640px) { - .dashboard-overview-grid { - grid-template-columns: 1fr; - } - - .dashboard-card--padded, - .dashboard-overview-card { - padding: 18px; - } -} diff --git a/legacy-dashboard/src/theme/DarkTheme.ts b/legacy-dashboard/src/theme/DarkTheme.ts deleted file mode 100644 index d1cedf4b..00000000 --- a/legacy-dashboard/src/theme/DarkTheme.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ThemeTypes } from '@/types/themeTypes/ThemeType'; - -const PurpleThemeDark: ThemeTypes = { - name: 'PurpleThemeDark', - dark: true, - variables: { - 'border-color': '#3c96ca', - 'carousel-control-size': 10 - }, - colors: { - primary: '#5ba4d4', - secondary: '#4a95c4', - info: '#03c9d7', - success: '#52c41a', - accent: '#FFAB91', - warning: '#faad14', - error: '#ff4d4f', - lightprimary: '#1a2e3d', - lightsecondary: '#1a2e3d', - lightsuccess: '#1a3a1a', - lighterror: '#3d1a1a', - lightwarning: '#3d351a', - primaryText: '#e8eaed', - secondaryText: '#ffffffdd', - darkprimary: '#3a8ab8', - darksecondary: '#3a8ab8', - borderLight: '#3a3a3a', - border: '#333333ee', - inputBorder: '#787878', - containerBg: '#1a1a1a', - surface: '#242424', - 'on-surface-variant': '#e0e0e0', - facebook: '#4267b2', - twitter: '#1da1f2', - linkedin: '#0e76a8', - gray100: '#cccccccc', - primary200: '#84c9ea', - secondary200: '#8cc4e1', - background: '#1a1a1a', - overlay: '#111111aa', - codeBg: '#282833', - preBg: 'rgb(23, 23, 23)', - code: '#ffffffdd', - chatMessageBubble: '#2d2e30', - mcpCardBg: '#2a2a2a', - } -}; - -export { PurpleThemeDark }; diff --git a/legacy-dashboard/src/theme/LightTheme.ts b/legacy-dashboard/src/theme/LightTheme.ts deleted file mode 100644 index a91e212a..00000000 --- a/legacy-dashboard/src/theme/LightTheme.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { ThemeTypes } from '@/types/themeTypes/ThemeType'; - -const PurpleTheme: ThemeTypes = { - name: 'PurpleTheme', - dark: false, - variables: { - 'border-color': '#1e88e5', - 'carousel-control-size': 10 - }, - colors: { - primary: '#3c96ca', - secondary: '#2f86bd', - info: '#03c9d7', - success: '#00c853', - accent: '#FFAB91', - warning: '#ffc107', - error: '#f44336', - lightprimary: '#eef2f6', - lightsecondary: '#e8f3fa', - lightsuccess: '#b9f6ca', - lighterror: '#f9d8d8', - lightwarning: '#fff8e1', - primaryText: '#1b1c1d', - secondaryText: '#000000aa', - darkprimary: '#1565c0', - darksecondary: '#236b99', - borderLight: '#d0d0d0', - border: '#d0d0d0', - inputBorder: '#787878', - containerBg: '#fffffff4', - surface: '#fff', - 'on-surface-variant': '#fff', - facebook: '#4267b2', - twitter: '#1da1f2', - linkedin: '#0e76a8', - gray100: '#fafafacc', - primary200: '#90caf9', - secondary200: '#8cc4e1', - background: '#ffffff', - overlay: '#ffffffaa', - codeBg: '#ececec', - preBg: 'rgb(249, 249, 249)', - code: 'rgb(13, 13, 13)', - chatMessageBubble: '#e7ebf4', - mcpCardBg: '#ecf2faff', - } -}; - -export { PurpleTheme }; diff --git a/legacy-dashboard/src/types/confirm.d.ts b/legacy-dashboard/src/types/confirm.d.ts deleted file mode 100644 index e29f634d..00000000 --- a/legacy-dashboard/src/types/confirm.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -import 'vue' - -import type { ConfirmDialogHandler } from '@/utils/confirmDialog' - -declare module 'vue' { - interface ComponentCustomProperties { - $confirm?: ConfirmDialogHandler - } -} - -export {} diff --git a/legacy-dashboard/src/types/desktop-bridge.d.ts b/legacy-dashboard/src/types/desktop-bridge.d.ts deleted file mode 100644 index a51500b1..00000000 --- a/legacy-dashboard/src/types/desktop-bridge.d.ts +++ /dev/null @@ -1,44 +0,0 @@ -export {}; - -declare global { - interface AstrBotDesktopAppUpdateCheckResult { - ok: boolean; - reason?: string | null; - currentVersion?: string; - latestVersion?: string | null; - hasUpdate: boolean; - } - - interface AstrBotDesktopAppUpdateResult { - ok: boolean; - reason?: string | null; - } - - interface AstrBotAppUpdaterBridge { - checkForAppUpdate: () => Promise; - installAppUpdate: () => Promise; - } - - interface Window { - astrbotAppUpdater?: AstrBotAppUpdaterBridge; - astrbotDesktop?: { - isDesktop: boolean; - isDesktopRuntime: () => Promise; - getBackendState: () => Promise<{ - running: boolean; - spawning: boolean; - restarting: boolean; - canManage: boolean; - }>; - restartBackend: (authToken?: string | null) => Promise<{ - ok: boolean; - reason: string | null; - }>; - stopBackend: () => Promise<{ - ok: boolean; - reason: string | null; - }>; - onTrayRestartBackend?: (callback: () => void) => () => void; - }; - } -} diff --git a/legacy-dashboard/src/types/themeTypes/ThemeType.ts b/legacy-dashboard/src/types/themeTypes/ThemeType.ts deleted file mode 100644 index 8d276004..00000000 --- a/legacy-dashboard/src/types/themeTypes/ThemeType.ts +++ /dev/null @@ -1,42 +0,0 @@ -export type ThemeTypes = { - name: string; - dark: boolean; - variables?: object; - colors: { - primary?: string; - secondary?: string; - info?: string; - success?: string; - accent?: string; - warning?: string; - error?: string; - lightprimary?: string; - lightsecondary?: string; - lightsuccess?: string; - lighterror?: string; - lightwarning?: string; - darkprimary?: string; - darksecondary?: string; - primaryText?: string; - secondaryText?: string; - borderLight?: string; - border?: string; - inputBorder?: string; - containerBg?: string; - surface?: string; - background?: string; - overlay?: string; - 'on-surface-variant'?: string; - facebook?: string; - twitter?: string; - linkedin?: string; - gray100?: string; - primary200?: string; - secondary200?: string; - codeBg?: string; - preBg?: string; - code?: string; - chatMessageBubble?: string; - mcpCardBg?: string; - }; -}; diff --git a/legacy-dashboard/src/types/vue3-print-nb.d.ts b/legacy-dashboard/src/types/vue3-print-nb.d.ts deleted file mode 100644 index 6c5f78e1..00000000 --- a/legacy-dashboard/src/types/vue3-print-nb.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'vue3-print-nb'; diff --git a/legacy-dashboard/src/types/vue_tabler_icon.d.ts b/legacy-dashboard/src/types/vue_tabler_icon.d.ts deleted file mode 100644 index 8afdef5d..00000000 --- a/legacy-dashboard/src/types/vue_tabler_icon.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { VNodeChild } from 'vue'; -declare module '@vue/runtime-dom' { - export interface HTMLAttributes { - $children?: VNodeChild; - } - export interface SVGAttributes { - $children?: VNodeChild; - strokeWidth?: string | number; - } -} diff --git a/legacy-dashboard/src/utils/chatConfigBinding.ts b/legacy-dashboard/src/utils/chatConfigBinding.ts deleted file mode 100644 index 192f25d9..00000000 --- a/legacy-dashboard/src/utils/chatConfigBinding.ts +++ /dev/null @@ -1,60 +0,0 @@ -export const CHAT_SELECTED_CONFIG_STORAGE_KEY = 'chat.selectedConfigId'; - -export type ChatMessageType = 'FriendMessage' | 'GroupMessage'; - -export interface WebchatUmoDetails { - platformId: string; - messageType: ChatMessageType; - username: string; - sessionKey: string; - umo: string; -} - -function getFromLocalStorage(key: string, fallback: string): string { - try { - if (typeof localStorage === 'undefined') { - return fallback; - } - const value = localStorage.getItem(key); - return value == null ? fallback : value; - } catch { - return fallback; - } -} - -function setToLocalStorage(key: string, value: string): void { - try { - if (typeof localStorage === 'undefined') { - return; - } - localStorage.setItem(key, value); - } catch { - // Ignore storage errors (e.g. private mode / restricted storage). - } -} - -export function getStoredDashboardUsername(): string { - return getFromLocalStorage('user', '').trim() || 'guest'; -} - -export function getStoredSelectedChatConfigId(): string { - return getFromLocalStorage(CHAT_SELECTED_CONFIG_STORAGE_KEY, '').trim() || 'default'; -} - -export function setStoredSelectedChatConfigId(configId: string): void { - setToLocalStorage(CHAT_SELECTED_CONFIG_STORAGE_KEY, configId); -} - -export function buildWebchatUmoDetails(sessionId: string, isGroup = false): WebchatUmoDetails { - const platformId = 'webchat'; - const username = getStoredDashboardUsername(); - const messageType: ChatMessageType = isGroup ? 'GroupMessage' : 'FriendMessage'; - const sessionKey = `${platformId}!${username}!${sessionId}`; - return { - platformId, - messageType, - username, - sessionKey, - umo: `${platformId}:${messageType}:${sessionKey}` - }; -} diff --git a/legacy-dashboard/src/utils/clipboard.ts b/legacy-dashboard/src/utils/clipboard.ts deleted file mode 100644 index 664a143f..00000000 --- a/legacy-dashboard/src/utils/clipboard.ts +++ /dev/null @@ -1,92 +0,0 @@ -interface CopyToClipboardOptions { - container?: HTMLElement | null; -} - -export async function copyToClipboard( - text: string, - options: CopyToClipboardOptions = {}, -): Promise { - const container = options.container; - const debugInfo = { - length: text?.length ?? 0, - trimmedLength: text?.trim().length ?? 0, - isSecureContext: typeof window !== "undefined" ? window.isSecureContext : false, - hasClipboardApi: - typeof navigator !== "undefined" && !!navigator.clipboard?.writeText, - containerTag: container?.tagName ?? null, - containerInBody: - typeof document !== "undefined" && !!container && document.body.contains(container), - }; - - if (!text) { - console.debug("[clipboard] empty text payload", debugInfo); - return false; - } - - console.debug("[clipboard] copy request", debugInfo); - - if (typeof navigator !== "undefined" && navigator.clipboard && window.isSecureContext) { - try { - await navigator.clipboard.writeText(text); - console.info("[clipboard] copied via Clipboard API", debugInfo); - return true; - } catch (err) { - console.warn("[clipboard] Clipboard API failed, falling back:", err, debugInfo); - } - } - - const fallbackOk = fallbackCopy(text, container); - if (fallbackOk) { - console.info("[clipboard] fallback succeeded via document.execCommand('copy')", debugInfo); - } else { - console.warn("[clipboard] fallback failed via document.execCommand('copy')", debugInfo); - } - return fallbackOk; -} - -function fallbackCopy(text: string, container?: HTMLElement | null): boolean { - if (typeof document === "undefined" || !document.body) return false; - - const mountTarget = - container && document.body.contains(container) ? container : document.body; - const textArea = document.createElement("textarea"); - const activeElement = - document.activeElement instanceof HTMLElement ? document.activeElement : null; - const selection = document.getSelection(); - const selectedRanges = selection - ? Array.from({ length: selection.rangeCount }, (_, index) => - selection.getRangeAt(index).cloneRange(), - ) - : []; - - textArea.value = text; - textArea.readOnly = true; - Object.assign(textArea.style, { - position: "fixed", - left: "-9999px", - top: "0", - opacity: "0", - pointerEvents: "none", - }); - - mountTarget.appendChild(textArea); - textArea.focus(); - textArea.select(); - textArea.setSelectionRange(0, text.length); - - try { - return document.execCommand("copy"); - } catch (err) { - console.error("Fallback copy failed:", err); - return false; - } finally { - if (textArea.parentNode) { - textArea.parentNode.removeChild(textArea); - } - if (selection) { - selection.removeAllRanges(); - selectedRanges.forEach((range) => selection.addRange(range)); - } - activeElement?.focus?.(); - } -} diff --git a/legacy-dashboard/src/utils/confirmDialog.ts b/legacy-dashboard/src/utils/confirmDialog.ts deleted file mode 100644 index 494f81e0..00000000 --- a/legacy-dashboard/src/utils/confirmDialog.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { inject } from 'vue' - -export type ConfirmDialogOptions = { - title?: string - message?: string -} - -export type ConfirmDialogHandler = (options: ConfirmDialogOptions) => Promise - -export type ConfirmDialogCandidate = ConfirmDialogHandler | null | undefined - -export function useConfirmDialog(): ConfirmDialogHandler | undefined { - return inject('$confirm', undefined) -} - -export async function askForConfirmation( - message: string, - candidate?: ConfirmDialogCandidate -): Promise { - const confirmDialog = candidate ?? undefined - - if (confirmDialog) { - try { - return await confirmDialog({ message }) - } catch { - return false - } - } - - return window.confirm(message) -} diff --git a/legacy-dashboard/src/utils/desktopBackendReadiness.mjs b/legacy-dashboard/src/utils/desktopBackendReadiness.mjs deleted file mode 100644 index c21c754d..00000000 --- a/legacy-dashboard/src/utils/desktopBackendReadiness.mjs +++ /dev/null @@ -1,30 +0,0 @@ -const defaultSleep = (milliseconds) => - new Promise((resolve) => setTimeout(resolve, milliseconds)); - -export async function waitForDesktopBackendReady({ - bridge = globalThis.window?.astrbotDesktop, - timeoutMs = 120000, - pollIntervalMs = 250, - now = Date.now, - sleep = defaultSleep, -} = {}) { - if (!bridge?.isDesktop || typeof bridge.getBackendState !== 'function') { - return false; - } - - const startedAt = now(); - while (now() - startedAt < timeoutMs) { - try { - const state = await bridge.getBackendState(); - if (state?.running) { - return true; - } - } catch { - // The bridge can be briefly unavailable while the Tauri page is loading. - } - await sleep(pollIntervalMs); - } - - console.warn('[desktop-runtime] Timed out waiting for the backend; mounting Dashboard anyway.'); - return false; -} diff --git a/legacy-dashboard/src/utils/desktopRuntime.ts b/legacy-dashboard/src/utils/desktopRuntime.ts deleted file mode 100644 index 66c2811a..00000000 --- a/legacy-dashboard/src/utils/desktopRuntime.ts +++ /dev/null @@ -1,32 +0,0 @@ -export type DesktopRuntimeInfo = { - bridge: Window['astrbotDesktop'] | undefined - hasDesktopRuntimeProbe: boolean - hasDesktopRestartCapability: boolean - isDesktopRuntime: boolean -} - -export async function getDesktopRuntimeInfo(): Promise { - const bridge = window.astrbotDesktop - const hasDesktopRuntimeProbe = - !!bridge && typeof bridge.isDesktopRuntime === 'function' - const hasDesktopRestartCapability = - !!bridge && - typeof bridge.restartBackend === 'function' && - hasDesktopRuntimeProbe - - let isDesktopRuntime = !!bridge?.isDesktop - if (hasDesktopRuntimeProbe) { - try { - isDesktopRuntime = isDesktopRuntime || !!(await bridge.isDesktopRuntime()) - } catch (error) { - console.warn('[desktop-runtime] Failed to detect desktop runtime.', error) - } - } - - return { - bridge, - hasDesktopRuntimeProbe, - hasDesktopRestartCapability, - isDesktopRuntime, - } -} diff --git a/legacy-dashboard/src/utils/errorUtils.js b/legacy-dashboard/src/utils/errorUtils.js deleted file mode 100644 index f8135487..00000000 --- a/legacy-dashboard/src/utils/errorUtils.js +++ /dev/null @@ -1,44 +0,0 @@ -const INVALID_ERROR_STRINGS = new Set(["[object Object]", "undefined", "null", ""]); - -const pickResponseMessage = (responseData) => { - if (typeof responseData === "string") { - return responseData.trim(); - } - if (!responseData || typeof responseData !== "object") { - return ""; - } - - const keys = ["message", "error", "detail", "details", "msg"]; - for (const key of keys) { - const value = responseData[key]; - if (typeof value === "string" && value.trim()) { - return value.trim(); - } - } - return ""; -}; - -export const resolveErrorMessage = (err, fallbackMessage = "") => { - if (typeof err === "string") { - return err.trim() || fallbackMessage; - } - if (typeof err === "number" || typeof err === "boolean") { - return String(err); - } - - const fromResponse = - pickResponseMessage(err?.response?.data) || - (typeof err?.response?.statusText === "string" - ? err.response.statusText.trim() - : ""); - const fromError = - typeof err?.message === "string" ? err.message.trim() : ""; - - let fromString = ""; - if (typeof err?.toString === "function") { - const value = err.toString().trim(); - fromString = INVALID_ERROR_STRINGS.has(value) ? "" : value; - } - - return fromResponse || fromError || fromString || fallbackMessage; -}; diff --git a/legacy-dashboard/src/utils/hashRouteTabs.mjs b/legacy-dashboard/src/utils/hashRouteTabs.mjs deleted file mode 100644 index c1d7c453..00000000 --- a/legacy-dashboard/src/utils/hashRouteTabs.mjs +++ /dev/null @@ -1,46 +0,0 @@ -import { EXTENSION_ROUTE_NAME } from '../router/routeConstants.mjs'; - -export function getValidHashTab(routeHash, validTabs) { - const hash = String(routeHash || ''); - const tab = hash.includes('#') ? hash.slice(hash.lastIndexOf('#') + 1) : hash; - return validTabs.includes(tab) ? tab : null; -} - -export function createTabRouteLocation(route, tab, fallbackRouteName = EXTENSION_ROUTE_NAME) { - const query = route?.query ? { ...route.query } : {}; - const params = route?.params ? { ...route.params } : undefined; - - if (route?.name) { - return { - name: route.name, - ...(params ? { params } : {}), - query, - hash: `#${tab}`, - }; - } - - if (route?.path) { - return { - path: route.path, - query, - hash: `#${tab}`, - }; - } - - return { - name: fallbackRouteName, - ...(params ? { params } : {}), - query, - hash: `#${tab}`, - }; -} - -export async function replaceTabRoute(router, route, tab, logger = console) { - try { - await router.replace(createTabRouteLocation(route, tab)); - return true; - } catch (error) { - logger.warn?.('Failed to update extension tab route:', error); - return false; - } -} diff --git a/legacy-dashboard/src/utils/imeInput.mjs b/legacy-dashboard/src/utils/imeInput.mjs deleted file mode 100644 index 9e8b4d67..00000000 --- a/legacy-dashboard/src/utils/imeInput.mjs +++ /dev/null @@ -1,31 +0,0 @@ -// Some IMEs emit Enter right after compositionend; treat that same-keystroke -// window as composition so selecting a candidate does not send the message. -const RECENT_COMPOSITION_END_THRESHOLD_MS = 100; - -/** - * @param {KeyboardEvent} event - * @param {boolean} compositionActive - * @param {number | null} lastCompositionEndAt - */ -export function isComposingEnter( - event, - compositionActive, - lastCompositionEndAt = null, -) { - const hasCompositionKeyCodeFallback = - typeof event.keyCode === "number" && event.keyCode === 229; - const isAfterRecentCompositionEnd = - typeof event.timeStamp === "number" && - typeof lastCompositionEndAt === "number" && - event.timeStamp >= lastCompositionEndAt && - event.timeStamp - lastCompositionEndAt < - RECENT_COMPOSITION_END_THRESHOLD_MS; - - return ( - event.key === "Enter" && - (compositionActive || - event.isComposing || - hasCompositionKeyCodeFallback || - isAfterRecentCompositionEnd) - ); -} diff --git a/legacy-dashboard/src/utils/inputValue.ts b/legacy-dashboard/src/utils/inputValue.ts deleted file mode 100644 index 332df189..00000000 --- a/legacy-dashboard/src/utils/inputValue.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const normalizeTextInput = (value: unknown): string => - typeof value === 'string' ? value : ''; diff --git a/legacy-dashboard/src/utils/platformUtils.js b/legacy-dashboard/src/utils/platformUtils.js deleted file mode 100644 index e95f69a1..00000000 --- a/legacy-dashboard/src/utils/platformUtils.js +++ /dev/null @@ -1,117 +0,0 @@ -/** - * 平台相关工具函数 - */ - -/** - * 获取平台图标 - * @param {string} name - 平台名称或类型 - * @returns {string|undefined} 图标URL - */ -export function getPlatformIcon(name) { - if (name === 'aiocqhttp') { - return new URL('@/assets/images/platform_logos/onebot.png', import.meta.url).href - } else if (name === 'qq_official' || name === 'qq_official_webhook') { - return new URL('@/assets/images/platform_logos/qq.png', import.meta.url).href - } else if (name === 'weixin_oc' || name === 'weixin_oc') { - return new URL('@/assets/images/platform_logos/wechat.png', import.meta.url).href - } else if (name === 'wecom' || name === 'wecom_ai_bot') { - return new URL('@/assets/images/platform_logos/wecom.png', import.meta.url).href - } else if (name === 'weixin_official_account') { - return new URL('@/assets/images/platform_logos/wechat.png', import.meta.url).href - } else if (name === 'lark') { - return new URL('@/assets/images/platform_logos/lark.png', import.meta.url).href - } else if (name === 'dingtalk') { - return new URL('@/assets/images/platform_logos/dingtalk.svg', import.meta.url).href - } else if (name === 'telegram') { - return new URL('@/assets/images/platform_logos/telegram.svg', import.meta.url).href - } else if (name === 'discord') { - return new URL('@/assets/images/platform_logos/discord.svg', import.meta.url).href - } else if (name === 'slack') { - return new URL('@/assets/images/platform_logos/slack.svg', import.meta.url).href - } else if (name === 'kook') { - return new URL('@/assets/images/platform_logos/kook.png', import.meta.url).href - } else if (name === 'satori' || name === 'Satori') { - return new URL('@/assets/images/platform_logos/satori.png', import.meta.url).href - } else if (name === 'misskey') { - return new URL('@/assets/images/platform_logos/misskey.png', import.meta.url).href - } else if (name === 'line') { - return new URL('@/assets/images/platform_logos/line.png', import.meta.url).href - } else if (name === 'matrix') { - return new URL('@/assets/images/platform_logos/matrix.svg', import.meta.url).href - } else if (name === 'mattermost') { - return new URL('@/assets/images/platform_logos/mattermost.svg', import.meta.url).href - } -} - -/** - * 获取平台教程链接 - * @param {string} platformType - 平台类型 - * @returns {string} 教程链接 - */ -export function getTutorialLink(platformType) { - const tutorialMap = { - "qq_official_webhook": "https://docs.astrbot.app/platform/qqofficial/webhook.html", - "qq_official": "https://docs.astrbot.app/platform/qqofficial/websockets.html", - "aiocqhttp": "https://docs.astrbot.app/platform/aiocqhttp.html", - "wecom": "https://docs.astrbot.app/platform/wecom.html", - "weixin_oc": "https://docs.astrbot.app/platform/weixin_oc.html", - "wecom_ai_bot": "https://docs.astrbot.app/platform/wecom_ai_bot.html", - "lark": "https://docs.astrbot.app/platform/lark.html", - "telegram": "https://docs.astrbot.app/platform/telegram.html", - "dingtalk": "https://docs.astrbot.app/platform/dingtalk.html", - "weixin_official_account": "https://docs.astrbot.app/platform/weixin-official-account.html", - "discord": "https://docs.astrbot.app/platform/discord.html", - "slack": "https://docs.astrbot.app/platform/slack.html", - "kook": "https://docs.astrbot.app/platform/kook.html", - "vocechat": "https://docs.astrbot.app/platform/vocechat.html", - "satori": "https://docs.astrbot.app/platform/satori/guide.html", - "misskey": "https://docs.astrbot.app/platform/misskey.html", - "line": "https://docs.astrbot.app/platform/line.html", - "matrix": "https://docs.astrbot.app/platform/matrix.html", - "mattermost": "https://docs.astrbot.app/platform/mattermost.html", - } - return tutorialMap[platformType] || "https://docs.astrbot.app"; -} - -/** - * 获取平台描述 - * @param {Object} template - 平台模板 - * @param {string} name - 平台名称 - * @returns {string} 平台描述 - */ -export function getPlatformDescription(template, name) { - // special judge for community platforms - if (name.includes('vocechat')) { - return "由 @HikariFroya 提供。"; - } else if (name.includes('kook')) { - return "由 @wuyan1003 提供。" - } - return ''; -} - -/** - * 获取平台展示名(用于插件支持平台显示) - * @param {string} platformId - 平台适配器 ID - * @returns {string} - */ -export function getPlatformDisplayName(platformId) { - const displayNameMap = { - aiocqhttp: 'aiocqhttp (OneBot v11)', - qq_official: 'qq_official (QQ 官方机器人平台)', - weixin_official_account: 'weixin_official_account (微信公众号)', - wecom: 'wecom (企业微信应用)', - wecom_ai_bot: 'wecom_ai_bot (企业微信智能机器人)', - lark: 'lark (飞书)', - dingtalk: 'dingtalk (钉钉)', - telegram: 'telegram (Telegram)', - discord: 'discord (Discord)', - misskey: 'misskey (Misskey)', - slack: 'slack (Slack)', - kook: 'kook (KOOK)', - vocechat: 'vocechat (VoceChat)', - satori: 'satori (Satori)', - line: 'line (LINE)', - matrix: 'matrix (Matrix)', - }; - return displayNameMap[platformId] || platformId; -} diff --git a/legacy-dashboard/src/utils/pluginI18n.js b/legacy-dashboard/src/utils/pluginI18n.js deleted file mode 100644 index 3a8b5033..00000000 --- a/legacy-dashboard/src/utils/pluginI18n.js +++ /dev/null @@ -1,116 +0,0 @@ -import { useI18n } from "@/i18n/composables"; - -function getLocaleData(i18n, locale) { - if (!i18n || typeof i18n !== "object" || !locale) return null; - return i18n[locale] || null; -} - -function getByPath(source, key) { - if (!source || typeof source !== "object" || !key) return undefined; - - const parts = key.split("."); - let current = source; - for (const part of parts) { - if (!current || typeof current !== "object" || !(part in current)) { - return undefined; - } - current = current[part]; - } - return current; -} - -export function resolvePluginI18n(i18n, locale, key, fallback = "") { - const localeData = getLocaleData(i18n, locale); - const value = getByPath(localeData, key); - return value === undefined || value === null ? fallback : value; -} - -function getPluginPageI18nBase(page) { - if (page && typeof page === "object") { - if (typeof page.i18n_key === "string" && page.i18n_key.trim()) { - return page.i18n_key.trim(); - } - const pageName = page.page_name || page.name; - return pageName ? `pages.${pageName}` : ""; - } - - return page ? `pages.${page}` : ""; -} - -export function usePluginI18n() { - const { locale } = useI18n(); - - const resolve = (i18n, key, fallback = "") => { - return resolvePluginI18n(i18n, locale.value, key, fallback); - }; - - const pluginName = (plugin) => { - const fallback = plugin?.display_name?.length - ? plugin.display_name - : plugin?.name; - return resolve(plugin?.i18n, "metadata.display_name", fallback || ""); - }; - - const pluginDesc = (plugin, fallback = "") => { - return resolve( - plugin?.i18n, - "metadata.desc", - fallback || plugin?.desc || plugin?.description || "", - ); - }; - - const pluginShortDesc = (plugin, fallback = "") => { - return resolve( - plugin?.i18n, - "metadata.short_desc", - fallback || - plugin?.short_desc || - plugin?.desc || - plugin?.description || - "", - ); - }; - - const pluginPageText = (plugin, page, attr, fallback = "") => { - const base = getPluginPageI18nBase(page); - if (!base || !attr) { - return fallback; - } - return resolve(plugin?.i18n, `${base}.${attr}`, fallback); - }; - - const pluginPageTitle = (plugin, page, fallback = "") => { - const pageFallback = - fallback || - (page && typeof page === "object" - ? page.title || page.name || page.page_name - : page) || - ""; - return pluginPageText(plugin, page, "title", pageFallback); - }; - - const pluginPageDescription = (plugin, page, fallback = "") => { - const pageFallback = - fallback || - (page && typeof page === "object" ? page.description || page.desc : "") || - ""; - return pluginPageText(plugin, page, "description", pageFallback); - }; - - const configText = (i18n, path, attr, fallback = "") => { - const key = path ? `config.${path}.${attr}` : `config.${attr}`; - return resolve(i18n, key, fallback); - }; - - return { - locale, - resolve, - pluginName, - pluginDesc, - pluginShortDesc, - pluginPageText, - pluginPageTitle, - pluginPageDescription, - configText, - }; -} diff --git a/legacy-dashboard/src/utils/pluginSearch.js b/legacy-dashboard/src/utils/pluginSearch.js deleted file mode 100644 index 830e8d38..00000000 --- a/legacy-dashboard/src/utils/pluginSearch.js +++ /dev/null @@ -1,103 +0,0 @@ -import { pinyin } from "pinyin-pro"; - -const HAN_IDEOGRAPH_RE = /\p{Unified_Ideograph}/u; - -export const normalizeStr = (s) => (s ?? "").toString().toLowerCase().trim(); - -const normalizeLooseFromNormalized = (normalized) => - normalized.replace(/[\s_-]+/g, "").replace(/[()()【】\[\]{}·•]+/g, ""); - -export const normalizeLoose = (s) => - normalizeLooseFromNormalized(normalizeStr(s)); - -const memoizeStringFn = (fn) => { - const cache = new Map(); - - return (raw) => { - const key = (raw ?? "").toString(); - if (cache.has(key)) { - return cache.get(key); - } - - const value = fn(key); - cache.set(key, value); - return value; - }; -}; - -const getNormalizedText = memoizeStringFn(normalizeStr); - -const getLooseText = memoizeStringFn((text) => - normalizeLooseFromNormalized(getNormalizedText(text)), -); - -export const toPinyinText = memoizeStringFn((text) => - pinyin(text, { toneType: "none" }) - .toLowerCase() - .replace(/\s+/g, ""), -); - -export const toInitials = memoizeStringFn((text) => - pinyin(text, { pattern: "first", toneType: "none" }) - .toLowerCase() - .replace(/\s+/g, ""), -); - -export const buildSearchQuery = (raw) => { - const norm = getNormalizedText(raw); - if (!norm) return null; - return { - norm, - loose: getLooseText(raw), - }; -}; - -export const matchesText = (value, query) => { - if (value == null || !query?.norm) return false; - const text = String(value); - - const normalizedValue = getNormalizedText(text); - const looseValue = query.loose ? getLooseText(text) : null; - - if (normalizedValue.includes(query.norm)) return true; - if (query.loose && looseValue?.includes(query.loose)) return true; - - if (!HAN_IDEOGRAPH_RE.test(text)) return false; - - const pinyinValue = toPinyinText(text); - if (pinyinValue.includes(query.norm)) return true; - - const initialsValue = toInitials(text); - if (initialsValue.includes(query.norm)) return true; - - return false; -}; - -export const getPluginSearchFields = (plugin) => { - const supportPlatforms = Array.isArray(plugin?.support_platforms) - ? plugin.support_platforms.join(" ") - : ""; - const tags = Array.isArray(plugin?.tags) ? plugin.tags.join(" ") : ""; - - return [ - plugin?.name, - plugin?.trimmedName, - plugin?.display_name, - plugin?.short_desc, - plugin?.desc, - plugin?.author, - plugin?.repo, - plugin?.version, - plugin?.astrbot_version, - supportPlatforms, - tags, - ]; -}; - -export const matchesPluginSearch = (plugin, query) => { - if (!query) return true; - - return getPluginSearchFields(plugin).some((candidate) => - matchesText(candidate, query), - ); -}; diff --git a/legacy-dashboard/src/utils/providerMetadata.ts b/legacy-dashboard/src/utils/providerMetadata.ts deleted file mode 100644 index 3d8faff4..00000000 --- a/legacy-dashboard/src/utils/providerMetadata.ts +++ /dev/null @@ -1,102 +0,0 @@ -export interface ProviderModelMetadata { - modalities?: { input?: string[] } - tool_call?: boolean - reasoning?: boolean - limit?: { context?: number } -} - -export interface ProviderMetadataSource { - model?: string - modalities?: string[] - max_context_tokens?: number - reasoning?: boolean -} - -export interface ProviderCapabilityBadge { - key: string - icon: string - enabled: boolean - tooltip: string -} - -export function contextLimit( - provider: ProviderMetadataSource | null | undefined, - metadata?: ProviderModelMetadata | null -): number { - const context = Number(metadata?.limit?.context || provider?.max_context_tokens || 0) - return Number.isFinite(context) && context > 0 ? context : 0 -} - -export function formatTokenCount(value: number): string { - if (!Number.isFinite(value)) return '' - const absValue = Math.abs(value) - if (absValue >= 1_000_000) return `${formatCompactNumber(value / 1_000_000)}M` - if (absValue >= 1_000) return `${formatCompactNumber(value / 1_000)}K` - return `${Math.round(value)}` -} - -export function formatContextLimit( - provider: ProviderMetadataSource | null | undefined, - metadata?: ProviderModelMetadata | null -): string { - const context = contextLimit(provider, metadata) - return context ? formatTokenCount(context) : '' -} - -export function providerCapabilityBadges( - provider: ProviderMetadataSource | null | undefined, - metadata: ProviderModelMetadata | null | undefined, - tm: (key: string, params?: Record) => string -): ProviderCapabilityBadge[] { - const inputs = metadata?.modalities?.input || [] - const providerModalities = provider?.modalities - const modalities = Array.isArray(providerModalities) ? providerModalities : [] - const definitions = [ - { - key: 'image', - icon: 'mdi-image-outline', - supported: inputs.includes('image'), - enabled: modalities.includes('image'), - label: tm('models.metadata.image') - }, - { - key: 'audio', - icon: 'mdi-music-note-outline', - supported: inputs.includes('audio'), - enabled: modalities.includes('audio'), - label: tm('models.metadata.audio') - }, - { - key: 'tool_use', - icon: 'mdi-wrench-outline', - supported: Boolean(metadata?.tool_call), - enabled: modalities.includes('tool_use'), - label: tm('models.metadata.toolUse') - }, - { - key: 'reasoning', - icon: 'mdi-brain', - supported: Boolean(metadata?.reasoning), - enabled: Boolean(provider?.reasoning), - label: tm('models.metadata.reasoning') - } - ] - - return definitions - .filter((item) => item.supported || item.enabled) - .map((item) => ({ - key: item.key, - icon: item.icon, - enabled: !metadata || item.enabled, - tooltip: - metadata && !item.enabled - ? tm('models.metadata.supportedDisabled', { capability: item.label }) - : tm('models.metadata.enabled', { capability: item.label }) - })) -} - -function formatCompactNumber(value: number): string { - const absValue = Math.abs(value) - const rounded = absValue >= 10 ? Math.round(value) : Math.round(value * 10) / 10 - return String(rounded).replace(/\.0$/, '') -} diff --git a/legacy-dashboard/src/utils/providerUtils.js b/legacy-dashboard/src/utils/providerUtils.js deleted file mode 100644 index ee76fafa..00000000 --- a/legacy-dashboard/src/utils/providerUtils.js +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 提供商相关的工具函数 - */ - -/** - * 获取提供商类型对应的图标 - * @param {string} type - 提供商类型 - * @returns {string} 图标 URL - */ -export function getProviderIcon(type) { - const icons = { - 'openai': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/openai.svg', - 'azure': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/azure.svg', - 'xai': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/xai.svg', - 'anthropic': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/anthropic.svg', - 'ollama': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/ollama.svg', - 'google': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/gemini-color.svg', - 'deepseek': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/deepseek.svg', - 'modelscope': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/modelscope.svg', - 'zhipu': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/zhipu.svg', - 'nvidia': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/nvidia-color.svg', - 'siliconflow': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/siliconcloud.svg', - 'moonshot': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/kimi.svg', - 'kimi': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/kimi.svg', - 'kimi-code': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/kimi.svg', - 'longcat': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/longcat-color.svg', - 'ppio': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/ppio.svg', - 'dify': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/dify-color.svg', - "coze": "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.66.0/icons/coze.svg", - 'dashscope': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/alibabacloud-color.svg', - 'deerflow': 'https://cdn.jsdelivr.net/gh/bytedance/deer-flow@main/frontend/public/images/deer.svg', - 'fastgpt': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/fastgpt-color.svg', - 'lm_studio': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/lmstudio.svg', - 'fishaudio': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/fishaudio.svg', - 'minimax': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/minimax.svg', - 'minimax-token-plan': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/minimax.svg', - 'mimo': 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/xiaomi.svg', - 'xiaomi': 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/xiaomi.svg', - 'xiaomi-token-plan': 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/xiaomi.svg', - '302ai': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@1.53.0/icons/ai302-color.svg', - 'microsoft': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/microsoft.svg', - 'vllm': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/vllm.svg', - 'groq': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/groq.svg', - 'aihubmix': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/aihubmix-color.svg', - 'openrouter': 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/openrouter.svg', - "tokenpony": "https://tokenpony.cn/tokenpony-web/logo.png", - "compshare": "https://compshare.cn/favicon.ico", - "xinference": "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/xinference-color.svg", - "bailian": "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/bailian-color.svg", - "volcengine": 'https://cdn.jsdelivr.net/npm/@lobehub/icons-static-svg@latest/icons/volcengine-color.svg', - }; - return icons[type] || ''; -} - -/** - * 获取提供商简介 - * @param {Object} template - 模板对象 - * @param {string} name - 提供商名称 - * @param {Function} tm - 翻译函数 - * @returns {string} 提供商描述 - */ -export function getProviderDescription(template, name, tm) { - if (name === 'OpenAI') { - return tm('providers.description.openai', { type: template.type }); - } else if (template.provider === 'kimi-code') { - return tm('providers.description.kimi_code'); - } else if (name === 'vLLM Rerank') { - return tm('providers.description.vllm_rerank', { type: template.type }); - } - return tm('providers.description.default', { type: template.type }); -} diff --git a/legacy-dashboard/src/utils/restartAstrBot.ts b/legacy-dashboard/src/utils/restartAstrBot.ts deleted file mode 100644 index 598d2583..00000000 --- a/legacy-dashboard/src/utils/restartAstrBot.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { statsApi } from '@/api/v1' -import { getDesktopRuntimeInfo } from '@/utils/desktopRuntime' - -type WaitingForRestartRef = { - check: (initialStartTime?: number | null) => void | Promise - stop?: () => void -} - -async function triggerWaiting( - waitingRef?: WaitingForRestartRef | null, - initialStartTime?: number | null -) { - if (!waitingRef) return - await waitingRef.check(initialStartTime) -} - -async function fetchCurrentStartTime(): Promise { - try { - const response = await statsApi.startTime() - const rawStartTime = response?.data?.data?.start_time - const numericStartTime = Number(rawStartTime) - return Number.isFinite(numericStartTime) ? numericStartTime : null - } catch (_error) { - return null - } -} - -export async function restartAstrBot( - waitingRef?: WaitingForRestartRef | null -): Promise { - const { bridge: desktopBridge, hasDesktopRestartCapability, isDesktopRuntime } = - await getDesktopRuntimeInfo() - - if (desktopBridge && hasDesktopRestartCapability && isDesktopRuntime) { - const authToken = localStorage.getItem('token') - const initialStartTime = await fetchCurrentStartTime() - try { - const restartPromise = desktopBridge.restartBackend(authToken) - await triggerWaiting(waitingRef, initialStartTime) - const result = await restartPromise - if (!result.ok) { - waitingRef?.stop?.() - throw new Error(result.reason || 'Failed to restart backend.') - } - } catch (error) { - waitingRef?.stop?.() - throw error - } - return - } - - await statsApi.restart() - await triggerWaiting(waitingRef) -} diff --git a/legacy-dashboard/src/utils/routerReadiness.mjs b/legacy-dashboard/src/utils/routerReadiness.mjs deleted file mode 100644 index d989212c..00000000 --- a/legacy-dashboard/src/utils/routerReadiness.mjs +++ /dev/null @@ -1,5 +0,0 @@ -export function waitForRouterReadyInBackground(router, logger = console) { - router.isReady().catch((error) => { - logger.warn?.('Router did not become ready after fallback mount:', error); - }); -} diff --git a/legacy-dashboard/src/utils/shiki.js b/legacy-dashboard/src/utils/shiki.js deleted file mode 100644 index c1551690..00000000 --- a/legacy-dashboard/src/utils/shiki.js +++ /dev/null @@ -1,81 +0,0 @@ -import { - createHighlighter, - normalizeLimitedShikiLanguage, -} from "./shikiLimitedBundle"; - -export const SHIKI_THEMES = { - light: "github-light", - dark: "github-dark", -}; - -let highlighterPromise; - -function normalizeLanguage(language) { - return normalizeLimitedShikiLanguage(language); -} - -export function escapeHtml(value = "") { - return String(value) - .replaceAll("&", "&") - .replaceAll("<", "<") - .replaceAll(">", ">") - .replaceAll('"', """) - .replaceAll("'", "'"); -} - -export async function getShikiHighlighter() { - if (!highlighterPromise) { - highlighterPromise = createHighlighter({ - themes: Object.values(SHIKI_THEMES), - }); - } - - return highlighterPromise; -} - -export async function ensureShikiLanguages() { - const highlighter = await getShikiHighlighter(); - - return highlighter; -} - -export function renderShikiCode(highlighter, code, language, colorMode = "auto") { - const normalizedLanguage = normalizeLanguage(language); - const options = - colorMode === "dark" - ? { lang: normalizedLanguage, theme: SHIKI_THEMES.dark } - : colorMode === "light" - ? { lang: normalizedLanguage, theme: SHIKI_THEMES.light } - : { lang: normalizedLanguage, themes: SHIKI_THEMES }; - - try { - return highlighter.codeToHtml(code, options); - } catch (err) { - console.warn( - `Failed to render code with Shiki language "${normalizedLanguage}". Falling back to plain text.`, - err, - ); - - const fallbackOptions = - colorMode === "dark" - ? { lang: "text", theme: SHIKI_THEMES.dark } - : colorMode === "light" - ? { lang: "text", theme: SHIKI_THEMES.light } - : { lang: "text", themes: SHIKI_THEMES }; - - return highlighter.codeToHtml(code, fallbackOptions); - } -} - -export function collectMarkdownFenceLanguages(markdownIt, markdown) { - if (!markdown) return []; - - return markdownIt - .parse(markdown, {}) - .filter((token) => token.type === "fence") - .map((token) => normalizeLanguage(token.info)); -} - -export function normalizeShikiLanguage(language) { - return normalizeLanguage(language); -} diff --git a/legacy-dashboard/src/utils/shikiLimitedBundle.js b/legacy-dashboard/src/utils/shikiLimitedBundle.js deleted file mode 100644 index b0ece538..00000000 --- a/legacy-dashboard/src/utils/shikiLimitedBundle.js +++ /dev/null @@ -1,196 +0,0 @@ -import { createHighlighterCore } from "shiki/core"; -import { createJavaScriptRegexEngine } from "shiki/engine/javascript"; -import bash from "shiki/langs/bash.mjs"; -import css from "shiki/langs/css.mjs"; -import diff from "shiki/langs/diff.mjs"; -import dockerfile from "shiki/langs/dockerfile.mjs"; -import html from "shiki/langs/html.mjs"; -import ini from "shiki/langs/ini.mjs"; -import java from "shiki/langs/java.mjs"; -import javascript from "shiki/langs/javascript.mjs"; -import json from "shiki/langs/json.mjs"; -import jsx from "shiki/langs/jsx.mjs"; -import markdown from "shiki/langs/markdown.mjs"; -import powershell from "shiki/langs/powershell.mjs"; -import python from "shiki/langs/python.mjs"; -import sql from "shiki/langs/sql.mjs"; -import tsx from "shiki/langs/tsx.mjs"; -import typescript from "shiki/langs/typescript.mjs"; -import vue from "shiki/langs/vue.mjs"; -import xml from "shiki/langs/xml.mjs"; -import yaml from "shiki/langs/yaml.mjs"; -import githubDark from "shiki/themes/github-dark.mjs"; -import githubLight from "shiki/themes/github-light.mjs"; -import vitesseDark from "shiki/themes/vitesse-dark.mjs"; -import vitesseLight from "shiki/themes/vitesse-light.mjs"; - -export const LIMITED_SHIKI_LANGUAGES = [ - ...bash, - ...css, - ...diff, - ...dockerfile, - ...html, - ...ini, - ...java, - ...javascript, - ...json, - ...jsx, - ...markdown, - ...powershell, - ...python, - ...sql, - ...tsx, - ...typescript, - ...vue, - ...xml, - ...yaml, -]; - -const THEME_BY_NAME = { - "github-dark": githubDark, - "github-light": githubLight, - "vitesse-dark": vitesseDark, - "vitesse-light": vitesseLight, -}; - -const BUILT_IN_LANGUAGES = ["text", "plaintext", "plain"]; - -export const LIMITED_SHIKI_LANGUAGE_ALIASES = { - bat: "powershell", - cjs: "javascript", - console: "bash", - cts: "typescript", - docker: "dockerfile", - htm: "html", - js: "javascript", - md: "markdown", - mjs: "javascript", - mts: "typescript", - plain: "text", - plaintext: "text", - ps1: "powershell", - pwsh: "powershell", - py: "python", - shell: "bash", - shellscript: "bash", - sh: "bash", - svg: "xml", - text: "text", - ts: "typescript", - txt: "text", - xhtml: "html", - yml: "yaml", - zsh: "bash", -}; - -export const LIMITED_SHIKI_SUPPORTED_LANGUAGES = new Set([ - ...BUILT_IN_LANGUAGES, - ...LIMITED_SHIKI_LANGUAGES.flatMap((language) => [ - language.name, - ...(language.aliases || []), - ]), -]); - -function getThemeName(theme) { - return typeof theme === "string" ? theme : theme?.name; -} - -function resolveTheme(theme) { - if (!theme) return null; - if (typeof theme !== "string") return theme; - return THEME_BY_NAME[theme] || null; -} - -function uniqueThemes(themes) { - const seen = new Set(); - const result = []; - - for (const theme of themes) { - const resolved = resolveTheme(theme); - const name = getThemeName(resolved); - if (!resolved || !name || seen.has(name)) continue; - seen.add(name); - result.push(resolved); - } - - return result; -} - -export function normalizeLimitedShikiLanguage(language) { - const normalized = String(language || "text") - .trim() - .split(/\s+/, 1)[0] - .toLowerCase(); - - if (!normalized) return "text"; - if (normalized in LIMITED_SHIKI_LANGUAGE_ALIASES) { - return LIMITED_SHIKI_LANGUAGE_ALIASES[normalized]; - } - - return LIMITED_SHIKI_SUPPORTED_LANGUAGES.has(normalized) ? normalized : "text"; -} - -function normalizeCodeOptions(options) { - if (!options || typeof options !== "object") return options; - return { - ...options, - lang: normalizeLimitedShikiLanguage(options.lang), - }; -} - -function wrapLimitedHighlighter(highlighter) { - const codeToHtml = highlighter.codeToHtml.bind(highlighter); - const codeToTokens = highlighter.codeToTokens.bind(highlighter); - const codeToHast = highlighter.codeToHast.bind(highlighter); - const getLanguage = highlighter.getLanguage.bind(highlighter); - const getLoadedLanguages = highlighter.getLoadedLanguages.bind(highlighter); - const loadThemeSync = highlighter.loadThemeSync?.bind(highlighter); - const loadTheme = highlighter.loadTheme?.bind(highlighter); - - return { - ...highlighter, - codeToHast(code, options) { - return codeToHast(code, normalizeCodeOptions(options)); - }, - codeToHtml(code, options) { - return codeToHtml(code, normalizeCodeOptions(options)); - }, - codeToTokens(code, options) { - return codeToTokens(code, normalizeCodeOptions(options)); - }, - getLanguage(language) { - return getLanguage(normalizeLimitedShikiLanguage(language)); - }, - getLoadedLanguages() { - return [...new Set([...getLoadedLanguages(), ...BUILT_IN_LANGUAGES])]; - }, - loadLanguage() { - return Promise.resolve(); - }, - loadLanguageSync() {}, - async loadTheme(...themes) { - const resolved = uniqueThemes(themes.flat()); - if (resolved.length && loadTheme) await loadTheme(...resolved); - }, - loadThemeSync(...themes) { - const resolved = uniqueThemes(themes.flat()); - if (resolved.length && loadThemeSync) loadThemeSync(...resolved); - }, - }; -} - -export async function createHighlighter(options = {}) { - const themes = uniqueThemes([ - ...(Array.isArray(options.themes) ? options.themes : []), - ...Object.values(THEME_BY_NAME), - ]); - - const highlighter = await createHighlighterCore({ - ...options, - engine: options.engine || createJavaScriptRegexEngine(), - langs: LIMITED_SHIKI_LANGUAGES, - themes, - }); - - return wrapLimitedHighlighter(highlighter); -} diff --git a/legacy-dashboard/src/utils/sidebarCustomization.js b/legacy-dashboard/src/utils/sidebarCustomization.js deleted file mode 100644 index 9e75df88..00000000 --- a/legacy-dashboard/src/utils/sidebarCustomization.js +++ /dev/null @@ -1,194 +0,0 @@ -// Utility for managing sidebar customization in localStorage -const STORAGE_KEY = 'astrbot_sidebar_customization'; - -/** - * Get the customized sidebar configuration from localStorage - * @returns {Object|null} The customization config or null if not set - */ -export function getSidebarCustomization() { - try { - const stored = localStorage.getItem(STORAGE_KEY); - return stored ? JSON.parse(stored) : null; - } catch (error) { - console.error('Error reading sidebar customization:', error); - return null; - } -} - -/** - * Save the sidebar customization to localStorage - * @param {Object} config - The customization configuration - * @param {Array} config.mainItems - Array of item titles for main sidebar - * @param {Array} config.moreItems - Array of item titles for "More Features" group - */ -export function setSidebarCustomization(config) { - try { - localStorage.setItem(STORAGE_KEY, JSON.stringify(config)); - } catch (error) { - console.error('Error saving sidebar customization:', error); - } -} - -/** - * Clear the sidebar customization (reset to default) - */ -export function clearSidebarCustomization() { - try { - localStorage.removeItem(STORAGE_KEY); - } catch (error) { - console.error('Error clearing sidebar customization:', error); - } -} - -/** - * 解析侧边栏默认项与用户定制,返回主区/更多区及可选的合并结果 - * @param {Array} defaultItems - 默认侧边栏结构 - * @param {Object|null} customization - 用户定制(mainItems/moreItems) - * @param {Object} options - * @param {boolean} [options.cloneItems=false] - 是否克隆条目以避免外部引用被修改 - * @param {boolean} [options.assembleMoreGroup=false] - 是否组装带更多分组的整体数组 - * @returns {{ mainItems: Array, moreItems: Array, merged?: Array }} - */ -import { MORE_GROUP_KEY } from "@/layouts/full/vertical-sidebar/sidebarItem"; - -export function resolveSidebarItems(defaultItems, customization, options = {}) { - const { cloneItems = false, assembleMoreGroup = false } = options; - - const normalizeKeys = (keys = []) => { - const list = Array.isArray(keys) ? keys : []; - const deduped = []; - const seen = new Set(); - - list.forEach((key) => { - if (typeof key !== 'string') return; - if (seen.has(key)) return; - seen.add(key); - deduped.push(key); - }); - - return deduped; - }; - - const all = new Map(); - const defaultMain = []; - const defaultMore = []; - - // 收集所有条目,按 title 建索引 - defaultItems.forEach(item => { - if (item.children && item.title === MORE_GROUP_KEY) { - item.children.forEach(child => { - all.set(child.title, cloneItems ? { ...child } : child); - defaultMore.push(child.title); - }); - } else { - all.set(item.title, cloneItems ? { ...item } : item); - defaultMain.push(item.title); - } - }); - - const hasCustomization = Boolean(customization); - let mainKeys = hasCustomization ? normalizeKeys(customization.mainItems || []) : [...defaultMain]; - let moreKeys = hasCustomization ? normalizeKeys(customization.moreItems || []) : [...defaultMore]; - - if (hasCustomization) { - mainKeys = mainKeys.filter(title => all.has(title)); - moreKeys = moreKeys.filter(title => all.has(title)); - } - - if (hasCustomization) { - // 如果同一项同时出现在主区与更多区,主区优先。 - const mainSet = new Set(mainKeys); - moreKeys = moreKeys.filter(title => !mainSet.has(title)); - } - - const used = hasCustomization - ? new Set([...mainKeys, ...moreKeys]) - : new Set(defaultMain.concat(defaultMore)); - - const mainItems = mainKeys - .map(title => all.get(title)) - .filter(Boolean); - - if (hasCustomization) { - // 补充新增默认主区项 - defaultMain.forEach(title => { - if (!used.has(title)) { - const item = all.get(title); - if (item) mainItems.push(item); - } - }); - } - - const moreItems = moreKeys - .map(title => all.get(title)) - .filter(Boolean); - - if (hasCustomization) { - // 补充新增默认更多区项 - defaultMore.forEach(title => { - if (!used.has(title)) { - const item = all.get(title); - if (item) moreItems.push(item); - } - }); - } - - let merged; - if (assembleMoreGroup) { - const children = cloneItems ? moreItems.map(item => ({ ...item })) : [...moreItems]; - if (children.length > 0) { - merged = [ - ...mainItems, - { - title: MORE_GROUP_KEY, - icon: 'mdi-dots-horizontal', - children - } - ]; - } else { - merged = [...mainItems]; - } - } - - return { - mainItems, - moreItems, - merged, - normalizedMainKeys: [...mainKeys], - normalizedMoreKeys: [...moreKeys] - }; -} - -/** - * 应用侧边栏定制,返回包含更多分组的完整结构 - * @param {Array} defaultItems - 默认侧边栏结构 - * @returns {Array} 自定义后的结构(新数组,不修改入参) - */ -export function applySidebarCustomization(defaultItems) { - const customization = getSidebarCustomization(); - const { - merged, - normalizedMainKeys, - normalizedMoreKeys - } = resolveSidebarItems(defaultItems, customization, { - cloneItems: true, - assembleMoreGroup: true - }); - - if (customization) { - const rawMainKeys = Array.isArray(customization.mainItems) ? customization.mainItems : []; - const rawMoreKeys = Array.isArray(customization.moreItems) ? customization.moreItems : []; - const hasChanged = - JSON.stringify(rawMainKeys) !== JSON.stringify(normalizedMainKeys) || - JSON.stringify(rawMoreKeys) !== JSON.stringify(normalizedMoreKeys); - - if (hasChanged) { - setSidebarCustomization({ - mainItems: normalizedMainKeys, - moreItems: normalizedMoreKeys - }); - } - } - - return merged || defaultItems; -} diff --git a/legacy-dashboard/src/utils/streamMonacoDisabled.js b/legacy-dashboard/src/utils/streamMonacoDisabled.js deleted file mode 100644 index 23430cbf..00000000 --- a/legacy-dashboard/src/utils/streamMonacoDisabled.js +++ /dev/null @@ -1 +0,0 @@ -throw new Error("stream-monaco is disabled in the dashboard bundle."); diff --git a/legacy-dashboard/src/utils/toast.js b/legacy-dashboard/src/utils/toast.js deleted file mode 100644 index edd7d436..00000000 --- a/legacy-dashboard/src/utils/toast.js +++ /dev/null @@ -1,16 +0,0 @@ -import { useToastStore } from '@/stores/toast' - -export function useToast() { - const store = useToastStore() - - const toast = (message, color = 'info', opts = {}) => - store.add({ message, color, ...opts }) - - return { - toast, - success: (msg, opts) => toast(msg, 'success', opts), - error: (msg, opts) => toast(msg, 'error', opts), - info: (msg, opts) => toast(msg, 'primary', opts), - warning: (msg, opts) => toast(msg, 'warning', opts) - } -} diff --git a/legacy-dashboard/src/views/AboutPage.vue b/legacy-dashboard/src/views/AboutPage.vue deleted file mode 100644 index b994c1d9..00000000 --- a/legacy-dashboard/src/views/AboutPage.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - \ No newline at end of file diff --git a/legacy-dashboard/src/views/AlkaidPage.vue b/legacy-dashboard/src/views/AlkaidPage.vue deleted file mode 100644 index c23cf1f3..00000000 --- a/legacy-dashboard/src/views/AlkaidPage.vue +++ /dev/null @@ -1,97 +0,0 @@ - - - - - \ No newline at end of file diff --git a/legacy-dashboard/src/views/ChatBoxPage.vue b/legacy-dashboard/src/views/ChatBoxPage.vue deleted file mode 100644 index 940b52dc..00000000 --- a/legacy-dashboard/src/views/ChatBoxPage.vue +++ /dev/null @@ -1,23 +0,0 @@ - - - - - \ No newline at end of file diff --git a/legacy-dashboard/src/views/ChatPage.vue b/legacy-dashboard/src/views/ChatPage.vue deleted file mode 100644 index b6f5e3fb..00000000 --- a/legacy-dashboard/src/views/ChatPage.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/legacy-dashboard/src/views/ConfigPage.vue b/legacy-dashboard/src/views/ConfigPage.vue deleted file mode 100644 index 2d1dc78e..00000000 --- a/legacy-dashboard/src/views/ConfigPage.vue +++ /dev/null @@ -1,1079 +0,0 @@ - - - - - - diff --git a/legacy-dashboard/src/views/ConsolePage.vue b/legacy-dashboard/src/views/ConsolePage.vue deleted file mode 100644 index 5ad67b77..00000000 --- a/legacy-dashboard/src/views/ConsolePage.vue +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - diff --git a/legacy-dashboard/src/views/ConversationPage.vue b/legacy-dashboard/src/views/ConversationPage.vue deleted file mode 100644 index 41e63391..00000000 --- a/legacy-dashboard/src/views/ConversationPage.vue +++ /dev/null @@ -1,1430 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/CronJobPage.vue b/legacy-dashboard/src/views/CronJobPage.vue deleted file mode 100644 index 597b6109..00000000 --- a/legacy-dashboard/src/views/CronJobPage.vue +++ /dev/null @@ -1,1532 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/ExtensionPage.vue b/legacy-dashboard/src/views/ExtensionPage.vue deleted file mode 100644 index df88f3df..00000000 --- a/legacy-dashboard/src/views/ExtensionPage.vue +++ /dev/null @@ -1,1355 +0,0 @@ - - - - - - - diff --git a/legacy-dashboard/src/views/PersonaPage.vue b/legacy-dashboard/src/views/PersonaPage.vue deleted file mode 100644 index 8b05193c..00000000 --- a/legacy-dashboard/src/views/PersonaPage.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/PlatformPage.vue b/legacy-dashboard/src/views/PlatformPage.vue deleted file mode 100644 index b671b8ab..00000000 --- a/legacy-dashboard/src/views/PlatformPage.vue +++ /dev/null @@ -1,698 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/PluginPagePage.vue b/legacy-dashboard/src/views/PluginPagePage.vue deleted file mode 100644 index 2621d7ac..00000000 --- a/legacy-dashboard/src/views/PluginPagePage.vue +++ /dev/null @@ -1,682 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/ProviderPage.vue b/legacy-dashboard/src/views/ProviderPage.vue deleted file mode 100644 index c5238bd6..00000000 --- a/legacy-dashboard/src/views/ProviderPage.vue +++ /dev/null @@ -1,920 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/SessionManagementPage.vue b/legacy-dashboard/src/views/SessionManagementPage.vue deleted file mode 100644 index f7392dc0..00000000 --- a/legacy-dashboard/src/views/SessionManagementPage.vue +++ /dev/null @@ -1,1898 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/Settings.vue b/legacy-dashboard/src/views/Settings.vue deleted file mode 100644 index 1020b51e..00000000 --- a/legacy-dashboard/src/views/Settings.vue +++ /dev/null @@ -1,1537 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/SubAgentPage.vue b/legacy-dashboard/src/views/SubAgentPage.vue deleted file mode 100644 index cd653184..00000000 --- a/legacy-dashboard/src/views/SubAgentPage.vue +++ /dev/null @@ -1,626 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/TracePage.vue b/legacy-dashboard/src/views/TracePage.vue deleted file mode 100644 index 02244bc2..00000000 --- a/legacy-dashboard/src/views/TracePage.vue +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - diff --git a/legacy-dashboard/src/views/WelcomePage.vue b/legacy-dashboard/src/views/WelcomePage.vue deleted file mode 100644 index 3ef8d00b..00000000 --- a/legacy-dashboard/src/views/WelcomePage.vue +++ /dev/null @@ -1,575 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/alkaid/KnowledgeBase.vue b/legacy-dashboard/src/views/alkaid/KnowledgeBase.vue deleted file mode 100644 index 19612da1..00000000 --- a/legacy-dashboard/src/views/alkaid/KnowledgeBase.vue +++ /dev/null @@ -1,1386 +0,0 @@ - - - - - - - diff --git a/legacy-dashboard/src/views/alkaid/LongTermMemory.vue b/legacy-dashboard/src/views/alkaid/LongTermMemory.vue deleted file mode 100644 index 1bd8fbed..00000000 --- a/legacy-dashboard/src/views/alkaid/LongTermMemory.vue +++ /dev/null @@ -1,1078 +0,0 @@ - - - - - - - diff --git a/legacy-dashboard/src/views/alkaid/Other.vue b/legacy-dashboard/src/views/alkaid/Other.vue deleted file mode 100644 index d701ae73..00000000 --- a/legacy-dashboard/src/views/alkaid/Other.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/authentication/auth/LoginPage.vue b/legacy-dashboard/src/views/authentication/auth/LoginPage.vue deleted file mode 100644 index fb3cf31e..00000000 --- a/legacy-dashboard/src/views/authentication/auth/LoginPage.vue +++ /dev/null @@ -1,367 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/authentication/auth/SetupPage.vue b/legacy-dashboard/src/views/authentication/auth/SetupPage.vue deleted file mode 100644 index 32d6bcf0..00000000 --- a/legacy-dashboard/src/views/authentication/auth/SetupPage.vue +++ /dev/null @@ -1,176 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/authentication/authForms/AuthLogin.vue b/legacy-dashboard/src/views/authentication/authForms/AuthLogin.vue deleted file mode 100644 index 7d1618b5..00000000 --- a/legacy-dashboard/src/views/authentication/authForms/AuthLogin.vue +++ /dev/null @@ -1,230 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/authentication/authForms/AuthSetup.vue b/legacy-dashboard/src/views/authentication/authForms/AuthSetup.vue deleted file mode 100644 index 1063782e..00000000 --- a/legacy-dashboard/src/views/authentication/authForms/AuthSetup.vue +++ /dev/null @@ -1,130 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageAccount.vue b/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageAccount.vue deleted file mode 100644 index 3ac66bfc..00000000 --- a/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageAccount.vue +++ /dev/null @@ -1,74 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageRecovery.vue b/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageRecovery.vue deleted file mode 100644 index af6fae5a..00000000 --- a/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageRecovery.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageTotp.vue b/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageTotp.vue deleted file mode 100644 index 940f01e9..00000000 --- a/legacy-dashboard/src/views/authentication/authForms/stages/AuthStageTotp.vue +++ /dev/null @@ -1,84 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/extension/InstalledPluginsTab.vue b/legacy-dashboard/src/views/extension/InstalledPluginsTab.vue deleted file mode 100644 index 521950f7..00000000 --- a/legacy-dashboard/src/views/extension/InstalledPluginsTab.vue +++ /dev/null @@ -1,572 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/extension/MarketPluginsTab.vue b/legacy-dashboard/src/views/extension/MarketPluginsTab.vue deleted file mode 100644 index 73767a2c..00000000 --- a/legacy-dashboard/src/views/extension/MarketPluginsTab.vue +++ /dev/null @@ -1,401 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/extension/PluginDetailPage.vue b/legacy-dashboard/src/views/extension/PluginDetailPage.vue deleted file mode 100644 index 97b0fa7e..00000000 --- a/legacy-dashboard/src/views/extension/PluginDetailPage.vue +++ /dev/null @@ -1,1389 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/extension/extensionPreferenceStorage.mjs b/legacy-dashboard/src/views/extension/extensionPreferenceStorage.mjs deleted file mode 100644 index c0ce80bd..00000000 --- a/legacy-dashboard/src/views/extension/extensionPreferenceStorage.mjs +++ /dev/null @@ -1,89 +0,0 @@ -export const PINNED_EXTENSIONS_STORAGE_KEY = "astrbot.pinnedExtensions"; - -const getStorageForRead = (storageOverride) => { - if (storageOverride === null) { - return null; - } - if (storageOverride !== undefined) { - return typeof storageOverride?.getItem === "function" - ? storageOverride - : null; - } - if (typeof window === "undefined") { - return null; - } - try { - const localStorage = window.localStorage ?? null; - return typeof localStorage?.getItem === "function" ? localStorage : null; - } catch { - return null; - } -}; - -const getStorageForWrite = (storageOverride) => { - if (storageOverride === null) { - return null; - } - if (storageOverride !== undefined) { - return typeof storageOverride?.setItem === "function" - ? storageOverride - : null; - } - if (typeof window === "undefined") { - return null; - } - try { - const localStorage = window.localStorage ?? null; - return typeof localStorage?.setItem === "function" ? localStorage : null; - } catch { - return null; - } -}; - -const normalizePinnedExtensions = (value) => { - if (!Array.isArray(value)) { - return []; - } - - const seen = new Set(); - return value - .filter((item) => typeof item === "string" && item.trim().length > 0) - .map((item) => item.trim()) - .filter((item) => { - if (seen.has(item)) { - return false; - } - seen.add(item); - return true; - }); -}; - -export const readPinnedExtensions = (storage) => { - const targetStorage = getStorageForRead(storage); - if (!targetStorage) { - return []; - } - - try { - const raw = targetStorage.getItem(PINNED_EXTENSIONS_STORAGE_KEY); - return normalizePinnedExtensions(raw ? JSON.parse(raw) : []); - } catch { - return []; - } -}; - -export const writePinnedExtensions = (names, storage) => { - const targetStorage = getStorageForWrite(storage); - if (!targetStorage) { - return; - } - - try { - targetStorage.setItem( - PINNED_EXTENSIONS_STORAGE_KEY, - JSON.stringify(normalizePinnedExtensions(names)), - ); - } catch { - // Ignore restricted storage environments. - } -}; diff --git a/legacy-dashboard/src/views/extension/useExtensionPage.js b/legacy-dashboard/src/views/extension/useExtensionPage.js deleted file mode 100644 index cb598438..00000000 --- a/legacy-dashboard/src/views/extension/useExtensionPage.js +++ /dev/null @@ -1,2513 +0,0 @@ -import { commandApi, pluginApi } from "@/api/v1"; -import { pluginSidebarState } from "@/composables/usePluginSidebarItems"; -import { useI18n, useModuleI18n } from "@/i18n/composables"; -import { useCommonStore } from "@/stores/common"; -import { resolveErrorMessage } from "@/utils/errorUtils"; -import { getValidHashTab, replaceTabRoute } from "@/utils/hashRouteTabs.mjs"; -import { getPlatformDisplayName } from "@/utils/platformUtils"; -import { - buildSearchQuery, - matchesPluginSearch, - normalizeStr, - toInitials, - toPinyinText, -} from "@/utils/pluginSearch"; -import { computed, onMounted, onUnmounted, reactive, ref, watch } from "vue"; -import { useRoute, useRouter } from "vue-router"; - -const buildFailedPluginItems = (raw) => { - return Object.entries(raw || {}).map(([dirName, info]) => { - const detail = info && typeof info === "object" ? info : {}; - return { - ...detail, - dir_name: dirName, - name: detail.name || dirName, - display_name: detail.display_name || detail.name || dirName, - error: detail.error || "", - traceback: detail.traceback || "", - reserved: !!detail.reserved, - }; - }); -}; - -export const useExtensionPage = () => { - const commonStore = useCommonStore(); - const { t } = useI18n(); - const { tm } = useModuleI18n("features/extension"); - const router = useRouter(); - const route = useRoute(); - - const getSelectedGitHubProxy = () => { - if (typeof window === "undefined" || !window.localStorage) return ""; - return localStorage.getItem("githubProxyRadioValue") === "1" - ? localStorage.getItem("selectedGitHubProxy") || "" - : ""; - }; - - // 检查指令冲突并提示 - const conflictDialog = reactive({ - show: false, - count: 0, - }); - const checkAndPromptConflicts = async () => { - try { - const res = await commandApi.list(); - if (res.data.status === "ok") { - const conflicts = res.data.data.summary?.conflicts || 0; - if (conflicts > 0) { - conflictDialog.count = conflicts; - conflictDialog.show = true; - } - } - } catch (err) { - console.debug("Failed to check command conflicts:", err); - } - }; - const handleConflictConfirm = () => { - activeTab.value = "commands"; - }; - - const fileInput = ref(null); - const activeTab = ref("installed"); - const validTabs = ["installed", "market", "mcp", "skills", "components"]; - const isValidTab = (tab) => validTabs.includes(tab); - const getLocationHash = () => route.hash || ""; - const extractTabFromHash = (hash) => getValidHashTab(hash, validTabs); - const syncTabFromHash = (hash) => { - const tab = extractTabFromHash(hash); - if (tab) { - activeTab.value = tab; - return true; - } - return false; - }; - const extension_data = reactive({ - data: [], - message: "", - }); - - const snack_message = ref(""); - const snack_show = ref(false); - const snack_success = ref("success"); - const configDialog = ref(false); - const extension_config = reactive({ - metadata: {}, - config: {}, - i18n: {}, - }); - const pluginMarketData = ref([]); - const loadingDialog = reactive({ - show: false, - title: "", - statusCode: 0, // 0: loading, 1: success, 2: error, - result: "", - }); - const curr_namespace = ref(""); - const currentConfigPlugin = ref(""); - const updatingAll = ref(false); - - const readmeDialog = reactive({ - show: false, - pluginName: "", - repoUrl: null, - }); - - // 强制更新确认对话框 - const forceUpdateDialog = reactive({ - show: false, - extensionName: "", - }); - - const updateConfirmDialog = reactive({ - show: false, - extensionName: "", - forceUpdate: false, - }); - - // 更新全部插件确认对话框 - const updateAllConfirmDialog = reactive({ - show: false, - }); - - // 插件更新日志对话框(复用 ReadmeDialog) - const changelogDialog = reactive({ - show: false, - pluginName: "", - repoUrl: null, - }); - - const pluginSearch = ref(""); - const loading_ = ref(false); - - // 分页相关 - const currentPage = ref(1); - - // 危险插件确认对话框 - const dangerConfirmDialog = ref(false); - const selectedDangerPlugin = ref(null); - const selectedMarketInstallPlugin = ref(null); - const installSupport = reactive({ - checked: false, - supported: true, - message: "", - }); - const installUrlValidation = reactive({ - validating: false, - status: "idle", - message: "", - version: "", - metadata: null, - }); - - // AstrBot 版本范围不兼容警告对话框 - const versionSupportDialog = reactive({ - show: false, - message: "", - }); - - // 卸载插件确认对话框(列表模式用) - const showUninstallDialog = ref(false); - const uninstallTarget = ref(null); - - // 自定义插件源相关 - const showSourceDialog = ref(false); - const showSourceManagerDialog = ref(false); - const sourceName = ref(""); - const sourceUrl = ref(""); - const sourceResolving = ref(false); - const sourceResolvedUrl = ref(""); - const sourceResolveVisible = ref(false); - const sourceMarketMeta = ref(null); - const customSources = ref([]); - const selectedSource = ref(null); - const showRemoveSourceDialog = ref(false); - const sourceToRemove = ref(null); - const editingSource = ref(false); - const originalSourceUrl = ref(""); - const sourceBindingDialog = reactive({ - show: false, - loading: false, - saving: false, - extension: null, - candidates: [], - selectedKey: "", - pendingUpdate: null, - validationSerial: 0, - }); - - // 插件市场相关 - const extension_url = ref(""); - const dialog = ref(false); - const upload_file = ref(null); - const uploadTab = ref("file"); - const showPluginFullName = ref(false); - const marketSearch = ref(""); - const debouncedMarketSearch = ref(""); - const refreshingMarket = ref(false); - const sortBy = ref("default"); // default, stars, author, updated - const sortOrder = ref("desc"); // desc (降序) or asc (升序) - const randomPluginNames = ref([]); - const marketCategoryFilter = ref("all"); - - // 插件市场拼音搜索 - - const normalizeMarketCategory = (rawCategory) => { - const normalized = String(rawCategory || "") - .trim() - .toLowerCase(); - if (!normalized) { - return "other"; - } - return normalized.replace(/[\s-]+/g, "_"); - }; - - const getMarketCategoryLabel = (key, rawCategory = "") => { - const fallbackMap = { - all: "All", - ai_tools: "AI Tools", - entertainment: "Entertainment", - productivity: "Productivity", - integrations: "Integrations", - utilities: "Utilities", - other: "Other", - }; - const i18nKey = `market.categories.${key}`; - const translated = tm(i18nKey); - if (translated && !translated.includes("[MISSING:")) { - return translated; - } - if (fallbackMap[key]) { - return fallbackMap[key]; - } - const normalizedRaw = String(rawCategory || "").trim(); - if (normalizedRaw) { - return normalizedRaw; - } - return key - .split(/[_-]+/) - .filter(Boolean) - .map((part) => part[0].toUpperCase() + part.slice(1)) - .join(" "); - }; - - const marketCategoryMeta = computed(() => { - const categories = new Map(); - - for (const plugin of pluginMarketData.value) { - const categoryKey = normalizeMarketCategory(plugin?.category); - const categoryData = categories.get(categoryKey); - if (categoryData) { - categoryData.count += 1; - continue; - } - categories.set(categoryKey, { - count: 1, - rawLabel: String(plugin?.category || "").trim(), - }); - } - - return categories; - }); - - const marketCategoryCounts = computed(() => { - const counts = { all: pluginMarketData.value.length }; - for (const [ - categoryKey, - categoryData, - ] of marketCategoryMeta.value.entries()) { - counts[categoryKey] = categoryData.count; - } - return counts; - }); - - const marketCategoryItems = computed(() => { - const items = [ - { - value: "all", - label: getMarketCategoryLabel("all"), - count: marketCategoryCounts.value.all || 0, - }, - ]; - - for (const [ - categoryKey, - categoryData, - ] of marketCategoryMeta.value.entries()) { - items.push({ - value: categoryKey, - label: getMarketCategoryLabel(categoryKey, categoryData.rawLabel), - count: categoryData.count, - }); - } - - return items; - }); - - // 过滤要显示的插件 - const filteredExtensions = computed(() => { - const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - return data; - }); - - const compareInstalledPluginNames = (left, right) => - normalizeStr(left?.name ?? "").localeCompare( - normalizeStr(right?.name ?? ""), - undefined, - { - sensitivity: "base", - }, - ); - - const compareInstalledFallback = (left, right) => { - const reservedDiff = - Number(!!left.plugin?.reserved) - Number(!!right.plugin?.reserved); - if (reservedDiff !== 0) { - return reservedDiff; - } - - const nameCompare = compareInstalledPluginNames(left.plugin, right.plugin); - return nameCompare !== 0 ? nameCompare : left.index - right.index; - }; - - const sortInstalledPlugins = (plugins) => { - return plugins - .map((plugin, index) => ({ - plugin, - index, - })) - .sort(compareInstalledFallback) - .map((item) => item.plugin); - }; - - // 通过搜索过滤插件 - const filteredPlugins = computed(() => { - const query = buildSearchQuery(pluginSearch.value); - const filtered = query - ? filteredExtensions.value.filter((plugin) => - matchesPluginSearch(plugin, query), - ) - : filteredExtensions.value; - - return sortInstalledPlugins(filtered); - }); - - // 过滤后的插件市场数据(带搜索) - const filteredMarketPlugins = computed(() => { - const query = buildSearchQuery(debouncedMarketSearch.value); - const targetCategory = normalizeMarketCategory(marketCategoryFilter.value); - const shouldFilterByCategory = marketCategoryFilter.value !== "all"; - if (!query) { - if (!shouldFilterByCategory) { - return pluginMarketData.value; - } - return pluginMarketData.value.filter( - (plugin) => - normalizeMarketCategory(plugin?.category) === targetCategory, - ); - } - - return pluginMarketData.value.filter((plugin) => { - const matchesSearch = matchesPluginSearch(plugin, query); - const matchesCategory = shouldFilterByCategory - ? normalizeMarketCategory(plugin?.category) === targetCategory - : true; - return matchesSearch && matchesCategory; - }); - }); - - // 所有插件列表,推荐插件排在前面 - const sortedPlugins = computed(() => { - let plugins = [...filteredMarketPlugins.value]; - - // 根据排序选项排序 - if (sortBy.value === "stars") { - // 按 star 数排序 - plugins.sort((a, b) => { - const starsA = a.stars ?? 0; - const starsB = b.stars ?? 0; - return sortOrder.value === "desc" ? starsB - starsA : starsA - starsB; - }); - } else if (sortBy.value === "author") { - // 按作者名字典序排序 - plugins.sort((a, b) => { - const authorA = (a.author ?? "").toLowerCase(); - const authorB = (b.author ?? "").toLowerCase(); - const result = authorA.localeCompare(authorB); - return sortOrder.value === "desc" ? -result : result; - }); - } else if (sortBy.value === "updated") { - // 按更新时间排序 - plugins.sort((a, b) => { - const dateA = a.updated_at ? new Date(a.updated_at).getTime() : 0; - const dateB = b.updated_at ? new Date(b.updated_at).getTime() : 0; - return sortOrder.value === "desc" ? dateB - dateA : dateA - dateB; - }); - } else { - // default: 推荐插件排在前面 - const pinned = plugins.filter((plugin) => plugin?.pinned); - const notPinned = plugins.filter((plugin) => !plugin?.pinned); - return [...pinned, ...notPinned]; - } - - return plugins; - }); - - const RANDOM_PLUGINS_COUNT = 3; - - const randomPlugins = computed(() => { - const allPlugins = pluginMarketData.value; - if (allPlugins.length === 0) return []; - - const pluginsByName = new Map( - allPlugins.map((plugin) => [plugin.name, plugin]), - ); - const selected = randomPluginNames.value - .map((name) => pluginsByName.get(name)) - .filter(Boolean); - - if (selected.length > 0) { - return selected; - } - - return allPlugins.slice( - 0, - Math.min(RANDOM_PLUGINS_COUNT, allPlugins.length), - ); - }); - - const shufflePlugins = (plugins) => { - const shuffled = [...plugins]; - for (let i = shuffled.length - 1; i > 0; i -= 1) { - const j = Math.floor(Math.random() * (i + 1)); - [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; - } - return shuffled; - }; - - const refreshRandomPlugins = () => { - const shuffled = shufflePlugins(pluginMarketData.value); - randomPluginNames.value = shuffled - .slice(0, Math.min(RANDOM_PLUGINS_COUNT, shuffled.length)) - .map((plugin) => plugin.name); - }; - - // 分页计算属性 - const displayItemsPerPage = 9; // 固定每页显示9个卡片(3行) - - const totalPages = computed(() => { - return Math.ceil(sortedPlugins.value.length / displayItemsPerPage); - }); - - const paginatedPlugins = computed(() => { - const start = (currentPage.value - 1) * displayItemsPerPage; - const end = start + displayItemsPerPage; - return sortedPlugins.value.slice(start, end); - }); - - const updatableExtensions = computed(() => { - const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - return data.filter((ext) => ext.has_update); - }); - - // 方法 - const toast = (message, success) => { - snack_message.value = message; - snack_show.value = true; - snack_success.value = success; - }; - - const resetLoadingDialog = () => { - loadingDialog.show = false; - loadingDialog.title = tm("dialogs.loading.title"); - loadingDialog.statusCode = 0; - loadingDialog.result = ""; - }; - - const onLoadingDialogResult = (statusCode, result, timeToClose = 2000) => { - loadingDialog.statusCode = statusCode; - loadingDialog.result = result; - if (timeToClose === -1) return; - setTimeout(resetLoadingDialog, timeToClose); - }; - - const failedPluginsDict = ref({}); - const failedPluginItems = computed(() => - buildFailedPluginItems(failedPluginsDict.value), - ); - - const getExtensions = async ({ withLoading = true } = {}) => { - if (withLoading) { - loading_.value = true; - } - try { - const res = await pluginApi.list(); - Object.assign(extension_data, res.data); - - // 同步插件数据到侧边栏共享状态 - pluginSidebarState.plugins = res.data?.data || []; - - const failRes = await pluginApi.failed(); - failedPluginsDict.value = failRes.data.data || {}; - - if (pluginMarketData.value.length > 0) { - await checkUpdate(); - } - } catch (err) { - toast(err, "error"); - } finally { - if (withLoading) { - loading_.value = false; - } - } - }; - - const handleReloadAllFailed = async () => { - const dirNames = Object.keys(failedPluginsDict.value); - if (dirNames.length === 0) { - toast("没有需要重载的失败插件", "info"); - return; - } - - loading_.value = true; - try { - const promises = dirNames.map((dir) => pluginApi.reloadFailed(dir)); - await Promise.all(promises); - - toast("已尝试重载所有失败插件", "success"); - - // 清空 message 关闭对话框 - extension_data.message = ""; - - // 刷新列表 - await getExtensions(); - } catch (e) { - console.error("重载失败:", e); - toast("批量重载过程中出现错误", "error"); - } finally { - loading_.value = false; - } - }; - - const reloadFailedPlugin = async (dirName) => { - if (!dirName) return; - - try { - const res = await pluginApi.reloadFailed(dirName); - if (res.data.status === "error") { - toast(res.data.message || tm("messages.reloadFailed"), "error"); - return; - } - toast(res.data.message || tm("messages.reloadSuccess"), "success"); - await getExtensions(); - } catch (err) { - toast(resolveErrorMessage(err, tm("messages.reloadFailed")), "error"); - } - }; - - const requestUninstall = (target) => { - if (!target?.id || !target?.kind) return; - uninstallTarget.value = target; - showUninstallDialog.value = true; - }; - - const uninstall = async ( - target, - { deleteConfig = false, deleteData = false, skipConfirm = false } = {}, - ) => { - if (!target?.id || !target?.kind) return; - - if (!skipConfirm) { - requestUninstall(target); - return; - } - - const isFailed = target.kind === "failed"; - const options = { - delete_config: deleteConfig, - delete_data: deleteData, - }; - - toast(`${tm("messages.uninstalling")} ${target.id}`, "primary"); - - try { - const res = isFailed - ? await pluginApi.uninstallFailed(target.id, options) - : await pluginApi.uninstall(target.id, options); - if (res.data.status === "error") { - toast(res.data.message, "error"); - return; - } - if (!isFailed) { - Object.assign(extension_data, res.data); - } - toast(res.data.message, "success"); - await getExtensions(); - } catch (err) { - toast(resolveErrorMessage(err, tm("messages.operationFailed")), "error"); - } - }; - - const requestUninstallPlugin = (name) => { - if (!name) return; - uninstall({ kind: "normal", id: name }, { skipConfirm: false }); - }; - - const requestUninstallFailedPlugin = (dirName) => { - if (!dirName) return; - uninstall({ kind: "failed", id: dirName }, { skipConfirm: false }); - }; - - const normalizeInstallUrl = (value) => - String(value || "") - .trim() - .replace(/\/+$/, ""); - - const isGithubRepoUrl = (value) => - /^https:\/\/github\.com\/[^/\s]+\/[^/\s]+(?:\.git)?(?:\/tree\/[^/\s]+)?$/i.test( - normalizeInstallUrl(value), - ); - - const normalizeRegistryUrl = (value) => - String(value || "") - .trim() - .replace(/\/+$/, ""); - - const getMarketPluginId = (plugin) => { - return String(plugin?.market_plugin_id || "").trim(); - }; - - const getMarketInstallSourcePayload = () => { - const plugin = selectedMarketInstallPlugin.value; - if ( - !plugin || - normalizeInstallUrl(plugin.repo) !== - normalizeInstallUrl(extension_url.value) - ) { - return {}; - } - - return { - install_method: "market", - registry_url: selectedSource.value || null, - market_plugin_id: getMarketPluginId(plugin), - }; - }; - - const getInstalledExtensionByName = (extensionName) => { - const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - return data.find((extension) => extension.name === extensionName) || null; - }; - - const findMarketPluginForExtension = (extension) => { - if (!extension) return null; - const source = extension.install_source || {}; - if (source.implicit === true || source.install_method !== "market") { - return null; - } - if (extension.update_market_plugin) { - return extension.update_market_plugin; - } - const repo = normalizeInstallUrl(extension.repo).toLowerCase(); - - if (repo) { - return ( - pluginMarketData.value.find( - (plugin) => normalizeInstallUrl(plugin?.repo).toLowerCase() === repo, - ) || null - ); - } - - return ( - pluginMarketData.value.find((plugin) => plugin.name === extension.name) || - null - ); - }; - - const getUpdateDownloadUrl = (extension) => - String(findMarketPluginForExtension(extension)?.download_url || "").trim(); - - const buildMarketPluginLookup = (plugins) => { - const byIdentifier = new Map(); - const byRepo = new Map(); - const byName = new Map(); - - plugins.forEach((plugin) => { - const identifier = getMarketPluginId(plugin); - if (identifier) { - byIdentifier.set(identifier, plugin); - } - if (plugin.repo) { - byRepo.set(normalizeInstallUrl(plugin.repo).toLowerCase(), plugin); - } - byName.set(normalizeStr(plugin.name), plugin); - }); - - return { byIdentifier, byRepo, byName }; - }; - - const checkUpdate = async () => { - const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - const sourcePlugins = new Map(); - - data.forEach((extension) => { - extension.online_version = ""; - extension.has_update = false; - extension.update_market_plugin = null; - - const source = extension.install_source; - if ( - !extension.updates_enabled || - !source || - source.implicit === true || - source.install_method !== "market" - ) { - return; - } - - const registryUrl = normalizeRegistryUrl(source.registry_url); - if (!sourcePlugins.has(registryUrl)) { - sourcePlugins.set(registryUrl, []); - } - sourcePlugins.get(registryUrl).push(extension); - }); - - await Promise.all( - [...sourcePlugins.entries()].map(async ([registryUrl, extensions]) => { - let marketPlugins = []; - try { - marketPlugins = - registryUrl === normalizeRegistryUrl(selectedSource.value) - ? pluginMarketData.value - : await commonStore.getPluginCollections( - false, - registryUrl || null, - ); - } catch (error) { - console.warn("Failed to load plugin source for update check:", error); - return; - } - - const lookup = buildMarketPluginLookup(marketPlugins); - extensions.forEach((extension) => { - const source = extension.install_source || {}; - const sourceIdentifier = String(source.market_plugin_id || "").trim(); - const sourceRepo = normalizeInstallUrl(source.repo).toLowerCase(); - const normalizedExtensionName = normalizeStr( - extension.marketplace_name, - ); - const matchedPlugin = - (sourceIdentifier && lookup.byIdentifier.get(sourceIdentifier)) || - (sourceRepo && lookup.byRepo.get(sourceRepo)) || - lookup.byName.get(normalizedExtensionName); - - if (!matchedPlugin) { - return; - } - - extension.update_market_plugin = matchedPlugin; - const localVersion = String(extension.version || "").trim(); - const onlineVersion = String(matchedPlugin.version || "").trim(); - const isKnownVersion = - /^v?\d+/.test(localVersion) && - /^v?\d+/.test(onlineVersion) && - onlineVersion !== tm("status.unknown"); - const versionCompare = isKnownVersion - ? compareVersions(localVersion, onlineVersion) - : 0; - - extension.online_version = onlineVersion; - extension.has_update = - isKnownVersion && - (versionCompare < 0 || - (versionCompare === 0 && - localVersion.includes("-") && - !onlineVersion.includes("-"))); - }); - }), - ); - }; - - const uninstallExtension = async ( - extensionName, - optionsOrSkipConfirm = false, - ) => { - if (!extensionName) return; - - if (typeof optionsOrSkipConfirm === "boolean") { - return uninstall( - { kind: "normal", id: extensionName }, - { skipConfirm: optionsOrSkipConfirm }, - ); - } - - return uninstall( - { kind: "normal", id: extensionName }, - { ...(optionsOrSkipConfirm || {}), skipConfirm: true }, - ); - }; - - // 处理卸载确认对话框的确认事件 - const handleUninstallConfirm = async (options) => { - const target = uninstallTarget.value; - if (!target) return; - - try { - await uninstall(target, { ...(options || {}), skipConfirm: true }); - } finally { - uninstallTarget.value = null; - showUninstallDialog.value = false; - } - }; - - const openUpdateConfirmDialog = (extensionName, forceUpdate = false) => { - updateConfirmDialog.extensionName = extensionName; - updateConfirmDialog.forceUpdate = forceUpdate; - updateConfirmDialog.show = true; - }; - - const closeUpdateConfirmDialog = () => { - updateConfirmDialog.show = false; - updateConfirmDialog.extensionName = ""; - updateConfirmDialog.forceUpdate = false; - }; - - const buildUpdateContext = (ext) => { - const source = ext?.install_source || null; - const installMethod = String(source?.install_method || "") - .trim() - .toLowerCase(); - const repoUrl = normalizeInstallUrl(ext?.repo || source?.repo); - const needsSourceSelection = - Boolean(ext) && - (!source || - source.implicit === true || - !["market", "github"].includes(installMethod)); - - return { source, installMethod, repoUrl, needsSourceSelection }; - }; - - const performUpdateWithoutSourceSelection = ( - extensionName, - ext, - forceUpdate, - repoUrl, - ) => { - if (ext && ext.updates_enabled === false && !repoUrl) { - toast( - ext.update_disabled_reason || tm("messages.updateDisabled"), - "info", - ); - return; - } - - // 如果没有检测到更新且不是强制更新,则弹窗确认 - if (!ext?.has_update && !forceUpdate) { - forceUpdateDialog.extensionName = extensionName; - forceUpdateDialog.show = true; - return; - } - - openUpdateConfirmDialog(extensionName, forceUpdate); - }; - - const updateExtension = async ( - extension_name, - forceUpdate = false, - options = {}, - ) => { - const ext = getInstalledExtensionByName(extension_name); - const { repoUrl, needsSourceSelection } = buildUpdateContext(ext); - - if ( - needsSourceSelection && - !options.skipSourceCheck && - !repoUrl - ) { - toast(tm("messages.updateDisabled"), "info"); - return; - } - - if (needsSourceSelection && !options.skipSourceCheck) { - await openPluginSourceBindingDialog(ext, { - pendingUpdate: { extensionName: extension_name, forceUpdate }, - }); - return; - } - - performUpdateWithoutSourceSelection( - extension_name, - ext, - forceUpdate, - repoUrl, - ); - }; - - const confirmUpdatePlugin = async () => { - const extensionName = updateConfirmDialog.extensionName; - const ext = getInstalledExtensionByName(extensionName); - if (!extensionName || !ext) { - closeUpdateConfirmDialog(); - return; - } - - const hasDownloadUrl = Boolean(getUpdateDownloadUrl(ext)); - - closeUpdateConfirmDialog(); - loadingDialog.title = tm("status.loading"); - loadingDialog.statusCode = 0; - loadingDialog.result = ""; - loadingDialog.show = true; - try { - const res = await pluginApi.update(extensionName, { - proxy: hasDownloadUrl ? "" : getSelectedGitHubProxy(), - }); - - if (res.data.status === "error") { - onLoadingDialogResult(2, res.data.message, -1); - return; - } - - Object.assign(extension_data, res.data); - onLoadingDialogResult(1, res.data.message); - setTimeout(async () => { - toast(tm("messages.refreshing"), "info", 2000); - try { - await getExtensions(); - toast(tm("messages.refreshSuccess"), "success"); - - // 更新完成后弹出更新日志 - viewChangelog({ - name: extensionName, - repo: ext?.repo || null, - }); - } catch (error) { - const errorMsg = - error.response?.data?.message || error.message || String(error); - toast(`${tm("messages.refreshFailed")}: ${errorMsg}`, "error"); - } - }, 1000); - } catch (err) { - toast(err, "error"); - } - }; - - // 确认强制更新 - // 显示更新全部插件确认对话框 - const showUpdateAllConfirm = () => { - if (updatableExtensions.value.length === 0) { - toast(tm("messages.noUpdatesAvailable"), "info"); - return; - } - updateAllConfirmDialog.show = true; - }; - - // 确认更新全部插件 - const confirmUpdateAll = () => { - updateAllConfirmDialog.show = false; - updateAllExtensions(); - }; - - // 取消更新全部插件 - const cancelUpdateAll = () => { - updateAllConfirmDialog.show = false; - }; - - const confirmForceUpdate = async () => { - const name = forceUpdateDialog.extensionName; - forceUpdateDialog.show = false; - forceUpdateDialog.extensionName = ""; - await updateExtension(name, true); - }; - - const updateAllExtensions = async () => { - if (updatingAll.value) return; - if (updatableExtensions.value.length === 0) { - toast(tm("messages.noUpdatesAvailable"), "info"); - return; - } - updatingAll.value = true; - loadingDialog.title = tm("status.loading"); - loadingDialog.statusCode = 0; - loadingDialog.result = ""; - loadingDialog.show = true; - - const targets = updatableExtensions.value.map((ext) => ext.name); - try { - const res = await pluginApi.updateMany({ - names: targets, - proxy: getSelectedGitHubProxy(), - }); - - if (res.data.status === "error") { - onLoadingDialogResult( - 2, - res.data.message || - tm("messages.updateAllFailed", { - failed: targets.length, - total: targets.length, - }), - -1, - ); - return; - } - - const results = res.data.data?.results || []; - const failures = results.filter((r) => r.status !== "ok"); - try { - await getExtensions(); - } catch (err) { - const errorMsg = - err.response?.data?.message || err.message || String(err); - failures.push({ name: "refresh", status: "error", message: errorMsg }); - } - - if (failures.length === 0) { - onLoadingDialogResult(1, tm("messages.updateAllSuccess")); - } else { - const failureText = tm("messages.updateAllFailed", { - failed: failures.length, - total: targets.length, - }); - const detail = failures - .map((f) => `${f.name}: ${f.message}`) - .join("\n"); - onLoadingDialogResult(2, `${failureText}\n${detail}`, -1); - } - } catch (err) { - const errorMsg = - err.response?.data?.message || err.message || String(err); - onLoadingDialogResult(2, errorMsg, -1); - } finally { - updatingAll.value = false; - } - }; - - const pluginOn = async (extension) => { - const previousActivated = extension.activated; - extension.activated = true; - try { - const res = await pluginApi.setEnabled(extension.name, true); - if (res.data.status === "error") { - extension.activated = previousActivated; - toast(res.data.message, "error"); - return; - } - toast(res.data.message, "success"); - await getExtensions(); - - await checkAndPromptConflicts(); - } catch (err) { - extension.activated = previousActivated; - toast(err, "error"); - } - }; - - const pluginOff = async (extension) => { - const previousActivated = extension.activated; - extension.activated = false; - try { - const res = await pluginApi.setEnabled(extension.name, false); - if (res.data.status === "error") { - extension.activated = previousActivated; - toast(res.data.message, "error"); - return; - } - toast(res.data.message, "success"); - await getExtensions(); - } catch (err) { - extension.activated = previousActivated; - toast(err, "error"); - } - }; - - const openExtensionConfig = async (extension_name) => { - curr_namespace.value = extension_name; - currentConfigPlugin.value = extension_name; - configDialog.value = true; - try { - const res = await pluginApi.config(extension_name); - extension_config.metadata = res.data.data.metadata; - extension_config.config = res.data.data.config; - extension_config.i18n = res.data.data.i18n || {}; - } catch (err) { - toast(err, "error"); - } - }; - - const updateConfig = async () => { - try { - const res = await pluginApi.updateConfig( - curr_namespace.value, - extension_config.config, - ); - if (res.data.status === "ok") { - toast(res.data.message, "success"); - } else { - toast(res.data.message, "error"); - } - configDialog.value = false; - currentConfigPlugin.value = ""; - extension_config.metadata = {}; - extension_config.config = {}; - extension_config.i18n = {}; - getExtensions(); - } catch (err) { - toast(err, "error"); - } - }; - - const showPluginInfo = (plugin) => { - if (!plugin?.name) return; - router.push({ - name: "ExtensionDetails", - params: { pluginId: plugin.name }, - hash: "#plugin-components", - }); - }; - - const reloadPlugin = async (plugin_name) => { - try { - const res = await pluginApi.reload(plugin_name); - if (res.data.status === "error") { - toast(res.data.message || tm("messages.reloadFailed"), "error"); - return; - } - toast(tm("messages.reloadSuccess"), "success"); - await getExtensions(); - } catch (err) { - toast(resolveErrorMessage(err, tm("messages.reloadFailed")), "error"); - } - }; - - const viewReadme = (plugin) => { - readmeDialog.pluginName = plugin.name; - readmeDialog.repoUrl = plugin.repo; - readmeDialog.show = true; - }; - - // 查看更新日志 - const viewChangelog = (plugin) => { - changelogDialog.pluginName = plugin.name; - changelogDialog.repoUrl = plugin.repo; - changelogDialog.show = true; - }; - - const resetInstallDialogState = () => { - selectedMarketInstallPlugin.value = null; - extension_url.value = ""; - upload_file.value = null; - uploadTab.value = "file"; - installSupport.checked = false; - installSupport.supported = true; - installSupport.message = ""; - }; - - const openInstallDialog = () => { - resetInstallDialogState(); - dialog.value = true; - }; - - const closeInstallDialog = () => { - dialog.value = false; - resetInstallDialogState(); - }; - - const selectedInstallDownloadUrl = computed(() => { - const plugin = selectedMarketInstallPlugin.value; - const downloadUrl = String(plugin?.download_url || "").trim(); - if (!downloadUrl) return ""; - if ( - normalizeInstallUrl(plugin?.repo) !== - normalizeInstallUrl(extension_url.value) - ) { - return ""; - } - return downloadUrl; - }); - - const selectedInstallSourceUrl = computed( - () => - selectedInstallDownloadUrl.value || - String(extension_url.value || "").trim(), - ); - - const installUsesGithubSource = computed( - () => - !selectedInstallDownloadUrl.value && isGithubRepoUrl(extension_url.value), - ); - - const resetInstallUrlValidation = () => { - installUrlValidation.validating = false; - installUrlValidation.status = "idle"; - installUrlValidation.message = ""; - installUrlValidation.version = ""; - installUrlValidation.metadata = null; - }; - - const validatePluginRepo = async (payload) => { - const res = await pluginApi.validateRepo(payload); - if (res.data.status === "error") { - throw new Error(res.data.message || tm("messages.pluginValidateFailed")); - } - return { - data: res.data.data || {}, - message: res.data.message || tm("messages.pluginValidateSuccess"), - }; - }; - - const buildInstallRepoValidationPayload = () => { - const url = String(extension_url.value || "").trim(); - if (!url) { - return null; - } - return { - url, - proxy: getSelectedGitHubProxy(), - }; - }; - - // 为表格视图创建一个处理安装插件的函数 - const handleInstallPlugin = async (plugin) => { - if (plugin.tags && plugin.tags.includes("danger")) { - selectedDangerPlugin.value = plugin; - dangerConfirmDialog.value = true; - } else { - selectedMarketInstallPlugin.value = plugin; - extension_url.value = plugin.repo; - upload_file.value = null; - dialog.value = true; - uploadTab.value = "url"; - } - }; - - // 确认安装危险插件 - const confirmDangerInstall = () => { - if (selectedDangerPlugin.value) { - selectedMarketInstallPlugin.value = selectedDangerPlugin.value; - extension_url.value = selectedDangerPlugin.value.repo; - upload_file.value = null; - dialog.value = true; - uploadTab.value = "url"; - } - dangerConfirmDialog.value = false; - selectedDangerPlugin.value = null; - }; - - // 取消安装危险插件 - const cancelDangerInstall = () => { - dangerConfirmDialog.value = false; - selectedDangerPlugin.value = null; - }; - - // 自定义插件源管理方法 - const loadCustomSources = async () => { - try { - const res = await pluginApi.sources(); - if (res.data.status === "ok") { - customSources.value = res.data.data?.sources || res.data.data || []; - } else { - toast(res.data.message, "error"); - } - } catch (e) { - console.warn("Failed to load custom sources:", e); - customSources.value = []; - } - - // 加载当前选中的插件源 - const currentSource = localStorage.getItem("selectedPluginSource"); - if (currentSource) { - selectedSource.value = currentSource; - } - }; - - const saveCustomSources = async () => { - try { - const res = await pluginApi.replaceSources(customSources.value); - if (res.data.status !== "ok") { - toast(res.data.message, "error"); - } - } catch (e) { - toast(e, "error"); - } - }; - - const addCustomSource = () => { - showSourceManagerDialog.value = false; - editingSource.value = false; - originalSourceUrl.value = ""; - sourceName.value = ""; - sourceUrl.value = ""; - sourceResolvedUrl.value = ""; - sourceResolveVisible.value = false; - sourceMarketMeta.value = null; - sourceResolving.value = false; - showSourceDialog.value = true; - }; - - const openSourceManagerDialog = async () => { - await loadCustomSources(); - showSourceManagerDialog.value = true; - }; - - const selectPluginSource = (sourceUrl) => { - selectedSource.value = sourceUrl; - if (sourceUrl) { - localStorage.setItem("selectedPluginSource", sourceUrl); - } else { - localStorage.removeItem("selectedPluginSource"); - } - // 重新加载插件市场数据 - refreshPluginMarket(); - }; - - const sourceResolveCurrent = computed( - () => - Boolean(sourceResolvedUrl.value) && - normalizeRegistryUrl(sourceResolvedUrl.value) === - normalizeRegistryUrl(sourceUrl.value), - ); - - const validateSourceBindingRepoCandidate = async (candidate) => { - if (!candidate || candidate.install_method !== "github") { - return; - } - if (candidate.validation_status === "loading") { - return; - } - - const serial = ++sourceBindingDialog.validationSerial; - candidate.validation_status = "loading"; - candidate.validation_message = ""; - candidate.version = ""; - try { - const { data, message } = await validatePluginRepo({ - url: candidate.repo, - proxy: getSelectedGitHubProxy(), - }); - if (serial !== sourceBindingDialog.validationSerial) { - return; - } - candidate.version = data.version || tm("status.unknown"); - candidate.validation_status = "valid"; - candidate.validation_message = message; - candidate.metadata = data; - } catch (error) { - if (serial !== sourceBindingDialog.validationSerial) { - return; - } - candidate.version = tm("status.unknown"); - candidate.validation_status = "error"; - candidate.validation_message = resolveErrorMessage( - error, - tm("messages.pluginValidateFailed"), - ); - } - }; - - const resolvePluginSourceCandidates = async (extension) => { - const extensionRepo = normalizeInstallUrl( - extension.repo || extension.install_source?.repo, - ).toLowerCase(); - if (!extensionRepo) { - return { candidates: [], selectedKey: "" }; - } - - await loadCustomSources(); - const sources = [ - { name: tm("market.defaultSource"), url: null }, - ...customSources.value.map((source) => ({ - name: source.name, - url: source.url, - })), - ]; - const currentMarketPluginId = String( - extension.install_source?.market_plugin_id || "", - ).trim(); - const currentRegistryUrl = normalizeRegistryUrl( - extension.install_source?.registry_url, - ); - const currentInstallMethod = String( - extension.install_source?.install_method || "", - ) - .trim() - .toLowerCase(); - const seen = new Set(); - const candidates = []; - - for (const source of sources) { - let marketPlugins = []; - try { - marketPlugins = await commonStore.getPluginCollections( - false, - source.url || null, - ); - } catch (error) { - console.warn("Failed to load plugin source for binding:", error); - continue; - } - - marketPlugins.forEach((plugin) => { - const marketPluginId = getMarketPluginId(plugin); - const pluginRepo = normalizeInstallUrl(plugin.repo).toLowerCase(); - if (!marketPluginId || !pluginRepo || pluginRepo !== extensionRepo) { - return; - } - if ( - currentMarketPluginId && - marketPluginId !== currentMarketPluginId - ) { - return; - } - - const registryUrl = normalizeRegistryUrl(source.url); - const key = `${registryUrl}|${marketPluginId}|${pluginRepo}`; - if (seen.has(key)) { - return; - } - seen.add(key); - candidates.push({ - key, - install_method: "market", - registry_url: registryUrl || null, - registry_name: source.name, - market_plugin_id: marketPluginId, - repo: plugin.repo, - download_url: plugin.download_url || "", - version: plugin.version || tm("status.unknown"), - validation_status: "valid", - validation_message: "", - }); - }); - } - - candidates.push({ - key: `github||${extensionRepo}`, - install_method: "github", - registry_url: null, - registry_name: tm("dialogs.sourceBinding.repoOption"), - market_plugin_id: "", - repo: extension.repo || extension.install_source?.repo, - download_url: "", - version: "", - validation_status: "idle", - validation_message: "", - }); - - const currentCandidate = - currentInstallMethod === "github" - ? candidates.find( - (candidate) => - candidate.install_method === "github" && - normalizeInstallUrl(candidate.repo).toLowerCase() === extensionRepo, - ) - : candidates.find((candidate) => { - return ( - candidate.install_method === "market" && - normalizeRegistryUrl(candidate.registry_url) === - currentRegistryUrl && - (!currentMarketPluginId || - candidate.market_plugin_id === currentMarketPluginId) && - normalizeInstallUrl(candidate.repo).toLowerCase() === extensionRepo - ); - }); - - return { - candidates, - selectedKey: currentCandidate?.key || candidates[0]?.key || "", - }; - }; - - const openPluginSourceBindingDialog = async (extension, options = {}) => { - if (!extension) return; - sourceBindingDialog.show = true; - sourceBindingDialog.loading = true; - sourceBindingDialog.saving = false; - sourceBindingDialog.extension = extension; - sourceBindingDialog.candidates = []; - sourceBindingDialog.selectedKey = ""; - sourceBindingDialog.pendingUpdate = options.pendingUpdate || null; - - try { - const { candidates, selectedKey } = - await resolvePluginSourceCandidates(extension); - sourceBindingDialog.candidates = candidates; - sourceBindingDialog.selectedKey = selectedKey; - } finally { - sourceBindingDialog.loading = false; - } - const selectedCandidate = sourceBindingDialog.candidates.find( - (item) => item.key === sourceBindingDialog.selectedKey, - ); - if (selectedCandidate?.install_method === "github") { - void validateSourceBindingRepoCandidate(selectedCandidate); - } - }; - - const closePluginSourceBindingDialog = () => { - sourceBindingDialog.validationSerial += 1; - sourceBindingDialog.show = false; - sourceBindingDialog.loading = false; - sourceBindingDialog.saving = false; - sourceBindingDialog.extension = null; - sourceBindingDialog.candidates = []; - sourceBindingDialog.selectedKey = ""; - sourceBindingDialog.pendingUpdate = null; - }; - - const continuePendingUpdateAfterBinding = async (pendingUpdate) => { - if (!pendingUpdate) { - return; - } - - await updateExtension(pendingUpdate.extensionName, pendingUpdate.forceUpdate, { - skipSourceCheck: true, - }); - }; - - const confirmPluginSourceBinding = async () => { - const extension = sourceBindingDialog.extension; - const candidate = sourceBindingDialog.candidates.find( - (item) => item.key === sourceBindingDialog.selectedKey, - ); - if (!extension || !candidate || sourceBindingDialog.saving) { - return; - } - - sourceBindingDialog.saving = true; - try { - const pendingUpdate = sourceBindingDialog.pendingUpdate; - const payload = - candidate.install_method === "github" - ? { install_method: "github" } - : { - install_method: "market", - registry_url: candidate.registry_url, - market_plugin_id: candidate.market_plugin_id, - }; - const res = await pluginApi.bindSource(extension.name, payload); - if (res.data.status === "error") { - toast(res.data.message, "error"); - return; - } - - toast(res.data.message || tm("messages.sourceBindSuccess"), "success"); - closePluginSourceBindingDialog(); - await getExtensions(); - checkAlreadyInstalled(); - await checkUpdate(); - await continuePendingUpdateAfterBinding(pendingUpdate); - } catch (error) { - const errorMsg = - error.response?.data?.message || error.message || String(error); - toast(errorMsg, "error"); - } finally { - sourceBindingDialog.saving = false; - } - }; - - const editCustomSource = (source) => { - if (!source) return; - showSourceManagerDialog.value = false; - editingSource.value = true; - originalSourceUrl.value = source.url; - sourceName.value = source.name; - sourceUrl.value = source.url; - sourceResolvedUrl.value = source.url; - sourceResolveVisible.value = false; - sourceMarketMeta.value = null; - sourceResolving.value = false; - showSourceDialog.value = true; - }; - - const removeCustomSource = (source) => { - if (!source) return; - showSourceManagerDialog.value = false; - sourceToRemove.value = source; - showRemoveSourceDialog.value = true; - }; - - const confirmRemoveSource = () => { - if (sourceToRemove.value) { - customSources.value = customSources.value.filter( - (s) => s.url !== sourceToRemove.value.url, - ); - saveCustomSources(); - - // 如果删除的是当前选中的源,切换到默认源 - if (selectedSource.value === sourceToRemove.value.url) { - selectedSource.value = null; - localStorage.removeItem("selectedPluginSource"); - // 重新加载插件市场数据 - refreshPluginMarket(); - } - - toast(tm("market.sourceRemoved"), "success"); - showRemoveSourceDialog.value = false; - sourceToRemove.value = null; - } - }; - - const resolveCustomSource = async () => { - const normalizedUrl = sourceUrl.value.trim(); - - if (!normalizedUrl) { - toast(tm("messages.fillSourceUrl"), "error"); - return; - } - - try { - new URL(normalizedUrl); - } catch (e) { - toast(tm("messages.invalidUrl"), "error"); - return; - } - - const normalizedRegistryUrl = normalizeRegistryUrl(normalizedUrl); - const originalRegistryUrl = normalizeRegistryUrl(originalSourceUrl.value); - if ( - (!editingSource.value || normalizedRegistryUrl !== originalRegistryUrl) && - customSources.value.some( - (source) => normalizeRegistryUrl(source.url) === normalizedRegistryUrl, - ) - ) { - toast(tm("market.sourceExists"), "error"); - return; - } - - sourceResolving.value = true; - try { - const res = await pluginApi.market({ - custom_registry: normalizedRegistryUrl, - force_refresh: true, - }); - if (res.data.status !== "ok") { - toast(res.data.message || tm("messages.sourceResolveFailed"), "error"); - return; - } - - const meta = res.data.data?.$meta; - sourceMarketMeta.value = - meta && typeof meta === "object" && !Array.isArray(meta) ? meta : null; - - const metaName = String(sourceMarketMeta.value?.name || "").trim(); - if (metaName && !sourceName.value.trim()) { - sourceName.value = metaName; - } - sourceResolvedUrl.value = normalizedRegistryUrl; - sourceResolveVisible.value = true; - toast(tm("market.sourceResolved"), "success"); - } catch (error) { - toast( - resolveErrorMessage(error, tm("messages.sourceResolveFailed")), - "error", - ); - } finally { - sourceResolving.value = false; - } - }; - - const saveCustomSource = () => { - const normalizedUrl = sourceUrl.value.trim(); - - if (!normalizedUrl) { - toast(tm("messages.fillSourceUrl"), "error"); - return; - } - - try { - new URL(normalizedUrl); - } catch (e) { - toast(tm("messages.invalidUrl"), "error"); - return; - } - - if (!sourceResolveCurrent.value) { - toast(tm("messages.resolveSourceFirst"), "error"); - return; - } - - if (!sourceName.value.trim()) { - toast(tm("messages.fillSourceName"), "error"); - return; - } - - const normalizedRegistryUrl = normalizeRegistryUrl(normalizedUrl); - const originalRegistryUrl = normalizeRegistryUrl(originalSourceUrl.value); - - if (editingSource.value) { - const index = customSources.value.findIndex( - (s) => normalizeRegistryUrl(s.url) === originalRegistryUrl, - ); - if (index !== -1) { - customSources.value[index] = { - name: sourceName.value.trim(), - url: normalizedRegistryUrl, - }; - - if ( - normalizeRegistryUrl(selectedSource.value) === originalRegistryUrl - ) { - selectedSource.value = normalizedRegistryUrl; - localStorage.setItem("selectedPluginSource", selectedSource.value); - refreshPluginMarket(); - } - } - } else { - if ( - customSources.value.some( - (source) => - normalizeRegistryUrl(source.url) === normalizedRegistryUrl, - ) - ) { - toast(tm("market.sourceExists"), "error"); - return; - } - - customSources.value.push({ - name: sourceName.value.trim(), - url: normalizedRegistryUrl, - }); - } - - saveCustomSources(); - toast( - editingSource.value - ? tm("market.sourceUpdated") - : tm("market.sourceAdded"), - "success", - ); - - // 重置表单 - sourceName.value = ""; - sourceUrl.value = ""; - sourceResolvedUrl.value = ""; - sourceResolveVisible.value = false; - sourceMarketMeta.value = null; - sourceResolving.value = false; - editingSource.value = false; - originalSourceUrl.value = ""; - showSourceDialog.value = false; - }; - - // 插件市场显示完整插件名称 - const trimExtensionName = () => { - pluginMarketData.value.forEach((plugin) => { - if (plugin.name) { - let name = plugin.name.trim().toLowerCase(); - if (name.startsWith("astrbot_plugin_")) { - plugin.trimmedName = name.substring(15); - } else if (name.startsWith("astrbot_") || name.startsWith("astrbot-")) { - plugin.trimmedName = name.substring(8); - } else plugin.trimmedName = plugin.name; - } - }); - }; - - const checkAlreadyInstalled = () => { - const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - const currentRegistryUrl = normalizeRegistryUrl(selectedSource.value); - const installedByIdentifier = new Map(); - const installedByRepo = new Map(); - const installedByName = new Map(); - - data.forEach((ext) => { - const source = ext.install_source || {}; - const isCurrentMarketInstall = - source.install_method === "market" && - normalizeRegistryUrl(source.registry_url) === currentRegistryUrl; - const identifier = String(source.market_plugin_id || "").trim(); - if (isCurrentMarketInstall && identifier) { - installedByIdentifier.set(identifier, ext); - } - - const repo = normalizeInstallUrl(ext.repo || source.repo).toLowerCase(); - if (repo) { - installedByRepo.set(repo, ext); - } else { - installedByName.set( - normalizeStr(ext.marketplace_name || ext.name), - ext, - ); - } - }); - - for (let i = 0; i < pluginMarketData.value.length; i++) { - const plugin = pluginMarketData.value[i]; - const pluginIdentifier = getMarketPluginId(plugin); - const pluginRepo = normalizeInstallUrl(plugin.repo).toLowerCase(); - const matchedInstalled = - (pluginIdentifier && installedByIdentifier.get(pluginIdentifier)) || - (pluginRepo && installedByRepo.get(pluginRepo)) || - installedByName.get(normalizeStr(plugin.name)); - - // 兜底:市场源未提供字段时,回填本地已安装插件中的元数据,便于在市场页直接展示 - if (matchedInstalled) { - if ( - (!Array.isArray(plugin.support_platforms) || - plugin.support_platforms.length === 0) && - Array.isArray(matchedInstalled.support_platforms) - ) { - plugin.support_platforms = matchedInstalled.support_platforms; - } - if (!plugin.astrbot_version && matchedInstalled.astrbot_version) { - plugin.astrbot_version = matchedInstalled.astrbot_version; - } - } - - plugin.installed = !!matchedInstalled; - } - - let installed = []; - let notInstalled = []; - for (let i = 0; i < pluginMarketData.value.length; i++) { - if (pluginMarketData.value[i].installed) { - installed.push(pluginMarketData.value[i]); - } else { - notInstalled.push(pluginMarketData.value[i]); - } - } - pluginMarketData.value = notInstalled.concat(installed); - }; - - const normalizeAstrBotVersionSpec = (value) => String(value || "").trim(); - - const normalizeVersionParts = (value) => { - const version = String(value || "") - .trim() - .replace(/^v/i, "") - .split(/[+-]/)[0]; - const parts = version.split(".").map((part) => { - const match = part.match(/^\d+/); - return match ? Number.parseInt(match[0], 10) : 0; - }); - return parts.length ? parts : [0]; - }; - - const compareVersions = (left, right) => { - const leftParts = normalizeVersionParts(left); - const rightParts = normalizeVersionParts(right); - const length = Math.max(leftParts.length, rightParts.length, 3); - for (let i = 0; i < length; i += 1) { - const leftPart = leftParts[i] || 0; - const rightPart = rightParts[i] || 0; - if (leftPart > rightPart) return 1; - if (leftPart < rightPart) return -1; - } - return 0; - }; - - const getSupportedReleaseUpperBound = (version) => { - const parts = normalizeVersionParts(version); - if (parts.length <= 2) { - return `${(parts[0] || 0) + 1}.0`; - } - return `${parts[0] || 0}.${(parts[1] || 0) + 1}.0`; - }; - - const checkVersionConstraint = (currentVersion, constraint) => { - const match = constraint.match(/^(<=|>=|==|!=|~=|<|>|=)\s*(.+)$/); - if (!match) return null; - - const [, operator, targetVersion] = match; - const normalizedTarget = targetVersion.trim(); - if (!normalizedTarget) return null; - if (!/^v?\d+/.test(normalizedTarget)) return null; - - if (operator === "~=") { - return ( - compareVersions(currentVersion, normalizedTarget) >= 0 && - compareVersions( - currentVersion, - getSupportedReleaseUpperBound(normalizedTarget), - ) < 0 - ); - } - - const comparison = compareVersions(currentVersion, normalizedTarget); - if (operator === ">" || operator === ">=") { - return operator === ">" ? comparison > 0 : comparison >= 0; - } - if (operator === "<" || operator === "<=") { - return operator === "<" ? comparison < 0 : comparison <= 0; - } - if (operator === "!=") return comparison !== 0; - return comparison === 0; - }; - - const checkAstrBotVersionSupport = (versionSpec, currentVersion) => { - const normalizedSpec = normalizeAstrBotVersionSpec(versionSpec); - if (!normalizedSpec) { - return { checked: false, supported: true, message: "" }; - } - if (!currentVersion) { - return { checked: false, supported: true, message: "" }; - } - - const constraints = normalizedSpec - .split(",") - .map((item) => item.trim()) - .filter(Boolean); - if (!constraints.length) { - return { checked: false, supported: true, message: "" }; - } - - for (const constraint of constraints) { - const supported = checkVersionConstraint(currentVersion, constraint); - if (supported === null) { - return { - checked: true, - supported: false, - message: - "Invalid astrbot_version. Use a PEP 440 range, e.g. >=4.16,<5.", - }; - } - if (!supported) { - return { - checked: true, - supported: false, - message: `AstrBot ${currentVersion} does not satisfy plugin astrbot_version: ${normalizedSpec}`, - }; - } - } - - return { checked: true, supported: true, message: "" }; - }; - - const annotateMarketVersionSupport = async () => { - const currentVersion = - commonStore.astrbotVersion || - (await commonStore.fetchAstrBotVersion().catch(() => "")); - pluginMarketData.value.forEach((plugin) => { - const result = checkAstrBotVersionSupport( - plugin?.astrbot_version, - currentVersion, - ); - plugin.astrbot_support_checked = result.checked; - plugin.astrbot_version_supported = result.supported; - plugin.astrbot_support_message = result.message; - }); - }; - - const showVersionSupportWarning = (message) => { - versionSupportDialog.message = message; - versionSupportDialog.show = true; - }; - - const refreshExtensionsAfterInstallFailure = async () => { - try { - await getExtensions(); - } catch (error) { - console.debug( - "Failed to refresh extensions after install failure:", - error, - ); - } - }; - - const continueInstallIgnoringVersionWarning = async () => { - versionSupportDialog.show = false; - await newExtension(true); - }; - - const cancelInstallOnVersionWarning = () => { - versionSupportDialog.show = false; - }; - - const handleInstallResponse = async (resData) => { - if ( - resData.status === "warning" && - resData.data?.warning_type === "astrbot_version_unsupported" - ) { - toast(resData.message, "warning"); - showVersionSupportWarning(resData.message); - await refreshExtensionsAfterInstallFailure(); - return false; - } - - if (resData.status === "error") { - toast(resData.message, "error"); - await refreshExtensionsAfterInstallFailure(); - return false; - } - - return true; - }; - - const performInstallRequest = async ({ source, ignoreVersionCheck }) => { - const shouldIgnoreVersionCheck = ignoreVersionCheck === true; - if (source === "file") { - const formData = new FormData(); - formData.append("file", upload_file.value); - formData.append("ignore_version_check", String(shouldIgnoreVersionCheck)); - return pluginApi.installUpload(formData); - } - - const payload = { - url: extension_url.value, - download_url: selectedInstallDownloadUrl.value, - proxy: selectedInstallDownloadUrl.value ? "" : getSelectedGitHubProxy(), - ignore_version_check: shouldIgnoreVersionCheck, - ...getMarketInstallSourcePayload(), - }; - - return installUsesGithubSource.value - ? pluginApi.installGithub(payload) - : pluginApi.installUrl(payload); - }; - - const finalizeSuccessfulInstall = async (resData, source) => { - if (source === "file") { - upload_file.value = null; - } else { - extension_url.value = ""; - } - - toast(resData.message, "success"); - dialog.value = false; - selectedMarketInstallPlugin.value = null; - await getExtensions(); - checkAlreadyInstalled(); - await checkUpdate(); - - viewReadme({ - name: resData.data.name, - repo: resData.data.repo || null, - }); - - await checkAndPromptConflicts(); - }; - - const newExtension = async (ignoreVersionCheck = false) => { - const shouldIgnoreVersionCheck = ignoreVersionCheck === true; - if (extension_url.value === "" && upload_file.value === null) { - toast(tm("messages.fillUrlOrFile"), "error"); - return; - } - - if (extension_url.value !== "" && upload_file.value !== null) { - toast(tm("messages.dontFillBoth"), "error"); - return; - } - const source = upload_file.value !== null ? "file" : "url"; - loading_.value = true; - - try { - if (source === "url" && !selectedInstallDownloadUrl.value) { - if (!installUsesGithubSource.value) { - toast(tm("messages.invalidGithubRepo"), "error"); - loading_.value = false; - return; - } - - const validationPayload = buildInstallRepoValidationPayload(); - if (!validationPayload) { - toast(tm("messages.fillUrlOrFile"), "error"); - loading_.value = false; - return; - } - installUrlValidation.validating = true; - installUrlValidation.status = "loading"; - installUrlValidation.message = tm("messages.validatingPlugin"); - installUrlValidation.version = ""; - installUrlValidation.metadata = null; - try { - const { data, message } = await validatePluginRepo(validationPayload); - installUrlValidation.status = "valid"; - installUrlValidation.message = message; - installUrlValidation.version = data.version || ""; - installUrlValidation.metadata = data; - } catch (error) { - const message = resolveErrorMessage( - error, - tm("messages.pluginValidateFailed"), - ); - installUrlValidation.status = "error"; - installUrlValidation.message = message; - toast(message, "error"); - loading_.value = false; - return; - } finally { - installUrlValidation.validating = false; - } - } - - const res = await performInstallRequest({ - source, - ignoreVersionCheck: shouldIgnoreVersionCheck, - }); - loading_.value = false; - - const canContinue = await handleInstallResponse(res.data); - if (!canContinue) return; - - await finalizeSuccessfulInstall(res.data, source); - } catch (err) { - loading_.value = false; - const message = resolveErrorMessage(err, tm("messages.installFailed")); - toast(message, "error"); - await refreshExtensionsAfterInstallFailure(); - } - }; - - const normalizePlatformList = (platforms) => { - if (!Array.isArray(platforms)) return []; - return platforms.filter((item) => typeof item === "string"); - }; - - const getPlatformDisplayList = (platforms) => { - return normalizePlatformList(platforms).map((platformId) => - getPlatformDisplayName(platformId), - ); - }; - - const resolveSelectedInstallPlugin = () => { - if ( - selectedMarketInstallPlugin.value && - selectedMarketInstallPlugin.value.repo === extension_url.value - ) { - return selectedMarketInstallPlugin.value; - } - return ( - pluginMarketData.value.find( - (plugin) => plugin.repo === extension_url.value, - ) || null - ); - }; - - const selectedInstallPlugin = computed(() => resolveSelectedInstallPlugin()); - - const selectedUpdateExtension = computed(() => - getInstalledExtensionByName(updateConfirmDialog.extensionName), - ); - - const selectedUpdateMarketPlugin = computed(() => - findMarketPluginForExtension(selectedUpdateExtension.value), - ); - - const selectedSourceBindingCandidate = computed( - () => - sourceBindingDialog.candidates.find( - (item) => item.key === sourceBindingDialog.selectedKey, - ) || null, - ); - - const selectedUpdateDownloadUrl = computed(() => - String(selectedUpdateMarketPlugin.value?.download_url || "").trim(), - ); - - const selectedUpdateSourceUrl = computed( - () => - selectedUpdateDownloadUrl.value || - String(selectedUpdateExtension.value?.repo || "").trim(), - ); - - const updateUsesGithubSource = computed( - () => - !selectedUpdateDownloadUrl.value && - isGithubRepoUrl(selectedUpdateSourceUrl.value), - ); - - const checkInstallVersionSupport = async () => { - installSupport.checked = false; - installSupport.supported = true; - installSupport.message = ""; - - const plugin = selectedInstallPlugin.value; - if (!plugin?.astrbot_version || uploadTab.value !== "url") { - return; - } - - const currentVersion = - commonStore.astrbotVersion || - (await commonStore.fetchAstrBotVersion().catch(() => "")); - const result = checkAstrBotVersionSupport( - plugin.astrbot_version, - currentVersion, - ); - installSupport.checked = result.checked; - installSupport.supported = result.supported; - installSupport.message = result.message; - }; - - // 刷新插件市场数据 - const refreshPluginMarket = async () => { - refreshingMarket.value = true; - loading_.value = true; - try { - // 强制刷新插件市场数据 - const data = await commonStore.getPluginCollections( - true, - selectedSource.value, - ); - pluginMarketData.value = data; - trimExtensionName(); - checkAlreadyInstalled(); - await annotateMarketVersionSupport(); - await checkUpdate(); - refreshRandomPlugins(); - currentPage.value = 1; // 重置到第一页 - - toast(tm("messages.refreshSuccess"), "success"); - } catch (err) { - toast(tm("messages.refreshFailed") + " " + err, "error"); - } finally { - refreshingMarket.value = false; - loading_.value = false; - } - }; - - // 生命周期 - onMounted(async () => { - if (!syncTabFromHash(getLocationHash())) { - await replaceTabRoute(router, route, activeTab.value); - } - loading_.value = true; - try { - await getExtensions({ withLoading: false }); - - // 加载自定义插件源 - await loadCustomSources(); - - // 检查是否有 open_config 参数 - const plugin_name = Array.isArray(route.query.open_config) - ? route.query.open_config[0] - : route.query.open_config; - if (plugin_name) { - console.log(`Opening config for plugin: ${plugin_name}`); - openExtensionConfig(plugin_name); - } - - const data = await commonStore.getPluginCollections( - false, - selectedSource.value, - ); - pluginMarketData.value = data; - trimExtensionName(); - checkAlreadyInstalled(); - await annotateMarketVersionSupport(); - await checkUpdate(); - refreshRandomPlugins(); - } catch (err) { - toast(tm("messages.getMarketDataFailed") + " " + err, "error"); - } finally { - loading_.value = false; - } - }); - - // 处理语言切换事件,重新加载插件配置以获取插件的 i18n 数据 - const handleLocaleChange = () => { - // 如果配置对话框是打开的,重新加载当前插件的配置 - if (configDialog.value && currentConfigPlugin.value) { - openExtensionConfig(currentConfigPlugin.value); - } - }; - - // 监听语言切换事件 - window.addEventListener("astrbot-locale-changed", handleLocaleChange); - - // 清理事件监听器 - onUnmounted(() => { - window.removeEventListener("astrbot-locale-changed", handleLocaleChange); - }); - - // 搜索防抖处理 - let searchDebounceTimer = null; - watch(marketSearch, (newVal) => { - if (searchDebounceTimer) { - clearTimeout(searchDebounceTimer); - } - - searchDebounceTimer = setTimeout(() => { - debouncedMarketSearch.value = newVal; - // 搜索时重置到第一页 - currentPage.value = 1; - }, 300); // 300ms 防抖延迟 - }); - - watch( - [() => dialog.value, () => extension_url.value, () => uploadTab.value], - async ([dialogOpen, _, currentUploadTab]) => { - resetInstallUrlValidation(); - if (!dialogOpen || currentUploadTab !== "url") { - installSupport.checked = false; - installSupport.supported = true; - installSupport.message = ""; - if (!dialogOpen) { - selectedMarketInstallPlugin.value = null; - } - return; - } - await checkInstallVersionSupport(); - }, - ); - - watch( - [() => sourceBindingDialog.show, () => sourceBindingDialog.selectedKey], - ([dialogOpen]) => { - if (!dialogOpen || sourceBindingDialog.loading) { - return; - } - const candidate = sourceBindingDialog.candidates.find( - (item) => item.key === sourceBindingDialog.selectedKey, - ); - if ( - candidate?.install_method === "github" && - candidate.validation_status !== "valid" - ) { - void validateSourceBindingRepoCandidate(candidate); - } - }, - ); - - watch( - () => route.hash, - (newHash) => { - const tab = extractTabFromHash(newHash); - if (tab && tab !== activeTab.value) { - activeTab.value = tab; - } - }, - ); - - watch(activeTab, (newTab) => { - if (!isValidTab(newTab)) return; - if (route.hash === `#${newTab}`) return; - void replaceTabRoute(router, route, newTab); - }); - - watch(marketCategoryFilter, () => { - if (activeTab.value === "market") { - currentPage.value = 1; - } - }); - - watch( - marketCategoryItems, - (newItems) => { - const validValues = new Set(newItems.map((item) => item.value)); - if (!validValues.has(marketCategoryFilter.value)) { - marketCategoryFilter.value = "all"; - } - }, - { immediate: true }, - ); - - return { - commonStore, - t, - tm, - router, - route, - getSelectedGitHubProxy, - conflictDialog, - checkAndPromptConflicts, - handleConflictConfirm, - fileInput, - activeTab, - validTabs, - isValidTab, - getLocationHash, - extractTabFromHash, - syncTabFromHash, - extension_data, - snack_message, - snack_show, - snack_success, - configDialog, - extension_config, - pluginMarketData, - loadingDialog, - curr_namespace, - updatingAll, - readmeDialog, - forceUpdateDialog, - updateConfirmDialog, - updateAllConfirmDialog, - changelogDialog, - pluginSearch, - loading_, - currentPage, - marketCategoryFilter, - marketCategoryItems, - marketCategoryCounts, - dangerConfirmDialog, - selectedDangerPlugin, - selectedMarketInstallPlugin, - installSupport, - installUrlValidation, - versionSupportDialog, - showUninstallDialog, - uninstallTarget, - showSourceDialog, - showSourceManagerDialog, - sourceName, - sourceUrl, - sourceResolving, - sourceResolveVisible, - sourceMarketMeta, - sourceResolveCurrent, - customSources, - selectedSource, - showRemoveSourceDialog, - sourceToRemove, - editingSource, - originalSourceUrl, - sourceBindingDialog, - selectedSourceBindingCandidate, - extension_url, - dialog, - upload_file, - uploadTab, - showPluginFullName, - marketSearch, - debouncedMarketSearch, - refreshingMarket, - sortBy, - sortOrder, - randomPluginNames, - normalizeStr, - toPinyinText, - toInitials, - filteredExtensions, - filteredPlugins, - filteredMarketPlugins, - sortedPlugins, - RANDOM_PLUGINS_COUNT, - randomPlugins, - shufflePlugins, - refreshRandomPlugins, - displayItemsPerPage, - totalPages, - paginatedPlugins, - updatableExtensions, - toast, - resetLoadingDialog, - onLoadingDialogResult, - failedPluginsDict, - failedPluginItems, - getExtensions, - handleReloadAllFailed, - reloadFailedPlugin, - checkUpdate, - uninstallExtension, - requestUninstallPlugin, - requestUninstallFailedPlugin, - handleUninstallConfirm, - updateExtension, - closeUpdateConfirmDialog, - confirmUpdatePlugin, - showUpdateAllConfirm, - confirmUpdateAll, - cancelUpdateAll, - confirmForceUpdate, - updateAllExtensions, - pluginOn, - pluginOff, - openExtensionConfig, - updateConfig, - showPluginInfo, - reloadPlugin, - viewReadme, - viewChangelog, - openInstallDialog, - closeInstallDialog, - handleInstallPlugin, - confirmDangerInstall, - cancelDangerInstall, - loadCustomSources, - saveCustomSources, - addCustomSource, - openSourceManagerDialog, - selectPluginSource, - editCustomSource, - removeCustomSource, - confirmRemoveSource, - resolveCustomSource, - saveCustomSource, - openPluginSourceBindingDialog, - closePluginSourceBindingDialog, - confirmPluginSourceBinding, - trimExtensionName, - checkAlreadyInstalled, - showVersionSupportWarning, - continueInstallIgnoringVersionWarning, - cancelInstallOnVersionWarning, - newExtension, - normalizePlatformList, - getPlatformDisplayList, - resolveSelectedInstallPlugin, - selectedInstallPlugin, - selectedInstallDownloadUrl, - selectedInstallSourceUrl, - installUsesGithubSource, - selectedUpdateExtension, - selectedUpdateMarketPlugin, - selectedUpdateDownloadUrl, - selectedUpdateSourceUrl, - updateUsesGithubSource, - checkInstallVersionSupport, - refreshPluginMarket, - handleLocaleChange, - searchDebounceTimer, - }; -}; diff --git a/legacy-dashboard/src/views/knowledge-base/DocumentDetail.vue b/legacy-dashboard/src/views/knowledge-base/DocumentDetail.vue deleted file mode 100644 index 23bb1a6d..00000000 --- a/legacy-dashboard/src/views/knowledge-base/DocumentDetail.vue +++ /dev/null @@ -1,505 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/knowledge-base/KBDetail.vue b/legacy-dashboard/src/views/knowledge-base/KBDetail.vue deleted file mode 100644 index 13797939..00000000 --- a/legacy-dashboard/src/views/knowledge-base/KBDetail.vue +++ /dev/null @@ -1,284 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/knowledge-base/KBList.vue b/legacy-dashboard/src/views/knowledge-base/KBList.vue deleted file mode 100644 index d9fd6772..00000000 --- a/legacy-dashboard/src/views/knowledge-base/KBList.vue +++ /dev/null @@ -1,678 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/knowledge-base/components/DocumentsTab.vue b/legacy-dashboard/src/views/knowledge-base/components/DocumentsTab.vue deleted file mode 100644 index 3344d6da..00000000 --- a/legacy-dashboard/src/views/knowledge-base/components/DocumentsTab.vue +++ /dev/null @@ -1,892 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/knowledge-base/components/RetrievalTab.vue b/legacy-dashboard/src/views/knowledge-base/components/RetrievalTab.vue deleted file mode 100644 index 28913266..00000000 --- a/legacy-dashboard/src/views/knowledge-base/components/RetrievalTab.vue +++ /dev/null @@ -1,243 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/knowledge-base/components/SettingsTab.vue b/legacy-dashboard/src/views/knowledge-base/components/SettingsTab.vue deleted file mode 100644 index 0ffb48aa..00000000 --- a/legacy-dashboard/src/views/knowledge-base/components/SettingsTab.vue +++ /dev/null @@ -1,310 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/knowledge-base/components/TavilyKeyDialog.vue b/legacy-dashboard/src/views/knowledge-base/components/TavilyKeyDialog.vue deleted file mode 100644 index 56c7c70c..00000000 --- a/legacy-dashboard/src/views/knowledge-base/components/TavilyKeyDialog.vue +++ /dev/null @@ -1,104 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/knowledge-base/index.vue b/legacy-dashboard/src/views/knowledge-base/index.vue deleted file mode 100644 index 13df70e6..00000000 --- a/legacy-dashboard/src/views/knowledge-base/index.vue +++ /dev/null @@ -1,111 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/CreateFolderDialog.vue b/legacy-dashboard/src/views/persona/CreateFolderDialog.vue deleted file mode 100644 index 3106d73c..00000000 --- a/legacy-dashboard/src/views/persona/CreateFolderDialog.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/persona/FolderBreadcrumb.vue b/legacy-dashboard/src/views/persona/FolderBreadcrumb.vue deleted file mode 100644 index cb88193e..00000000 --- a/legacy-dashboard/src/views/persona/FolderBreadcrumb.vue +++ /dev/null @@ -1,63 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/FolderCard.vue b/legacy-dashboard/src/views/persona/FolderCard.vue deleted file mode 100644 index 7e5695c5..00000000 --- a/legacy-dashboard/src/views/persona/FolderCard.vue +++ /dev/null @@ -1,73 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/FolderTree.vue b/legacy-dashboard/src/views/persona/FolderTree.vue deleted file mode 100644 index 88f1a95a..00000000 --- a/legacy-dashboard/src/views/persona/FolderTree.vue +++ /dev/null @@ -1,246 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/FolderTreeNode.vue b/legacy-dashboard/src/views/persona/FolderTreeNode.vue deleted file mode 100644 index c6a511fd..00000000 --- a/legacy-dashboard/src/views/persona/FolderTreeNode.vue +++ /dev/null @@ -1,66 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/persona/MoveTargetNode.vue b/legacy-dashboard/src/views/persona/MoveTargetNode.vue deleted file mode 100644 index 90e1113f..00000000 --- a/legacy-dashboard/src/views/persona/MoveTargetNode.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/legacy-dashboard/src/views/persona/MoveToFolderDialog.vue b/legacy-dashboard/src/views/persona/MoveToFolderDialog.vue deleted file mode 100644 index 62f890c5..00000000 --- a/legacy-dashboard/src/views/persona/MoveToFolderDialog.vue +++ /dev/null @@ -1,201 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/PersonaCard.vue b/legacy-dashboard/src/views/persona/PersonaCard.vue deleted file mode 100644 index 7cebb26a..00000000 --- a/legacy-dashboard/src/views/persona/PersonaCard.vue +++ /dev/null @@ -1,190 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/PersonaManager.vue b/legacy-dashboard/src/views/persona/PersonaManager.vue deleted file mode 100644 index a241dbe1..00000000 --- a/legacy-dashboard/src/views/persona/PersonaManager.vue +++ /dev/null @@ -1,616 +0,0 @@ - - - - - diff --git a/legacy-dashboard/src/views/persona/index.ts b/legacy-dashboard/src/views/persona/index.ts deleted file mode 100644 index 322155b9..00000000 --- a/legacy-dashboard/src/views/persona/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Persona 管理相关组件 - * - * 这些组件使用了 dashboard/src/components/folder 下的通用文件夹组件 - * 通过包装器模式将 personaStore 的状态和方法连接到通用组件 - */ - -// 主组件 -export { default as PersonaManager } from './PersonaManager.vue'; - -// 文件夹相关组件 -export { default as FolderTree } from './FolderTree.vue'; -export { default as FolderTreeNode } from './FolderTreeNode.vue'; -export { default as FolderBreadcrumb } from './FolderBreadcrumb.vue'; -export { default as FolderCard } from './FolderCard.vue'; - -// 对话框组件 -export { default as CreateFolderDialog } from './CreateFolderDialog.vue'; -export { default as MoveToFolderDialog } from './MoveToFolderDialog.vue'; -export { default as MoveTargetNode } from './MoveTargetNode.vue'; - -// Persona 相关组件 -export { default as PersonaCard } from './PersonaCard.vue'; diff --git a/legacy-dashboard/src/views/stats/StatsPage.vue b/legacy-dashboard/src/views/stats/StatsPage.vue deleted file mode 100644 index 71133c0e..00000000 --- a/legacy-dashboard/src/views/stats/StatsPage.vue +++ /dev/null @@ -1,1168 +0,0 @@ - - - - - diff --git a/legacy-dashboard/tests/desktopBackendReadiness.test.mjs b/legacy-dashboard/tests/desktopBackendReadiness.test.mjs deleted file mode 100644 index 379953a0..00000000 --- a/legacy-dashboard/tests/desktopBackendReadiness.test.mjs +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'node:assert/strict'; -import test from 'node:test'; - -import { waitForDesktopBackendReady } from '../src/utils/desktopBackendReadiness.mjs'; - -test('returns immediately outside the desktop runtime', async () => { - assert.equal(await waitForDesktopBackendReady({ bridge: undefined }), false); -}); - -test('waits until the desktop backend reports running', async () => { - let checks = 0; - let clock = 0; - const ready = await waitForDesktopBackendReady({ - bridge: { - isDesktop: true, - async getBackendState() { - checks += 1; - return { running: checks >= 3 }; - }, - }, - timeoutMs: 1000, - pollIntervalMs: 10, - now: () => clock, - sleep: async (milliseconds) => { - clock += milliseconds; - }, - }); - - assert.equal(ready, true); - assert.equal(checks, 3); -}); diff --git a/legacy-dashboard/tests/extensionPreferenceStorage.test.mjs b/legacy-dashboard/tests/extensionPreferenceStorage.test.mjs deleted file mode 100644 index 9062d202..00000000 --- a/legacy-dashboard/tests/extensionPreferenceStorage.test.mjs +++ /dev/null @@ -1,54 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; - -import { - PINNED_EXTENSIONS_STORAGE_KEY, - readPinnedExtensions, - writePinnedExtensions, -} from '../src/views/extension/extensionPreferenceStorage.mjs'; - -test('readPinnedExtensions uses the legacy pinned extension storage key', () => { - assert.equal(PINNED_EXTENSIONS_STORAGE_KEY, 'astrbot.pinnedExtensions'); -}); - -test('readPinnedExtensions parses stored pinned extension names', () => { - const storage = { - getItem(key) { - return key === PINNED_EXTENSIONS_STORAGE_KEY - ? JSON.stringify(['alpha', 'beta', 'alpha', '', 1]) - : null; - }, - }; - - assert.deepEqual(readPinnedExtensions(storage), ['alpha', 'beta']); -}); - -test('readPinnedExtensions returns an empty array when storage access fails', () => { - const storage = { - getItem() { - throw new Error('SecurityError'); - }, - }; - - assert.deepEqual(readPinnedExtensions(storage), []); -}); - -test('writePinnedExtensions stores normalized pinned extension names', () => { - const writes = []; - const storage = { - setItem(key, value) { - writes.push([key, value]); - }, - }; - - writePinnedExtensions(['alpha', 'beta', 'alpha', '', null], storage); - - assert.deepEqual(writes, [ - [PINNED_EXTENSIONS_STORAGE_KEY, JSON.stringify(['alpha', 'beta'])], - ]); -}); - -test('writePinnedExtensions ignores unavailable storage', () => { - assert.doesNotThrow(() => writePinnedExtensions(['alpha'], null)); - assert.doesNotThrow(() => writePinnedExtensions(['alpha'], {})); -}); diff --git a/legacy-dashboard/tests/hashRouteTabs.test.mjs b/legacy-dashboard/tests/hashRouteTabs.test.mjs deleted file mode 100644 index 2aefd679..00000000 --- a/legacy-dashboard/tests/hashRouteTabs.test.mjs +++ /dev/null @@ -1,123 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; - -import * as hashRouteTabs from '../src/utils/hashRouteTabs.mjs'; -import { EXTENSION_ROUTE_NAME } from '../src/router/routeConstants.mjs'; - -const { createTabRouteLocation, getValidHashTab } = hashRouteTabs; - -test('getValidHashTab returns the tab name for a valid route hash', () => { - const validTabs = ['installed', 'market', 'mcp']; - - assert.equal(getValidHashTab('#market', validTabs), 'market'); -}); - -test('getValidHashTab rejects empty and unknown hashes', () => { - const validTabs = ['installed', 'market', 'mcp']; - - assert.equal(getValidHashTab('', validTabs), null); - assert.equal(getValidHashTab('#unknown', validTabs), null); -}); - -test('getValidHashTab uses the last hash segment when multiple hashes are present', () => { - const validTabs = ['installed', 'market', 'mcp']; - - assert.equal(getValidHashTab('#/extension#foo#installed', validTabs), 'installed'); -}); - -test('createTabRouteLocation preserves the current path and query', () => { - const query = { open_config: 'sample-plugin', page: '2' }; - const location = createTabRouteLocation( - { - path: '/extension', - query, - }, - 'market', - ); - - assert.deepEqual(location, { - path: '/extension', - query: { open_config: 'sample-plugin', page: '2' }, - hash: '#market', - }); - assert.notEqual(location.query, query); -}); - -test('createTabRouteLocation falls back to the extension route name', () => { - const location = createTabRouteLocation(undefined, 'installed'); - - assert.deepEqual(location, { - name: EXTENSION_ROUTE_NAME, - query: {}, - hash: '#installed', - }); -}); - -test('createTabRouteLocation prefers route name and preserves params', () => { - const params = { pluginId: 'demo-plugin' }; - const location = createTabRouteLocation( - { - name: 'ExtensionDetails', - path: '/extension/demo-plugin', - params, - query: { tab: 'details' }, - }, - 'market', - ); - - assert.deepEqual(location, { - name: 'ExtensionDetails', - params: { pluginId: 'demo-plugin' }, - query: { tab: 'details' }, - hash: '#market', - }); - assert.notEqual(location.params, params); -}); - -test('createTabRouteLocation omits params for path-based routes', () => { - const params = { pluginId: 'demo-plugin' }; - const location = createTabRouteLocation( - { - path: '/extension/demo-plugin', - params, - }, - 'installed', - ); - - assert.deepEqual(location, { - path: '/extension/demo-plugin', - query: {}, - hash: '#installed', - }); - assert.equal(location.params, undefined); -}); - -test('replaceTabRoute catches rejected router updates', async () => { - assert.equal(typeof hashRouteTabs.replaceTabRoute, 'function'); - - const error = new Error('blocked'); - let logged; - const router = { - replace: async () => { - throw error; - }, - }; - const logger = { - warn: (message, cause) => { - logged = { message, cause }; - }, - }; - - const result = await hashRouteTabs.replaceTabRoute( - router, - { name: EXTENSION_ROUTE_NAME, query: { page: '1' } }, - 'installed', - logger, - ); - - assert.equal(result, false); - assert.deepEqual(logged, { - message: 'Failed to update extension tab route:', - cause: error, - }); -}); diff --git a/legacy-dashboard/tests/imeInput.test.mjs b/legacy-dashboard/tests/imeInput.test.mjs deleted file mode 100644 index 1c5fea47..00000000 --- a/legacy-dashboard/tests/imeInput.test.mjs +++ /dev/null @@ -1,36 +0,0 @@ -import assert from "node:assert/strict"; -import test from "node:test"; - -import { isComposingEnter } from "../src/utils/imeInput.mjs"; - -test("detects Enter while an IME composition is active", () => { - assert.equal(isComposingEnter({ key: "Enter", isComposing: true }, false), true); - assert.equal(isComposingEnter({ key: "Enter", isComposing: false }, true), true); -}); - -test("does not treat normal Enter as IME composition", () => { - assert.equal(isComposingEnter({ key: "Enter", isComposing: false }, false), false); - assert.equal(isComposingEnter({ key: "a", isComposing: true }, true), false); -}); - -test("detects Enter fired immediately after composition ended", () => { - assert.equal( - isComposingEnter( - { key: "Enter", isComposing: false, timeStamp: 105 }, - false, - 100, - ), - true, - ); -}); - -test("does not treat delayed Enter after composition ended as IME composition", () => { - assert.equal( - isComposingEnter( - { key: "Enter", isComposing: false, timeStamp: 250 }, - false, - 100, - ), - false, - ); -}); diff --git a/legacy-dashboard/tests/routerReadiness.test.mjs b/legacy-dashboard/tests/routerReadiness.test.mjs deleted file mode 100644 index 171b271b..00000000 --- a/legacy-dashboard/tests/routerReadiness.test.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; - -test('waitForRouterReadyInBackground returns immediately and logs failures', async () => { - const module = await import('../src/utils/routerReadiness.mjs').catch(() => null); - - assert.ok(module?.waitForRouterReadyInBackground); - - const error = new Error('router blocked'); - let warned; - const readyPromise = Promise.reject(error); - const logger = { - warn: (message, cause) => { - warned = { message, cause }; - }, - }; - - const result = module.waitForRouterReadyInBackground( - { isReady: () => readyPromise }, - logger, - ); - - assert.equal(result, undefined); - await Promise.resolve(); - assert.deepEqual(warned, { - message: 'Router did not become ready after fallback mount:', - cause: error, - }); -}); diff --git a/legacy-dashboard/tests/subsetMdiFont.test.mjs b/legacy-dashboard/tests/subsetMdiFont.test.mjs deleted file mode 100644 index 8bfdbb01..00000000 --- a/legacy-dashboard/tests/subsetMdiFont.test.mjs +++ /dev/null @@ -1,273 +0,0 @@ -import test from 'node:test'; -import assert from 'node:assert/strict'; -import { mkdirSync, writeFileSync, rmSync } from 'fs'; -import { join } from 'path'; -import { tmpdir } from 'os'; - -import { - collectFiles, - scanUsedIcons, - parseIconCodepoints, - resolveUsedIcons, - extractUtilityCss, - ICON_CLASS_PATTERN, - REQUIRED_ICONS, -} from '../scripts/subset-mdi-font.mjs'; - -// ── Helper: create a temporary directory tree for file-system tests ───────── - -function makeTmpDir() { - const base = join(tmpdir(), `mdi-test-${Date.now()}-${Math.random().toString(36).slice(2)}`); - mkdirSync(base, { recursive: true }); - return base; -} - -// ── collectFiles ──────────────────────────────────────────────────────────── - -test('collectFiles yields files matching given extensions', () => { - const tmp = makeTmpDir(); - writeFileSync(join(tmp, 'a.vue'), ''); - writeFileSync(join(tmp, 'b.ts'), ''); - writeFileSync(join(tmp, 'c.txt'), ''); - - const files = [...collectFiles(tmp, ['.vue', '.ts'])]; - assert.equal(files.length, 2); - assert.ok(files.some(f => f.endsWith('a.vue'))); - assert.ok(files.some(f => f.endsWith('b.ts'))); - - rmSync(tmp, { recursive: true }); -}); - -test('collectFiles recurses into subdirectories', () => { - const tmp = makeTmpDir(); - const sub = join(tmp, 'sub'); - mkdirSync(sub); - writeFileSync(join(sub, 'deep.vue'), ''); - - const files = [...collectFiles(tmp, ['.vue'])]; - assert.equal(files.length, 1); - assert.ok(files[0].endsWith('deep.vue')); - - rmSync(tmp, { recursive: true }); -}); - -test('collectFiles skips node_modules directories', () => { - const tmp = makeTmpDir(); - const nm = join(tmp, 'node_modules'); - mkdirSync(nm); - writeFileSync(join(nm, 'pkg.vue'), ''); - writeFileSync(join(tmp, 'app.vue'), ''); - - const files = [...collectFiles(tmp, ['.vue'])]; - assert.equal(files.length, 1); - assert.ok(files[0].endsWith('app.vue')); - - rmSync(tmp, { recursive: true }); -}); - -test('collectFiles yields nothing for empty directory', () => { - const tmp = makeTmpDir(); - const files = [...collectFiles(tmp, ['.vue'])]; - assert.equal(files.length, 0); - - rmSync(tmp, { recursive: true }); -}); - -// ── scanUsedIcons ─────────────────────────────────────────────────────────── - -test('scanUsedIcons extracts mdi-* icon names from files', () => { - const tmp = makeTmpDir(); - writeFileSync(join(tmp, 'A.vue'), 'mdi-homemdi-close'); - writeFileSync(join(tmp, 'B.vue'), 'icon="mdi-home"'); - - const icons = scanUsedIcons(collectFiles(tmp, ['.vue'])); - assert.ok(icons instanceof Set); - assert.ok(icons.has('mdi-home')); - assert.ok(icons.has('mdi-close')); - for (const requiredIcon of REQUIRED_ICONS) { - assert.ok(icons.has(requiredIcon)); - } - const expectedIcons = new Set([...REQUIRED_ICONS, 'mdi-home', 'mdi-close']); - assert.deepEqual(icons, expectedIcons); - - rmSync(tmp, { recursive: true }); -}); - -test('scanUsedIcons excludes utility classes', () => { - const tmp = makeTmpDir(); - writeFileSync(join(tmp, 'A.vue'), 'mdi-spin mdi-rotate-90 mdi-flip-h mdi-home'); - - const icons = scanUsedIcons(collectFiles(tmp, ['.vue'])); - assert.ok(icons.has('mdi-home')); - assert.ok(!icons.has('mdi-spin')); - assert.ok(!icons.has('mdi-rotate-90')); - assert.ok(!icons.has('mdi-flip-h')); - - rmSync(tmp, { recursive: true }); -}); - -test('scanUsedIcons includes all required icons even when no mdi-* icons are found in source', () => { - const tmp = makeTmpDir(); - writeFileSync(join(tmp, 'A.vue'), '
Hello
'); - - const icons = scanUsedIcons(collectFiles(tmp, ['.vue'])); - for (const requiredIcon of REQUIRED_ICONS) { - assert.ok(icons.has(requiredIcon)); - } - assert.equal(icons.size, REQUIRED_ICONS.size); - - rmSync(tmp, { recursive: true }); -}); - -test('scanUsedIcons deduplicates required icons when source already references them', () => { - const tmp = makeTmpDir(); - const requiredIcon = [...REQUIRED_ICONS][0]; - writeFileSync(join(tmp, 'A.vue'), `${requiredIcon}mdi-home`); - - const icons = [...scanUsedIcons(collectFiles(tmp, ['.vue']))]; - assert.equal(icons.filter(icon => icon === requiredIcon).length, 1); - for (const builtInRequiredIcon of REQUIRED_ICONS) { - assert.ok(icons.includes(builtInRequiredIcon)); - } - assert.ok(icons.includes('mdi-home')); - - rmSync(tmp, { recursive: true }); -}); - -// ── parseIconCodepoints ───────────────────────────────────────────────────── - -test('parseIconCodepoints parses icon definitions from CSS', () => { - const css = ` -.mdi-home::before { content: "\\F02DC"; } -.mdi-close::before { content: "\\F0156"; } -`; - const map = parseIconCodepoints(css); - assert.equal(map.size, 2); - assert.equal(map.get('mdi-home'), 'F02DC'); - assert.equal(map.get('mdi-close'), 'F0156'); -}); - -test('parseIconCodepoints handles CSS with semicolons inside braces', () => { - const css = `.mdi-check::before { content: "\\F012C"; }`; - const map = parseIconCodepoints(css); - assert.equal(map.get('mdi-check'), 'F012C'); -}); - -test('parseIconCodepoints returns empty map for non-matching CSS', () => { - const css = `.some-other-class { color: red; }`; - const map = parseIconCodepoints(css); - assert.equal(map.size, 0); -}); - -// ── resolveUsedIcons ──────────────────────────────────────────────────────── - -test('resolveUsedIcons separates resolved and missing icons', () => { - const usedIcons = new Set(['mdi-home', 'mdi-close', 'mdi-nonexistent']); - const iconMap = new Map([ - ['mdi-home', 'F02DC'], - ['mdi-close', 'F0156'], - ]); - - const { resolvedIcons, missingIcons, subsetChars } = resolveUsedIcons(usedIcons, iconMap); - - assert.ok(resolvedIcons.includes('mdi-home')); - assert.ok(resolvedIcons.includes('mdi-close')); - assert.equal(resolvedIcons.length, 2); - - assert.deepEqual(missingIcons, ['mdi-nonexistent']); - - // Verify subsetChars contains correct Unicode characters - assert.equal(subsetChars.length, 2); - assert.equal(subsetChars[0], String.fromCodePoint(0xF02DC)); - assert.equal(subsetChars[1], String.fromCodePoint(0xF0156)); -}); - -test('resolveUsedIcons returns all missing when iconMap is empty', () => { - const usedIcons = new Set(['mdi-home']); - const iconMap = new Map(); - - const { resolvedIcons, missingIcons, subsetChars } = resolveUsedIcons(usedIcons, iconMap); - assert.equal(resolvedIcons.length, 0); - assert.deepEqual(missingIcons, ['mdi-home']); - assert.equal(subsetChars.length, 0); -}); - -// ── extractUtilityCss ─────────────────────────────────────────────────────── - -test('extractUtilityCss removes icon definitions and keeps utility rules', () => { - const css = ` -@font-face { - font-family: "Material Design Icons"; - src: url("../fonts/materialdesignicons-webfont.woff2") format("woff2"); -} - -.mdi:before, -.mdi-set { - display: inline-block; - font: normal normal normal 24px/1 "Material Design Icons"; -} - -.mdi-home::before { content: "\\F02DC"; } -.mdi-close::before { content: "\\F0156"; } - -.mdi-spin:before { - animation: mdi-spin 2s infinite linear; -} - -.mdi-18px.mdi-set, .mdi-18px.mdi:before { - font-size: 18px; -} -/*# sourceMappingURL=materialdesignicons.css.map */ -`; - - const result = extractUtilityCss(css, ICON_CLASS_PATTERN); - - // Should NOT contain icon definitions - assert.ok(!result.includes('mdi-home')); - assert.ok(!result.includes('mdi-close')); - - // Should NOT contain @font-face - assert.ok(!result.includes('@font-face')); - - // Should NOT contain base .mdi rules - assert.ok(!result.includes('display: inline-block')); - - // Should NOT contain source map - assert.ok(!result.includes('sourceMappingURL')); - - // SHOULD contain utility classes - assert.ok(result.includes('mdi-spin')); - assert.ok(result.includes('mdi-18px')); -}); - -test('extractUtilityCss returns empty string when only icon defs exist', () => { - const css = ` -@font-face { font-family: "MDI"; src: url("font.woff2"); } -.mdi:before, .mdi-set { display: inline-block; } -.mdi-home::before { content: "\\F02DC"; } -`; - - const result = extractUtilityCss(css, ICON_CLASS_PATTERN); - assert.equal(result, ''); -}); - -test('extractUtilityCss handles empty CSS input', () => { - const result = extractUtilityCss('', ICON_CLASS_PATTERN); - assert.equal(result, ''); -}); - -// ── ICON_CLASS_PATTERN ────────────────────────────────────────────────────── - -test('ICON_CLASS_PATTERN matches standard MDI icon definitions', () => { - const css = `.mdi-home::before { content: "\\F02DC"; }`; - const matches = [...css.matchAll(ICON_CLASS_PATTERN)]; - assert.equal(matches.length, 1); - assert.equal(matches[0][1], 'mdi-home'); - assert.equal(matches[0][2], 'F02DC'); -}); - -test('ICON_CLASS_PATTERN does not match non-icon classes', () => { - const css = `.some-class::before { content: "hello"; }`; - const matches = [...css.matchAll(ICON_CLASS_PATTERN)]; - assert.equal(matches.length, 0); -}); diff --git a/legacy-dashboard/tsconfig.json b/legacy-dashboard/tsconfig.json deleted file mode 100644 index 7820a40b..00000000 --- a/legacy-dashboard/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "@vue/tsconfig/tsconfig.dom.json", - "include": ["env.d.ts", "src/**/*", "src/**/*.vue", "src/types/.d.ts"], - "compilerOptions": { - "ignoreDeprecations": "5.0", - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - "allowJs": true - }, - - "references": [ - { - "path": "./tsconfig.vite-config.json" - } - ] -} diff --git a/legacy-dashboard/tsconfig.vite-config.json b/legacy-dashboard/tsconfig.vite-config.json deleted file mode 100644 index a3d4b215..00000000 --- a/legacy-dashboard/tsconfig.vite-config.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@vue/tsconfig/tsconfig.json", - "include": ["vite.config.*"], - "compilerOptions": { - "composite": true, - "allowJs": true, - "types": ["node"] - } -} diff --git a/legacy-dashboard/vite.config.ts b/legacy-dashboard/vite.config.ts deleted file mode 100644 index ef1bf7e5..00000000 --- a/legacy-dashboard/vite.config.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { fileURLToPath, URL } from 'url'; -import { defineConfig } from 'vite'; -import vue from '@vitejs/plugin-vue'; -import vuetify from 'vite-plugin-vuetify'; -import webfontDl from 'vite-plugin-webfont-dl'; -// @ts-ignore — .mjs not in TS project scope; Vite resolves this at runtime -import { runMdiSubset } from './scripts/subset-mdi-font.mjs'; - -// Vite plugin: run MDI icon font subsetting (build only) -function mdiSubset() { - return { - name: 'vite-plugin-mdi-subset', - async buildStart() { - console.log('\n🔧 Running MDI icon font subsetting...'); - await runMdiSubset(); - }, - }; -} - -// https://vitejs.dev/config/ -export default defineConfig(({ command }) => ({ - plugins: [ - // Only run MDI subsetting during production builds, skip in dev server - ...(command === 'build' ? [mdiSubset()] : []), - vue({ - template: { - compilerOptions: { - isCustomElement: (tag) => ['v-list-recognize-title'].includes(tag) - } - } - }), - vuetify({ - autoImport: true - }), - webfontDl() - ], - resolve: { - alias: [ - { - find: /^shiki$/, - replacement: fileURLToPath(new URL('./src/utils/shikiLimitedBundle.js', import.meta.url)) - }, - { - find: /^stream-monaco$/, - replacement: fileURLToPath(new URL('./src/utils/streamMonacoDisabled.js', import.meta.url)) - }, - { - find: 'mermaid', - replacement: 'mermaid/dist/mermaid.js' - }, - { - find: '@', - replacement: fileURLToPath(new URL('./src', import.meta.url)) - } - ] - }, - css: { - preprocessorOptions: { - scss: {} - } - }, - build: { - sourcemap: false, - chunkSizeWarningLimit: 1024 * 1024 // Set the limit to 1 MB - }, - optimizeDeps: { - exclude: ['vuetify'], - entries: ['./index.html', './src/main.ts', './src/**/*.vue'] - }, - server: { - host: '0.0.0.0', - port: 1420, - strictPort: true, - proxy: { - '/api': { - target: 'http://127.0.0.1:6185/', - changeOrigin: true, - ws: true - } - } - } -})); diff --git a/scripts/ci/dashboard-cutover.test.mjs b/scripts/ci/dashboard-cutover.test.mjs deleted file mode 100644 index c73c14e3..00000000 --- a/scripts/ci/dashboard-cutover.test.mjs +++ /dev/null @@ -1,43 +0,0 @@ -import assert from 'node:assert/strict'; -import { readFile } from 'node:fs/promises'; -import { test } from 'node:test'; - -const readProjectFile = (relativePath) => - readFile(new URL(`../../${relativePath}`, import.meta.url), 'utf8'); - -test('default package scripts use the React dashboard only', async () => { - const packageJson = JSON.parse(await readProjectFile('package.json')); - const scripts = packageJson.scripts; - - assert.equal(scripts['install:dashboard'], 'pnpm --dir dashboard install --frozen-lockfile'); - assert.equal(scripts['dev:dashboard'], 'pnpm --dir dashboard dev'); - assert.equal(scripts['typecheck:dashboard'], 'pnpm --dir dashboard typecheck'); - assert.equal(scripts['prepare:webui'], 'node scripts/prepare-resources.mjs webui'); - assert.equal(scripts.dev, 'node scripts/run-tauri.mjs dev'); - assert.equal(scripts.build, 'node scripts/run-tauri.mjs build'); - - const serializedScripts = JSON.stringify(scripts); - assert.doesNotMatch(serializedScripts, /new-dashboard|legacy-dashboard|:new|run-tauri-new/); -}); - -test('dashboard quality workflow targets the React dashboard directory', async () => { - const workflow = await readProjectFile('.github/workflows/check-dashboard.yml'); - - assert.match(workflow, /working-directory: dashboard/); - assert.match(workflow, /node-cache-dependency-path: dashboard\/pnpm-lock\.yaml/); - assert.match(workflow, /pnpm install --frozen-lockfile/); - assert.doesNotMatch(workflow, /new-dashboard|legacy-dashboard/); -}); - -test('desktop build configuration cannot reference the legacy dashboard', async () => { - const files = await Promise.all([ - readProjectFile('src-tauri/tauri.conf.json'), - readProjectFile('Makefile'), - readProjectFile('.github/workflows/build-desktop-tauri.yml'), - readProjectFile('.github/actions/setup-desktop-build/action.yml'), - ]); - const buildConfiguration = files.join('\n'); - - assert.match(buildConfiguration, /pnpm run prepare:resources/); - assert.doesNotMatch(buildConfiguration, /new-dashboard|legacy-dashboard/); -}); diff --git a/scripts/prepare-resources/source-repo.mjs b/scripts/prepare-resources/source-repo.mjs index fca4d9fd..a517d0a4 100644 --- a/scripts/prepare-resources/source-repo.mjs +++ b/scripts/prepare-resources/source-repo.mjs @@ -1,4 +1,4 @@ -import { existsSync, mkdirSync } from 'node:fs'; +import { existsSync, mkdirSync, rmSync } from 'node:fs'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; @@ -65,6 +65,30 @@ export const resolveSourceDir = (projectRoot, sourceDirOverrideRaw, cwd = proces return path.join(projectRoot, 'vendor', 'AstrBot'); }; +export const removeUpstreamDashboard = (sourceDir) => { + const dashboardDir = path.join(sourceDir, 'dashboard'); + if (!existsSync(dashboardDir)) return false; + + rmSync(dashboardDir, { + force: true, + maxRetries: 3, + recursive: true, + retryDelay: 100, + }); + console.log(`[source-repo] Removed upstream Dashboard: ${dashboardDir}`); + return true; +}; + +const restoreTrackedDashboardForCheckout = (sourceDir) => { + const trackedResult = spawnSync('git', ['-C', sourceDir, 'cat-file', '-e', 'HEAD:dashboard'], { stdio: 'ignore' }); + if (trackedResult.status !== 0) return; + + const restoreResult = spawnSync('git', ['-C', sourceDir, 'checkout', '--', 'dashboard'], { stdio: 'inherit' }); + if (restoreResult.status !== 0) { + throw new Error(`Failed to restore the managed upstream Dashboard before checkout: ${sourceDir}`); + } +}; + export const ensureSourceRepo = ({ sourceDir, sourceRepoUrl, @@ -105,6 +129,11 @@ export const ensureSourceRepo = ({ } if (sourceRepoRef) { + // The managed checkout intentionally excludes the upstream WebUI. Restore only + // that tracked path before switching refs so Git does not treat its removal as + // a conflicting local change; all other local backend changes remain protected. + restoreTrackedDashboardForCheckout(sourceDir); + const fetchResult = spawnSync( 'git', ['-C', sourceDir, 'fetch', '--depth', '1', 'origin', sourceRepoRef], @@ -127,4 +156,6 @@ export const ensureSourceRepo = ({ if (!existsSync(path.join(sourceDir, 'main.py'))) { throw new Error(`Resolved source repository is invalid: ${sourceDir}. Cannot find main.py.`); } + + removeUpstreamDashboard(sourceDir); }; diff --git a/scripts/prepare-resources/source-repo.test.mjs b/scripts/prepare-resources/source-repo.test.mjs index ce5caf3d..014006fe 100644 --- a/scripts/prepare-resources/source-repo.test.mjs +++ b/scripts/prepare-resources/source-repo.test.mjs @@ -1,13 +1,37 @@ import { test } from 'node:test'; import assert from 'node:assert/strict'; +import { spawnSync } from 'node:child_process'; +import { existsSync } from 'node:fs'; +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import os from 'node:os'; import path from 'node:path'; import { + ensureSourceRepo, getSourceRefInfo, normalizeSourceRepoConfig, resolveSourceDir, } from './source-repo.mjs'; +const runGit = (cwd, args) => { + const result = spawnSync('git', args, { cwd, encoding: 'utf8' }); + assert.equal(result.status, 0, `git ${args.join(' ')} failed:\n${result.stdout}\n${result.stderr}`); +}; + +const createUpstreamFixture = async (root) => { + const upstreamDir = path.join(root, 'upstream'); + await mkdir(path.join(upstreamDir, 'dashboard'), { recursive: true }); + await writeFile(path.join(upstreamDir, 'main.py'), '# AstrBot fixture\n'); + await writeFile(path.join(upstreamDir, 'dashboard', 'index.html'), 'legacy dashboard\n'); + runGit(upstreamDir, ['init']); + runGit(upstreamDir, ['checkout', '-b', 'main']); + runGit(upstreamDir, ['config', 'user.email', 'tests@example.com']); + runGit(upstreamDir, ['config', 'user.name', 'AstrBot Tests']); + runGit(upstreamDir, ['add', '.']); + runGit(upstreamDir, ['commit', '-m', 'initial fixture']); + return upstreamDir; +}; + test('normalizeSourceRepoConfig normalizes GitHub tree URL and infers ref', () => { const { repoUrl, repoRef } = normalizeSourceRepoConfig( 'https://github.com/AstrBotDevs/AstrBot/tree/release-1.2.3/dashboard', @@ -51,3 +75,48 @@ test('resolveSourceDir honors override and default project layout', () => { const resolvedDefault = resolveSourceDir('/project/root', '', '/work'); assert.equal(resolvedDefault, path.join('/project/root', 'vendor', 'AstrBot')); }); + +test('ensureSourceRepo removes the upstream dashboard after clone and ref updates', async (t) => { + const root = await mkdtemp(path.join(os.tmpdir(), 'astrbot-source-repo-')); + t.after(() => rm(root, { force: true, recursive: true })); + const upstreamDir = await createUpstreamFixture(root); + const sourceDir = path.join(root, 'managed-source'); + const options = { + isSourceRepoRefCommitSha: false, + sourceDir, + sourceDirOverrideRaw: '', + sourceRepoRef: 'main', + sourceRepoUrl: upstreamDir, + }; + + ensureSourceRepo(options); + assert.equal(existsSync(path.join(sourceDir, 'main.py')), true); + assert.equal(existsSync(path.join(sourceDir, 'dashboard')), false); + + await writeFile(path.join(upstreamDir, 'dashboard', 'index.html'), 'updated legacy dashboard\n'); + await writeFile(path.join(upstreamDir, 'updated.txt'), 'updated backend\n'); + runGit(upstreamDir, ['add', '.']); + runGit(upstreamDir, ['commit', '-m', 'update fixture']); + + ensureSourceRepo(options); + assert.equal(existsSync(path.join(sourceDir, 'updated.txt')), true); + assert.equal(existsSync(path.join(sourceDir, 'dashboard')), false); +}); + +test('ensureSourceRepo does not modify an explicit source directory override', async (t) => { + const sourceDir = await mkdtemp(path.join(os.tmpdir(), 'astrbot-source-override-')); + t.after(() => rm(sourceDir, { force: true, recursive: true })); + await mkdir(path.join(sourceDir, 'dashboard')); + await writeFile(path.join(sourceDir, 'main.py'), '# Local source fixture\n'); + await writeFile(path.join(sourceDir, 'dashboard', 'index.html'), 'local dashboard\n'); + + ensureSourceRepo({ + isSourceRepoRefCommitSha: false, + sourceDir, + sourceDirOverrideRaw: sourceDir, + sourceRepoRef: '', + sourceRepoUrl: '', + }); + + assert.equal(existsSync(path.join(sourceDir, 'dashboard', 'index.html')), true); +});