diff --git a/.eslintignore b/.eslintignore index e9f080cf..83eaa25a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -16,3 +16,5 @@ app/view/ test.js config/manifest.json + +dt-skill/dist/ diff --git a/.gitignore b/.gitignore index 4f79674c..1762aee8 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,20 @@ app/view # Research docs (working notes, not for commit) docs/research + +# Local Agent configuration & test skill folder +.agents/ +test-cli-skill/ + +# Universal/Dev tools ignores +dist/ +build/ +*.log +.env* +.DS_Store +Thumbs.db +*.tmp +*.swp +.vscode/ +.idea/ + diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..cc0a54d9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,158 @@ +# AGENTS.md + +This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. + +**项目**: dt-doraemon (哆啦A梦) — 开发者工具箱平台,包含代理服务、主机管理、配置中心、MCP 服务器注册中心、Skills 市场等模块。 + +**技术栈**: Egg.js 2.x + React 16 SSR + MySQL (Sequelize) + Webpack 4 + Redux + Socket.IO。 + +## 环境准备 + +项目使用 Node.js 18(`.nvmrc` 锁定为 `18.20.3`),依赖管理使用 Yarn。 + +```bash +# 切换到正确的 Node 版本 +fnm use 18 +# 或 +nvm use + +# 安装依赖 +yarn install +``` + +## 常用命令 + +```bash +# 开发启动(Egg.js dev + Webpack HMR,访问 http://127.0.0.1:7001) +npm run dev + +# 生产启动 +npm start + +# 构建前端资源 +npm run build + +# 代码检查 +npm run lint # prettier + eslint + stylelint +npm run lint:fix # 自动修复 +npm run check-types # TypeScript 类型检查(仅前端 app/web/) + +# 单独运行测试(主项目使用 Node.js 内置 test runner) +node --test test/*.test.js + +# dt-skill CLI 子项目(独立 npm 包) +cd dt-skill +npm run build +npm run test:src # vitest 测试 +node bin/dt-skill.js --help +``` + +## 项目结构 + +``` +app/ + controller/ # Egg.js 控制器(路由处理) + service/ # Egg.js 服务层(业务逻辑) + model/ # Sequelize 模型定义 + middleware/ # Egg.js 中间件(access, proxy) + schedule/ # Egg.js 定时任务 + web/ # React 前端源码(SSR) + pages/ # 页面组件 + router/ # React Router 配置 + store/ # Redux store(thunk + devtools) + api/ # API 请求封装(基于 url + method 映射) + layouts/ # 布局组件(basicLayout, sider, header) + mcp/ # MCP 服务器生命周期管理逻辑 + agent/ # Agent 进程 MCP 相关处理器 + utils/ # 后端工具函数 +config/ + config.default.js # Egg.js 默认配置 + config.local.js # 本地开发配置(含 MySQL、Webpack) + config.test.js # 测试环境配置 + config.prod.js # 生产配置 + plugin.js # Egg.js 插件注册(sequelize, reactssr, io, ssh) +dt-skill/ # ClawHub CLI 子项目(TypeScript + Vitest,独立包) +specs/ # 功能规格说明文档 +sql/ # 数据库初始化 SQL(doraemon.sql) +test/ # 主项目测试文件(Node.js 内置 test runner) +``` + +## 后端架构 + +### Egg.js 应用结构 +- **入口**: `app.js` (AppBootHook) 和 `agent.js` (Agent 进程生命周期)。 +- **路由**: `app/router.js` 集中定义所有 RESTful 路由和代理路由 `/proxy/:id/*`。 +- **MCP 生命周期**: Agent 进程 (`agent.js`) 负责 MCP 服务器的启动、停止、重启代理。Worker 进程通过 `app.messenger` 与 Agent 通信。 +- **定时任务**: `app/schedule/` 包含文章订阅和 MCP 健康检查任务。 +- **文件上传**: 使用 Egg.js multipart,临时文件存放于 `cache/uploads/`,大小限制 200MB,白名单允许所有类型。 + +### 数据库 +- **ORM**: Sequelize(`egg-sequelize` 插件)。 +- **配置位置**: `config/config.local.js`(本地默认数据库 `doraemon_test`,host `127.0.0.1`,用户名 `root`,密码 `123456`)。 +- **生产配置**: 从 `env.json` 的 `mysql.prod` 读取。 +- **初始化**: 导入 `sql/doraemon.sql` 到 MySQL。 + +### 关键配置 +- `env.json`: 存放 webhook URL、MySQL 配置、MCP 端口(`mcpEndpointPort: 7005`,`mcpInspectorWebPort: 7003`,`mcpInspectorServerPort: 7004`)。 +- CSRF 已全局禁用(`config.default.js`)。 +- `app.utils` 在 `app.js` 启动时挂载为全局工具对象。 + +## 前端架构 + +### 技术栈 +- React 16.9 + Redux + Redux-Thunk + React Router 4 +- Ant Design 4.15.6 + 自定义主题 (`theme.js`) +- TypeScript(仅用于前端 `app/web/`,编译目标 ES5) +- SCSS / Less(`app/web/scss/`,`app/web/pages/*/style.scss`) + +### 构建 +- Webpack 4 通过 `easywebpack-react` 配置(`webpack.config.js`)。 +- 开发时自动注入 CSS(`injectCss: true`)。 +- DLL 预打包:react, redux, react-router, xterm 等。 +- 路径别名:`@` -> `app/web/`,`@env` -> `env.json`。 +- 全局变量:`EASY_ENV_IS_DEV` 用于区分开发/生产环境。 + +### 状态管理 +- Redux store 在 `app/web/store/index.ts` 创建,使用 `redux-devtools-extension`(开发环境)。 +- Thunk 注入 `{ API }` 作为 extraArgument,API 通过 `app/web/api/index.ts` 根据 `url.ts` 中的 URL + method 配置自动生成。 + +### 路由 +- 前端路由定义在 `app/web/router/index.ts`,使用 `react-router-config` 风格配置。 +- 所有页面路由前缀为 `/page/*`,SSR 布局为 `BasicLayout`。 +- 部分页面使用 `react-loadable` 做代码分割(如 ConfigDetail, SwitchHostsEdit)。 + +## 子项目 dt-skill (ClawHub CLI) + +- **位置**: `dt-skill/`,独立的 npm 包,ES Module(`"type": "module"`)。 +- **用途**: 命令行工具,用于安装、搜索、发布 agent skills 和 OpenClaw 包。 +- **入口**: `bin/dt-skill.js`。 +- **构建**: `node ./scripts/build.mjs`,输出到 `dist/`。 +- **测试**: Vitest,配置在 `vitest.config.ts`(测试 `src/**/*.test.ts`)。 +- **Node 版本要求**: `>=20`(与主项目的 `>=18` 不同)。 + +## 测试 + +- **主项目**: 使用 Node.js 内置 `node:test` + `node:assert/strict`。测试文件在 `test/*.test.js`。 +- **dt-skill**: 使用 Vitest。运行 `cd dt-skill && npm run test:src`。 +- **原则**: 测试时不应该绕过待测组件用 curl 模拟。如果要测 CLI,运行真正的 CLI 命令;如果要测 API,通过客户端发请求。 + +## 代码规范 + +- ESLint 继承 `ko-lint-config`(`.eslintrc.js`)。 +- Prettier 继承 `ko-lint-config/.prettierrc`(`.prettierrc.js`)。 +- Stylelint 继承 `ko-lint-config/.stylelintrc`(`.stylelintrc.js`)。 +- Git 提交使用 Commitizen(`cz-conventional-changelog`),commit message 需符合 conventional commits 规范,由 husky + commitlint 校验。 + +## CI/CD + +- GitHub Actions: `.github/workflows/CI.yml`。 +- 在 `push` 到 `master` 或任意 `pull_request` 时触发。 +- 流程: 安装依赖 -> Prettier -> ESLint -> Stylelint -> check-types -> build。 +- Node 版本: 18.x,需设置 `NODE_OPTIONS=--openssl-legacy-provider`。 + +## 分支规范(CONTRIBUTING.md) + +- `master`: 主干分支,用于生产发布。 +- `dev`: 主开发分支。 +- `feat_版本号_xxx`: 新特性分支,从 `master` 切出,开发完 PR 到 `dev`。 +- `hotfix_版本号_xxx`: Bug 修复分支,从 `master` 切出,修复完 PR 到 `dev`,验证后合并到 `master`。 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..89d48f2e --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,158 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +**项目**: dt-doraemon (哆啦A梦) — 开发者工具箱平台,包含代理服务、主机管理、配置中心、MCP 服务器注册中心、Skills 市场等模块。 + +**技术栈**: Egg.js 2.x + React 16 SSR + MySQL (Sequelize) + Webpack 4 + Redux + Socket.IO。 + +## 环境准备 + +项目使用 Node.js 18(`.nvmrc` 锁定为 `18.20.3`),依赖管理使用 Yarn。 + +```bash +# 切换到正确的 Node 版本 +fnm use 18 +# 或 +nvm use + +# 安装依赖 +yarn install +``` + +## 常用命令 + +```bash +# 开发启动(Egg.js dev + Webpack HMR,访问 http://127.0.0.1:7001) +npm run dev + +# 生产启动 +npm start + +# 构建前端资源 +npm run build + +# 代码检查 +npm run lint # prettier + eslint + stylelint +npm run lint:fix # 自动修复 +npm run check-types # TypeScript 类型检查(仅前端 app/web/) + +# 单独运行测试(主项目使用 Node.js 内置 test runner) +node --test test/*.test.js + +# dt-skill CLI 子项目(独立 npm 包) +cd dt-skill +npm run build +npm run test:src # vitest 测试 +node bin/dt-skill.js --help +``` + +## 项目结构 + +``` +app/ + controller/ # Egg.js 控制器(路由处理) + service/ # Egg.js 服务层(业务逻辑) + model/ # Sequelize 模型定义 + middleware/ # Egg.js 中间件(access, proxy) + schedule/ # Egg.js 定时任务 + web/ # React 前端源码(SSR) + pages/ # 页面组件 + router/ # React Router 配置 + store/ # Redux store(thunk + devtools) + api/ # API 请求封装(基于 url + method 映射) + layouts/ # 布局组件(basicLayout, sider, header) + mcp/ # MCP 服务器生命周期管理逻辑 + agent/ # Agent 进程 MCP 相关处理器 + utils/ # 后端工具函数 +config/ + config.default.js # Egg.js 默认配置 + config.local.js # 本地开发配置(含 MySQL、Webpack) + config.test.js # 测试环境配置 + config.prod.js # 生产配置 + plugin.js # Egg.js 插件注册(sequelize, reactssr, io, ssh) +dt-skill/ # ClawHub CLI 子项目(TypeScript + Vitest,独立包) +specs/ # 功能规格说明文档 +sql/ # 数据库初始化 SQL(doraemon.sql) +test/ # 主项目测试文件(Node.js 内置 test runner) +``` + +## 后端架构 + +### Egg.js 应用结构 +- **入口**: `app.js` (AppBootHook) 和 `agent.js` (Agent 进程生命周期)。 +- **路由**: `app/router.js` 集中定义所有 RESTful 路由和代理路由 `/proxy/:id/*`。 +- **MCP 生命周期**: Agent 进程 (`agent.js`) 负责 MCP 服务器的启动、停止、重启代理。Worker 进程通过 `app.messenger` 与 Agent 通信。 +- **定时任务**: `app/schedule/` 包含文章订阅和 MCP 健康检查任务。 +- **文件上传**: 使用 Egg.js multipart,临时文件存放于 `cache/uploads/`,大小限制 200MB,白名单允许所有类型。 + +### 数据库 +- **ORM**: Sequelize(`egg-sequelize` 插件)。 +- **配置位置**: `config/config.local.js`(本地默认数据库 `doraemon_test`,host `127.0.0.1`,用户名 `root`,密码 `123456`)。 +- **生产配置**: 从 `env.json` 的 `mysql.prod` 读取。 +- **初始化**: 导入 `sql/doraemon.sql` 到 MySQL。 + +### 关键配置 +- `env.json`: 存放 webhook URL、MySQL 配置、MCP 端口(`mcpEndpointPort: 7005`,`mcpInspectorWebPort: 7003`,`mcpInspectorServerPort: 7004`)。 +- CSRF 已全局禁用(`config.default.js`)。 +- `app.utils` 在 `app.js` 启动时挂载为全局工具对象。 + +## 前端架构 + +### 技术栈 +- React 16.9 + Redux + Redux-Thunk + React Router 4 +- Ant Design 4.15.6 + 自定义主题 (`theme.js`) +- TypeScript(仅用于前端 `app/web/`,编译目标 ES5) +- SCSS / Less(`app/web/scss/`,`app/web/pages/*/style.scss`) + +### 构建 +- Webpack 4 通过 `easywebpack-react` 配置(`webpack.config.js`)。 +- 开发时自动注入 CSS(`injectCss: true`)。 +- DLL 预打包:react, redux, react-router, xterm 等。 +- 路径别名:`@` -> `app/web/`,`@env` -> `env.json`。 +- 全局变量:`EASY_ENV_IS_DEV` 用于区分开发/生产环境。 + +### 状态管理 +- Redux store 在 `app/web/store/index.ts` 创建,使用 `redux-devtools-extension`(开发环境)。 +- Thunk 注入 `{ API }` 作为 extraArgument,API 通过 `app/web/api/index.ts` 根据 `url.ts` 中的 URL + method 配置自动生成。 + +### 路由 +- 前端路由定义在 `app/web/router/index.ts`,使用 `react-router-config` 风格配置。 +- 所有页面路由前缀为 `/page/*`,SSR 布局为 `BasicLayout`。 +- 部分页面使用 `react-loadable` 做代码分割(如 ConfigDetail, SwitchHostsEdit)。 + +## 子项目 dt-skill (ClawHub CLI) + +- **位置**: `dt-skill/`,独立的 npm 包,ES Module(`"type": "module"`)。 +- **用途**: 命令行工具,用于安装、搜索、发布 agent skills 和 OpenClaw 包。 +- **入口**: `bin/dt-skill.js`。 +- **构建**: `node ./scripts/build.mjs`,输出到 `dist/`。 +- **测试**: Vitest,配置在 `vitest.config.ts`(测试 `src/**/*.test.ts`)。 +- **Node 版本要求**: `>=20`(与主项目的 `>=18` 不同)。 + +## 测试 + +- **主项目**: 使用 Node.js 内置 `node:test` + `node:assert/strict`。测试文件在 `test/*.test.js`。 +- **dt-skill**: 使用 Vitest。运行 `cd dt-skill && npm run test:src`。 +- **原则**: 测试时不应该绕过待测组件用 curl 模拟。如果要测 CLI,运行真正的 CLI 命令;如果要测 API,通过客户端发请求。 + +## 代码规范 + +- ESLint 继承 `ko-lint-config`(`.eslintrc.js`)。 +- Prettier 继承 `ko-lint-config/.prettierrc`(`.prettierrc.js`)。 +- Stylelint 继承 `ko-lint-config/.stylelintrc`(`.stylelintrc.js`)。 +- Git 提交使用 Commitizen(`cz-conventional-changelog`),commit message 需符合 conventional commits 规范,由 husky + commitlint 校验。 + +## CI/CD + +- GitHub Actions: `.github/workflows/CI.yml`。 +- 在 `push` 到 `master` 或任意 `pull_request` 时触发。 +- 流程: 安装依赖 -> Prettier -> ESLint -> Stylelint -> check-types -> build。 +- Node 版本: 18.x,需设置 `NODE_OPTIONS=--openssl-legacy-provider`。 + +## 分支规范(CONTRIBUTING.md) + +- `master`: 主干分支,用于生产发布。 +- `dev`: 主开发分支。 +- `feat_版本号_xxx`: 新特性分支,从 `master` 切出,开发完 PR 到 `dev`。 +- `hotfix_版本号_xxx`: Bug 修复分支,从 `master` 切出,修复完 PR 到 `dev`,验证后合并到 `master`。 diff --git a/app/controller/skillsRegistry.js b/app/controller/skillsRegistry.js new file mode 100644 index 00000000..63454f53 --- /dev/null +++ b/app/controller/skillsRegistry.js @@ -0,0 +1,184 @@ +const Controller = require('egg').Controller; + +class SkillsRegistryController extends Controller { + // GET /.well-known/dt-skill.json + async registryMetadata() { + const { ctx } = this; + const data = await ctx.service.skillsRegistry.getRegistryMetadata(ctx.origin); + ctx.body = data; + } + + // GET /api/v1/search + async search() { + const { ctx } = this; + const { q, limit } = ctx.query; + const results = await ctx.service.skillsRegistry.searchSkills(q, limit); + ctx.body = { results }; + } + + // GET /api/v1/skills + async list() { + const { ctx } = this; + const { cursor, sort, limit } = ctx.query; + const data = await ctx.service.skillsRegistry.listSkills(cursor, sort, limit); + ctx.body = data; + } + + // GET /api/v1/skills/:slug + async detail() { + const { ctx } = this; + const { slug } = ctx.params; + const data = await ctx.service.skillsRegistry.getSkillDetail(slug); + if (!data) { + ctx.status = 404; + ctx.body = { error: '技能不存在' }; + return; + } + ctx.body = data; + } + + // GET /api/v1/skills/:slug/versions + async versions() { + const { ctx } = this; + const { slug } = ctx.params; + const data = await ctx.service.skillsRegistry.listSkillVersions(slug); + if (!data) { + ctx.status = 404; + ctx.body = { error: '技能不存在' }; + return; + } + ctx.body = data; + } + + // GET /api/v1/skills/:slug/versions/:version + async versionDetail() { + const { ctx } = this; + const { slug, version } = ctx.params; + const data = await ctx.service.skillsRegistry.getSkillVersionDetail(slug, version); + if (!data) { + ctx.status = 404; + ctx.body = { error: '版本不存在' }; + return; + } + ctx.body = data; + } + + // GET /api/v1/skills/:slug/file + async fileContent() { + const { ctx } = this; + const { slug } = ctx.params; + const { path: filePath } = ctx.query; + const data = await ctx.service.skillsRegistry.getSkillFileContent(slug, filePath); + if (!data) { + ctx.status = 404; + ctx.body = { error: '文件不存在' }; + return; + } + ctx.body = data.content; + } + + // GET /api/v1/download + async download() { + const { ctx } = this; + const { slug } = ctx.query; + const result = await ctx.service.skillsRegistry.buildSkillZip(slug); + if (!result) { + ctx.status = 404; + ctx.body = { error: '技能不存在' }; + return; + } + ctx.set('Content-Type', 'application/zip'); + ctx.set( + 'Content-Disposition', + `attachment; filename="${encodeURIComponent(result.fileName)}"` + ); + ctx.body = result.content; + } + + // GET /api/v1/resolve + async resolve() { + const { ctx } = this; + const { slug, hash } = ctx.query; + const data = await ctx.service.skillsRegistry.resolveFingerprint(slug, hash); + if (!data) { + ctx.status = 404; + ctx.body = { error: '未找到匹配的技能' }; + return; + } + ctx.body = data; + } + + // POST /api/v1/skills + async publish() { + const { ctx } = this; + const payload = ctx.request.body || {}; + const files = ctx.request.files + ? Array.isArray(ctx.request.files) + ? ctx.request.files + : [ctx.request.files] + : []; + + try { + // If multipart, payload comes as a JSON string field + let parsedPayload = payload; + if (payload.payload && typeof payload.payload === 'string') { + try { + parsedPayload = JSON.parse(payload.payload); + } catch (e) { + ctx.throw(400, 'payload 必须是有效的 JSON 字符串'); + } + } + + const result = await ctx.service.skillsRegistry.publishSkill(parsedPayload, files); + ctx.body = result; + } finally { + try { + await ctx.cleanupRequestFiles(); + } catch (err) { + ctx.logger.warn('[skillsRegistry] 清理临时上传文件失败:', err); + } + } + } + + // DELETE /api/v1/skills/:slug + async delete() { + const { ctx } = this; + const { slug } = ctx.params; + const result = await ctx.service.skillsRegistry.deleteSkill(slug); + ctx.body = result; + } + + // POST /api/v1/skills/:slug/undelete + async undelete() { + const { ctx } = this; + const { slug } = ctx.params; + const result = await ctx.service.skillsRegistry.undeleteSkill(slug); + ctx.body = result; + } + + // POST /api/v1/stars/:slug + async star() { + const { ctx } = this; + const { slug } = ctx.params; + const ip = ctx.service.skillLike.resolveClientIp(); + const data = await ctx.service.skillLike.like(slug, ip); + ctx.body = { + starred: data.liked, + starCount: data.likeCount, + }; + } + + // DELETE /api/v1/stars/:slug + async unstar() { + const { ctx } = this; + const { slug } = ctx.params; + const ip = ctx.service.skillLike.resolveClientIp(); + const data = await ctx.service.skillLike.unlike(slug, ip); + ctx.body = { + starred: data.liked, + starCount: data.likeCount, + }; + } +} + +module.exports = SkillsRegistryController; diff --git a/app/middleware/skillsStorageReady.js b/app/middleware/skillsStorageReady.js new file mode 100644 index 00000000..1ec53770 --- /dev/null +++ b/app/middleware/skillsStorageReady.js @@ -0,0 +1,14 @@ +module.exports = () => { + let storageReadyPromise = null; + + return async function skillsStorageReady(ctx, next) { + if (!storageReadyPromise) { + storageReadyPromise = ctx.service.skills.ensureStorageReady().catch((error) => { + storageReadyPromise = null; + throw error; + }); + } + await storageReadyPromise; + await next(); + }; +}; diff --git a/app/model/skills_item.js b/app/model/skills_item.js index fcbd1b7f..7bbc531e 100644 --- a/app/model/skills_item.js +++ b/app/model/skills_item.js @@ -81,6 +81,17 @@ module.exports = (app) => { allowNull: false, defaultValue: 0, }, + is_package: { + type: TINYINT, + allowNull: false, + defaultValue: 0, + comment: '是否是技能包', + }, + parent_slug: { + type: STRING(255), + allowNull: true, + comment: '所属技能包的 slug', + }, created_at: { type: DATE, allowNull: false, diff --git a/app/router.js b/app/router.js index 2ce1e9b3..c3f6c8fd 100644 --- a/app/router.js +++ b/app/router.js @@ -162,6 +162,42 @@ module.exports = (app) => { app.post('/api/skills/unlike', app.controller.skillLike.unlike); app.get('/api/skills/like-status', app.controller.skillLike.getLikeStatus); + /** + * Skills Registry API (v1) + */ + const skillsStorageReady = app.middleware.skillsStorageReady(); + + app.get('/.well-known/dt-skill.json', app.controller.skillsRegistry.registryMetadata); + app.get('/api/v1/search', skillsStorageReady, app.controller.skillsRegistry.search); + app.get('/api/v1/skills', skillsStorageReady, app.controller.skillsRegistry.list); + app.get('/api/v1/skills/:slug', skillsStorageReady, app.controller.skillsRegistry.detail); + app.get( + '/api/v1/skills/:slug/versions', + skillsStorageReady, + app.controller.skillsRegistry.versions + ); + app.get( + '/api/v1/skills/:slug/versions/:version', + skillsStorageReady, + app.controller.skillsRegistry.versionDetail + ); + app.get( + '/api/v1/skills/:slug/file', + skillsStorageReady, + app.controller.skillsRegistry.fileContent + ); + app.get('/api/v1/download', skillsStorageReady, app.controller.skillsRegistry.download); + app.get('/api/v1/resolve', skillsStorageReady, app.controller.skillsRegistry.resolve); + app.post('/api/v1/skills', skillsStorageReady, app.controller.skillsRegistry.publish); + app.delete('/api/v1/skills/:slug', skillsStorageReady, app.controller.skillsRegistry.delete); + app.post( + '/api/v1/skills/:slug/undelete', + skillsStorageReady, + app.controller.skillsRegistry.undelete + ); + app.post('/api/v1/stars/:slug', skillsStorageReady, app.controller.skillsRegistry.star); + app.delete('/api/v1/stars/:slug', skillsStorageReady, app.controller.skillsRegistry.unstar); + // io.of('/').route('getShellCommand', io.controller.home.getShellCommand) // 暂时close Terminal相关功能 // io.of('/').route('loginServer', io.controller.home.loginServer) diff --git a/app/service/skills.js b/app/service/skills.js index ef76feeb..8a4b4d6e 100644 --- a/app/service/skills.js +++ b/app/service/skills.js @@ -1,11 +1,16 @@ const Service = require('egg').Service; const AdmZip = require('adm-zip'); const crypto = require('crypto'); -const { spawn } = require('child_process'); const fs = require('fs'); const os = require('os'); const path = require('path'); -const fetch = require('node-fetch'); +const { + createInstallKeyMap, + resolveSkillIdentifier, + createUniqueSkillNames, +} = require('../utils/skill-install-key'); +const GitHubStarsClient = require('../utils/github-stars'); +const CommandRunner = require('../utils/command-runner'); const CACHE_TTL_MS = 60 * 1000; const MAX_FILE_LIST_COUNT = 2000; @@ -20,106 +25,6 @@ const SKILLS_ROOT_DISCOVER_DEPTH_LIMIT = 8; const DISCOVER_MAX_DIR_COUNT = 3000; const SKILL_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; -function sanitizeInstallKeySegment(value) { - return String(value || '') - .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, '') - .replace(/-{2,}/g, '-'); -} - -function createInstallKeyCandidates(skill = {}) { - const candidates = []; - const pushCandidate = (value) => { - const normalized = sanitizeInstallKeySegment(value); - if (normalized && !candidates.includes(normalized)) { - candidates.push(normalized); - } - }; - - pushCandidate(skill.name); - - const sourcePath = String(skill.sourcePath || '') - .trim() - .replace(/\\/g, '/'); - if (sourcePath) { - const segments = sourcePath.split('/').filter(Boolean); - if (segments.length > 0) { - pushCandidate(segments[segments.length - 1]); - } - } - - pushCandidate(skill.slug); - pushCandidate('skill'); - return candidates; -} - -function createInstallKeyMap(skills = []) { - const bySlug = new Map(); - const byInstallKey = new Map(); - const counts = new Map(); - const list = skills.map((skill) => { - const candidates = createInstallKeyCandidates(skill); - let installKey = candidates.find((candidate) => !byInstallKey.has(candidate)) || ''; - - if (!installKey) { - const baseKey = candidates[0] || 'skill'; - const nextCount = (counts.get(baseKey) || 1) + 1; - counts.set(baseKey, nextCount); - installKey = `${baseKey}-${nextCount}`; - while (byInstallKey.has(installKey)) { - const currentCount = (counts.get(baseKey) || nextCount) + 1; - counts.set(baseKey, currentCount); - installKey = `${baseKey}-${currentCount}`; - } - } else { - const baseKey = candidates[0] || installKey; - counts.set(baseKey, Math.max(counts.get(baseKey) || 1, 1)); - } - - const normalizedSkill = { - ...skill, - installKey, - }; - bySlug.set(normalizedSkill.slug, normalizedSkill); - byInstallKey.set(installKey, normalizedSkill); - return normalizedSkill; - }); - - return { - list, - bySlug, - byInstallKey, - }; -} - -function resolveSkillIdentifier(identifier, indexes = {}) { - const value = String(identifier || '').trim(); - if (!value) return null; - if (indexes.bySlug instanceof Map && indexes.bySlug.has(value)) { - return indexes.bySlug.get(value); - } - if (indexes.byInstallKey instanceof Map && indexes.byInstallKey.has(value)) { - return indexes.byInstallKey.get(value); - } - return null; -} - -function createUniqueSkillNames(skillNames = []) { - const values = []; - const seen = new Set(); - - skillNames.forEach((item) => { - const name = String(item || '').trim(); - if (!name) return; - if (seen.has(name)) return; - seen.add(name); - values.push(name); - }); - - return values; -} - const SKILL_CATEGORY_OPTIONS = [ '通用', '前端', @@ -173,6 +78,7 @@ class SkillsService extends Service { this.skillCache = null; this.storageReady = false; this.storageReadyPromise = null; + this.commandRunner = new CommandRunner({ defaultTimeout: GIT_COMMAND_TIMEOUT_MS }); } getSkillCategoryOptions() { @@ -217,6 +123,7 @@ class SkillsService extends Service { await SkillsItem.sync(); await SkillsFile.sync(); await this.ensureSkillsItemVersionColumn(); + await this.ensureSkillsItemPackageColumns(); this.storageReady = true; })(); @@ -240,6 +147,26 @@ class SkillsService extends Service { }); } + async ensureSkillsItemPackageColumns() { + const queryInterface = this.app.model.getQueryInterface(); + const table = await queryInterface.describeTable('skills_items'); + if (!table.is_package) { + await queryInterface.addColumn('skills_items', 'is_package', { + type: this.app.Sequelize.TINYINT, + allowNull: false, + defaultValue: 0, + comment: '是否是技能包', + }); + } + if (!table.parent_slug) { + await queryInterface.addColumn('skills_items', 'parent_slug', { + type: this.app.Sequelize.STRING(255), + allowNull: true, + comment: '所属技能包的 slug', + }); + } + } + parseJsonArray(value) { if (!value) return []; if (Array.isArray(value)) return value; @@ -268,6 +195,8 @@ class SkillsService extends Service { sourceRepo: skill.sourceRepo, sourcePath: skill.sourcePath, installCommand: skill.installCommand, + isPackage: skill.isPackage ? 1 : 0, + parentSlug: skill.parentSlug || null, }; } @@ -295,6 +224,8 @@ class SkillsService extends Service { skillMd: row.skill_md || '', installCommand: row.install_command || '', fileCount: Number(row.file_count) || 0, + isPackage: Number(row.is_package) || 0, + parentSlug: row.parent_slug || null, }; } @@ -340,7 +271,23 @@ class SkillsService extends Service { const safePageSize = Math.max(parseInt(pageSize, 10) || 20, 1); const { skills, categories } = this.skillCache; - let list = [...skills]; + // Aggregate child stars by parent slug for package star totals + const childStarsByParent = new Map(); + for (const item of skills) { + if (item.parentSlug) { + const current = childStarsByParent.get(item.parentSlug) || 0; + childStarsByParent.set(item.parentSlug, current + (Number(item.stars) || 0)); + } + } + + let list = [...skills] + .filter((item) => !item.parentSlug) + .map((item) => { + if (item.isPackage === 1 && childStarsByParent.has(item.slug)) { + return { ...item, stars: childStarsByParent.get(item.slug) }; + } + return item; + }); if (keyword) { const value = String(keyword).toLowerCase(); list = list.filter( @@ -405,7 +352,7 @@ class SkillsService extends Service { async getSkillDetail(slug) { await this.ensureSkillCache(); const skill = this.getSkillByIdentifier(slug); - const { SkillsFile } = this.app.model; + const { SkillsFile, SkillsItem } = this.app.model; const rows = await SkillsFile.findAll({ where: { @@ -417,11 +364,33 @@ class SkillsService extends Service { limit: MAX_FILE_LIST_COUNT, }); - return { + const detail = { ...this.toPublicSkill(skill), skillMd: skill.skillMd, fileList: rows.map((row) => row.file_path), }; + + if (skill.isPackage === 1) { + const children = await SkillsItem.findAll({ + where: { + parent_slug: skill.slug, + is_delete: 0, + }, + order: [ + ['stars', 'DESC'], + ['updated_at_remote', 'DESC'], + ['updated_at', 'DESC'], + ['id', 'DESC'], + ], + }); + detail.children = children.map((row) => this.toPublicSkill(this.toSkillDto(row))); + detail.stars = detail.children.reduce( + (sum, child) => sum + (Number(child.stars) || 0), + 0 + ); + } + + return detail; } async getSkillFileContent(slug, filePath) { @@ -461,24 +430,53 @@ class SkillsService extends Service { async getSkillArchive(slug) { await this.ensureSkillCache(); const skill = this.getSkillByIdentifier(slug); - const { SkillsFile } = this.app.model; - const rows = await SkillsFile.findAll({ - where: { - skill_id: skill.id, - is_delete: 0, - }, - order: [['file_path', 'ASC']], - limit: MAX_FILE_LIST_COUNT, - }); - + const { SkillsItem, SkillsFile } = this.app.model; const zip = new AdmZip(); const rootFolder = this.sanitizeFileName(skill.name || skill.slug || 'skill'); - rows.forEach((row) => { - const safeRelativePath = this.normalizeRelativePath(row.file_path); - const zipPath = path.posix.join(rootFolder, safeRelativePath); - const buffer = this.decodeStoredFileContent(row.content, Boolean(row.is_binary)); - zip.addFile(zipPath, buffer); - }); + + if (skill.isPackage === 1) { + const children = await SkillsItem.findAll({ + where: { + parent_slug: skill.slug, + is_delete: 0, + }, + }); + for (const child of children) { + const childFolder = this.sanitizeFileName(child.name || child.slug || 'sub-skill'); + const childFiles = await SkillsFile.findAll({ + where: { + skill_id: child.id, + is_delete: 0, + }, + order: [['file_path', 'ASC']], + limit: MAX_FILE_LIST_COUNT, + }); + childFiles.forEach((row) => { + const safeRelativePath = this.normalizeRelativePath(row.file_path); + const zipPath = path.posix.join(rootFolder, childFolder, safeRelativePath); + const buffer = this.decodeStoredFileContent( + row.content, + Boolean(row.is_binary) + ); + zip.addFile(zipPath, buffer); + }); + } + } else { + const rows = await SkillsFile.findAll({ + where: { + skill_id: skill.id, + is_delete: 0, + }, + order: [['file_path', 'ASC']], + limit: MAX_FILE_LIST_COUNT, + }); + rows.forEach((row) => { + const safeRelativePath = this.normalizeRelativePath(row.file_path); + const zipPath = path.posix.join(rootFolder, safeRelativePath); + const buffer = this.decodeStoredFileContent(row.content, Boolean(row.is_binary)); + zip.addFile(zipPath, buffer); + }); + } return { fileName: `${rootFolder}.zip`, @@ -600,11 +598,14 @@ class SkillsService extends Service { isLikelyBinary(buffer) { if (!buffer || buffer.length === 0) return false; - const sampleLength = Math.min(buffer.length, 1024); - for (let i = 0; i < sampleLength; i += 1) { - if (buffer[i] === 0) return true; + const sample = buffer.subarray(0, Math.min(buffer.length, 4096)); + if (sample.includes(0)) return true; + try { + new TextDecoder('utf-8', { fatal: true }).decode(buffer); + return false; + } catch { + return true; } - return false; } sanitizeFileName(fileName) { @@ -633,11 +634,23 @@ class SkillsService extends Service { } buildSkillSlug(sourceMeta, relativeSkillPath, skillName, usedSlugs = new Set()) { - const sourceKey = `${sourceMeta.repoHost || 'local'}-${ - sourceMeta.repoPath || sourceMeta.sourceUrl || '' - }-${sourceMeta.ref || 'default'}`; - const relativeKey = String(relativeSkillPath || skillName || 'skill').replace(/\\/g, '/'); - const base = this.sanitizeSlugSegment(`${sourceKey}-${relativeKey}`) || 'skill'; + const isUpload = + sourceMeta && (sourceMeta.repoHost === 'upload' || sourceMeta.sourceType === 'upload'); + const relativeKey = String( + relativeSkillPath && relativeSkillPath !== '.' + ? relativeSkillPath + : skillName || 'skill' + ).replace(/\\/g, '/'); + + let base; + if (isUpload) { + base = this.sanitizeSlugSegment(relativeKey) || 'skill'; + } else { + const sourceKey = `${sourceMeta.repoHost || 'local'}-${ + sourceMeta.repoPath || sourceMeta.sourceUrl || '' + }-${sourceMeta.ref || 'default'}`; + base = this.sanitizeSlugSegment(`${sourceKey}-${relativeKey}`) || 'skill'; + } let slug = base; if (slug.length > 220) { @@ -745,9 +758,14 @@ class SkillsService extends Service { const identityText = String(identityKey || '').trim(); const normalizedIdentity = this.sanitizeSlugSegment(identityText); const identityHash = identityText ? this.hashString(identityText).slice(0, 8) : ''; - const sourceKey = - [normalizedName, normalizedIdentity, identityHash].filter(Boolean).join('-') || - normalizedName; + + // repoPath 用于包名展示,保持简洁可读:有 identityKey 时直接用,否则 fallback 到文件名 + const repoPath = normalizedIdentity || normalizedName; + // sourceUrl 用于数据库 source 去重,可带 hash 确保唯一性 + const sourceKey = normalizedIdentity + ? `${normalizedIdentity}-${identityHash}` + : normalizedName; + return { sourceUrl: `upload://${sourceKey}.skill`, sourceType: 'upload', @@ -756,19 +774,15 @@ class SkillsService extends Service { ref: '', subpath: '', repoHost: 'upload', - repoPath: sourceKey, + repoPath, originalAction: 'upload', }; } - getUploadIdentityKey(skillRecords = [], preferredName = '') { + getUploadIdentityKey(_skillRecords = [], preferredName = '') { const name = String(preferredName || '').trim(); if (name) return name; - return skillRecords - .map((item) => String(item?.name || '').trim()) - .filter(Boolean) - .sort((a, b) => a.localeCompare(b)) - .join('|'); + return ''; } extractDescription(content) { @@ -1091,14 +1105,14 @@ class SkillsService extends Service { sparseCloneArgs.push(parsedSource.cloneUrl, targetDir); try { - await this.runCommand( + await this.commandRunner.runCommand( 'git', sparseCloneArgs, GIT_COMMAND_TIMEOUT_MS, process.cwd(), env ); - await this.runCommand( + await this.commandRunner.runCommand( 'git', ['-C', targetDir, 'sparse-checkout', 'set', parsedSource.subpath], GIT_COMMAND_TIMEOUT_MS, @@ -1130,7 +1144,13 @@ class SkillsService extends Service { cloneArgs.push(parsedSource.cloneUrl, targetDir); try { - await this.runCommand('git', cloneArgs, GIT_COMMAND_TIMEOUT_MS, process.cwd(), env); + await this.commandRunner.runCommand( + 'git', + cloneArgs, + GIT_COMMAND_TIMEOUT_MS, + process.cwd(), + env + ); } catch (error) { if (!parsedSource.ref) { throw error; @@ -1145,7 +1165,13 @@ class SkillsService extends Service { parsedSource.cloneUrl, targetDir, ]; - await this.runCommand('git', fallbackArgs, GIT_COMMAND_TIMEOUT_MS, process.cwd(), env); + await this.commandRunner.runCommand( + 'git', + fallbackArgs, + GIT_COMMAND_TIMEOUT_MS, + process.cwd(), + env + ); } } @@ -1487,9 +1513,15 @@ class SkillsService extends Service { }; } + if (options.excludeSlugs && options.excludeSlugs.length > 0) { + where.slug = { + [Op.notIn]: options.excludeSlugs, + }; + } + const existing = await SkillsItem.findOne({ where, - attributes: ['id', 'name'], + attributes: ['id', 'name', 'slug'], transaction: options.transaction, }); @@ -1603,6 +1635,7 @@ class SkillsService extends Service { async importSkillFile(params = {}, file) { const skillName = String(params.skillName || '').trim(); + const packageName = String(params.packageName || '').trim(); const category = this.normalizeCategory(params.category); const tags = this.normalizePlatformTags(params.tags); const fileName = String((file && file.filename) || '').trim(); @@ -1638,11 +1671,14 @@ class SkillsService extends Service { this.ctx.throw(400, '.zip 包内未发现有效技能(缺少 SKILL.md)'); } + // skillName 仅用于覆盖单技能的名称;packageName 用于多技能时指定包名 if (skillName && discoveredSkillDirs.length !== 1) { this.ctx.throw(400, '填写技能名称时,.zip 包必须且只能包含一个技能目录'); } - const parsedSource = this.buildUploadSourceMeta(fileName, skillName); + // 多技能时优先使用 packageName 作为 identityKey,回退到 skillName + const identityKey = packageName || skillName; + const parsedSource = this.buildUploadSourceMeta(fileName, identityKey); const skillRecords = discoveredSkillDirs.map((skillDir) => { const record = this.prepareSkillRecord( @@ -1658,18 +1694,29 @@ class SkillsService extends Service { return record; }); - await this.assertSkillNamesUnique(skillRecords.map((item) => item.name)); + const tempUsedSlugs = new Set(); + const excludeSlugs = skillRecords.map((record) => + this.buildSkillSlug(parsedSource, record.sourcePath, record.name, tempUsedSlugs) + ); + + await this.assertSkillNamesUnique( + skillRecords.map((item) => item.name), + { + excludeSlugs, + } + ); const uploadSourceMeta = this.buildUploadSourceMeta( fileName, - this.getUploadIdentityKey(skillRecords, skillName) + this.getUploadIdentityKey(skillRecords, identityKey) ); sourceRecord = await this.upsertSourceRecord(uploadSourceMeta, 'syncing'); const importedSkills = await this.persistSkillsForSource( sourceRecord.id, uploadSourceMeta, - skillRecords + skillRecords, + packageName ); await sourceRecord.update({ @@ -1957,7 +2004,12 @@ class SkillsService extends Service { }; } - async persistSkillsForSource(sourceId, sourceMeta, skillRecords = []) { + async persistSkillsForSource( + sourceId, + sourceMeta, + skillRecords = [], + preferredPackageName = '' + ) { const { SkillsItem, SkillsFile } = this.app.model; const { Op } = this.app.Sequelize; const repoStars = await this.fetchStarsBySourceRepo(sourceMeta.sourceRepo); @@ -1986,6 +2038,69 @@ class SkillsService extends Service { const usedSlugs = new Set(); const createdSkills = []; + let parentSlug = ''; + if (skillRecords.length > 1) { + let parentName = ''; + if (preferredPackageName) { + // CLI 批量上传时使用用户指定的包名 + parentName = preferredPackageName; + } else if (sourceMeta.repoHost === 'upload' || sourceMeta.sourceType === 'upload') { + parentName = sourceMeta.repoPath || 'uploaded-skills-package'; + } else { + const parts = (sourceMeta.repoPath || '').split('/'); + parentName = parts[parts.length - 1] || 'git-skills-package'; + } + parentSlug = this.buildSkillSlug(sourceMeta, '.', parentName, usedSlugs); + + const parentPayload = { + source_id: sourceId, + slug: parentSlug, + name: parentName, + description: `技能包,包含以下子技能:\n${skillRecords + .map((r) => `- **${r.name}**: ${r.description || ''}`) + .join('\n')}`, + category: skillRecords[0].category || '通用', + version: '1.0.0', + tags: JSON.stringify( + Array.from(new Set(skillRecords.flatMap((r) => r.tags || []))) + ), + allowed_tools: JSON.stringify( + Array.from(new Set(skillRecords.flatMap((r) => r.allowedTools || []))) + ), + stars: resolvedStars, + updated_at_remote: new Date(), + source_repo: sourceMeta.sourceRepo || '', + source_path: '.', + skill_md: `# ${parentName}\n\n这是一个技能包,包含以下子技能:\n${skillRecords + .map((r) => `- **${r.name}**: ${r.description || ''}`) + .join('\n')}`, + install_command: '', + file_count: skillRecords.reduce((acc, r) => acc + (r.files || []).length, 0), + is_delete: 0, + is_package: 1, + parent_slug: null, + }; + + const globalExistingParent = await SkillsItem.findOne({ + where: { slug: parentSlug }, + transaction, + }); + + if (globalExistingParent) { + await globalExistingParent.update(parentPayload, { transaction }); + oldRowMap.delete(parentSlug); + } else { + await SkillsItem.create(parentPayload, { transaction }); + } + + createdSkills.push({ + slug: parentSlug, + name: parentName, + sourceRepo: sourceMeta.sourceRepo || '', + sourcePath: '.', + }); + } + for (const record of skillRecords) { const slug = this.buildSkillSlug( sourceMeta, @@ -2010,12 +2125,27 @@ class SkillsService extends Service { install_command: record.installCommand, file_count: record.files.length, is_delete: 0, + is_package: 0, + parent_slug: parentSlug || null, }; - const existingRow = oldRowMap.get(slug); + const globalExisting = await SkillsItem.findOne({ + where: { slug }, + transaction, + }); - let itemRow = existingRow; - if (existingRow) { - await existingRow.update(payload, { transaction }); + if ( + globalExisting && + globalExisting.is_delete === 0 && + globalExisting.name !== record.name + ) { + this.ctx.throw(400, 'slug 已存在'); + } + + const targetRow = globalExisting || oldRowMap.get(slug); + let itemRow; + if (targetRow) { + itemRow = targetRow; + await targetRow.update(payload, { transaction }); oldRowMap.delete(slug); } else { itemRow = await SkillsItem.create(payload, { transaction }); @@ -2082,155 +2212,12 @@ class SkillsService extends Service { } async fetchStarsBySourceRepo(sourceRepo = '') { - const repoFullName = this.extractGitHubRepoFullName(sourceRepo); - if (!repoFullName) return null; - return await this.fetchGitHubRepoStars(repoFullName); - } - - extractGitHubRepoFullName(sourceRepo = '') { - const raw = String(sourceRepo || '').trim(); - if (!raw) return ''; - const normalized = raw.replace(/^git\+/, '').replace(/\.git$/, ''); - const sshMatch = normalized.match(/^git@github\.com:([^/]+)\/([^/]+)$/i); - if (sshMatch) { - return `${sshMatch[1]}/${sshMatch[2]}`; - } - const httpsMatch = normalized.match(/^https?:\/\/github\.com\/([^/]+)\/([^/#?]+)/i); - if (httpsMatch) { - return `${httpsMatch[1]}/${httpsMatch[2]}`; - } - return ''; - } - - parseCompactNumber(input) { - const raw = String(input || '') - .trim() - .replace(/,/g, '') - .toLowerCase(); - if (!raw) return null; - - const match = raw.match(/^(\d+(?:\.\d+)?)\s*([kmb])?$/i); - if (!match) return null; - - const value = Number(match[1]); - if (!Number.isFinite(value)) return null; - - const suffix = (match[2] || '').toLowerCase(); - if (!suffix) return Math.round(value); - if (suffix === 'k') return Math.round(value * 1000); - if (suffix === 'm') return Math.round(value * 1000 * 1000); - if (suffix === 'b') return Math.round(value * 1000 * 1000 * 1000); - return null; - } - - extractStarsFromGitHubHtml(html = '') { - const content = String(html || ''); - if (!content) return null; - - const titleMatch = content.match(/id="repo-stars-counter-star"[^>]*title="([^"]+)"/i); - if (titleMatch) { - const stars = this.parseCompactNumber(titleMatch[1]); - if (typeof stars === 'number' && Number.isFinite(stars) && stars >= 0) return stars; - } - - const ariaMatch = content.match(/id="repo-stars-counter-star"[^>]*aria-label="([^"]+)"/i); - if (ariaMatch) { - const numberLike = ariaMatch[1].match(/[\d,.]+\s*[kmb]?/i); - if (numberLike) { - const stars = this.parseCompactNumber(numberLike[0]); - if (typeof stars === 'number' && Number.isFinite(stars) && stars >= 0) return stars; - } - } - - const textMatch = content.match(/id="repo-stars-counter-star"[^>]*>([^<]+)= 0) return stars; - } - - return null; - } - - async fetchGitHubRepoStarsFromHtml(repoFullName) { - const url = `https://github.com/${repoFullName}`; - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), GITHUB_API_TIMEOUT_MS); - - try { - const response = await fetch(url, { - method: 'GET', - headers: { - 'User-Agent': 'doraemon-skills-market', - Accept: 'text/html', - }, - signal: controller.signal, - }); - - if (!response.ok) { - this.ctx.logger.warn( - `[skills] HTML兜底获取 stars 失败: ${repoFullName}, status=${response.status}` - ); - return null; - } - - const html = await response.text(); - return this.extractStarsFromGitHubHtml(html); - } catch (error) { - this.ctx.logger.warn( - `[skills] HTML兜底获取 stars 异常: ${repoFullName}, ${error.message}` - ); - return null; - } finally { - clearTimeout(timer); - } - } - - async fetchGitHubRepoStars(repoFullName) { - if (!repoFullName) return null; - - const url = `https://api.github.com/repos/${repoFullName}`; - const headers = { - Accept: 'application/vnd.github+json', - 'User-Agent': 'doraemon-skills-market', - }; - - const token = this.resolveGitHubToken(); - if (token) { - headers.Authorization = `Bearer ${token}`; - } - - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), GITHUB_API_TIMEOUT_MS); - - try { - const response = await fetch(url, { - method: 'GET', - headers, - signal: controller.signal, - }); - - if (!response.ok) { - this.ctx.logger.warn( - `[skills] 获取 GitHub stars 失败: ${repoFullName}, status=${response.status}` - ); - if (response.status === 403 || response.status === 429) { - return await this.fetchGitHubRepoStarsFromHtml(repoFullName); - } - return null; - } - - const data = await response.json(); - const stars = Number(data.stargazers_count); - if (!Number.isFinite(stars) || stars < 0) return null; - return stars; - } catch (error) { - this.ctx.logger.warn( - `[skills] 获取 GitHub stars 异常: ${repoFullName}, ${error.message}` - ); - return null; - } finally { - clearTimeout(timer); - } + const client = new GitHubStarsClient({ + token: this.resolveGitHubToken(), + timeoutMs: GITHUB_API_TIMEOUT_MS, + logger: this.ctx.logger, + }); + return client.fetchByRepoUrl(sourceRepo); } extractHostFromRemote(remoteUrl = '') { @@ -2350,71 +2337,11 @@ class SkillsService extends Service { const basicToken = Buffer.from(`oauth2:${token}`).toString('base64'); return ['-c', `http.https://${host}/.extraHeader=Authorization: Basic ${basicToken}`]; } - - runCommand( - command, - args = [], - timeout = GIT_COMMAND_TIMEOUT_MS, - cwd = process.cwd(), - env = process.env - ) { - return new Promise((resolve, reject) => { - const child = spawn(command, args, { - cwd, - env, - }); - - let stdout = ''; - let stderr = ''; - let timedOut = false; - - const timer = setTimeout(() => { - timedOut = true; - child.kill('SIGTERM'); - }, timeout); - - child.stdout.on('data', (chunk) => { - stdout += chunk.toString(); - }); - - child.stderr.on('data', (chunk) => { - stderr += chunk.toString(); - }); - - child.on('error', (error) => { - clearTimeout(timer); - reject(error); - }); - - child.on('close', (code) => { - clearTimeout(timer); - - if (timedOut) { - reject(new Error(`命令执行超时(${timeout}ms): ${command}`)); - return; - } - - if (code !== 0) { - const detail = this.trimCommandOutput(stderr || stdout); - reject(new Error(detail || `命令退出码: ${code}`)); - return; - } - - resolve({ stdout, stderr }); - }); - }); - } - - trimCommandOutput(content = '') { - const value = String(content || '').trim(); - if (!value) return ''; - const maxLength = 3000; - if (value.length <= maxLength) return value; - return value.slice(value.length - maxLength); - } } +const installKeyModule = require('../utils/skill-install-key'); + module.exports = SkillsService; -module.exports.createInstallKeyMap = createInstallKeyMap; -module.exports.resolveSkillIdentifier = resolveSkillIdentifier; -module.exports.createUniqueSkillNames = createUniqueSkillNames; +module.exports.createInstallKeyMap = installKeyModule.createInstallKeyMap; +module.exports.resolveSkillIdentifier = installKeyModule.resolveSkillIdentifier; +module.exports.createUniqueSkillNames = installKeyModule.createUniqueSkillNames; diff --git a/app/service/skillsRegistry.js b/app/service/skillsRegistry.js new file mode 100644 index 00000000..746e488f --- /dev/null +++ b/app/service/skillsRegistry.js @@ -0,0 +1,614 @@ +const Service = require('egg').Service; +const AdmZip = require('adm-zip'); +const fs = require('fs'); +const ignore = require('ignore'); +const path = require('path'); +const skillFingerprint = require('../../contracts/skill-fingerprint'); + +const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[\w.-]+)?(?:\+[\w.-]+)?$/; +const SKILL_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; + +class SkillsRegistryService extends Service { + // Well-Known Registry Metadata + async getRegistryMetadata(origin) { + return { + apiBase: origin, + authBase: null, + minCliVersion: '0.9.0', + }; + } + + // Search skills by name/description LIKE match + async searchSkills(query, limit = 20) { + const { SkillsItem } = this.app.model; + const { Op } = this.app.Sequelize; + const searchQuery = String(query || '').trim(); + const where = { is_delete: 0, parent_slug: null }; + + if (searchQuery) { + where[Op.or] = [ + { name: { [Op.like]: `%${searchQuery}%` } }, + { description: { [Op.like]: `%${searchQuery}%` } }, + ]; + } + + const skills = await SkillsItem.findAll({ + where, + limit: Math.min(Number(limit) || 20, 100), + order: [['stars', 'DESC']], + }); + + return skills.map((skill) => ({ + slug: skill.slug, + displayName: skill.name, + summary: skill.description || null, + version: skill.version || null, + score: 1.0, + updatedAt: skill.updated_at ? new Date(skill.updated_at).getTime() : null, + ownerHandle: null, + owner: null, + })); + } + + // List skills with cursor pagination and sorting + async listSkills(cursor, sort, limit = 20) { + const { SkillsItem } = this.app.model; + const where = { is_delete: 0, parent_slug: null }; + const sortMap = { + newest: { key: 'newest', field: 'updated_at', type: 'date' }, + createdAt: { key: 'newest', field: 'updated_at', type: 'date' }, + updated: { key: 'newest', field: 'updated_at', type: 'date' }, + downloads: { key: 'stars', field: 'stars', type: 'number' }, + stars: { key: 'stars', field: 'stars', type: 'number' }, + }; + const sortConfig = sortMap[sort] || sortMap.newest; + const order = [ + [sortConfig.field, 'DESC'], + ['id', 'DESC'], + ]; + const safeLimit = Math.min(Math.max(Number(limit) || 20, 1), 100); + + if (cursor) { + const decoded = this.decodeListCursor(cursor, sortConfig); + if (decoded) { + const { Op } = this.app.Sequelize; + where[Op.or] = [ + { [sortConfig.field]: { [Op.lt]: decoded.value } }, + { + [sortConfig.field]: decoded.value, + id: { [Op.lt]: decoded.id }, + }, + ]; + } + } + + const skills = await SkillsItem.findAll({ + where, + limit: safeLimit + 1, + order, + }); + + const hasMore = skills.length > safeLimit; + const items = hasMore ? skills.slice(0, -1) : skills; + const nextCursor = + hasMore && items.length > 0 + ? this.encodeListCursor(items[items.length - 1], sortConfig) + : null; + + return { + items: items.map((skill) => { + const tags = this.parseJsonArray(skill.tags); + const stats = { stars: skill.stars || 0, downloads: 0 }; + const item = { + slug: skill.slug, + displayName: skill.name, + summary: skill.description || null, + tags, + stats, + createdAt: skill.created_at ? new Date(skill.created_at).getTime() : null, + updatedAt: skill.updated_at ? new Date(skill.updated_at).getTime() : null, + }; + if (skill.version) { + item.latestVersion = { + version: skill.version, + createdAt: skill.updated_at ? new Date(skill.updated_at).getTime() : null, + changelog: '', + license: null, + }; + } + return item; + }), + nextCursor, + }; + } + + // Get skill detail + async _resolveSlug(slug) { + const { SkillsItem } = this.app.model; + let skill = await SkillsItem.findOne({ + where: { slug, is_delete: 0 }, + }); + + if (!skill && this.ctx.service?.skills?.ensureSkillCache) { + const skillCache = await this.ctx.service.skills.ensureSkillCache(); + const resolved = skillCache?.byInstallKey?.get(slug); + if (resolved) { + skill = await SkillsItem.findOne({ + where: { slug: resolved.slug, is_delete: 0 }, + }); + } + } + + return skill; + } + + async getSkillDetail(slug) { + const { SkillsItem } = this.app.model; + const skill = await this._resolveSlug(slug); + + if (!skill) { + return null; + } + + const version = skill.version || ''; + const tags = this.parseJsonArray(skill.tags); + const stats = { stars: skill.stars || 0, downloads: 0 }; + const createdAt = skill.created_at ? new Date(skill.created_at).getTime() : 0; + const updatedAt = skill.updated_at ? new Date(skill.updated_at).getTime() : 0; + + const detail = { + skill: { + slug: skill.slug, + displayName: skill.name, + summary: skill.description || null, + version, + tags, + stats, + createdAt, + updatedAt, + isPackage: skill.is_package === 1, + parentSlug: skill.parent_slug || null, + }, + latestVersion: version + ? { + version, + createdAt: updatedAt, + changelog: '', + license: null, + } + : null, + owner: null, + moderation: null, + }; + + if (skill.is_package === 1) { + const children = await SkillsItem.findAll({ + where: { parent_slug: skill.slug, is_delete: 0 }, + order: [['stars', 'DESC']], + }); + detail.skill.children = children.map((child) => ({ + slug: child.slug, + displayName: child.name, + summary: child.description || null, + version: child.version || null, + tags: this.parseJsonArray(child.tags), + stats: { stars: child.stars || 0, downloads: 0 }, + createdAt: child.created_at ? new Date(child.created_at).getTime() : 0, + updatedAt: child.updated_at ? new Date(child.updated_at).getTime() : 0, + isPackage: false, + parentSlug: child.parent_slug, + })); + } + + return detail; + } + + // List skill versions (single version only) + async listSkillVersions(slug) { + const skill = await this._resolveSlug(slug); + + if (!skill) { + return null; + } + + const version = skill.version || ''; + const createdAt = skill.updated_at ? new Date(skill.updated_at).getTime() : 0; + + return { + items: [ + { + version, + createdAt, + changelog: '', + changelogSource: null, + }, + ], + nextCursor: null, + }; + } + + // Get skill version detail + async getSkillVersionDetail(slug, version) { + const skill = await this._resolveSlug(slug); + + if (!skill) { + return null; + } + + const currentVersion = skill.version || ''; + if (currentVersion !== version) { + return null; + } + + const createdAt = skill.updated_at ? new Date(skill.updated_at).getTime() : 0; + + return { + version: { + version: currentVersion, + createdAt, + changelog: '', + changelogSource: null, + license: null, + }, + skill: { + slug: skill.slug, + displayName: skill.name, + }, + }; + } + + // Get skill file content + async getSkillFileContent(slug, filePath) { + const { SkillsFile } = this.app.model; + const skill = await this._resolveSlug(slug); + + if (!skill) { + return null; + } + + const targetPath = String(filePath || 'SKILL.md').trim(); + const file = await SkillsFile.findOne({ + where: { skill_id: skill.id, file_path: targetPath, is_delete: 0 }, + }); + + if (!file) { + return null; + } + + return { + content: file.content || '', + isBinary: file.is_binary === 1, + path: file.file_path, + }; + } + + // Build skill ZIP archive in memory + async buildSkillZip(slug) { + const { SkillsItem, SkillsFile } = this.app.model; + const skill = await this._resolveSlug(slug); + + if (!skill) { + return null; + } + + const zip = new AdmZip(); + + if (skill.is_package === 1) { + const children = await SkillsItem.findAll({ + where: { parent_slug: skill.slug, is_delete: 0 }, + }); + const sanitizeFileName = (fileName) => { + return String(fileName || 'skill') + .trim() + .replace(/[^a-zA-Z0-9._-]+/g, '-') + .replace(/^-+|-+$/g, '') + .toLowerCase(); + }; + for (const child of children) { + const childFolder = sanitizeFileName(child.name || child.slug || 'sub-skill'); + const childFiles = await SkillsFile.findAll({ + where: { skill_id: child.id, is_delete: 0 }, + }); + for (const file of childFiles) { + const content = file.content || ''; + const zipPath = path.posix.join(slug, childFolder, file.file_path); + if (file.is_binary === 1) { + zip.addFile(zipPath, Buffer.from(content, 'base64')); + } else { + zip.addFile(zipPath, Buffer.from(content, 'utf8')); + } + } + } + } else { + const files = await SkillsFile.findAll({ + where: { skill_id: skill.id, is_delete: 0 }, + }); + for (const file of files) { + const content = file.content || ''; + if (file.is_binary === 1) { + zip.addFile(file.file_path, Buffer.from(content, 'base64')); + } else { + zip.addFile(file.file_path, Buffer.from(content, 'utf8')); + } + } + } + + const version = skill.version || 'latest'; + return { + fileName: `${slug}-${version}.zip`, + content: zip.toBuffer(), + }; + } + + // Validate SemVer format + validateSemVer(version) { + return SEMVER_PATTERN.test(String(version || '').trim()); + } + + // Publish or update a skill + async publishSkill(payload, files) { + const { SkillsItem, SkillsFile, SkillsSource } = this.app.model; + const { slug, displayName, version, tags } = payload; + + if (!SKILL_SLUG_PATTERN.test(String(slug || ''))) { + this.ctx.throw(400, 'slug 格式无效'); + } + + if (!this.validateSemVer(version)) { + this.ctx.throw(400, 'version 必须是有效的 SemVer 格式'); + } + + const parsedTags = Array.isArray(tags) ? tags : []; + + // Read files content from disk and determine original filenames + const processedFiles = []; + for (const file of files) { + const originalName = file.filename || path.basename(file.filepath); + let content = ''; + let isBinary = false; + try { + if (file.filepath && fs.existsSync(file.filepath)) { + const buffer = fs.readFileSync(file.filepath); + isBinary = this.isBinaryBuffer(buffer); + if (isBinary) { + content = buffer.toString('base64'); + } else { + content = buffer.toString('utf8'); + } + } + } catch (err) { + this.ctx.logger.error(`[skillsRegistry] 读取上传文件 ${originalName} 失败:`, err); + } + processedFiles.push({ + filename: originalName, + content, + isBinary, + }); + } + + // Check for SKILL.md + const skillMdFile = processedFiles.find( + (f) => f.filename && f.filename.toLowerCase().endsWith('skill.md') + ); + if (!skillMdFile) { + const uploadedNames = processedFiles.map((f) => f.filename).join(', '); + this.ctx.throw(400, `上传内容必须包含 SKILL.md。已上传: ${uploadedNames}`); + } + + // Upsert skill + let skill = await SkillsItem.findOne({ where: { slug } }); + + // Create or update source record + let source = await SkillsSource.findOne({ where: { source_url: 'clawhub-publish' } }); + if (!source) { + source = await SkillsSource.create({ + source_url: 'clawhub-publish', + source_type: 'upload', + clone_url: 'clawhub-publish', + source_repo: 'clawhub-publish', + }); + } + + if (skill) { + await skill.update({ + name: displayName, + description: payload.description || '', + version, + tags: JSON.stringify(parsedTags), + skill_md: skillMdFile.content || '', + is_delete: 0, + source_id: source.id, + }); + // Delete old files + await SkillsFile.update({ is_delete: 1 }, { where: { skill_id: skill.id } }); + } else { + skill = await SkillsItem.create({ + source_id: source.id, + slug, + name: displayName, + description: payload.description || '', + version, + tags: JSON.stringify(parsedTags), + skill_md: skillMdFile.content || '', + category: '通用', + file_count: files.length, + }); + } + + // Save files + for (const file of processedFiles) { + await SkillsFile.create({ + skill_id: skill.id, + file_path: file.filename, + language: this.detectLanguage(file.filename), + size: Buffer.byteLength(file.content, file.isBinary ? 'base64' : 'utf8'), + is_binary: file.isBinary ? 1 : 0, + encoding: file.isBinary ? 'base64' : 'utf8', + content: file.content, + }); + } + + // Update file count + await skill.update({ file_count: files.length }); + + return { + ok: true, + skillId: String(skill.id), + versionId: `v${version}`, + }; + } + + // Compute SHA256 fingerprint for a skill + async computeSkillFingerprint(skillId) { + const { SkillsFile } = this.app.model; + const files = await SkillsFile.findAll({ + where: { skill_id: skillId, is_delete: 0 }, + order: [['file_path', 'ASC']], + }); + const fingerprintIgnore = this.createFingerprintIgnore(files); + + return skillFingerprint.buildSkillFingerprintFromStoredFiles(files, { + ignoreMatcher: fingerprintIgnore, + }); + } + + // Resolve fingerprint to skill + async resolveFingerprint(slug, hash) { + const { SkillsItem } = this.app.model; + const skill = await SkillsItem.findOne({ + where: { slug, is_delete: 0 }, + }); + + if (!skill) { + return { + match: null, + latestVersion: null, + }; + } + + const skillFingerprint = await this.computeSkillFingerprint(skill.id); + const match = skillFingerprint === hash ? { version: skill.version || '' } : null; + const latestVersion = skill.version ? { version: skill.version } : null; + + return { + match, + latestVersion, + }; + } + + // Soft delete skill + async deleteSkill(slug) { + const { SkillsItem } = this.app.model; + const skill = await SkillsItem.findOne({ where: { slug } }); + if (!skill) { + this.ctx.throw(404, '技能不存在'); + } + await skill.update({ is_delete: 1 }); + return { ok: true }; + } + + // Undelete skill + async undeleteSkill(slug) { + const { SkillsItem } = this.app.model; + const skill = await SkillsItem.findOne({ where: { slug } }); + if (!skill) { + this.ctx.throw(404, '技能不存在'); + } + await skill.update({ is_delete: 0 }); + return { ok: true }; + } + + createFingerprintIgnore(files) { + const matcher = ignore(); + for (const ignoreFileName of skillFingerprint.FINGERPRINT_IGNORE_FILENAMES) { + const ignoreFile = files.find((file) => file.file_path === ignoreFileName); + if (ignoreFile && ignoreFile.is_binary !== 1 && ignoreFile.content) { + matcher.add(String(ignoreFile.content).split(/\r?\n/)); + } + } + return matcher; + } + + // Utility: parse JSON array string + parseJsonArray(value) { + if (!value) return []; + try { + const parsed = JSON.parse(value); + return Array.isArray(parsed) ? parsed : []; + } catch { + return []; + } + } + + encodeListCursor(skill, sortConfig) { + const rawValue = skill[sortConfig.field]; + const value = + sortConfig.type === 'date' ? new Date(rawValue).getTime() : Number(rawValue) || 0; + return Buffer.from( + JSON.stringify({ + sort: sortConfig.key, + value, + id: Number(skill.id), + }) + ).toString('base64'); + } + + decodeListCursor(cursor, sortConfig) { + try { + const parsed = JSON.parse(Buffer.from(cursor, 'base64').toString('utf8')); + if ( + parsed?.sort !== sortConfig.key || + !Number.isFinite(Number(parsed.value)) || + !Number.isFinite(Number(parsed.id)) + ) { + return null; + } + return { + value: + sortConfig.type === 'date' + ? new Date(Number(parsed.value)) + : Number(parsed.value), + id: Number(parsed.id), + }; + } catch { + return null; + } + } + + isBinaryBuffer(buffer) { + if (!buffer || buffer.length === 0) return false; + const sample = buffer.subarray(0, Math.min(buffer.length, 4096)); + if (sample.includes(0)) return true; + try { + new TextDecoder('utf-8', { fatal: true }).decode(sample); + return false; + } catch { + return true; + } + } + + // Utility: detect language from file extension + detectLanguage(fileName) { + const ext = (fileName || '').toLowerCase().slice(fileName.lastIndexOf('.') + 1); + const langMap = { + md: 'markdown', + js: 'javascript', + ts: 'typescript', + json: 'json', + yml: 'yaml', + yaml: 'yaml', + html: 'html', + css: 'css', + scss: 'scss', + less: 'less', + sh: 'bash', + py: 'python', + go: 'go', + rs: 'rust', + java: 'java', + }; + return langMap[ext] || 'text'; + } +} + +module.exports = SkillsRegistryService; diff --git a/app/utils/command-runner.js b/app/utils/command-runner.js new file mode 100644 index 00000000..4086b31c --- /dev/null +++ b/app/utils/command-runner.js @@ -0,0 +1,73 @@ +const { spawn } = require('child_process'); + +const DEFAULT_TIMEOUT_MS = 120 * 1000; + +class CommandRunner { + constructor({ defaultTimeout = DEFAULT_TIMEOUT_MS } = {}) { + this.defaultTimeout = defaultTimeout; + } + + runCommand( + command, + args = [], + timeout = this.defaultTimeout, + cwd = process.cwd(), + env = process.env + ) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd, + env, + }); + + let stdout = ''; + let stderr = ''; + let timedOut = false; + + const timer = setTimeout(() => { + timedOut = true; + child.kill('SIGTERM'); + }, timeout); + + child.stdout.on('data', (chunk) => { + stdout += chunk.toString(); + }); + + child.stderr.on('data', (chunk) => { + stderr += chunk.toString(); + }); + + child.on('error', (error) => { + clearTimeout(timer); + reject(error); + }); + + child.on('close', (code) => { + clearTimeout(timer); + + if (timedOut) { + reject(new Error(`命令执行超时(${timeout}ms): ${command}`)); + return; + } + + if (code !== 0) { + const detail = this.trimCommandOutput(stderr || stdout); + reject(new Error(detail || `命令退出码: ${code}`)); + return; + } + + resolve({ stdout, stderr }); + }); + }); + } + + trimCommandOutput(content = '') { + const value = String(content || '').trim(); + if (!value) return ''; + const maxLength = 3000; + if (value.length <= maxLength) return value; + return value.slice(value.length - maxLength); + } +} + +module.exports = CommandRunner; diff --git a/app/utils/github-stars.js b/app/utils/github-stars.js new file mode 100644 index 00000000..f6ba65a4 --- /dev/null +++ b/app/utils/github-stars.js @@ -0,0 +1,172 @@ +const fetch = require('node-fetch'); + +const DEFAULT_TIMEOUT_MS = 10 * 1000; + +class GitHubStarsClient { + constructor({ token = '', timeoutMs = DEFAULT_TIMEOUT_MS, logger = null } = {}) { + this.token = token; + this.timeoutMs = timeoutMs; + this.logger = logger; + } + + async fetchByRepoUrl(sourceRepo = '') { + const repoFullName = this.extractGitHubRepoFullName(sourceRepo); + if (!repoFullName) return null; + return this.fetchGitHubRepoStars(repoFullName); + } + + extractGitHubRepoFullName(sourceRepo = '') { + const raw = String(sourceRepo || '').trim(); + if (!raw) return ''; + const normalized = raw.replace(/^git\+/, '').replace(/\.git$/, ''); + const sshMatch = normalized.match(/^git@github\.com:([^/]+)\/([^/]+)$/i); + if (sshMatch) { + return `${sshMatch[1]}/${sshMatch[2]}`; + } + const httpsMatch = normalized.match(/^https?:\/\/github\.com\/([^/]+)\/([^/#?]+)/i); + if (httpsMatch) { + return `${httpsMatch[1]}/${httpsMatch[2]}`; + } + return ''; + } + + parseCompactNumber(input) { + const raw = String(input || '') + .trim() + .replace(/,/g, '') + .toLowerCase(); + if (!raw) return null; + + const match = raw.match(/^(\d+(?:\.\d+)?)\s*([kmb])?$/i); + if (!match) return null; + + const value = Number(match[1]); + if (!Number.isFinite(value)) return null; + + const suffix = (match[2] || '').toLowerCase(); + if (!suffix) return Math.round(value); + if (suffix === 'k') return Math.round(value * 1000); + if (suffix === 'm') return Math.round(value * 1000 * 1000); + if (suffix === 'b') return Math.round(value * 1000 * 1000 * 1000); + return null; + } + + extractStarsFromGitHubHtml(html = '') { + const content = String(html || ''); + if (!content) return null; + + const titleMatch = content.match(/id="repo-stars-counter-star"[^>]*title="([^"]+)"/i); + if (titleMatch) { + const stars = this.parseCompactNumber(titleMatch[1]); + if (typeof stars === 'number' && Number.isFinite(stars) && stars >= 0) return stars; + } + + const ariaMatch = content.match(/id="repo-stars-counter-star"[^>]*aria-label="([^"]+)"/i); + if (ariaMatch) { + const numberLike = ariaMatch[1].match(/[\d,.]+\s*[kmb]?/i); + if (numberLike) { + const stars = this.parseCompactNumber(numberLike[0]); + if (typeof stars === 'number' && Number.isFinite(stars) && stars >= 0) return stars; + } + } + + const textMatch = content.match(/id="repo-stars-counter-star"[^>]*>([^<]+)= 0) return stars; + } + + return null; + } + + async fetchGitHubRepoStarsFromHtml(repoFullName) { + const url = `https://github.com/${repoFullName}`; + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), this.timeoutMs); + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + 'User-Agent': 'doraemon-skills-market', + Accept: 'text/html', + }, + signal: controller.signal, + }); + + if (!response.ok) { + if (this.logger) { + this.logger.warn( + `[github-stars] HTML兜底获取 stars 失败: ${repoFullName}, status=${response.status}` + ); + } + return null; + } + + const html = await response.text(); + return this.extractStarsFromGitHubHtml(html); + } catch (error) { + if (this.logger) { + this.logger.warn( + `[github-stars] HTML兜底获取 stars 异常: ${repoFullName}, ${error.message}` + ); + } + return null; + } finally { + clearTimeout(timer); + } + } + + async fetchGitHubRepoStars(repoFullName) { + if (!repoFullName) return null; + + const url = `https://api.github.com/repos/${repoFullName}`; + const headers = { + Accept: 'application/vnd.github+json', + 'User-Agent': 'doraemon-skills-market', + }; + + if (this.token) { + headers.Authorization = `Bearer ${this.token}`; + } + + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), this.timeoutMs); + + try { + const response = await fetch(url, { + method: 'GET', + headers, + signal: controller.signal, + }); + + if (!response.ok) { + if (this.logger) { + this.logger.warn( + `[github-stars] 获取 GitHub stars 失败: ${repoFullName}, status=${response.status}` + ); + } + if (response.status === 403 || response.status === 429) { + return await this.fetchGitHubRepoStarsFromHtml(repoFullName); + } + return null; + } + + const data = await response.json(); + const stars = Number(data.stargazers_count); + if (!Number.isFinite(stars) || stars < 0) return null; + return stars; + } catch (error) { + if (this.logger) { + this.logger.warn( + `[github-stars] 获取 GitHub stars 异常: ${repoFullName}, ${error.message}` + ); + } + return null; + } finally { + clearTimeout(timer); + } + } +} + +module.exports = GitHubStarsClient; diff --git a/app/utils/skill-install-key.js b/app/utils/skill-install-key.js new file mode 100644 index 00000000..465f852f --- /dev/null +++ b/app/utils/skill-install-key.js @@ -0,0 +1,107 @@ +function sanitizeInstallKeySegment(value) { + return String(value || '') + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .replace(/-{2,}/g, '-'); +} + +function createInstallKeyCandidates(skill = {}) { + const candidates = []; + const pushCandidate = (value) => { + const normalized = sanitizeInstallKeySegment(value); + if (normalized && !candidates.includes(normalized)) { + candidates.push(normalized); + } + }; + + pushCandidate(skill.name); + + const sourcePath = String(skill.sourcePath || '') + .trim() + .replace(/\\/g, '/'); + if (sourcePath) { + const segments = sourcePath.split('/').filter(Boolean); + if (segments.length > 0) { + pushCandidate(segments[segments.length - 1]); + } + } + + pushCandidate(skill.slug); + pushCandidate('skill'); + return candidates; +} + +function createInstallKeyMap(skills = []) { + const bySlug = new Map(); + const byInstallKey = new Map(); + const counts = new Map(); + const list = skills.map((skill) => { + const candidates = createInstallKeyCandidates(skill); + let installKey = candidates.find((candidate) => !byInstallKey.has(candidate)) || ''; + + if (!installKey) { + const baseKey = candidates[0] || 'skill'; + const nextCount = (counts.get(baseKey) || 1) + 1; + counts.set(baseKey, nextCount); + installKey = `${baseKey}-${nextCount}`; + while (byInstallKey.has(installKey)) { + const currentCount = (counts.get(baseKey) || nextCount) + 1; + counts.set(baseKey, currentCount); + installKey = `${baseKey}-${currentCount}`; + } + } else { + const baseKey = candidates[0] || installKey; + counts.set(baseKey, Math.max(counts.get(baseKey) || 1, 1)); + } + + const normalizedSkill = { + ...skill, + installKey, + }; + bySlug.set(normalizedSkill.slug, normalizedSkill); + byInstallKey.set(installKey, normalizedSkill); + return normalizedSkill; + }); + + return { + list, + bySlug, + byInstallKey, + }; +} + +function resolveSkillIdentifier(identifier, indexes = {}) { + const value = String(identifier || '').trim(); + if (!value) return null; + if (indexes.bySlug instanceof Map && indexes.bySlug.has(value)) { + return indexes.bySlug.get(value); + } + if (indexes.byInstallKey instanceof Map && indexes.byInstallKey.has(value)) { + return indexes.byInstallKey.get(value); + } + return null; +} + +function createUniqueSkillNames(skillNames = []) { + const values = []; + const seen = new Set(); + + skillNames.forEach((item) => { + const name = String(item || '').trim(); + if (!name) return; + if (seen.has(name)) return; + seen.add(name); + values.push(name); + }); + + return values; +} + +module.exports = { + sanitizeInstallKeySegment, + createInstallKeyCandidates, + createInstallKeyMap, + resolveSkillIdentifier, + createUniqueSkillNames, +}; diff --git a/app/view/app.js b/app/view/app.js index 2de73133..f8c073ac 100644 --- a/app/view/app.js +++ b/app/view/app.js @@ -1 +1 @@ -!function(n,t){for(var e in t)n[e]=t[e]}(exports,function(n){var t={};function e(a){if(t[a])return t[a].exports;var r=t[a]={i:a,l:!1,exports:{}};return n[a].call(r.exports,r,r.exports,e),r.l=!0,r.exports}return e.m=n,e.c=t,e.d=function(n,t,a){e.o(n,t)||Object.defineProperty(n,t,{enumerable:!0,get:a})},e.r=function(n){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(n,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(n,"__esModule",{value:!0})},e.t=function(n,t){if(1&t&&(n=e(n)),8&t)return n;if(4&t&&"object"==typeof n&&n&&n.__esModule)return n;var a=Object.create(null);if(e.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:n}),2&t&&"string"!=typeof n)for(var r in n)e.d(a,r,function(t){return n[t]}.bind(null,r));return a},e.n=function(n){var t=n&&n.__esModule?function(){return n.default}:function(){return n};return e.d(t,"a",t),t},e.o=function(n,t){return Object.prototype.hasOwnProperty.call(n,t)},e.p="/public/",e(e.s=74)}([function(n,t){n.exports=require("react")},function(n,t){n.exports=require("antd")},function(n,t,e){"use strict";n.exports=function(n){var t=[];return t.toString=function(){return this.map((function(t){var e=function(n,t){var e=n[1]||"",a=n[3];if(!a)return e;if(t&&"function"==typeof btoa){var r=(i=a,l=btoa(unescape(encodeURIComponent(JSON.stringify(i)))),s="sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(l),"/*# ".concat(s," */")),o=a.sources.map((function(n){return"/*# sourceURL=".concat(a.sourceRoot||"").concat(n," */")}));return[e].concat(o).concat([r]).join("\n")}var i,l,s;return[e].join("\n")}(t,n);return t[2]?"@media ".concat(t[2]," {").concat(e,"}"):e})).join("")},t.i=function(n,e,a){"string"==typeof n&&(n=[[null,n,""]]);var r={};if(a)for(var o=0;o1&&void 0!==arguments[1]?arguments[1]:{},e=t.replace,o=void 0!==e&&e,c=t.prepend,d=void 0!==c&&c,p=[],b=0;b0&&r[r.length-1])||6!==o[0]&&2!==o[0])){i=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]0?a:e)(n)}},function(n,t,e){var a=e(51)("keys"),r=e(52);n.exports=function(n){return a[n]||(a[n]=r(n))}},function(n,t){n.exports=require("js-cookie")},function(n,t,e){"use strict";var a,r;Object.defineProperty(t,"__esModule",{value:!0}),t.SUBSCRIPTIONSENDTYPECN=t.SUBSCRIPTIONSENDTYPE=t.SUBSCRIPTIONSTATUS=void 0,function(n){n[n.OPEN=1]="OPEN",n[n.CLOSE=2]="CLOSE"}(t.SUBSCRIPTIONSTATUS||(t.SUBSCRIPTIONSTATUS={})),function(n){n[n.FRIDAY=0]="FRIDAY",n[n.WORKDAY=1]="WORKDAY",n[n.EVERYDAY=2]="EVERYDAY",n[n.MONDAY=3]="MONDAY"}(r=t.SUBSCRIPTIONSENDTYPE||(t.SUBSCRIPTIONSENDTYPE={})),t.SUBSCRIPTIONSENDTYPECN=((a={})[r.MONDAY]="每周一",a[r.FRIDAY]="每周五",a[r.WORKDAY]="周一至周五",a[r.EVERYDAY]="每天",a)},function(n,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.urlReg=void 0,t.urlReg=new RegExp(/(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?/,"i")},function(n,t){n.exports=require("socket.io-parser")},function(n,t){n.exports=require("prop-types")},function(n,t,e){"use strict";var a=this&&this.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var r=a(e(0)),o=e(1),i=e(4);t.default=function(n){var t=n.status,e=n.errorMsg;return r.default.createElement("div",{style:{display:"inline-flex",alignItems:"center",gap:4}},function(){switch(t){case"running":return r.default.createElement(o.Badge,{status:"success",text:"运行中"});case"stopped":return r.default.createElement(o.Badge,{status:"default",text:"已停止"});case"error":return r.default.createElement("span",{style:{display:"inline-flex",alignItems:"center",gap:4}},r.default.createElement(o.Badge,{status:"error",text:"运行错误"}),r.default.createElement(o.Tooltip,{title:e},r.default.createElement(i.InfoCircleOutlined,{style:{color:"#999"}})));default:return r.default.createElement(o.Badge,{status:"warning",text:"未知"})}}())}},function(n,t,e){"use strict";var a=this&&this.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var r=a(e(0)),o=e(1),i=e(4);e(235);t.default=function(n){var t=function(n){switch(n){case"stdio":return{color:"blue",icon:r.default.createElement(i.ApiOutlined,null),name:"STDIO",description:"原始传输方式:标准输入输出"};case"streamable-http":return{color:"green",icon:r.default.createElement(i.CloudServerOutlined,null),name:"HTTP",description:"原始传输方式:HTTP传输"};case"sse":return{color:"orange",icon:r.default.createElement(i.ThunderboltOutlined,null),name:"SSE",description:"原始传输方式:SSE流式传输"};default:return{color:"default",icon:r.default.createElement(i.ExclamationCircleOutlined,null),name:"UNKNOWN",description:"未知传输方式"}}}(n.transport);return r.default.createElement(o.Tooltip,{title:t.description,placement:"top"},r.default.createElement(o.Tag,{color:t.color,className:"transport-tag",icon:t.icon},t.name))}},function(n,t,e){"use strict";var a=e(16);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=a(e(81)),o=a(e(82)),i=a(e(39)),l={lang:(0,r.default)({placeholder:"请选择日期",yearPlaceholder:"请选择年份",quarterPlaceholder:"请选择季度",monthPlaceholder:"请选择月份",weekPlaceholder:"请选择周",rangePlaceholder:["开始日期","结束日期"],rangeYearPlaceholder:["开始年份","结束年份"],rangeMonthPlaceholder:["开始月份","结束月份"],rangeWeekPlaceholder:["开始周","结束周"]},o.default),timePickerLocale:(0,r.default)({},i.default)};l.lang.ok="确 定";var s=l;t.default=s},function(n,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a={placeholder:"请选择时间",rangePlaceholder:["开始时间","结束时间"]};t.default=a},function(n,t,e){"use strict";var a=this&&this.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var r=a(e(84)),o=a(e(123)),i=a(e(133)),l=a(e(139)),s=a(e(147)),c=a(e(151)),d=a(e(157)),p=a(e(161)),b=a(e(188)),u=a(e(192)),f=a(e(201)),m=a(e(209)),h=a(e(210)),g=a(e(212)),x=a(e(220)),w=a(e(233)),k=a(e(241)),v=a(e(251)),y=a(e(260)),z=a(e(263)),_=[{path:"/",component:r.default,routes:[{path:"".concat("/page","/toolbox"),component:x.default},{path:"".concat("/page","/home"),component:d.default},{path:"".concat("/page","/internal-url-navigation"),component:b.default},{path:"".concat("/page","/proxy-server"),component:f.default},{path:"".concat("/page","/mail-sign"),component:u.default},{path:"".concat("/page","/host-management"),component:p.default},{path:"".concat("/page","/env-management"),component:s.default},{path:"".concat("/page","/config-center"),component:i.default},{path:"".concat("/page","/config-detail/:id"),component:l.default},{path:"".concat("/page","/switch-hosts-list"),component:m.default},{path:"".concat("/page","/switch-hosts-edit/:id/:type"),component:h.default},{path:"".concat("/page","/article-subscription-list"),component:o.default},{path:"".concat("/page","/tags"),component:g.default},{path:"".concat("/page","/mcp-server-market"),component:w.default},{path:"".concat("/page","/mcp-server-registry/edit/:serverId"),component:k.default},{path:"".concat("/page","/mcp-server-registry"),component:k.default},{path:"".concat("/page","/mcp-server-detail/:serverId"),component:v.default},{path:"".concat("/page","/mcp-server-inspector"),component:y.default},{path:"".concat("/page","/mcp-server-management"),component:z.default},{path:"*",component:c.default}]}];t.default=_},function(n,t){n.exports=require("classnames")},function(n,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.changeLocalIp=void 0;var a=e(1),r=e(43);t.changeLocalIp=function(n){return void 0===n&&(n=!1),function(t,e,o){o.API.getLocalIp().then((function(e){var o=e.success,i=e.data;o&&(console.log(i),n&&a.message.success("刷新成功!"),t({type:r.CHANGE_LOCAL_IP,payload:i}))})).catch((function(){n&&a.message.warning("刷新失败!")}))}}},function(n,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CHANGE_LOCAL_IP=void 0,t.CHANGE_LOCAL_IP="CHANGE_LOCAL_IP"},function(n,t,e){e(94);for(var a=e(12),r=e(15),o=e(14),i=e(9)("toStringTag"),l="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),s=0;s=t.length?{value:void 0,done:!0}:(n=a(t,e),this._i+=n.length,{value:n,done:!1})}))},function(n,t,e){var a=e(45),r=e(9)("toStringTag"),o="Arguments"==a(function(){return arguments}());n.exports=function(n){var t,e,i;return void 0===n?"Undefined":null===n?"Null":"string"==typeof(e=function(n,t){try{return n[t]}catch(n){}}(t=Object(n),r))?e:o?a(t):"Object"==(i=a(t))&&"function"==typeof t.callee?"Arguments":i}},function(n,t,e){n.exports=e.p+"img/help-icon.3d0854a5.png"},function(n,t){n.exports=require("react-codemirror2")},function(n,t,e){"use strict";var a=this&&this.__assign||function(){return(a=Object.assign||function(n){for(var t,e=1,a=arguments.length;e*\/]/.test(p)?l(null,"select-op"):/[;{}:\[\]]/.test(p)?l(null,p):(n.eatWhile(/[\w\\\-]/),l("variable","variable")):l(null,"compare"):void l(null,"compare")}function c(n,t){for(var e,a=!1;null!=(e=n.next());){if(a&&"/"==e){t.tokenize=s;break}a="*"==e}return l("comment","comment")}function d(n,t){for(var e,a=0;null!=(e=n.next());){if(a>=2&&">"==e){t.tokenize=s;break}a="-"==e?a+1:0}return l("comment","comment")}return{startState:function(n){return{tokenize:s,baseIndent:n||0,stack:[]}},token:function(n,t){if(n.eatSpace())return null;e=null;var a=t.tokenize(n,t),r=t.stack[t.stack.length-1];return"hash"==e&&"rule"==r?a="atom":"variable"==a&&("rule"==r?a="number":r&&"@media{"!=r||(a="tag")),"rule"==r&&/^[\{\};]$/.test(e)&&t.stack.pop(),"{"==e?"@media"==r?t.stack[t.stack.length-1]="@media{":t.stack.push("{"):"}"==e?t.stack.pop():"@media"==e?t.stack.push("@media"):"{"==r&&"comment"!=e&&t.stack.push("rule"),a},indent:function(n,t){var e=n.stack.length;return/^\}/.test(t)&&(e-="rule"==n.stack[n.stack.length-1]?2:1),n.baseIndent+e*i},electricChars:"}"}})),n.defineMIME("text/x-nginx-conf","nginx")}(e(61))},function(n,t,e){n.exports=function(){"use strict";var n=navigator.userAgent,t=navigator.platform,e=/gecko\/\d/i.test(n),a=/MSIE \d/.test(n),r=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(n),o=/Edge\/(\d+)/.exec(n),i=a||r||o,l=i&&(a?document.documentMode||6:+(o||r)[1]),s=!o&&/WebKit\//.test(n),c=s&&/Qt\/\d+\.\d+/.test(n),d=!o&&/Chrome\/(\d+)/.exec(n),p=d&&+d[1],b=/Opera\//.test(n),u=/Apple Computer/.test(navigator.vendor),f=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(n),m=/PhantomJS/.test(n),h=u&&(/Mobile\/\w+/.test(n)||navigator.maxTouchPoints>2),g=/Android/.test(n),x=h||g||/webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(n),w=h||/Mac/.test(t),k=/\bCrOS\b/.test(n),v=/win/i.test(t),y=b&&n.match(/Version\/(\d*\.\d*)/);y&&(y=Number(y[1])),y&&y>=15&&(b=!1,s=!0);var z=w&&(c||b&&(null==y||y<12.11)),_=e||i&&l>=9;function F(n){return new RegExp("(^|\\s)"+n+"(?:$|\\s)\\s*")}var E,C=function(n,t){var e=n.className,a=F(t).exec(e);if(a){var r=e.slice(a.index+a[0].length);n.className=e.slice(0,a.index)+(r?a[1]+r:"")}};function S(n){for(var t=n.childNodes.length;t>0;--t)n.removeChild(n.firstChild);return n}function O(n,t){return S(n).appendChild(t)}function D(n,t,e,a){var r=document.createElement(n);if(e&&(r.className=e),a&&(r.style.cssText=a),"string"==typeof t)r.appendChild(document.createTextNode(t));else if(t)for(var o=0;o=t)return i+(t-o);i+=l-o,i+=e-i%e,o=l+1}}h?B=function(n){n.selectionStart=0,n.selectionEnd=n.value.length}:i&&(B=function(n){try{n.select()}catch(n){}});var U=function(){this.id=null,this.f=null,this.time=0,this.handler=N(this.onTimeout,this)};function H(n,t){for(var e=0;e=t)return a+Math.min(i,t-r);if(r+=o-a,a=o+1,(r+=e-r%e)>=t)return a}}var V=[""];function G(n){for(;V.length<=n;)V.push($(V)+" ");return V[n]}function $(n){return n[n.length-1]}function J(n,t){for(var e=[],a=0;a"€"&&(n.toUpperCase()!=n.toLowerCase()||tn.test(n))}function an(n,t){return t?!!(t.source.indexOf("\\w")>-1&&en(n))||t.test(n):en(n)}function rn(n){for(var t in n)if(n.hasOwnProperty(t)&&n[t])return!1;return!0}var on=/[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/;function ln(n){return n.charCodeAt(0)>=768&&on.test(n)}function sn(n,t,e){for(;(e<0?t>0:te?-1:1;;){if(t==e)return t;var r=(t+e)/2,o=a<0?Math.ceil(r):Math.floor(r);if(o==t)return n(o)?t:e;n(o)?e=o:t=o+a}}var dn=null;function pn(n,t,e){var a;dn=null;for(var r=0;rt)return r;o.to==t&&(o.from!=o.to&&"before"==e?a=r:dn=r),o.from==t&&(o.from!=o.to&&"before"!=e?a=r:dn=r)}return null!=a?a:dn}var bn=function(){var n=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,t=/[stwN]/,e=/[LRr]/,a=/[Lb1n]/,r=/[1n]/;function o(n,t,e){this.level=n,this.from=t,this.to=e}return function(i,l){var s="ltr"==l?"L":"R";if(0==i.length||"ltr"==l&&!n.test(i))return!1;for(var c,d=i.length,p=[],b=0;b-1&&(a[t]=r.slice(0,o).concat(r.slice(o+1)))}}}function xn(n,t){var e=hn(n,t);if(e.length)for(var a=Array.prototype.slice.call(arguments,2),r=0;r0}function yn(n){n.prototype.on=function(n,t){mn(this,n,t)},n.prototype.off=function(n,t){gn(this,n,t)}}function zn(n){n.preventDefault?n.preventDefault():n.returnValue=!1}function _n(n){n.stopPropagation?n.stopPropagation():n.cancelBubble=!0}function Fn(n){return null!=n.defaultPrevented?n.defaultPrevented:0==n.returnValue}function En(n){zn(n),_n(n)}function Cn(n){return n.target||n.srcElement}function Sn(n){var t=n.which;return null==t&&(1&n.button?t=1:2&n.button?t=3:4&n.button&&(t=2)),w&&n.ctrlKey&&1==t&&(t=3),t}var On,Dn,In=function(){if(i&&l<9)return!1;var n=D("div");return"draggable"in n||"dragDrop"in n}();function Mn(n){if(null==On){var t=D("span","​");O(n,D("span",[t,document.createTextNode("x")])),0!=n.firstChild.offsetHeight&&(On=t.offsetWidth<=1&&t.offsetHeight>2&&!(i&&l<8))}var e=On?D("span","​"):D("span"," ",null,"display: inline-block; width: 1px; margin-right: -1px");return e.setAttribute("cm-text",""),e}function Pn(n){if(null!=Dn)return Dn;var t=O(n,document.createTextNode("AخA")),e=E(t,0,1).getBoundingClientRect(),a=E(t,1,2).getBoundingClientRect();return S(n),!(!e||e.left==e.right)&&(Dn=a.right-e.right<3)}var jn,Tn=3!="\n\nb".split(/\n/).length?function(n){for(var t=0,e=[],a=n.length;t<=a;){var r=n.indexOf("\n",t);-1==r&&(r=n.length);var o=n.slice(t,"\r"==n.charAt(r-1)?r-1:r),i=o.indexOf("\r");-1!=i?(e.push(o.slice(0,i)),t+=i+1):(e.push(o),t=r+1)}return e}:function(n){return n.split(/\r\n?|\n/)},Bn=window.getSelection?function(n){try{return n.selectionStart!=n.selectionEnd}catch(n){return!1}}:function(n){var t;try{t=n.ownerDocument.selection.createRange()}catch(n){}return!(!t||t.parentElement()!=n)&&0!=t.compareEndPoints("StartToEnd",t)},Ln="oncopy"in(jn=D("div"))||(jn.setAttribute("oncopy","return;"),"function"==typeof jn.oncopy),An=null,Nn={},Rn={};function Yn(n,t){arguments.length>2&&(t.dependencies=Array.prototype.slice.call(arguments,2)),Nn[n]=t}function Un(n){if("string"==typeof n&&Rn.hasOwnProperty(n))n=Rn[n];else if(n&&"string"==typeof n.name&&Rn.hasOwnProperty(n.name)){var t=Rn[n.name];"string"==typeof t&&(t={name:t}),(n=nn(t,n)).name=t.name}else{if("string"==typeof n&&/^[\w\-]+\/[\w\-]+\+xml$/.test(n))return Un("application/xml");if("string"==typeof n&&/^[\w\-]+\/[\w\-]+\+json$/.test(n))return Un("application/json")}return"string"==typeof n?{name:n}:n||{name:"null"}}function Hn(n,t){t=Un(t);var e=Nn[t.name];if(!e)return Hn(n,"text/plain");var a=e(n,t);if(Wn.hasOwnProperty(t.name)){var r=Wn[t.name];for(var o in r)r.hasOwnProperty(o)&&(a.hasOwnProperty(o)&&(a["_"+o]=a[o]),a[o]=r[o])}if(a.name=t.name,t.helperType&&(a.helperType=t.helperType),t.modeProps)for(var i in t.modeProps)a[i]=t.modeProps[i];return a}var Wn={};function Xn(n,t){R(t,Wn.hasOwnProperty(n)?Wn[n]:Wn[n]={})}function qn(n,t){if(!0===t)return t;if(n.copyState)return n.copyState(t);var e={};for(var a in t){var r=t[a];r instanceof Array&&(r=r.concat([])),e[a]=r}return e}function Zn(n,t){for(var e;n.innerMode&&(e=n.innerMode(t))&&e.mode!=n;)t=e.state,n=e.mode;return e||{mode:n,state:t}}function Kn(n,t,e){return!n.startState||n.startState(t,e)}var Vn=function(n,t,e){this.pos=this.start=0,this.string=n,this.tabSize=t||8,this.lastColumnPos=this.lastColumnValue=0,this.lineStart=0,this.lineOracle=e};function Gn(n,t){if((t-=n.first)<0||t>=n.size)throw new Error("There is no line "+(t+n.first)+" in the document.");for(var e=n;!e.lines;)for(var a=0;;++a){var r=e.children[a],o=r.chunkSize();if(t=n.first&&te?rt(e,Gn(n,e).text.length):function(n,t){var e=n.ch;return null==e||e>t?rt(n.line,t):e<0?rt(n.line,0):n}(t,Gn(n,t.line).text.length)}function bt(n,t){for(var e=[],a=0;a=this.string.length},Vn.prototype.sol=function(){return this.pos==this.lineStart},Vn.prototype.peek=function(){return this.string.charAt(this.pos)||void 0},Vn.prototype.next=function(){if(this.post},Vn.prototype.eatSpace=function(){for(var n=this.pos;/[\s\u00a0]/.test(this.string.charAt(this.pos));)++this.pos;return this.pos>n},Vn.prototype.skipToEnd=function(){this.pos=this.string.length},Vn.prototype.skipTo=function(n){var t=this.string.indexOf(n,this.pos);if(t>-1)return this.pos=t,!0},Vn.prototype.backUp=function(n){this.pos-=n},Vn.prototype.column=function(){return this.lastColumnPos0?null:(a&&!1!==t&&(this.pos+=a[0].length),a)}var r=function(n){return e?n.toLowerCase():n};if(r(this.string.substr(this.pos,n.length))==r(n))return!1!==t&&(this.pos+=n.length),!0},Vn.prototype.current=function(){return this.string.slice(this.start,this.pos)},Vn.prototype.hideFirstChars=function(n,t){this.lineStart+=n;try{return t()}finally{this.lineStart-=n}},Vn.prototype.lookAhead=function(n){var t=this.lineOracle;return t&&t.lookAhead(n)},Vn.prototype.baseToken=function(){var n=this.lineOracle;return n&&n.baseToken(this.pos)};var ut=function(n,t){this.state=n,this.lookAhead=t},ft=function(n,t,e,a){this.state=t,this.doc=n,this.line=e,this.maxLookAhead=a||0,this.baseTokens=null,this.baseTokenPos=1};function mt(n,t,e,a){var r=[n.state.modeGen],o={};_t(n,t.text,n.doc.mode,e,(function(n,t){return r.push(n,t)}),o,a);for(var i=e.state,l=function(a){e.baseTokens=r;var l=n.state.overlays[a],s=1,c=0;e.state=!0,_t(n,t.text,l.mode,e,(function(n,t){for(var e=s;cn&&r.splice(s,1,n,r[s+1],a),s+=2,c=Math.min(n,a)}if(t)if(l.opaque)r.splice(e,s-e,n,"overlay "+t),s=e+2;else for(;en.options.maxHighlightLength&&qn(n.doc.mode,a.state),o=mt(n,t,a);r&&(a.state=r),t.stateAfter=a.save(!r),t.styles=o.styles,o.classes?t.styleClasses=o.classes:t.styleClasses&&(t.styleClasses=null),e===n.doc.highlightFrontier&&(n.doc.modeFrontier=Math.max(n.doc.modeFrontier,++n.doc.highlightFrontier))}return t.styles}function gt(n,t,e){var a=n.doc,r=n.display;if(!a.mode.startState)return new ft(a,!0,t);var o=function(n,t,e){for(var a,r,o=n.doc,i=e?-1:t-(n.doc.mode.innerMode?1e3:100),l=t;l>i;--l){if(l<=o.first)return o.first;var s=Gn(o,l-1),c=s.stateAfter;if(c&&(!e||l+(c instanceof ut?c.lookAhead:0)<=o.modeFrontier))return l;var d=Y(s.text,null,n.options.tabSize);(null==r||a>d)&&(r=l-1,a=d)}return r}(n,t,e),i=o>a.first&&Gn(a,o-1).stateAfter,l=i?ft.fromSaved(a,i,o):new ft(a,Kn(a.mode),o);return a.iter(o,t,(function(e){xt(n,e.text,l);var a=l.line;e.stateAfter=a==t-1||a%5==0||a>=r.viewFrom&&at.start)return o}throw new Error("Mode "+n.name+" failed to advance stream.")}ft.prototype.lookAhead=function(n){var t=this.doc.getLine(this.line+n);return null!=t&&n>this.maxLookAhead&&(this.maxLookAhead=n),t},ft.prototype.baseToken=function(n){if(!this.baseTokens)return null;for(;this.baseTokens[this.baseTokenPos]<=n;)this.baseTokenPos+=2;var t=this.baseTokens[this.baseTokenPos+1];return{type:t&&t.replace(/( |^)overlay .*/,""),size:this.baseTokens[this.baseTokenPos]-n}},ft.prototype.nextLine=function(){this.line++,this.maxLookAhead>0&&this.maxLookAhead--},ft.fromSaved=function(n,t,e){return t instanceof ut?new ft(n,qn(n.mode,t.state),e,t.lookAhead):new ft(n,qn(n.mode,t),e)},ft.prototype.save=function(n){var t=!1!==n?qn(this.doc.mode,this.state):this.state;return this.maxLookAhead>0?new ut(t,this.maxLookAhead):t};var vt=function(n,t,e){this.start=n.start,this.end=n.pos,this.string=n.current(),this.type=t||null,this.state=e};function yt(n,t,e,a){var r,o,i=n.doc,l=i.mode,s=Gn(i,(t=pt(i,t)).line),c=gt(n,t.line,e),d=new Vn(s.text,n.options.tabSize,c);for(a&&(o=[]);(a||d.posn.options.maxHighlightLength?(l=!1,i&&xt(n,t,a,p.pos),p.pos=t.length,s=null):s=zt(kt(e,p,a.state,b),o),b){var u=b[0].name;u&&(s="m-"+(s?u+" "+s:u))}if(!l||d!=s){for(;c=t:o.to>t);(a||(a=[])).push(new Ct(i,o.from,l?null:o.to))}}return a}(e,r,i),s=function(n,t,e){var a;if(n)for(var r=0;r=t:o.to>t)||o.from==t&&"bookmark"==i.type&&(!e||o.marker.insertLeft)){var l=null==o.from||(i.inclusiveLeft?o.from<=t:o.from0&&l)for(var w=0;wt)&&(!e||Bt(e,o.marker)<0)&&(e=o.marker)}return e}function Yt(n,t,e,a,r){var o=Gn(n,t),i=Et&&o.markedSpans;if(i)for(var l=0;l=0&&p<=0||d<=0&&p>=0)&&(d<=0&&(s.marker.inclusiveRight&&r.inclusiveLeft?ot(c.to,e)>=0:ot(c.to,e)>0)||d>=0&&(s.marker.inclusiveRight&&r.inclusiveLeft?ot(c.from,a)<=0:ot(c.from,a)<0)))return!0}}}function Ut(n){for(var t;t=At(n);)n=t.find(-1,!0).line;return n}function Ht(n,t){var e=Gn(n,t),a=Ut(e);return e==a?t:nt(a)}function Wt(n,t){if(t>n.lastLine())return t;var e,a=Gn(n,t);if(!Xt(n,a))return t;for(;e=Nt(a);)a=e.find(1,!0).line;return nt(a)+1}function Xt(n,t){var e=Et&&t.markedSpans;if(e)for(var a=void 0,r=0;rt.maxLineLength&&(t.maxLineLength=e,t.maxLine=n)}))}var Gt=function(n,t,e){this.text=n,Pt(this,t),this.height=e?e(this):1};function $t(n){n.parent=null,Mt(n)}Gt.prototype.lineNo=function(){return nt(this)},yn(Gt);var Jt={},Qt={};function ne(n,t){if(!n||/^\s*$/.test(n))return null;var e=t.addModeClass?Qt:Jt;return e[n]||(e[n]=n.replace(/\S+/g,"cm-$&"))}function te(n,t){var e=I("span",null,null,s?"padding-right: .1px":null),a={pre:I("pre",[e],"CodeMirror-line"),content:e,col:0,pos:0,cm:n,trailingSpace:!1,splitSpaces:n.getOption("lineWrapping")};t.measure={};for(var r=0;r<=(t.rest?t.rest.length:0);r++){var o=r?t.rest[r-1]:t.line,i=void 0;a.pos=0,a.addToken=ae,Pn(n.display.measure)&&(i=un(o,n.doc.direction))&&(a.addToken=re(a.addToken,i)),a.map=[],ie(o,a,ht(n,o,t!=n.display.externalMeasured&&nt(o))),o.styleClasses&&(o.styleClasses.bgClass&&(a.bgClass=T(o.styleClasses.bgClass,a.bgClass||"")),o.styleClasses.textClass&&(a.textClass=T(o.styleClasses.textClass,a.textClass||""))),0==a.map.length&&a.map.push(0,0,a.content.appendChild(Mn(n.display.measure))),0==r?(t.measure.map=a.map,t.measure.cache={}):((t.measure.maps||(t.measure.maps=[])).push(a.map),(t.measure.caches||(t.measure.caches=[])).push({}))}if(s){var l=a.content.lastChild;(/\bcm-tab\b/.test(l.className)||l.querySelector&&l.querySelector(".cm-tab"))&&(a.content.className="cm-tab-wrap-hack")}return xn(n,"renderLine",n,t.line,a.pre),a.pre.className&&(a.textClass=T(a.pre.className,a.textClass||"")),a}function ee(n){var t=D("span","•","cm-invalidchar");return t.title="\\u"+n.charCodeAt(0).toString(16),t.setAttribute("aria-label",t.title),t}function ae(n,t,e,a,r,o,s){if(t){var c,d=n.splitSpaces?function(n,t){if(n.length>1&&!/ /.test(n))return n;for(var e=t,a="",r=0;rc&&p.from<=c);b++);if(p.to>=d)return n(e,a,r,o,i,l,s);n(e,a.slice(0,p.to-c),r,o,null,l,s),o=null,a=a.slice(p.to-c),c=p.to}}}function oe(n,t,e,a){var r=!a&&e.widgetNode;r&&n.map.push(n.pos,n.pos+t,r),!a&&n.cm.display.input.needsContentAttribute&&(r||(r=n.content.appendChild(document.createElement("span"))),r.setAttribute("cm-marker",e.id)),r&&(n.cm.display.input.setUneditable(r),n.content.appendChild(r)),n.pos+=t,n.trailingSpace=!1}function ie(n,t,e){var a=n.markedSpans,r=n.text,o=0;if(a)for(var i,l,s,c,d,p,b,u=r.length,f=0,m=1,h="",g=0;;){if(g==f){s=c=d=l="",b=null,p=null,g=1/0;for(var x=[],w=void 0,k=0;kf||y.collapsed&&v.to==f&&v.from==f)){if(null!=v.to&&v.to!=f&&g>v.to&&(g=v.to,c=""),y.className&&(s+=" "+y.className),y.css&&(l=(l?l+";":"")+y.css),y.startStyle&&v.from==f&&(d+=" "+y.startStyle),y.endStyle&&v.to==g&&(w||(w=[])).push(y.endStyle,v.to),y.title&&((b||(b={})).title=y.title),y.attributes)for(var z in y.attributes)(b||(b={}))[z]=y.attributes[z];y.collapsed&&(!p||Bt(p.marker,y)<0)&&(p=v)}else v.from>f&&g>v.from&&(g=v.from)}if(w)for(var _=0;_=u)break;for(var E=Math.min(u,g);;){if(h){var C=f+h.length;if(!p){var S=C>E?h.slice(0,E-f):h;t.addToken(t,S,i?i+s:s,d,f+S.length==g?c:"",l,b)}if(C>=E){h=h.slice(E-f),f=E;break}f=C,d=""}h=r.slice(o,o=e[m++]),i=ne(e[m++],t.cm.options)}}else for(var O=1;Oe)return{map:n.measure.maps[r],cache:n.measure.caches[r],before:!0}}}function Pe(n,t,e,a){return Be(n,Te(n,t),e,a)}function je(n,t){if(t>=n.display.viewFrom&&t=e.lineN&&t2&&o.push((s.bottom+c.top)/2-e.top)}}o.push(e.bottom-e.top)}}(n,t.view,t.rect),t.hasHeights=!0),(o=function(n,t,e,a){var r,o=Ne(t.map,e,a),s=o.node,c=o.start,d=o.end,p=o.collapse;if(3==s.nodeType){for(var b=0;b<4;b++){for(;c&&ln(t.line.text.charAt(o.coverStart+c));)--c;for(;o.coverStart+d1}(n))return t;var e=screen.logicalXDPI/screen.deviceXDPI,a=screen.logicalYDPI/screen.deviceYDPI;return{left:t.left*e,right:t.right*e,top:t.top*a,bottom:t.bottom*a}}(n.display.measure,r))}else{var u;c>0&&(p=a="right"),r=n.options.lineWrapping&&(u=s.getClientRects()).length>1?u["right"==a?u.length-1:0]:s.getBoundingClientRect()}if(i&&l<9&&!c&&(!r||!r.left&&!r.right)){var f=s.parentNode.getClientRects()[0];r=f?{left:f.left,right:f.left+la(n.display),top:f.top,bottom:f.bottom}:Ae}for(var m=r.top-t.rect.top,h=r.bottom-t.rect.top,g=(m+h)/2,x=t.view.measure.heights,w=0;wt)&&(r=(o=s-l)-1,t>=s&&(i="right")),null!=r){if(a=n[c+2],l==s&&e==(a.insertLeft?"left":"right")&&(i=e),"left"==e&&0==r)for(;c&&n[c-2]==n[c-3]&&n[c-1].insertLeft;)a=n[2+(c-=3)],i="left";if("right"==e&&r==s-l)for(;c=0&&(e=n[r]).left==e.right;r--);return e}function Ye(n){if(n.measure&&(n.measure.cache={},n.measure.heights=null,n.rest))for(var t=0;t=a.text.length?(s=a.text.length,c="before"):s<=0&&(s=0,c="after"),!l)return i("before"==c?s-1:s,"before"==c);function d(n,t,e){return i(e?n-1:n,1==l[t].level!=e)}var p=pn(l,s,c),b=dn,u=d(s,p,"before"==c);return null!=b&&(u.other=d(s,b,"before"!=c)),u}function $e(n,t){var e=0;t=pt(n.doc,t),n.options.lineWrapping||(e=la(n.display)*t.ch);var a=Gn(n.doc,t.line),r=Zt(a)+Ee(n.display);return{left:e,right:e,top:r,bottom:r+a.height}}function Je(n,t,e,a,r){var o=rt(n,t,e);return o.xRel=r,a&&(o.outside=a),o}function Qe(n,t,e){var a=n.doc;if((e+=n.display.viewOffset)<0)return Je(a.first,0,null,-1,-1);var r=tt(a,e),o=a.first+a.size-1;if(r>o)return Je(a.first+a.size-1,Gn(a,o).text.length,null,1,1);t<0&&(t=0);for(var i=Gn(a,r);;){var l=aa(n,i,r,t,e),s=Rt(i,l.ch+(l.xRel>0||l.outside>0?1:0));if(!s)return l;var c=s.find(1);if(c.line==r)return c;i=Gn(a,r=c.line)}}function na(n,t,e,a){a-=qe(t);var r=t.text.length,o=cn((function(t){return Be(n,e,t-1).bottom<=a}),r,0);return{begin:o,end:r=cn((function(t){return Be(n,e,t).top>a}),o,r)}}function ta(n,t,e,a){return e||(e=Te(n,t)),na(n,t,e,Ze(n,t,Be(n,e,a),"line").top)}function ea(n,t,e,a){return!(n.bottom<=e)&&(n.top>e||(a?n.left:n.right)>t)}function aa(n,t,e,a,r){r-=Zt(t);var o=Te(n,t),i=qe(t),l=0,s=t.text.length,c=!0,d=un(t,n.doc.direction);if(d){var p=(n.options.lineWrapping?oa:ra)(n,t,e,o,d,a,r);l=(c=1!=p.level)?p.from:p.to-1,s=c?p.to:p.from-1}var b,u,f=null,m=null,h=cn((function(t){var e=Be(n,o,t);return e.top+=i,e.bottom+=i,!!ea(e,a,r,!1)&&(e.top<=r&&e.left<=a&&(f=t,m=e),!0)}),l,s),g=!1;if(m){var x=a-m.left=k.bottom?1:0}return Je(e,h=sn(t.text,h,1),u,g,a-b)}function ra(n,t,e,a,r,o,i){var l=cn((function(l){var s=r[l],c=1!=s.level;return ea(Ge(n,rt(e,c?s.to:s.from,c?"before":"after"),"line",t,a),o,i,!0)}),0,r.length-1),s=r[l];if(l>0){var c=1!=s.level,d=Ge(n,rt(e,c?s.from:s.to,c?"after":"before"),"line",t,a);ea(d,o,i,!0)&&d.top>i&&(s=r[l-1])}return s}function oa(n,t,e,a,r,o,i){var l=na(n,t,a,i),s=l.begin,c=l.end;/\s/.test(t.text.charAt(c-1))&&c--;for(var d=null,p=null,b=0;b=c||u.to<=s)){var f=Be(n,a,1!=u.level?Math.min(c,u.to)-1:Math.max(s,u.from)).right,m=fm)&&(d=u,p=m)}}return d||(d=r[r.length-1]),d.fromc&&(d={from:d.from,to:c,level:d.level}),d}function ia(n){if(null!=n.cachedTextHeight)return n.cachedTextHeight;if(null==Le){Le=D("pre",null,"CodeMirror-line-like");for(var t=0;t<49;++t)Le.appendChild(document.createTextNode("x")),Le.appendChild(D("br"));Le.appendChild(document.createTextNode("x"))}O(n.measure,Le);var e=Le.offsetHeight/50;return e>3&&(n.cachedTextHeight=e),S(n.measure),e||1}function la(n){if(null!=n.cachedCharWidth)return n.cachedCharWidth;var t=D("span","xxxxxxxxxx"),e=D("pre",[t],"CodeMirror-line-like");O(n.measure,e);var a=t.getBoundingClientRect(),r=(a.right-a.left)/10;return r>2&&(n.cachedCharWidth=r),r||10}function sa(n){for(var t=n.display,e={},a={},r=t.gutters.clientLeft,o=t.gutters.firstChild,i=0;o;o=o.nextSibling,++i){var l=n.display.gutterSpecs[i].className;e[l]=o.offsetLeft+o.clientLeft+r,a[l]=o.clientWidth}return{fixedPos:ca(t),gutterTotalWidth:t.gutters.offsetWidth,gutterLeft:e,gutterWidth:a,wrapperWidth:t.wrapper.clientWidth}}function ca(n){return n.scroller.getBoundingClientRect().left-n.sizer.getBoundingClientRect().left}function da(n){var t=ia(n.display),e=n.options.lineWrapping,a=e&&Math.max(5,n.display.scroller.clientWidth/la(n.display)-3);return function(r){if(Xt(n.doc,r))return 0;var o=0;if(r.widgets)for(var i=0;i0&&(s=Gn(n.doc,c.line).text).length==c.ch){var d=Y(s,s.length,n.options.tabSize)-s.length;c=rt(c.line,Math.max(0,Math.round((o-Se(n.display).left)/la(n.display))-d))}return c}function ua(n,t){if(t>=n.display.viewTo)return null;if((t-=n.display.viewFrom)<0)return null;for(var e=n.display.view,a=0;at)&&(r.updateLineNumbers=t),n.curOp.viewChanged=!0,t>=r.viewTo)Et&&Ht(n.doc,t)r.viewFrom?ha(n):(r.viewFrom+=a,r.viewTo+=a);else if(t<=r.viewFrom&&e>=r.viewTo)ha(n);else if(t<=r.viewFrom){var o=ga(n,e,e+a,1);o?(r.view=r.view.slice(o.index),r.viewFrom=o.lineN,r.viewTo+=a):ha(n)}else if(e>=r.viewTo){var i=ga(n,t,t,-1);i?(r.view=r.view.slice(0,i.index),r.viewTo=i.lineN):ha(n)}else{var l=ga(n,t,t,-1),s=ga(n,e,e+a,1);l&&s?(r.view=r.view.slice(0,l.index).concat(se(n,l.lineN,s.lineN)).concat(r.view.slice(s.index)),r.viewTo+=a):ha(n)}var c=r.externalMeasured;c&&(e=r.lineN&&t=a.viewTo)){var o=a.view[ua(n,t)];if(null!=o.node){var i=o.changes||(o.changes=[]);-1==H(i,e)&&i.push(e)}}}function ha(n){n.display.viewFrom=n.display.viewTo=n.doc.first,n.display.view=[],n.display.viewOffset=0}function ga(n,t,e,a){var r,o=ua(n,t),i=n.display.view;if(!Et||e==n.doc.first+n.doc.size)return{index:o,lineN:e};for(var l=n.display.viewFrom,s=0;s0){if(o==i.length-1)return null;r=l+i[o].size-t,o++}else r=l-t;t+=r,e+=r}for(;Ht(n.doc,e)!=e;){if(o==(a<0?0:i.length-1))return null;e+=a*i[o-(a<0?1:0)].size,o+=a}return{index:o,lineN:e}}function xa(n){for(var t=n.display.view,e=0,a=0;a=n.display.viewTo||s.to().line0?i:n.defaultCharWidth())+"px"}if(a.other){var l=e.appendChild(D("div"," ","CodeMirror-cursor CodeMirror-secondarycursor"));l.style.display="",l.style.left=a.other.left+"px",l.style.top=a.other.top+"px",l.style.height=.85*(a.other.bottom-a.other.top)+"px"}}function ya(n,t){return n.top-t.top||n.left-t.left}function za(n,t,e){var a=n.display,r=n.doc,o=document.createDocumentFragment(),i=Se(n.display),l=i.left,s=Math.max(a.sizerWidth,De(n)-a.sizer.offsetLeft)-i.right,c="ltr"==r.direction;function d(n,t,e,a){t<0&&(t=0),t=Math.round(t),a=Math.round(a),o.appendChild(D("div",null,"CodeMirror-selected","position: absolute; left: "+n+"px;\n top: "+t+"px; width: "+(null==e?s-n:e)+"px;\n height: "+(a-t)+"px"))}function p(t,e,a){var o,i,p=Gn(r,t),b=p.text.length;function u(e,a){return Ve(n,rt(t,e),"div",p,a)}function f(t,e,a){var r=ta(n,p,null,t),o="ltr"==e==("after"==a)?"left":"right";return u("after"==a?r.begin:r.end-(/\s/.test(p.text.charAt(r.end-1))?2:1),o)[o]}var m=un(p,r.direction);return function(n,t,e,a){if(!n)return a(t,e,"ltr",0);for(var r=!1,o=0;ot||t==e&&i.to==t)&&(a(Math.max(i.from,t),Math.min(i.to,e),1==i.level?"rtl":"ltr",o),r=!0)}r||a(t,e,"ltr")}(m,e||0,null==a?b:a,(function(n,t,r,p){var h="ltr"==r,g=u(n,h?"left":"right"),x=u(t-1,h?"right":"left"),w=null==e&&0==n,k=null==a&&t==b,v=0==p,y=!m||p==m.length-1;if(x.top-g.top<=3){var z=(c?k:w)&&y,_=(c?w:k)&&v?l:(h?g:x).left,F=z?s:(h?x:g).right;d(_,g.top,F-_,g.bottom)}else{var E,C,S,O;h?(E=c&&w&&v?l:g.left,C=c?s:f(n,r,"before"),S=c?l:f(t,r,"after"),O=c&&k&&y?s:x.right):(E=c?f(n,r,"before"):l,C=!c&&w&&v?s:g.right,S=!c&&k&&y?l:x.left,O=c?f(t,r,"after"):s),d(E,g.top,C-E,g.bottom),g.bottom0?t.blinker=setInterval((function(){n.hasFocus()||Sa(n),t.cursorDiv.style.visibility=(e=!e)?"":"hidden"}),n.options.cursorBlinkRate):n.options.cursorBlinkRate<0&&(t.cursorDiv.style.visibility="hidden")}}function Fa(n){n.hasFocus()||(n.display.input.focus(),n.state.focused||Ca(n))}function Ea(n){n.state.delayingBlurEvent=!0,setTimeout((function(){n.state.delayingBlurEvent&&(n.state.delayingBlurEvent=!1,n.state.focused&&Sa(n))}),100)}function Ca(n,t){n.state.delayingBlurEvent&&!n.state.draggingText&&(n.state.delayingBlurEvent=!1),"nocursor"!=n.options.readOnly&&(n.state.focused||(xn(n,"focus",n,t),n.state.focused=!0,j(n.display.wrapper,"CodeMirror-focused"),n.curOp||n.display.selForContextMenu==n.doc.sel||(n.display.input.reset(),s&&setTimeout((function(){return n.display.input.reset(!0)}),20)),n.display.input.receivedFocus()),_a(n))}function Sa(n,t){n.state.delayingBlurEvent||(n.state.focused&&(xn(n,"blur",n,t),n.state.focused=!1,C(n.display.wrapper,"CodeMirror-focused")),clearInterval(n.display.blinker),setTimeout((function(){n.state.focused||(n.display.shift=!1)}),150))}function Oa(n){for(var t=n.display,e=t.lineDiv.offsetTop,a=Math.max(0,t.scroller.getBoundingClientRect().top),r=t.lineDiv.getBoundingClientRect().top,o=0,s=0;s.005||m<-.005)&&(rn.display.sizerWidth){var g=Math.ceil(b/la(n.display));g>n.display.maxLineLength&&(n.display.maxLineLength=g,n.display.maxLine=c.line,n.display.maxLineChanged=!0)}}}Math.abs(o)>2&&(t.scroller.scrollTop+=o)}function Da(n){if(n.widgets)for(var t=0;t=i&&(o=tt(t,Zt(Gn(t,s))-n.wrapper.clientHeight),i=s)}return{from:o,to:Math.max(i,o+1)}}function Ma(n,t){var e=n.display,a=ia(n.display);t.top<0&&(t.top=0);var r=n.curOp&&null!=n.curOp.scrollTop?n.curOp.scrollTop:e.scroller.scrollTop,o=Ie(n),i={};t.bottom-t.top>o&&(t.bottom=t.top+o);var l=n.doc.height+Ce(e),s=t.topl-a;if(t.topr+o){var d=Math.min(t.top,(c?l:t.bottom)-o);d!=r&&(i.scrollTop=d)}var p=n.options.fixedGutter?0:e.gutters.offsetWidth,b=n.curOp&&null!=n.curOp.scrollLeft?n.curOp.scrollLeft:e.scroller.scrollLeft-p,u=De(n)-e.gutters.offsetWidth,f=t.right-t.left>u;return f&&(t.right=t.left+u),t.left<10?i.scrollLeft=0:t.leftu+b-3&&(i.scrollLeft=t.right+(f?0:10)-u),i}function Pa(n,t){null!=t&&(Ba(n),n.curOp.scrollTop=(null==n.curOp.scrollTop?n.doc.scrollTop:n.curOp.scrollTop)+t)}function ja(n){Ba(n);var t=n.getCursor();n.curOp.scrollToPos={from:t,to:t,margin:n.options.cursorScrollMargin}}function Ta(n,t,e){null==t&&null==e||Ba(n),null!=t&&(n.curOp.scrollLeft=t),null!=e&&(n.curOp.scrollTop=e)}function Ba(n){var t=n.curOp.scrollToPos;t&&(n.curOp.scrollToPos=null,La(n,$e(n,t.from),$e(n,t.to),t.margin))}function La(n,t,e,a){var r=Ma(n,{left:Math.min(t.left,e.left),top:Math.min(t.top,e.top)-a,right:Math.max(t.right,e.right),bottom:Math.max(t.bottom,e.bottom)+a});Ta(n,r.scrollLeft,r.scrollTop)}function Aa(n,t){Math.abs(n.doc.scrollTop-t)<2||(e||br(n,{top:t}),Na(n,t,!0),e&&br(n),ir(n,100))}function Na(n,t,e){t=Math.max(0,Math.min(n.display.scroller.scrollHeight-n.display.scroller.clientHeight,t)),(n.display.scroller.scrollTop!=t||e)&&(n.doc.scrollTop=t,n.display.scrollbars.setScrollTop(t),n.display.scroller.scrollTop!=t&&(n.display.scroller.scrollTop=t))}function Ra(n,t,e,a){t=Math.max(0,Math.min(t,n.display.scroller.scrollWidth-n.display.scroller.clientWidth)),(e?t==n.doc.scrollLeft:Math.abs(n.doc.scrollLeft-t)<2)&&!a||(n.doc.scrollLeft=t,mr(n),n.display.scroller.scrollLeft!=t&&(n.display.scroller.scrollLeft=t),n.display.scrollbars.setScrollLeft(t))}function Ya(n){var t=n.display,e=t.gutters.offsetWidth,a=Math.round(n.doc.height+Ce(n.display));return{clientHeight:t.scroller.clientHeight,viewHeight:t.wrapper.clientHeight,scrollWidth:t.scroller.scrollWidth,clientWidth:t.scroller.clientWidth,viewWidth:t.wrapper.clientWidth,barLeft:n.options.fixedGutter?e:0,docHeight:a,scrollHeight:a+Oe(n)+t.barHeight,nativeBarWidth:t.nativeBarWidth,gutterWidth:e}}var Ua=function(n,t,e){this.cm=e;var a=this.vert=D("div",[D("div",null,null,"min-width: 1px")],"CodeMirror-vscrollbar"),r=this.horiz=D("div",[D("div",null,null,"height: 100%; min-height: 1px")],"CodeMirror-hscrollbar");a.tabIndex=r.tabIndex=-1,n(a),n(r),mn(a,"scroll",(function(){a.clientHeight&&t(a.scrollTop,"vertical")})),mn(r,"scroll",(function(){r.clientWidth&&t(r.scrollLeft,"horizontal")})),this.checkedZeroWidth=!1,i&&l<8&&(this.horiz.style.minHeight=this.vert.style.minWidth="18px")};Ua.prototype.update=function(n){var t=n.scrollWidth>n.clientWidth+1,e=n.scrollHeight>n.clientHeight+1,a=n.nativeBarWidth;if(e){this.vert.style.display="block",this.vert.style.bottom=t?a+"px":"0";var r=n.viewHeight-(t?a:0);this.vert.firstChild.style.height=Math.max(0,n.scrollHeight-n.clientHeight+r)+"px"}else this.vert.scrollTop=0,this.vert.style.display="",this.vert.firstChild.style.height="0";if(t){this.horiz.style.display="block",this.horiz.style.right=e?a+"px":"0",this.horiz.style.left=n.barLeft+"px";var o=n.viewWidth-n.barLeft-(e?a:0);this.horiz.firstChild.style.width=Math.max(0,n.scrollWidth-n.clientWidth+o)+"px"}else this.horiz.style.display="",this.horiz.firstChild.style.width="0";return!this.checkedZeroWidth&&n.clientHeight>0&&(0==a&&this.zeroWidthHack(),this.checkedZeroWidth=!0),{right:e?a:0,bottom:t?a:0}},Ua.prototype.setScrollLeft=function(n){this.horiz.scrollLeft!=n&&(this.horiz.scrollLeft=n),this.disableHoriz&&this.enableZeroWidthBar(this.horiz,this.disableHoriz,"horiz")},Ua.prototype.setScrollTop=function(n){this.vert.scrollTop!=n&&(this.vert.scrollTop=n),this.disableVert&&this.enableZeroWidthBar(this.vert,this.disableVert,"vert")},Ua.prototype.zeroWidthHack=function(){var n=w&&!f?"12px":"18px";this.horiz.style.height=this.vert.style.width=n,this.horiz.style.visibility=this.vert.style.visibility="hidden",this.disableHoriz=new U,this.disableVert=new U},Ua.prototype.enableZeroWidthBar=function(n,t,e){n.style.visibility="",t.set(1e3,(function a(){var r=n.getBoundingClientRect();("vert"==e?document.elementFromPoint(r.right-1,(r.top+r.bottom)/2):document.elementFromPoint((r.right+r.left)/2,r.bottom-1))!=n?n.style.visibility="hidden":t.set(1e3,a)}))},Ua.prototype.clear=function(){var n=this.horiz.parentNode;n.removeChild(this.horiz),n.removeChild(this.vert)};var Ha=function(){};function Wa(n,t){t||(t=Ya(n));var e=n.display.barWidth,a=n.display.barHeight;Xa(n,t);for(var r=0;r<4&&e!=n.display.barWidth||a!=n.display.barHeight;r++)e!=n.display.barWidth&&n.options.lineWrapping&&Oa(n),Xa(n,Ya(n)),e=n.display.barWidth,a=n.display.barHeight}function Xa(n,t){var e=n.display,a=e.scrollbars.update(t);e.sizer.style.paddingRight=(e.barWidth=a.right)+"px",e.sizer.style.paddingBottom=(e.barHeight=a.bottom)+"px",e.heightForcer.style.borderBottom=a.bottom+"px solid transparent",a.right&&a.bottom?(e.scrollbarFiller.style.display="block",e.scrollbarFiller.style.height=a.bottom+"px",e.scrollbarFiller.style.width=a.right+"px"):e.scrollbarFiller.style.display="",a.bottom&&n.options.coverGutterNextToScrollbar&&n.options.fixedGutter?(e.gutterFiller.style.display="block",e.gutterFiller.style.height=a.bottom+"px",e.gutterFiller.style.width=t.gutterWidth+"px"):e.gutterFiller.style.display=""}Ha.prototype.update=function(){return{bottom:0,right:0}},Ha.prototype.setScrollLeft=function(){},Ha.prototype.setScrollTop=function(){},Ha.prototype.clear=function(){};var qa={native:Ua,null:Ha};function Za(n){n.display.scrollbars&&(n.display.scrollbars.clear(),n.display.scrollbars.addClass&&C(n.display.wrapper,n.display.scrollbars.addClass)),n.display.scrollbars=new qa[n.options.scrollbarStyle]((function(t){n.display.wrapper.insertBefore(t,n.display.scrollbarFiller),mn(t,"mousedown",(function(){n.state.focused&&setTimeout((function(){return n.display.input.focus()}),0)})),t.setAttribute("cm-not-content","true")}),(function(t,e){"horizontal"==e?Ra(n,t):Aa(n,t)}),n),n.display.scrollbars.addClass&&j(n.display.wrapper,n.display.scrollbars.addClass)}var Ka=0;function Va(n){var t;n.curOp={cm:n,viewChanged:!1,startHeight:n.doc.height,forceUpdate:!1,updateInput:0,typing:!1,changeObjs:null,cursorActivityHandlers:null,cursorActivityCalled:0,selectionChanged:!1,updateMaxLine:!1,scrollLeft:null,scrollTop:null,scrollToPos:null,focus:!1,id:++Ka,markArrays:null},t=n.curOp,ce?ce.ops.push(t):t.ownsGroup=ce={ops:[t],delayedCallbacks:[]}}function Ga(n){var t=n.curOp;t&&function(n,t){var e=n.ownsGroup;if(e)try{!function(n){var t=n.delayedCallbacks,e=0;do{for(;e=e.viewTo)||e.maxLineChanged&&t.options.lineWrapping,n.update=n.mustUpdate&&new sr(t,n.mustUpdate&&{top:n.scrollTop,ensure:n.scrollToPos},n.forceUpdate)}function Ja(n){n.updatedDisplay=n.mustUpdate&&dr(n.cm,n.update)}function Qa(n){var t=n.cm,e=t.display;n.updatedDisplay&&Oa(t),n.barMeasure=Ya(t),e.maxLineChanged&&!t.options.lineWrapping&&(n.adjustWidthTo=Pe(t,e.maxLine,e.maxLine.text.length).left+3,t.display.sizerWidth=n.adjustWidthTo,n.barMeasure.scrollWidth=Math.max(e.scroller.clientWidth,e.sizer.offsetLeft+n.adjustWidthTo+Oe(t)+t.display.barWidth),n.maxScrollLeft=Math.max(0,e.sizer.offsetLeft+n.adjustWidthTo-De(t))),(n.updatedDisplay||n.selectionChanged)&&(n.preparedSelection=e.input.prepareSelection())}function nr(n){var t=n.cm;null!=n.adjustWidthTo&&(t.display.sizer.style.minWidth=n.adjustWidthTo+"px",n.maxScrollLeft(o.defaultView.innerHeight||o.documentElement.clientHeight)&&(r=!1),null!=r&&!m){var i=D("div","​",null,"position: absolute;\n top: "+(t.top-e.viewOffset-Ee(n.display))+"px;\n height: "+(t.bottom-t.top+Oe(n)+e.barHeight)+"px;\n left: "+t.left+"px; width: "+Math.max(2,t.right-t.left)+"px;");n.display.lineSpace.appendChild(i),i.scrollIntoView(r),n.display.lineSpace.removeChild(i)}}}(t,function(n,t,e,a){var r;null==a&&(a=0),n.options.lineWrapping||t!=e||(e="before"==t.sticky?rt(t.line,t.ch+1,"before"):t,t=t.ch?rt(t.line,"before"==t.sticky?t.ch-1:t.ch,"after"):t);for(var o=0;o<5;o++){var i=!1,l=Ge(n,t),s=e&&e!=t?Ge(n,e):l,c=Ma(n,r={left:Math.min(l.left,s.left),top:Math.min(l.top,s.top)-a,right:Math.max(l.left,s.left),bottom:Math.max(l.bottom,s.bottom)+a}),d=n.doc.scrollTop,p=n.doc.scrollLeft;if(null!=c.scrollTop&&(Aa(n,c.scrollTop),Math.abs(n.doc.scrollTop-d)>1&&(i=!0)),null!=c.scrollLeft&&(Ra(n,c.scrollLeft),Math.abs(n.doc.scrollLeft-p)>1&&(i=!0)),!i)break}return r}(t,pt(a,n.scrollToPos.from),pt(a,n.scrollToPos.to),n.scrollToPos.margin));var r=n.maybeHiddenMarkers,o=n.maybeUnhiddenMarkers;if(r)for(var i=0;i=n.display.viewTo)){var e=+new Date+n.options.workTime,a=gt(n,t.highlightFrontier),r=[];t.iter(a.line,Math.min(t.first+t.size,n.display.viewTo+500),(function(o){if(a.line>=n.display.viewFrom){var i=o.styles,l=o.text.length>n.options.maxHighlightLength?qn(t.mode,a.state):null,s=mt(n,o,a,!0);l&&(a.state=l),o.styles=s.styles;var c=o.styleClasses,d=s.classes;d?o.styleClasses=d:c&&(o.styleClasses=null);for(var p=!i||i.length!=o.styles.length||c!=d&&(!c||!d||c.bgClass!=d.bgClass||c.textClass!=d.textClass),b=0;!p&&be)return ir(n,n.options.workDelay),!0})),t.highlightFrontier=a.line,t.modeFrontier=Math.max(t.modeFrontier,a.line),r.length&&er(n,(function(){for(var t=0;t=e.viewFrom&&t.visible.to<=e.viewTo&&(null==e.updateLineNumbers||e.updateLineNumbers>=e.viewTo)&&e.renderedView==e.view&&0==xa(n))return!1;hr(n)&&(ha(n),t.dims=sa(n));var r=a.first+a.size,o=Math.max(t.visible.from-n.options.viewportMargin,a.first),i=Math.min(r,t.visible.to+n.options.viewportMargin);e.viewFromi&&e.viewTo-i<20&&(i=Math.min(r,e.viewTo)),Et&&(o=Ht(n.doc,o),i=Wt(n.doc,i));var l=o!=e.viewFrom||i!=e.viewTo||e.lastWrapHeight!=t.wrapperHeight||e.lastWrapWidth!=t.wrapperWidth;!function(n,t,e){var a=n.display;0==a.view.length||t>=a.viewTo||e<=a.viewFrom?(a.view=se(n,t,e),a.viewFrom=t):(a.viewFrom>t?a.view=se(n,t,a.viewFrom).concat(a.view):a.viewFrome&&(a.view=a.view.slice(0,ua(n,e)))),a.viewTo=e}(n,o,i),e.viewOffset=Zt(Gn(n.doc,e.viewFrom)),n.display.mover.style.top=e.viewOffset+"px";var c=xa(n);if(!l&&0==c&&!t.force&&e.renderedView==e.view&&(null==e.updateLineNumbers||e.updateLineNumbers>=e.viewTo))return!1;var d=cr(n);return c>4&&(e.lineDiv.style.display="none"),function(n,t,e){var a=n.display,r=n.options.lineNumbers,o=a.lineDiv,i=o.firstChild;function l(t){var e=t.nextSibling;return s&&w&&n.display.currentWheelTarget==t?t.style.display="none":t.parentNode.removeChild(t),e}for(var c=a.view,d=a.viewFrom,p=0;p-1&&(u=!1),ue(n,b,d,e)),u&&(S(b.lineNumber),b.lineNumber.appendChild(document.createTextNode(at(n.options,d)))),i=b.node.nextSibling}else{var f=ke(n,b,d,e);o.insertBefore(f,i)}d+=b.size}for(;i;)i=l(i)}(n,e.updateLineNumbers,t.dims),c>4&&(e.lineDiv.style.display=""),e.renderedView=e.view,function(n){if(n&&n.activeElt&&n.activeElt!=P(n.activeElt.ownerDocument)&&(n.activeElt.focus(),!/^(INPUT|TEXTAREA)$/.test(n.activeElt.nodeName)&&n.anchorNode&&M(document.body,n.anchorNode)&&M(document.body,n.focusNode))){var t=n.activeElt.ownerDocument,e=t.defaultView.getSelection(),a=t.createRange();a.setEnd(n.anchorNode,n.anchorOffset),a.collapse(!1),e.removeAllRanges(),e.addRange(a),e.extend(n.focusNode,n.focusOffset)}}(d),S(e.cursorDiv),S(e.selectionDiv),e.gutters.style.height=e.sizer.style.minHeight=0,l&&(e.lastWrapHeight=t.wrapperHeight,e.lastWrapWidth=t.wrapperWidth,ir(n,400)),e.updateLineNumbers=null,!0}function pr(n,t){for(var e=t.viewport,a=!0;;a=!1){if(a&&n.options.lineWrapping&&t.oldDisplayWidth!=De(n))a&&(t.visible=Ia(n.display,n.doc,e));else if(e&&null!=e.top&&(e={top:Math.min(n.doc.height+Ce(n.display)-Ie(n),e.top)}),t.visible=Ia(n.display,n.doc,e),t.visible.from>=n.display.viewFrom&&t.visible.to<=n.display.viewTo)break;if(!dr(n,t))break;Oa(n);var r=Ya(n);wa(n),Wa(n,r),fr(n,r),t.force=!1}t.signal(n,"update",n),n.display.viewFrom==n.display.reportedViewFrom&&n.display.viewTo==n.display.reportedViewTo||(t.signal(n,"viewportChange",n,n.display.viewFrom,n.display.viewTo),n.display.reportedViewFrom=n.display.viewFrom,n.display.reportedViewTo=n.display.viewTo)}function br(n,t){var e=new sr(n,t);if(dr(n,e)){Oa(n),pr(n,e);var a=Ya(n);wa(n),Wa(n,a),fr(n,a),e.finish()}}function ur(n){var t=n.gutters.offsetWidth;n.sizer.style.marginLeft=t+"px",pe(n,"gutterChanged",n)}function fr(n,t){n.display.sizer.style.minHeight=t.docHeight+"px",n.display.heightForcer.style.top=t.docHeight+"px",n.display.gutters.style.height=t.docHeight+n.display.barHeight+Oe(n)+"px"}function mr(n){var t=n.display,e=t.view;if(t.alignWidgets||t.gutters.firstChild&&n.options.fixedGutter){for(var a=ca(t)-t.scroller.scrollLeft+n.doc.scrollLeft,r=t.gutters.offsetWidth,o=a+"px",i=0;i=105&&(o.wrapper.style.clipPath="inset(0px)"),o.wrapper.setAttribute("translate","no"),i&&l<8&&(o.gutters.style.zIndex=-1,o.scroller.style.paddingRight=0),s||e&&x||(o.scroller.draggable=!0),n&&(n.appendChild?n.appendChild(o.wrapper):n(o.wrapper)),o.viewFrom=o.viewTo=t.first,o.reportedViewFrom=o.reportedViewTo=t.first,o.view=[],o.renderedView=null,o.externalMeasured=null,o.viewOffset=0,o.lastWrapHeight=o.lastWrapWidth=0,o.updateLineNumbers=null,o.nativeBarWidth=o.barHeight=o.barWidth=0,o.scrollbarsClipped=!1,o.lineNumWidth=o.lineNumInnerWidth=o.lineNumChars=null,o.alignWidgets=!1,o.cachedCharWidth=o.cachedTextHeight=o.cachedPaddingH=null,o.maxLine=null,o.maxLineLength=0,o.maxLineChanged=!1,o.wheelDX=o.wheelDY=o.wheelStartX=o.wheelStartY=null,o.shift=!1,o.selForContextMenu=null,o.activeTouch=null,o.gutterSpecs=gr(r.gutters,r.lineNumbers),xr(o),a.init(o)}sr.prototype.signal=function(n,t){vn(n,t)&&this.events.push(arguments)},sr.prototype.finish=function(){for(var n=0;nc.clientWidth,f=c.scrollHeight>c.clientHeight;if(r&&u||o&&f){if(o&&w&&s)n:for(var m=t.target,h=l.view;m!=c;m=m.parentNode)for(var g=0;g=0&&ot(n,a.to())<=0)return e}return-1};var Cr=function(n,t){this.anchor=n,this.head=t};function Sr(n,t,e){var a=n&&n.options.selectionsMayTouch,r=t[e];t.sort((function(n,t){return ot(n.from(),t.from())})),e=H(t,r);for(var o=1;o0:s>=0){var c=ct(l.from(),i.from()),d=st(l.to(),i.to()),p=l.empty()?i.from()==i.head:l.from()==l.head;o<=e&&--e,t.splice(--o,2,new Cr(p?d:c,p?c:d))}}return new Er(t,e)}function Or(n,t){return new Er([new Cr(n,t||n)],0)}function Dr(n){return n.text?rt(n.from.line+n.text.length-1,$(n.text).length+(1==n.text.length?n.from.ch:0)):n.to}function Ir(n,t){if(ot(n,t.from)<0)return n;if(ot(n,t.to)<=0)return Dr(t);var e=n.line+t.text.length-(t.to.line-t.from.line)-1,a=n.ch;return n.line==t.to.line&&(a+=Dr(t).ch-t.to.ch),rt(e,a)}function Mr(n,t){for(var e=[],a=0;a1&&n.remove(l.line+1,f-1),n.insert(l.line+1,g)}pe(n,"change",n,t)}function Ar(n,t,e){!function n(a,r,o){if(a.linked)for(var i=0;il-(n.cm?n.cm.options.historyEventDelay:500)||"*"==t.origin.charAt(0)))&&(o=function(n,t){return t?(Hr(n.done),$(n.done)):n.done.length&&!$(n.done).ranges?$(n.done):n.done.length>1&&!n.done[n.done.length-2].ranges?(n.done.pop(),$(n.done)):void 0}(r,r.lastOp==a)))i=$(o.changes),0==ot(t.from,t.to)&&0==ot(t.from,i.to)?i.to=Dr(t):o.changes.push(Ur(n,t));else{var s=$(r.done);for(s&&s.ranges||qr(n.sel,r.done),o={changes:[Ur(n,t)],generation:r.generation},r.done.push(o);r.done.length>r.undoDepth;)r.done.shift(),r.done[0].ranges||r.done.shift()}r.done.push(e),r.generation=++r.maxGeneration,r.lastModTime=r.lastSelTime=l,r.lastOp=r.lastSelOp=a,r.lastOrigin=r.lastSelOrigin=t.origin,i||xn(n,"historyAdded")}function Xr(n,t,e,a){var r=n.history,o=a&&a.origin;e==r.lastSelOp||o&&r.lastSelOrigin==o&&(r.lastModTime==r.lastSelTime&&r.lastOrigin==o||function(n,t,e,a){var r=t.charAt(0);return"*"==r||"+"==r&&e.ranges.length==a.ranges.length&&e.somethingSelected()==a.somethingSelected()&&new Date-n.history.lastSelTime<=(n.cm?n.cm.options.historyEventDelay:500)}(n,o,$(r.done),t))?r.done[r.done.length-1]=t:qr(t,r.done),r.lastSelTime=+new Date,r.lastSelOrigin=o,r.lastSelOp=e,a&&!1!==a.clearRedo&&Hr(r.undone)}function qr(n,t){var e=$(t);e&&e.ranges&&e.equals(n)||t.push(n)}function Zr(n,t,e,a){var r=t["spans_"+n.id],o=0;n.iter(Math.max(n.first,e),Math.min(n.first+n.size,a),(function(e){e.markedSpans&&((r||(r=t["spans_"+n.id]={}))[o]=e.markedSpans),++o}))}function Kr(n){if(!n)return null;for(var t,e=0;e-1&&($(l)[p]=c[p],delete c[p])}}}return a}function $r(n,t,e,a){if(a){var r=n.anchor;if(e){var o=ot(t,r)<0;o!=ot(e,r)<0?(r=t,t=e):o!=ot(t,e)<0&&(t=e)}return new Cr(r,t)}return new Cr(e||t,t)}function Jr(n,t,e,a,r){null==r&&(r=n.cm&&(n.cm.display.shift||n.extend)),ao(n,new Er([$r(n.sel.primary(),t,e,r)],0),a)}function Qr(n,t,e){for(var a=[],r=n.cm&&(n.cm.display.shift||n.extend),o=0;o=t.ch:l.to>t.ch))){if(r&&(xn(s,"beforeCursorEnter"),s.explicitlyCleared)){if(o.markedSpans){--i;continue}break}if(!s.atomic)continue;if(e){var p=s.find(a<0?1:-1),b=void 0;if((a<0?d:c)&&(p=po(n,p,-a,p&&p.line==t.line?o:null)),p&&p.line==t.line&&(b=ot(p,e))&&(a<0?b<0:b>0))return so(n,p,t,a,r)}var u=s.find(a<0?-1:1);return(a<0?c:d)&&(u=po(n,u,a,u.line==t.line?o:null)),u?so(n,u,t,a,r):null}}return t}function co(n,t,e,a,r){var o=a||1,i=so(n,t,e,o,r)||!r&&so(n,t,e,o,!0)||so(n,t,e,-o,r)||!r&&so(n,t,e,-o,!0);return i||(n.cantEdit=!0,rt(n.first,0))}function po(n,t,e,a){return e<0&&0==t.ch?t.line>n.first?pt(n,rt(t.line-1)):null:e>0&&t.ch==(a||Gn(n,t.line)).text.length?t.line0)){var d=[s,1],p=ot(c.from,l.from),b=ot(c.to,l.to);(p<0||!i.inclusiveLeft&&!p)&&d.push({from:c.from,to:l.from}),(b>0||!i.inclusiveRight&&!b)&&d.push({from:l.to,to:c.to}),r.splice.apply(r,d),s+=d.length-3}}return r}(n,t.from,t.to);if(a)for(var r=a.length-1;r>=0;--r)mo(n,{from:a[r].from,to:a[r].to,text:r?[""]:t.text,origin:t.origin});else mo(n,t)}}function mo(n,t){if(1!=t.text.length||""!=t.text[0]||0!=ot(t.from,t.to)){var e=Mr(n,t);Wr(n,t,e,n.cm?n.cm.curOp.id:NaN),xo(n,t,e,Dt(n,t));var a=[];Ar(n,(function(n,e){e||-1!=H(a,n.history)||(yo(n.history,t),a.push(n.history)),xo(n,t,null,Dt(n,t))}))}}function ho(n,t,e){var a=n.cm&&n.cm.state.suppressEdits;if(!a||e){for(var r,o=n.history,i=n.sel,l="undo"==t?o.done:o.undone,s="undo"==t?o.undone:o.done,c=0;c=0;--u){var f=b(u);if(f)return f.v}}}}function go(n,t){if(0!=t&&(n.first+=t,n.sel=new Er(J(n.sel.ranges,(function(n){return new Cr(rt(n.anchor.line+t,n.anchor.ch),rt(n.head.line+t,n.head.ch))})),n.sel.primIndex),n.cm)){fa(n.cm,n.first,n.first-t,t);for(var e=n.cm.display,a=e.viewFrom;an.lastLine())){if(t.from.lineo&&(t={from:t.from,to:rt(o,Gn(n,o).text.length),text:[t.text[0]],origin:t.origin}),t.removed=$n(n,t.from,t.to),e||(e=Mr(n,t)),n.cm?function(n,t,e){var a=n.doc,r=n.display,o=t.from,i=t.to,l=!1,s=o.line;n.options.lineWrapping||(s=nt(Ut(Gn(a,o.line))),a.iter(s,i.line+1,(function(n){if(n==r.maxLine)return l=!0,!0}))),a.sel.contains(t.from,t.to)>-1&&kn(n),Lr(a,t,e,da(n)),n.options.lineWrapping||(a.iter(s,o.line+t.text.length,(function(n){var t=Kt(n);t>r.maxLineLength&&(r.maxLine=n,r.maxLineLength=t,r.maxLineChanged=!0,l=!1)})),l&&(n.curOp.updateMaxLine=!0)),function(n,t){if(n.modeFrontier=Math.min(n.modeFrontier,t),!(n.highlightFrontiere;a--){var r=Gn(n,a).stateAfter;if(r&&(!(r instanceof ut)||a+r.lookAhead1||!(this.children[0]instanceof _o))){var l=[];this.collapse(l),this.children=[new _o(l)],this.children[0].parent=this}},collapse:function(n){for(var t=0;t50){for(var i=r.lines.length%25+25,l=i;l10);n.parent.maybeSpill()}},iterN:function(n,t,e){for(var a=0;a0||0==i&&!1!==o.clearWhenEmpty)return o;if(o.replacedWith&&(o.collapsed=!0,o.widgetNode=I("span",[o.replacedWith],"CodeMirror-widget"),a.handleMouseEvents||o.widgetNode.setAttribute("cm-ignore-events","true"),a.insertLeft&&(o.widgetNode.insertLeft=!0)),o.collapsed){if(Yt(n,t.line,t,e,o)||t.line!=e.line&&Yt(n,e.line,t,e,o))throw new Error("Inserting collapsed marker partially overlapping an existing one");Et=!0}o.addToHistory&&Wr(n,{from:t,to:e,origin:"markText"},n.sel,NaN);var l,s=t.line,c=n.cm;if(n.iter(s,e.line+1,(function(a){c&&o.collapsed&&!c.options.lineWrapping&&Ut(a)==c.display.maxLine&&(l=!0),o.collapsed&&s!=t.line&&Qn(a,0),function(n,t,e){var a=e&&window.WeakSet&&(e.markedSpans||(e.markedSpans=new WeakSet));a&&n.markedSpans&&a.has(n.markedSpans)?n.markedSpans.push(t):(n.markedSpans=n.markedSpans?n.markedSpans.concat([t]):[t],a&&a.add(n.markedSpans)),t.marker.attachLine(n)}(a,new Ct(o,s==t.line?t.ch:null,s==e.line?e.ch:null),n.cm&&n.cm.curOp),++s})),o.collapsed&&n.iter(t.line,e.line+1,(function(t){Xt(n,t)&&Qn(t,0)})),o.clearOnEnter&&mn(o,"beforeCursorEnter",(function(){return o.clear()})),o.readOnly&&(Ft=!0,(n.history.done.length||n.history.undone.length)&&n.clearHistory()),o.collapsed&&(o.id=++So,o.atomic=!0),c){if(l&&(c.curOp.updateMaxLine=!0),o.collapsed)fa(c,t.line,e.line+1);else if(o.className||o.startStyle||o.endStyle||o.css||o.attributes||o.title)for(var d=t.line;d<=e.line;d++)ma(c,d,"text");o.atomic&&io(c.doc),pe(c,"markerAdded",c,o)}return o}Oo.prototype.clear=function(){if(!this.explicitlyCleared){var n=this.doc.cm,t=n&&!n.curOp;if(t&&Va(n),vn(this,"clear")){var e=this.find();e&&pe(this,"clear",e.from,e.to)}for(var a=null,r=null,o=0;on.display.maxLineLength&&(n.display.maxLine=c,n.display.maxLineLength=d,n.display.maxLineChanged=!0)}null!=a&&n&&this.collapsed&&fa(n,a,r+1),this.lines.length=0,this.explicitlyCleared=!0,this.atomic&&this.doc.cantEdit&&(this.doc.cantEdit=!1,n&&io(n.doc)),n&&pe(n,"markerCleared",n,this,a,r),t&&Ga(n),this.parent&&this.parent.clear()}},Oo.prototype.find=function(n,t){var e,a;null==n&&"bookmark"==this.type&&(n=1);for(var r=0;r=0;s--)fo(this,a[s]);l?eo(this,l):this.cm&&ja(this.cm)})),undo:or((function(){ho(this,"undo")})),redo:or((function(){ho(this,"redo")})),undoSelection:or((function(){ho(this,"undo",!0)})),redoSelection:or((function(){ho(this,"redo",!0)})),setExtending:function(n){this.extend=n},getExtending:function(){return this.extend},historySize:function(){for(var n=this.history,t=0,e=0,a=0;a=n.ch)&&t.push(r.marker.parent||r.marker)}return t},findMarks:function(n,t,e){n=pt(this,n),t=pt(this,t);var a=[],r=n.line;return this.iter(n.line,t.line+1,(function(o){var i=o.markedSpans;if(i)for(var l=0;l=s.to||null==s.from&&r!=n.line||null!=s.from&&r==t.line&&s.from>=t.ch||e&&!e(s.marker)||a.push(s.marker.parent||s.marker)}++r})),a},getAllMarks:function(){var n=[];return this.iter((function(t){var e=t.markedSpans;if(e)for(var a=0;an)return t=n,!0;n-=o,++e})),pt(this,rt(e,t))},indexFromPos:function(n){var t=(n=pt(this,n)).ch;if(n.linet&&(t=n.from),null!=n.to&&n.to-1)return t.state.draggingText(n),void setTimeout((function(){return t.display.input.focus()}),20);try{var p=n.dataTransfer.getData("Text");if(p){var b;if(t.state.draggingText&&!t.state.draggingText.copy&&(b=t.listSelections()),ro(t.doc,Or(e,e)),b)for(var u=0;u=0;t--)wo(n.doc,"",a[t].from,a[t].to,"+delete");ja(n)}))}function ei(n,t,e){var a=sn(n.text,t+e,e);return a<0||a>n.text.length?null:a}function ai(n,t,e){var a=ei(n,t.ch,e);return null==a?null:new rt(t.line,a,e<0?"after":"before")}function ri(n,t,e,a,r){if(n){"rtl"==t.doc.direction&&(r=-r);var o=un(e,t.doc.direction);if(o){var i,l=r<0?$(o):o[0],s=r<0==(1==l.level)?"after":"before";if(l.level>0||"rtl"==t.doc.direction){var c=Te(t,e);i=r<0?e.text.length-1:0;var d=Be(t,c,i).top;i=cn((function(n){return Be(t,c,n).top==d}),r<0==(1==l.level)?l.from:l.to-1,i),"before"==s&&(i=ei(e,i,1))}else i=r<0?l.to:l.from;return new rt(a,i,s)}}return new rt(a,r<0?e.text.length:0,r<0?"before":"after")}Zo.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharAfter",Backspace:"delCharBefore","Shift-Backspace":"delCharBefore",Tab:"defaultTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite",Esc:"singleSelection"},Zo.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Up":"goLineUp","Ctrl-Down":"goLineDown","Ctrl-Left":"goGroupLeft","Ctrl-Right":"goGroupRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delGroupBefore","Ctrl-Delete":"delGroupAfter","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore","Ctrl-U":"undoSelection","Shift-Ctrl-U":"redoSelection","Alt-U":"redoSelection",fallthrough:"basic"},Zo.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageDown","Shift-Ctrl-V":"goPageUp","Ctrl-D":"delCharAfter","Ctrl-H":"delCharBefore","Alt-Backspace":"delWordBefore","Ctrl-K":"killLine","Ctrl-T":"transposeChars","Ctrl-O":"openLine"},Zo.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Home":"goDocStart","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goGroupLeft","Alt-Right":"goGroupRight","Cmd-Left":"goLineLeft","Cmd-Right":"goLineRight","Alt-Backspace":"delGroupBefore","Ctrl-Alt-Backspace":"delGroupAfter","Alt-Delete":"delGroupAfter","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore","Cmd-Backspace":"delWrappedLineLeft","Cmd-Delete":"delWrappedLineRight","Cmd-U":"undoSelection","Shift-Cmd-U":"redoSelection","Ctrl-Up":"goDocStart","Ctrl-Down":"goDocEnd",fallthrough:["basic","emacsy"]},Zo.default=w?Zo.macDefault:Zo.pcDefault;var oi={selectAll:bo,singleSelection:function(n){return n.setSelection(n.getCursor("anchor"),n.getCursor("head"),X)},killLine:function(n){return ti(n,(function(t){if(t.empty()){var e=Gn(n.doc,t.head.line).text.length;return t.head.ch==e&&t.head.line0)r=new rt(r.line,r.ch+1),n.replaceRange(o.charAt(r.ch-1)+o.charAt(r.ch-2),rt(r.line,r.ch-2),r,"+transpose");else if(r.line>n.doc.first){var i=Gn(n.doc,r.line-1).text;i&&(r=new rt(r.line,1),n.replaceRange(o.charAt(0)+n.doc.lineSeparator()+i.charAt(i.length-1),rt(r.line-1,i.length-1),r,"+transpose"))}e.push(new Cr(r,r))}n.setSelections(e)}))},newlineAndIndent:function(n){return er(n,(function(){for(var t=n.listSelections(),e=t.length-1;e>=0;e--)n.replaceRange(n.doc.lineSeparator(),t[e].anchor,t[e].head,"+input");t=n.listSelections();for(var a=0;a-1&&(ot((r=c.ranges[r]).from(),t)<0||t.xRel>0)&&(ot(r.to(),t)>0||t.xRel<0)?function(n,t,e,a){var r=n.display,o=!1,c=ar(n,(function(t){s&&(r.scroller.draggable=!1),n.state.draggingText=!1,n.state.delayingBlurEvent&&(n.hasFocus()?n.state.delayingBlurEvent=!1:Ea(n)),gn(r.wrapper.ownerDocument,"mouseup",c),gn(r.wrapper.ownerDocument,"mousemove",d),gn(r.scroller,"dragstart",p),gn(r.scroller,"drop",c),o||(zn(t),a.addNew||Jr(n.doc,e,null,null,a.extend),s&&!u||i&&9==l?setTimeout((function(){r.wrapper.ownerDocument.body.focus({preventScroll:!0}),r.input.focus()}),20):r.input.focus())})),d=function(n){o=o||Math.abs(t.clientX-n.clientX)+Math.abs(t.clientY-n.clientY)>=10},p=function(){return o=!0};s&&(r.scroller.draggable=!0),n.state.draggingText=c,c.copy=!a.moveOnDrag,mn(r.wrapper.ownerDocument,"mouseup",c),mn(r.wrapper.ownerDocument,"mousemove",d),mn(r.scroller,"dragstart",p),mn(r.scroller,"drop",c),n.state.delayingBlurEvent=!0,setTimeout((function(){return r.input.focus()}),20),r.scroller.dragDrop&&r.scroller.dragDrop()}(n,a,t,o):function(n,t,e,a){i&&Ea(n);var r=n.display,o=n.doc;zn(t);var l,s,c=o.sel,d=c.ranges;if(a.addNew&&!a.extend?(s=o.sel.contains(e),l=s>-1?d[s]:new Cr(e,e)):(l=o.sel.primary(),s=o.sel.primIndex),"rectangle"==a.unit)a.addNew||(l=new Cr(e,e)),e=ba(n,t,!0,!0),s=-1;else{var p=vi(n,e,a.unit);l=a.extend?$r(l,p.anchor,p.head,a.extend):p}a.addNew?-1==s?(s=d.length,ao(o,Sr(n,d.concat([l]),s),{scroll:!1,origin:"*mouse"})):d.length>1&&d[s].empty()&&"char"==a.unit&&!a.extend?(ao(o,Sr(n,d.slice(0,s).concat(d.slice(s+1)),0),{scroll:!1,origin:"*mouse"}),c=o.sel):no(o,s,l,q):(s=0,ao(o,new Er([l],0),q),c=o.sel);var b=e;function u(t){if(0!=ot(b,t))if(b=t,"rectangle"==a.unit){for(var r=[],i=n.options.tabSize,d=Y(Gn(o,e.line).text,e.ch,i),p=Y(Gn(o,t.line).text,t.ch,i),u=Math.min(d,p),f=Math.max(d,p),m=Math.min(e.line,t.line),h=Math.min(n.lastLine(),Math.max(e.line,t.line));m<=h;m++){var g=Gn(o,m).text,x=K(g,u,i);u==f?r.push(new Cr(rt(m,x),rt(m,x))):g.length>x&&r.push(new Cr(rt(m,x),rt(m,K(g,f,i))))}r.length||r.push(new Cr(e,e)),ao(o,Sr(n,c.ranges.slice(0,s).concat(r),s),{origin:"*mouse",scroll:!1}),n.scrollIntoView(t)}else{var w,k=l,v=vi(n,t,a.unit),y=k.anchor;ot(v.anchor,y)>0?(w=v.head,y=ct(k.from(),v.anchor)):(w=v.anchor,y=st(k.to(),v.head));var z=c.ranges.slice(0);z[s]=function(n,t){var e=t.anchor,a=t.head,r=Gn(n.doc,e.line);if(0==ot(e,a)&&e.sticky==a.sticky)return t;var o=un(r);if(!o)return t;var i=pn(o,e.ch,e.sticky),l=o[i];if(l.from!=e.ch&&l.to!=e.ch)return t;var s,c=i+(l.from==e.ch==(1!=l.level)?0:1);if(0==c||c==o.length)return t;if(a.line!=e.line)s=(a.line-e.line)*("ltr"==n.doc.direction?1:-1)>0;else{var d=pn(o,a.ch,a.sticky),p=d-i||(a.ch-e.ch)*(1==l.level?-1:1);s=d==c-1||d==c?p<0:p>0}var b=o[c+(s?-1:0)],u=s==(1==b.level),f=u?b.from:b.to,m=u?"after":"before";return e.ch==f&&e.sticky==m?t:new Cr(new rt(e.line,f,m),a)}(n,new Cr(pt(o,y),w)),ao(o,Sr(n,z,s),q)}}var f=r.wrapper.getBoundingClientRect(),m=0;function h(t){n.state.selectingText=!1,m=1/0,t&&(zn(t),r.input.focus()),gn(r.wrapper.ownerDocument,"mousemove",g),gn(r.wrapper.ownerDocument,"mouseup",x),o.history.lastSelOrigin=null}var g=ar(n,(function(t){0!==t.buttons&&Sn(t)?function t(e){var i=++m,l=ba(n,e,!0,"rectangle"==a.unit);if(l)if(0!=ot(l,b)){n.curOp.focus=P(L(n)),u(l);var s=Ia(r,o);(l.line>=s.to||l.linef.bottom?20:0;c&&setTimeout(ar(n,(function(){m==i&&(r.scroller.scrollTop+=c,t(e))})),50)}}(t):h(t)})),x=ar(n,h);n.state.selectingText=x,mn(r.wrapper.ownerDocument,"mousemove",g),mn(r.wrapper.ownerDocument,"mouseup",x)}(n,a,t,o)}(t,a,o,n):Cn(n)==e.scroller&&zn(n):2==r?(a&&Jr(t.doc,a),setTimeout((function(){return e.input.focus()}),20)):3==r&&(_?t.display.input.onContextMenu(n):Ea(t)))}}function vi(n,t,e){if("char"==e)return new Cr(t,t);if("word"==e)return n.findWordAt(t);if("line"==e)return new Cr(rt(t.line,0),pt(n.doc,rt(t.line+1,0)));var a=e(n,t);return new Cr(a.from,a.to)}function yi(n,t,e,a){var r,o;if(t.touches)r=t.touches[0].clientX,o=t.touches[0].clientY;else try{r=t.clientX,o=t.clientY}catch(n){return!1}if(r>=Math.floor(n.display.gutters.getBoundingClientRect().right))return!1;a&&zn(t);var i=n.display,l=i.lineDiv.getBoundingClientRect();if(o>l.bottom||!vn(n,e))return Fn(t);o-=l.top-i.viewOffset;for(var s=0;s=r)return xn(n,e,n,tt(n.doc,o),n.display.gutterSpecs[s].className,t),Fn(t)}}function zi(n,t){return yi(n,t,"gutterClick",!0)}function _i(n,t){Fe(n.display,t)||function(n,t){return!!vn(n,"gutterContextMenu")&&yi(n,t,"gutterContextMenu",!1)}(n,t)||wn(n,t,"contextmenu")||_||n.display.input.onContextMenu(t)}function Fi(n){n.display.wrapper.className=n.display.wrapper.className.replace(/\s*cm-s-\S+/g,"")+n.options.theme.replace(/(^|\s)\s*/g," cm-s-"),He(n)}wi.prototype.compare=function(n,t,e){return this.time+400>n&&0==ot(t,this.pos)&&e==this.button};var Ei={toString:function(){return"CodeMirror.Init"}},Ci={},Si={};function Oi(n,t,e){if(!t!=!(e&&e!=Ei)){var a=n.display.dragFunctions,r=t?mn:gn;r(n.display.scroller,"dragstart",a.start),r(n.display.scroller,"dragenter",a.enter),r(n.display.scroller,"dragover",a.over),r(n.display.scroller,"dragleave",a.leave),r(n.display.scroller,"drop",a.drop)}}function Di(n){n.options.lineWrapping?(j(n.display.wrapper,"CodeMirror-wrap"),n.display.sizer.style.minWidth="",n.display.sizerWidth=null):(C(n.display.wrapper,"CodeMirror-wrap"),Vt(n)),pa(n),fa(n),He(n),setTimeout((function(){return Wa(n)}),100)}function Ii(n,t){var e=this;if(!(this instanceof Ii))return new Ii(n,t);this.options=t=t?R(t):{},R(Ci,t,!1);var a=t.value;"string"==typeof a?a=new To(a,t.mode,null,t.lineSeparator,t.direction):t.mode&&(a.modeOption=t.mode),this.doc=a;var r=new Ii.inputStyles[t.inputStyle](this),o=this.display=new kr(n,a,r,t);for(var c in o.wrapper.CodeMirror=this,Fi(this),t.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap"),Za(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,delayingBlurEvent:!1,focused:!1,suppressEdits:!1,pasteIncoming:-1,cutIncoming:-1,selectingText:!1,draggingText:!1,highlight:new U,keySeq:null,specialChars:null},t.autofocus&&!x&&o.input.focus(),i&&l<11&&setTimeout((function(){return e.display.input.reset(!0)}),20),function(n){var t=n.display;mn(t.scroller,"mousedown",ar(n,ki)),mn(t.scroller,"dblclick",i&&l<11?ar(n,(function(t){if(!wn(n,t)){var e=ba(n,t);if(e&&!zi(n,t)&&!Fe(n.display,t)){zn(t);var a=n.findWordAt(e);Jr(n.doc,a.anchor,a.head)}}})):function(t){return wn(n,t)||zn(t)}),mn(t.scroller,"contextmenu",(function(t){return _i(n,t)})),mn(t.input.getField(),"contextmenu",(function(e){t.scroller.contains(e.target)||_i(n,e)}));var e,a={end:0};function r(){t.activeTouch&&(e=setTimeout((function(){return t.activeTouch=null}),1e3),(a=t.activeTouch).end=+new Date)}function o(n,t){if(null==t.left)return!0;var e=t.left-n.left,a=t.top-n.top;return e*e+a*a>400}mn(t.scroller,"touchstart",(function(r){if(!wn(n,r)&&!function(n){if(1!=n.touches.length)return!1;var t=n.touches[0];return t.radiusX<=1&&t.radiusY<=1}(r)&&!zi(n,r)){t.input.ensurePolled(),clearTimeout(e);var o=+new Date;t.activeTouch={start:o,moved:!1,prev:o-a.end<=300?a:null},1==r.touches.length&&(t.activeTouch.left=r.touches[0].pageX,t.activeTouch.top=r.touches[0].pageY)}})),mn(t.scroller,"touchmove",(function(){t.activeTouch&&(t.activeTouch.moved=!0)})),mn(t.scroller,"touchend",(function(e){var a=t.activeTouch;if(a&&!Fe(t,e)&&null!=a.left&&!a.moved&&new Date-a.start<300){var i,l=n.coordsChar(t.activeTouch,"page");i=!a.prev||o(a,a.prev)?new Cr(l,l):!a.prev.prev||o(a,a.prev.prev)?n.findWordAt(l):new Cr(rt(l.line,0),pt(n.doc,rt(l.line+1,0))),n.setSelection(i.anchor,i.head),n.focus(),zn(e)}r()})),mn(t.scroller,"touchcancel",r),mn(t.scroller,"scroll",(function(){t.scroller.clientHeight&&(Aa(n,t.scroller.scrollTop),Ra(n,t.scroller.scrollLeft,!0),xn(n,"scroll",n))})),mn(t.scroller,"mousewheel",(function(t){return Fr(n,t)})),mn(t.scroller,"DOMMouseScroll",(function(t){return Fr(n,t)})),mn(t.wrapper,"scroll",(function(){return t.wrapper.scrollTop=t.wrapper.scrollLeft=0})),t.dragFunctions={enter:function(t){wn(n,t)||En(t)},over:function(t){wn(n,t)||(function(n,t){var e=ba(n,t);if(e){var a=document.createDocumentFragment();va(n,e,a),n.display.dragCursor||(n.display.dragCursor=D("div",null,"CodeMirror-cursors CodeMirror-dragcursors"),n.display.lineSpace.insertBefore(n.display.dragCursor,n.display.cursorDiv)),O(n.display.dragCursor,a)}}(n,t),En(t))},start:function(t){return function(n,t){if(i&&(!n.state.draggingText||+new Date-Bo<100))En(t);else if(!wn(n,t)&&!Fe(n.display,t)&&(t.dataTransfer.setData("Text",n.getSelection()),t.dataTransfer.effectAllowed="copyMove",t.dataTransfer.setDragImage&&!u)){var e=D("img",null,null,"position: fixed; left: 0; top: 0;");e.src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==",b&&(e.width=e.height=1,n.display.wrapper.appendChild(e),e._top=e.offsetTop),t.dataTransfer.setDragImage(e,0,0),b&&e.parentNode.removeChild(e)}}(n,t)},drop:ar(n,Lo),leave:function(t){wn(n,t)||Ao(n)}};var s=t.input.getField();mn(s,"keyup",(function(t){return mi.call(n,t)})),mn(s,"keydown",ar(n,fi)),mn(s,"keypress",ar(n,hi)),mn(s,"focus",(function(t){return Ca(n,t)})),mn(s,"blur",(function(t){return Sa(n,t)}))}(this),Yo(),Va(this),this.curOp.forceUpdate=!0,Nr(this,a),t.autofocus&&!x||this.hasFocus()?setTimeout((function(){e.hasFocus()&&!e.state.focused&&Ca(e)}),20):Sa(this),Si)Si.hasOwnProperty(c)&&Si[c](this,t[c],Ei);hr(this),t.finishInit&&t.finishInit(this);for(var d=0;d150)){if(!a)return;e="prev"}}else c=0,e="not";"prev"==e?c=t>o.first?Y(Gn(o,t-1).text,null,i):0:"add"==e?c=s+n.options.indentUnit:"subtract"==e?c=s-n.options.indentUnit:"number"==typeof e&&(c=s+e),c=Math.max(0,c);var p="",b=0;if(n.options.indentWithTabs)for(var u=Math.floor(c/i);u;--u)b+=i,p+="\t";if(bi,s=Tn(t),c=null;if(l&&a.ranges.length>1)if(ji&&ji.text.join("\n")==t){if(a.ranges.length%ji.text.length==0){c=[];for(var d=0;d=0;b--){var u=a.ranges[b],f=u.from(),m=u.to();u.empty()&&(e&&e>0?f=rt(f.line,f.ch-e):n.state.overwrite&&!l?m=rt(m.line,Math.min(Gn(o,m.line).text.length,m.ch+$(s).length)):l&&ji&&ji.lineWise&&ji.text.join("\n")==s.join("\n")&&(f=m=rt(f.line,0)));var h={from:f,to:m,text:c?c[b%c.length]:s,origin:r||(l?"paste":n.state.cutIncoming>i?"cut":"+input")};fo(n.doc,h),pe(n,"inputRead",n,h)}t&&!l&&Ai(n,t),ja(n),n.curOp.updateInput<2&&(n.curOp.updateInput=p),n.curOp.typing=!0,n.state.pasteIncoming=n.state.cutIncoming=-1}function Li(n,t){var e=n.clipboardData&&n.clipboardData.getData("Text");if(e)return n.preventDefault(),t.isReadOnly()||t.options.disableInput||!t.hasFocus()||er(t,(function(){return Bi(t,e,0,null,"paste")})),!0}function Ai(n,t){if(n.options.electricChars&&n.options.smartIndent)for(var e=n.doc.sel,a=e.ranges.length-1;a>=0;a--){var r=e.ranges[a];if(!(r.head.ch>100||a&&e.ranges[a-1].head.line==r.head.line)){var o=n.getModeAt(r.head),i=!1;if(o.electricChars){for(var l=0;l-1){i=Pi(n,r.head.line,"smart");break}}else o.electricInput&&o.electricInput.test(Gn(n.doc,r.head.line).text.slice(0,r.head.ch))&&(i=Pi(n,r.head.line,"smart"));i&&pe(n,"electricInput",n,r.head.line)}}}function Ni(n){for(var t=[],e=[],a=0;a0?0:-1));if(isNaN(d))i=null;else{var p=e>0?d>=55296&&d<56320:d>=56320&&d<57343;i=new rt(t.line,Math.max(0,Math.min(l.text.length,t.ch+e*(p?2:1))),-e)}}else i=r?function(n,t,e,a){var r=un(t,n.doc.direction);if(!r)return ai(t,e,a);e.ch>=t.text.length?(e.ch=t.text.length,e.sticky="before"):e.ch<=0&&(e.ch=0,e.sticky="after");var o=pn(r,e.ch,e.sticky),i=r[o];if("ltr"==n.doc.direction&&i.level%2==0&&(a>0?i.to>e.ch:i.from=i.from&&b>=d.begin)){var u=p?"before":"after";return new rt(e.line,b,u)}}var f=function(n,t,a){for(var o=function(n,t){return t?new rt(e.line,s(n,1),"before"):new rt(e.line,n,"after")};n>=0&&n0==(1!=i.level),c=l?a.begin:s(a.end,-1);if(i.from<=c&&c0?d.end:s(d.begin,-1);return null==h||a>0&&h==t.text.length||!(m=f(a>0?0:r.length-1,a,c(h)))?null:m}(n.cm,l,t,e):ai(l,t,e);if(null==i){if(o||(c=t.line+s)=n.first+n.size||(t=new rt(c,t.ch,t.sticky),!(l=Gn(n,c))))return!1;t=ri(r,n.cm,l,t.line,s)}else t=i;return!0}if("char"==a||"codepoint"==a)c();else if("column"==a)c(!0);else if("word"==a||"group"==a)for(var d=null,p="group"==a,b=n.cm&&n.cm.getHelper(t,"wordChars"),u=!0;!(e<0)||c(!u);u=!1){var f=l.text.charAt(t.ch)||"\n",m=an(f,b)?"w":p&&"\n"==f?"n":!p||/\s/.test(f)?null:"p";if(!p||u||m||(m="s"),d&&d!=m){e<0&&(e=1,c(),t.sticky="after");break}if(m&&(d=m),e>0&&!c(!u))break}var h=co(n,t,o,i,!0);return it(o,h)&&(h.hitSide=!0),h}function Hi(n,t,e,a){var r,o,i=n.doc,l=t.left;if("page"==a){var s=Math.min(n.display.wrapper.clientHeight,A(n).innerHeight||i(n).documentElement.clientHeight),c=Math.max(s-.5*ia(n.display),3);r=(e>0?t.bottom:t.top)+e*c}else"line"==a&&(r=e>0?t.bottom+3:t.top-3);for(;(o=Qe(n,l,r)).outside;){if(e<0?r<=0:r>=i.height){o.hitSide=!0;break}r+=5*e}return o}var Wi=function(n){this.cm=n,this.lastAnchorNode=this.lastAnchorOffset=this.lastFocusNode=this.lastFocusOffset=null,this.polling=new U,this.composing=null,this.gracePeriod=!1,this.readDOMTimeout=null};function Xi(n,t){var e=je(n,t.line);if(!e||e.hidden)return null;var a=Gn(n.doc,t.line),r=Me(e,a,t.line),o=un(a,n.doc.direction),i="left";o&&(i=pn(o,t.ch)%2?"right":"left");var l=Ne(r.map,t.ch,i);return l.offset="right"==l.collapse?l.end:l.start,l}function qi(n,t){return t&&(n.bad=!0),n}function Zi(n,t,e){var a;if(t==n.display.lineDiv){if(!(a=n.display.lineDiv.childNodes[e]))return qi(n.clipPos(rt(n.display.viewTo-1)),!0);t=null,e=0}else for(a=t;;a=a.parentNode){if(!a||a==n.display.lineDiv)return null;if(a.parentNode&&a.parentNode==n.display.lineDiv)break}for(var r=0;r=t.display.viewTo||o.line=t.display.viewFrom&&Xi(t,r)||{node:s[0].measure.map[2],offset:0},d=o.linea.firstLine()&&(i=rt(i.line-1,Gn(a.doc,i.line-1).length)),l.ch==Gn(a.doc,l.line).text.length&&l.liner.viewTo-1)return!1;i.line==r.viewFrom||0==(n=ua(a,i.line))?(t=nt(r.view[0].line),e=r.view[0].node):(t=nt(r.view[n].line),e=r.view[n-1].node.nextSibling);var s,c,d=ua(a,l.line);if(d==r.view.length-1?(s=r.viewTo-1,c=r.lineDiv.lastChild):(s=nt(r.view[d+1].line)-1,c=r.view[d+1].node.previousSibling),!e)return!1;for(var p=a.doc.splitLines(function(n,t,e,a,r){var o="",i=!1,l=n.doc.lineSeparator(),s=!1;function c(){i&&(o+=l,s&&(o+=l),i=s=!1)}function d(n){n&&(c(),o+=n)}function p(t){if(1==t.nodeType){var e=t.getAttribute("cm-text");if(e)return void d(e);var o,b=t.getAttribute("cm-marker");if(b){var u=n.findMarks(rt(a,0),rt(r+1,0),(h=+b,function(n){return n.id==h}));return void(u.length&&(o=u[0].find(0))&&d($n(n.doc,o.from,o.to).join(l)))}if("false"==t.getAttribute("contenteditable"))return;var f=/^(pre|div|p|li|table|br)$/i.test(t.nodeName);if(!/^br$/i.test(t.nodeName)&&0==t.textContent.length)return;f&&c();for(var m=0;m1&&b.length>1;)if($(p)==$(b))p.pop(),b.pop(),s--;else{if(p[0]!=b[0])break;p.shift(),b.shift(),t++}for(var u=0,f=0,m=p[0],h=b[0],g=Math.min(m.length,h.length);ui.ch&&x.charCodeAt(x.length-f-1)==w.charCodeAt(w.length-f-1);)u--,f++;p[p.length-1]=x.slice(0,x.length-f).replace(/^\u200b+/,""),p[0]=p[0].slice(u).replace(/\u200b+$/,"");var v=rt(t,u),y=rt(s,b.length?$(b).length-f:0);return p.length>1||p[0]||ot(v,y)?(wo(a.doc,p,v,y,"+input"),!0):void 0},Wi.prototype.ensurePolled=function(){this.forceCompositionEnd()},Wi.prototype.reset=function(){this.forceCompositionEnd()},Wi.prototype.forceCompositionEnd=function(){this.composing&&(clearTimeout(this.readDOMTimeout),this.composing=null,this.updateFromDOM(),this.div.blur(),this.div.focus())},Wi.prototype.readFromDOMSoon=function(){var n=this;null==this.readDOMTimeout&&(this.readDOMTimeout=setTimeout((function(){if(n.readDOMTimeout=null,n.composing){if(!n.composing.done)return;n.composing=null}n.updateFromDOM()}),80))},Wi.prototype.updateFromDOM=function(){var n=this;!this.cm.isReadOnly()&&this.pollContent()||er(this.cm,(function(){return fa(n.cm)}))},Wi.prototype.setUneditable=function(n){n.contentEditable="false"},Wi.prototype.onKeyPress=function(n){0==n.charCode||this.composing||(n.preventDefault(),this.cm.isReadOnly()||ar(this.cm,Bi)(this.cm,String.fromCharCode(null==n.charCode?n.keyCode:n.charCode),0))},Wi.prototype.readOnlyChanged=function(n){this.div.contentEditable=String("nocursor"!=n)},Wi.prototype.onContextMenu=function(){},Wi.prototype.resetPosition=function(){},Wi.prototype.needsContentAttribute=!0;var Vi=function(n){this.cm=n,this.prevInput="",this.pollingFast=!1,this.polling=new U,this.hasSelection=!1,this.composing=null,this.resetting=!1};Vi.prototype.init=function(n){var t=this,e=this,a=this.cm;this.createField(n);var r=this.textarea;function o(n){if(!wn(a,n)){if(a.somethingSelected())Ti({lineWise:!1,text:a.getSelections()});else{if(!a.options.lineWiseCopyCut)return;var t=Ni(a);Ti({lineWise:!0,text:t.text}),"cut"==n.type?a.setSelections(t.ranges,null,X):(e.prevInput="",r.value=t.text.join("\n"),B(r))}"cut"==n.type&&(a.state.cutIncoming=+new Date)}}n.wrapper.insertBefore(this.wrapper,n.wrapper.firstChild),h&&(r.style.width="0px"),mn(r,"input",(function(){i&&l>=9&&t.hasSelection&&(t.hasSelection=null),e.poll()})),mn(r,"paste",(function(n){wn(a,n)||Li(n,a)||(a.state.pasteIncoming=+new Date,e.fastPoll())})),mn(r,"cut",o),mn(r,"copy",o),mn(n.scroller,"paste",(function(t){if(!Fe(n,t)&&!wn(a,t)){if(!r.dispatchEvent)return a.state.pasteIncoming=+new Date,void e.focus();var o=new Event("paste");o.clipboardData=t.clipboardData,r.dispatchEvent(o)}})),mn(n.lineSpace,"selectstart",(function(t){Fe(n,t)||zn(t)})),mn(r,"compositionstart",(function(){var n=a.getCursor("from");e.composing&&e.composing.range.clear(),e.composing={start:n,range:a.markText(n,a.getCursor("to"),{className:"CodeMirror-composing"})}})),mn(r,"compositionend",(function(){e.composing&&(e.poll(),e.composing.range.clear(),e.composing=null)}))},Vi.prototype.createField=function(n){this.wrapper=Yi(),this.textarea=this.wrapper.firstChild},Vi.prototype.screenReaderLabelChanged=function(n){n?this.textarea.setAttribute("aria-label",n):this.textarea.removeAttribute("aria-label")},Vi.prototype.prepareSelection=function(){var n=this.cm,t=n.display,e=n.doc,a=ka(n);if(n.options.moveInputWithCursor){var r=Ge(n,e.sel.primary().head,"div"),o=t.wrapper.getBoundingClientRect(),i=t.lineDiv.getBoundingClientRect();a.teTop=Math.max(0,Math.min(t.wrapper.clientHeight-10,r.top+i.top-o.top)),a.teLeft=Math.max(0,Math.min(t.wrapper.clientWidth-10,r.left+i.left-o.left))}return a},Vi.prototype.showSelection=function(n){var t=this.cm.display;O(t.cursorDiv,n.cursors),O(t.selectionDiv,n.selection),null!=n.teTop&&(this.wrapper.style.top=n.teTop+"px",this.wrapper.style.left=n.teLeft+"px")},Vi.prototype.reset=function(n){if(!(this.contextMenuPending||this.composing&&n)){var t=this.cm;if(this.resetting=!0,t.somethingSelected()){this.prevInput="";var e=t.getSelection();this.textarea.value=e,t.state.focused&&B(this.textarea),i&&l>=9&&(this.hasSelection=e)}else n||(this.prevInput=this.textarea.value="",i&&l>=9&&(this.hasSelection=null));this.resetting=!1}},Vi.prototype.getField=function(){return this.textarea},Vi.prototype.supportsTouch=function(){return!1},Vi.prototype.focus=function(){if("nocursor"!=this.cm.options.readOnly&&(!x||P(this.textarea.ownerDocument)!=this.textarea))try{this.textarea.focus()}catch(n){}},Vi.prototype.blur=function(){this.textarea.blur()},Vi.prototype.resetPosition=function(){this.wrapper.style.top=this.wrapper.style.left=0},Vi.prototype.receivedFocus=function(){this.slowPoll()},Vi.prototype.slowPoll=function(){var n=this;this.pollingFast||this.polling.set(this.cm.options.pollInterval,(function(){n.poll(),n.cm.state.focused&&n.slowPoll()}))},Vi.prototype.fastPoll=function(){var n=!1,t=this;t.pollingFast=!0,t.polling.set(20,(function e(){t.poll()||n?(t.pollingFast=!1,t.slowPoll()):(n=!0,t.polling.set(60,e))}))},Vi.prototype.poll=function(){var n=this,t=this.cm,e=this.textarea,a=this.prevInput;if(this.contextMenuPending||this.resetting||!t.state.focused||Bn(e)&&!a&&!this.composing||t.isReadOnly()||t.options.disableInput||t.state.keySeq)return!1;var r=e.value;if(r==a&&!t.somethingSelected())return!1;if(i&&l>=9&&this.hasSelection===r||w&&/[\uf700-\uf7ff]/.test(r))return t.display.input.reset(),!1;if(t.doc.sel==t.display.selForContextMenu){var o=r.charCodeAt(0);if(8203!=o||a||(a="​"),8666==o)return this.reset(),this.cm.execCommand("undo")}for(var s=0,c=Math.min(a.length,r.length);s1e3||r.indexOf("\n")>-1?e.value=n.prevInput="":n.prevInput=r,n.composing&&(n.composing.range.clear(),n.composing.range=t.markText(n.composing.start,t.getCursor("to"),{className:"CodeMirror-composing"}))})),!0},Vi.prototype.ensurePolled=function(){this.pollingFast&&this.poll()&&(this.pollingFast=!1)},Vi.prototype.onKeyPress=function(){i&&l>=9&&(this.hasSelection=null),this.fastPoll()},Vi.prototype.onContextMenu=function(n){var t=this,e=t.cm,a=e.display,r=t.textarea;t.contextMenuPending&&t.contextMenuPending();var o=ba(e,n),c=a.scroller.scrollTop;if(o&&!b){e.options.resetSelectionOnContextMenu&&-1==e.doc.sel.contains(o)&&ar(e,ao)(e.doc,Or(o),X);var d,p=r.style.cssText,u=t.wrapper.style.cssText,f=t.wrapper.offsetParent.getBoundingClientRect();if(t.wrapper.style.cssText="position: static",r.style.cssText="position: absolute; width: 30px; height: 30px;\n top: "+(n.clientY-f.top-5)+"px; left: "+(n.clientX-f.left-5)+"px;\n z-index: 1000; background: "+(i?"rgba(255, 255, 255, .05)":"transparent")+";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",s&&(d=r.ownerDocument.defaultView.scrollY),a.input.focus(),s&&r.ownerDocument.defaultView.scrollTo(null,d),a.input.reset(),e.somethingSelected()||(r.value=t.prevInput=" "),t.contextMenuPending=g,a.selForContextMenu=e.doc.sel,clearTimeout(a.detectingSelectAll),i&&l>=9&&h(),_){En(n);var m=function(){gn(window,"mouseup",m),setTimeout(g,20)};mn(window,"mouseup",m)}else setTimeout(g,50)}function h(){if(null!=r.selectionStart){var n=e.somethingSelected(),o="​"+(n?r.value:"");r.value="⇚",r.value=o,t.prevInput=n?"":"​",r.selectionStart=1,r.selectionEnd=o.length,a.selForContextMenu=e.doc.sel}}function g(){if(t.contextMenuPending==g&&(t.contextMenuPending=!1,t.wrapper.style.cssText=u,r.style.cssText=p,i&&l<9&&a.scrollbars.setScrollTop(a.scroller.scrollTop=c),null!=r.selectionStart)){(!i||i&&l<9)&&h();var n=0,o=function(){a.selForContextMenu==e.doc.sel&&0==r.selectionStart&&r.selectionEnd>0&&"​"==t.prevInput?ar(e,bo)(e):n++<10?a.detectingSelectAll=setTimeout(o,500):(a.selForContextMenu=null,a.input.reset())};a.detectingSelectAll=setTimeout(o,200)}}},Vi.prototype.readOnlyChanged=function(n){n||this.reset(),this.textarea.disabled="nocursor"==n,this.textarea.readOnly=!!n},Vi.prototype.setUneditable=function(){},Vi.prototype.needsContentAttribute=!1,function(n){var t=n.optionHandlers;function e(e,a,r,o){n.defaults[e]=a,r&&(t[e]=o?function(n,t,e){e!=Ei&&r(n,t,e)}:r)}n.defineOption=e,n.Init=Ei,e("value","",(function(n,t){return n.setValue(t)}),!0),e("mode",null,(function(n,t){n.doc.modeOption=t,jr(n)}),!0),e("indentUnit",2,jr,!0),e("indentWithTabs",!1),e("smartIndent",!0),e("tabSize",4,(function(n){Tr(n),He(n),fa(n)}),!0),e("lineSeparator",null,(function(n,t){if(n.doc.lineSep=t,t){var e=[],a=n.doc.first;n.doc.iter((function(n){for(var r=0;;){var o=n.text.indexOf(t,r);if(-1==o)break;r=o+t.length,e.push(rt(a,o))}a++}));for(var r=e.length-1;r>=0;r--)wo(n.doc,t,e[r],rt(e[r].line,e[r].ch+t.length))}})),e("specialChars",/[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]/g,(function(n,t,e){n.state.specialChars=new RegExp(t.source+(t.test("\t")?"":"|\t"),"g"),e!=Ei&&n.refresh()})),e("specialCharPlaceholder",ee,(function(n){return n.refresh()}),!0),e("electricChars",!0),e("inputStyle",x?"contenteditable":"textarea",(function(){throw new Error("inputStyle can not (yet) be changed in a running editor")}),!0),e("spellcheck",!1,(function(n,t){return n.getInputField().spellcheck=t}),!0),e("autocorrect",!1,(function(n,t){return n.getInputField().autocorrect=t}),!0),e("autocapitalize",!1,(function(n,t){return n.getInputField().autocapitalize=t}),!0),e("rtlMoveVisually",!v),e("wholeLineUpdateBefore",!0),e("theme","default",(function(n){Fi(n),wr(n)}),!0),e("keyMap","default",(function(n,t,e){var a=ni(t),r=e!=Ei&&ni(e);r&&r.detach&&r.detach(n,a),a.attach&&a.attach(n,r||null)})),e("extraKeys",null),e("configureMouse",null),e("lineWrapping",!1,Di,!0),e("gutters",[],(function(n,t){n.display.gutterSpecs=gr(t,n.options.lineNumbers),wr(n)}),!0),e("fixedGutter",!0,(function(n,t){n.display.gutters.style.left=t?ca(n.display)+"px":"0",n.refresh()}),!0),e("coverGutterNextToScrollbar",!1,(function(n){return Wa(n)}),!0),e("scrollbarStyle","native",(function(n){Za(n),Wa(n),n.display.scrollbars.setScrollTop(n.doc.scrollTop),n.display.scrollbars.setScrollLeft(n.doc.scrollLeft)}),!0),e("lineNumbers",!1,(function(n,t){n.display.gutterSpecs=gr(n.options.gutters,t),wr(n)}),!0),e("firstLineNumber",1,wr,!0),e("lineNumberFormatter",(function(n){return n}),wr,!0),e("showCursorWhenSelecting",!1,wa,!0),e("resetSelectionOnContextMenu",!0),e("lineWiseCopyCut",!0),e("pasteLinesPerSelection",!0),e("selectionsMayTouch",!1),e("readOnly",!1,(function(n,t){"nocursor"==t&&(Sa(n),n.display.input.blur()),n.display.input.readOnlyChanged(t)})),e("screenReaderLabel",null,(function(n,t){t=""===t?null:t,n.display.input.screenReaderLabelChanged(t)})),e("disableInput",!1,(function(n,t){t||n.display.input.reset()}),!0),e("dragDrop",!0,Oi),e("allowDropFileTypes",null),e("cursorBlinkRate",530),e("cursorScrollMargin",0),e("cursorHeight",1,wa,!0),e("singleCursorHeightPerLine",!0,wa,!0),e("workTime",100),e("workDelay",100),e("flattenSpans",!0,Tr,!0),e("addModeClass",!1,Tr,!0),e("pollInterval",100),e("undoDepth",200,(function(n,t){return n.doc.history.undoDepth=t})),e("historyEventDelay",1250),e("viewportMargin",10,(function(n){return n.refresh()}),!0),e("maxHighlightLength",1e4,Tr,!0),e("moveInputWithCursor",!0,(function(n,t){t||n.display.input.resetPosition()})),e("tabindex",null,(function(n,t){return n.display.input.getField().tabIndex=t||""})),e("autofocus",null),e("direction","ltr",(function(n,t){return n.doc.setDirection(t)}),!0),e("phrases",null)}(Ii),function(n){var t=n.optionHandlers,e=n.helpers={};n.prototype={constructor:n,focus:function(){A(this).focus(),this.display.input.focus()},setOption:function(n,e){var a=this.options,r=a[n];a[n]==e&&"mode"!=n||(a[n]=e,t.hasOwnProperty(n)&&ar(this,t[n])(this,e,r),xn(this,"optionChange",this,n))},getOption:function(n){return this.options[n]},getDoc:function(){return this.doc},addKeyMap:function(n,t){this.state.keyMaps[t?"push":"unshift"](ni(n))},removeKeyMap:function(n){for(var t=this.state.keyMaps,e=0;ee&&(Pi(this,r.head.line,n,!0),e=r.head.line,a==this.doc.sel.primIndex&&ja(this));else{var o=r.from(),i=r.to(),l=Math.max(e,o.line);e=Math.min(this.lastLine(),i.line-(i.ch?0:1))+1;for(var s=l;s0&&no(this.doc,a,new Cr(o,c[a].to()),X)}}})),getTokenAt:function(n,t){return yt(this,n,t)},getLineTokens:function(n,t){return yt(this,rt(n),t,!0)},getTokenTypeAt:function(n){n=pt(this.doc,n);var t,e=ht(this,Gn(this.doc,n.line)),a=0,r=(e.length-1)/2,o=n.ch;if(0==o)t=e[2];else for(;;){var i=a+r>>1;if((i?e[2*i-1]:0)>=o)r=i;else{if(!(e[2*i+1]o&&(n=o,r=!0),a=Gn(this.doc,n)}else a=n;return Ze(this,a,{top:0,left:0},t||"page",e||r).top+(r?this.doc.height-Zt(a):0)},defaultTextHeight:function(){return ia(this.display)},defaultCharWidth:function(){return la(this.display)},getViewport:function(){return{from:this.display.viewFrom,to:this.display.viewTo}},addWidget:function(n,t,e,a,r){var o,i,l,s=this.display,c=(n=Ge(this,pt(this.doc,n))).bottom,d=n.left;if(t.style.position="absolute",t.setAttribute("cm-ignore-events","true"),this.display.input.setUneditable(t),s.sizer.appendChild(t),"over"==a)c=n.top;else if("above"==a||"near"==a){var p=Math.max(s.wrapper.clientHeight,this.doc.height),b=Math.max(s.sizer.clientWidth,s.lineSpace.clientWidth);("above"==a||n.bottom+t.offsetHeight>p)&&n.top>t.offsetHeight?c=n.top-t.offsetHeight:n.bottom+t.offsetHeight<=p&&(c=n.bottom),d+t.offsetWidth>b&&(d=b-t.offsetWidth)}t.style.top=c+"px",t.style.left=t.style.right="","right"==r?(d=s.sizer.clientWidth-t.offsetWidth,t.style.right="0px"):("left"==r?d=0:"middle"==r&&(d=(s.sizer.clientWidth-t.offsetWidth)/2),t.style.left=d+"px"),e&&(o=this,i={left:d,top:c,right:d+t.offsetWidth,bottom:c+t.offsetHeight},null!=(l=Ma(o,i)).scrollTop&&Aa(o,l.scrollTop),null!=l.scrollLeft&&Ra(o,l.scrollLeft))},triggerOnKeyDown:rr(fi),triggerOnKeyPress:rr(hi),triggerOnKeyUp:mi,triggerOnMouseDown:rr(ki),execCommand:function(n){if(oi.hasOwnProperty(n))return oi[n].call(null,this)},triggerElectric:rr((function(n){Ai(this,n)})),findPosH:function(n,t,e,a){var r=1;t<0&&(r=-1,t=-t);for(var o=pt(this.doc,n),i=0;i0&&i(t.charAt(e-1));)--e;for(;a.5||this.options.lineWrapping)&&pa(this),xn(this,"refresh",this)})),swapDoc:rr((function(n){var t=this.doc;return t.cm=null,this.state.selectingText&&this.state.selectingText(),Nr(this,n),He(this),this.display.input.reset(),Ta(this,n.scrollLeft,n.scrollTop),this.curOp.forceScroll=!0,pe(this,"swapDoc",this,t),t})),phrase:function(n){var t=this.options.phrases;return t&&Object.prototype.hasOwnProperty.call(t,n)?t[n]:n},getInputField:function(){return this.display.input.getField()},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}},yn(n),n.registerHelper=function(t,a,r){e.hasOwnProperty(t)||(e[t]=n[t]={_global:[]}),e[t][a]=r},n.registerGlobalHelper=function(t,a,r,o){n.registerHelper(t,a,o),e[t]._global.push({pred:r,val:o})}}(Ii);var Gi="iter insert remove copy getEditor constructor".split(" ");for(var $i in To.prototype)To.prototype.hasOwnProperty($i)&&H(Gi,$i)<0&&(Ii.prototype[$i]=function(n){return function(){return n.apply(this.doc,arguments)}}(To.prototype[$i]));return yn(To),Ii.inputStyles={textarea:Vi,contenteditable:Wi},Ii.defineMode=function(n){Ii.defaults.mode||"null"==n||(Ii.defaults.mode=n),Yn.apply(this,arguments)},Ii.defineMIME=function(n,t){Rn[n]=t},Ii.defineMode("null",(function(){return{token:function(n){return n.skipToEnd()}}})),Ii.defineMIME("text/plain","null"),Ii.defineExtension=function(n,t){Ii.prototype[n]=t},Ii.defineDocExtension=function(n,t){To.prototype[n]=t},Ii.fromTextArea=function(n,t){if((t=t?R(t):{}).value=n.value,!t.tabindex&&n.tabIndex&&(t.tabindex=n.tabIndex),!t.placeholder&&n.placeholder&&(t.placeholder=n.placeholder),null==t.autofocus){var e=P(n.ownerDocument);t.autofocus=e==n||null!=n.getAttribute("autofocus")&&e==document.body}function a(){n.value=l.getValue()}var r;if(n.form&&(mn(n.form,"submit",a),!t.leaveSubmitMethodAlone)){var o=n.form;r=o.submit;try{var i=o.submit=function(){a(),o.submit=r,o.submit(),o.submit=i}}catch(n){}}t.finishInit=function(e){e.save=a,e.getTextArea=function(){return n},e.toTextArea=function(){e.toTextArea=isNaN,a(),n.parentNode.removeChild(e.getWrapperElement()),n.style.display="",n.form&&(gn(n.form,"submit",a),t.leaveSubmitMethodAlone||"function"!=typeof n.form.submit||(n.form.submit=r))}},n.style.display="none";var l=Ii((function(t){return n.parentNode.insertBefore(t,n.nextSibling)}),t);return l},function(n){n.off=gn,n.on=mn,n.wheelEventPixels=_r,n.Doc=To,n.splitLines=Tn,n.countColumn=Y,n.findColumn=K,n.isWordChar=en,n.Pass=W,n.signal=xn,n.Line=Gt,n.changeEnd=Dr,n.scrollbarModel=qa,n.Pos=rt,n.cmpPos=ot,n.modes=Nn,n.mimeModes=Rn,n.resolveMode=Un,n.getMode=Hn,n.modeExtensions=Wn,n.extendMode=Xn,n.copyState=qn,n.startState=Kn,n.innerMode=Zn,n.commands=oi,n.keyMap=Zo,n.keyName=Qo,n.isModifierKey=$o,n.lookupKey=Go,n.normalizeKeyMap=Vo,n.StringStream=Vn,n.SharedTextMarker=Io,n.TextMarker=Oo,n.LineWidget=Eo,n.e_preventDefault=zn,n.e_stopPropagation=_n,n.e_stop=En,n.addClass=j,n.contains=M,n.rmClass=C,n.keyNames=Ho}(Ii),Ii.version="5.65.10",Ii}()},function(n,t,e){!function(n){"use strict";n.defineMode("shell",(function(){var t={};function e(n,e){for(var a=0;a1&&n.eat("$");var e=n.next();return/['"({]/.test(e)?(t.tokens[0]=l(e,"("==e?"quote":"{"==e?"def":"string"),d(n,t)):(/\d/.test(e)||n.eatWhile(/\w/),t.tokens.shift(),"def")};function d(n,t){return(t.tokens[0]||i)(n,t)}return{startState:function(){return{tokens:[]}},token:function(n,t){return d(n,t)},closeBrackets:"()[]{}''\"\"``",lineComment:"#",fold:"brace"}})),n.defineMIME("text/x-sh","shell"),n.defineMIME("application/x-sh","shell")}(e(61))},function(n,t,e){var a=e(146),r=e(3);"string"==typeof a&&(a=[[n.i,a,""]]),n.exports=a.locals||{},n.exports._getContent=function(){return a},n.exports._getCss=function(){return a.toString()},n.exports._insertCss=function(n){return r(a,n)}},function(n,t,e){var a=e(176),r=e(65),o=e(66),i=e(34),l=e(67),s=e(68),c=e(22)("socket.io-client:manager"),d=e(179),p=e(180),b=Object.prototype.hasOwnProperty;function u(n,t){if(!(this instanceof u))return new u(n,t);n&&"object"==typeof n&&(t=n,n=void 0),(t=t||{}).path=t.path||"/socket.io",this.nsps={},this.subs=[],this.opts=t,this.reconnection(!1!==t.reconnection),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor(t.randomizationFactor||.5),this.backoff=new p({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(null==t.timeout?2e4:t.timeout),this.readyState="closed",this.uri=n,this.connecting=[],this.lastPing=null,this.encoding=!1,this.packetBuffer=[],this.encoder=new i.Encoder,this.decoder=new i.Decoder,this.autoConnect=!1!==t.autoConnect,this.autoConnect&&this.open()}n.exports=u,u.prototype.emitAll=function(){for(var n in this.emit.apply(this,arguments),this.nsps)b.call(this.nsps,n)&&this.nsps[n].emit.apply(this.nsps[n],arguments)},u.prototype.updateSocketIds=function(){for(var n in this.nsps)b.call(this.nsps,n)&&(this.nsps[n].id=this.engine.id)},o(u.prototype),u.prototype.reconnection=function(n){return arguments.length?(this._reconnection=!!n,this):this._reconnection},u.prototype.reconnectionAttempts=function(n){return arguments.length?(this._reconnectionAttempts=n,this):this._reconnectionAttempts},u.prototype.reconnectionDelay=function(n){return arguments.length?(this._reconnectionDelay=n,this.backoff&&this.backoff.setMin(n),this):this._reconnectionDelay},u.prototype.randomizationFactor=function(n){return arguments.length?(this._randomizationFactor=n,this.backoff&&this.backoff.setJitter(n),this):this._randomizationFactor},u.prototype.reconnectionDelayMax=function(n){return arguments.length?(this._reconnectionDelayMax=n,this.backoff&&this.backoff.setMax(n),this):this._reconnectionDelayMax},u.prototype.timeout=function(n){return arguments.length?(this._timeout=n,this):this._timeout},u.prototype.maybeReconnectOnOpen=function(){!this.reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()},u.prototype.open=u.prototype.connect=function(n,t){if(c("readyState %s",this.readyState),~this.readyState.indexOf("open"))return this;c("opening %s",this.uri),this.engine=a(this.uri,this.opts);var e=this.engine,r=this;this.readyState="opening",this.skipReconnect=!1;var o=l(e,"open",(function(){r.onopen(),n&&n()})),i=l(e,"error",(function(t){if(c("connect_error"),r.cleanup(),r.readyState="closed",r.emitAll("connect_error",t),n){var e=new Error("Connection error");e.data=t,n(e)}else r.maybeReconnectOnOpen()}));if(!1!==this._timeout){var s=this._timeout;c("connect attempt will timeout after %d",s);var d=setTimeout((function(){c("connect attempt timed out after %d",s),o.destroy(),e.close(),e.emit("error","timeout"),r.emitAll("connect_timeout",s)}),s);this.subs.push({destroy:function(){clearTimeout(d)}})}return this.subs.push(o),this.subs.push(i),this},u.prototype.onopen=function(){c("open"),this.cleanup(),this.readyState="open",this.emit("open");var n=this.engine;this.subs.push(l(n,"data",s(this,"ondata"))),this.subs.push(l(n,"ping",s(this,"onping"))),this.subs.push(l(n,"pong",s(this,"onpong"))),this.subs.push(l(n,"error",s(this,"onerror"))),this.subs.push(l(n,"close",s(this,"onclose"))),this.subs.push(l(this.decoder,"decoded",s(this,"ondecoded")))},u.prototype.onping=function(){this.lastPing=new Date,this.emitAll("ping")},u.prototype.onpong=function(){this.emitAll("pong",new Date-this.lastPing)},u.prototype.ondata=function(n){this.decoder.add(n)},u.prototype.ondecoded=function(n){this.emit("packet",n)},u.prototype.onerror=function(n){c("error",n),this.emitAll("error",n)},u.prototype.socket=function(n,t){var e=this.nsps[n];if(!e){e=new r(this,n,t),this.nsps[n]=e;var a=this;e.on("connecting",o),e.on("connect",(function(){e.id=a.engine.id})),this.autoConnect&&o()}function o(){~d(a.connecting,e)||a.connecting.push(e)}return e},u.prototype.destroy=function(n){var t=d(this.connecting,n);~t&&this.connecting.splice(t,1),this.connecting.length||this.close()},u.prototype.packet=function(n){c("writing packet %j",n);var t=this;n.query&&0===n.type&&(n.nsp+="?"+n.query),t.encoding?t.packetBuffer.push(n):(t.encoding=!0,this.encoder.encode(n,(function(e){for(var a=0;a0&&!this.encoding){var n=this.packetBuffer.shift();this.packet(n)}},u.prototype.cleanup=function(){c("cleanup");for(var n=this.subs.length,t=0;t=this._reconnectionAttempts)c("reconnect failed"),this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var t=this.backoff.duration();c("will wait %dms before reconnect attempt",t),this.reconnecting=!0;var e=setTimeout((function(){n.skipReconnect||(c("attempting reconnect"),n.emitAll("reconnect_attempt",n.backoff.attempts),n.emitAll("reconnecting",n.backoff.attempts),n.skipReconnect||n.open((function(t){t?(c("reconnect attempt error"),n.reconnecting=!1,n.reconnect(),n.emitAll("reconnect_error",t.data)):(c("reconnect success"),n.onreconnect())})))}),t);this.subs.push({destroy:function(){clearTimeout(e)}})}},u.prototype.onreconnect=function(){var n=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",n)}},function(n,t,e){var a=e(34),r=e(66),o=e(177),i=e(67),l=e(68),s=e(22)("socket.io-client:socket"),c=e(178);n.exports=b;var d={connect:1,connect_error:1,connect_timeout:1,connecting:1,disconnect:1,error:1,reconnect:1,reconnect_attempt:1,reconnect_failed:1,reconnect_error:1,reconnecting:1,ping:1,pong:1},p=r.prototype.emit;function b(n,t,e){this.io=n,this.nsp=t,this.json=this,this.ids=0,this.acks={},this.receiveBuffer=[],this.sendBuffer=[],this.connected=!1,this.disconnected=!0,e&&e.query&&(this.query=e.query),this.io.autoConnect&&this.open()}r(b.prototype),b.prototype.subEvents=function(){if(!this.subs){var n=this.io;this.subs=[i(n,"open",l(this,"onopen")),i(n,"packet",l(this,"onpacket")),i(n,"close",l(this,"onclose"))]}},b.prototype.open=b.prototype.connect=function(){return this.connected||(this.subEvents(),this.io.open(),"open"===this.io.readyState&&this.onopen(),this.emit("connecting")),this},b.prototype.send=function(){var n=o(arguments);return n.unshift("message"),this.emit.apply(this,n),this},b.prototype.emit=function(n){if(d.hasOwnProperty(n))return p.apply(this,arguments),this;var t=o(arguments),e=a.EVENT;c(t)&&(e=a.BINARY_EVENT);var r={type:e,data:t,options:{}};return r.options.compress=!this.flags||!1!==this.flags.compress,"function"==typeof t[t.length-1]&&(s("emitting packet with ack id %d",this.ids),this.acks[this.ids]=t.pop(),r.id=this.ids++),this.connected?this.packet(r):this.sendBuffer.push(r),delete this.flags,this},b.prototype.packet=function(n){n.nsp=this.nsp,this.io.packet(n)},b.prototype.onopen=function(){s("transport is open - connecting"),"/"!==this.nsp&&(this.query?this.packet({type:a.CONNECT,query:this.query}):this.packet({type:a.CONNECT}))},b.prototype.onclose=function(n){s("close (%s)",n),this.connected=!1,this.disconnected=!0,delete this.id,this.emit("disconnect",n)},b.prototype.onpacket=function(n){if(n.nsp===this.nsp)switch(n.type){case a.CONNECT:this.onconnect();break;case a.EVENT:case a.BINARY_EVENT:this.onevent(n);break;case a.ACK:case a.BINARY_ACK:this.onack(n);break;case a.DISCONNECT:this.ondisconnect();break;case a.ERROR:this.emit("error",n.data)}},b.prototype.onevent=function(n){var t=n.data||[];s("emitting event %j",t),null!=n.id&&(s("attaching ack callback to event"),t.push(this.ack(n.id))),this.connected?p.apply(this,t):this.receiveBuffer.push(t)},b.prototype.ack=function(n){var t=this,e=!1;return function(){if(!e){e=!0;var r=o(arguments);s("sending ack %j",r);var i=c(r)?a.BINARY_ACK:a.ACK;t.packet({type:i,id:n,data:r})}}},b.prototype.onack=function(n){var t=this.acks[n.id];"function"==typeof t?(s("calling ack %s with %j",n.id,n.data),t.apply(this,n.data),delete this.acks[n.id]):s("bad ack %s",n.id)},b.prototype.onconnect=function(){this.connected=!0,this.disconnected=!1,this.emit("connect"),this.emitBuffered()},b.prototype.emitBuffered=function(){var n;for(n=0;n0&&r[r.length-1])||6!==o[0]&&2!==o[0])){i=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]0&&s.forEach((function(n){n.key&&n.value&&(p[n.key]=n.value)}));var b={mcpServers:(t={},t[r||"your-server-name"]={command:i||"node",args:l?l.split("\n").filter(Boolean):["path/to/your/server.js"],env:Object.keys(p).length>0?p:void 0},t)};return JSON.stringify(b,null,2)}return"streamable-http"===o?JSON.stringify({mcpServers:(e={},e[r||"your-server-name"]={url:c},e)},null,2):"sse"===o?JSON.stringify({mcpServers:(a={},a[r||"your-server-name"]={url:d},a)},null,2):"{}"}},function(n,t,e){"use strict";var a=this&&this.__createBinding||(Object.create?function(n,t,e,a){void 0===a&&(a=e);var r=Object.getOwnPropertyDescriptor(t,e);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(n,a,r)}:function(n,t,e,a){void 0===a&&(a=e),n[a]=t[e]}),r=this&&this.__setModuleDefault||(Object.create?function(n,t){Object.defineProperty(n,"default",{enumerable:!0,value:t})}:function(n,t){n.default=t}),o=this&&this.__importStar||function(n){if(n&&n.__esModule)return n;var t={};if(null!=n)for(var e in n)"default"!==e&&Object.prototype.hasOwnProperty.call(n,e)&&a(t,n,e);return r(t,n),t},i=this&&this.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var l=o(e(0)),s=i(e(8)),c=s.default.mcpInspectorWebPort,d=s.default.mcpInspectorServerPort;t.default=function(n){var t=n.config,e=(0,l.useMemo)((function(){return function(n){var t,e={MCP_PROXY_PORT:d.toString(),transport:(null==n?void 0:n.transport)||"streamable-http",serverCommand:(null==n?void 0:n.serverCommand)||"npx",serverArgs:(null===(t=null==n?void 0:n.serverArgs)||void 0===t?void 0:t.join(","))||"",serverUrl:(null==n?void 0:n.serverUrl)||"".concat(location.origin,"/mcp-endpoint/your-server-id/mcp")};return new URLSearchParams(e).toString()}(t)}),[t]);return l.default.createElement("div",{className:"mcp-inspector-iframe",style:{minHeight:800,height:800}},l.default.createElement("iframe",{src:"".concat(location.protocol,"//").concat(location.hostname,":").concat(c,"?").concat(e),style:{width:"100%",height:"100%"}}))}},function(n,t,e){"use strict";var a=this&&this.__createBinding||(Object.create?function(n,t,e,a){void 0===a&&(a=e);var r=Object.getOwnPropertyDescriptor(t,e);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(n,a,r)}:function(n,t,e,a){void 0===a&&(a=e),n[a]=t[e]}),r=this&&this.__setModuleDefault||(Object.create?function(n,t){Object.defineProperty(n,"default",{enumerable:!0,value:t})}:function(n,t){n.default=t}),o=this&&this.__importStar||function(n){if(n&&n.__esModule)return n;var t={};if(null!=n)for(var e in n)"default"!==e&&Object.prototype.hasOwnProperty.call(n,e)&&a(t,n,e);return r(t,n),t},i=this&&this.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var l=o(e(0)),s=(o(e(75)),e(76),e(10)),c=e(23),d=e(7),p=i(e(77)),b=i(e(273)),u=e(274),f=i(e(40));e(278),e(280);t.default=function(n,t){var e=n.state.url,a=(0,c.matchRoutes)(f.default,e).map((function(n){var t=n.route,e=t.component&&t.component.fetch;return e instanceof Function?e():Promise.resolve(null)}));return Promise.all(a).then((function(t){var a=n.state;t.forEach((function(n){Object.assign(a,n)})),n.state=Object.assign({},n.state,a);var r=(0,u.create)(a);return function(){return l.createElement(b.default,null,l.createElement("div",{style:{height:"100%"}},l.createElement(s.Provider,{store:r},l.createElement(d.StaticRouter,{location:e,context:{}},l.createElement(p.default,null)))))}}))}},function(n,t){n.exports=require("react-dom")},function(n,t){n.exports=require("react-hot-loader")},function(n,t,e){"use strict";var a=this&&this.__createBinding||(Object.create?function(n,t,e,a){void 0===a&&(a=e);var r=Object.getOwnPropertyDescriptor(t,e);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[e]}}),Object.defineProperty(n,a,r)}:function(n,t,e,a){void 0===a&&(a=e),n[a]=t[e]}),r=this&&this.__setModuleDefault||(Object.create?function(n,t){Object.defineProperty(n,"default",{enumerable:!0,value:t})}:function(n,t){n.default=t}),o=this&&this.__importStar||function(n){if(n&&n.__esModule)return n;var t={};if(null!=n)for(var e in n)"default"!==e&&Object.prototype.hasOwnProperty.call(n,e)&&a(t,n,e);return r(t,n),t},i=this&&this.__importDefault||function(n){return n&&n.__esModule?n:{default:n}};Object.defineProperty(t,"__esModule",{value:!0});var l=o(e(0)),s=e(10),c=e(23),d=e(7),p=e(1),b=i(e(78)),u=e(24),f=i(e(40)),m=o(e(42)),h=e(266);e(267),e(269),e(271);t.default=function(){var n=(0,u.bindActionCreators)(m,(0,s.useDispatch)()).changeLocalIp;return(0,l.useEffect)((function(){var t,e,a,r;window.console.log("%cApp current version: v".concat(h.version),"font-family: Cabin, Helvetica, Arial, sans-serif;text-align: left;font-size:32px;color:#B21212;"),t=window,e=document,t.hj=t.hj||function(){(t.hj.q=t.hj.q||[]).push(arguments)},t._hjSettings={hjid:2133522,hjsv:6},a=e.getElementsByTagName("head")[0],(r=e.createElement("script")).async=1,r.src="https://static.hotjar.com/c/hotjar-"+t._hjSettings.hjid+".js?sv="+t._hjSettings.hjsv,a.appendChild(r),n()}),[]),l.default.createElement("div",{style:{height:"100%"}},l.default.createElement(p.ConfigProvider,{locale:b.default},l.default.createElement(d.Switch,null,(0,c.renderRoutes)(f.default))))}},function(n,t,e){"use strict";var a=e(16);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=a(e(79)).default;t.default=r},function(n,t,e){"use strict";var a=e(16);Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var r=a(e(80)),o=a(e(38)),i=a(e(39)),l=a(e(83)),s="${label}不是一个有效的${type}",c={locale:"zh-cn",Pagination:r.default,DatePicker:o.default,TimePicker:i.default,Calendar:l.default,global:{placeholder:"请选择"},Table:{filterTitle:"筛选",filterConfirm:"确定",filterReset:"重置",filterEmptyText:"无筛选项",selectAll:"全选当页",selectInvert:"反选当页",selectNone:"清空所有",selectionAll:"全选所有",sortTitle:"排序",expand:"展开行",collapse:"关闭行",triggerDesc:"点击降序",triggerAsc:"点击升序",cancelSort:"取消排序"},Modal:{okText:"确定",cancelText:"取消",justOkText:"知道了"},Popconfirm:{cancelText:"取消",okText:"确定"},Transfer:{searchPlaceholder:"请输入搜索内容",itemUnit:"项",itemsUnit:"项",remove:"删除",selectCurrent:"全选当页",removeCurrent:"删除当页",selectAll:"全选所有",removeAll:"删除全部",selectInvert:"反选当页"},Upload:{uploading:"文件上传中",removeFile:"删除文件",uploadError:"上传错误",previewFile:"预览文件",downloadFile:"下载文件"},Empty:{description:"暂无数据"},Icon:{icon:"图标"},Text:{edit:"编辑",copy:"复制",copied:"复制成功",expand:"展开"},PageHeader:{back:"返回"},Form:{optional:"(可选)",defaultValidateMessages:{default:"字段验证错误${label}",required:"请输入${label}",enum:"${label}必须是其中一个[${enum}]",whitespace:"${label}不能为空字符",date:{format:"${label}日期格式无效",parse:"${label}不能转换为日期",invalid:"${label}是一个无效日期"},types:{string:s,method:s,array:s,object:s,number:s,date:s,boolean:s,integer:s,float:s,regexp:s,email:s,url:s,hex:s},string:{len:"${label}须为${len}个字符",min:"${label}最少${min}个字符",max:"${label}最多${max}个字符",range:"${label}须在${min}-${max}字符之间"},number:{len:"${label}必须等于${len}",min:"${label}最小值为${min}",max:"${label}最大值为${max}",range:"${label}须在${min}-${max}之间"},array:{len:"须为${len}个${label}",min:"最少${min}个${label}",max:"最多${max}个${label}",range:"${label}数量须在${min}-${max}之间"},pattern:{mismatch:"${label}与模式不匹配${pattern}"}}},Image:{preview:"预览"}};t.default=c},function(n,t,e){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={items_per_page:"条/页",jump_to:"跳至",jump_to_confirm:"确定",page:"页",prev_page:"上一页",next_page:"下一页",prev_5:"向前 5 页",next_5:"向后 5 页",prev_3:"向前 3 页",next_3:"向后 3 页",page_size:"页码"}},function(n,t){function e(){return n.exports=e=Object.assign?Object.assign.bind():function(n){for(var t=1;t-1}))}));n.length&&w([n[0].path])}),[r]),l.default.createElement(g,{className:"dt-layout-header header_component"},l.default.createElement("div",{className:"dt-header-log-wrapper logo"},l.default.createElement(c.Link,{to:"/page/toolbox"},l.default.createElement("img",{className:"logo_img",src:u.default}),l.default.createElement("span",{className:"system-title"},"Doraemon"))),l.default.createElement("div",{className:"menu_content"},l.default.createElement(p.Menu,{mode:"horizontal",theme:"dark",onClick:function(n){w(n.key)},selectedKeys:i},x.map((function(n){var t=n.children,e=n.name,a=n.path,r=n.icon;return Array.isArray(t)&&t.length>0?l.default.createElement(h,{key:e,title:l.default.createElement("span",null,r,l.default.createElement("span",null,"Navigation Two"))},t.map((function(n){return l.default.createElement(p.Menu.Item,{key:n.path},l.default.createElement(c.Link,{to:n.path},n.icon,l.default.createElement("span",null,n.name)))}))):l.default.createElement(p.Menu.Item,{key:a},l.default.createElement(c.Link,{to:a},r,l.default.createElement("span",null,e)))}))),l.default.createElement("div",null,l.default.createElement("a",{href:(null===m.default||void 0===m.default?void 0:m.default.helpDocUrl)||"",rel:"noopener noreferrer",target:"_blank"},l.default.createElement(d.QuestionCircleOutlined,{className:"help-link"})),l.default.createElement("span",{className:"local-ip ml-20"},"本机IP: ".concat(a)),l.default.createElement(d.SyncOutlined,{className:"refresh-cion",onClick:function(){return k(!0)}}))))}},function(n,t,e){n.exports=e.p+"img/logo.ff9eed58.png"},function(n,t,e){var a=e(88),r=e(3);"string"==typeof a&&(a=[[n.i,a,""]]),n.exports=a.locals||{},n.exports._getContent=function(){return a},n.exports._getCss=function(){return a.toString()},n.exports._insertCss=function(n){return r(a,n)}},function(n,t,e){(t=e(2)(!1)).push([n.i,".header_component .menu_content{-webkit-box-flex:1;-webkit-flex:1;flex:1;display:-webkit-box;display:-webkit-flex;display:flex;-webkit-box-pack:justify;-webkit-justify-content:space-between;justify-content:space-between;-webkit-box-align:center;-webkit-align-items:center;align-items:center;padding:0 20px}.header_component .menu_content .ant-menu{overflow:hidden;font-size:14px}.header_component .help-link{color:#fff;font-size:18px;vertical-align:sub;cursor:pointer}.header_component .refresh-cion{color:#fff;font-size:16px;margin-left:10px}",""]),n.exports=t},function(n,t,e){n.exports={default:e(90),__esModule:!0}},function(n,t,e){var a=e(11),r=a.JSON||(a.JSON={stringify:JSON.stringify});n.exports=function(n){return r.stringify.apply(r,arguments)}},function(n,t,e){"use strict";t.__esModule=!0;var a=o(e(92)),r=o(e(117));function o(n){return n&&n.__esModule?n:{default:n}}t.default=function(n,t){if(Array.isArray(n))return n;if((0,a.default)(Object(n)))return function(n,t){var e=[],a=!0,o=!1,i=void 0;try{for(var l,s=(0,r.default)(n);!(a=(l=s.next()).done)&&(e.push(l.value),!t||e.length!==t);a=!0);}catch(n){o=!0,i=n}finally{try{!a&&s.return&&s.return()}finally{if(o)throw i}}return e}(n,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")}},function(n,t,e){n.exports={default:e(93),__esModule:!0}},function(n,t,e){e(44),e(55),n.exports=e(116)},function(n,t,e){"use strict";var a=e(95),r=e(96),o=e(14),i=e(25);n.exports=e(46)(Array,"Array",(function(n,t){this._t=i(n),this._i=0,this._k=t}),(function(){var n=this._t,t=this._k,e=this._i++;return!n||e>=n.length?(this._t=void 0,r(1)):r(0,"keys"==t?e:"values"==t?n[e]:[e,n[e]])}),"values"),o.Arguments=o.Array,a("keys"),a("values"),a("entries")},function(n,t){n.exports=function(){}},function(n,t){n.exports=function(n,t){return{value:t,done:!!n}}},function(n,t,e){var a=e(45);n.exports=Object("z").propertyIsEnumerable(0)?Object:function(n){return"String"==a(n)?n.split(""):Object(n)}},function(n,t,e){var a=e(12),r=e(11),o=e(99),i=e(15),l=e(19),s=function(n,t,e){var c,d,p,b=n&s.F,u=n&s.G,f=n&s.S,m=n&s.P,h=n&s.B,g=n&s.W,x=u?r:r[t]||(r[t]={}),w=x.prototype,k=u?a:f?a[t]:(a[t]||{}).prototype;for(c in u&&(e=t),e)(d=!b&&k&&void 0!==k[c])&&l(x,c)||(p=d?k[c]:e[c],x[c]=u&&"function"!=typeof k[c]?e[c]:h&&d?o(p,a):g&&k[c]==p?function(n){var t=function(t,e,a){if(this instanceof n){switch(arguments.length){case 0:return new n;case 1:return new n(t);case 2:return new n(t,e)}return new n(t,e,a)}return n.apply(this,arguments)};return t.prototype=n.prototype,t}(p):m&&"function"==typeof p?o(Function.call,p):p,m&&((x.virtual||(x.virtual={}))[c]=p,n&s.R&&w&&!w[c]&&i(w,c,p)))};s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,n.exports=s},function(n,t,e){var a=e(100);n.exports=function(n,t,e){if(a(n),void 0===t)return n;switch(e){case 1:return function(e){return n.call(t,e)};case 2:return function(e,a){return n.call(t,e,a)};case 3:return function(e,a,r){return n.call(t,e,a,r)}}return function(){return n.apply(t,arguments)}}},function(n,t){n.exports=function(n){if("function"!=typeof n)throw TypeError(n+" is not a function!");return n}},function(n,t,e){n.exports=!e(18)&&!e(48)((function(){return 7!=Object.defineProperty(e(49)("div"),"a",{get:function(){return 7}}).a}))},function(n,t,e){var a=e(28);n.exports=function(n,t){if(!a(n))return n;var e,r;if(t&&"function"==typeof(e=n.toString)&&!a(r=e.call(n)))return r;if("function"==typeof(e=n.valueOf)&&!a(r=e.call(n)))return r;if(!t&&"function"==typeof(e=n.toString)&&!a(r=e.call(n)))return r;throw TypeError("Can't convert object to primitive value")}},function(n,t,e){n.exports=e(15)},function(n,t,e){"use strict";var a=e(105),r=e(50),o=e(54),i={};e(15)(i,e(9)("iterator"),(function(){return this})),n.exports=function(n,t,e){n.prototype=a(i,{next:r(1,e)}),o(n,t+" Iterator")}},function(n,t,e){var a=e(17),r=e(106),o=e(53),i=e(30)("IE_PROTO"),l=function(){},s=function(){var n,t=e(49)("iframe"),a=o.length;for(t.style.display="none",e(112).appendChild(t),t.src="javascript:",(n=t.contentWindow.document).open(),n.write("