diff --git a/.github/workflows/gui-build.yml b/.github/workflows/gui-build.yml new file mode 100644 index 000000000..48040b5d3 --- /dev/null +++ b/.github/workflows/gui-build.yml @@ -0,0 +1,125 @@ +# DeepSeek GUI(Tauri)桌面安装包:Windows NSIS + macOS DMG +# 无需实体 Mac;macOS 作业在 GitHub-hosted runner 上执行。 +name: DeepSeek GUI Build + +on: + workflow_dispatch: + push: + branches: [main, master] + paths: + - 'Deepseek-GUI/**' + - 'crates/tui/**' + - 'crates/tui-core/**' + - '.github/workflows/gui-build.yml' + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +jobs: + build-gui: + name: GUI (${{ matrix.label }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + label: macos-arm64 + sidecar: deepseek-tui + artifact: deepseek-gui-macos-arm64 + - os: windows-latest + label: windows-x64 + sidecar: deepseek-tui.exe + artifact: deepseek-gui-windows-x64 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: Deepseek-GUI/package-lock.json + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Rust cache + uses: Swatinem/rust-cache@v2 + with: + cache-bin: false + + - name: Install Linux system dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y libdbus-1-dev pkg-config libwebkit2gtk-4.1-dev \ + libappindicator3-dev librsvg2-dev patchelf + + - name: Build deepseek-tui sidecar + run: cargo build --release -p deepseek-tui + + - name: Install frontend dependencies + working-directory: Deepseek-GUI + run: npm ci + + - name: Build frontend dist + working-directory: Deepseek-GUI + run: npm run build + + - name: Stage sidecar for Tauri bundle + shell: bash + run: | + set -euo pipefail + mkdir -p Deepseek-GUI/src-tauri/bin + if [ "${{ runner.os }}" = "Windows" ]; then + cp -f target/release/deepseek-tui.exe Deepseek-GUI/src-tauri/bin/deepseek-tui.exe + else + cp -f target/release/deepseek-tui Deepseek-GUI/src-tauri/bin/deepseek-tui + chmod +x Deepseek-GUI/src-tauri/bin/deepseek-tui + fi + + - name: Tauri bundle + working-directory: Deepseek-GUI + run: npm run tauri:build + + - name: Collect installer artifacts (macOS) + if: runner.os == 'macOS' + shell: bash + run: | + set -euo pipefail + mkdir -p "Deepseek-GUI/artifacts/${{ matrix.artifact }}" + shopt -s nullglob + for f in Deepseek-GUI/src-tauri/target/release/bundle/dmg/*.dmg; do + cp -f "$f" "Deepseek-GUI/artifacts/${{ matrix.artifact }}/" + done + for f in Deepseek-GUI/src-tauri/target/release/bundle/macos/*.app; do + ditto -c -k --sequesterRsrc --keepParent "$f" \ + "Deepseek-GUI/artifacts/${{ matrix.artifact }}/$(basename "$f").zip" + done + ls -la "Deepseek-GUI/artifacts/${{ matrix.artifact }}/" + + - name: Collect installer artifacts (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $dest = "Deepseek-GUI/artifacts/${{ matrix.artifact }}" + New-Item -ItemType Directory -Force -Path $dest | Out-Null + $nsis = Get-ChildItem "Deepseek-GUI/src-tauri/target/release/bundle/nsis/*.exe" -ErrorAction SilentlyContinue + $msi = Get-ChildItem "Deepseek-GUI/src-tauri/target/release/bundle/msi/*.msi" -ErrorAction SilentlyContinue + if (-not $nsis) { throw "NSIS installer not found" } + Copy-Item $nsis.FullName $dest + if ($msi) { Copy-Item $msi.FullName $dest } + Get-ChildItem $dest + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.artifact }} + path: Deepseek-GUI/artifacts/${{ matrix.artifact }}/ + if-no-files-found: error diff --git a/.gitignore b/.gitignore index f81c444e1..76badb4ab 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,5 @@ docs/*_PLAN.md # direnv .envrc .direnv + +!Deepseek-GUI/scripts/** diff --git a/Deepseek-GUI/.gitignore b/Deepseek-GUI/.gitignore new file mode 100644 index 000000000..82242858b --- /dev/null +++ b/Deepseek-GUI/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +src-tauri/target/ +src-tauri/gen/ diff --git a/Deepseek-GUI/README.md b/Deepseek-GUI/README.md new file mode 100644 index 000000000..88eb44d20 --- /dev/null +++ b/Deepseek-GUI/README.md @@ -0,0 +1,799 @@ +# DeepSeek GUI + +DeepSeek CLI/TUI 的桌面图形版本,基于 **Tauri + React + TypeScript** 构建。 +通过本地 HTTP/SSE 运行时(`deepseek serve --http`)与 DeepSeek agent 内核通信, +提供三栏 IDE 式界面:左侧文件树、中间代码查看、右侧多会话聊天。 + +## 功能概览 + +- **多会话管理**:水平标签栏,新建 / 切换 / 重命名(双击)/ 关闭归档 / 恢复。 +- **流式对话**:实时显示助手回复、推理(思考)块、工具调用 / 命令执行 / 文件变更卡片。 +- **思考块可视化**:流式实时计时 + 动画,完成后折叠显示「已思考 X.Xs」。 +- **审批交互**:工具/命令需确认时弹出审批对话框,支持允许/拒绝/记住选择。 +- **三栏 IDE**:文件树(含删除)、代码查看(多标签、可关闭)、聊天面板实时联动。 +- **@ 文件引用**:输入 `@` 触发工作区文件补全,键盘导航选择并插入相对路径。 +- **图片粘贴**:粘贴图片自动存入 `.deepseek/attachments/` 并插入 `@路径` 供 agent 分析。 +- **斜杠命令**:`/compact`、`/fork`、`/review`、`/diff`、`/sessions`、`/help`。 +- **任务 / 自动化**:创建并管理后台自主任务,查看状态、耗时、计数。 +- **技能 / MCP**:技能启用/禁用开关;只读展示 MCP 服务器连接状态。 +- **历史会话浏览器**:搜索后端持久化历史会话,恢复为新线程或删除。 +- **命令面板**:`Ctrl/Cmd+K` 模糊检索并执行全部命令与视图切换。 +- **配置中心**:管理 DeepSeek API Key、Base URL,连接测试,多套配置档。 + +## 目录结构 + +``` +Deepseek-GUI/ +├─ src/ 前端(React + TS) +│ ├─ api/ 运行时 API 客户端、Tauri 桥接、类型定义 +│ ├─ components/ 各 UI 组件(聊天、文件树、任务、技能、会话、命令面板等) +│ ├─ state/ 会话状态 Hook(SSE 订阅与聚合) +│ └─ styles.css 全局样式 +├─ src-tauri/ Tauri 壳(Rust):进程托管、文件系统命令、配置读写 +└─ dist/ 前端构建产物 +``` + +## 构建与运行 + +> **路径说明**:仓库目录已统一为无空格路径 `E:\Coding\DeekSeel-TUI-GUI`,可直接在本目录构建,无需 `C:\dsgui` 目录联结。 +> Windows 构建需将 `D:\Config\mingw64\bin` 与 Rust GNU 工具链加入 `PATH`(本机 Rust 安装在 `D:\Config\rust`)。 + +### 一键打包(推荐) + +```powershell +cd E:\Coding\DeekSeel-TUI-GUI\Deepseek-GUI +npm install # 首次或依赖变更时 +.\scripts\build-release.ps1 +``` + +脚本依次:`cargo build --release -p deepseek-tui` → `npm run build` → 复制 `deepseek-tui.exe` 到 `src-tauri/bin/` → `npm run tauri:build`(安装包内会带上 sidecar)。 + +### 手动分步 + +```powershell +# 1. 安装依赖 +cd E:\Coding\DeekSeel-TUI-GUI\Deepseek-GUI +npm install + +# 2. 构建后端 sidecar(仓库根目录) +cd E:\Coding\DeekSeel-TUI-GUI +cargo build --release -p deepseek-tui + +# 3. 构建前端 +cd Deepseek-GUI +npm run build + +# 4. 复制 sidecar 并打包 +Copy-Item ..\target\release\deepseek-tui.exe src-tauri\bin\deepseek-tui.exe -Force +npm run tauri:build # 生成 .msi 与 .exe 安装器 +npm run tauri:build -- --no-bundle # 仅生成可执行 deepseek-gui.exe +``` + +产物位置: + +- 可执行:`src-tauri/target/release/deepseek-gui.exe` +- 安装器:`src-tauri/target/release/bundle/{msi,nsis}/` + +开发模式:`npm run tauri:dev`。 + +## 注释 / 编码注意 + +- 本 README 必须以 **UTF-8** 保存;编辑时请使用整文件写入,避免按片段替换导致中文损坏。 + +## 更新记录 + +### 001 +- 更新时间:2026-06-19 11:00:00 +- 涉及模块:项目脚手架 +- 功能变更:初始化 Tauri + React + TypeScript + Vite 工程,搭建桌面壳与前端骨架。 +- 实现说明:配置 tauri.conf.json、Vite、TS;建立 src/ 与 src-tauri/ 基本结构。 + +### 002 +- 更新时间:2026-06-19 11:10:00 +- 涉及模块:运行时 API 客户端 +- 功能变更:实现与 `deepseek serve --http` 通信的客户端,封装线程、回合、用量等接口。 +- 实现说明:新增 api/client.ts、api/types.ts,统一请求与错误处理、Bearer 鉴权。 + +### 003 +- 更新时间:2026-06-19 11:20:00 +- 涉及模块:SSE 事件流 +- 功能变更:订阅线程事件流,按 item 聚合增量并落定,支持断线续传。 +- 实现说明:新增 api/events.ts 与 state/useConversation.ts,按 seq 单调去重。 + +### 004 +- 更新时间:2026-06-19 11:30:00 +- 涉及模块:聊天界面 +- 功能变更:消息列表渲染(用户/助手/推理/工具等),输入区发送/转向/打断。 +- 实现说明:新增 MessageItem.tsx、Composer.tsx,按 TurnItemKind 区分样式。 + +### 005 +- 更新时间:2026-06-19 11:40:00 +- 涉及模块:审批交互 +- 功能变更:工具/命令需确认时弹出审批对话框,支持允许/拒绝/记住选择。 +- 实现说明:新增 ApprovalDialog.tsx;useConversation 维护审批队列。 + +### 006 +- 更新时间:2026-06-19 11:50:00 +- 涉及模块:会话管理 +- 功能变更:会话列表的新建、切换、删除(归档)。 +- 实现说明:client 增加线程增删改查;App 维护 threads 与 activeId。 + +### 007 +- 更新时间:2026-06-19 12:00:00 +- 涉及模块:用量统计 +- 功能变更:底部展示输入/输出/缓存 token、回合数与估算成本。 +- 实现说明:回合完成后拉取 /v1/usage 聚合并展示。 + +### 008 +- 更新时间:2026-06-19 12:10:00 +- 涉及模块:配置中心 +- 功能变更:新增设置界面,管理 DeepSeek API Key 与 Base URL。 +- 实现说明:新增 ConfigView.tsx;Tauri 壳读写 ~/.deepseek/config.toml。 + +### 009 +- 更新时间:2026-06-19 12:20:00 +- 涉及模块:连接测试 +- 功能变更:设置中可测试 API Key / Base URL 连通性。 +- 实现说明:Tauri 壳新增 test_connection 命令。 + +### 010 +- 更新时间:2026-06-19 12:25:00 +- 涉及模块:多配置档 +- 功能变更:支持保存多套 API 配置档并切换。 +- 实现说明:壳层读写 ~/.deepseek/gui_profiles.json。 + +### 011 +- 更新时间:2026-06-19 12:30:00 +- 涉及模块:后端进程托管 +- 功能变更:壳启动时自动拉起后端 sidecar,崩溃重启,退出清理。 +- 实现说明:src-tauri 管理子进程与运行时 token、CORS。 + +### 012 +- 更新时间:2026-06-19 12:35:00 +- 涉及模块:三栏 IDE 布局 +- 功能变更:左侧文件树、中间代码查看、右侧聊天的三栏布局。 +- 实现说明:新增 FileTree.tsx、CodeView.tsx;壳新增 pick_folder/list_dir/read_file/set_workspace。 + +### 013 +- 更新时间:2026-06-19 12:40:00 +- 涉及模块:文件树实时更新 +- 功能变更:回合完成后自动刷新文件树,反映 agent 的文件改动。 +- 实现说明:treeTick 自增触发重读盘。 + +### 014 +- 更新时间:2026-06-19 12:45:00 +- 涉及模块:代码查看标签 +- 功能变更:代码查看支持文件标签,可关闭。 +- 实现说明:CodeView 管理打开文件与关闭按钮。 + +### 015 +- 更新时间:2026-06-19 12:50:00 +- 涉及模块:输入交互 +- 功能变更:Enter 发送、Shift+Enter 换行(兼容输入法组合)。 +- 实现说明:Composer onKeyDown 处理 isComposing。 + +### 016 +- 更新时间:2026-06-19 12:55:00 +- 涉及模块:多会话标签栏 +- 功能变更:水平标签栏管理多会话,新建按钮与在线状态指示。 +- 实现说明:chat-tabs 横向滚动布局。 + +### 017 +- 更新时间:2026-06-19 13:00:00 +- 涉及模块:工具卡片 +- 功能变更:工具调用/命令执行/文件变更改为带状态徽标的折叠卡片,文件变更按行着色 diff。 +- 实现说明:UiItem 新增 title;抽出 ToolCard 与 DiffBody,新增 .tool-card* 样式。 + +### 018 +- 更新时间:2026-06-19 13:05:00 +- 涉及模块:会话管理(路线图 P1-3) +- 功能变更:会话支持重命名(双击标签)与查看已归档(归档置灰、关闭按钮变恢复)。 +- 实现说明:App 新增 showArchived;新增 onRenameThread、onRestoreThread。至此 P1 阶段完成。 + +### 019 +- 更新时间:2026-06-19 13:10:00 +- 涉及模块:斜杠命令(路线图 P2-1) +- 功能变更:支持 /compact、/fork、/help;标签栏命令菜单一键执行。 +- 实现说明:client 新增 compactThread、forkThread;App 新增 runSlashCommand,onSend 拦截 / 开头输入。 + +### 020 +- 更新时间:2026-06-19 13:25:00 +- 涉及模块:任务/自动化界面(路线图 P2-2) +- 功能变更:新增「任务/自动化」界面(📋):创建后台任务、卡片展示状态/模型/模式/耗时/错误、可取消、每 3 秒轮询并显示计数。 +- 实现说明:types 新增 Task* 类型;client 新增 listTasks/createTask/cancelTask;新增 TasksView.tsx。 + +### 021 +- 更新时间:2026-06-19 13:40:00 +- 涉及模块:技能/MCP(路线图 P2-3) +- 功能变更:新增「技能/MCP」界面(🧩):技能启用/禁用开关;MCP 只读展示连接状态、命令/URL、工具数。至此 P2 阶段完成。 +- 实现说明:types 新增 Skill*/Mcp* 类型;client 新增 listSkills/setSkillEnabled/listMcpServers;新增 SkillsView.tsx(乐观更新+回滚)。 + +### 022 +- 更新时间:2026-06-19 13:50:00 +- 涉及模块:目录结构 / 文档 +- 功能变更:GUI 目录由 gui/ 重命名为 Deepseek-GUI/,同步更新文档路径引用。 +- 实现说明:同步更新 docs/GUI_DEVELOPMENT_PLAN.md。 + +### 023 +- 更新时间:2026-06-19 13:55:00 +- 涉及模块:文件树 / Tauri 壳 +- 功能变更:文件树新增删除功能(悬浮垃圾桶按钮,确认后删除,目录递归);删除后刷新,若删的是当前文件则关闭代码视图。 +- 实现说明:壳新增 delete_path(拒绝删盘根);tauri.ts 新增 deletePath;FileTree 透传 onChanged/onDeleted。 + +### 024 +- 更新时间:2026-06-19 13:58:00 +- 涉及模块:输入区 Composer(路线图 P3-1 @文件引用) +- 功能变更:输入框支持 @ 文件引用——键入 @ 触发工作区文件下拉,可过滤、键盘导航、Enter/Tab 选中、Esc 关闭,插入 @相对路径。 +- 实现说明:Composer 新增 rootPath;首次触发惰性广度优先遍历(复用 list_dir,上限 2000、深度 6);新增 .mention-pop 样式。 + +### 025 +- 更新时间:2026-06-19 14:05:00 +- 涉及模块:消息渲染(路线图 P3-2 思考块可视化) +- 功能变更:推理块改为「思考卡片」:流式实时计时+动画,完成后折叠显示「已思考 X.Xs」,可展开查看推理文本。 +- 实现说明:UiItem 新增 startedAt/durationMs;useConversation 记录起止时间;MessageItem 抽出 ThinkingCard,新增 .think-card* 样式。 + +### 026 +- 更新时间:2026-06-19 14:15:00 +- 涉及模块:输入区 Composer / Tauri 壳(路线图 P3-3 图片粘贴) +- 功能变更:粘贴图片自动保存到 .deepseek/attachments/ 并插入 @相对路径(运行时回合仅支持文本,故用落盘+引用方案)。 +- 实现说明:壳新增 save_attachment(dir,name,bytes);tauri.ts 新增 saveAttachment;Composer 新增 onPaste。 + +### 027 +- 更新时间:2026-06-19 14:25:00 +- 涉及模块:历史会话(路线图 P3-4 会话历史浏览器) +- 功能变更:新增「历史会话」界面(🕘):搜索历史会话、恢复为新线程、删除。 +- 实现说明:types 新增 Session* 类型;client 新增 listSessions/resumeSession/deleteSession;新增 SessionsView.tsx。 + +### 028 +- 更新时间:2026-06-19 14:35:00 +- 涉及模块:斜杠命令 / Tauri 壳(路线图 P3-5、P3-6、P3-7) +- 功能变更:新增 /diff(模态框展示工作区 git diff,按行着色)与 /review(构造审查提示词发起回合,可带 @文件);P3-5(/undo /restore)因运行时无对应接口,沿用 /fork 作等价替代并在 /help 说明。 +- 实现说明:壳新增 git_diff(dir);tauri.ts 新增 gitDiff;新增 DiffModal.tsx;runSlashCommand 扩展 diff/review/sessions。 + +### 029 +- 更新时间:2026-06-19 14:45:00 +- 涉及模块:命令面板(路线图 P3-8) +- 功能变更:新增命令面板——Ctrl/Cmd+K 唤出,模糊检索全部命令与视图切换,键盘导航。至此 P3 阶段全部完成。 +- 实现说明:新增 CommandPalette.tsx 与 .palette*/.modal-overlay 样式;App 新增 showPalette、全局 keydown 监听与 paletteCommands。 + +### 030 +- 更新时间:2026-06-19 14:55:00 +- 涉及模块:右侧面板视图切换(缺陷修复) +- 功能变更:修复「点开第一个面板再点第二个仍显示第一个」的问题——任务/技能/历史/设置改为互斥显示,同一时刻仅一个,再次点击当前面板收起回到聊天。 +- 实现说明:App 新增 toggleView 互斥切换函数,工具栏按钮与命令面板视图入口统一改走该逻辑。已重建前端并重新打包安装器。 + +### 031 +- 更新时间:2026-06-19 15:05:00 +- 涉及模块:文档 / 编码修复 +- 功能变更:修复本 README 因按片段替换导致全文中文损坏的问题,整文件以分块写入+二进制拼接方式重建为正确 UTF-8。 +- 实现说明:经测试整文件写入对大体积中文会损坏,改用约 1.8KB 分块写入并用 Python 二进制拼接;后续维护本文件请沿用分块或小步写入避免编码损坏。 + +### 032 +- 更新时间:2026-06-19 15:20:00 +- 涉及模块:代码查看 CodeView / Tauri 壳 +- 功能变更:中间栏代码区改为可编辑——文本文件可直接修改;标签栏新增「保存」按钮与 Ctrl+S 快捷键;未保存时显示 ● 标记,关闭前确认;二进制/超大截断文件仍为只读。 +- 实现说明:Tauri 壳新增 write_file(path, content);tauri.ts 新增 writeFile;CodeView 由只读高亮改为 textarea 编辑器,Tab 插入 4 空格;新增 .code-editor/.code-dirty 样式。已 npm run build 验证。 + +### 033 +- 更新时间:2026-06-19 15:35:00 +- 涉及模块:代码编辑 CodeView +- 功能变更:修复可编辑模式下代码全白无高亮的问题——改用 CodeMirror(VS Code 暗色主题),编辑时按文件类型着色(HTML/CSS/JS/TS/JSON/MD/PY/RS 等),保留行号、折叠、Ctrl+S 保存。 +- 实现说明:引入 @uiw/react-codemirror 与对应语言包;CodeView 用 ResizeObserver 自适应中间栏高度。已 npm run build 验证。 + +### 034 +- 更新时间:2026-06-19 15:50:00 +- 涉及模块:App / SnakeGame +- 功能变更:移除开发期彩蛋「贪吃蛇」——删除右上角 🐍 按钮、SnakeGame 组件及相关样式;中间栏仅保留代码编辑。 +- 实现说明:删除 src/components/SnakeGame.tsx;App.tsx 去掉 showGame 状态与切换逻辑。已 npm run build 验证。 + +### 035 +- 更新时间:2026-06-19 16:05:00 +- 涉及模块:代码编辑 CodeView +- 功能变更:新增代码字号缩放——标签栏提供 − / + 按钮与当前字号显示(点击字号重置为 13px);支持 Ctrl+滚轮缩放;字号范围 10–24px 并持久化到 localStorage。 +- 实现说明:CodeMirror 通过 EditorView.theme 动态设置 fontSize;新增 .code-zoom* 样式。已 npm run build 验证。 + +### 036 +- 更新时间:2026-06-19 16:25:00 +- 涉及模块:全局 UI / App 布局 / styles.css +- 功能变更:仿 Cursor 优化界面——新增左侧活动栏(资源管理器/历史/设置)、VS Code 暗色配色、Chat 面板标题栏与 ghost 图标按钮、底部状态栏(工作区/文件/Token/后端状态)、会话标签下划线风格、消息与输入区视觉 refinement;侧边栏可折叠。 +- 实现说明:grid 布局改为 活动栏+侧栏+编辑区+Chat;新增 .activity-bar/.icon-btn/.status-bar/.app-shell 等样式;Token 用量移至状态栏。已 npm run build 验证。 + +### 037 +- 更新时间:2026-06-19 16:40:00 +- 涉及模块:App 会话标签 / 右侧面板 +- 功能变更:修复在「任务/技能/设置」等子页面时点击会话标签无反应的问题——点击标签现在会自动切回聊天视图并切换会话;新建会话同样回到聊天;标签对比度提高便于识别当前会话。 +- 实现说明:新增 showChatView/selectThread;标签 onClick 改走 selectThread。已 npm run build 验证。 + +### 038 +* 更新时间:2026-06-19 14:33:59 +* 涉及模块:编辑器 / useConversation / App(路线图 P4-1) +* 功能变更:中间栏支持多文件标签;Agent 回合结束后自动打开本回合修改的文件;状态栏显示已打开标签数量。 +* 实现说明:新增 EditorPanel、useEditorTabs、workspacePaths 路径解析;useConversation 在 turn.completed 收集 file_change 路径;CodeView 支持 embedded 多实例挂载保留未保存状态。 + +### 039 +* 更新时间:2026-06-19 14:33:59 +* 涉及模块:FileTree / Tauri 壳(路线图 P4-2) +* 功能变更:文件树支持新建文件/文件夹、重命名;删除后同步关闭编辑器标签,重命名同步更新标签路径。 +* 实现说明:壳新增 create_file/create_dir/rename_path;FileTree 工具栏与行内重命名按钮;useEditorTabs.renameFile。 + +### 040 +* 更新时间:2026-06-19 14:33:59 +* 涉及模块:App 会话设置(路线图 P4-3) +* 功能变更:Chat 面板新增 per-thread 开关:Shell、信任模式、自动批准(allow_shell/trust_mode/auto_approve)。 +* 实现说明:ThreadRecord 扩展字段;patchThread 本地合并;chat-modelbar 下方 thread-settings 复选框。 + +### 041 +* 更新时间:2026-06-19 14:33:59 +* 涉及模块:布局 / QuickOpen / 状态栏(路线图 P4-4) +* 功能变更:左栏与 Chat 分栏可拖拽调整宽度(持久化);Ctrl+P 快速打开文件;状态栏展示 Git 分支与变更计数。 +* 实现说明:useResizablePanels 写 CSS 变量;QuickOpen 递归索引工作区;client.getWorkspaceStatus。 + +### 042 +* 更新时间:2026-06-19 14:33:59 +* 涉及模块:TasksView / AutomationsView / SkillsView / client(路线图 P4-5) +* 功能变更:任务页新增「定时自动化」子页签(CRUD、运行/暂停/恢复);技能页展示 MCP 工具列表。 +* 实现说明:AutomationsView + /v1/automations API 封装;SkillsView 调用 listMcpTools。 + +### 043 +* 更新时间:2026-06-19 14:33:59 +* 涉及模块:斜杠命令 / 命令面板(路线图 P4-6) +* 功能变更:扩展 /clear /model /models /rename /cost;/help 更新;命令面板新增「快速打开文件」。 +* 实现说明:/clear 以 fork 等价替代无 HTTP 的清空接口;/cost 调用 getUsage 弹窗展示。已 npm run build 验证。 + +### 044 +* 更新时间:2026-06-19 14:37:07 +* 涉及模块:useConversation / threadConvCache(路线图 P5-1) +* 功能变更:多会话状态缓存——切换标签时保留消息列表,SSE 从 latest_seq 续传,避免每次从 0 全量回放。 +* 实现说明:新增 threadConvCache.ts;useConversation 切换线程时读写快照;SSE 订阅起始 seq 取自缓存。 + +### 045 +* 更新时间:2026-06-19 14:37:07 +* 涉及模块:ThreadSearch / client(路线图 P5-2) +* 功能变更:新增会话搜索——Ctrl+Shift+P 或 Chat 栏 ⌕ 按钮,调用 /v1/threads/summary?search= 模糊检索并切换。 +* 实现说明:新增 ThreadSearch.tsx;client.searchThreads;命令面板增加入口。 + +### 046 +* 更新时间:2026-06-19 14:37:07 +* 涉及模块:NoticeList / MessageItem(路线图 P5-3) +* 功能变更:展示 sandbox.denied 与 coherence.state 系统通知;文件变更卡片点击可在编辑器打开对应文件。 +* 实现说明:useConversation 解析两类事件为 notices;NoticeList 组件;ToolCard 支持 openChangedFile。 + +### 047 +* 更新时间:2026-06-19 14:37:07 +* 涉及模块:i18n / App 状态栏(路线图 P5-4) +* 功能变更:新增中英文界面切换(状态栏 中文/EN 按钮,localStorage 持久化);核心 UI 文案 i18n 化。 +* 实现说明:src/i18n/index.ts 轻量 t() 函数;App 主要标签与占位符接入 locale。 + +### 048 +* 更新时间:2026-06-19 14:37:07 +* 涉及模块:会话 system_prompt / 文档(路线图 P5-5) +* 功能变更:Chat 面板可编辑并保存 per-thread system_prompt;docs/GUI_DEVELOPMENT_PLAN.md 路线图同步为 P0–P5 已完成。 +* 实现说明:thread-prompt 折叠编辑区 + patchThread;已 npm run build 验证。 + +### 049 +* 更新时间:2026-06-19 14:41:28 +* 涉及模块:分栏布局 / useResizablePanels / styles.css +* 功能变更:修复 Chat 面板过宽或窗口较窄时中间编辑器与文件树被挤没、界面看似「只剩 Chat」的问题;状态栏新增 ⊞ 重置布局按钮。 +* 实现说明:grid 使用 minmax 保证中间栏至少 200px;启动与 resize 时 clamp sidebar/chat 宽度;ide 填满 ide-resize-host。 + +### 050 +* 更新时间:2026-06-19 14:43:09 +* 涉及模块:发布打包(Tauri bundle) +* 功能变更:重新构建前端 dist 并打包桌面安装器,生成最新 MSI 与 NSIS setup 安装包。 +* 实现说明:经由 C:\dsgui junction(规避路径空格)执行 npm run tauri:build;产物位于 src-tauri/target/release/bundle/{msi,nsis}/,版本 0.1.0_x64(MSI 7.77MB、NSIS setup 5.24MB、deepseek-gui.exe 22.12MB)。 + +### 051 +* 更新时间:2026-06-19 14:46:00 +* 涉及模块:App.tsx(命令面板)/ 代码审查 +* 功能变更:修复 paletteCommands 的 useMemo 依赖数组缺失 rootPath,导致「快速打开文件」命令的 disabled 状态在打开文件夹后不及时更新的问题。 +* 实现说明:审查中核对 SSE sandbox.denied/coherence.state 字段与后端一致、tsc --noEmit 通过;其余为不影响功能的性能/缓存建议(见会话审查结论)。 + +### 052 +* 更新时间:2026-06-19 15:05:00 +* 涉及模块:App.tsx / api/client.ts / workspacePaths.ts / i18n / styles.css +* 功能变更:修复「换文件夹后 Agent 仍在旧目录创建文件」:打开文件夹时等待后端重启并自动新建绑定该目录的会话;新建会话始终携带 workspace;会话与资源管理器目录不一致时显示警告条。 +* 实现说明:根因是每个 thread 创建时 workspace 写死且 PATCH 不可改;chooseFolder 调用 waitForBackend + createThread(dir);文件路径解析改用 activeThread.workspace。 + +### 053 +* 更新时间:2026-06-19 15:14:13 +* 涉及模块:发布打包(Tauri bundle) +* 功能变更:重新构建含 workspace 同步修复(#052)的桌面安装包。 +* 实现说明:npm run build + C:\dsgui junction 下 npm run tauri:build;产物 0.1.0_x64(MSI 7.77MB、NSIS 5.24MB、exe 22.12MB)。 + +### 054 +* 更新时间:2026-06-19 15:25:00 +* 涉及模块:App.tsx / ApprovalDialog / runtime_threads.rs +* 功能变更:修复「已开自动批准/信任仍反复弹审批」:GUI 在 auto_approve/trust_mode 下静默代批且不弹窗;remember 与 PATCH 同步 active_turn;记住此选择默认勾选。 +* 实现说明:根因是 approval.required 始终 SSE 推送且 remember 未更新进行中的回合 flags;后端 sync_active_turn_approval_flags + 前端 showApprovalDialog 条件与 auto-approve effect。 + +### 055 +* 更新时间:2026-06-19 15:32:00 +* 涉及模块:FileTree / styles.css +* 功能变更:移除资源管理器顶部「+文件 / +文件夹」按钮;改为在文件夹、空白区域或文件上右键弹出菜单新建(新建文件/新建文件夹),条目上另含重命名与删除。 +* 实现说明:TreeContextMenu 固定定位;右键目标目录为所点文件夹或其父目录;空目录「(空)」行亦可右键新建。 + +### 056 +* 更新时间:2026-06-19 15:40:00 +* 涉及模块:useConversation / App.tsx / workspacePaths.ts +* 功能变更:Agent 创建项目时资源管理器实时更新:每个 write_file 等工具完成即刷新文件树并打开新文件,不再等整轮结束;运行中每 2s 轻量轮询兜底。 +* 实现说明:新增 fileChangeTick/lastFileChangePaths;item.completed 聚合路径后立即 notify;extractPaths 兼容 file_path 字段。 + +### 057 +* 更新时间:2026-06-19 16:05:00 +* 涉及模块:RulesView / cursorRules.ts / project_context.rs / App.tsx / styles.css +* 功能变更:新增 Cursor 风格「项目规则」:在 `.cursor/rules/*.mdc` 管理规则(description、globs、alwaysApply、正文),活动栏与 Chat 工具栏入口;后端自动合并规则到 Agent 上下文。 +* 实现说明:格式与 Cursor Rules 兼容;保存后下一条消息生效(prompts 每轮重载 project context);支持 `.cursorrules` legacy 文件。 + +### 058 +* 更新时间:2026-06-19 16:35:00 +* 涉及模块:App.tsx / workspaceSessions.ts +* 功能变更:打开项目时类似 Cursor:自动恢复该项目上次 Agent 会话,无历史则新建;标签栏仅显示当前项目会话;跨项目搜索会话时同步切换资源管理器目录。 +* 实现说明:`ds_workspace_threads` 持久化项目→会话映射;`openWorkspace` 替代每次打开文件夹都新建;启动时按 `ds_root` 恢复;`switchToThread` 支持 syncWorkspace。 + +### 059 +* 更新时间:2026-06-19 15:50:00 +* 涉及模块:发布打包(Tauri bundle / deepseek-tui) +* 功能变更:重新构建前端 dist、deepseek-tui release 与桌面安装器(含项目规则 #057、按项目恢复会话 #058 等近期改动)。 +* 实现说明:`cargo build --release -p deepseek-tui` + 真实路径 `npm run build` + `C:\dsgui` junction 下 `npm run tauri:build`;`deepseek-tui.exe` 已复制至 release 目录与 GUI 同目录运行;产物 0.1.0_x64(MSI 7.78MB、NSIS 5.25MB、gui 22.13MB、tui 61.88MB)。 + +### 060 +* 更新时间:2026-06-19 16:10:00 +* 涉及模块:App.tsx / styles.css +* 功能变更:模型/模式选择与安全开关(Shell、信任、自动批准、系统提示词)从 Chat 顶栏移至底部输入区下方,布局对齐 Cursor Agent 面板。 +* 实现说明:新增 `chat-main`/`chat-footer`/`chat-bottom-bar`;顶栏仅保留标题与会话标签;系统提示词展开区在输入框上方。 + +### 061 +* 更新时间:2026-06-19 15:55:00 +* 涉及模块:发布打包(Tauri bundle) +* 功能变更:重新构建前端与桌面安装器(含 Chat 底部设置栏 #060 等近期 GUI 改动)。 +* 实现说明:真实路径 `npm run build` + `C:\dsgui` 下 `npm run tauri:build`;产物 0.1.0_x64(MSI 7.78MB、NSIS 5.25MB、gui 22.13MB、tui 61.88MB)。 + +### 062 +* 更新时间:2026-06-19 16:20:00 +* 涉及模块:Composer.tsx / App.tsx / styles.css +* 功能变更:Chat 输入区改为 Cursor 风格一体化 Composer:圆角容器、底栏 ∞ Agent / 模型 pill 选择、圆形 ↑ 发送;Shell/信任/自动批准/系统提示词收入 ⋯ 菜单。 +* 实现说明:移除独立 chat-bottom-bar;输入框自动增高;focus 时容器高亮边框。 + +### 063 +* 更新时间:2026-06-19 16:25:00 +* 涉及模块:发布打包(Tauri bundle) +* 功能变更:重新构建并打包桌面安装器(含 Cursor 风格一体化 Composer #062)。 +* 实现说明:`npm run build` + `C:\dsgui` 下 `npm run tauri:build`;产物 0.1.0_x64(MSI 7.78MB、NSIS 5.25MB、gui 22.13MB、tui 61.88MB)。 + +### 065 +* 更新时间:2026-06-19 16:40:00 +* 涉及模块:SettingsView / App.tsx / ConfigView / TasksView / RulesView / SkillsView / styles.css +* 功能变更:Chat 顶栏归档/连接/任务/规则/技能图标移入统一「设置」页;Cursor 风格左侧分类导航(模型、连接、任务、规则、技能、聊天);活动栏移除独立规则入口。 +* 实现说明:新增 SettingsView 整合各子模块 embedded 模式;后端连接与「显示已归档会话」在设置内;Chat 顶栏仅保留搜索、斜杠命令与可点击状态点。 + +### 066 +* 更新时间:2026-06-19 16:45:00 +* 涉及模块:App.tsx +* 功能变更:Agent 创建/修改文件时不再自动打开编辑器标签,仅刷新左侧文件树;用户仍可通过文件树或变更卡片手动打开。 +* 实现说明:移除 fileChangeTick/usageTick 中对 `editor.openFilesBatch` 的调用,保留 treeTick 增量刷新与运行中 2s 轮询。 + +### 067 +* 更新时间:2026-06-19 16:50:00 +* 涉及模块:EditorPanel / useEditorTabs / styles.css +* 功能变更:编辑器标签支持批量关闭(仿 Cursor/VS Code):关闭、关闭其他、关闭左侧、关闭右侧、关闭全部;标签右键或 ⋯ 菜单触发;含未保存确认。 +* 实现说明:useEditorTabs 新增 closeOthers/closeToRight/closeToLeft;EditorPanel 右侧 ⋯ 与右键 TabCloseMenu。 + +### 068 +* 更新时间:2026-06-19 17:05:00 +* 涉及模块:runtime_threads.rs / threadTitle.ts / App.tsx / ThreadSearch.tsx / ThreadList.tsx / i18n +* 功能变更:Agent 会话命名仿 Cursor:未发送消息时标签显示「新对话」而非 `thr_` id;首条用户消息首行自动截断为标题(32 字);历史无标题会话在 list/get 时补全。 +* 实现说明:后端 `derive_thread_title_from_text` + `start_turn` 首回合写入 title;前端 `formatThreadTabTitle` 统一展示,`onSend` 合并 API 返回 thread 并乐观更新。 + +### 069 +* 更新时间:2026-06-19 17:15:00 +* 涉及模块:发布打包(Tauri bundle) +* 功能变更:重新构建并打包桌面安装器(含 Cursor 风格会话命名 #068)。 +* 实现说明:`cargo build --release -p deepseek-tui` + 真实路径 `npm run build` + `C:\dsgui` 下 `npm run tauri:build`;`deepseek-tui.exe` 已复制至 release 与 GUI 同目录;产物 0.1.0_x64(MSI 7.78MB、NSIS 5.25MB、gui 22.13MB、tui 61.83MB)。 + +### 070 +* 更新时间:2026-06-19 17:25:00 +* 涉及模块:App.tsx / SettingsView.tsx / styles.css +* 功能变更:设置页从中栏右侧 Chat 区域移至中间编辑器主区域显示(仿 Cursor);打开设置时 Chat 面板保持可见可对话。 +* 实现说明:`pane-center` 在 `showSettings` 时渲染 `SettingsView`,否则渲染 `EditorPanel`;补充 `.pane-center .settings-shell` 满高样式。 + +### 071 +* 更新时间:2026-06-19 17:40:00 +* 涉及模块:TitleMenuBar / EditorEmptyState / EditorPanel / App.tsx / styles.css +* 功能变更:仿 Cursor 顶栏菜单(文件/编辑/选择/视图/转到/运行/终端/帮助)与中央搜索条;编辑器空状态改为居中引导 + 可点击快捷键列表。 +* 实现说明:菜单仅接线已有能力(打开文件夹、快速打开、命令面板、设置、会话搜索等);Undo/终端等占位 disabled;不要求实现 Cursor 全量菜单功能。 + +### 072 +* 更新时间:2026-06-19 17:50:00 +* 涉及模块:发布打包(Tauri bundle) +* 功能变更:重新构建并打包桌面安装器(含顶栏菜单 + 编辑器空状态 #071、设置居中 #070、会话命名 #068)。 +* 实现说明:`cargo build --release -p deepseek-tui` + 真实路径 `npm run build` + `C:\dsgui` 下 `npm run tauri:build`;`deepseek-tui.exe` 已复制至 release 同目录;产物 0.1.0_x64(MSI 7.79MB、NSIS 5.25MB、gui 22.13MB、tui 61.83MB)。 + +### 073 +* 更新时间:2026-06-19 18:05:00 +* 涉及模块:tauri.conf.json / WindowControls / TitleMenuBar / capabilities / styles.css +* 功能变更:方案 A 自定义标题栏:`decorations: false`,菜单与最小化/最大化/关闭合并为单行 Cursor 风格顶栏;空白区可拖动窗口、双击最大化。 +* 实现说明:`WindowControls` 组件 + `data-tauri-drag-region` 拖拽区;`theme: Dark`;补充 window 权限;已重新打包 0.1.0_x64(MSI 7.79MB、NSIS 5.26MB、gui 22.14MB、tui 61.83MB)。 + +### 074 +* 更新时间:2026-06-19 18:15:00 +* 涉及模块:TitleMenuBar / App.tsx / styles.css +* 功能变更:顶栏搜索框改为三列网格真正居中;设置齿轮移至顶栏右侧(窗口按钮左侧);活动栏移除重复设置入口。 +* 实现说明:`grid-template-columns: 1fr minmax(280px,480px) 1fr`;`title-menu-icon` 切换设置页;已重新打包 0.1.0_x64(MSI 7.79MB、NSIS 5.26MB、gui 22.14MB、tui 61.83MB)。 + +### 075 +* 更新时间:2026-06-19 18:25:00 +* 涉及模块:fileIcons.tsx / FileTree.tsx / styles.css +* 功能变更:左侧资源管理器仿 Cursor/VS Code:按扩展名 SVG 彩色文件图标(JS/TS/RS/MD 等)、文件夹主题色、Chevron 展开箭头、22px 行高、选中左侧蓝条、子级缩进引导线。 +* 实现说明:新增 `FileTypeIcon`/`FolderIcon`/`TreeChevron`;移除 emoji 图标;已重新打包 0.1.0_x64(MSI 7.79MB、NSIS 5.26MB、gui 22.14MB、tui 61.83MB)。 + +### 076 +* 更新时间:2026-06-19 18:35:00 +* 涉及模块:uiZoom / useUiZoom / StatusZoom / CodeView / App.tsx / TitleMenuBar / capabilities +* 功能变更:全局界面缩放仿 Cursor:Tauri `setWebviewZoom` 整窗缩放;状态栏 − / 100% / 🔍+ 控件;Ctrl+=/−/0 与 Ctrl+滚轮;视图菜单增缩放项;移除编辑器局部字号缩放。 +* 实现说明:缩放持久化 `ds_ui_zoom`(50%–250%);已重新打包 0.1.0_x64(MSI 7.79MB、NSIS 5.26MB、gui 22.14MB、tui 61.83MB)。 + +### 077 +* 更新时间:2026-06-19 19:10:00 +* 涉及模块:SettingsView.tsx / styles.css +* 功能变更:设置页布局优化:内容区居中列(max-width 680px)、四周留白(32/40/48px)、各 Tab 卡片纵向等距;连接/聊天区块统一卡片样式。 +* 实现说明:新增 `settings-panel-inner` 容器;嵌入视图移除重复 padding;已重新打包 0.1.0_x64。 + +### 078 +* 更新时间:2026-06-19 19:45:00 +* 涉及模块:EditorEmptyState / styles.css / runtime_threads.rs / App.tsx / useConversation.ts / client.ts +* 功能变更:编辑器空状态改为居中卡片 + 四周留白 + 快捷键与装饰图标并排(窄屏换行);修复转向时 `Thread is not loaded` HTTP 400。 +* 实现说明:后端 `prepare_active_turn_engine` 在 steer/interrupt 前重载引擎并恢复 active_turn;前端切换线程时对照 API 校正 running 状态,steer 失败时 resume 重试或降级为 startTurn。 + +### 079 +* 更新时间:2026-06-19 20:05:00 +* 涉及模块:ConfigView.tsx +* 功能变更:编辑已配置档案时 API Key 输入框显示 `********` 掩码,不再空白;保存/测试时掩码未改动则不覆盖原 Key。 +* 实现说明:新增 `KEY_MASK` 与 `resolveApiKeyForSubmit`;掩码态用 `type=text` 显示星号,聚焦清空、失焦恢复;已重新打包 0.1.0_x64。 + +### 080 +* 更新时间:2026-06-19 20:20:00 +* 涉及模块:runtimeStatus.ts / MessageItem.tsx / App.tsx +* 功能变更:聊天区「状态」消息中文化,如串行执行工具、会话上下文已同步等;界面为中文时自动翻译后端英文 status。 +* 实现说明:新增 `translateRuntimeStatus`,精确匹配 + 正则覆盖常见引擎状态文案;已重新打包 0.1.0_x64(NSIS 5.26MB、MSI 7.79MB、gui 22.15MB、tui 62.21MB)。 + +### 081 +* 更新时间:2026-06-19 20:35:00 +* 涉及模块:useTranscriptAutoScroll.ts / App.tsx +* 功能变更:发送/转向消息后聊天区自动滚到底部;流式回复时在底部则持续跟随;切换会话时滚至最新消息。 +* 实现说明:新增 `useTranscriptAutoScroll` Hook,绑定 `.transcript` 容器 ref;已重新打包 0.1.0_x64(NSIS 5.26MB、MSI 7.79MB、gui 22.15MB、tui 62.21MB)。 + +### 082 +* 更新时间:2026-06-19 20:50:00 +* 涉及模块:RulesView.tsx / tauri.ts / FileTree.tsx +* 功能变更:修复新增第二条项目规则时「保存失败: undefined」;保存时不再对已存在的 `.cursor/rules` 目录重复 createDir。 +* 实现说明:根因是目录已存在时 createDir 报错,且 Tauri reject 为 string 导致 message 为 undefined;新增 `formatInvokeError` 统一解析错误文案;已重新打包 0.1.0_x64。 + +### 083 +* 更新时间:2026-06-19 21:05:00 +* 涉及模块:CodeView.tsx / package.json +* 功能变更:编辑器补充 Java/C/C++/SQL/XML 等语法高亮;`.java` 不再整页同色,关键字/字符串/注释分色(VS Code 暗色主题)。 +* 实现说明:新增 `@codemirror/lang-java`、`lang-cpp`、`lang-sql`、`lang-xml`;扩展 `langExtension` 映射;已重新打包 0.1.0_x64。 + +### 064 +* 更新时间:2026-06-19 16:30:00 +* 涉及模块:MessageItem.tsx / styles.css +* 功能变更:用户消息改为 Cursor 风格圆角 pill:去掉「我」标签与气泡框,全宽胶囊条 + 右侧 ↵ 图标;助手消息保持无框正文。 +* 实现说明:新增 `UserMessagePill` 组件与 `.user-msg-pill` 样式。 + +### 084 +* 更新时间:2026-06-19 22:30:00 +* 涉及模块:project_context.rs / ruleCompliance.ts / useRuleCompliance.ts / RuleComplianceBanner.tsx / App.tsx / cursorRules.ts / i18n / styles.css +* 功能变更:项目规则从「仅注入提示词」升级为「可执行合规」:alwaysApply 规则(如必须写 README)在回合结束后自动检测;缺 README 变更记录时自动发起跟进回合,仍缺口则显示横幅一键补充。 +* 实现说明:后端解析 .mdc frontmatter 并注入 Mandatory 前言;前端 loadCursorRules + detectReadmeChangelogGap;useRuleCompliance 防重复自动跟进;新增 readmeChangelogRuleTemplate 模板。 + +### 085 +* 更新时间:2026-06-19 23:15:00 +* 涉及模块:ruleCompliance/* / cursorRules.ts / RulesView.tsx / useRuleCompliance.ts / RuleComplianceBanner.tsx / project_context.rs / i18n / styles.css +* 功能变更:规则合规扩展为可插拔检查器注册表;支持 5 类 compliance(readme_changelog、code_comments、database_schema、tests_required、api_docs);.mdc 新增 frontmatter compliance: 字段;多项缺口合并自动跟进;规则页提供 5 种快捷模板。 +* 实现说明:无 explicit compliance 时仍按关键词回退匹配;新增检查器只需在 checkers.ts 注册并实现 detect/buildPrompt;注释类规则无法路径验证,回合结束后触发 Agent 自检。 + +### 086 +* 更新时间:2026-06-19 17:55:00 +* 涉及模块:EditorPanel.tsx +* 功能变更:修复编辑器标签右键菜单「关闭 / 关闭其他 / 关闭全部」等项点击无效。 +* 实现说明:根因是 document mousedown 在 click 前关闭浮层且未排除 editor-tab-menu-float;新增 menuFloatRef 并在菜单上 stopPropagation。 + +### 087 +* 更新时间:2026-06-19 18:10:00 +* 涉及模块:codemirrorLang.ts / CodeView.tsx / package.json +* 功能变更:.properties / .ini / .env 使用专用 properties 语法高亮(键、值、注释分色);.yaml/.toml 改用 legacy-modes 高亮。 +* 实现说明:新增 @codemirror/legacy-modes 与 langExtensionsForPath;修复此前误用 markdown 导致 application.properties 几乎无高亮。 + +### 088 +* 更新时间:2026-06-19 18:25:00 +* 涉及模块:styles.css(title-menu-bar) +* 功能变更:修复顶栏「帮助」菜单与中央搜索框文字重叠。 +* 实现说明:搜索条改为绝对居中(仿 Cursor);左侧菜单 max-width + overflow:hidden,搜索按钮 z-index 与 pointer-events 分离。 + +### 089 +* 更新时间:2026-06-19 18:35:00 +* 涉及模块:ComposerPillDropdown.tsx / Composer.tsx / styles.css +* 功能变更:Plan/Agent/YOLO 与模型切换改为自定义深色下拉,修复 WebView2 原生 select 白底低对比、选项重叠问题。 +* 实现说明:向上展开菜单、当前项 ✓ 标记、模式附带中英文说明副标题;已重新打包 0.1.0_x64。 + +### 090 +* 更新时间:2026-06-19 19:00:00 +* 涉及模块:uiZoom.ts / useUiZoom.ts / main.tsx / styles.css(title-menu-bar) +* 功能变更:界面缩放对齐 Cursor/VS Code:按 zoom level 步进(每级约 20%,1.2^level);顶栏菜单缩放时不再竖排换行。 +* 实现说明:持久化改为 ds_ui_zoom_level,自动迁移旧 ds_ui_zoom;顶栏改为三列 grid + nowrap 溢出裁剪;监听 tauri://scale-change 在 DPI 变化后重应用缩放;浏览器首帧前同步恢复 zoom。 + +### 091 +* 更新时间:2026-06-19 20:30:00 +* 涉及模块:src-tauri/lsp/* / lsp/* / useEditorLsp.ts / CodeView.tsx / api/tauri.ts / package.json +* 功能变更:编辑器一步到位接入 LSP IntelliSense(补全、悬停、诊断);Rust stdio 桥 + @codemirror/lsp-client;支持 rust-analyzer、pyright、gopls、clangd、typescript-language-server。 +* 实现说明:Tauri 命令 lsp_start_session/lsp_send/lsp_stop_session;前端 TauriLspTransport + 会话池按 workspace+语言复用;需本机 PATH 安装对应 language server;Java 暂未默认 jdtls。 + +### 092 +* 更新时间:2026-06-19 21:00:00 +* 涉及模块:styles.css(title-menu-bar) +* 功能变更:修复界面缩放时顶栏菜单文字拆成上下两行的问题。 +* 实现说明:菜单 trigger 设 inline-flex + nowrap + min-width:max-content + flex:0 0 auto,禁止 flex 压扁;nav 用 max-content;搜索条 min-width 降至 120px 让出左侧空间;顶栏改 min-height 32px。 + +### 093 +* 更新时间:2026-06-19 21:30:00 +* 涉及模块:TitleMenuBar.tsx / useUiZoom.ts / styles.css +* 功能变更:缩放或窗口变窄时,右侧放不下的菜单(终端、帮助等)自动隐藏,避免与搜索框重叠。 +* 实现说明:ResizeObserver + ds-ui-zoom 事件重算可见数量;从右向左 display:none;与搜索条保留 16px 间距。 + +### 094 +* 更新时间:2026-06-19 22:00:00 +* 涉及模块:打包发布 +* 功能变更:重新打包 0.1.0_x64,含顶栏缩放溢出菜单自动隐藏(093)。 +* 实现说明:NSIS 5.39MB、MSI 7.94MB、gui 22.5MB、tui 61.8MB;产物路径 C:\dsgui\Deepseek-GUI\src-tauri\target\release\bundle\ + +### 095 +* 更新时间:2026-06-19 22:30:00 +* 涉及模块:providerPresets.ts / ConfigView.tsx +* 功能变更:模型配置服务商扩展为 17 项(DeepSeek/OpenAI/NVIDIA NIM/OpenRouter/Groq/Together/Moonshot/智谱/硅基流动/Ollama/vLLM/SGLang 等);切换预设自动填充 Base URL 与默认模型;模型名称 datalist 按服务商给出候选。 +* 实现说明:OpenAI 兼容网关仍写 provider=openai;与 deepseek-tui ProviderKind 对齐;自定义 URL 时回落「自定义」项。 + +### 096 +* 更新时间:2026-06-19 23:15:00 +* 涉及模块:tabCompletionExtension.ts / tabCompletionSettings.ts / tabCompletionService.ts / TabSettingsPanel.tsx / SettingsView.tsx / CodeView.tsx / main.rs / api/tauri.ts / styles.css +* 功能变更:新增 Cursor Tab 风格 AI 内联补全:幽灵文本预览、Tab 接受、Ctrl+→ 逐词接受、Esc 取消;设置页新增 Tab 分类(开关、注释内建议、空白建议、忽略 glob);Tauri 命令 tab_complete 调用 DeepSeek chat completions。 +* 实现说明:与 LSP IntelliSense 互补;默认忽略 *.md 与 generated;TS/Python 自动 import 预留占位;需 config.toml 配置 API Key 与 default_text_model。 + +### 097 +* 更新时间:2026-06-19 23:45:00 +* 涉及模块:docs/GUI-TUI-ROADMAP.md +* 功能变更:新增 GUI 对齐 TUI 功能路线图文档,分四阶段(快赢 / 配置面板 / 新 API / 打磨)与 M1–M4 里程碑。 +* 实现说明:含 30+ 项任务 ID、工作量、优先级、HTTP API 缺口与 Top 5 开工建议;详见 `Deepseek-GUI/docs/GUI-TUI-ROADMAP.md`。 + +### 098 +* 更新时间:2026-06-20 00:15:00 +* 涉及模块:slashCommands.ts / executeSlashCommand.ts / UsageModal.tsx / Composer.tsx / App.tsx / reasoningEffort.ts / main.rs / styles.css / ConfigView.tsx +* 功能变更:M1 阶段一落地:斜杠命令扩展(/mode /trust /retry /export /workspace /task /provider /profile /attach /tokens 等);Composer 斜杠自动补全;Shift+Tab 推理强度切换;/cost|/tokens 用量弹窗;Tauri pick_file。 +* 实现说明:命令逻辑抽至 executeSlashCommand;/help 从注册表生成;YOLO 模式自动开 trust+auto_approve;会话 JSON 导出为浏览器下载。 + +### 099 +* 更新时间:2026-06-20 01:30:00 +* 涉及模块:config_bridge.rs / main.rs / McpSettingsPanel.tsx / HooksPanel.tsx / NetworkPanel.tsx / SubagentsPanel.tsx / JobsPanel.tsx / SettingsView.tsx / SkillsView.tsx / slashCommands.ts / executeSlashCommand.ts / api/tauri.ts / styles.css +* 功能变更:M2 完成:MCP/Hooks/Network 配置面板(Tauri 读写本地配置,保存后 restart_backend);斜杠 /mcp /hooks /network;M3 起步:Subagents 只读面板、Jobs 占位(展示运行中 Tasks);Skills 页跳转 MCP 管理。 +* 实现说明:config_bridge 读 ~/.deepseek 下 mcp.json 与 config.toml [hooks]/[network];subagents 读工作区 .deepseek/subagents.v1.json 每 5s 轮询;Jobs 说明 HTTP 尚无 /v1/jobs 差距。 + +### 100 +* 更新时间:2026-06-20 01:30:00 +* 涉及模块:docs/GUI-TUI-ROADMAP.md +* 功能变更:路线图标记 M2(2.1–2.3)完成;M3 部分交付(3.1 只读 Jobs、3.3 Subagents 本地读)。 +* 实现说明:3.2 集成终端、3.4 RLM 仍待 HTTP API 或后续 sprint。 + +### 101 +* 更新时间:2026-06-20 02:45:00 +* 涉及模块:runtime_api.rs / runtime_threads.rs / tools/spec.rs / core/engine.rs / JobsPanel.tsx / SubagentsPanel.tsx / api/client.ts / api/types.ts +* 功能变更:M3 续:新增 HTTP API /v1/jobs(list/detail/cancel/stdin)与 /v1/subagents(list/detail/cancel);HTTP 运行时挂载共享 Shell/Subagent 管理器;GUI Jobs/Subagents 面板升级为可操作 UI。 +* 实现说明:RuntimeThreadManager.attach_shell_manager/subagent_manager;引擎 RuntimeToolServices 复用同一实例;Subagents 面板 API 失败时回退读 subagents.v1.json;集成终端(3.2)/RLM(3.4)仍待后续。 + +### 102 +* 更新时间:2026-06-20 03:30:00 +* 涉及模块:rlm/session.rs / runtime_threads.rs / runtime_api.rs / pty.rs / main.rs / RlmPanel.tsx / TerminalPanel.tsx / SettingsView.tsx / api/client.ts / package.json / styles.css / slashCommands.ts +* 功能变更:M3 收尾:GET /v1/rlm/sessions 与 RlmPanel;Tauri PTY + xterm 集成终端(Terminal Tab);斜杠 /rlm /terminal;共享 RLM session store 挂载到 HTTP 运行时。 +* 实现说明:portable-pty 0.8;pty-output 事件推送;终端默认 PowerShell(Windows)或 \;需重装 deepseek-tui 以启用 RLM/Jobs API。 + +### 103 +* 更新时间:2026-06-20 04:15:00 +* 涉及模块:发布打包(Tauri bundle / deepseek-tui) +* 功能变更:重新打包 0.1.0_x64,含 M1–M3 全部近期功能(斜杠命令、MCP/Hooks/Network、Jobs/Subagents/RLM、集成终端 PTY+xterm)。 +* 实现说明:cargo build --release -p deepseek-tui + 真实路径 +pm run build + C:\dsgui junction 下 +pm run tauri:build;deepseek-tui.exe 已复制至 release 与 GUI 同目录;产物:NSIS 5.56MB、MSI 8.17MB、gui 22.99MB、tui 62.16MB;路径 C:\dsgui\Deepseek-GUI\src-tauri\target\release\bundle\。 + +### 104 +* 更新时间:2026-06-20 04:35:00 +* 涉及模块:Composer.tsx / styles.css +* 功能变更:修复 Chat 面板较窄时 Composer 底栏 pill 换行上下重叠;输入框增高上限 160px,超出后禁止继续拉高、改为内部滚动。 +* 实现说明:toolbar 改为单行 nowrap + 横向滚动;composer-box flex 纵向布局;textarea max-height 与 JS 常量 COMPOSER_INPUT_MAX_H 对齐。 + +### 105 +* 更新时间:2026-06-20 09:40:00 +* 涉及模块:config_bridge.rs / main.rs / api/tauri.ts / MemoryPanel.tsx / SettingsView.tsx / slashCommands.ts / executeSlashCommand.ts / styles.css +* 功能变更:M4 对齐 TUI——新增记忆/笔记/锚点面板(设置页 Memory Tab),斜杠 /memory /note /anchor 跳转;读写 ~/.deepseek/memory.md 与工作区 .deepseek/notes.md、anchors.md,与 TUI 互通。 +* 实现说明:Tauri 命令 get_memory/save_memory_cmd/get_notes/save_notes_cmd/get_anchors/save_anchors_cmd;条目以 \\n---\\n 分隔;整列表覆盖写入。 + +### 106 +* 涉及模块:executeSlashCommand.ts / slashCommands.ts +* 更新时间:2026-06-20 09:45:00 +* 功能变更:补齐斜杠命令 /agent(开持久子代理,发 agent_open 指令)、/relay(生成 .deepseek/handoff.md 接力)、/settings /config /load /save,以及 /undo /restore(指示模型用 revert_turn 回滚上一回合改动)。 +* 实现说明:均复用现有 onSend/openSettings/setShowSessions,无需新增后端接口;/agent 支持深度前缀 N(0-3)。 + +### 107 +* 更新时间:2026-06-20 09:50:00 +* 涉及模块:config_bridge.rs / main.rs / api/tauri.ts / TrustPanel.tsx / SettingsView.tsx / executeSlashCommand.ts +* 功能变更:新增信任目录面板(设置页 Trust Tab),/trust list|add|remove 打开;读写 ~/.deepseek/workspace-trust.json,按工作区分组,与 TUI workspace_trust 互通。 +* 实现说明:Tauri 命令 get_trust/add_trust_cmd/remove_trust_cmd;路径用 std canonicalize 规范化以匹配 TUI 键(Windows 带 \\?\\ 前缀);/trust on|off 仍切换信任模式。 + +### 108 +* 更新时间:2026-06-20 09:55:00 +* 涉及模块:App.tsx / QueueBar.tsx / executeSlashCommand.ts / slashCommands.ts / styles.css +* 功能变更:新增 Composer 消息队列/暂存(对齐 TUI /queue /stash);回合进行中排队、结束后自动按序发送;暂存停泊到 localStorage 可弹回。队列条展示在 Composer 上方,支持编辑/删除/清空/整体暂存。 +* 实现说明:纯 GUI 本地状态,未改后端;自动排空用 useEffect 监听 conv.running;/queue <消息>|clear|stash、/stash [pop|clear]。 + +### 109 +* 更新时间:2026-06-20 10:05:00 +* 涉及模块:runtime_api.rs / api/client.ts / api/types.ts / SnapshotsModal.tsx / App.tsx / executeSlashCommand.ts / styles.css +* 功能变更:快照还原真打通(对齐 TUI /restore /undo)。后端新增 GET /v1/threads/{id}/snapshots(按会话工作区列出 pre/post-turn 快照)与 POST /v1/threads/{id}/snapshots/restore(还原到指定快照,缺省取最近);GUI 新增 SnapshotsModal 浏览/一键还原;/restore 无参开面板、/restore N 还原第 N 新、/undo 还原最近 pre-turn 快照(不再仅指示模型)。 +* 实现说明:后端直接复用 snapshot::repo::SnapshotRepo(side-git),git 操作放 spawn_blocking;还原前自动打 pre-restore 安全快照便于反悔。已 cargo check 通过并重建 release deepseek-tui.exe(65402071B)覆盖 GUI 同目录 sidecar;前端 npm run build 通过。还原后 GUI 刷新文件树,已打开文件需重新打开以加载新内容。 + +### 110 +* 更新时间:2026-06-20 10:30:00 +* 涉及模块:utils/editorCommands.ts(新增)/ TitleMenuBar.tsx / CodeView.tsx / 发布打包 +* 功能变更:顶部菜单栏仿 VS Code 真正可用。文件:新增 保存(Ctrl+S)/退出;编辑:撤销/重做/剪切/复制/粘贴/查找全部生效;选择:全选/展开选择;视图:新增 集成终端入口;终端:新建终端打开 PTY 终端面板(移除占位提示)。 +* 实现说明:新增「编辑命令总线」editorCommands.ts,通过全局 focusin 跟踪「最后聚焦目标」——CodeMirror 视图走原生 undo/redo/selectAll/selectParentSyntax/openSearchPanel,普通 input/textarea 走 execCommand + navigator.clipboard,统一覆盖代码编辑器与 Composer 输入框;CodeView 用 onCreateEditor 注册视图、卸载时注销,并监听 ds-editor-save 响应菜单保存(仅当前可见标签);终端/退出复用 onOpenSettings("terminal") 与 Tauri getCurrentWindow().close()。已 npm run build 通过并重打 NSIS/MSI 安装包。 + +### 111 +* 更新时间:2026-06-20 10:33:00 +* 涉及模块:TitleMenuBar.tsx +* 功能变更:修复顶部菜单「点了没反应/下拉不出现」。根因:下拉为 position:absolute,被顶栏 .title-menu-nav/.title-menu-left 的 overflow:hidden 裁剪掉,导致看不见也点不到。 +* 实现说明:下拉改为 createPortal 渲染到 document.body,用 position:fixed 按触发按钮 getBoundingClientRect 定位(右溢出钳制);外部点击监听放行 .title-menu-dropdown 内点击,避免 mousedown 提前关闭导致菜单项 onClick 不触发;滚动/缩放时自动关闭防错位。已 npm run build 通过并重打 NSIS/MSI 安装包;需重新安装或重启应用生效。 + +### 112 +* 更新时间:2026-06-20 10:18:00 +* 涉及模块:App.tsx / TitleMenuBar.tsx / TerminalPanel.tsx / SettingsView.tsx / styles.css / src-tauri/main.rs / api/tauri.ts / tabCompletionService.ts / codemirror/tabCompletionExtension.ts / TabSettingsPanel.tsx +* 功能变更:处理三项反馈。①终端改为底部停靠面板(仿 VS Code/Cursor):Ctrl+` 或菜单切换,可拖动高度,从设置页移除终端 Tab。②修复「设置点多了卡死」:终端不再随设置 Tab 反复挂载/重复 spawn PTY,改为首次打开后常驻单实例(切显隐保活)。③补全 Tab 的两项「自动 Import」(TypeScript/Python):启用开关,按语言在补全请求中携带 autoImport,后端 tab_complete 据此在系统提示中要求补全自动补上缺失 import。 +* 实现说明:底部终端在 pane-center 内 center-main(flex:1)+terminal-dock(高度 JS 控制) 布局,TerminalPanel 新增 fill 模式与 ResizeObserver 自适应;终端 everOpened 后保持挂载、用 is-hidden 切显隐以保活 PTY;autoImport 经 tauri.ts→service→extension 透传,main.rs tab_complete 新增 auto_import 参数并拼接 import 指令。已 npm run build + cargo(随 tauri:build)通过并重打 NSIS/MSI 安装包;需重装或重启生效。 + +### 113 +* 更新时间:2026-06-20 10:35:00 +* 涉及模块:App.tsx / TitleMenuBar.tsx / styles.css +* 功能变更:仿 Cursor 三项布局调整。①终端「往左到底」:终端停靠从 pane-center 内移出,改为 .ide 网格底部行(grid-row 2,跨「侧栏+编辑器」两列 grid-column 2/4),活动栏与右侧聊天整列全高。②两边收齐:顶栏新增「左侧栏」「右侧聊天」两个面板开关按钮,右侧聊天可收起(.ide.chat-collapsed 将第 4 列宽设为 0)。③聊天头合并成一行:去掉「CHAT」标题行,标签页与操作图标(历史/搜索//命令/状态点)合并为单行(.chat-top-row + flex order),历史记录入口从活动栏时钟按钮移入右侧聊天头部,新建会话仍用「+」(仿 Cursor)。 +* 实现说明:.ide 新增 grid-template-rows: minmax(0,1fr) auto 与显式行列定位(活动栏/聊天 grid-row 1/3 全高,终端 grid-column 2/4 grid-row 2);新增 chatOpen 状态与 .ide.chat-collapsed / .pane-right.collapsed 样式;TitleMenuBar 新增 onToggleChat/chatOpen 及两枚 SVG 面板切换按钮;聊天头 .chat-top-row 用 flex order 将标签置左、操作置右,历史按钮调用 toggleView("sessions")。已 npm run build 通过并重打 NSIS/MSI 安装包;需重装或重启生效。 + +### 114 +* 更新时间:2026-06-20 11:05:00 +* 涉及模块:App.tsx / TitleMenuBar.tsx / EditorPanel.tsx / PanelToggleButton.tsx / AgentHistoryPanel.tsx / styles.css / useResizablePanels.ts +* 功能变更:移除左侧活动栏(与侧栏开关冲突);左侧收起按钮移至资源管理器 pane-head 最左及侧栏收起后的 IDE 左缘悬浮位(仿 Cursor);顶栏右侧两枚面板开关移除,右侧聊天开关改到聊天头最右;新增 Cursor 风格 Agent 历史模块(搜索、新建会话 Ctrl+N 提示、按今天/昨天/本周/更早分组列表),点击历史图标在右栏全屏展示,选中会话后回到聊天。 +* 实现说明:.ide 网格改为三列(去掉 --act-w 活动栏列),终端 grid-column 1/3;新增 PanelToggleButton 复用组件、AgentHistoryPanel 替代原 SessionsView 全页切换;sidebar-edge-toggle / chat-edge-toggle 在面板收起时提供边缘 reopen;useResizablePanels ACT_W 改为 0。已 npm run build 通过。 + +### 115 +* 更新时间:2026-06-20 11:02:00 +* 涉及模块:发布打包 / tauri.conf.json / scripts/build-release.ps1 / 项目路径 +* 功能变更:文件夹重命名为 `DeekSeel-TUI-GUI`(无空格)后完整重建并打包;安装包现包含 `deepseek-tui.exe` sidecar;新增一键构建脚本。 +* 实现说明:`tauri.conf.json` 增加 `bundle.resources` 将 `bin/deepseek-tui.exe` 打入安装目录;`scripts/build-release.ps1` 串联 tui 编译、前端 build、sidecar 复制与 tauri:build。产物 0.1.0_x64:NSIS 20.44MB、MSI 30.09MB、gui 22.99MB、tui 62.38MB;路径 `Deepseek-GUI\src-tauri\target\release\bundle\`。 + +### 116 +* 更新时间:2026-06-20 11:15:00 +* 涉及模块:TitleMenuBar.tsx / App.tsx / styles.css +* 功能变更:左右侧栏开关移至顶栏菜单同一行(左开关在最左、右开关在最右,仿 Cursor);移除资源管理器 pane-head 与聊天头内的重复开关及收起时的悬浮边缘开关。 +* 实现说明:TitleMenuBar 左侧品牌前、右侧设置按钮前各放一枚 PanelToggleButton;顶栏收起后仍可切换面板。已 npm run build 通过。 + +### 117 +* 更新时间:2026-06-20 12:30:00 +* 涉及模块:ComposerPillDropdown.tsx / Composer.tsx / App.tsx / styles.css +* 功能变更:修复 Chat 底栏 Composer(Agent/模型/推理 pill 与发送按钮)无法点击的问题。 +* 实现说明:分栏 resizer z-index 降至 5、Chat 面板 z-index 6/8,避免 4px 拖拽条误挡底栏;transcript 增加 min-height:0 防止 flex 溢出盖住 footer;pill 下拉 portal 到 body 防 overflow 裁剪;底栏与 pill 触发改用 mousedown;模态浮层移出 .ide 网格;Tauri 无边框窗口为 composer/chat-footer 加 no-drag。已 npm run build 通过。 + +### 118 +* 更新时间:2026-06-20 11:45:00 +* 涉及模块:useResizablePanels.ts / TitleMenuBar.tsx / EditorEmptyState.tsx / styles.css +* 功能变更:修复三栏缩小时 Chat 底栏控件被裁切、左侧栏开关消失、中间空状态挤压三项布局问题。 +* 实现说明:Chat 最小宽度提升至 380px 且拖拽/窗口缩放不再压低于此;顶栏改为四列网格,左右 PanelToggle 独立固定列;中间编辑器空状态改纵向卡片 + container query 窄屏隐藏说明文字;Composer 发送按钮固定右侧不收缩。已 npm run build 通过。 + +### 119 +* 更新时间:2026-06-20 12:05:00 +* 涉及模块:.github/workflows/gui-build.yml / tauri.conf.json / tauri.macos.conf.json / tauri.windows.conf.json / scripts/build-release.sh +* 功能变更:新增 GitHub Actions 云端打包 DeepSeek GUI(Windows NSIS + macOS DMG),无需实体 Mac。 +* 实现说明:sidecar 按平台写入 `tauri.*.conf.json` resources;workflow 在 `macos-latest`/`windows-latest` 构建 deepseek-tui + tauri bundle,Artifacts 上传 `deepseek-gui-macos-arm64`(dmg+app.zip)与 `deepseek-gui-windows-x64`(nsis/msi)。本地 Mac 可用 `scripts/build-release.sh`。 diff --git a/Deepseek-GUI/app-icon-square.png b/Deepseek-GUI/app-icon-square.png new file mode 100644 index 000000000..96ea389be Binary files /dev/null and b/Deepseek-GUI/app-icon-square.png differ diff --git a/Deepseek-GUI/docs/GUI-TUI-ROADMAP.md b/Deepseek-GUI/docs/GUI-TUI-ROADMAP.md new file mode 100644 index 000000000..b44666d11 --- /dev/null +++ b/Deepseek-GUI/docs/GUI-TUI-ROADMAP.md @@ -0,0 +1,210 @@ +# GUI 对齐 TUI 功能路线图 + +> 文档版本:2026-06-19 +> 适用范围:`Deepseek-GUI` 桌面客户端 vs `deepseek-tui` 终端客户端 + +## 背景 + +- **GUI** 通过 `deepseek serve --http` 复用 TUI 同一套 agent 内核。 +- 聊天时 **模型侧工具**(shell、subagent、RLM、MCP 等)大多仍可用。 +- 差距主要在 **用户操作面**:TUI 有 50+ 斜杠命令与完整管理 UI,GUI 目前约 12 条斜杠命令 + 设置页。 + +### HTTP API 现状 + +| 已有 `/v1/*` | 尚未暴露 HTTP | +|--------------|---------------| +| threads / turns / compact / fork / steer / interrupt | jobs(后台 Shell) | +| tasks / automations | subagent 状态 | +| skills(开关)/ MCP(只读) | RLM 会话 | +| sessions / usage / approvals | hooks / network / memory | +| workspace/status | queue / stash / undo | + +--- + +## 阶段划分 + +``` +阶段一(快赢) → 斜杠命令、Composer、设置扩展 +阶段二(配置面板) → MCP / Hooks / Network / Memory +阶段三(新 API) → Jobs / 终端 / Subagent / RLM +阶段四(打磨) → 主题、CLI 桥接、高级调试 +``` + +--- + +## 阶段一:快赢(约 1–2 周) + +目标:常用操作不必回 TUI;不改后端或改动极小。 + +| ID | 功能 | TUI 对照 | 实现方式 | 模块 | 工作量 | 优先级 | +|----|------|----------|----------|------|--------|--------| +| 1.1 | 斜杠命令补齐(第一批) | `/mode` `/trust` `/retry` `/provider` | 扩展 `runSlashCommand` + 命令面板 | `App.tsx` | S | P0 | +| 1.2 | 推理强度快捷切换 | Shift+Tab | Composer 底栏 effort pill | `Composer.tsx` | S | P0 | +| 1.3 | 会话导出 | `/export` | Tauri 聚合 thread JSON 另存为 | `SessionsView` | S | P1 | +| 1.4 | 工作区切换命令 | `/workspace` | `/workspace` + `set_workspace` | `App.tsx` | S | P1 | +| 1.5 | 斜杠自动补全 | slash_menu | Composer 输入 `/` 弹出候选 | 新 `slashCommands.ts` | M | P1 | +| 1.6 | 用量/上下文面板 | `/tokens` `/cost` `/context` | 设置页 Usage 卡片 | 新 `UsagePanel.tsx` | M | P1 | +| 1.7 | Profile 快捷切换 | `/profile` | Composer/顶栏调 `activateProfile` | `ConfigView.tsx` | S | P1 | +| 1.8 | 附件命令 | `/attach` | 文件选择 → `save_attachment` | `Composer.tsx` | S | P2 | + +**1.1 首批斜杠命令行为** + +| 命令 | GUI 行为 | +|------|----------| +| `/mode [plan\|agent\|yolo]` | PATCH thread | +| `/trust [on\|off]` | PATCH `trust_mode` | +| `/retry` | 重发上一条 user 消息 | +| `/provider` | 打开设置 → 模型 | +| `/task` | 打开设置 → 任务 | + +--- + +## 阶段二:配置与管理面板(约 2–3 周) + +目标:TUI 的 `/mcp` `/hooks` `/network` `/memory` 有 GUI 等价物;主要靠 Tauri 读写配置。 + +| ID | 功能 | TUI 对照 | 实现方式 | 模块 | 工作量 | 优先级 | +|----|------|----------|----------|------|--------|--------| +| 2.1 | MCP 完整管理 | `/mcp add/enable/remove/reload` | Tauri 读写 MCP 配置 + 重启 backend | `McpSettingsPanel.tsx` | L | P0 | +| 2.2 | Hooks 管理 | `/hooks` | Tauri 读写 hooks 配置 | `HooksView.tsx` | M | P1 | +| 2.3 | Network 策略 | `/network` | Tauri 读写 network 配置 | `NetworkPanel.tsx` | M | P1 | +| 2.4 | Memory / Note / Anchor | `/memory` `/note` `/anchor` | Tauri 读写本地 memory/notes | `MemoryPanel.tsx` | M | P2 | +| 2.5 | Skills 安装/卸载 | `/skill install/uninstall` | 文件系统 + `/v1/skills` | `SkillsView.tsx` | M | P2 | +| 2.6 | 运行时 LSP 开关 | `/lsp on/off` | config.toml + 编辑器 LSP 联动 | `SettingsView` | S | P2 | +| 2.7 | 信任目录列表 | `/trust list/add/remove` | Tauri 读写 trusted paths | 设置新 Tab | M | P2 | +| 2.8 | Onboarding 向导 | TUI onboarding | 首次启动 Stepper | `Onboarding.tsx` | M | P2 | + +--- + +## 阶段三:需扩展 HTTP API(约 3–4 周) + +目标:补齐 TUI 进程内能力;需改 `crates/tui/src/runtime_api.rs`。 + +| ID | 功能 | TUI 对照 | 建议新路由 | 前端 | 工作量 | 优先级 | +|----|------|----------|------------|------|--------|--------| +| 3.1 | 后台 Shell Jobs | `/jobs` | `GET/POST /v1/jobs`, poll/cancel/stdin | `JobsPanel.tsx` | XL | P0 | +| 3.2 | 集成终端 | 内嵌 shell | xterm.js + Tauri PTY | `TerminalPanel.tsx` | XL | P0 | +| 3.3 | Subagent 面板 | `/subagents` `/agent` | `GET /v1/subagents` | `SubagentsPanel.tsx` | L | P0 | +| 3.4 | RLM 会话面板 | `/rlm` | `GET /v1/rlm/sessions` | `RlmPanel.tsx` | L | P1 | +| 3.5 | Queue / Stash | `/queue` `/stash` | `PATCH /v1/threads/{id}/composer` | Composer 排队条 | L | P1 | +| 3.6 | Patch Undo | `/undo` `/restore` | `POST .../undo`, `GET .../snapshots` | DiffModal 还原 | L | P1 | +| 3.7 | Context 调试 | `/context` `/cycles` | `GET /v1/threads/{id}/context` | 调试抽屉 | M | P2 | +| 3.8 | 会话 Save/Load | `/save` `/load` | 复用 `/v1/sessions` + 导出 | `SessionsView` | M | P2 | + +**推荐顺序**:Jobs 只读面板 → Subagent 面板 → xterm 终端集成。 + +--- + +## 阶段四:体验增强(约 2–3 周,可选) + +| ID | 功能 | TUI 对照 | 说明 | 工作量 | 优先级 | +|----|------|----------|------|--------|--------| +| 4.1 | 主题切换 | `/theme` | 2–3 套 CSS 变量主题 | M | P3 | +| 4.2 | Vim 输入模式 | composer vim | IDE 场景优先级低 | L | P4 | +| 4.3 | 外部编辑器 | `/edit` | 「在 VS Code 打开」 | S | P2 | +| 4.4 | Doctor 诊断 | `deepseek doctor` | 设置页 spawn CLI | M | P2 | +| 4.5 | 非交互 Exec | `deepseek exec` | 任务页脚本任务 | M | P3 | +| 4.6 | PR 预填 | `deepseek pr` | 命令面板从 PR 导入 | M | P3 | +| 4.7 | Tab 补全增强 | — | auto-import、FIM `/beta` | M | P2 | +| 4.8 | 引擎 LSP 钩子 | post-tool lint | 保存时 LSP 诊断 toast | M | P2 | + +--- + +## 里程碑 + +| 里程碑 | 周期 | 交付 | 对齐估算 | +|--------|------|------|----------| +| **M1 命令基线** | 第 1–2 周 | 1.1–1.7 + slash 补全 | ~45% 常用命令 | +| **M2 配置中心** | 第 3–5 周 | 2.1–2.3 MCP/Hooks/Network | ~60% 管理面 | +| **M3 运行时面板** | 第 6–9 周 | 3.1–3.4 Jobs/终端/Subagent/RLM | ~80% 核心能力 | +| **M4 打磨** | 第 10–12 周 | 3.5–3.8 + 4.x | ~90% | + +--- + +## 工作量图例 + +| 代号 | 含义 | 人天(约) | +|------|------|-----------| +| S | 小改 | 1–2 | +| M | 新组件 | 3–5 | +| L | 新面板 + API | 1–2 周 | +| XL | 终端/PTY 等 | 2–3 周 | + +--- + +## 建议优先开工 Top 5 + +1. **1.1** 斜杠命令第一批(`/mode` `/trust` `/retry`) +2. **1.5** Composer 斜杠自动补全 +3. **2.1** MCP 完整管理 +4. **3.3** Subagent 面板(需 API) +5. **3.1** Jobs 只读面板 + +--- + +## GUI 已有、无需重复 + +- 三栏 IDE、多标签编辑器、语法高亮 +- 编辑器 LSP IntelliSense +- Cursor Tab AI 内联补全 +- Rules 编辑器 + Rule Compliance Banner +- 任务/自动化、技能开关、历史会话 +- Plan/Agent/YOLO、审批、steer/interrupt +- @ 文件引用、图片粘贴、Git diff + +--- + +## 风险 + +| 风险 | 缓解 | +|------|------| +| Jobs/Subagent 无 HTTP API | 优先在 `runtime_api.rs` 专开 sprint | +| MCP 改配置需重启 backend | 保存后自动 `restart_backend` | +| Windows PTY 兼容 | 先做 Jobs 输出 WebView,PTY 二期 | + +--- + +## 变更记录 + +| 日期 | 说明 | +|------|------| +| 2026-06-19 | 初版:基于 GUI vs TUI 功能差距分析 | +| 2026-06-20 | **M1 完成**:斜杠命令扩展、自动补全、推理强度、用量弹窗、export/attach/profile | +| 2026-06-20 | **M2 完成**:MCP/Hooks/Network 配置面板 + Tauri config_bridge | +| 2026-06-20 | **M3 部分**:Jobs 占位、Subagents 本地只读 | +| 2026-06-20 | **M3 续**:`/v1/jobs` + `/v1/subagents` HTTP API 与 GUI 可操作面板 | +| 2026-06-20 | **M3 完成**:RLM API、集成终端(PTY+xterm)、/rlm /terminal 斜杠命令 | + +### M1 已完成项 + +| ID | 状态 | +|----|------| +| 1.1 | ✅ 斜杠命令补齐 | +| 1.2 | ✅ 推理强度 Shift+Tab + 底栏 pill | +| 1.3 | ✅ /export 会话 JSON | +| 1.4 | ✅ /workspace | +| 1.5 | ✅ 斜杠自动补全 | +| 1.6 | ✅ UsageModal(/cost / /tokens) | +| 1.7 | ✅ /profile | +| 1.8 | ✅ /attach + pick_file | + +**下一步:M2** — MCP 完整管理(2.1)、Hooks(2.2)、Network(2.3) + +### M2 已完成项 + +| ID | 状态 | +|----|------| +| 2.1 | ✅ MCP 完整管理(McpSettingsPanel + /mcp) | +| 2.2 | ✅ Hooks 管理(HooksPanel + /hooks) | +| 2.3 | ✅ Network 策略(NetworkPanel + /network) | + +### M3 已完成(核心) + +| ID | 状态 | +|----|------| +| 3.1 | ✅ Jobs API + GUI | +| 3.2 | ✅ 集成终端(Tauri PTY + xterm,设置页 Terminal Tab) | +| 3.3 | ✅ Subagents API + GUI | +| 3.4 | ✅ RLM 面板(GET /v1/rlm/sessions + RlmPanel) | + +**下一步:M4** — Queue/Stash、Patch Undo、Context 调试、体验增强 diff --git a/Deepseek-GUI/index.html b/Deepseek-GUI/index.html new file mode 100644 index 000000000..f1204e523 --- /dev/null +++ b/Deepseek-GUI/index.html @@ -0,0 +1,12 @@ + + + + + + DeepSeek GUI + + +
+ + + diff --git a/Deepseek-GUI/package-lock.json b/Deepseek-GUI/package-lock.json new file mode 100644 index 000000000..7b676c3c0 --- /dev/null +++ b/Deepseek-GUI/package-lock.json @@ -0,0 +1,2741 @@ +{ + "name": "deepseek-gui", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "deepseek-gui", + "version": "0.1.0", + "dependencies": { + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/lang-cpp": "^6.0.3", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/lang-rust": "^6.0.2", + "@codemirror/lang-sql": "^6.10.0", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/legacy-modes": "^6.5.3", + "@codemirror/lsp-client": "^6.2.5", + "@tauri-apps/api": "^2.11.1", + "@uiw/codemirror-theme-vscode": "^4.25.10", + "@uiw/react-codemirror": "^4.25.10", + "@xterm/addon-fit": "^0.11.0", + "@xterm/xterm": "^6.0.0", + "dompurify": "^3.4.11", + "highlight.js": "^11.11.1", + "marked": "^18.0.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "vscode-languageserver-protocol": "^3.18.0" + }, + "devDependencies": { + "@tauri-apps/cli": "^2.1.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.0", + "vite": "^6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@codemirror/autocomplete": { + "version": "6.20.3", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.3.tgz", + "integrity": "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.3.tgz", + "integrity": "sha512-JFRiqhKu+bvSkDLI+rUhJwSxQxYb759W5GBezE8Uc8mHLqC9aV/9aTC7yJSqCtB3F00pylrLCwnyS91Ap5ej4Q==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.6.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-cpp": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.3.tgz", + "integrity": "sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/cpp": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.12" + } + }, + "node_modules/@codemirror/lang-java": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.2.tgz", + "integrity": "sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/java": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.5.tgz", + "integrity": "sha512-zD4e5mS+50htS7F+TYjBPsiIFGanfVqg4HyUz6WNFikgOPf2BgKlx+TQedI1w6n/IqRBVBbBWmGFdLB/7uxO4A==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-json": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/json": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-markdown": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz", + "integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.7.1", + "@codemirror/lang-html": "^6.0.0", + "@codemirror/language": "^6.3.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/markdown": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-python": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", + "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.3.2", + "@codemirror/language": "^6.8.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.1", + "@lezer/python": "^1.1.4" + } + }, + "node_modules/@codemirror/lang-rust": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.2.tgz", + "integrity": "sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@lezer/rust": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-sql": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", + "integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@codemirror/lang-xml": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", + "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.4.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0", + "@lezer/xml": "^1.0.0" + } + }, + "node_modules/@codemirror/language": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.12.3.tgz", + "integrity": "sha512-QwCZW6Tt1siP37Jet9Tb02Zs81TQt6qQrZR2H+eGMcFsL1zMrk2/b9CLC7/9ieP1fjIUMgviLWMmgiHoJrj+ZA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.23.0", + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0", + "style-mod": "^4.0.0" + } + }, + "node_modules/@codemirror/legacy-modes": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.3.tgz", + "integrity": "sha512-xCsmIzH78MyWkib9jlPaaun57XNkfbMIhagfaZVd0iLTqlpw3jXaIcbZm72MTmmn64eTZpBVNjbyYh+QXnxRsg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0" + } + }, + "node_modules/@codemirror/lint": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.7.tgz", + "integrity": "sha512-28/+iWLYxKxsvGYhSYL7zaCZqLz5+FFFDq9tVsvGv9kv8RY4fFAchJ5WX9M3YrrRlTIsECjsXPqeNgnSmNP2dg==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.42.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/lsp-client": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@codemirror/lsp-client/-/lsp-client-6.2.5.tgz", + "integrity": "sha512-1EqhGRmCZOV7Me+rRuwwkTuvkNoD4Nz6UcE1yx5gdwTVTLD4D9xIy48MJc0LeBQGFLn/HNRW/pHmet4EAEkJFQ==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.20.0", + "@codemirror/language": "^6.11.0", + "@codemirror/lint": "^6.8.5", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.37.0", + "@lezer/highlight": "^1.2.1", + "marked": "^15.0.12", + "vscode-languageserver-protocol": "^3.17.5" + } + }, + "node_modules/@codemirror/lsp-client/node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@codemirror/search": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.7.1.tgz", + "integrity": "sha512-uMe5UO6PamJtSHrXhhHOzSX3ReWtiJrva6GnPMwSOrZtiExb5X5eExhr2OUZQVvdxPsKpY3Ro2mFbQadpPWmHA==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.37.0", + "crelt": "^1.0.5" + } + }, + "node_modules/@codemirror/state": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.6.0.tgz", + "integrity": "sha512-4nbvra5R5EtiCzr9BTHiTLc+MLXK2QGiAVYMyi8PkQd3SR+6ixar/Q/01Fa21TBIDOZXgeWV4WppsQolSreAPQ==", + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } + }, + "node_modules/@codemirror/theme-one-dark": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz", + "integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@codemirror/view": { + "version": "6.43.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.1.tgz", + "integrity": "sha512-+BIjw/AG3tDQ4pJgTLPYdAW25eDE66YsvM4LKyVPgGzVgZ4a9Wj1SRX8kPVKgBDdPt8oHtZ15F0qx7p0oOHdHw==", + "license": "MIT", + "dependencies": { + "@codemirror/state": "^6.6.0", + "crelt": "^1.0.6", + "style-mod": "^4.1.0", + "w3c-keyname": "^2.2.4" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@lezer/common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.2.tgz", + "integrity": "sha512-sxQE460fPZyU3sdc8lafxiPwJHBzZRy/udNFynGQky1SePYBdhkBl1kOagA9uT3pxR8K09bOrmTUqA9wb/PjSQ==", + "license": "MIT" + }, + "node_modules/@lezer/cpp": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.6.tgz", + "integrity": "sha512-vh9gWWJOXFVY8HBHK3Twzq8MgwG2iN4GSyzBP9sCGTe37P15x2R14VaBQk0VA0ezTRN1KHYBBsHhvpGZ2Xy/pA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/css": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.3.tgz", + "integrity": "sha512-RzBo8r+/6QJeow7aPHIpGVIH59xTcJXp399820gZoMo9noQDRVpJLheIBUicYwKcsbOYoBRoLZlf2720dG/4Tg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/highlight": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.3.0" + } + }, + "node_modules/@lezer/html": { + "version": "1.3.13", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.13.tgz", + "integrity": "sha512-oI7n6NJml729m7pjm9lvLvmXbdoMoi2f+1pwSDJkl9d68zGr7a9Btz8NdHTGQZtW2DA25ybeuv/SyDb9D5tseg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/java": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", + "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/javascript": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.1.3", + "@lezer/lr": "^1.3.0" + } + }, + "node_modules/@lezer/json": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/lr": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.10.tgz", + "integrity": "sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lezer/markdown": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.4.tgz", + "integrity": "sha512-N0SxazMj4k65DBfaf1azqtMZd6u7MqluP84/NZnB/io8Td9aleFmAhz9hcbvSfsxT5tdYlJ5qgv5aMJGY4zEtA==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.5.0", + "@lezer/highlight": "^1.0.0" + } + }, + "node_modules/@lezer/python": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.19.tgz", + "integrity": "sha512-MhQIURHRytsNzP/YXnqpYKW6la6voAH3kyplTOOiCdjyFY6cWWGFVmYVdHIPrElqSDf4iCDktQCockB9FxuhzQ==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/rust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", + "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@lezer/xml": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.2.0", + "@lezer/highlight": "^1.0.0", + "@lezer/lr": "^1.0.0" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz", + "integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz", + "integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz", + "integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz", + "integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz", + "integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz", + "integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz", + "integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz", + "integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz", + "integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz", + "integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz", + "integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz", + "integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz", + "integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz", + "integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz", + "integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz", + "integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz", + "integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz", + "integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz", + "integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz", + "integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz", + "integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz", + "integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz", + "integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz", + "integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz", + "integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tauri-apps/api": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.1.tgz", + "integrity": "sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/cli": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz", + "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==", + "dev": true, + "license": "Apache-2.0 OR MIT", + "bin": { + "tauri": "tauri.js" + }, + "engines": { + "node": ">= 10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + }, + "optionalDependencies": { + "@tauri-apps/cli-darwin-arm64": "2.11.2", + "@tauri-apps/cli-darwin-x64": "2.11.2", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2", + "@tauri-apps/cli-linux-arm64-gnu": "2.11.2", + "@tauri-apps/cli-linux-arm64-musl": "2.11.2", + "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2", + "@tauri-apps/cli-linux-x64-gnu": "2.11.2", + "@tauri-apps/cli-linux-x64-musl": "2.11.2", + "@tauri-apps/cli-win32-arm64-msvc": "2.11.2", + "@tauri-apps/cli-win32-ia32-msvc": "2.11.2", + "@tauri-apps/cli-win32-x64-msvc": "2.11.2" + } + }, + "node_modules/@tauri-apps/cli-darwin-arm64": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz", + "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-darwin-x64": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz", + "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz", + "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-gnu": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz", + "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-arm64-musl": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz", + "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz", + "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-gnu": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz", + "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-linux-x64-musl": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz", + "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-arm64-msvc": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz", + "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-ia32-msvc": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz", + "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tauri-apps/cli-win32-x64-msvc": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz", + "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 OR MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@uiw/codemirror-extensions-basic-setup": { + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-extensions-basic-setup/-/codemirror-extensions-basic-setup-4.25.10.tgz", + "integrity": "sha512-P3vytLlpE62KYSWrMUnwDCv2lvaQDuDZzyj03mHntuHo5bSl34fRZpjTY3kQTPGuXHxkGSYpoPFFj+hMTqaaMQ==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/autocomplete": ">=6.0.0", + "@codemirror/commands": ">=6.0.0", + "@codemirror/language": ">=6.0.0", + "@codemirror/lint": ">=6.0.0", + "@codemirror/search": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@uiw/codemirror-theme-vscode": { + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-theme-vscode/-/codemirror-theme-vscode-4.25.10.tgz", + "integrity": "sha512-5wyYu4/WBsdDVeHStWHnvgkaSQ4X97tdwXETsJQAt/ZSjHY4Gs+mzwMj9azJQnr9uA19yjFtU2PkAcXFQIFOsA==", + "license": "MIT", + "dependencies": { + "@uiw/codemirror-themes": "4.25.10" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + } + }, + "node_modules/@uiw/codemirror-themes": { + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@uiw/codemirror-themes/-/codemirror-themes-4.25.10.tgz", + "integrity": "sha512-Fqiz1HIuDlDftcL+/O53V333UOH6MqQ84VbiQB5egn6u+uDwAqACp1FrdAoi4wgpR3b3TGW4Gr0wIYcrJSSz1A==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@codemirror/language": ">=6.0.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/view": ">=6.0.0" + } + }, + "node_modules/@uiw/react-codemirror": { + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@uiw/react-codemirror/-/react-codemirror-4.25.10.tgz", + "integrity": "sha512-DzgSMwM5qzB7v1FIb4gEeriYt67iiay756/HIOM9mAbeOVK0MO7rqefHf0O5c0269pJKMW7AH9FjclExD23V9w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.6", + "@codemirror/commands": "^6.1.0", + "@codemirror/state": "^6.1.1", + "@codemirror/theme-one-dark": "^6.0.0", + "@uiw/codemirror-extensions-basic-setup": "4.25.10", + "codemirror": "^6.0.0" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.11.0", + "@codemirror/state": ">=6.0.0", + "@codemirror/theme-one-dark": ">=6.0.0", + "@codemirror/view": ">=6.0.0", + "codemirror": ">=6.0.0", + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@xterm/addon-fit": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz", + "integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==", + "license": "MIT" + }, + "node_modules/@xterm/xterm": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.38", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz", + "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/codemirror": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz", + "integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/commands": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/lint": "^6.0.0", + "@codemirror/search": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dompurify": { + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.376", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.376.tgz", + "integrity": "sha512-cUVA7/RvbFTEuw/i3obUwDTRIXojaxkResf+ibByPFxjc6XK3VNtcQXV0NSbAlJ0FMjcJGgftVVB4Qo184EXvA==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/marked": { + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.13.tgz", + "integrity": "sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.48", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.48.tgz", + "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.0.tgz", + "integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.0", + "@rollup/rollup-android-arm64": "4.62.0", + "@rollup/rollup-darwin-arm64": "4.62.0", + "@rollup/rollup-darwin-x64": "4.62.0", + "@rollup/rollup-freebsd-arm64": "4.62.0", + "@rollup/rollup-freebsd-x64": "4.62.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.0", + "@rollup/rollup-linux-arm-musleabihf": "4.62.0", + "@rollup/rollup-linux-arm64-gnu": "4.62.0", + "@rollup/rollup-linux-arm64-musl": "4.62.0", + "@rollup/rollup-linux-loong64-gnu": "4.62.0", + "@rollup/rollup-linux-loong64-musl": "4.62.0", + "@rollup/rollup-linux-ppc64-gnu": "4.62.0", + "@rollup/rollup-linux-ppc64-musl": "4.62.0", + "@rollup/rollup-linux-riscv64-gnu": "4.62.0", + "@rollup/rollup-linux-riscv64-musl": "4.62.0", + "@rollup/rollup-linux-s390x-gnu": "4.62.0", + "@rollup/rollup-linux-x64-gnu": "4.62.0", + "@rollup/rollup-linux-x64-musl": "4.62.0", + "@rollup/rollup-openbsd-x64": "4.62.0", + "@rollup/rollup-openharmony-arm64": "4.62.0", + "@rollup/rollup-win32-arm64-msvc": "4.62.0", + "@rollup/rollup-win32-ia32-msvc": "4.62.0", + "@rollup/rollup-win32-x64-gnu": "4.62.0", + "@rollup/rollup-win32-x64-msvc": "4.62.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-mod": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vscode-jsonrpc": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0.tgz", + "integrity": "sha512-+VvMmQPJhtvJ+8O+zu2JKIRiLxXF8NW7krWgyMGeOHrp4Cn23T5hc0v2LknNeopDOB70wghHAds7mKtcZ0I4Sg==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.0.tgz", + "integrity": "sha512-Zdz+kJ12Iz6tc11xfZyEo501bBATHXrCjmMfnaR3pMnf1CoqZBKIynba3P+/bi9VEdrMbNtAVKYpKhbODvqy+Q==", + "license": "MIT", + "dependencies": { + "vscode-jsonrpc": "9.0.0", + "vscode-languageserver-types": "3.18.0" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.18.0.tgz", + "integrity": "sha512-8TsGPNMIMiiBdkORgRSvLjuiEIiAFtO+KssmYWxQ+uSVvlf7RjK8YKCOjPzZ+YA04jXEV7+7LvkSmHkhpNS99g==", + "license": "MIT" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/Deepseek-GUI/package.json b/Deepseek-GUI/package.json new file mode 100644 index 000000000..8fd8e001f --- /dev/null +++ b/Deepseek-GUI/package.json @@ -0,0 +1,50 @@ +{ + "name": "deepseek-gui", + "private": true, + "version": "0.1.0", + "type": "module", + "description": "DeepSeek 桌面 GUI(方案 A:Tauri + React),复用 deepseek serve --http 运行时 API", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "tauri": "tauri", + "tauri:dev": "tauri dev", + "tauri:build": "tauri build" + }, + "dependencies": { + "@codemirror/autocomplete": "^6.20.3", + "@codemirror/lang-cpp": "^6.0.3", + "@codemirror/lang-css": "^6.3.1", + "@codemirror/lang-html": "^6.4.11", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-javascript": "^6.2.5", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.5.0", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/lang-rust": "^6.0.2", + "@codemirror/lang-sql": "^6.10.0", + "@codemirror/lang-xml": "^6.1.0", + "@codemirror/legacy-modes": "^6.5.3", + "@codemirror/lsp-client": "^6.2.5", + "@tauri-apps/api": "^2.11.1", + "@uiw/codemirror-theme-vscode": "^4.25.10", + "@uiw/react-codemirror": "^4.25.10", + "@xterm/addon-fit": "^0.11.0", + "@xterm/xterm": "^6.0.0", + "dompurify": "^3.4.11", + "highlight.js": "^11.11.1", + "marked": "^18.0.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "vscode-languageserver-protocol": "^3.18.0" + }, + "devDependencies": { + "@tauri-apps/cli": "^2.1.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "@vitejs/plugin-react": "^4.3.4", + "typescript": "^5.7.0", + "vite": "^6.0.0" + } +} diff --git a/Deepseek-GUI/scripts/build-release.ps1 b/Deepseek-GUI/scripts/build-release.ps1 new file mode 100644 index 000000000..396d5d647 --- /dev/null +++ b/Deepseek-GUI/scripts/build-release.ps1 @@ -0,0 +1,45 @@ +# DeepSeek GUI 一键 release 构建脚本(Windows) +# 用法:在 PowerShell 中执行 .\scripts\build-release.ps1 +$ErrorActionPreference = "Stop" + +# Rust / MinGW 工具链(按本机 D 盘安装路径配置) +$RustBin = "D:\Config\rust\rustup\toolchains\stable-x86_64-pc-windows-gnu\bin" +$MingwBin = "D:\Config\mingw64\bin" +$CargoHome = "D:\Config\rust\cargo\bin" +$env:Path = "$MingwBin;$RustBin;$CargoHome;" + $env:Path + +$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot "..\..") +$GuiRoot = Join-Path $RepoRoot "Deepseek-GUI" +$TuiOut = Join-Path $RepoRoot "target\release\deepseek-tui.exe" +$SidecarDir = Join-Path $GuiRoot "src-tauri\bin" +$ReleaseDir = Join-Path $GuiRoot "src-tauri\target\release" + +Write-Host "==> 停止可能占用文件的进程..." +Get-Process deepseek-gui, deepseek-tui -ErrorAction SilentlyContinue | Stop-Process -Force + +Write-Host "==> 构建 deepseek-tui (release)..." +Push-Location $RepoRoot +cargo build --release -p deepseek-tui +Pop-Location + +Write-Host "==> 构建前端 dist..." +Push-Location $GuiRoot +npm run build +Pop-Location + +Write-Host "==> 复制 sidecar 到 src-tauri/bin..." +New-Item -ItemType Directory -Force -Path $SidecarDir | Out-Null +Copy-Item -Force $TuiOut (Join-Path $SidecarDir "deepseek-tui.exe") +Copy-Item -Force $TuiOut (Join-Path $ReleaseDir "deepseek-tui.exe") + +Write-Host "==> Tauri 打包 (NSIS + MSI)..." +Push-Location $GuiRoot +npm run tauri:build +Pop-Location + +Write-Host "" +Write-Host "构建完成。产物:" +Write-Host " GUI: $ReleaseDir\deepseek-gui.exe" +Write-Host " Sidecar: $ReleaseDir\deepseek-tui.exe" +Write-Host " NSIS: $ReleaseDir\bundle\nsis\DeepSeek GUI_0.1.0_x64-setup.exe" +Write-Host " MSI: $ReleaseDir\bundle\msi\DeepSeek GUI_0.1.0_x64_en-US.msi" diff --git a/Deepseek-GUI/scripts/build-release.sh b/Deepseek-GUI/scripts/build-release.sh new file mode 100644 index 000000000..42c486417 --- /dev/null +++ b/Deepseek-GUI/scripts/build-release.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# DeepSeek GUI 一键 release 构建(macOS / Linux) +# 用法:chmod +x scripts/build-release.sh && ./scripts/build-release.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +GUI="$(cd "$(dirname "$0")/.." && pwd)" +TUI_OUT="${ROOT}/target/release/deepseek-tui" +SIDECAR_DIR="${GUI}/src-tauri/bin" +RELEASE_DIR="${GUI}/src-tauri/target/release" + +echo "==> 构建 deepseek-tui (release)..." +(cd "${ROOT}" && cargo build --release -p deepseek-tui) + +echo "==> 构建前端 dist..." +(cd "${GUI}" && npm run build) + +echo "==> 复制 sidecar 到 src-tauri/bin..." +mkdir -p "${SIDECAR_DIR}" +cp -f "${TUI_OUT}" "${SIDECAR_DIR}/deepseek-tui" +chmod +x "${SIDECAR_DIR}/deepseek-tui" +mkdir -p "${RELEASE_DIR}" +cp -f "${TUI_OUT}" "${RELEASE_DIR}/deepseek-tui" +chmod +x "${RELEASE_DIR}/deepseek-tui" + +echo "==> Tauri 打包..." +(cd "${GUI}" && npm run tauri:build) + +echo "" +echo "构建完成。产物目录:" +echo " ${RELEASE_DIR}/bundle/" diff --git a/Deepseek-GUI/src-tauri/Cargo.lock b/Deepseek-GUI/src-tauri/Cargo.lock new file mode 100644 index 000000000..2a8191926 --- /dev/null +++ b/Deepseek-GUI/src-tauri/Cargo.lock @@ -0,0 +1,5327 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "ashpd" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +dependencies = [ + "async-fs", + "async-net", + "enumflags2", + "futures-channel", + "futures-util", + "rand", + "raw-window-handle", + "serde", + "serde_repr", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-fs" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-net" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce8d3bd5823c7504d3f579f13e7b2f3da252fcb938c594d5680ee508bf846f" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "time", + "version_check", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.0", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.0", + "core-foundation", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dbus" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "deepseek-gui" +version = "0.1.0" +dependencies = [ + "portable-pty", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tokio", + "toml 0.9.12+spec-1.1.0", + "ureq", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.2+spec-1.1.0", + "vswhom", + "winreg 0.55.0", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset 0.9.1", + "rustc_version", +] + +[[package]] +name = "filedescriptor" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d" +dependencies = [ + "libc", + "thiserror 1.0.69", + "winapi", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ioctl-rs" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7970510895cee30b3e9128319f2cefd4bde883a39f38baa279567ba3a7eb97d" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", + "pin-utils", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plist" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "portable-pty" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "downcast-rs", + "filedescriptor", + "lazy_static", + "libc", + "log", + "nix", + "serial", + "shared_library", + "shell-words", + "winapi", + "winreg 0.10.1", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rfd" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +dependencies = [ + "ashpd", + "block2", + "dispatch2", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "pollster", + "raw-window-handle", + "urlencoding", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.118", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.13.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serial" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1237a96570fc377c13baa1b88c7589ab66edced652e43ffb17088f003db3e86" +dependencies = [ + "serial-core", + "serial-unix", + "serial-windows", +] + +[[package]] +name = "serial-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f46209b345401737ae2125fe5b19a77acce90cd53e1658cda928e4fe9a64581" +dependencies = [ + "libc", +] + +[[package]] +name = "serial-unix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f03fbca4c9d866e24a459cbca71283f545a37f8e3e002ad8c70593871453cab7" +dependencies = [ + "ioctl-rs", + "libc", + "serial-core", + "termios", +] + +[[package]] +name = "serial-windows" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c6d3b776267a75d31bbdfd5d36c0ca051251caafc285827052bc53bcdc8162" +dependencies = [ + "libc", + "serial-core", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.13.0", + "block2", + "core-foundation", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2616f96cb644bf2c5c456d9de4d5d5100e592d7424c74d8b55c5cb96e359e93" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.118", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe41e015bf8fc4d6477ff4926a0ef769dc64ff34c7b0038b6f7cacae892acb5c" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dom_query", + "dunce", + "glob", + "http", + "infer", + "json-patch", + "log", + "memchr", + "phf", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.2+spec-1.1.0", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + +[[package]] +name = "termios" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d9cf598a6d7ce700a4e6a9199da127e6819a61e64b68609683cc9a01b5683a" +dependencies = [ + "libc", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711a53c2d47bbd818258c498c8dbfe186a2526c631495cfe7e078567f86b8469" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c652a3727a9cbb9a02f707f530b618ce00d0ccd762009c8c23bd191df3c17d" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.0", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset 0.9.1", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots 0.26.11", +] + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.13.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags 2.13.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.8", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +dependencies = [ + "winapi", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.3", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow 1.0.3", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +dependencies = [ + "endi", + "enumflags2", + "serde", + "url", + "winnow 1.0.3", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.118", + "winnow 1.0.3", +] diff --git a/Deepseek-GUI/src-tauri/Cargo.toml b/Deepseek-GUI/src-tauri/Cargo.toml new file mode 100644 index 000000000..231053c6a --- /dev/null +++ b/Deepseek-GUI/src-tauri/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "deepseek-gui" +version = "0.1.0" +description = "DeepSeek 桌面 GUI 壳(Tauri)" +edition = "2021" +rust-version = "1.77" + +# 声明为独立 workspace,避免被仓库根 workspace 纳管 +[workspace] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +toml = "0.9" +# 原生文件夹选择对话框 +rfd = "0.15" +# 轻量 HTTP 客户端,用于「测试连接」直连 DeepSeek API(自带 rustls TLS) +ureq = "2" +# LSP stdio 桥异步 IO +tokio = { version = "1", features = ["sync", "process", "io-util", "rt-multi-thread", "macros", "time"] } +portable-pty = "0.8" + +[features] +# 生产构建时由 Tauri 注入的自定义协议特性 +custom-protocol = ["tauri/custom-protocol"] diff --git a/Deepseek-GUI/src-tauri/build.rs b/Deepseek-GUI/src-tauri/build.rs new file mode 100644 index 000000000..2096c13e6 --- /dev/null +++ b/Deepseek-GUI/src-tauri/build.rs @@ -0,0 +1,4 @@ +// Tauri 构建脚本:生成平台资源与权限清单 +fn main() { + tauri_build::build(); +} diff --git a/Deepseek-GUI/src-tauri/capabilities/default.json b/Deepseek-GUI/src-tauri/capabilities/default.json new file mode 100644 index 000000000..2f503a87b --- /dev/null +++ b/Deepseek-GUI/src-tauri/capabilities/default.json @@ -0,0 +1,17 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "DeepSeek GUI 主窗口的默认权限集合", + "windows": ["main"], + "permissions": [ + "core:default", + "core:window:allow-minimize", + "core:window:allow-maximize", + "core:window:allow-unmaximize", + "core:window:allow-close", + "core:window:allow-toggle-maximize", + "core:window:allow-is-maximized", + "core:window:allow-start-dragging", + "core:webview:allow-set-webview-zoom" + ] +} diff --git a/Deepseek-GUI/src-tauri/icons/128x128.png b/Deepseek-GUI/src-tauri/icons/128x128.png new file mode 100644 index 000000000..41fac6063 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/128x128.png differ diff --git a/Deepseek-GUI/src-tauri/icons/128x128@2x.png b/Deepseek-GUI/src-tauri/icons/128x128@2x.png new file mode 100644 index 000000000..8d17ad949 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/128x128@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/32x32.png b/Deepseek-GUI/src-tauri/icons/32x32.png new file mode 100644 index 000000000..77ccb2246 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/32x32.png differ diff --git a/Deepseek-GUI/src-tauri/icons/64x64.png b/Deepseek-GUI/src-tauri/icons/64x64.png new file mode 100644 index 000000000..7434b99a5 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/64x64.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square107x107Logo.png b/Deepseek-GUI/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 000000000..0ccbcd8be Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square107x107Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square142x142Logo.png b/Deepseek-GUI/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 000000000..c9ad96702 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square142x142Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square150x150Logo.png b/Deepseek-GUI/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 000000000..5c0f9147a Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square150x150Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square284x284Logo.png b/Deepseek-GUI/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 000000000..e9f43e6cc Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square284x284Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square30x30Logo.png b/Deepseek-GUI/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 000000000..55d761fdd Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square30x30Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square310x310Logo.png b/Deepseek-GUI/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 000000000..cd24c8387 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square310x310Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square44x44Logo.png b/Deepseek-GUI/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 000000000..b450f2532 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square44x44Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square71x71Logo.png b/Deepseek-GUI/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 000000000..56f86bfa1 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square71x71Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/Square89x89Logo.png b/Deepseek-GUI/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 000000000..63800f7f1 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/Square89x89Logo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/StoreLogo.png b/Deepseek-GUI/src-tauri/icons/StoreLogo.png new file mode 100644 index 000000000..4712c80ed Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/StoreLogo.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/Deepseek-GUI/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000..2ffbf24b6 --- /dev/null +++ b/Deepseek-GUI/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..7d5154cbc Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..d5bcf6f35 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 000000000..f8cc179f1 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..78706e4ee Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..bbece6538 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 000000000..a7b673458 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..55e539b1e Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..12ca83eb8 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 000000000..2401f184b Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..001ad25d3 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..d52eb3e93 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..c9b69832b Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..48dc46bea Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..bcf9ca08b Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 000000000..3a32961a0 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/Deepseek-GUI/src-tauri/icons/android/values/ic_launcher_background.xml b/Deepseek-GUI/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 000000000..ea9c223a6 --- /dev/null +++ b/Deepseek-GUI/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #fff + \ No newline at end of file diff --git a/Deepseek-GUI/src-tauri/icons/icon.icns b/Deepseek-GUI/src-tauri/icons/icon.icns new file mode 100644 index 000000000..aa45e6dba Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/icon.icns differ diff --git a/Deepseek-GUI/src-tauri/icons/icon.ico b/Deepseek-GUI/src-tauri/icons/icon.ico new file mode 100644 index 000000000..f834a3a94 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/icon.ico differ diff --git a/Deepseek-GUI/src-tauri/icons/icon.png b/Deepseek-GUI/src-tauri/icons/icon.png new file mode 100644 index 000000000..28f4107c5 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/icon.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@1x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 000000000..92b3fd5ae Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 000000000..541ca77c4 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 000000000..541ca77c4 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@3x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 000000000..694fabd46 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@1x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 000000000..aba2ecb52 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 000000000..6fe004fdb Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 000000000..6fe004fdb Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@3x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 000000000..795fc1308 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@1x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 000000000..541ca77c4 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 000000000..1b9e4f22f Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 000000000..1b9e4f22f Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@3x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 000000000..24deafb88 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-512@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 000000000..5c3f40e4d Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-60x60@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 000000000..24deafb88 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-60x60@3x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 000000000..92586b4ea Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-76x76@1x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 000000000..e65f4e729 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-76x76@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 000000000..54de8e1a8 Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/Deepseek-GUI/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 000000000..02f202d3e Binary files /dev/null and b/Deepseek-GUI/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/Deepseek-GUI/src-tauri/src/config_bridge.rs b/Deepseek-GUI/src-tauri/src/config_bridge.rs new file mode 100644 index 000000000..c44c3be8c --- /dev/null +++ b/Deepseek-GUI/src-tauri/src/config_bridge.rs @@ -0,0 +1,452 @@ +//! 读写 ~/.deepseek 下的 MCP / Hooks / Network 配置(供 GUI 设置页使用) + +use serde_json::{json, Value}; +use std::path::{Path, PathBuf}; + +/// 解析 DeepSeek 配置主目录 +pub fn deepseek_home() -> Option { + let home = std::env::var("USERPROFILE") + .ok() + .or_else(|| std::env::var("HOME").ok())?; + Some(PathBuf::from(home).join(".deepseek")) +} + +/// config.toml 路径 +pub fn config_toml_path() -> Option { + deepseek_home().map(|h| h.join("config.toml")) +} + +/// 从 config.toml 读取 mcp.json 路径(缺省 ~/.deepseek/mcp.json) +pub fn mcp_json_path() -> Option { + if let Some(p) = config_toml_path() { + if let Ok(s) = std::fs::read_to_string(&p) { + if let Ok(v) = s.parse::() { + if let Some(custom) = v.get("mcp_config_path").and_then(|x| x.as_str()) { + if !custom.trim().is_empty() { + return Some(PathBuf::from(custom)); + } + } + } + } + } + deepseek_home().map(|h| h.join("mcp.json")) +} + +/// 读取 MCP 配置 JSON +pub fn read_mcp_config() -> Result { + let path = mcp_json_path().ok_or_else(|| "无法定位 MCP 配置路径".to_string())?; + if !path.exists() { + return Ok(json!({ "path": path.to_string_lossy(), "exists": false, "servers": {} })); + } + let s = std::fs::read_to_string(&path).map_err(|e| format!("读取 MCP 配置失败:{e}"))?; + let mut v: Value = serde_json::from_str(&s).map_err(|e| format!("MCP JSON 解析失败:{e}"))?; + if let Some(obj) = v.as_object_mut() { + obj.insert("path".to_string(), json!(path.to_string_lossy())); + obj.insert("exists".to_string(), json!(true)); + if !obj.contains_key("servers") { + if let Some(alt) = obj.get("mcpServers").cloned() { + obj.insert("servers".to_string(), alt); + } + } + } + Ok(v) +} + +/// 保存 MCP 配置 +pub fn save_mcp_config(mut doc: Value) -> Result<(), String> { + let path = mcp_json_path().ok_or_else(|| "无法定位 MCP 配置路径".to_string())?; + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + if let Some(obj) = doc.as_object_mut() { + obj.remove("path"); + obj.remove("exists"); + } + let out = serde_json::to_string_pretty(&doc).map_err(|e| e.to_string())?; + std::fs::write(&path, out).map_err(|e| format!("写入 MCP 配置失败:{e}"))?; + Ok(()) +} + +/// 初始化空 mcp.json +pub fn init_mcp_config(force: bool) -> Result { + let path = mcp_json_path().ok_or_else(|| "无法定位 MCP 配置路径".to_string())?; + if path.exists() && !force { + return Err("MCP 配置已存在".to_string()); + } + let empty = json!({ + "servers": {}, + "timeouts": { "connect_timeout": 10, "execute_timeout": 60, "read_timeout": 120 } + }); + save_mcp_config(empty.clone())?; + Ok(empty) +} + +/// 读取 [hooks] 配置段 +pub fn read_hooks_config() -> Result { + let path = config_toml_path().ok_or_else(|| "无法定位 config.toml".to_string())?; + let mut out = json!({ + "config_path": path.to_string_lossy(), + "enabled": true, + "hooks": [] + }); + if !path.exists() { + return Ok(out); + } + let s = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; + let v = s.parse::().map_err(|e| e.to_string())?; + if let Some(hooks) = v.get("hooks") { + if let Some(en) = hooks.get("enabled").and_then(|x| x.as_bool()) { + out["enabled"] = json!(en); + } + if let Some(arr) = hooks.get("hooks").and_then(|x| x.as_array()) { + let items: Vec = arr.iter().filter_map(toml_to_json).collect(); + out["hooks"] = json!(items); + } + } + Ok(out) +} + +/// 保存 hooks 配置 +pub fn save_hooks_config(enabled: bool, hooks: Value) -> Result<(), String> { + let path = config_toml_path().ok_or_else(|| "无法定位 config.toml".to_string())?; + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + let mut doc: toml::Value = std::fs::read_to_string(&path) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or_else(|| toml::Value::Table(Default::default())); + let root = doc.as_table_mut().ok_or_else(|| "config 根节点无效".to_string())?; + let hooks_arr = hooks + .as_array() + .ok_or_else(|| "hooks 必须为数组".to_string())? + .iter() + .filter_map(json_to_toml) + .collect::>(); + let mut hooks_table = toml::map::Map::new(); + hooks_table.insert("enabled".to_string(), toml::Value::Boolean(enabled)); + hooks_table.insert("hooks".to_string(), toml::Value::Array(hooks_arr)); + root.insert("hooks".to_string(), toml::Value::Table(hooks_table)); + let out = toml::to_string_pretty(&doc).map_err(|e| e.to_string())?; + std::fs::write(&path, out).map_err(|e| e.to_string())?; + Ok(()) +} + +/// 读取 [network] 策略 +pub fn read_network_config() -> Result { + let path = config_toml_path().ok_or_else(|| "无法定位 config.toml".to_string())?; + let mut out = json!({ + "config_path": path.to_string_lossy(), + "default": "prompt", + "allow": [], + "deny": [], + "audit": true + }); + if !path.exists() { + return Ok(out); + } + let s = std::fs::read_to_string(&path).map_err(|e| e.to_string())?; + let v = s.parse::().map_err(|e| e.to_string())?; + if let Some(net) = v.get("network").and_then(|x| x.as_table()) { + if let Some(d) = net.get("default").and_then(|x| x.as_str()) { + out["default"] = json!(d); + } + if let Some(a) = net.get("audit").and_then(|x| x.as_bool()) { + out["audit"] = json!(a); + } + if let Some(arr) = net.get("allow").and_then(|x| x.as_array()) { + out["allow"] = json!(arr.iter().filter_map(|x| x.as_str()).collect::>()); + } + if let Some(arr) = net.get("deny").and_then(|x| x.as_array()) { + out["deny"] = json!(arr.iter().filter_map(|x| x.as_str()).collect::>()); + } + } + Ok(out) +} + +/// 保存 network 策略 +pub fn save_network_config( + default: &str, + allow: &[String], + deny: &[String], + audit: bool, +) -> Result<(), String> { + let path = config_toml_path().ok_or_else(|| "无法定位 config.toml".to_string())?; + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + let mut doc: toml::Value = std::fs::read_to_string(&path) + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or_else(|| toml::Value::Table(Default::default())); + let root = doc.as_table_mut().ok_or_else(|| "config 根节点无效".to_string())?; + let mut net = toml::map::Map::new(); + net.insert("default".to_string(), toml::Value::String(default.to_string())); + net.insert( + "allow".to_string(), + toml::Value::Array(allow.iter().map(|h| toml::Value::String(h.clone())).collect()), + ); + net.insert( + "deny".to_string(), + toml::Value::Array(deny.iter().map(|h| toml::Value::String(h.clone())).collect()), + ); + net.insert("audit".to_string(), toml::Value::Boolean(audit)); + root.insert("network".to_string(), toml::Value::Table(net)); + let out = toml::to_string_pretty(&doc).map_err(|e| e.to_string())?; + std::fs::write(&path, out).map_err(|e| e.to_string())?; + Ok(()) +} + +// ===================== Memory / Note / Anchor 读写 ===================== +// +// 与 TUI 保持一致的存储约定: +// - 用户记忆 memory:全局文件 ~/.deepseek/memory.md(纯 Markdown 文本)。 +// - 工作区笔记 note:/.deepseek/notes.md,条目以 "\n---\n" 分隔。 +// - 工作区锚点 anchor:/.deepseek/anchors.md,条目以 "\n---\n" 分隔。 +// GUI 读写时复用这些路径,保证与 TUI 的 /memory、/note、/anchor 命令互通。 + +/// 全局用户记忆文件路径 ~/.deepseek/memory.md +fn memory_path() -> Option { + deepseek_home().map(|h| h.join("memory.md")) +} + +/// 工作区笔记文件路径 /.deepseek/notes.md +fn notes_path(workspace: &str) -> PathBuf { + Path::new(workspace).join(".deepseek").join("notes.md") +} + +/// 工作区锚点文件路径 /.deepseek/anchors.md +fn anchors_path(workspace: &str) -> PathBuf { + Path::new(workspace).join(".deepseek").join("anchors.md") +} + +/// 将以 "\n---\n" 分隔的文本拆分为条目列表(去除首尾空白并过滤空条目) +fn split_entries(content: &str) -> Vec { + content + .split("\n---\n") + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect() +} + +/// 将条目列表合并为以 "\n---\n" 分隔的存储文本 +fn join_entries(items: &[String]) -> String { + items + .iter() + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) + .collect::>() + .join("\n---\n") +} + +/// 读取全局用户记忆内容 +pub fn read_memory() -> Result { + let path = memory_path().ok_or_else(|| "无法定位记忆文件路径".to_string())?; + if !path.exists() { + return Ok(json!({ "path": path.to_string_lossy(), "exists": false, "content": "" })); + } + let content = std::fs::read_to_string(&path).map_err(|e| format!("读取记忆文件失败:{e}"))?; + Ok(json!({ "path": path.to_string_lossy(), "exists": true, "content": content })) +} + +/// 保存全局用户记忆内容(整文件覆盖写入) +pub fn save_memory(content: &str) -> Result<(), String> { + let path = memory_path().ok_or_else(|| "无法定位记忆文件路径".to_string())?; + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + std::fs::write(&path, content).map_err(|e| format!("写入记忆文件失败:{e}")) +} + +/// 读取工作区笔记条目列表 +pub fn read_notes(workspace: &str) -> Result { + let path = notes_path(workspace); + if !path.exists() { + return Ok(json!({ "path": path.to_string_lossy(), "exists": false, "items": [] })); + } + let s = std::fs::read_to_string(&path).map_err(|e| format!("读取笔记失败:{e}"))?; + Ok(json!({ "path": path.to_string_lossy(), "exists": true, "items": split_entries(&s) })) +} + +/// 保存工作区笔记条目列表(整文件覆盖写入) +pub fn save_notes(workspace: &str, items: &[String]) -> Result<(), String> { + let path = notes_path(workspace); + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + std::fs::write(&path, join_entries(items)).map_err(|e| format!("写入笔记失败:{e}")) +} + +/// 读取工作区锚点条目列表 +pub fn read_anchors(workspace: &str) -> Result { + let path = anchors_path(workspace); + if !path.exists() { + return Ok(json!({ "path": path.to_string_lossy(), "exists": false, "items": [] })); + } + let s = std::fs::read_to_string(&path).map_err(|e| format!("读取锚点失败:{e}"))?; + Ok(json!({ "path": path.to_string_lossy(), "exists": true, "items": split_entries(&s) })) +} + +/// 保存工作区锚点条目列表(整文件覆盖写入) +pub fn save_anchors(workspace: &str, items: &[String]) -> Result<(), String> { + let path = anchors_path(workspace); + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + std::fs::write(&path, join_entries(items)).map_err(|e| format!("写入锚点失败:{e}")) +} + +// ===================== 工作区信任目录列表 ===================== +// +// 与 TUI workspace_trust 保持一致:存储在 ~/.deepseek/workspace-trust.json, +// 结构为 { "workspaces": { <规范化工作区路径>: [<规范化信任路径>...] } }。 +// 路径统一用 std 规范化(Windows 会带 \\?\ 前缀),与 TUI 生成的键互通。 + +/// 信任列表文件路径 ~/.deepseek/workspace-trust.json +fn trust_file_path() -> Option { + deepseek_home().map(|h| h.join("workspace-trust.json")) +} + +/// 规范化路径;失败时保留原值(与 TUI canonicalize_or_keep 一致) +fn canonicalize_or_keep(path: &str) -> String { + let p = Path::new(path); + p.canonicalize() + .unwrap_or_else(|_| p.to_path_buf()) + .to_string_lossy() + .into_owned() +} + +/// 读取整个信任文件(不存在或损坏返回空对象) +fn read_trust_doc() -> Value { + trust_file_path() + .and_then(|p| std::fs::read_to_string(p).ok()) + .and_then(|s| serde_json::from_str::(&s).ok()) + .unwrap_or_else(|| json!({ "workspaces": {} })) +} + +/// 写入整个信任文件 +fn write_trust_doc(doc: &Value) -> Result<(), String> { + let path = trust_file_path().ok_or_else(|| "无法定位信任列表路径".to_string())?; + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + let out = serde_json::to_string_pretty(doc).map_err(|e| e.to_string())?; + std::fs::write(&path, out).map_err(|e| format!("写入信任列表失败:{e}")) +} + +/// 读取某工作区的信任路径列表 +pub fn read_trust(workspace: &str) -> Result { + let key = canonicalize_or_keep(workspace); + let doc = read_trust_doc(); + let items = doc + .get("workspaces") + .and_then(|w| w.get(&key)) + .and_then(|x| x.as_array()) + .map(|a| a.iter().filter_map(|x| x.as_str().map(String::from)).collect::>()) + .unwrap_or_default(); + let file = trust_file_path() + .map(|p| p.to_string_lossy().into_owned()) + .unwrap_or_default(); + Ok(json!({ "path": file, "key": key, "items": items })) +} + +/// 向某工作区信任列表新增路径,返回实际存储的规范化路径 +pub fn add_trust(workspace: &str, path: &str) -> Result { + let key = canonicalize_or_keep(workspace); + let stored = canonicalize_or_keep(path); + let mut doc = read_trust_doc(); + let workspaces = doc + .get_mut("workspaces") + .and_then(|w| w.as_object_mut()) + .ok_or_else(|| "信任文件格式错误".to_string())?; + let entry = workspaces + .entry(key) + .or_insert_with(|| Value::Array(vec![])); + let arr = entry.as_array_mut().ok_or_else(|| "信任条目格式错误".to_string())?; + if !arr.iter().any(|x| x.as_str() == Some(stored.as_str())) { + arr.push(json!(stored)); + arr.sort_by(|a, b| a.as_str().unwrap_or("").cmp(b.as_str().unwrap_or(""))); + } + write_trust_doc(&doc)?; + Ok(stored) +} + +/// 从某工作区信任列表移除路径,返回是否实际移除 +pub fn remove_trust(workspace: &str, path: &str) -> Result { + let key = canonicalize_or_keep(workspace); + let stored = canonicalize_or_keep(path); + let mut doc = read_trust_doc(); + let Some(workspaces) = doc.get_mut("workspaces").and_then(|w| w.as_object_mut()) else { + return Ok(false); + }; + let mut removed = false; + let mut clear_key = false; + if let Some(arr) = workspaces.get_mut(&key).and_then(|x| x.as_array_mut()) { + let before = arr.len(); + arr.retain(|x| x.as_str() != Some(stored.as_str())); + removed = arr.len() != before; + clear_key = arr.is_empty(); + } + if clear_key { + workspaces.remove(&key); + } + if removed { + write_trust_doc(&doc)?; + } + Ok(removed) +} + +/// 读取工作区 subagents 状态文件 +pub fn read_subagent_state(workspace: &str) -> Result { + let p = Path::new(workspace).join(".deepseek").join("subagents.v1.json"); + if !p.exists() { + return Ok(json!({ "path": p.to_string_lossy(), "exists": false, "raw": null })); + } + let s = std::fs::read_to_string(&p).map_err(|e| e.to_string())?; + let v: Value = serde_json::from_str(&s).map_err(|e| e.to_string())?; + Ok(json!({ "path": p.to_string_lossy(), "exists": true, "raw": v })) +} + +fn toml_to_json(v: &toml::Value) -> Option { + Some(match v { + toml::Value::String(s) => json!(s), + toml::Value::Integer(i) => json!(i), + toml::Value::Float(f) => json!(f), + toml::Value::Boolean(b) => json!(b), + toml::Value::Array(a) => json!(a.iter().filter_map(toml_to_json).collect::>()), + toml::Value::Table(t) => { + let mut m = serde_json::Map::new(); + for (k, val) in t { + if let Some(j) = toml_to_json(val) { + m.insert(k.clone(), j); + } + } + Value::Object(m) + } + _ => return None, + }) +} + +fn json_to_toml(v: &Value) -> Option { + match v { + Value::String(s) => Some(toml::Value::String(s.clone())), + Value::Number(n) => n + .as_i64() + .map(toml::Value::Integer) + .or_else(|| n.as_f64().map(toml::Value::Float)), + Value::Bool(b) => Some(toml::Value::Boolean(*b)), + Value::Array(a) => Some(toml::Value::Array( + a.iter().filter_map(json_to_toml).collect(), + )), + Value::Object(o) => { + let mut t = toml::map::Map::new(); + for (k, val) in o { + if let Some(tv) = json_to_toml(val) { + t.insert(k.clone(), tv); + } + } + Some(toml::Value::Table(t)) + } + _ => None, + } +} diff --git a/Deepseek-GUI/src-tauri/src/lsp/bridge.rs b/Deepseek-GUI/src-tauri/src/lsp/bridge.rs new file mode 100644 index 000000000..0c2a4a607 --- /dev/null +++ b/Deepseek-GUI/src-tauri/src/lsp/bridge.rs @@ -0,0 +1,291 @@ +//! LSP stdio 桥:spawn language server,与前端 @codemirror/lsp-client Transport 对接 + +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::process::Stdio; +use std::sync::Mutex; + +use serde::Serialize; +use tauri::{AppHandle, Emitter}; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::process::{Child, Command}; +use tokio::sync::mpsc; + +use super::registry::{Language, detect_language, server_for}; + +/// 前端 listen 的事件名 +pub const LSP_INBOUND_EVENT: &str = "lsp-inbound"; + +/// 启动会话的返回信息 +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct LspSessionInfo { + pub session_id: String, + pub language_id: String, + pub root_uri: String, + pub server_command: String, +} + +/// 推送给前端的入站消息 +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct LspInboundPayload { + pub session_id: String, + pub message: String, +} + +/// 单个 LSP 会话句柄 +struct LspSession { + /// 出站 JSON(无 Content-Length 头)→ 写 stdin + tx_outbound: mpsc::UnboundedSender, + /// 子进程句柄,drop 时 kill_on_drop 生效 + _child: Child, +} + +/// 全局 LSP 会话池 +pub struct LspBridge { + sessions: Mutex>, +} + +impl Default for LspBridge { + fn default() -> Self { + Self { + sessions: Mutex::new(HashMap::new()), + } + } +} + +impl LspBridge { + /// 确保 workspace+语言 对应的 LSP 会话已启动 + pub async fn ensure_session( + &self, + app: &AppHandle, + workspace: &str, + file_path: &str, + ) -> Result { + let workspace_path = PathBuf::from(workspace); + if workspace.trim().is_empty() || !workspace_path.is_dir() { + return Err("请先打开项目文件夹".to_string()); + } + let file = PathBuf::from(file_path); + let lang = detect_language(&file); + let Some((command, args)) = server_for(lang) else { + return Err(format!( + "当前文件类型暂无 LSP 支持({})", + lang.language_id() + )); + }; + + let session_id = session_id_for(workspace, lang); + { + let guard = self.sessions.lock().map_err(|e| e.to_string())?; + if guard.contains_key(&session_id) { + return Ok(LspSessionInfo { + session_id: session_id.clone(), + language_id: lang.language_id().to_string(), + root_uri: uri_from_path(&workspace_path), + server_command: command.to_string(), + }); + } + } + + let mut child = spawn_lsp_process(command, args, &workspace_path)?; + let stdin = child + .stdin + .take() + .ok_or_else(|| format!("LSP `{command}` 无 stdin"))?; + let stdout = child + .stdout + .take() + .ok_or_else(|| format!("LSP `{command}` 无 stdout"))?; + + let (tx_outbound, mut rx_outbound) = mpsc::unbounded_channel::(); + let app_reader = app.clone(); + let session_id_reader = session_id.clone(); + + // 写 stdin:前端 JSON → Content-Length 帧 + tauri::async_runtime::spawn(async move { + let mut stdin = stdin; + while let Some(json) = rx_outbound.recv().await { + if write_framed(&mut stdin, json.as_bytes()).await.is_err() { + break; + } + } + }); + + // 读 stdout:Content-Length 帧 → 前端 JSON 事件 + tauri::async_runtime::spawn(async move { + read_stdout_loop(stdout, app_reader, session_id_reader).await; + }); + + let session = LspSession { + tx_outbound, + _child: child, + }; + self.sessions + .lock() + .map_err(|e| e.to_string())? + .insert(session_id.clone(), session); + + Ok(LspSessionInfo { + session_id, + language_id: lang.language_id().to_string(), + root_uri: uri_from_path(&workspace_path), + server_command: command.to_string(), + }) + } + + /// 转发前端 JSON-RPC 到 LSP stdin + pub fn send(&self, session_id: &str, message: &str) -> Result<(), String> { + let guard = self.sessions.lock().map_err(|e| e.to_string())?; + let session = guard + .get(session_id) + .ok_or_else(|| format!("LSP 会话不存在:{session_id}"))?; + session + .tx_outbound + .send(message.to_string()) + .map_err(|_| "LSP 出站通道已关闭".to_string()) + } + + /// 关闭并移除会话 + pub fn stop_session(&self, session_id: &str) -> Result<(), String> { + let mut guard = self.sessions.lock().map_err(|e| e.to_string())?; + guard.remove(session_id); + Ok(()) + } + + /// 应用退出时清理全部 LSP 子进程 + pub fn shutdown_all(&self) { + if let Ok(mut guard) = self.sessions.lock() { + guard.clear(); + } + } +} + +/// 生成会话 id:workspace 哈希 + 语言键(JS/TS 共用 typescript-language-server) +fn session_id_for(workspace: &str, lang: Language) -> String { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + let mut hasher = DefaultHasher::new(); + workspace.to_lowercase().hash(&mut hasher); + let lang_key = match lang { + Language::JavaScript => "typescript", + _ => lang.session_key(), + }; + format!("{}:{:x}", lang_key, hasher.finish()) +} + +/// 启动 LSP 子进程(工作目录设为 workspace 根) +fn spawn_lsp_process( + command: &str, + args: &[&str], + workspace: &Path, +) -> Result { + let mut cmd = Command::new(command); + cmd.args(args) + .current_dir(workspace) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .kill_on_drop(true); + cmd.spawn() + .map_err(|e| format!("无法启动 LSP `{command}`:{e}(请确认已安装并在 PATH 中)")) +} + +/// 将 JSON body 按 LSP Content-Length 协议写入 stdin +async fn write_framed( + stdin: &mut tokio::process::ChildStdin, + body: &[u8], +) -> Result<(), ()> { + let header = format!("Content-Length: {}\r\n\r\n", body.len()); + if stdin.write_all(header.as_bytes()).await.is_err() { + return Err(()); + } + if stdin.write_all(body).await.is_err() { + return Err(()); + } + if stdin.flush().await.is_err() { + return Err(()); + } + Ok(()) +} + +/// 持续读取 stdout 并 emit 到前端 +async fn read_stdout_loop( + mut stdout: tokio::process::ChildStdout, + app: AppHandle, + session_id: String, +) { + let mut buf: Vec = Vec::with_capacity(16 * 1024); + let mut tmp = [0u8; 4096]; + loop { + let n = match stdout.read(&mut tmp).await { + Ok(0) => break, + Ok(n) => n, + Err(_) => break, + }; + buf.extend_from_slice(&tmp[..n]); + while let Some((header_end, content_length)) = parse_header(&buf) { + if buf.len() < header_end + content_length { + break; + } + let body = &buf[header_end..header_end + content_length]; + let text = String::from_utf8_lossy(body).to_string(); + buf.drain(..header_end + content_length); + let payload = LspInboundPayload { + session_id: session_id.clone(), + message: text, + }; + let _ = app.emit(LSP_INBOUND_EVENT, payload); + } + } +} + +/// 解析 Content-Length 头 +fn parse_header(buf: &[u8]) -> Option<(usize, usize)> { + let term = b"\r\n\r\n"; + let pos = buf.windows(term.len()).position(|w| w == term)?; + let header = std::str::from_utf8(&buf[..pos]).ok()?; + let mut content_length: Option = None; + for line in header.split("\r\n") { + if let Some(rest) = line.strip_prefix("Content-Length:") { + content_length = rest.trim().parse().ok(); + } + } + content_length.map(|cl| (pos + term.len(), cl)) +} + +/// 本地路径 → file:// URI(兼容 Windows 盘符) +pub fn uri_from_path(path: &Path) -> String { + let canonical = path.canonicalize().unwrap_or_else(|_| path.to_path_buf()); + let mut s = canonical.to_string_lossy().replace('\\', "/"); + if s.starts_with("//") { + return format!("file:{s}"); + } + if s.len() >= 2 && s.as_bytes()[1] == b':' { + return format!("file:///{}", s); + } + if !s.starts_with('/') { + s = format!("/{s}"); + } + format!("file://{s}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_content_length_header() { + let frame = b"Content-Length: 5\r\n\r\nhello"; + let (end, len) = parse_header(frame).unwrap(); + assert_eq!(end, 21); + assert_eq!(len, 5); + } + + #[test] + fn windows_uri_has_drive() { + let uri = uri_from_path(Path::new("E:/tmp/foo.rs")); + assert!(uri.starts_with("file:///")); + } +} diff --git a/Deepseek-GUI/src-tauri/src/lsp/mod.rs b/Deepseek-GUI/src-tauri/src/lsp/mod.rs new file mode 100644 index 000000000..5173166a4 --- /dev/null +++ b/Deepseek-GUI/src-tauri/src/lsp/mod.rs @@ -0,0 +1,6 @@ +//! GUI 编辑器 LSP 桥接模块 + +mod bridge; +mod registry; + +pub use bridge::{LspBridge, LspSessionInfo}; diff --git a/Deepseek-GUI/src-tauri/src/lsp/registry.rs b/Deepseek-GUI/src-tauri/src/lsp/registry.rs new file mode 100644 index 000000000..84c6ffa90 --- /dev/null +++ b/Deepseek-GUI/src-tauri/src/lsp/registry.rs @@ -0,0 +1,84 @@ +//! 语言检测与默认 LSP 可执行文件映射(与 deepseek-tui registry 对齐) + +use std::path::Path; + +/// GUI 编辑器支持 LSP 的语言 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Language { + Rust, + Go, + Python, + TypeScript, + JavaScript, + C, + Cpp, + Java, + Other, +} + +impl Language { + /// LSP textDocument.languageId + pub fn language_id(self) -> &'static str { + match self { + Language::Rust => "rust", + Language::Go => "go", + Language::Python => "python", + Language::TypeScript => "typescript", + Language::JavaScript => "javascript", + Language::C => "c", + Language::Cpp => "cpp", + Language::Java => "java", + Language::Other => "plaintext", + } + } + + /// 会话键后缀 + pub fn session_key(self) -> &'static str { + match self { + Language::Rust => "rust", + Language::Go => "go", + Language::Python => "python", + Language::TypeScript => "typescript", + Language::JavaScript => "javascript", + Language::C => "c", + Language::Cpp => "cpp", + Language::Java => "java", + Language::Other => "other", + } + } +} + +/// 从文件路径推断语言 +pub fn detect_language(path: &Path) -> Language { + let ext = match path.extension().and_then(|e| e.to_str()) { + Some(ext) => ext.to_ascii_lowercase(), + None => return Language::Other, + }; + match ext.as_str() { + "rs" => Language::Rust, + "go" => Language::Go, + "py" | "pyi" => Language::Python, + "ts" | "tsx" => Language::TypeScript, + "js" | "jsx" | "mjs" | "cjs" => Language::JavaScript, + "c" | "h" => Language::C, + "cpp" | "cc" | "cxx" | "hpp" | "hxx" | "hh" => Language::Cpp, + "java" => Language::Java, + _ => Language::Other, + } +} + +/// 默认 LSP 启动命令;None 表示该语言无内置 server +pub fn server_for(lang: Language) -> Option<(&'static str, &'static [&'static str])> { + match lang { + Language::Rust => Some(("rust-analyzer", &[])), + Language::Go => Some(("gopls", &["serve"])), + Language::Python => Some(("pyright-langserver", &["--stdio"])), + Language::TypeScript | Language::JavaScript => { + Some(("typescript-language-server", &["--stdio"])) + } + Language::C | Language::Cpp => Some(("clangd", &[])), + // jdtls 启动参数因环境差异大,MVP 暂不默认 + Language::Java => None, + Language::Other => None, + } +} diff --git a/Deepseek-GUI/src-tauri/src/main.rs b/Deepseek-GUI/src-tauri/src/main.rs new file mode 100644 index 000000000..c70ae4027 --- /dev/null +++ b/Deepseek-GUI/src-tauri/src/main.rs @@ -0,0 +1,1162 @@ +// DeepSeek GUI 的 Tauri 壳入口 +// 职责: +// 1. 启动时以子进程方式拉起后端 `deepseek-tui serve --http`(sidecar)。 +// 2. 退出时清理后端子进程。 +// 3. 暴露给前端的命令:读写 API Key(落到 ~/.deepseek/config.toml)、 +// 重启后端、查询后端二进制与配置状态。 +// 后端二进制解析顺序:环境变量 DEEPSEEK_SERVE_BIN → 与本程序同目录的 +// deepseek-tui(.exe) → PATH 上的 deepseek-tui。 + +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +mod config_bridge; +mod lsp; +mod pty; + +use config_bridge::{ + add_trust, init_mcp_config, read_anchors, read_hooks_config, read_mcp_config, read_memory, + read_network_config, read_notes, read_subagent_state, read_trust, remove_trust, save_anchors, + save_hooks_config, save_mcp_config, save_memory, save_network_config, save_notes, +}; + +use std::path::PathBuf; +use std::process::{Child, Command}; +use std::sync::Mutex; + +use tauri::{AppHandle, Manager, RunEvent, State}; + +use lsp::LspBridge; +use pty::{PtyBridge, pty_close, pty_resize, pty_spawn, pty_write}; + +/// 后端进程管理状态:固定 token + 子进程句柄 + 工作目录(agent 读写文件的根) +struct Backend { + token: String, + child: Mutex>, + /// 后端工作目录:agent 的文件工具相对此目录读写。None 表示用默认(程序目录)。 + workspace: Mutex>, +} + +/// 解析后端可执行文件路径 +fn resolve_backend_bin() -> String { + // 1. 环境变量显式指定 + if let Ok(p) = std::env::var("DEEPSEEK_SERVE_BIN") { + if !p.trim().is_empty() { + return p; + } + } + // 2. 与本程序同目录的 deepseek-tui(.exe) + if let Ok(exe) = std::env::current_exe() { + if let Some(dir) = exe.parent() { + let name = if cfg!(windows) { + "deepseek-tui.exe" + } else { + "deepseek-tui" + }; + let sibling = dir.join(name); + if sibling.exists() { + return sibling.to_string_lossy().to_string(); + } + } + } + // 3. 退回 PATH 查找 + "deepseek-tui".to_string() +} + +/// 启动后端子进程;失败返回 None(前端会显示离线并轮询重试)。 +/// workspace 为 Some 时,将其设为后端进程的工作目录,使 agent 的文件读写落在该目录。 +fn spawn_backend(token: &str, workspace: Option<&str>) -> Option { + let bin = resolve_backend_bin(); + // WebView2 在 Windows 上的页面来源是 http(s)://tauri.localhost, + // 在 macOS/Linux 上是 tauri://localhost。后端默认 CORS 白名单只含 + // tauri://localhost,这里补充 http(s)://tauri.localhost,避免被 CORS 拦截。 + let cors_origins = "http://tauri.localhost,https://tauri.localhost,tauri://localhost"; + let mut cmd = Command::new(&bin); + cmd.args(["serve", "--http"]) + .env("DEEPSEEK_RUNTIME_TOKEN", token) + .env("DEEPSEEK_CORS_ORIGINS", cors_origins); + // 设置工作目录(agent 文件工具的根目录) + if let Some(dir) = workspace { + if !dir.trim().is_empty() { + cmd.current_dir(dir); + } + } + match cmd.spawn() { + Ok(child) => { + eprintln!( + "[gui] backend started: {bin} serve --http (cwd={})", + workspace.unwrap_or("default") + ); + Some(child) + } + Err(err) => { + eprintln!("[gui] 未能启动后端 `{bin} serve --http`:{err}"); + None + } + } +} + +/// 解析 ~/.deepseek/config.toml 路径 +fn config_path() -> Option { + let home = std::env::var("USERPROFILE") + .ok() + .or_else(|| std::env::var("HOME").ok())?; + Some(PathBuf::from(home).join(".deepseek").join("config.toml")) +} + +/// 命令:返回前端需要的连接信息(后端固定 token、配置文件路径) +#[tauri::command] +fn get_settings() -> serde_json::Value { + let path = config_path() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_default(); + serde_json::json!({ + "config_path": path, + "api_key_present": api_key_present_inner(), + }) +} + +/// 命令:返回后端固定 token,供前端 API 客户端使用 +#[tauri::command] +fn get_runtime_token(state: State) -> String { + state.token.clone() +} + +/// 判断 API Key 是否已配置(环境变量或 config.toml) +fn api_key_present_inner() -> bool { + if std::env::var("DEEPSEEK_API_KEY") + .map(|v| !v.trim().is_empty()) + .unwrap_or(false) + { + return true; + } + if let Some(p) = config_path() { + if let Ok(s) = std::fs::read_to_string(&p) { + if let Ok(v) = s.parse::() { + return v + .get("api_key") + .and_then(|x| x.as_str()) + .map(|s| !s.trim().is_empty()) + .unwrap_or(false); + } + } + } + false +} + +/// 命令:把 API Key 写入 config.toml(保留其他已有配置) +#[tauri::command] +fn save_api_key(key: String) -> Result<(), String> { + let key = key.trim().to_string(); + if key.is_empty() { + return Err("API Key 不能为空".to_string()); + } + let p = config_path().ok_or_else(|| "无法定位配置目录".to_string())?; + if let Some(dir) = p.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + // 读取已有配置(不存在则空表),仅更新 api_key 字段 + let mut doc: toml::Value = std::fs::read_to_string(&p) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or_else(|| toml::Value::Table(Default::default())); + if let toml::Value::Table(ref mut t) = doc { + t.insert("api_key".to_string(), toml::Value::String(key)); + } + let out = toml::to_string_pretty(&doc).map_err(|e| e.to_string())?; + std::fs::write(&p, out).map_err(|e| e.to_string())?; + Ok(()) +} + +/// 命令:读取当前配置(api_key 仅返回是否已配置,不回传明文) +#[tauri::command] +fn get_config() -> serde_json::Value { + let mut out = serde_json::Map::new(); + if let Some(p) = config_path() { + if let Ok(s) = std::fs::read_to_string(&p) { + if let Ok(toml::Value::Table(t)) = s.parse::() { + for k in [ + "base_url", + "provider", + "default_text_model", + "reasoning_effort", + ] { + if let Some(v) = t.get(k).and_then(|x| x.as_str()) { + out.insert(k.to_string(), serde_json::Value::String(v.to_string())); + } + } + if let Some(b) = t.get("allow_shell").and_then(|x| x.as_bool()) { + out.insert("allow_shell".to_string(), serde_json::Value::Bool(b)); + } + } + } + } + out.insert( + "api_key_present".to_string(), + serde_json::Value::Bool(api_key_present_inner()), + ); + out.insert( + "config_path".to_string(), + serde_json::Value::String( + config_path() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_default(), + ), + ); + serde_json::Value::Object(out) +} + +/// 命令:把一组配置项合并写入 config.toml(保留其他已有配置)。 +/// 字符串为空表示删除该键;布尔直接写入;前端不传的键保持不变。 +#[tauri::command] +fn save_config(patch: serde_json::Value) -> Result<(), String> { + let p = config_path().ok_or_else(|| "无法定位配置目录".to_string())?; + if let Some(dir) = p.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + let mut doc: toml::Value = std::fs::read_to_string(&p) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or_else(|| toml::Value::Table(Default::default())); + let toml::Value::Table(ref mut t) = doc else { + return Err("配置文件格式错误".to_string()); + }; + if let serde_json::Value::Object(obj) = patch { + for (k, v) in obj { + match v { + serde_json::Value::String(s) => { + let s = s.trim().to_string(); + if s.is_empty() { + t.remove(&k); + } else { + t.insert(k, toml::Value::String(s)); + } + } + serde_json::Value::Bool(b) => { + t.insert(k, toml::Value::Boolean(b)); + } + serde_json::Value::Null => { + t.remove(&k); + } + _ => {} + } + } + } + let out = toml::to_string_pretty(&doc).map_err(|e| e.to_string())?; + std::fs::write(&p, out).map_err(|e| e.to_string())?; + Ok(()) +} + +// ===================== 文件系统命令(供三栏 IDE 布局使用)===================== + +/// 目录项:文件树渲染所需的最小信息 +#[derive(serde::Serialize)] +struct DirEntryInfo { + /// 文件/目录名 + name: String, + /// 绝对路径 + path: String, + /// 是否为目录 + is_dir: bool, +} + +/// 读取文件返回结构 +#[derive(serde::Serialize)] +struct FileContent { + /// 文件文本内容(按 UTF-8 有损解码) + content: String, + /// 是否因超过大小上限而被截断 + truncated: bool, + /// 是否疑似二进制文件 + binary: bool, +} + +/// 读取单个文件的大小上限(2 MiB),超出则截断,避免卡死渲染 +const MAX_READ_BYTES: usize = 2 * 1024 * 1024; + +/// 命令:弹出原生「选择文件夹」对话框,返回所选目录绝对路径 +#[tauri::command] +fn pick_folder() -> Option { + rfd::FileDialog::new() + .pick_folder() + .map(|p| p.to_string_lossy().to_string()) +} + +/// 命令:弹出原生「选择文件」对话框(/attach 等) +#[tauri::command] +fn pick_file() -> Option { + rfd::FileDialog::new() + .pick_file() + .map(|p| p.to_string_lossy().to_string()) +} + +/// 命令:列出目录下一层条目(目录在前、按名称排序)。 +/// 用于文件树的惰性展开,每次只读一层,避免深递归卡顿。 +#[tauri::command] +fn list_dir(path: String) -> Result, String> { + let rd = std::fs::read_dir(&path).map_err(|e| format!("读取目录失败:{e}"))?; + let mut entries: Vec = Vec::new(); + for item in rd.flatten() { + let p = item.path(); + let name = item.file_name().to_string_lossy().to_string(); + // 跳过常见的重型/隐藏目录,保持文件树清爽 + if matches!(name.as_str(), ".git" | "node_modules" | "target" | "dist") { + continue; + } + let is_dir = p.is_dir(); + entries.push(DirEntryInfo { + name, + path: p.to_string_lossy().to_string(), + is_dir, + }); + } + // 目录优先,其次按名称不区分大小写排序 + entries.sort_by(|a, b| match (a.is_dir, b.is_dir) { + (true, false) => std::cmp::Ordering::Less, + (false, true) => std::cmp::Ordering::Greater, + _ => a.name.to_lowercase().cmp(&b.name.to_lowercase()), + }); + Ok(entries) +} + +/// 命令:读取文件内容(有损 UTF-8 解码 + 大小上限 + 二进制探测) +#[tauri::command] +fn read_file(path: String) -> Result { + let bytes = std::fs::read(&path).map_err(|e| format!("读取文件失败:{e}"))?; + // 二进制探测:前 8KiB 内出现 NUL 字节则视为二进制 + let probe = &bytes[..bytes.len().min(8192)]; + let binary = probe.contains(&0u8); + if binary { + return Ok(FileContent { + content: String::new(), + truncated: false, + binary: true, + }); + } + let truncated = bytes.len() > MAX_READ_BYTES; + let slice = &bytes[..bytes.len().min(MAX_READ_BYTES)]; + Ok(FileContent { + content: String::from_utf8_lossy(slice).to_string(), + truncated, + binary: false, + }) +} + +/// 命令:将文本内容写入指定文件(覆盖写入,UTF-8)。 +/// 用于 GUI 代码编辑器保存;路径不存在时由调用方保证父目录已存在。 +#[tauri::command] +fn write_file(path: String, content: String) -> Result<(), String> { + let p = std::path::Path::new(&path); + if !p.exists() { + return Err("文件不存在".to_string()); + } + if p.is_dir() { + return Err("目标是目录,无法写入".to_string()); + } + std::fs::write(p, content.as_bytes()).map_err(|e| format!("写入文件失败:{e}")) +} + +/// 命令:删除指定路径的文件或目录(目录递归删除)。 +/// 返回前会做基本安全校验,避免误删盘根等危险路径。 +#[tauri::command] +fn delete_path(path: String) -> Result<(), String> { + let p = std::path::Path::new(&path); + if !p.exists() { + return Err("路径不存在".to_string()); + } + // 安全校验:拒绝删除盘根(如 C:\ 或 / 等组件数过少的路径),避免灾难性误删 + let comp_count = p.components().count(); + if comp_count <= 2 { + return Err("出于安全考虑,拒绝删除根级路径".to_string()); + } + // 目录递归删除,文件直接删除 + let result = if p.is_dir() { + std::fs::remove_dir_all(p) + } else { + std::fs::remove_file(p) + }; + result.map_err(|e| format!("删除失败:{e}")) +} + +/// 命令:新建文件(可选初始内容;父目录不存在时自动创建)。 +#[tauri::command] +fn create_file(path: String, content: Option) -> Result<(), String> { + let p = std::path::Path::new(&path); + if p.exists() { + return Err("路径已存在".to_string()); + } + if let Some(parent) = p.parent() { + if !parent.as_os_str().is_empty() { + std::fs::create_dir_all(parent).map_err(|e| format!("创建父目录失败:{e}"))?; + } + } + let body = content.unwrap_or_default(); + std::fs::write(p, body.as_bytes()).map_err(|e| format!("创建文件失败:{e}")) +} + +/// 命令:新建文件夹(含中间层级)。 +#[tauri::command] +fn create_dir(path: String) -> Result<(), String> { + let p = std::path::Path::new(&path); + if p.exists() { + return Err("路径已存在".to_string()); + } + std::fs::create_dir_all(p).map_err(|e| format!("创建文件夹失败:{e}")) +} + +/// 命令:重命名/移动文件或文件夹。 +#[tauri::command] +fn rename_path(from: String, to: String) -> Result<(), String> { + let src = std::path::Path::new(&from); + let dst = std::path::Path::new(&to); + if !src.exists() { + return Err("源路径不存在".to_string()); + } + if dst.exists() { + return Err("目标路径已存在".to_string()); + } + if let Some(parent) = dst.parent() { + if !parent.as_os_str().is_empty() { + std::fs::create_dir_all(parent).map_err(|e| format!("创建目标父目录失败:{e}"))?; + } + } + std::fs::rename(src, dst).map_err(|e| format!("重命名失败:{e}")) +} + +/// 命令:获取工作区的 git 变更(未暂存 + 已暂存)合并文本。 +/// 用于 GUI 内「查看全部变更」(/diff)。无 git 或无变更时返回友好提示。 +#[tauri::command] +fn git_diff(dir: String) -> Result { + let workdir = std::path::Path::new(&dir); + if dir.trim().is_empty() || !workdir.exists() { + return Err("请先打开一个项目文件夹".to_string()); + } + // 分别取未暂存与已暂存的 diff + let run = |args: &[&str]| -> Result { + let out = std::process::Command::new("git") + .args(args) + .current_dir(workdir) + .output() + .map_err(|e| format!("执行 git 失败:{e}(请确认已安装 git)"))?; + if !out.status.success() { + let err = String::from_utf8_lossy(&out.stderr); + return Err(format!("git 命令出错:{err}")); + } + Ok(String::from_utf8_lossy(&out.stdout).to_string()) + }; + let unstaged = run(&["diff"])?; + let staged = run(&["diff", "--staged"])?; + let mut combined = String::new(); + if !staged.trim().is_empty() { + combined.push_str("# 已暂存的变更 (staged)\n"); + combined.push_str(&staged); + combined.push('\n'); + } + if !unstaged.trim().is_empty() { + combined.push_str("# 未暂存的变更 (unstaged)\n"); + combined.push_str(&unstaged); + } + if combined.trim().is_empty() { + return Ok("(工作区没有 git 变更)".to_string()); + } + Ok(combined) +} + +/// 命令:保存附件(如粘贴的图片)到工作区 .deepseek/attachments 目录,返回绝对路径。 +/// GUI 内粘贴图片后保存为文件,再以 @相对路径 让 agent 通过工具读取/分析。 +#[tauri::command] +fn save_attachment(dir: String, name: String, bytes: Vec) -> Result { + // 基目录:优先用传入工作区,否则退回临时目录 + let base = if dir.trim().is_empty() { + std::env::temp_dir() + } else { + std::path::Path::new(&dir).to_path_buf() + }; + let att_dir = base.join(".deepseek").join("attachments"); + std::fs::create_dir_all(&att_dir).map_err(|e| format!("创建附件目录失败:{e}"))?; + // 文件名净化,避免路径穿越 + let safe = name + .chars() + .map(|c| if "\\/:*?\"<>|".contains(c) { '_' } else { c }) + .collect::(); + let target = att_dir.join(&safe); + std::fs::write(&target, &bytes).map_err(|e| format!("写入附件失败:{e}"))?; + Ok(target.to_string_lossy().to_string()) +} + +/// 从 config.toml 读取某个字符串配置项 +fn config_str(key: &str) -> Option { + let p = config_path()?; + let s = std::fs::read_to_string(&p).ok()?; + let v = s.parse::().ok()?; + v.get(key) + .and_then(|x| x.as_str()) + .map(|x| x.to_string()) + .filter(|x| !x.trim().is_empty()) +} + +/// 命令:测试与 DeepSeek API 的连通性。 +/// 优先使用传入的 api_key/base_url(用户刚输入但未保存),否则回退环境变量/配置文件; +/// 通过 GET {base}/models 验证鉴权是否有效。 +#[tauri::command] +fn test_connection(api_key: Option, base_url: Option) -> Result { + // 解析 API Key:入参 → 环境变量 → 配置文件 + let key = api_key + .map(|k| k.trim().to_string()) + .filter(|k| !k.is_empty()) + .or_else(|| std::env::var("DEEPSEEK_API_KEY").ok().filter(|k| !k.trim().is_empty())) + .or_else(|| config_str("api_key")) + .ok_or_else(|| "未配置 API Key".to_string())?; + + // 解析 Base URL:入参 → 配置文件 → 默认官方域名 + let base = base_url + .map(|b| b.trim().to_string()) + .filter(|b| !b.is_empty()) + .or_else(|| config_str("base_url")) + .unwrap_or_else(|| "https://api.deepseek.com".to_string()); + let url = format!("{}/models", base.trim_end_matches('/')); + + // 打 /models 端点(OpenAI 兼容),15s 超时 + let resp = ureq::get(&url) + .timeout(std::time::Duration::from_secs(15)) + .set("Authorization", &format!("Bearer {key}")) + .call(); + + match resp { + Ok(r) => Ok(format!("连接成功(HTTP {})", r.status())), + Err(ureq::Error::Status(401, _)) => { + Err("连接失败:HTTP 401,API Key 无效或已过期".to_string()) + } + Err(ureq::Error::Status(code, _)) => { + Err(format!("连接失败:HTTP {code},请检查 Base URL 与 Key")) + } + Err(e) => Err(format!("连接失败:{e}")), + } +} + +// ===================== 模型配置档案(多供应商管理)===================== + +/// 单个模型配置档案 +#[derive(serde::Serialize, serde::Deserialize, Clone, Default)] +struct Profile { + /// 档案唯一 id + id: String, + /// 配置名称(用户自定义,如 "DeepSeek 主号") + name: String, + /// 服务商标识(deepseek / ollama / openai …) + #[serde(default)] + provider: String, + /// API Base URL + #[serde(default)] + base_url: String, + /// 模型名称 + #[serde(default)] + model: String, + /// API Key(明文存于本地 JSON,与 config.toml 同等本地信任级别) + #[serde(default)] + api_key: String, +} + +/// 档案集合文档 +#[derive(serde::Serialize, serde::Deserialize, Default)] +struct ProfilesDoc { + /// 当前「使用中」档案 id + #[serde(default)] + active_id: String, + /// 全部档案 + #[serde(default)] + profiles: Vec, +} + +/// 档案存储文件路径:~/.deepseek/gui_profiles.json +fn profiles_path() -> Option { + let home = std::env::var("USERPROFILE") + .ok() + .or_else(|| std::env::var("HOME").ok())?; + Some( + PathBuf::from(home) + .join(".deepseek") + .join("gui_profiles.json"), + ) +} + +/// 读取档案文档(不存在或损坏则返回空文档) +fn load_profiles() -> ProfilesDoc { + profiles_path() + .and_then(|p| std::fs::read_to_string(p).ok()) + .and_then(|s| serde_json::from_str::(&s).ok()) + .unwrap_or_default() +} + +/// 写入档案文档 +fn store_profiles(doc: &ProfilesDoc) -> Result<(), String> { + let p = profiles_path().ok_or_else(|| "无法定位配置目录".to_string())?; + if let Some(dir) = p.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + let s = serde_json::to_string_pretty(doc).map_err(|e| e.to_string())?; + std::fs::write(&p, s).map_err(|e| e.to_string())?; + Ok(()) +} + +/// 命令:列出全部档案(Key 仅返回是否存在与掩码,不回传明文) +#[tauri::command] +fn list_profiles() -> serde_json::Value { + let doc = load_profiles(); + let profiles: Vec = doc + .profiles + .iter() + .map(|p| { + let present = !p.api_key.trim().is_empty(); + serde_json::json!({ + "id": p.id, + "name": p.name, + "provider": p.provider, + "base_url": p.base_url, + "model": p.model, + "key_present": present, + "key_masked": if present { "********" } else { "" }, + }) + }) + .collect(); + serde_json::json!({ "active_id": doc.active_id, "profiles": profiles }) +} + +/// 命令:新增或更新档案。 +/// 传入 id 为空 → 新增并返回新 id;否则按 id 更新。 +/// api_key 留空表示「保持原 Key 不变」(更新场景)。新增且无 active 时自动设为使用中。 +#[tauri::command] +fn upsert_profile(profile: serde_json::Value) -> Result { + let mut doc = load_profiles(); + let id_in = profile + .get("id") + .and_then(|v| v.as_str()) + .unwrap_or("") + .trim() + .to_string(); + let name = profile + .get("name") + .and_then(|v| v.as_str()) + .unwrap_or("") + .trim() + .to_string(); + if name.is_empty() { + return Err("配置名称不能为空".to_string()); + } + let provider = profile + .get("provider") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); + let base_url = profile + .get("base_url") + .and_then(|v| v.as_str()) + .unwrap_or("") + .trim() + .to_string(); + let model = profile + .get("model") + .and_then(|v| v.as_str()) + .unwrap_or("") + .trim() + .to_string(); + let key_in = profile + .get("api_key") + .and_then(|v| v.as_str()) + .unwrap_or("") + .trim() + .to_string(); + + if id_in.is_empty() { + // 新增:用毫秒时间戳生成 id + let id = format!( + "p{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_millis()) + .unwrap_or(0) + ); + if key_in.is_empty() { + return Err("新增配置必须填写 API Key".to_string()); + } + doc.profiles.push(Profile { + id: id.clone(), + name, + provider, + base_url, + model, + api_key: key_in, + }); + // 首个档案自动设为使用中 + if doc.active_id.trim().is_empty() { + doc.active_id = id.clone(); + } + store_profiles(&doc)?; + Ok(id) + } else { + // 更新:定位现有档案 + let p = doc + .profiles + .iter_mut() + .find(|p| p.id == id_in) + .ok_or_else(|| "未找到要更新的配置".to_string())?; + p.name = name; + p.provider = provider; + p.base_url = base_url; + p.model = model; + // Key 留空则保留原值 + if !key_in.is_empty() { + p.api_key = key_in; + } + store_profiles(&doc)?; + Ok(id_in) + } +} + +/// 命令:删除档案。若删的是使用中档案,则清空 active(或落到第一个)。 +#[tauri::command] +fn delete_profile(id: String) -> Result<(), String> { + let mut doc = load_profiles(); + doc.profiles.retain(|p| p.id != id); + if doc.active_id == id { + doc.active_id = doc.profiles.first().map(|p| p.id.clone()).unwrap_or_default(); + } + store_profiles(&doc)?; + Ok(()) +} + +/// 命令:将某档案设为「使用中」——把其 Key/BaseURL/模型/服务商写入 +/// config.toml 后重启后端使其生效。 +#[tauri::command] +fn activate_profile(state: State, id: String) -> Result<(), String> { + let mut doc = load_profiles(); + let profile = doc + .profiles + .iter() + .find(|p| p.id == id) + .cloned() + .ok_or_else(|| "未找到该配置".to_string())?; + + // 写入 config.toml(保留其他键) + let cfg = config_path().ok_or_else(|| "无法定位配置目录".to_string())?; + if let Some(dir) = cfg.parent() { + std::fs::create_dir_all(dir).map_err(|e| e.to_string())?; + } + let mut toml_doc: toml::Value = std::fs::read_to_string(&cfg) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or_else(|| toml::Value::Table(Default::default())); + if let toml::Value::Table(ref mut t) = toml_doc { + t.insert( + "api_key".to_string(), + toml::Value::String(profile.api_key.clone()), + ); + // 空串视为清除,让后端用默认 + set_or_remove(t, "base_url", &profile.base_url); + set_or_remove(t, "provider", &profile.provider); + set_or_remove(t, "default_text_model", &profile.model); + } + let out = toml::to_string_pretty(&toml_doc).map_err(|e| e.to_string())?; + std::fs::write(&cfg, out).map_err(|e| e.to_string())?; + + // 标记使用中 + doc.active_id = id; + store_profiles(&doc)?; + + // 重启后端 + restart_backend_inner(&state) +} + +/// 辅助:非空则写入键,空串则删除键 +fn set_or_remove(t: &mut toml::value::Table, key: &str, val: &str) { + let v = val.trim(); + if v.is_empty() { + t.remove(key); + } else { + t.insert(key.to_string(), toml::Value::String(v.to_string())); + } +} + +/// 重启后端的内部实现(供命令复用)。重启时沿用当前工作目录设置。 +fn restart_backend_inner(state: &State) -> Result<(), String> { + // 先取工作目录(独立锁,避免与 child 锁交叉持有) + let workspace = state.workspace.lock().ok().and_then(|w| w.clone()); + let mut guard = state.child.lock().map_err(|e| e.to_string())?; + if let Some(mut c) = guard.take() { + let _ = c.kill(); + let _ = c.wait(); + } + std::thread::sleep(std::time::Duration::from_millis(900)); + *guard = spawn_backend(&state.token, workspace.as_deref()); + if guard.is_none() { + return Err("后端启动失败:未找到 deepseek-tui 可执行文件".to_string()); + } + Ok(()) +} + +/// 命令:设置后端工作目录(agent 文件读写根目录)并重启后端使其生效。 +/// 前端在「打开文件夹」或启动时携带记忆的根目录调用。 +#[tauri::command] +fn set_workspace(state: State, path: String) -> Result<(), String> { + { + let mut w = state.workspace.lock().map_err(|e| e.to_string())?; + *w = if path.trim().is_empty() { + None + } else { + Some(path.trim().to_string()) + }; + } + restart_backend_inner(&state) +} + +/// 命令:重启后端,使新写入的配置生效 +#[tauri::command] +fn restart_backend(state: State) -> Result<(), String> { + restart_backend_inner(&state) +} + +// ===================== LSP 编辑器补全(stdio 桥 → CodeMirror lsp-client)===================== + +/// 命令:为 workspace+文件 确保 LSP 会话已启动(按语言复用 server 进程) +#[tauri::command] +async fn lsp_start_session( + app: AppHandle, + state: State<'_, LspBridge>, + workspace: String, + file_path: String, +) -> Result { + state.ensure_session(&app, &workspace, &file_path).await +} + +/// 命令:向前端 Transport 转发 JSON-RPC(纯 JSON 字符串,无 Content-Length 头) +#[tauri::command] +fn lsp_send(state: State<'_, LspBridge>, session_id: String, message: String) -> Result<(), String> { + state.send(&session_id, &message) +} + +/// 命令:关闭 LSP 会话并终止子进程 +#[tauri::command] +fn lsp_stop_session(state: State<'_, LspBridge>, session_id: String) -> Result<(), String> { + state.stop_session(&session_id) +} + +/// 截取字符串末尾若干字符(按 Unicode 标量,避免切断多字节字符) +fn tail_chars(s: &str, max: usize) -> String { + s.chars() + .rev() + .take(max) + .collect::() + .chars() + .rev() + .collect() +} + +/// 截取字符串开头若干字符 +fn head_chars(s: &str, max: usize) -> String { + s.chars().take(max).collect() +} + +/// 清理模型输出:去掉 markdown 围栏与首尾空白 +fn sanitize_inline_completion(raw: &str) -> String { + let mut t = raw.trim().to_string(); + if t.starts_with("```") { + if let Some(rest) = t.strip_prefix("```") { + let rest = rest.trim_start(); + if let Some(i) = rest.find('\n') { + t = rest[i + 1..].to_string(); + } else { + t = rest.to_string(); + } + } + if let Some(end) = t.rfind("```") { + t = t[..end].to_string(); + } + } + t.trim_end().to_string() +} + +/// 命令:Cursor Tab 风格 AI 内联补全(chat completions,非流式) +#[tauri::command] +fn tab_complete( + file_path: String, + prefix: String, + suffix: String, + language_id: Option, + auto_import: Option, +) -> Result { + let key = std::env::var("DEEPSEEK_API_KEY") + .ok() + .filter(|k| !k.trim().is_empty()) + .or_else(|| config_str("api_key")) + .ok_or_else(|| "未配置 API Key".to_string())?; + + let base = config_str("base_url").unwrap_or_else(|| "https://api.deepseek.com".to_string()); + let base = base.trim_end_matches('/').to_string(); + let url = format!("{base}/chat/completions"); + + let model = config_str("default_text_model").unwrap_or_else(|| "deepseek-v4-flash".to_string()); + let lang = language_id.unwrap_or_else(|| "plaintext".to_string()); + + let prefix_tail = tail_chars(&prefix, 3500); + let suffix_head = head_chars(&suffix, 1500); + + // 基础系统提示:纯插入式补全 + let mut system = String::from( + "You are an inline code completion engine (like GitHub Copilot or Cursor Tab). \ +Output ONLY the exact text to insert at the cursor between PREFIX and SUFFIX. \ +No markdown fences, no quotes, no explanation. Match indentation, naming, and style of the file.", + ); + // 自动 import:开启时允许补全在开头补上缺失的 import 语句 + if auto_import.unwrap_or(false) { + system.push_str( + " If the inserted code references a symbol that is not yet imported in the PREFIX, \ +prepend the minimal required import statement(s) on their own line(s) before the completion, \ +using the language's conventional import syntax and matching existing import style.", + ); + } + + let user = format!( + "File: {file_path}\nLanguage: {lang}\n\n\ +PREFIX (ends at cursor):\n```\n{prefix_tail}\n```\n\n\ +SUFFIX (starts after cursor):\n```\n{suffix_head}\n```" + ); + + let body = serde_json::json!({ + "model": model, + "messages": [ + {"role": "system", "content": system}, + {"role": "user", "content": user} + ], + "max_tokens": 160, + "temperature": 0.1, + "stream": false + }); + + let body_str = serde_json::to_string(&body).map_err(|e| format!("序列化请求失败:{e}"))?; + + let resp = ureq::post(&url) + .timeout(std::time::Duration::from_secs(12)) + .set("Authorization", &format!("Bearer {key}")) + .set("Content-Type", "application/json") + .send_string(&body_str); + + match resp { + Ok(r) => { + let status = r.status(); + let text = r.into_string().map_err(|e| format!("读取响应失败:{e}"))?; + if status >= 400 { + return Err(format!("Tab 补全失败:HTTP {status} — {text}")); + } + let v: serde_json::Value = + serde_json::from_str(&text).map_err(|e| format!("解析 JSON 失败:{e}"))?; + let content = v + .pointer("/choices/0/message/content") + .and_then(|x| x.as_str()) + .unwrap_or(""); + Ok(sanitize_inline_completion(content)) + } + Err(ureq::Error::Status(code, r)) => { + let detail = r.into_string().unwrap_or_default(); + Err(format!("Tab 补全失败:HTTP {code} — {detail}")) + } + Err(e) => Err(format!("Tab 补全请求失败:{e}")), + } +} + +// ===================== MCP / Hooks / Network 配置桥接 ===================== + +#[tauri::command] +fn get_mcp_config() -> Result { + read_mcp_config() +} + +#[tauri::command] +fn save_mcp_config_cmd(doc: serde_json::Value) -> Result<(), String> { + save_mcp_config(doc) +} + +#[tauri::command] +fn init_mcp_config_cmd(force: bool) -> Result { + init_mcp_config(force) +} + +#[tauri::command] +fn get_hooks_config() -> Result { + read_hooks_config() +} + +#[tauri::command] +fn save_hooks_config_cmd(enabled: bool, hooks: serde_json::Value) -> Result<(), String> { + save_hooks_config(enabled, hooks) +} + +#[tauri::command] +fn get_network_config() -> Result { + read_network_config() +} + +#[tauri::command] +fn save_network_config_cmd( + default: String, + allow: Vec, + deny: Vec, + audit: bool, +) -> Result<(), String> { + save_network_config(&default, &allow, &deny, audit) +} + +#[tauri::command] +fn get_subagent_state(workspace: String) -> Result { + read_subagent_state(&workspace) +} + +// ===================== Memory / Note / Anchor 配置桥接 ===================== + +/// 命令:读取全局用户记忆内容 +#[tauri::command] +fn get_memory() -> Result { + read_memory() +} + +/// 命令:保存全局用户记忆内容(整文件覆盖) +#[tauri::command] +fn save_memory_cmd(content: String) -> Result<(), String> { + save_memory(&content) +} + +/// 命令:读取工作区笔记条目 +#[tauri::command] +fn get_notes(workspace: String) -> Result { + read_notes(&workspace) +} + +/// 命令:保存工作区笔记条目(整列表覆盖) +#[tauri::command] +fn save_notes_cmd(workspace: String, items: Vec) -> Result<(), String> { + save_notes(&workspace, &items) +} + +/// 命令:读取工作区锚点条目 +#[tauri::command] +fn get_anchors(workspace: String) -> Result { + read_anchors(&workspace) +} + +/// 命令:保存工作区锚点条目(整列表覆盖) +#[tauri::command] +fn save_anchors_cmd(workspace: String, items: Vec) -> Result<(), String> { + save_anchors(&workspace, &items) +} + +// ===================== 工作区信任目录列表桥接 ===================== + +/// 命令:读取某工作区的信任路径列表 +#[tauri::command] +fn get_trust(workspace: String) -> Result { + read_trust(&workspace) +} + +/// 命令:新增信任路径,返回实际存储的规范化路径 +#[tauri::command] +fn add_trust_cmd(workspace: String, path: String) -> Result { + add_trust(&workspace, &path) +} + +/// 命令:移除信任路径,返回是否实际移除 +#[tauri::command] +fn remove_trust_cmd(workspace: String, path: String) -> Result { + remove_trust(&workspace, &path) +} + +fn main() { + // 本地开发固定 token:后端默认随机生成 token,这里固定为已知值, + // 前端通过 get_runtime_token 命令获取,实现开箱即用。 + let token = + std::env::var("DEEPSEEK_RUNTIME_TOKEN").unwrap_or_else(|_| "dev-local-token".to_string()); + + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![ + get_settings, + get_runtime_token, + save_api_key, + get_config, + save_config, + restart_backend, + pick_folder, + pick_file, + list_dir, + read_file, + write_file, + delete_path, + create_file, + create_dir, + rename_path, + git_diff, + save_attachment, + test_connection, + list_profiles, + upsert_profile, + delete_profile, + activate_profile, + set_workspace, + lsp_start_session, + lsp_send, + lsp_stop_session, + tab_complete, + get_mcp_config, + save_mcp_config_cmd, + init_mcp_config_cmd, + get_hooks_config, + save_hooks_config_cmd, + get_network_config, + save_network_config_cmd, + get_subagent_state, + get_memory, + save_memory_cmd, + get_notes, + save_notes_cmd, + get_anchors, + save_anchors_cmd, + get_trust, + add_trust_cmd, + remove_trust_cmd, + pty_spawn, + pty_write, + pty_resize, + pty_close + ]) + .setup({ + let token = token.clone(); + move |app| { + // 启动时尚未选择工作目录,用默认(程序目录);前端会在挂载时 + // 携带记忆的根目录调用 set_workspace 重启到正确目录。 + let child = spawn_backend(&token, None); + app.manage(LspBridge::default()); + app.manage(PtyBridge::default()); + app.manage(Backend { + token: token.clone(), + child: Mutex::new(child), + workspace: Mutex::new(None), + }); + Ok(()) + } + }) + .build(tauri::generate_context!()) + .expect("error while building tauri application") + .run(|app_handle, event| { + if let RunEvent::Exit = event { + if let Some(state) = app_handle.try_state::() { + state.shutdown_all(); + } + if let Some(state) = app_handle.try_state::() { + state.shutdown_all(); + } + if let Some(state) = app_handle.try_state::() { + if let Ok(mut guard) = state.child.lock() { + if let Some(mut child) = guard.take() { + let _ = child.kill(); + } + } + } + } + }); +} diff --git a/Deepseek-GUI/src-tauri/src/pty.rs b/Deepseek-GUI/src-tauri/src/pty.rs new file mode 100644 index 000000000..3e9045530 --- /dev/null +++ b/Deepseek-GUI/src-tauri/src/pty.rs @@ -0,0 +1,211 @@ +//! GUI 集成终端:portable-pty .spawn + Tauri 事件推送 stdout 到前端 xterm + +use std::collections::HashMap; +use std::io::{Read, Write}; +use std::path::PathBuf; +use std::sync::Mutex; +use std::thread; + +use portable_pty::{native_pty_system, CommandBuilder, MasterPty, PtySize}; +use tauri::{AppHandle, Emitter, State}; + +/// PTY 输出事件(前端 listen `pty-output`) +#[derive(Clone, serde::Serialize)] +struct PtyOutputEvent { + id: String, + data: String, +} + +/// 单个活跃 PTY 会话 +struct ActivePty { + master: Box, + writer: Box, + child: Box, + reader_handle: Option>, +} + +/// 全局 PTY 会话表(Tauri managed state) +pub struct PtyBridge { + sessions: Mutex>, +} + +impl Default for PtyBridge { + fn default() -> Self { + Self { + sessions: Mutex::new(HashMap::new()), + } + } +} + +impl PtyBridge { + /// 退出应用时关闭全部 PTY + pub fn shutdown_all(&self) { + if let Ok(mut map) = self.sessions.lock() { + for (_, mut session) in map.drain() { + let _ = session.child.kill(); + if let Some(h) = session.reader_handle.take() { + let _ = h.join(); + } + } + } + } +} + +/// 默认交互式 shell 命令 +fn build_shell_command() -> CommandBuilder { + let mut cmd = if cfg!(windows) { + let mut c = CommandBuilder::new("powershell.exe"); + c.arg("-NoLogo"); + c + } else { + let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/bash".into()); + CommandBuilder::new(shell) + }; + cmd.env("TERM", "xterm-256color"); + cmd.env("COLORTERM", "truecolor"); + cmd +} + +/// 生成唯一 PTY 会话 id +fn new_pty_id() -> String { + format!( + "pty-{}", + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_nanos()) + .unwrap_or(0) + ) +} + +/// 启动 PTY 会话并返回 id +#[tauri::command] +pub fn pty_spawn( + app: AppHandle, + bridge: State, + cwd: Option, + cols: Option, + rows: Option, +) -> Result { + let cols = cols.unwrap_or(120).max(20); + let rows = rows.unwrap_or(30).max(5); + let id = new_pty_id(); + + let pty_system = native_pty_system(); + let pair = pty_system + .openpty(PtySize { + rows, + cols, + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| format!("openpty 失败:{e}"))?; + + let mut cmd = build_shell_command(); + if let Some(dir) = cwd.filter(|d| !d.trim().is_empty()) { + cmd.cwd(PathBuf::from(dir)); + } + + let child = pair + .slave + .spawn_command(cmd) + .map_err(|e| format!("spawn shell 失败:{e}"))?; + drop(pair.slave); + + let mut reader = pair + .master + .try_clone_reader() + .map_err(|e| format!("clone reader 失败:{e}"))?; + let writer = pair + .master + .take_writer() + .map_err(|e| format!("take writer 失败:{e}"))?; + + let app_for_reader = app.clone(); + let reader_id = id.clone(); + let reader_handle = thread::spawn(move || { + let mut buf = [0u8; 4096]; + loop { + match reader.read(&mut buf) { + Ok(0) => break, + Ok(n) => { + let chunk = String::from_utf8_lossy(&buf[..n]).into_owned(); + let _ = app_for_reader.emit( + "pty-output", + PtyOutputEvent { + id: reader_id.clone(), + data: chunk, + }, + ); + } + Err(_) => break, + } + } + }); + + let session = ActivePty { + master: pair.master, + writer, + child, + reader_handle: Some(reader_handle), + }; + + bridge + .sessions + .lock() + .map_err(|_| "PTY 锁中毒".to_string())? + .insert(id.clone(), session); + + Ok(id) +} + +/// 向 PTY 写入用户输入 +#[tauri::command] +pub fn pty_write(bridge: State, id: String, data: String) -> Result<(), String> { + let mut map = bridge.sessions.lock().map_err(|_| "PTY 锁中毒".to_string())?; + let session = map + .get_mut(&id) + .ok_or_else(|| format!("PTY `{id}` 不存在"))?; + session + .writer + .write_all(data.as_bytes()) + .map_err(|e| format!("写入失败:{e}"))?; + session.writer.flush().map_err(|e| format!("flush 失败:{e}"))?; + Ok(()) +} + +/// 调整 PTY 尺寸 +#[tauri::command] +pub fn pty_resize( + bridge: State, + id: String, + cols: u16, + rows: u16, +) -> Result<(), String> { + let map = bridge.sessions.lock().map_err(|_| "PTY 锁中毒".to_string())?; + let session = map + .get(&id) + .ok_or_else(|| format!("PTY `{id}` 不存在"))?; + session + .master + .resize(PtySize { + rows: rows.max(5), + cols: cols.max(20), + pixel_width: 0, + pixel_height: 0, + }) + .map_err(|e| format!("resize 失败:{e}"))?; + Ok(()) +} + +/// 关闭 PTY 会话 +#[tauri::command] +pub fn pty_close(bridge: State, id: String) -> Result<(), String> { + let mut map = bridge.sessions.lock().map_err(|_| "PTY 锁中毒".to_string())?; + if let Some(mut session) = map.remove(&id) { + let _ = session.child.kill(); + if let Some(h) = session.reader_handle.take() { + let _ = h.join(); + } + } + Ok(()) +} diff --git a/Deepseek-GUI/src-tauri/tauri.conf.json b/Deepseek-GUI/src-tauri/tauri.conf.json new file mode 100644 index 000000000..ca4d51832 --- /dev/null +++ b/Deepseek-GUI/src-tauri/tauri.conf.json @@ -0,0 +1,39 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "DeepSeek GUI", + "version": "0.1.0", + "identifier": "com.deepseek.gui", + "build": { + "frontendDist": "../dist", + "devUrl": "http://localhost:1420", + "beforeDevCommand": "npm run dev", + "beforeBuildCommand": "" + }, + "app": { + "windows": [ + { + "label": "main", + "title": "DeepSeek GUI", + "width": 1100, + "height": 720, + "minWidth": 800, + "minHeight": 560, + "decorations": false, + "theme": "Dark" + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.ico" + ] + } +} diff --git a/Deepseek-GUI/src-tauri/tauri.macos.conf.json b/Deepseek-GUI/src-tauri/tauri.macos.conf.json new file mode 100644 index 000000000..2e41845b9 --- /dev/null +++ b/Deepseek-GUI/src-tauri/tauri.macos.conf.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "bundle": { + "resources": { + "bin/deepseek-tui": "./" + }, + "icon": [ + "icons/icon.icns", + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png" + ] + } +} diff --git a/Deepseek-GUI/src-tauri/tauri.windows.conf.json b/Deepseek-GUI/src-tauri/tauri.windows.conf.json new file mode 100644 index 000000000..918155e8a --- /dev/null +++ b/Deepseek-GUI/src-tauri/tauri.windows.conf.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "bundle": { + "resources": { + "bin/deepseek-tui.exe": "./" + } + } +} diff --git a/Deepseek-GUI/src/App.tsx b/Deepseek-GUI/src/App.tsx new file mode 100644 index 000000000..2203018e1 --- /dev/null +++ b/Deepseek-GUI/src/App.tsx @@ -0,0 +1,1486 @@ +// 应用主组件:连接后端、管理会话列表与当前会话、消息收发与审批 + +import { useCallback, useEffect, useMemo, useRef, useState, type MouseEvent as ReactMouseEvent } from "react"; +import { RuntimeClient, DEFAULT_BASE_URL, waitForBackend, type ClientConfig } from "./api/client"; +import type { ThreadRecord, UsageTotals, WorkspaceStatus } from "./api/types"; +import { useConversation } from "./state/useConversation"; +import { MessageItem } from "./components/MessageItem"; +import { Composer } from "./components/Composer"; +import { QueueBar } from "./components/QueueBar"; +import { ApprovalDialog } from "./components/ApprovalDialog"; +import { SettingsView, type SettingsTab } from "./components/SettingsView"; +import { AgentHistoryPanel } from "./components/AgentHistoryPanel"; +import { DiffModal } from "./components/DiffModal"; +import { SnapshotsModal } from "./components/SnapshotsModal"; +import { TerminalPanel } from "./components/TerminalPanel"; +import { UsageModal } from "./components/UsageModal"; +import { CommandPalette, type PaletteCommand } from "./components/CommandPalette"; +import { QuickOpen } from "./components/QuickOpen"; +import { ThreadSearch } from "./components/ThreadSearch"; +import { NoticeList } from "./components/NoticeList"; +import { RuleComplianceBanner } from "./components/RuleComplianceBanner"; +import { FileTree } from "./components/FileTree"; +import { EditorPanel } from "./components/EditorPanel"; +import { TitleMenuBar } from "./components/TitleMenuBar"; +import { StatusZoom } from "./components/StatusZoom"; +import { getRuntimeToken, getConfig, isTauri, pickFolder, restartBackend, saveConfig, setWorkspace } from "./api/tauri"; +import { executeSlashCommand } from "./utils/executeSlashCommand"; +import { cycleReasoningEffort, type ReasoningEffort } from "./utils/reasoningEffort"; +import { useEditorTabs } from "./hooks/useEditorTabs"; +import { useUiZoom } from "./hooks/useUiZoom"; +import { useResizablePanels } from "./hooks/useResizablePanels"; +import { useTranscriptAutoScroll } from "./hooks/useTranscriptAutoScroll"; +import { useRuleCompliance } from "./hooks/useRuleCompliance"; +import { resolveWorkspacePath, workspacePathsEqual } from "./utils/workspacePaths"; +import { + filterThreadsForWorkspace, + pickThreadForWorkspace, + saveLastThreadForWorkspace, +} from "./utils/workspaceSessions"; +import { loadLocale, saveLocale, t, type Locale } from "./i18n"; +import { deriveThreadTitleFromMessage, formatThreadTabTitle, isUntitledThread } from "./utils/threadTitle"; + +/** 可选模型与模式 */ +const MODELS = ["deepseek-v4-pro", "deepseek-v4-flash", "auto"]; +const MODES = ["plan", "agent", "yolo"]; + +/** 从 localStorage 读取已保存的连接配置 */ +function loadCfg(): ClientConfig { + const baseUrl = localStorage.getItem("ds_base_url") || DEFAULT_BASE_URL; + // 默认携带本地开发 token(与 Tauri 壳启动后端时设置的值一致) + const token = localStorage.getItem("ds_token") || "dev-local-token"; + return { baseUrl, token }; +} + +export function App() { + // 后端连接配置(可在顶部连接栏修改 baseUrl/token,持久化到 localStorage) + const [cfg, setCfg] = useState(loadCfg); + const client = useMemo(() => new RuntimeClient(cfg), [cfg]); + + // 连接栏的临时输入值 + const [formUrl, setFormUrl] = useState(cfg.baseUrl); + const [formToken, setFormToken] = useState(cfg.token ?? ""); + + /** 保存连接配置并触发重连 */ + const applyConn = () => { + const next: ClientConfig = { + baseUrl: formUrl.trim() || DEFAULT_BASE_URL, + token: formToken.trim() || undefined, + }; + localStorage.setItem("ds_base_url", next.baseUrl); + if (next.token) localStorage.setItem("ds_token", next.token); + else localStorage.removeItem("ds_token"); + setCfg(next); + }; + + const [backendUp, setBackendUp] = useState(null); + const [threads, setThreads] = useState([]); + const [activeId, setActiveId] = useState(null); + const [showSettings, setShowSettings] = useState(false); + /** 设置页当前分类(Cursor 风格侧栏) */ + const [settingsTab, setSettingsTab] = useState("models"); + // 历史会话浏览器开关 + const [showSessions, setShowSessions] = useState(false); + // 全量变更(/diff)模态开关 + const [showDiff, setShowDiff] = useState(false); + // 快照还原模态框(/restore) + const [showSnapshots, setShowSnapshots] = useState(false); + // 底部集成终端面板(VS Code 风格):是否显示 + const [showTerminal, setShowTerminal] = useState(false); + // 终端是否已被打开过(首次打开后保持挂载,避免反复 spawn PTY) + const [terminalEverOpened, setTerminalEverOpened] = useState(false); + // 终端面板高度(px),可拖动调整 + const [termHeight, setTermHeight] = useState(260); + // 切换底部终端显示;首次打开时标记已挂载 + const toggleTerminal = useCallback(() => { + setShowTerminal((v) => { + const next = !v; + if (next) setTerminalEverOpened(true); + return next; + }); + }, []); + // 拖动终端面板顶边调整高度 + const onTermResizeStart = useCallback((e: ReactMouseEvent) => { + e.preventDefault(); + const startY = e.clientY; + const startH = termHeight; + const onMove = (ev: MouseEvent) => { + const delta = startY - ev.clientY; + // 限制在 120~视口高度-160 之间,避免压垮其他区域 + const max = window.innerHeight - 160; + setTermHeight(Math.max(120, Math.min(max, startH + delta))); + }; + const onUp = () => { + window.removeEventListener("mousemove", onMove); + window.removeEventListener("mouseup", onUp); + }; + window.addEventListener("mousemove", onMove); + window.addEventListener("mouseup", onUp); + }, [termHeight]); + /** Token 用量弹窗(/cost / /tokens) */ + const [showUsage, setShowUsage] = useState(false); + /** 推理强度(config.toml,Shift+Tab 切换) */ + const [reasoningEffort, setReasoningEffort] = useState("medium"); + /** Composer 插入 @ 路径(/attach 命令回调) */ + const composerInsertRef = useRef<((relPath: string) => void) | null>(null); + // 命令面板(Ctrl+K)开关 + const [showPalette, setShowPalette] = useState(false); + // 快速打开文件(Ctrl+P) + const [showQuickOpen, setShowQuickOpen] = useState(false); + // 搜索会话(Ctrl+Shift+P) + const [showThreadSearch, setShowThreadSearch] = useState(false); + /** 界面语言 */ + const [locale, setLocale] = useState(loadLocale); + /** 会话 system_prompt 编辑草稿 */ + const [systemPromptDraft, setSystemPromptDraft] = useState(""); + const [showSystemPrompt, setShowSystemPrompt] = useState(false); + /** Git 工作区状态(分支、变更数) */ + const [wsStatus, setWsStatus] = useState(null); + /** 左侧资源管理器是否展开(仿 Cursor 活动栏切换) */ + const [sidebarOpen, setSidebarOpen] = useState(true); + // 右侧聊天面板是否展开(仿 Cursor 两边收起) + const [chatOpen, setChatOpen] = useState(true); + // 是否在标签栏显示已归档会话 + const [showArchived, setShowArchived] = useState(false); + // 命令菜单开关 + const [showCmd, setShowCmd] = useState(false); + // 新建会话时使用的默认模型/模式 + const [newModel, setNewModel] = useState(MODELS[0]); + const [newMode, setNewMode] = useState("agent"); + // 会话用量统计 + const [usage, setUsage] = useState(null); + // 三栏 IDE:项目根目录与当前打开的文件 + const [rootPath, setRootPath] = useState( + () => localStorage.getItem("ds_root") || null, + ); + // 多标签编辑器状态 + const editor = useEditorTabs(); + // 可拖拽分栏宽度(传入 sidebarOpen 以便 clamp 计算) + const panels = useResizablePanels(sidebarOpen); + /** 全局 UI 缩放(整窗,仿 Cursor) */ + const uiZoom = useUiZoom(); + // 文件树刷新令牌:自增即触发文件树重新读盘 + const [treeTick, setTreeTick] = useState(0); + // Composer 消息队列:回合进行中排队,结束后自动按序发送(对齐 TUI /queue) + const [queued, setQueued] = useState([]); + // 消息暂存:停泊到本地持久存储,稍后弹回队列(对齐 TUI /stash) + const [stash, setStash] = useState(() => { + try { + const raw = localStorage.getItem("ds_stash"); + return raw ? (JSON.parse(raw) as string[]) : []; + } catch { + return []; + } + }); + // 暂存变化时持久化到 localStorage + useEffect(() => { + try { + localStorage.setItem("ds_stash", JSON.stringify(stash)); + } catch { + // 忽略持久化失败(隐私模式 / 配额) + } + }, [stash]); + /** 桌面版:加载推理强度配置 */ + useEffect(() => { + if (!isTauri()) return; + void getConfig().then((c) => { + const e = c?.reasoning_effort; + if (e && typeof e === "string") setReasoningEffort(e as ReasoningEffort); + }); + }, [showSettings]); + + /** 切换推理强度并重启后端使 config.toml 生效 */ + const onReasoningEffortChange = useCallback(async (next: ReasoningEffort) => { + setReasoningEffort(next); + if (!isTauri()) return; + try { + await saveConfig({ reasoning_effort: next }); + await restartBackend(); + } catch (e) { + alert(`推理强度保存失败:${(e as Error).message}`); + } + }, []); + + /** Shift+Tab 循环推理强度 */ + const cycleReasoningEffortHandler = useCallback(() => { + void onReasoningEffortChange(cycleReasoningEffort(reasoningEffort)); + }, [reasoningEffort, onReasoningEffortChange]); + + /** 启动时是否已按记忆的项目根目录恢复过会话 */ + const didStartupSessionRestore = useRef(false); + + // 当前激活的线程记录 + const activeThread = threads.find((t) => t.id === activeId) ?? null; + /** 当前资源管理器项目下的会话(标签栏仅显示这些,类似 Cursor) */ + const workspaceThreads = useMemo(() => { + if (!rootPath) { + return threads.filter((t) => showArchived || !t.archived); + } + return filterThreadsForWorkspace(threads, rootPath, showArchived); + }, [threads, rootPath, showArchived]); + /** Agent 实际读写的工作目录(会话创建时绑定,与左侧资源管理器可能不同) */ + const agentWorkspace = activeThread?.workspace ?? rootPath; + /** 当前会话工作目录与资源管理器不一致(换文件夹后仍用旧会话时) */ + const workspaceMismatch = Boolean( + activeThread && rootPath && !workspacePathsEqual(activeThread.workspace, rootPath), + ); + + /** 关闭任务/技能/历史/设置等子视图,回到聊天主界面 */ + const showChatView = useCallback(() => { + setShowSessions(false); + setShowSettings(false); + }, []); + + /** 打开设置页并定位到指定分类 */ + const openSettings = useCallback((tab: SettingsTab = "models") => { + setShowSessions(false); + setSettingsTab(tab); + setShowSettings(true); + }, []); + + /** 新建会话:绑定当前(或指定)工作目录,避免 Agent 写到旧文件夹 */ + const createThread = useCallback( + async (workspaceOverride?: string) => { + const ws = workspaceOverride ?? rootPath ?? undefined; + try { + const t = await client.createThread({ model: newModel, mode: newMode, workspace: ws }); + setThreads((prev) => [t, ...prev]); + showChatView(); + setActiveId(t.id); + if (ws) saveLastThreadForWorkspace(ws, t.id); + } catch (e) { + alert(`新建会话失败:${(e as Error).message}`); + } + }, + [client, newModel, newMode, rootPath, showChatView], + ); + + /** + * 打开项目文件夹:同步后端 cwd,并恢复该项目上次 Agent 或新建会话(Cursor 行为) + */ + const openWorkspace = useCallback( + async (dir: string) => { + setRootPath(dir); + editor.closeAll(); + localStorage.setItem("ds_root", dir); + await setWorkspace(dir); + const ok = await waitForBackend(client); + if (!ok) { + alert(t("workspace.backendTimeout", locale)); + setTreeTick((n) => n + 1); + return; + } + setBackendUp(true); + let list: ThreadRecord[] = []; + try { + list = await client.listThreads(showArchived); + setThreads(list); + } catch { + list = []; + } + const existing = pickThreadForWorkspace(list, dir); + showChatView(); + if (existing) { + setActiveId(existing.id); + saveLastThreadForWorkspace(dir, existing.id); + } else { + await createThread(dir); + } + setTreeTick((n) => n + 1); + }, + [client, createThread, editor, locale, showArchived, showChatView], + ); + + /** 选择项目文件夹(文件对话框) */ + const chooseFolder = useCallback(async () => { + try { + const dir = await pickFolder(); + if (!dir) return; + await openWorkspace(dir); + } catch (e) { + alert(`打开文件夹失败:${(e as Error).message}`); + } + }, [openWorkspace]); + + /** 打开文件:供文件树、@ 引用等调用 */ + const handleOpenFile = useCallback( + (path: string) => { + editor.openFile(path); + }, + [editor], + ); + + /** 关闭文件标签 */ + const handleCloseFile = useCallback( + (path: string) => { + editor.closeFile(path); + }, + [editor], + ); + + /** 从消息/Agent 事件打开文件(相对路径基于会话绑定的工作目录解析) */ + const openFileFromWorkspace = useCallback( + (path: string) => { + const abs = resolveWorkspacePath(agentWorkspace, path); + if (abs) handleOpenFile(abs); + }, + [agentWorkspace, handleOpenFile], + ); + + /** 保存当前会话 system_prompt */ + const saveSystemPrompt = useCallback(async () => { + if (!activeId) return; + const sp = systemPromptDraft.trim(); + try { + await client.patchThread(activeId, { system_prompt: sp || null }); + setThreads((prev) => + prev.map((t) => (t.id === activeId ? { ...t, system_prompt: sp || undefined } : t)), + ); + } catch (e) { + alert(`保存系统提示词失败:${(e as Error).message}`); + } + }, [client, activeId, systemPromptDraft]); + + /** 切换界面语言 */ + const toggleLocale = useCallback(() => { + setLocale((cur) => { + const next: Locale = cur === "zh" ? "en" : "zh"; + saveLocale(next); + return next; + }); + }, []); + + // 桌面应用内:从壳层获取后端真实 token,覆盖默认值 + useEffect(() => { + if (!isTauri()) return; + getRuntimeToken() + .then((tok) => { + if (tok) { + setFormToken(tok); + setCfg((prev) => ({ ...prev, token: tok })); + } + }) + .catch(() => { + /* 忽略 */ + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // 启动时若记忆了项目根目录,把后端工作目录切到该目录(重启后端) + useEffect(() => { + if (!isTauri() || !rootPath) return; + setWorkspace(rootPath).catch(() => { + /* 忽略:后端可能尚未就绪,轮询会重试 */ + }); + // 仅在挂载时执行一次 + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // 当前激活线程变化时同步 system_prompt 草稿 + useEffect(() => { + setSystemPromptDraft(activeThread?.system_prompt ?? ""); + }, [activeThread?.id, activeThread?.system_prompt]); + + // 订阅当前会话事件(多线程缓存 + since_seq 续传) + const conv = useConversation(cfg, activeId); + /** 最后一条用户消息(/retry) */ + const lastUserMessage = useMemo(() => { + for (let i = conv.items.length - 1; i >= 0; i--) { + const it = conv.items[i]!; + if (it.kind === "user_message" && it.text.trim()) return it.text.trim(); + } + return null; + }, [conv.items]); + /** 聊天消息区滚底:发送后跳转 + 流式跟随 */ + const { ref: transcriptRef, scrollAfterSend } = useTranscriptAutoScroll( + conv.items, + conv.running, + activeId, + ); + + // 自动批准 / 信任:后端仍会 SSE 推送 approval.required,前端静默代批避免重复弹窗 + useEffect(() => { + if (!activeThread || conv.approvals.length === 0) return; + if (!activeThread.auto_approve && !activeThread.trust_mode) return; + const pending = conv.approvals[0]; + void conv.resolveApproval(pending.approvalId, "approve", true); + }, [ + activeThread?.auto_approve, + activeThread?.trust_mode, + conv.approvals, + conv.resolveApproval, + ]); + + /** 用户手动审批:批准后若勾选 remember,同步本地 thread 状态 */ + const handleResolveApproval = useCallback( + async (approvalId: string, decision: "approve" | "reject", remember: boolean) => { + await conv.resolveApproval(approvalId, decision, remember); + if (decision !== "approve" || !remember || !activeId) return; + setThreads((prev) => + prev.map((t) => (t.id === activeId ? { ...t, auto_approve: true } : t)), + ); + }, + [conv.resolveApproval, activeId], + ); + + /** 是否应对用户展示审批弹窗(自动批准/信任时由 effect 静默处理) */ + const showApprovalDialog = + conv.approvals.length > 0 && + !activeThread?.auto_approve && + !activeThread?.trust_mode; + + // 回合进行中:每个文件写入完成即刷新文件树(不自动打开编辑器标签) + useEffect(() => { + if (conv.fileChangeTick <= 0) return; + setTreeTick((n) => n + 1); + }, [conv.fileChangeTick]); + + // Agent 运行中轻量轮询,兜底 shell 等方式创建的目录/文件 + useEffect(() => { + if (!conv.running) return; + const timer = setInterval(() => setTreeTick((n) => n + 1), 2000); + return () => clearInterval(timer); + }, [conv.running]); + + // 回合结束后再刷新一次文件树(不批量打开标签) + useEffect(() => { + if (conv.usageTick <= 0) return; + setTreeTick((n) => n + 1); + }, [conv.usageTick]); + + /** 探测后端健康并加载会话列表(可含已归档) */ + const refresh = useCallback(async () => { + const ok = await client.health(); + setBackendUp(ok); + if (ok) { + try { + setThreads(await client.listThreads(showArchived)); + } catch { + setThreads([]); + } + } + }, [client, showArchived]); + + // 启动时探测,并定时重试直到后端就绪 + useEffect(() => { + refresh(); + const timer = setInterval(refresh, 5000); + return () => clearInterval(timer); + }, [refresh]); + + // 启动后:若记忆了项目根目录,自动恢复该项目的 Agent 会话(无则新建) + useEffect(() => { + if (didStartupSessionRestore.current || !backendUp || !rootPath || threads.length === 0) { + return; + } + const active = activeId ? threads.find((th) => th.id === activeId) : null; + if (active && workspacePathsEqual(active.workspace, rootPath)) { + didStartupSessionRestore.current = true; + return; + } + didStartupSessionRestore.current = true; + const existing = pickThreadForWorkspace(threads, rootPath); + if (existing) { + showChatView(); + setActiveId(existing.id); + saveLastThreadForWorkspace(rootPath, existing.id); + } else if (!activeId) { + void createThread(rootPath); + } + }, [backendUp, rootPath, threads, activeId, createThread, showChatView]); + + // 回合完成或切换会话时刷新用量统计 + useEffect(() => { + if (!backendUp) return; + client + .getUsage() + .then((u) => setUsage(u.totals)) + .catch(() => setUsage(null)); + }, [client, backendUp, conv.usageTick, activeId]); + + // 定期刷新 Git 工作区状态(分支、暂存/未暂存计数) + useEffect(() => { + if (!backendUp) return; + client + .getWorkspaceStatus() + .then(setWsStatus) + .catch(() => setWsStatus(null)); + }, [client, backendUp, conv.usageTick, rootPath]); + + /** 选中某个会话并切回聊天视图(在任务/设置等子页时点击标签也能生效) */ + const selectThread = useCallback( + (id: string) => { + showChatView(); + setActiveId(id); + const thread = threads.find((th) => th.id === id); + if (thread?.workspace) saveLastThreadForWorkspace(thread.workspace, id); + }, + [showChatView, threads], + ); + + /** 将资源管理器根目录同步到指定工作区(切换跨项目会话时使用) */ + const syncRootPathForThread = useCallback( + async (workspace: string): Promise => { + if (!workspace.trim()) return true; + if (rootPath && workspacePathsEqual(rootPath, workspace)) return true; + setRootPath(workspace); + editor.closeAll(); + localStorage.setItem("ds_root", workspace); + if (!isTauri()) { + setTreeTick((n) => n + 1); + return true; + } + await setWorkspace(workspace); + const ok = await waitForBackend(client); + if (!ok) { + alert(t("workspace.backendTimeout", locale)); + return false; + } + setBackendUp(true); + setTreeTick((n) => n + 1); + return true; + }, + [client, editor, locale, rootPath], + ); + + /** + * 切换会话;可选同步左侧项目文件夹到该会话绑定的工作区 + */ + const switchToThread = useCallback( + async (id: string, options?: { syncWorkspace?: boolean }) => { + let thread = threads.find((th) => th.id === id); + // 搜索到的会话可能不在当前标签列表,需拉取详情以获知 workspace + if (!thread && options?.syncWorkspace) { + try { + const detail = await client.getThread(id); + thread = detail.thread; + setThreads((prev) => + prev.some((t) => t.id === id) ? prev : [detail.thread, ...prev], + ); + } catch { + /* 仍尝试切换 id */ + } + } + if (options?.syncWorkspace && thread?.workspace) { + const ok = await syncRootPathForThread(thread.workspace); + if (!ok) return; + } + selectThread(id); + }, + [client, selectThread, syncRootPathForThread, threads], + ); + + /** 修改当前会话的模型、模式或安全开关 */ + const onChangeThreadField = useCallback( + async (patch: { + model?: string; + mode?: string; + allow_shell?: boolean; + trust_mode?: boolean; + auto_approve?: boolean; + }) => { + if (!activeId) return; + try { + await client.patchThread(activeId, patch); + setThreads((prev) => prev.map((t) => (t.id === activeId ? { ...t, ...patch } : t))); + } catch (e) { + alert(`更新会话失败:${(e as Error).message}`); + } + }, + [client, activeId], + ); + + /** 关闭(归档)会话:从列表移除(除非正在查看归档),若是当前会话则清空 */ + const onCloseThread = useCallback( + async (id: string) => { + try { + await client.patchThread(id, { archived: true }); + if (showArchived) { + // 归档视图下:保留并标记为已归档 + setThreads((prev) => prev.map((t) => (t.id === id ? { ...t, archived: true } : t))); + } else { + setThreads((prev) => prev.filter((t) => t.id !== id)); + } + setActiveId((cur) => (cur === id ? null : cur)); + } catch (e) { + alert(`关闭会话失败:${(e as Error).message}`); + } + }, + [client, showArchived], + ); + + /** 恢复已归档会话 */ + const onRestoreThread = useCallback( + async (id: string) => { + try { + await client.patchThread(id, { archived: false }); + setThreads((prev) => prev.map((t) => (t.id === id ? { ...t, archived: false } : t))); + } catch (e) { + alert(`恢复会话失败:${(e as Error).message}`); + } + }, + [client], + ); + + /** 重命名会话 */ + const onRenameThread = useCallback( + async (id: string, current: string) => { + const next = window.prompt("会话名称", current); + if (next == null) return; + const title = next.trim(); + if (!title || title === current) return; + try { + await client.patchThread(id, { title }); + setThreads((prev) => prev.map((t) => (t.id === id ? { ...t, title } : t))); + } catch (e) { + alert(`重命名失败:${(e as Error).message}`); + } + }, + [client], + ); + + /** 发送普通用户消息(不含斜杠拦截) */ + const sendPrompt = useCallback( + async (text: string) => { + if (!activeId) return; + scrollAfterSend(); + const threadId = activeId; + const shouldAutoTitle = activeThread && isUntitledThread(activeThread); + const optimisticTitle = shouldAutoTitle ? deriveThreadTitleFromMessage(text) : null; + try { + const { thread } = await client.startTurn(threadId, { prompt: text }); + setThreads((prev) => + prev.map((th) => { + if (th.id !== threadId) return th; + const merged = { ...th, ...thread }; + if (shouldAutoTitle && optimisticTitle && isUntitledThread(merged)) { + return { ...merged, title: optimisticTitle }; + } + return merged; + }), + ); + } catch (e) { + alert(`发送失败:${(e as Error).message}`); + } + }, + [client, activeId, activeThread, scrollAfterSend], + ); + + /** 入队一条消息(去除首尾空白后非空才入队) */ + const enqueue = useCallback((text: string) => { + const v = text.trim(); + if (v) setQueued((prev) => [...prev, v]); + }, []); + + /** 队列进行中自动排空:回合结束且有排队项时,按序发送下一条 */ + useEffect(() => { + if (conv.running) return; + if (queued.length === 0) return; + if (!activeId) return; + const [next, ...rest] = queued; + setQueued(rest); + void sendPrompt(next); + }, [conv.running, queued, activeId, sendPrompt]); + + /** 执行斜杠命令:返回 true 表示已处理(不再作为普通消息发送) */ + const runSlashCommand = useCallback( + async (raw: string): Promise => { + return executeSlashCommand(raw, { + client, + locale, + models: MODELS, + modes: MODES, + activeId, + activeThread, + rootPath, + lastUserMessage, + setShowDiff, + setShowSessions, + setShowUsage, + setShowSnapshots, + afterRestore: () => { + // 还原后刷新文件树(打开的文件可重新打开以加载新内容) + setTreeTick((n) => n + 1); + }, + openSettings, + onChangeThreadField, + refresh, + setActiveId, + openWorkspace, + chooseFolder, + onSend: sendPrompt, + insertAttachmentPath: (rel) => composerInsertRef.current?.(rel), + enqueue, + clearQueue: () => setQueued([]), + stashQueue: () => + setQueued((q) => { + if (q.length > 0) setStash((s) => [...s, ...q]); + return []; + }), + popStash: () => + setStash((s) => { + if (s.length > 0) setQueued((q) => [...q, ...s]); + return []; + }), + clearStash: () => setStash([]), + }); + }, + [ + client, + locale, + activeId, + activeThread, + rootPath, + lastUserMessage, + openSettings, + onChangeThreadField, + refresh, + openWorkspace, + chooseFolder, + sendPrompt, + enqueue, + ], + ); + + /** 发送消息(支持斜杠命令) */ + const onSend = useCallback( + async (text: string) => { + if (!activeId) return; + if (text.trim().startsWith("/")) { + await runSlashCommand(text); + return; + } + await sendPrompt(text); + }, + [activeId, runSlashCommand, sendPrompt], + ); + + /** 转向 */ + const onSteer = useCallback( + async (text: string) => { + if (!activeId) return; + scrollAfterSend(); + const turnId = conv.currentTurnId; + if (!turnId) { + await onSend(text); + return; + } + const trySteer = () => client.steerTurn(activeId, turnId, text); + try { + await trySteer(); + } catch (e) { + const msg = (e as Error).message; + const stale = + msg.includes("Thread is not loaded") || + msg.includes("No active turn") || + msg.includes("not active on thread"); + if (!stale) { + alert(`转向失败:${msg}`); + return; + } + // 后端引擎未加载或回合状态过期:先 resume 再重试,仍失败则作为新消息发送 + try { + await client.resumeThread(activeId); + await trySteer(); + } catch { + try { + const { thread } = await client.startTurn(activeId, { prompt: text }); + setThreads((prev) => + prev.map((th) => (th.id === activeId ? { ...th, ...thread } : th)), + ); + } catch (e2) { + alert(`发送失败:${(e2 as Error).message}`); + } + } + } + }, + [client, activeId, conv.currentTurnId, onSend, scrollAfterSend], + ); + + /** 回合结束后按 alwaysApply 规则检测缺口(如 README),并自动/手动跟进 */ + const ruleCompliance = useRuleCompliance({ + rootPath: agentWorkspace, + running: conv.running, + lastTurnChangedPaths: conv.lastTurnChangedPaths, + onFollowUp: onSend, + locale, + }); + + /** 打断 */ + const onInterrupt = useCallback(async () => { + if (!activeId || !conv.currentTurnId) return; + try { + await client.interruptTurn(activeId, conv.currentTurnId); + } catch (e) { + alert(`打断失败:${(e as Error).message}`); + } + }, [client, activeId, conv.currentTurnId]); + + /** + * 切换右栏历史会话 / 中栏设置:设置打开时占中间编辑器区域,Chat 保持可见。 + */ + const toggleView = useCallback( + (view: "sessions" | "settings" | null) => { + const isOpen = + (view === "sessions" && showSessions) || + (view === "settings" && showSettings); + const target = isOpen ? null : view; + setShowSessions(target === "sessions"); + if (target === "settings") { + openSettings("models"); + } else { + setShowSettings(false); + } + }, + [showSessions, showSettings, openSettings], + ); + + /** 历史会话恢复成功:刷新列表并切换到新线程 */ + const onSessionResumed = useCallback( + async (threadId: string) => { + setShowSessions(false); + try { + const detail = await client.getThread(threadId); + if (detail.thread.workspace) { + await syncRootPathForThread(detail.thread.workspace); + } + setThreads((prev) => { + const exists = prev.some((t) => t.id === threadId); + if (exists) { + return prev.map((t) => (t.id === threadId ? detail.thread : t)); + } + return [detail.thread, ...prev]; + }); + } catch { + await refresh(); + } + selectThread(threadId); + }, + [client, refresh, selectThread, syncRootPathForThread], + ); + + // 全局快捷键:Ctrl/Cmd+K 命令面板、Ctrl/Cmd+P 快速打开 + useEffect(() => { + const onKey = (e: KeyboardEvent) => { + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") { + e.preventDefault(); + setShowPalette((v) => !v); + } + if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "p") { + e.preventDefault(); + if (e.shiftKey) setShowThreadSearch((v) => !v); + else setShowQuickOpen((v) => !v); + } + // Ctrl+` 切换底部集成终端(仿 VS Code) + if ((e.ctrlKey || e.metaKey) && e.key === "`") { + e.preventDefault(); + toggleTerminal(); + } + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [toggleTerminal]); + + /** 命令面板可执行项 */ + const paletteCommands = useMemo( + () => [ + { id: "new", title: "新建会话", hint: "+", run: () => void createThread() }, + { id: "sessions", title: "浏览历史会话", hint: "/sessions", run: () => toggleView("sessions") }, + { id: "diff", title: "查看工作区变更", hint: "/diff", run: () => setShowDiff(true) }, + { + id: "review", + title: "代码审查当前改动", + hint: "/review", + run: () => runSlashCommand("/review"), + disabled: !activeId, + }, + { + id: "compact", + title: "压缩上下文", + hint: "/compact", + run: () => runSlashCommand("/compact"), + disabled: !activeId, + }, + { + id: "fork", + title: "复刻当前会话", + hint: "/fork", + run: () => runSlashCommand("/fork"), + disabled: !activeId, + }, + { + id: "mode", + title: "切换 Agent 模式", + hint: "/mode", + run: () => runSlashCommand("/mode"), + disabled: !activeId, + }, + { + id: "trust", + title: "切换信任模式", + hint: "/trust", + run: () => runSlashCommand("/trust"), + disabled: !activeId, + }, + { + id: "export", + title: "导出当前会话", + hint: "/export", + run: () => runSlashCommand("/export"), + disabled: !activeId, + }, + { + id: "tokens", + title: "Token 用量", + hint: "/tokens", + run: () => runSlashCommand("/tokens"), + disabled: !activeId, + }, + { + id: "workspace", + title: "切换工作区", + hint: "/workspace", + run: () => runSlashCommand("/workspace"), + }, + { id: "tasks", title: "任务 / 自动化", hint: "📋", run: () => openSettings("tasks") }, + { id: "skills", title: "技能 / MCP", hint: "🧩", run: () => openSettings("skills") }, + { id: "rules", title: "项目规则", hint: "📜", run: () => openSettings("rules"), disabled: !rootPath }, + { id: "open", title: "打开项目文件夹", hint: "📁", run: chooseFolder }, + { + id: "quickopen", + title: t("palette.quickOpen", locale), + hint: "Ctrl+P", + run: () => setShowQuickOpen(true), + disabled: !rootPath, + }, + { + id: "threadsearch", + title: t("search.threadsTitle", locale), + hint: "Ctrl+Shift+P", + run: () => setShowThreadSearch(true), + }, + { id: "settings", title: "设置", hint: "⚙", run: () => toggleView("settings") }, + { id: "conn", title: "后端连接", hint: "🔌", run: () => openSettings("connection") }, + ], + [createThread, chooseFolder, runSlashCommand, toggleView, openSettings, activeId, locale, rootPath], + ); + + return ( +
+ {/* 顶栏菜单(仿 Cursor File / Edit / View …) */} + void chooseFolder()} + onQuickOpen={() => setShowQuickOpen(true)} + onCommandPalette={() => setShowPalette(true)} + onSearchChats={() => setShowThreadSearch(true)} + onOpenSettings={openSettings} + onToggleSidebar={() => setSidebarOpen((v) => !v)} + onToggleChat={() => setChatOpen((v) => !v)} + chatOpen={chatOpen} + onNewChat={() => void createThread()} + onShowDiff={() => setShowDiff(true)} + onToggleSessions={() => toggleView("sessions")} + onToggleTerminal={toggleTerminal} + settingsOpen={showSettings} + onToggleSettings={() => toggleView("settings")} + onZoomIn={uiZoom.zoomIn} + onZoomOut={uiZoom.zoomOut} + onZoomReset={uiZoom.zoomReset} + /> +
+
+ {/* 左栏:资源管理器(文件树);侧栏开关已移至顶栏菜单栏 */} + + + {/* 中栏:代码编辑 或 设置(Cursor 风格:设置占中间主区域),底部可停靠终端 */} +
+
+ {showSettings ? ( + setShowSettings(false)} + onSaved={refresh} + rootPath={rootPath} + backendUp={backendUp} + formUrl={formUrl} + formToken={formToken} + onFormUrlChange={setFormUrl} + onFormTokenChange={setFormToken} + onApplyConnection={() => { + applyConn(); + void refresh(); + }} + showArchived={showArchived} + onShowArchivedChange={setShowArchived} + /> + ) : ( + void chooseFolder()} + onQuickOpen={() => setShowQuickOpen(true)} + onCommandPalette={() => setShowPalette(true)} + onSearchChats={() => setShowThreadSearch(true)} + onOpenSettings={() => openSettings("models")} + onNewChat={() => void createThread()} + workspaceRoot={rootPath} + /> + )} +
+
+ + {/* 底部集成终端(仿 Cursor):作为 .ide 网格底部行,跨侧栏+编辑器,往左到底;首次打开后保持挂载,仅切显隐以保活 PTY */} + {terminalEverOpened && ( +
+
+
+ {locale === "zh" ? "终端" : "Terminal"} + +
+
+ +
+
+ )} + + {/* 右栏:聊天(Agent 面板) */} +